README.md

ActiveHash

ActiveHash is a simple base class that allows you to use a ruby hash as a readonly datasource for an ActiveRecord-like model.

ActiveHash assumes that every hash has an :id key, which is what you would probably store in a database. This allows you to seamlessly upgrade from ActiveHash objects to full ActiveRecord objects without having to change any code in your app, or any foreign keys in your database.

It also allows you to use #has_many and #belongs_to (via belongs_to_active_hash) in your AR objects.

ActiveHash can also be useful to create simple test classes that run without a database - ideal for testing plugins or gems that rely on simple AR behavior, but don't want to deal with databases or migrations for the spec suite.

ActiveHash also ships with:

ActiveFile: a base class that you can use to create file data sources

ActiveYaml: a base class that will turn YAML into a hash and load the data into an ActiveHash object

Installation

Bundler:

gem'active_hash'

Other:

gem install active_hash

Reason for being

We wrote ActiveHash so that we could use simple, in-memory, ActiveRecord-like data structures that play well with Rails forms, like:

Class Methods

ActiveHash gives you ActiveRecord-esque methods like:

Country.all # => returns all Country objectsCountry.count # => returns the length of the .data arrayCountry.first # => returns the first country objectCountry.last # => returns the last country objectCountry.find 1# => returns the first country object with that idCountry.find [1,2] # => returns all Country objects with ids in the arrayCountry.find :all# => same as .allCountry.find :all, args # => the second argument is totally ignored, but allows it to play nicely with ARCountry.find_by_id 1# => find the first object that matches the id

It also gives you a few dynamic finder methods. For example, if you defined :name as a field, you'd get:

Country.find_by_name "foo"# => returns the first object matching that nameCountry.find_all_by_name "foo"# => returns an array of the objects with matching namesCountry.find_by_id_and_name 1, "Germany"# => returns the first object matching that id and nameCountry.find_all_by_id_and_name 1, "Germany"# => returns an array of objects matching that name and id

Instance Methods

ActiveHash objects implement enough of the ActiveRecord api to satisfy most common needs. For example:

NOTE: this needs to be called on a subclass of ActiveRecord::Base. If you extend ActiveRecord::Base, it will not work.
If you want to extend ActiveRecord::Base so all your AR models can belong to ActiveHash::Base objects, you can use the
belongs_to_active_hash method:

NOTE: You cannot use ActiveHash objects as children of ActiveRecord and I don't plan on adding support for that. It doesn't really make any sense, since you'd have to hard-code your database ids in your class or yaml files, which is a dependency inversion.

Thanks to baldwindavid for the ideas and code on that one.

ActiveYaml

If you want to store your data in YAML files, just inherit from ActiveYaml and specify your path information:

classCountry < ActiveYaml::Baseend

By default, this class will look for a yml file named "countries.yml" in the same directory as the file. You can either change the directory it looks in, the filename it looks for, or both:

Using aliases in YAML

Aliases can be used in ActiveYaml using either array or hash style by including ActiveYaml::Aliases.
With that module included, keys beginning with a '/' character can be safely added, and will be ignored, allowing you to add aliases anywhere in your code:

Multiple files per model

This works as it does for ActiveYaml

ActiveFile

If you store encrypted data, or you'd like to store your flat files as CSV or XML or any other format, you can easily include ActiveHash to parse and load your file. Just add a custom ::load_file method, and define the extension you want the file to use:

The two methods you need to implement are load_file, which needs to return an array of hashes, and .extension, which returns the file extension you are using. You have full_path available to you if you wish, or you can provide your own path.

Constants are formed by first stripping all non-word characters and then upcasing the result. This means strings like "Blazing Saddles", "ReBar", "Mike & Ike" and "Ho! Ho! Ho!" become BLAZING_SADDLES, REBAR, MIKE_IKE and HO_HO_HO.

The field specified as the enum_accessor must contain unique data values.

Contributing

If you'd like to become an ActiveHash contributor, the easiest way it to fork this repo, make your changes, run the specs and submit a pull request once they pass.

To run specs, run:

bundle install
bundle exec rspec spec

If your changes seem reasonable and the specs pass I'll give you commit rights to this repo and add you to the list of people who can push the gem.

Releasing a new version

To make users' lives easier, please maintain support for:

Ruby 1.8.7

Ruby Enterprise 1.8.7

Ruby 1.92

ActiveRecord/ActiveSupport from 2.3.2 through edge

To that end, run specs against all rubies before committing:

wwtd

Once appraisal passes in all supported rubies, follow these steps to release a new version of active_hash:

update the changelog with a brief summary of the changes that are included in the release

bump the gem version by editing the version.rb file

if there are new contributors, add them to the list of authors in the gemspec

run rake build

commit those changes

run rake install and verify that the gem loads correctly from an irb session

run rake release, which will rebuild the gem, tag it, push the tags (and your latest commit) to github, then push the gem to rubygems.org

If you have any questions about how to maintain backwards compatibility, please email me and we can figure it out.