Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 385 lines (264 sloc) 13.918 kb
362fba6b »
2009-07-22 updated readme
1 # ActiveHash
2
3 ActiveHash is a simple base class that allows you to use a ruby hash as a readonly datasource for an ActiveRecord-like model.
4
5 ActiveHash assumes that every hash has an :id key, which is what you would probably store in a database. This allows you to seemlessly upgrade from ActiveHash objects to full ActiveRecord objects without having to change any code in your app, or any foreign keys in your database.
6
f255c083 »
2011-09-01 update documentation
7 It also allows you to use #has_many and #belongs_to (via belongs_to_active_hash) in your AR objects.
362fba6b »
2009-07-22 updated readme
8
9 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.
10
11 ActiveHash also ships with:
12
de8e6787 »
2009-10-22 updated docs
13 * ActiveFile: a base class that you can use to create file data sources
362fba6b »
2009-07-22 updated readme
14 * ActiveYaml: a base class that will turn YAML into a hash and load the data into an ActiveHash object
15
16 ## Installation
17
de8e6787 »
2009-10-22 updated docs
18 Make sure gemcutter.org is one of your gem sources, then run:
19
20 sudo gem install active_hash
362fba6b »
2009-07-22 updated readme
21
22 ## Usage
23
24 To use ActiveHash, you need to:
25
26 * Inherit from ActiveHash::Base
27 * Define your data
de8e6787 »
2009-10-22 updated docs
28 * Define your fields and/or default values
362fba6b »
2009-07-22 updated readme
29
30 A quick example would be:
31
32 class Country < ActiveHash::Base
33 self.data = [
34 {:id => 1, :name => "US"},
35 {:id => 2, :name => "Canada"}
36 ]
37 end
38
caf926e1 »
2009-07-23 updated readme
39 country = Country.new(:name => "Mexico")
40 country.name # => "Mexico"
41 country.name? # => true
42
63f6c4ee »
2010-04-25 Prepare for release
43 You can also use _create_:
44
45 class Country < ActiveHash::Base
46 create :id => 1, :name => "US"
47 create :id => 2, :name => "Canada"
48 end
49
50 If you are Pat Nakajima, you probably prefer _add_:
51
52 class Country < ActiveHash::Base
53 add :id => 1, :name => "US"
54 add :id => 2, :name => "Canada"
55 end
56
eba92e17 »
2012-02-07 added warning about initializers to README.
57 ## WARNING
58
59 If you add data to an ActiveHash file during an initializer, it will not be reloaded in development in Rails.
60
8e027f5f »
2009-07-23 added auto-defining fields to ActiveHash
61 ## Auto-Defined fields
362fba6b »
2009-07-22 updated readme
62
8e027f5f »
2009-07-23 added auto-defining fields to ActiveHash
63 ActiveHash will auto-define all fields for you when you load the hash. For example, if you have the following class:
64
65 class CustomField < ActiveYaml::Base
66 self.data = [
67 {:custom_field_1 => "foo"},
68 {:custom_field_2 => "foo"},
69 {:custom_field_3 => "foo"}
70 ]
71 end
72
73 Once you call CustomField.all it will define methods for :custom_field_1, :custom_field_2 etc...
74
75 If you need the fields at load time, as opposed to after .all is called, you can also define them manually, like so:
76
77 class CustomField < ActiveYaml::Base
78 fields :custom_field_1, :custom_field_2, :custom_field_3
79 end
80
81 NOTE: auto-defined fields will _not_ override fields you've defined, either on the class or on the instance.
82
83 ## Defining Fields with default values
84
85 If some of your hash values contain nil, and you want to provide a default, you can specify defaults with the :field method:
362fba6b »
2009-07-22 updated readme
86
87 class Country < ActiveHash::Base
88 field :is_axis_of_evil, :default => false
89 end
90
91 ## Defining Data
92
93 You can define data inside your class or outside. For example, you might have a class like this:
94
95 # app/models/country.rb
96 class Country < ActiveHash::Base
97 end
98
99 # config/initializers/data.rb
100 Country.data = [
101 {:id => 1, :name => "US"},
102 {:id => 2, :name => "Canada"}
103 ]
104
105 If you prefer to store your data in YAML, see below.
106
107 ## Class Methods
108
109 ActiveHash gives you ActiveRecord-esque methods like:
110
111 Country.all # => returns all Country objects
112 Country.count # => returns the length of the .data array
113 Country.first # => returns the first country object
114 Country.last # => returns the last country object
115 Country.find 1 # => returns the first country object with that id
116 Country.find [1,2] # => returns all Country objects with ids in the array
117 Country.find :all # => same as .all
118 Country.find :all, args # => the second argument is totally ignored, but allows it to play nicely with AR
119 Country.find_by_id 1 # => find the first object that matches the id
120
121 It also gives you a few dynamic finder methods. For example, if you defined :name as a field, you'd get:
122
10d07289 »
2009-10-13 Updated Readme
123 Country.find_by_name "foo" # => returns the first object matching that name
124 Country.find_all_by_name "foo" # => returns an array of the objects with matching names
125 Country.find_by_id_and_name 1, "Germany" # => returns the first object matching that id and name
126 Country.find_all_by_id_and_name 1, "Germany" # => returns an array of objects matching that name and id
362fba6b »
2009-07-22 updated readme
127
128 ## Instance Methods
129
130 ActiveHash objects implement enough of the ActiveRecord api to satisfy most common needs. For example:
131
de8e6787 »
2009-10-22 updated docs
132 Country#id # => returns the id or nil
133 Country#id= # => sets the id attribute
362fba6b »
2009-07-22 updated readme
134 Country#quoted_id # => returns the numeric id
135 Country#to_param # => returns the id as a string
10d07289 »
2009-10-13 Updated Readme
136 Country#new_record? # => returns true if is not part of Country.all, false otherwise
362fba6b »
2009-07-22 updated readme
137 Country#readonly? # => true
138 Country#hash # => the hash of the id (or the hash of nil)
139 Country#eql? # => compares type and id, returns false if id is nil
140
141 ActiveHash also gives you methods related to the fields you defined. For example, if you defined :name as a field, you'd get:
142
143 Country#name # => returns the passed in name
144 Country#name? # => returns true if the name is not blank
de8e6787 »
2009-10-22 updated docs
145 Country#name= # => sets the name
362fba6b »
2009-07-22 updated readme
146
10d07289 »
2009-10-13 Updated Readme
147 ## Saving in-memory records
362fba6b »
2009-07-22 updated readme
148
10d07289 »
2009-10-13 Updated Readme
149 The ActiveHash::Base.all method functions like an in-memory data store. You can save your records to the the .all array by using standard ActiveRecord create and save methods:
150
151 Country.all # => []
152 Country.create
153 Country.all # [ <Country :id => 1> ]
154 country = Country.new
155 country.new_record? # => true
156 country.save
157 country.new_record? # => false
158 Country.all # [ <Country :id => 1>, <Country :id => 2> ]
159
160 Notice that when adding records to the collection, it will auto-increment the id for you by default. If you use string ids, it will not auto-increment the id. Available methods are:
161
162 Country.insert( record )
163 Country#save
164 Country#save!
165 Country.create
166 Country.create!
167
168 As such, ActiveHash::Base and its descendants should work with Fixjour or FactoryGirl, so you can treat ActiveHash records the same way you would any other ActiveRecord model in tests.
169
41afaeb9 »
2009-10-13 Added delete_all
170 To clear all records from the in-memory array, call delete_all:
171
172 Country.delete_all # => does not affect the yaml files in any way - just clears the in-memory array which can be useful for testing
173
153c58ec »
2011-08-31 fix test suite for Rails 3.1, add belongs_to_active_hash method, prep…
174 ## Referencing ActiveHash objects from ActiveRecord Associations
10d07289 »
2009-10-13 Updated Readme
175
153c58ec »
2011-08-31 fix test suite for Rails 3.1, add belongs_to_active_hash method, prep…
176 One common use case for ActiveHash is to have top-level objects in memory that ActiveRecord objects belong to.
177
178 In versions of ActiveRecord previous to 3.1, you should be able to do the following:
362fba6b »
2009-07-22 updated readme
179
180 class Country < ActiveHash::Base
181 end
182
183 class Person < ActiveRecord::Base
184 belongs_to :country
185 end
186
153c58ec »
2011-08-31 fix test suite for Rails 3.1, add belongs_to_active_hash method, prep…
187 However, as of ActiveRecord 3.1 support for ActiveRecord's `belong_to` is broken. Instead, you must use the `belongs_to_active_hash` method:
188
189 class Country < ActiveHash::Base
190 end
191
192 class Person < ActiveRecord::Base
193 extend ActiveHash::Associations::ActiveRecordExtensions
194 belongs_to_active_hash :country
195 end
196
197 With ActiveRecord versions < 3.1, ActiveHash will also work as a polymorphic parent:
10d07289 »
2009-10-13 Updated Readme
198
199 class Country < ActiveHash::Base
200 end
201
202 class Person < ActiveRecord::Base
203 belongs_to :location, :polymorphic => true
204 end
205
206 person = Person.new
207 person.location = Country.first
208 person.save
209 person.location # => Country.first
210
153c58ec »
2011-08-31 fix test suite for Rails 3.1, add belongs_to_active_hash method, prep…
211 However, as of ActiveRecord 3.1 this will not work. If you need support for that, please open an issue.
212
362fba6b »
2009-07-22 updated readme
213 You can also use standard rails view helpers, like #collection_select:
214
215 <%= collection_select :person, :country_id, Country.all, :id, :name %>
216
153c58ec »
2011-08-31 fix test suite for Rails 3.1, add belongs_to_active_hash method, prep…
217 ## Referencing ActiveRecord objects from ActiveHash
218
eee90fbe » Jeff Dean
2010-03-01 reverted the extend-related changes that broke rails integration
219 If you include the ActiveHash::Associations module, you can also create associations from your ActiveHash classes, like so:
10d07289 »
2009-10-13 Updated Readme
220
221 class Country < ActiveHash::Base
eee90fbe » Jeff Dean
2010-03-01 reverted the extend-related changes that broke rails integration
222 include ActiveHash::Associations
10d07289 »
2009-10-13 Updated Readme
223 has_many :people
224 end
225
226 class Person < ActiveHash::Base
eee90fbe » Jeff Dean
2010-03-01 reverted the extend-related changes that broke rails integration
227 include ActiveHash::Associations
10d07289 »
2009-10-13 Updated Readme
228 belongs_to :country
229 has_many :pets
230 end
231
232 class Pet < ActiveRecord::Base
233 end
234
2263b4fc »
2009-10-13 Added setter when defining belongs_to associations
235 Once you define a belongs to, you also get the setter method:
236
237 class City < ActiveHash::Base
eee90fbe » Jeff Dean
2010-03-01 reverted the extend-related changes that broke rails integration
238 include ActiveHash::Associations
2263b4fc »
2009-10-13 Added setter when defining belongs_to associations
239 belongs_to :state
240 end
241
242 city = City.new
243 city.state = State.first
244 city.state_id # is State.first.id
245
10d07289 »
2009-10-13 Updated Readme
246 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.
247
69daebac »
2009-10-13 Credit baldwindavid for his contributions
248 Thanks to baldwindavid for the ideas and code on that one.
249
362fba6b »
2009-07-22 updated readme
250 ## ActiveYaml
251
252 If you want to store your data in YAML files, just inherit from ActiveYaml and specify your path information:
253
254 class Country < ActiveYaml::Base
255 end
256
257 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:
258
259 class Country < ActiveYaml::Base
260 set_root_path "/u/data"
261 set_filename "sample"
262 end
263
264 The above example will look for the file "/u/data/sample.yml".
265
270b7509 »
2009-10-13 Add support for loading yaml files that are organized as hashes
266 Since ActiveYaml just creates a hash from the YAML file, you will have all fields specified in YAML auto-defined for you once you call all. You can format your YAML as an array, or as a hash:
267
268 # array style
269 - id: 1
270 name: US
271 - id: 2
272 name: Canada
273 - id: 3
274 name: Mexico
275
276 # hash style
277 us:
278 id: 1
279 name: US
280 canada:
281 id: 2
282 name: Canada
283 mexico:
284 id: 3
285 name: Mexico
8e027f5f »
2009-07-23 added auto-defining fields to ActiveHash
286
362fba6b »
2009-07-22 updated readme
287 ## ActiveFile
288
eee90fbe » Jeff Dean
2010-03-01 reverted the extend-related changes that broke rails integration
289 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:
362fba6b »
2009-07-22 updated readme
290
291 class Country < ActiveFile::Base
292 set_root_path "/u/data"
293 set_filename "sample"
294
295 class << self
296 def extension
297 ".super_secret"
298 end
299
300 def load_file
301 MyAwesomeDecoder.load_file(full_path)
302 end
303 end
304 end
305
8e027f5f »
2009-07-23 added auto-defining fields to ActiveHash
306 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.
307
6f9ae897 » Jeff Dean and Mike Dalessio
2009-07-24 updated readme
308 Setting the default file location in Rails:
309
310 # config/initializers/active_file.rb
172fd593 » mrhaddad
2011-05-20 Fix documentation
311 ActiveFile::Base.set_root_path "config/activefiles"
6f9ae897 » Jeff Dean and Mike Dalessio
2009-07-24 updated readme
312
de8e6787 »
2009-10-22 updated docs
313 In Rails, in development mode, it reloads the entire class, which reloads the file. In production, the data cached in memory.
6f9ae897 » Jeff Dean and Mike Dalessio
2009-07-24 updated readme
314
8e027f5f »
2009-07-23 added auto-defining fields to ActiveHash
315 NOTE: By default, .full_path refers to the current working directory. In a rails app, this will be RAILS_ROOT.
362fba6b »
2009-07-22 updated readme
316
8f63b1e8 » John Pignata
2009-12-20 Add Enum module for using enum accessors with ActiveHash
317 ## Enum
318
319 ActiveHash can expose its data in an Enumeration by setting constants for each record. This allows records to be accessed in code through a constant set in the ActiveHash class.
320
321 The field to be used as the constant is set using _enum_accessor_ which takes the name of a field as an argument.
322
323 class Country < ActiveHash::Base
eee90fbe » Jeff Dean
2010-03-01 reverted the extend-related changes that broke rails integration
324 include ActiveHash::Enum
8f63b1e8 » John Pignata
2009-12-20 Add Enum module for using enum accessors with ActiveHash
325 self.data = [
326 {:id => 1, :name => "US", :capital => "Washington, DC"},
327 {:id => 2, :name => "Canada", :capital => "Ottawa"},
328 {:id => 3, :name => "Mexico", :capital => "Mexico City"}
329 ]
330 enum_accessor :name
331 end
332
333 Records can be accessed by looking up the field constant:
334
335 >> Country::US.capital
336 => "Washington DC"
337 >> Country::MEXICO.id
338 => 3
339 >> Country::CANADA
340 => #<Country:0x10229fb28 @attributes={:name=>"Canada", :id=>2}
341
fd8e107e »
2012-07-18 update README for Enum to be more correct (thanks to mistidoi!)
342 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.
8f63b1e8 » John Pignata
2009-12-20 Add Enum module for using enum accessors with ActiveHash
343
344 The field specified as the _enum_accessor_ must contain unique data values.
345
21485dde »
2011-06-05 make it easier to run specs against multiple ruby/ar versions, add Ad…
346 ## Contributing
347
348 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.
349
350 To run specs, run:
351
352 bundle install
353 bundle exec rspec spec
354
355 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.
356
357 ## Releasing a new version
358
359 To make users' lives easier, please maintain support for:
360
361 * Ruby 1.8.7
362 * Ruby Enterprise 1.8.7
363 * Ruby 1.92
364 * ActiveRecord/ActiveSupport from 2.3.2 through edge
365
d77056bc »
2012-01-19 fixed the readme
366 To that end, run specs against all rubies before committing:
21485dde »
2011-06-05 make it easier to run specs against multiple ruby/ar versions, add Ad…
367
d77056bc »
2012-01-19 fixed the readme
368 rake appraisal:install
369 rake appraisal spec
21485dde »
2011-06-05 make it easier to run specs against multiple ruby/ar versions, add Ad…
370
b7be46fe »
2012-04-05 release new version - 0.9.9
371 Once appraisal passes in all supported rubies, follow these steps to release a new version of active_hash:
21485dde »
2011-06-05 make it easier to run specs against multiple ruby/ar versions, add Ad…
372
373 * update the changelog with a brief summary of the changes that are included in the release
374 * bump the gem version by editing the `version.rb` file
b7be46fe »
2012-04-05 release new version - 0.9.9
375 * if there are new contributors, add them to the list of authors in the gemspec
21485dde »
2011-06-05 make it easier to run specs against multiple ruby/ar versions, add Ad…
376 * run `rake build`
377 * commit those changes
378 * run `rake install` and verify that the gem loads correctly from an irb session
379 * 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
380
381 If you have any questions about how to maintain backwards compatibility, please email me and we can figure it out.
382
8f63b1e8 » John Pignata
2009-12-20 Add Enum module for using enum accessors with ActiveHash
383 ## Copyright
362fba6b »
2009-07-22 updated readme
384
a197ee65 »
2010-11-07 rollout 0.8.6
385 Copyright (c) 2010 Jeff Dean. See LICENSE for details.
Something went wrong with that request. Please try again.