Collage
Rack middleware that packages your JS into a single file.
git clone git://github.com/djanowski/collage.git
GET http://github.com/djanowski/collage
Collage
Rack middleware that packages your JS into a single file.
Usage
This middleware will package all your JavaScript into a single file – very much inspired by Rails’
javascript_include_tag(:all, :cache => true).use Collage, :path => File.dirname(__FILE__) + "/public" use Collage, :path => File.dirname(__FILE__) + "/public", :files => ["jquery*.js", "*.js"]Collage also provides a handy helper for your views. This is useful because it appends the correct timestamp to the
srcattribute, so you won’t have any issues with intermediate caches.<%= Collage.html_tag("./public") %>Installation
$ gem sources -a http://gems.github.com (you only have to do this once) $ sudo gem install djanowski-collageLicense
Copyright (c) 2009 Damian Janowski for Citrusbyte
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Contest
Contexts for Test::Unit.
git clone git://github.com/citrusbyte/contest.git
GET http://github.com/citrusbyte/contest
Contest
Contexts for Test::Unit.
Description
Write declarative tests using nested contexts without performance penalties. Contest is less than 100 lines of code and gets the job done.
Usage
Declare your tests as you would in RSpec or Shoulda:
require 'contest' class SomeTest < Test::Unit::TestCase setup do @value = 1 end teardown do @value = nil end test "sample test" do assert_equal 1, @value end context "a context" do setup do @value += 1 end test "more tests" do assert_equal 2, @value end context "a nested context" do setup do @value += 1 end test "yet more tests" do assert_equal 3, @value end end end endFor your convenience,
contextis aliased asdescribeandtestis aliased asshould, so this is valid:class SomeTest < Test::Unit::TestCase setup do @value = 1 end describe "something" do setup do @value += 1 end should "equal 2" do assert_equal 2, @value end end endYou can run it normally, it’s Test::Unit after all. If you want to run a particular test, say “yet more tests”, try this:
$ testrb my_test.rb -n test_yet_more_testsOr with a regular expression:
$ testrb my_test.rb -n /yet_more_tests/Installation
$ sudo gem install contestIf you want to use it with Rails, add this to config/environment.rb:
config.gem "contest"Then you can vendor the gem:
rake gems:install rake gems:unpackLicense
Copyright (c) 2009 Damian Janowski and Michel Martens for Citrusbyte
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Joe
Release your gems to RubyForge, no pain involved.
git clone git://github.com/djanowski/joe.git
GET http://github.com/djanowski/joe
Joe
Release your gems to RubyForge, no pain involved.
Usage
Joe assumes you have a .gemspec file in the current directory and it will use it to build the gem. Once you have it, try this:
$ thor joe:packageCongratulations, you should have your gem built and an archive copy inside the
pkgdirectory.Now go ahead and release your new gem to RubyForge:
$ thor joe:releaseEasy, right? Wait a few minutes until RubyForge updates its gems index and you will be able to run
sudo gem install foo.Troubleshooting
If you get an error about a missing
group_id, try runningrubyforge config. This hooks up your RubyForge account with the gem and configures where to release it.Maintaining a gemspec file
The easiest way we’ve found to maintain a gemspec file is by creating a
foo.gemspec.erbtemplate (see example). Then you can use a Thor task to produce the real gemspec file:$ thor joe:gemspecInstallation
You need the
rubyforgegem in order to release files. If you don’t have it already:$ sudo gem install rubyforge $ rubyforge setup $ rubyforge configMake sure you have Thor installed:
$ sudo gem install thorAnd then simply:
$ thor install http://dimaion.com/joe/joe.thorThat’s it. Try:
$ thor -TAnd you will get a list of Thor tasks.
Thanks
Thanks to Blake Mizerany for pointing us to the Sinatra Rakefile for reference.
License
Copyright (c) 2009 Damian Janowski and Michel Martens
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
jQuery Form Watermark
Add a watermark to your form elements – the jQuery way
git clone git://github.com/citrusbyte/jquery-watermark.git
GET http://github.com/citrusbyte/jquery-watermark
jQuery Form Watermark
Add a watermark to your form elements – the jQuery way
Description
So you have a text box and you want a default text on it acting as a call to action. When the user focuses on the text box, the default text should go away (and its styling could change, too). What you need is jQuery and this little snippet.
Usage
The only thing you need to add to you form is a
titleattribute:<form id="search"> <input type="text" title="Type to search" /> </form>Now in your JavaScript file:
$(document).ready(function() { $("#search").watermark(); });And if you want to make it look prettier:
input.watermark { color: #888; }Or, clone the repo and try the example.
Installation
You can grab the file directly from http://github.com/citrusbyte/jquery-watermark, or
$ git clone git://github.com/citrusbyte/jquery-watermark.git…and pick it from there. Make sure it’s required after jquery and jquery-ui.
License
Copyright (c) 2009 Damian Janowski for Citrusbyte
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
MicroMachine
Minimal Finite State Machine.
git clone git://github.com/soveran/micromachine.git
GET http://github.com/soveran/micromachine
MicroMachine
Minimal Finite State Machine.
Description
There are many finite state machine implementations for Ruby, and they all provide a nice DSL for declaring events, exceptions, callbacks, and all kinds of niceties in general.
But if all you want is a finite state machine, look no further: this is only 22 lines of code and provides everything a finite state machine must have, and nothing more.
Usage
require 'micromachine' machine = MicroMachine.new(:new) # Initial state. machine.transitions_for[:confirm] = { :new => :confirmed } machine.transitions_for[:ignore] = { :new => :ignored } machine.transitions_for[:reset] = { :confirmed => :new, :ignored => :new } machine.trigger(:confirm) #=> true machine.trigger(:ignore) #=> false machine.trigger(:reset) #=> true machine.trigger(:ignore) #=> trueIt can also have callbacks when entering some state:
machine.on(:confirmed) do puts "Confirmed" endAdding MicroMachine to your models
The most popular pattern among Ruby libraries that tackle this problem is to extend the model and transform it into a finite state machine. Instead of working as a mixin, MicroMachine’s implementation is by composition: you instantiate a finite state machine (or many!) inside your model and you are in charge of querying and persisting the state. Here’s an example of how to use it with an ActiveRecord model:
class Event < ActiveRecord::Base before_save :persist_confirmation def confirm! confirmation.trigger(:confirm) end def cancel! confirmation.trigger(:cancel) end def reset! confirmation.trigger(:reset) end def confirmation @confirmation ||= begin @confirmation = MicroMachine.new(confirmation_state || "pending") @confirmation.transitions_for[:confirm] = { "pending" => "confirmed" } @confirmation.transitions_for[:cancel] = { "confirmed" => "cancelled" } @confirmation.transitions_for[:reset] = { "confirmed" => "pending", "cancelled" => "pending" } @confirmation end end private def persist_confirmation self.confirmation_state = confirmation.state end endThis example asumes you have a :confirmationstate attribute in your model. This may look like a very verbose implementation, but you gain a lot in flexibility.
Installation
$ sudo gem install micromachineLicense
Copyright (c) 2009 Michel Martens for Citrusbyte
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Milton
Milton is an upload and attachment handling plugin for Rails. It is built for extensibility and has support for resizing images and Amazon S3 built in.
git clone git://github.com/citrusbyte/milton.git
GET http://github.com/citrusbyte/milton
Milton
Milton is an upload and attachment handling plugin for Rails. It is built for extensibility and has support for resizing images and Amazon S3 built in.
Description
Milton is an upload handling plugin for Rails. It is meant to handle multiple types of file uploads. It includes support for thumbnailing of image uploads, and more processors can easily be added for handling other file types. It also supports multiple file stores including disk storage and Amazon S3. Milton also supports on-demand file processing for prototyping.
Getting Started
You can get started with Milton with the default settings by simply calling
is_attachmenton your ActiveRecord model you’ll be handling files with. It is expected that your underlying table will have afilename(string) column. If you’d like you can also definecontent_type(string) andsize(integer) columns to have that info automatically stored as well.Example
An
Assetmodel:class Asset < ActiveRecord::Base is_attachment endYour new
Assetform:- form_for Asset.new, :html => { :enctype => 'multipart/form-data' } do |f| = f.file_field :file = f.submit 'Upload'Note: don’t forget to add
:html => { :enctype => 'multipart/form-data' }Your
AssetsController:class AssetsController < ApplicationController def create @asset = Asset.create params[:asset] end endResizing Images
Currently Milton relies on ImageMagick (but not RMagick!). Once you have ImageMagick installed, add the
:thumbnailprocessor to your configuration and define your processing recipes:class Image < ActiveRecord::Base is_attachment :recipes => { :thumb => [{ :thumbnail => { :size => '100x100', :crop => true } }] } end @image.path => .../000/000/000/001/milton.jpg @image.path(:thumb) => .../000/000/000/001/milton.crop_size-100x100.jpgThumbnail Options
Note: currently the only options supported are
:sizeand:crop.
:sizetakes a geometry string similar to other image-manipulation plugins (based off ImageMagick’s geometry strings).
:size => '50x50'will resize the larger dimension down to 50px and maintain aspect ratio (you can use:cropfor forced zoom/cropping).
:size => '50x'will resize the width to 50px and maintain aspect ratio.
:size => 'x50'will resize the height to 50px and maintain aspect ratio.Then you can throw in
:cropto get zoom/cropping functionality:@image.path(:thumbnail => { :size => '50x50', :crop => true })This will create a 50px x 50px version of the image regardless of the source aspect-ratio. It will not distort the source image, rather it will resize the image as close to fitting as possible without distorting, then crop off the remainder.
By default
:cropuses a North/Center gravity – so the remainder will be cropped from the bottom and equally from both sides.Note: the
:sizeoption is required when resizing.Embedding Images
#pathwill always return the full path to the image, in your views you probably want to refer to the “public” path – the portion of the path from your/publicfolder up for embedding your images. For now there is a helper method that gets attached to your model called#public_paththat simply gives you the path frompublicon.@image.public_path => '/assets/000/000/001/234/milton.jpg'As opposed to:
@image.path(:thumb) => '/var/www/site/public/assets/000/000/001/234/milton.jpg'Note: if you use the
:file_system_pathoption to upload your files to somewhere outside of yourpublicfolder this will no longer work. You can pass a different folder topublic_pathto use as an alternate base.@image.public_path(:thumb, 'uploads')Processors
Processors are defined in recipes and are loaded when Milton is loaded. In the above example we’re telling Milton to load the thumbnail processor (which comes with Milton) and then telling it to pass
{ :size => '100x100', :crop => true }to the thumbnail processor in order to create a derivative called:thumbfor all uploaded Images.More processors can be loaded and combined into the recipe, to be run in the order specified (hence the array syntax), i.e.:
class Image < ActiveRecord::Base is_attachment :recipes => { :watermarked_thumb => [{ :watermark => 'Milton' }, { :thumbnail => { :size => '100x100', :crop => true } }] } endThis recipe would create a watermarked version of the originally uploaded file, then create a 100x100, cropped thumbnail of the watermarked version.
When processors would create the same derivative they use the already created derivative and do not recreate it, so if you had:
class Image < ActiveRecord::Base is_attachment :processors => [ :thumbnail, :watermark ], :recipes => { :thumb => [{ :watermark => 'Milton' }, { :thumbnail => { :size => '100x100', :crop => true } }], :small => [{ :watermark => 'Milton' }, { :thumbnail => { :size => '250x' } }] } endThe watermarking would only be done once and both thumbnails would be created from the same watermarked version of the original image.
Note: There is no
:watermarkprocessor, just an example of how processors can be combined.Post-processing
Post-processing allows you to create derivatives by running processors on demand instead of when the file is uploaded. This is particularly useful for prototyping or early-on in development when processing options are changing rapidly and you want to play with results immediately.
You can pass
:postprocessing => truetois_attachmentin order to turn on post-processing of files. This is recommended only for prototyping as it works by checking the existence of the requested derivative every time the derivative is requested to determine if it should be processed or not. With disk storage in development this can be quite fast, but when using S3 or in production mode it is definitely not recommended.Post-processing allows you to pass recipes to
path, i.e.:@image.path(:thumbnail => { :size => '100x100', :crop => true })If the particular derivative (size of 100x100 and cropped) doesn’t exist it will be created.
Note: Without post-processing turned on the call to
pathabove would still return the same path, it just wouldn’t create the underlying file.Amazon S3
Milton comes with support for Amazon S3. When using S3 uploads and derivatives are stored locally in temp files then sent to S3. To use S3 you need to pass a few options to
is_attachment:class Asset < ActiveRecord::Base is_attachment :storage => :s3, :storage_options => { :api_access_key => '123', :secret_access_key => '456', :bucket => 'assets' } endWhere
:api_access_keyand:secret_access_keyare your API access key and secret access key from your Amazon AWS account and:bucketis the S3 bucket you would like your files to be stored in.When using S3 files are stored in folders according to the associated model’s ID. So in the above example the URL to a stored file might be:
@image.path => http://assets.amazonaws.com/1/milton.jpgStorage Options
By default Milton uses your local disk for storing files. Additional storage methods can be used by passing the
:storageoption tois_attachment(as in the S3 example above). Milton comes included with:s3and:diskstorage.Disk Storage Options
When using disk storage (default) the following options can be passed in
:storage_options:
:root(default<Rails.root>/public/<table name>) – is the root path to where files are/will be stored on your file system. The partitioned portion of the path is then added onto this root to generate the full path. You can do some useful stuff with this like pulling your assets out of /public if you want them to be non-web-accessible.:chmod(default0755) – is the mode to set on on created folders and uploaded files.
Note: If you’re using Capistrano for deployment with disk storage remember to put your :root path in shared and link it up on deploy so you don’t lose your uploads between deployments!
General Options
:separator(default'.') – is the character used to separate the options from the filename in cached derivative files (i.e. resized images). It will be stripped from the filename of any incoming file.:replacement(default'-') – is the character used to replace:separatorafter stripping it from the incoming filename.:tempfile_path(default<Rails.root>/tmp/milton) – is the path used for Milton’s temporary storage.
Installation
Gem
$ gem install citrusbyte-milton --source=http://gems.github.com
Ruby on Rails gem plugin:
Add to your environment.rb:
config.gem "citrusbyte-milton", :lib => 'milton', :source => 'http://gems.github.com'
Then run:
$ rake gems:install
Ruby on Rails plugin
script/plugin install git://github.com/citrusbyte/milton.git
You will also need to install ImageMagick to use image resizing.
Dependencies
- Ruby on Rails w/ ActiveRecord
- A filesystem (hopefully this one is covered…)
- ImageMagick (only if using :thumbnail processor)
- mimetype-fu (not required, but if available will be used to recognized mime types of incoming files)
- rightaws (if using S3 storage option)
Migrating
From 0.2.4
Milton is initialized with only is_attachment as of 0.3.0, so is_resizeable, is_uploadable, etc… are no longer used.
:postprocessing is off by default and should only be used in development now. Recipes should be used instead of post-processing – see Processors above.
Extended Usage Examples
Basic User Avatar
class User < ActiveRecord::Base
has_one :avatar, :dependent => :destroy
end
class Avatar < ActiveRecord::Base
is_attachment :recipes => { :small => [{ :thumbnail => { :size => '100x100', :crop => true } }] }
belongs_to :user
end
Allow user to upload an avatar when creating
class UsersController < ActiveRecord::Base
def create
@user = User.new params[:user]
@user.avatar = Avatar.new(params[:avatar]) if params[:avatar] && params[:avatar][:file]
if @user.save
...
else
...
end
...
end
end
Allow user to upload a new avatar, note that we don’t care about updating files in this has_one case, we’re just gonna set a new relationship (which will destroy the existing one)
class AvatarsController < ActiveRecord::Base
def create
@user = User.find params[:user_id]
# setting a has_one on a saved object saves the new related object
if @user.avatar = Avatar.new(params[:avatar])
...
else
...
end
...
end
end
User’s profile snippet (in Haml)
#profile
= image_tag(@user.avatar.public_path(:small))
= @user.name
Contributors
- Upload and file handling based on AttachmentPow by Ari Lerner (auser)
- Cropping and thumbnailing calculations based on code by Nicolás Sanguinetti (foca)
- Damian Janowski (djanowski)
- Michel Martens (soveran)
License
Copyright (c) 2009 Ben Alavi for Citrusbyte
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Monk
The glue framework for web development.
git clone git://github.com/monkrb/monk.git
GET http://github.com/monkrb/monk
Monk
The glue framework for web development.
Description
Monk is a glue framework for web development. It means that instead of installing all the tools you need for your projects, you can rely on a git repository and a list of dependencies, and Monk takes care of the rest. By default, it ships with a Sinatra application that includes Contest, Stories, Webrat, Ohm and some other niceties, along with a structure and helpful documentation to get your hands wet in no time.
But Monk also respects your tastes, and you are invited to create your own versions of the skeleton app and your own list of dependencies. You can add many different templates (different git repositories) and Monk will help you manage them all.
Usage
Install the monk gem and create your project:
$ sudo gem install monk $ monk init myapp $ cd myapp $ rakeIf the tests pass, it means that you can start hacking right away. If they don’t, just follow the instructions. As the default skeleton is very opinionated, you will probably need to install and run Redis, a key-value database.
Check the
dependenciesfile in the root of your project to see what else is there. Monk works with git almost exclusively, but your project can also depend on gems. Check the dependencies file to see what options you have, and run thedepcommand line tool to verify it.Installation
$ sudo gem install monkLicense
Copyright (c) 2009 Michel Martens and Damian Janowski
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Ohm ॐ
Object-hash mapping library for Redis.
git clone git://github.com/soveran/ohm.git
GET http://github.com/soveran/ohm
Ohm ॐ
Object-hash mapping library for Redis.
Description
Ohm is a library for storing objects in Redis, a persistent key-value database. It includes an extensible list of validations and has very good performance.
Community
Join the mailing list: http://groups.google.com/group/ohm-ruby
Meet us on IRC: #ohm on freenode.net
Getting started
Install Redis. On most platforms it’s as easy as grabbing the sources, running make and then putting the
redis-serverbinary in the PATH.Once you have it installed, you can execute
redis-serverand it will run onlocalhost:6379by default. Check theredis.conffile that comes with the sources if you want to change some settings.If you don’t have Ohm, try this:
$ sudo gem install ohmOr you can grab the code from http://github.com/soveran/ohm.
Now, in an irb session you can test the Redis adapter directly:
>> require "ohm" => true >> Ohm.connect => [] >> Ohm.redis.set "Foo", "Bar" => "OK" >> Ohm.redis.get "Foo" => "Bar"Models
Ohm’s purpose in life is to map objects to a key value datastore. It doesn’t need migrations or external schema definitions. Take a look at the example below:
Example
class Event < Ohm::Model attribute :name set :participants list :comments counter :votes index :name def validate assert_present :name end endAll models have the
idattribute built in, you don’t need to declare it.This is how you interact with ids:
event = Event.create :name => "Ohm Worldwide Conference 2031" event.id # => 1 # Find an event by id event == Event[1] # => true # Trying to find a non existent event Event[2] # => nilThis example shows some basic features, like attribute declarations and validations. Keep reading to find out what you can do with models.
Attribute types
Ohm::Model provides four attribute types:
attribute,set,listandcounter.attribute
An
attributeis just any value that can be stored as a string. In the example above, we used this field to store the Event’sname. You can use it to store numbers, but be aware that Redis will return a string when you retrieve the value.set
A
setin Redis is an unordered list, with an external behavior similar to that of Ruby arrays, but optimized for faster membership lookups. It’s used internally by Ohm to keep track of the instances of each model and for generating and maintaining indexes.list
A
listis like an array in Ruby. It’s perfectly suited for queues and for keeping elements in order.counter
A
counteris like a regular attribute, but the direct manipulation of the value is not allowed. You can retrieve, increase or decrease the value, but you can not assign it. In the example above, we used a counter attribute for tracking votes. As the incr and decr operations are atomic, you can rest assured a vote won’t be counted twice.Persistence strategy
The attributes declared with
attributeare only persisted after callingsave. If the object is in an invalid state, no value is sent to Redis (see the section on Validations below).Operations on attributes of type
list,setandcounterare possible only after the object is created (when it has an assignedid). Any operation on these kinds of attributes is performed immediately, without running the object validations. This design yields better performance than running the validations on each operation or buffering the operations and waiting for a call tosave.For most use cases, this pattern doesn’t represent a problem. If you need to check for validity before operating on lists, sets or counters, you can use this pattern:
if event.valid? event.comments << "Great event!" endIf you are saving the object, this will suffice:
if event.save event.comments << "Wonderful event!" endAssociations
Ohm lets you use collections (lists and sets) to represent associations. For this, you only need to provide a second parameter when declaring a list or a set:
set :attendees, PersonAfter this, every time you refer to
event.attendeesyou will be talking about instances of the modelPerson. If you want to get the raw values of the set, you can useevent.attendees.raw.The
attendeescollection also exposes two sorting methods:sortreturns the elements ordered byid, andsort_byreceives a parameter with an attribute name, which will determine the sorting order. Both methods receive an options hash which is explained in the documentation for {Ohm::Attributes::Collection#sort}.Adding instances of
Personto the attendees hash is done with theaddmethod:@event.attendees.add(Person.create(name: "Albert")) # And now... @event.attendees.each do |person| # ...do what you want with this person. endJust to clarify: when you add items to a set with
<<, Ohm inserts whatever you send without checking it. When you useadd, it assumes it’s an instance of someOhm::Modeland stores its id.Indexes
An index is a set that’s handled automatically by Ohm. For any index declared, Ohm maintains different sets of objects ids for quick lookups.
For example, in the example above, the index on the name attribute will allow for searches like Event.find(name: “some value”).
Note that the
assert_uniquevalidation and the methodsfindandexceptneed a corresponding index in order to work.Finding
You can find a collection of records with the
findmethod:# This returns a collection of users with the username "Albert" User.find(username: "Albert")Filtering results
# Find all users from Argentina User.find(country: "Argentina") # Find all activated users from Argentina User.find(country: "Argentina", status: "activated") # Find all users from Argentina, except those with a suspended account. User.find(country: "Argentina").except(status: "suspended")Note that calling these methods results in new sets being created on the fly. This is important so that you can perform further operations before reading the items to the client.
For more information, see SINTERSTORE and SDIFFSTORE.
Validations
Before every save, the
validatemethod is called by Ohm. In the method definition you can use assertions that will determine if the attributes are valid. Nesting assertions is a good practice, and you are also encouraged to create your own assertions. You can trigger validations at any point by callingvalid?on a model instance.Assertions
Ohm ships with some basic assertions. Check Ohm::Validations to see the method definitions.
assert
The
assertmethod is used by all the other assertions. It pushes the second parameter to the list of errors if the first parameter evaluates to false.def assert(value, error) value or errors.push(error) && false endassertpresent
Checks that the given field is not nil or empty. The error code for this assertion is :notpresent.
assert_present :nameassertformat
Checks that the given field matches the provided format. The error code for this assertion is :format.
assert_format :username, /^\w+$/assertnumeric
Checks that the given field holds a number as a Fixnum or as a string representation. The error code for this assertion is :notnumeric.
assert_numeric :votesassertunique
Validates that the attribute or array of attributes are unique. For this, an index of the same kind must exist. The error code is :notunique.
assert_unique :emailErrors
When an assertion fails, the error report is added to the errors array. Each error report contains two elements: the field where the assertion was issued and the error code.
Validation example
Given the following example:
def validate assert_present :foo assert_numeric :bar assert_format :baz, /^\d{2}$/ assert_unique :qux endIf all the assertions fail, the following errors will be present:
obj.errors # => [[:foo, :not_present], [:bar, :not_numeric], [:baz, :format], [:qux, :not_unique]]Presenting errors
Unlike other ORMs, that define the full error messages in the model itself, Ohm encourages you to define the error messages outside. If you are using Ohm in the context of a web framework, the views are the proper place to write the error messages.
Ohm provides a presenter that helps you in this quest. The basic usage is as follows:
error_messages = @model.errors.present do |e| e.on [:name, :not_present], "Name must be present" e.on [:account, :not_present], "You must supply an account" end error_messages # => ["Name must be present", "You must supply an account"]Having the error message definitions in the views means you can use any sort of helpers. You can also use blocks instead of strings for the values. The result of the block is used as the error message:
error_messages = @model.errors.present do |e| e.on [:email, :not_unique] do "The email #{@model.email} is already registered." end end error_messages # => ["The email foo@example.com is already registered."]
OpenFriends
OpenFriends is about more than just your contacts: it’s about a distributed social layer for the web. Add your friends once, and take them with you, everywhere you go.
git clone git://github.com/citrusbyte/openfriends.git
GET http://github.com/citrusbyte/openfriends
OpenFriends
OpenFriends is about more than just your contacts: it’s about a distributed social layer for the web. Add your friends once, and take them with you, everywhere you go.
About OpenFriends
When you join a social network community site, you have to start adding your list of friends one by one. Either that, or you have to grant them access to your email account. In the end, it is either a real pain or a dangerous privacy violation. Besides, doing the same thing over and over for each new social website has turned the experience into a constant hassle.
OpenFriends solves this problem by providing a way for you to share your list of friends that respects your privacy.
Some solutions
Right now there is one way of easing the pain: microformats. You can have an hCard in some website, along with a list of contacts in XFN format, and point to that URL when signing up to that new social network website. There are many sites that already provide hCards and XFN lists for you, like Twitter and Flickr, and there are also some sites that are ready to consume that information. What is the problem then?
XFN and hCard limitations
Even though both XFN and hCard are standardized, each provider exposes the set of contact data of its preference. Besides, as the profile and the list of contacts are publicly accessible via web, there are a lot of privacy issues at stake. In the end, the XFN list shows only a list of related usernames within that website, and it is usually not enough information for recreating your network of friends somewhere else.
More information
Please refer to the openfriends website for more information about the initiative and what are the ideas behind the implementation.
License
Copyright (c) 2009 Citrusbyte
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Quiki
Quiki is a simple wiki aimed at developers.
git clone git://github.com/citrusbyte/quiki.git
GET http://github.com/citrusbyte/quiki
Quiki
Quiki is a simple wiki aimed at developers.
NAQ (Never Asked Questions)
Q. Why is a tiny app like Quiki written in big bulky Rails and not in something quick like Sinatra/Merb/Raw Machine Code?
A. Because Rails is so easy to extend and add to and can take advantage of all the wonderful plugins! (Quiki is built upon the backs of many previous laborers).
Installation
Quiki is a Ruby on Rails application, so in order to use it just clone this repository and start playing.
git clone git://github.com/citrusbyte/quiki.gitDependencies
It depends on Ultraviolet for code syntax highlighting:
$ gem install ultravioletIf it complains about needed Oniguruma, check http://snippets.aktagon.com/snippets/61-Installing-Ultraviolet-and-Oniguruma
It also depends on Graphviz for diagram generation
http://graphviz.org/License
Copyright (c) 2009 Ben Alavi for Citrusbyte
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Relay
Relay commands over SSH.
git clone git://github.com/soveran/relay.git
GET http://github.com/soveran/relay
Relay
Relay commands over SSH.
Description
Relay is a simple library that allows you to send commands over SSH. It uses your own SSH, not a Ruby version, so you can profit from your settings and public/private keys.
Usage
To send a command to a server called
myserver:$ relay execute "ls -al" myserver $ relay execute "cd foo; ls" myserverIf you want to send more commands, you can write a shell script:
$ cat recipe.sh cd foo ls mkdir -p bar/baz $ relay recipe.sh myserverIt will execute those commands on
myserverand show the output.This last form accepts one file as the recipe and one or many servers:
$ relay recipe.sh server1 server2 server3You can also add your public key to a remote server’s authorized keys file:
$ relay identify myserverRelay exposes the
executemethod, which returns the output of the command:>> require "relay" >> Relay.execute "echo foo", "myserver" => ["foo\n"]Installation
$ sudo gem install relayLicense
Copyright (c) 2009 Michel Martens and Damian Janowski
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Schemer
On-the-fly ActiveRecord schema changes for extremely rapid prototyping.
git clone git://github.com/citrusbyte/schemer.git
GET http://github.com/citrusbyte/schemer
Schemer
On-the-fly ActiveRecord schema changes for extremely rapid prototyping.
Description
Loosely define your schema in your ActiveRecord model and have it created and updated for you without worrying about migrations. Useful for when you want to play around with real data handling during prototyping but you really don’t care about keeping the data or how it’s defined.
This isn’t meant to be a replacement for migrations, it is simply a way to get started on a project quickly while you iron out your data definitions.
WARNING: This will create and delete data definitions on the fly with no warning! Only use this with volatile data! Never attach it to an existing model you care about as it will start adding and dropping columns without your consent!
Usage
class User < ActiveRecord::Base schema :username, :password endCreates a
userstable if it doesn’t exist, complete withusernameandpasswordcolumns the first time the User class is loaded.Need another column to play with?
class User < ActiveRecord::Base schema :username, :password, :email endAdds an
Userclass is loaded.Want a relationship? (but fear commitment…)
class Widget < ActiveRecord::Base schema :user_id, :name belongs_to :user endWorks just fine, and you can drop it at any time!
class Widget < ActiveRecord::Base schema :name endRemoves the
user_idcolumn the next time theWidgetclass is loaded.Have a need for a particular type of column?
class Widget < ActiveRecord::Base schema :name, { :size => :integer }, :description endWill create
sizeas an:integercolumn so you can take advantage of Rails’ casting.Feeling more confident and ready to make that big leap to migrations? Just run:
rake schemer:migrationTo get:
Migration from schema declarations in User, Widget create_table :users do |t| t.string :username t.string :password t.string :email end create_table :widgets do |t| t.string :name t.integer :size t.string :description endThen you can paste the output into a migration file, setup your columns (don’t forget your indexes!) and get rid of your
schemacalls in the listed classes and you’re on your way to the big leagues!NOTE: All columns are created as string columns unless type is given.
Installation
$ sudo gem install citrusbyte-schemer --source=http://gems.github.comLicense
Copyright (c) 2009 Ben Alavi for Citrusbyte
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Spawn
A ridiculously simple fixtures replacement for your web framework of choice.
git clone git://github.com/soveran/spawn.git
GET http://github.com/soveran/spawn
Spawn
A ridiculously simple fixtures replacement for your web framework of choice.
Description
Spawn is a very small library that can effectively replace fixtures or any other huge library for the same task.
Usage
In the examples below we are using Faker to generate random data, but you can use any method.
With ActiveRecord:
class User < ActiveRecord::Base spawner do |user| user.name = Faker::Name.name user.email = Faker::Internet.email end endWith Sequel:
class User < Sequel::Model extend Spawn spawner do |user| user.name = Faker::Name.name user.email = Faker::Internet.email end endWith Ohm:
class User < Ohm::Model extend Spawn attribute :name attribute :email spawner do |user| user.name = Faker::Name.name user.email = Faker::Internet.email end endIf you don’t want to pollute your class definition, you can of course use it from outside:
User.spawner do |user| user.name = Faker::Name.name user.email = Faker::Internet.email endThen, in your test or in any other place:
@user = User.spawnOr, if you need something special:
@user = User.spawn :name => "Michel Martens"This sends to User.new all the attributes defined in the spawner block, along with the hash of attributes passed when spawning.
If you want a reference to the model before the validity is checked, you can pass a block:
@user = User.spawn { |u| u.name = "Michel Martens" }Conditional evaluation
Spawn will execute the right hand side of your assignment even if you provide a value for some particular key. Consider the following example:
User.spawner do |user| user.profile = Profile.spawn # Profile.spawn is executed even if you provide a value for :profile. end User.spawn(:profile => Profile.first)Here, you will be creating an extra instance of
Profile, because when the block is evaluated it callsProfile.spawn. If the right hand side of your assignment is costly or has side effects, you may want to avoid this behavior by using||=:User.spawner do |user| user.profile ||= Profile.spawn endThen, if you pass a
:profile:User.spawn(:profile => Profile.first)You can verify that
Profile.spawnis never called. Although this may sound evident, it can bite you if you rely on the RHS not executing (e.g. if you’re using Spawn to populate fake data into a database and you want to control how many instances you create).Installation
$ sudo gem install spawnThanks
Thanks to Foca for his suggestions and Pedro for the original gemspec.
License
Copyright (c) 2009 Michel Martens and Damian Janowski
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
StoreObserver
Automatically expire cached fragments in Ruby on Rails based on the state of the models involved.
git clone git://github.com/citrusbyte/store_observer.git
GET http://github.com/citrusbyte/store_observer
StoreObserver
Automatically expire cached fragments in Ruby on Rails based on the state of the models involved.
Usage
In any view:
<% store 'fragment_name', :observe => [:users, :coments] do %> cached fragment which does something intensive <% end %>This fragment will be cached until a save or destroy occur in the User and Comment models.
Installation
You can install it as a Rails plugin or as a gem:
$ gem sources -a http://gems.github.com (you only have to do this once) $ sudo gem install citrusbyte-store_observerLicense
Copyright (c) 2008 Michel Martens and Ben Alavi for Citrusbyte
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
git clone git://github.com/citrusbyte/stories.git
GET http://github.com/citrusbyte/stories
Stories
Stories and User Acceptance Tests for the minimalist test framework Contest.
Description
Write user stories and user acceptace tests using Contest, the tiny add on to Test::Unit that provides nested contexts and declarative tests.
Usage
Declare your stories as follows:
require 'stories' class UserStoryTest < Test::Unit::TestCase story "As a user I want to create stories so I can test if they pass" do setup do @user = "valid user" end scenario "A valid user" do assert_equal "valid user", @user end end endIf you are using Rails, you can use stories for your integration tests with Webrat:
class UserStoriesTest < ActionController::IntegrationTest story "As a user I want to log in so that I can access restricted features" do setup do @user = User.spawn :name => "Albert", :email => "albert@example.com" end scenario "Using good information" do visit "/" click_link "Log in" fill_in "Email", :with => user.email fill_in "Password", :with => user.password click_button "Sign in" assert_contain "Logout" assert_contain "Welcome Albert" end end endThis will produce the following output:
- As a user I want to log in so that I can access restricted features Using good information Go to “/” Click “Log in” Fill in “Email” with “albert@example.com” Fill in “Password” with “secret” Click “Sign in” I should see “Logout” I should see “Welcome Albert”Custom reports
Stories ships with reports for many Webrat helpers, but you can write your own version or add reports for other helpers.
For instance, let’s say you add this assertion:
module Test::Unit::Assertions def assert_current_page(path) assert_equal path, current_url.sub(%r{^http://www\.example\.com}, '') end endWhen you run it, no output will be generated because Stories doesn’t know what to say about this assertion. Adding a custom report is easy:
module Stories::Webrat report_for :assert_current_page do |page| "I should be on #{quote(page)}" end endAll this should reside in your
stories_helper.rb.Step wrappers
There are two other methods that are useful for reporting steps or assertions:
silentandreport.The
silenthelper gives you the ability to suspend the steps and assertions’ normal output:... silent do assert_contain "Some obscure hash" end ...In a similar fashion,
reportlets you replace the normal output with your own message:... report "I verify that the hash generated correctly" do assert_contain "The same obscure hash" end ...Running stories
You can run it normally, it’s Test::Unit after all. If you want to run a particular test, say “yet more tests”, try this:
$ ruby my_test.rb -n test_yet_more_testsOr with a regular expression:
$ ruby my_test.rb -n /yet_more_tests/Pending stories
Since Stories aims to improve your project’s documentation, you can have pending stories:
class UserStoryTest < Test::Unit::TestCase story "As a user I want to create stories so I can test if they pass" endThis is useful if you want to write all your stories upfront, even before you write the acceptance tests.
Awesome output
You can get a nice output with your user stories with the
storiesrunner:$ ruby my_test.rb --runner=storiesNow, if you want to impress everyone around you, try this:
$ ruby my_test.rb --runner=stories-pdfYou will get a nicely formatted PDF with your user stories. It uses Prawn, so you will need to install it first.
If you’re using Rails, you can run the whole build with the following Rake tasks:
$ rake stories $ rake stories:pdfInstallation
$ sudo gem install storiesIf you want to use it with Rails, add this to config/environment.rb:
config.gem "stories"Then you can vendor the gem:
rake gems:install rake gems:unpackLicense
Copyright (c) 2009 Damian Janowski and Michel Martens for Citrusbyte
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.