honkster-active_hash 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ .idea
7
+ junk.*
data/CHANGELOG ADDED
@@ -0,0 +1,40 @@
1
+ 2009-10-22
2
+ - added setters to ActiveHash::Base for all fields
3
+ - instantiating an ActiveHash object with a hash calls the setter methods on the object
4
+ - boolean default values now work
5
+
6
+ 2009-10-21
7
+ - Removed auto-reloading of files based on mtime - maybe it will come back later
8
+ - Made ActiveFile::Base.all a bit more sane
9
+
10
+ 2009-10-13
11
+ - added ActiveHash::Base.has_many, which works with ActiveRecord or ActiveHash classes (thanks to baldwindavid)
12
+ - added ActiveHash::Base.belongs_to, which works with ActiveRecord or ActiveHash classes (thanks to baldwindavid)
13
+ - added .delete_all method that clears the in-memory array
14
+ - added support for Hash-style yaml (think, Rails fixtures)
15
+ - added setter for parent object on belongs_to ( city = City.new; city.state = State.first; city.state_id == State.first.id )
16
+
17
+ 2009-10-12
18
+ - auto-assign fields after calling data= instead of after calling .all
19
+ - remove require 'rubygems', so folks with non-gem setups can still use AH
20
+ - added more specific activesupport dependency to ensure that metaclass is available
21
+ - AH no longer calls to_i on ids. If you pass in a string as an id, you'll get a string back
22
+ - Fancy finders, such as find_all_by_id_and_name, will compare the to_s values of the fields, so you can pass in strings
23
+ - You can now use ActiveHash models as the parents of polymorphic belongs_to associations
24
+ - save, save!, create and create! now add items to the in-memory collection, and naively adds autoincrementing id
25
+ - new_record? returns false if the record is part of the collection
26
+ - ActiveHash now works with Fixjour!
27
+
28
+ 2009-08-19
29
+ - Added custom finders for multiple fields, such as .find_all_by_name_and_age
30
+
31
+ 2009-07-23
32
+ - Added support for auto-defining methods based on hash keys in ActiveHash::Base
33
+ - Changed the :field and :fields API so that they don't overwrite existing methods (useful when ActiveHash auto-defines methods)
34
+ - Fixed a bug where ActiveFile incorrectly set the root_path to be the path in the gem directory, not the current working directory
35
+
36
+ 2009-07-24
37
+ - ActiveFile no longer reloads files by default
38
+ - Added ActiveFile.reload_active_file= so you can cause ActiveFile to reload
39
+ - Setting data to nil correctly causes .all to return an empty array
40
+ - Added reload(force) method, so that you can force a reload from files in ActiveFile, useful for tests
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Jeff Dean
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,291 @@
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
+
7
+ It also allows you to use #has_many and #belongs_to in your AR objects.
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
+
13
+ * ActiveFile: a base class that you can use to create file data sources
14
+ * ActiveYaml: a base class that will turn YAML into a hash and load the data into an ActiveHash object
15
+
16
+ ## Installation
17
+
18
+ Make sure gemcutter.org is one of your gem sources, then run:
19
+
20
+ sudo gem install active_hash
21
+
22
+ ## Usage
23
+
24
+ To use ActiveHash, you need to:
25
+
26
+ * Inherit from ActiveHash::Base
27
+ * Define your data
28
+ * Define your fields and/or default values
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
+
39
+ country = Country.new(:name => "Mexico")
40
+ country.name # => "Mexico"
41
+ country.name? # => true
42
+
43
+ ## Auto-Defined fields
44
+
45
+ ActiveHash will auto-define all fields for you when you load the hash. For example, if you have the following class:
46
+
47
+ class CustomField < ActiveYaml::Base
48
+ self.data = [
49
+ {:custom_field_1 => "foo"},
50
+ {:custom_field_2 => "foo"},
51
+ {:custom_field_3 => "foo"}
52
+ ]
53
+ end
54
+
55
+ Once you call CustomField.all it will define methods for :custom_field_1, :custom_field_2 etc...
56
+
57
+ If you need the fields at load time, as opposed to after .all is called, you can also define them manually, like so:
58
+
59
+ class CustomField < ActiveYaml::Base
60
+ fields :custom_field_1, :custom_field_2, :custom_field_3
61
+ end
62
+
63
+ NOTE: auto-defined fields will _not_ override fields you've defined, either on the class or on the instance.
64
+
65
+ ## Defining Fields with default values
66
+
67
+ If some of your hash values contain nil, and you want to provide a default, you can specify defaults with the :field method:
68
+
69
+ class Country < ActiveHash::Base
70
+ field :is_axis_of_evil, :default => false
71
+ end
72
+
73
+ ## Defining Data
74
+
75
+ You can define data inside your class or outside. For example, you might have a class like this:
76
+
77
+ # app/models/country.rb
78
+ class Country < ActiveHash::Base
79
+ end
80
+
81
+ # config/initializers/data.rb
82
+ Country.data = [
83
+ {:id => 1, :name => "US"},
84
+ {:id => 2, :name => "Canada"}
85
+ ]
86
+
87
+ If you prefer to store your data in YAML, see below.
88
+
89
+ ## Class Methods
90
+
91
+ ActiveHash gives you ActiveRecord-esque methods like:
92
+
93
+ Country.all # => returns all Country objects
94
+ Country.count # => returns the length of the .data array
95
+ Country.first # => returns the first country object
96
+ Country.last # => returns the last country object
97
+ Country.find 1 # => returns the first country object with that id
98
+ Country.find [1,2] # => returns all Country objects with ids in the array
99
+ Country.find :all # => same as .all
100
+ Country.find :all, args # => the second argument is totally ignored, but allows it to play nicely with AR
101
+ Country.find_by_id 1 # => find the first object that matches the id
102
+
103
+ It also gives you a few dynamic finder methods. For example, if you defined :name as a field, you'd get:
104
+
105
+ Country.find_by_name "foo" # => returns the first object matching that name
106
+ Country.find_all_by_name "foo" # => returns an array of the objects with matching names
107
+ Country.find_by_id_and_name 1, "Germany" # => returns the first object matching that id and name
108
+ Country.find_all_by_id_and_name 1, "Germany" # => returns an array of objects matching that name and id
109
+
110
+ ## Instance Methods
111
+
112
+ ActiveHash objects implement enough of the ActiveRecord api to satisfy most common needs. For example:
113
+
114
+ Country#id # => returns the id or nil
115
+ Country#id= # => sets the id attribute
116
+ Country#quoted_id # => returns the numeric id
117
+ Country#to_param # => returns the id as a string
118
+ Country#new_record? # => returns true if is not part of Country.all, false otherwise
119
+ Country#readonly? # => true
120
+ Country#hash # => the hash of the id (or the hash of nil)
121
+ Country#eql? # => compares type and id, returns false if id is nil
122
+
123
+ ActiveHash also gives you methods related to the fields you defined. For example, if you defined :name as a field, you'd get:
124
+
125
+ Country#name # => returns the passed in name
126
+ Country#name? # => returns true if the name is not blank
127
+ Country#name= # => sets the name
128
+
129
+ ## Saving in-memory records
130
+
131
+ 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:
132
+
133
+ Country.all # => []
134
+ Country.create
135
+ Country.all # [ <Country :id => 1> ]
136
+ country = Country.new
137
+ country.new_record? # => true
138
+ country.save
139
+ country.new_record? # => false
140
+ Country.all # [ <Country :id => 1>, <Country :id => 2> ]
141
+
142
+ 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:
143
+
144
+ Country.insert( record )
145
+ Country#save
146
+ Country#save!
147
+ Country.create
148
+ Country.create!
149
+
150
+ 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.
151
+
152
+ To clear all records from the in-memory array, call delete_all:
153
+
154
+ Country.delete_all # => does not affect the yaml files in any way - just clears the in-memory array which can be useful for testing
155
+
156
+ ## Associations
157
+
158
+ You can create has_many and belongs_to associations to and from ActiveRecord. Out of the box, you can create .belongs_to associations from rails objects, like so:
159
+
160
+ class Country < ActiveHash::Base
161
+ end
162
+
163
+ class Person < ActiveRecord::Base
164
+ belongs_to :country
165
+ end
166
+
167
+ ActiveHash will also work as a polymorphic parent:
168
+
169
+ class Country < ActiveHash::Base
170
+ end
171
+
172
+ class Person < ActiveRecord::Base
173
+ belongs_to :location, :polymorphic => true
174
+ end
175
+
176
+ person = Person.new
177
+ person.location = Country.first
178
+ person.save
179
+ person.location # => Country.first
180
+
181
+ You can also use standard rails view helpers, like #collection_select:
182
+
183
+ <%= collection_select :person, :country_id, Country.all, :id, :name %>
184
+
185
+ If you include the ActiveHash::Associations module, you can also create associations from your ActiveHash classes, like so:
186
+
187
+ class Country < ActiveHash::Base
188
+ include ActiveHash::Associations
189
+ has_many :people
190
+ end
191
+
192
+ class Person < ActiveHash::Base
193
+ include ActiveHash::Associations
194
+ belongs_to :country
195
+ has_many :pets
196
+ end
197
+
198
+ class Pet < ActiveRecord::Base
199
+ end
200
+
201
+ Once you define a belongs to, you also get the setter method:
202
+
203
+ class City < ActiveHash::Base
204
+ include ActiveHash::Associations
205
+ belongs_to :state
206
+ end
207
+
208
+ city = City.new
209
+ city.state = State.first
210
+ city.state_id # is State.first.id
211
+
212
+ 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.
213
+
214
+ Also, the implementation of has_many and belongs_to is very simple - I hope to add better support for it later - it will only work in the trivial cases for now.
215
+
216
+ Thanks to baldwindavid for the ideas and code on that one.
217
+
218
+ ## ActiveYaml
219
+
220
+ If you want to store your data in YAML files, just inherit from ActiveYaml and specify your path information:
221
+
222
+ class Country < ActiveYaml::Base
223
+ end
224
+
225
+ 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:
226
+
227
+ class Country < ActiveYaml::Base
228
+ set_root_path "/u/data"
229
+ set_filename "sample"
230
+ end
231
+
232
+ The above example will look for the file "/u/data/sample.yml".
233
+
234
+ 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:
235
+
236
+ # array style
237
+ - id: 1
238
+ name: US
239
+ - id: 2
240
+ name: Canada
241
+ - id: 3
242
+ name: Mexico
243
+
244
+ # hash style
245
+ us:
246
+ id: 1
247
+ name: US
248
+ canada:
249
+ id: 2
250
+ name: Canada
251
+ mexico:
252
+ id: 3
253
+ name: Mexico
254
+
255
+ ## ActiveFile
256
+
257
+ 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 extend ActiveHash to parse and load your file. Just add a custom ::load_file method, and define the extension you want the file to use:
258
+
259
+ class Country < ActiveFile::Base
260
+ set_root_path "/u/data"
261
+ set_filename "sample"
262
+
263
+ class << self
264
+ def extension
265
+ ".super_secret"
266
+ end
267
+
268
+ def load_file
269
+ MyAwesomeDecoder.load_file(full_path)
270
+ end
271
+ end
272
+ end
273
+
274
+ 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.
275
+
276
+ Setting the default file location in Rails:
277
+
278
+ # config/initializers/active_file.rb
279
+ ActiveFile.set_root_path "config/activefiles"
280
+
281
+ In Rails, in development mode, it reloads the entire class, which reloads the file. In production, the data cached in memory.
282
+
283
+ NOTE: By default, .full_path refers to the current working directory. In a rails app, this will be RAILS_ROOT.
284
+
285
+ ## Authors
286
+
287
+ Written by Jeff Dean, Mike Dalessio and Ben Woosley
288
+
289
+ == Copyright
290
+
291
+ Copyright (c) 2009 Jeff Dean. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "honkster-active_hash"
8
+ gem.summary = %Q{An ActiveRecord-like model that uses a hash or file as a datasource}
9
+ gem.email = "jeff@zilkey.com"
10
+ gem.homepage = "http://github.com/zilkey/active_hash"
11
+ gem.authors = ["Jeff Dean", "Mike Dalessio", "Corey Innis", "Peter Jaros"]
12
+ gem.add_dependency('activesupport', [">= 2.2.2"])
13
+ end
14
+
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
17
+ end
18
+
19
+ require 'spec/rake/spectask'
20
+ Spec::Rake::SpecTask.new(:spec) do |spec|
21
+ spec.libs << 'lib' << 'spec'
22
+ spec.spec_files = FileList['spec/**/*_spec.rb']
23
+ end
24
+
25
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
26
+ spec.libs << 'lib' << 'spec'
27
+ spec.pattern = 'spec/**/*_spec.rb'
28
+ spec.rcov = true
29
+ end
30
+
31
+
32
+ task :default => :spec
33
+
34
+ require 'rake/rdoctask'
35
+ Rake::RDocTask.new do |rdoc|
36
+ if File.exist?('VERSION.yml')
37
+ config = YAML.load(File.read('VERSION.yml'))
38
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
39
+ else
40
+ version = ""
41
+ end
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "active_hash #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
48
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.7.3
@@ -0,0 +1,68 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{active_hash}
8
+ s.version = "0.7.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jeff Dean", "Mike Dalessio", "Corey Innis", "Peter Jaros"]
12
+ s.date = %q{2009-10-22}
13
+ s.email = %q{jeff@zilkey.com}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".document",
20
+ ".gitignore",
21
+ "CHANGELOG",
22
+ "LICENSE",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "active_hash.gemspec",
27
+ "geminstaller.yml",
28
+ "lib/active_file/base.rb",
29
+ "lib/active_hash.rb",
30
+ "lib/active_hash/base.rb",
31
+ "lib/active_yaml/base.rb",
32
+ "lib/associations/associations.rb",
33
+ "spec/active_file/base_spec.rb",
34
+ "spec/active_hash/base_spec.rb",
35
+ "spec/active_yaml/base_spec.rb",
36
+ "spec/associations/associations_spec.rb",
37
+ "spec/fixtures/array_rows.yml",
38
+ "spec/fixtures/cities.yml",
39
+ "spec/fixtures/countries.yml",
40
+ "spec/fixtures/states.yml",
41
+ "spec/spec_helper.rb"
42
+ ]
43
+ s.homepage = %q{http://github.com/zilkey/active_hash}
44
+ s.rdoc_options = ["--charset=UTF-8"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = %q{1.3.5}
47
+ s.summary = %q{An ActiveRecord-like model that uses a hash or file as a datasource}
48
+ s.test_files = [
49
+ "spec/active_file/base_spec.rb",
50
+ "spec/active_hash/base_spec.rb",
51
+ "spec/active_yaml/base_spec.rb",
52
+ "spec/associations/associations_spec.rb",
53
+ "spec/spec_helper.rb"
54
+ ]
55
+
56
+ if s.respond_to? :specification_version then
57
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
58
+ s.specification_version = 3
59
+
60
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
61
+ s.add_runtime_dependency(%q<activesupport>, [">= 2.2.2"])
62
+ else
63
+ s.add_dependency(%q<activesupport>, [">= 2.2.2"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<activesupport>, [">= 2.2.2"])
67
+ end
68
+ end