active_hash 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,4 +3,5 @@
3
3
  coverage
4
4
  rdoc
5
5
  pkg
6
- .idea
6
+ .idea
7
+ junk.*
data/CHANGELOG CHANGED
@@ -1,3 +1,10 @@
1
+ 2009-10-13
2
+ - added ActiveHash::Base.has_many, which works with ActiveRecord or ActiveHash classes (thanks to baldwindavid)
3
+ - added ActiveHash::Base.belongs_to, which works with ActiveRecord or ActiveHash classes (thanks to baldwindavid)
4
+ - added .delete_all method that clears the in-memory array
5
+ - added support for Hash-style yaml (think, Rails fixtures)
6
+ - added setter for parent object on belongs_to ( city = City.new; city.state = State.first; city.state_id == State.first.id )
7
+
1
8
  2009-10-12
2
9
  - auto-assign fields after calling data= instead of after calling .all
3
10
  - remove require 'rubygems', so folks with non-gem setups can still use AH
data/README.md CHANGED
@@ -100,8 +100,10 @@ ActiveHash gives you ActiveRecord-esque methods like:
100
100
 
101
101
  It also gives you a few dynamic finder methods. For example, if you defined :name as a field, you'd get:
102
102
 
103
- Country.find_by_name "foo" # => returns the first object matching that name
104
- Country.find_all_by_name "foo" # => returns an array of the objects with matching names
103
+ Country.find_by_name "foo" # => returns the first object matching that name
104
+ Country.find_all_by_name "foo" # => returns an array of the objects with matching names
105
+ Country.find_by_id_and_name 1, "Germany" # => returns the first object matching that id and name
106
+ Country.find_all_by_id_and_name 1, "Germany" # => returns an array of objects matching that name and id
105
107
 
106
108
  ## Instance Methods
107
109
 
@@ -110,7 +112,7 @@ ActiveHash objects implement enough of the ActiveRecord api to satisfy most comm
110
112
  Country#id # => returns the numeric id or nil
111
113
  Country#quoted_id # => returns the numeric id
112
114
  Country#to_param # => returns the id as a string
113
- Country#new_record? # => false
115
+ Country#new_record? # => returns true if is not part of Country.all, false otherwise
114
116
  Country#readonly? # => true
115
117
  Country#hash # => the hash of the id (or the hash of nil)
116
118
  Country#eql? # => compares type and id, returns false if id is nil
@@ -120,9 +122,36 @@ ActiveHash also gives you methods related to the fields you defined. For exampl
120
122
  Country#name # => returns the passed in name
121
123
  Country#name? # => returns true if the name is not blank
122
124
 
123
- ## Integration with Rails
125
+ ## Saving in-memory records
124
126
 
125
- You can create .belongs_to associations from rails objects, like so:
127
+ 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:
128
+
129
+ Country.all # => []
130
+ Country.create
131
+ Country.all # [ <Country :id => 1> ]
132
+ country = Country.new
133
+ country.new_record? # => true
134
+ country.save
135
+ country.new_record? # => false
136
+ Country.all # [ <Country :id => 1>, <Country :id => 2> ]
137
+
138
+ 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:
139
+
140
+ Country.insert( record )
141
+ Country#save
142
+ Country#save!
143
+ Country.create
144
+ Country.create!
145
+
146
+ 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.
147
+
148
+ To clear all records from the in-memory array, call delete_all:
149
+
150
+ Country.delete_all # => does not affect the yaml files in any way - just clears the in-memory array which can be useful for testing
151
+
152
+ ## Associations
153
+
154
+ 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:
126
155
 
127
156
  class Country < ActiveHash::Base
128
157
  end
@@ -131,10 +160,57 @@ You can create .belongs_to associations from rails objects, like so:
131
160
  belongs_to :country
132
161
  end
133
162
 
163
+ ActiveHash will also work as a polymorphic parent:
164
+
165
+ class Country < ActiveHash::Base
166
+ end
167
+
168
+ class Person < ActiveRecord::Base
169
+ belongs_to :location, :polymorphic => true
170
+ end
171
+
172
+ person = Person.new
173
+ person.location = Country.first
174
+ person.save
175
+ person.location # => Country.first
176
+
134
177
  You can also use standard rails view helpers, like #collection_select:
135
178
 
136
179
  <%= collection_select :person, :country_id, Country.all, :id, :name %>
137
180
 
181
+ If you include the ActiveHash::Associations module, you can also create associations from your ActiveHash classes, like so:
182
+
183
+ class Country < ActiveHash::Base
184
+ include ActiveHash::Associations
185
+ has_many :people
186
+ end
187
+
188
+ class Person < ActiveHash::Base
189
+ include ActiveHash::Associations
190
+ belongs_to :country
191
+ has_many :pets
192
+ end
193
+
194
+ class Pet < ActiveRecord::Base
195
+ end
196
+
197
+ Once you define a belongs to, you also get the setter method:
198
+
199
+ class City < ActiveHash::Base
200
+ include ActiveHash::Associations
201
+ belongs_to :state
202
+ end
203
+
204
+ city = City.new
205
+ city.state = State.first
206
+ city.state_id # is State.first.id
207
+
208
+ 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.
209
+
210
+ 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.
211
+
212
+ Thanks to baldwindavid for the ideas and code on that one.
213
+
138
214
  ## ActiveYaml
139
215
 
140
216
  If you want to store your data in YAML files, just inherit from ActiveYaml and specify your path information:
@@ -153,7 +229,26 @@ The above example will look for the file "/u/data/sample.yml".
153
229
 
154
230
  ActiveYaml, as well as ActiveFile, check the mtime of the file you specified, and reloads the data if the mtime has changed. So you can replace the data in the files even if your app is running in production mode in rails.
155
231
 
156
- 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.
232
+ 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:
233
+
234
+ # array style
235
+ - id: 1
236
+ name: US
237
+ - id: 2
238
+ name: Canada
239
+ - id: 3
240
+ name: Mexico
241
+
242
+ # hash style
243
+ us:
244
+ id: 1
245
+ name: US
246
+ canada:
247
+ id: 2
248
+ name: Canada
249
+ mexico:
250
+ id: 3
251
+ name: Mexico
157
252
 
158
253
  ## ActiveFile
159
254
 
@@ -191,7 +286,7 @@ NOTE: By default, .full_path refers to the current working directory. In a rai
191
286
 
192
287
  ## Authors
193
288
 
194
- Written by Jeff Dean, Mike Dalessio and Ben Woosley
289
+ Written by Jeff Dean, Mike Dalessio and Ben Woosley
195
290
 
196
291
  == Copyright
197
292
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.0
1
+ 0.7.1
@@ -1,8 +1,11 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
1
4
  # -*- encoding: utf-8 -*-
2
5
 
3
6
  Gem::Specification.new do |s|
4
7
  s.name = %q{active_hash}
5
- s.version = "0.7.0"
8
+ s.version = "0.7.1"
6
9
 
7
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
11
  s.authors = ["Jeff Dean", "Mike Dalessio", "Corey Innis", "Peter Jaros"]
@@ -26,21 +29,27 @@ Gem::Specification.new do |s|
26
29
  "lib/active_hash.rb",
27
30
  "lib/active_hash/base.rb",
28
31
  "lib/active_yaml/base.rb",
32
+ "lib/associations/associations.rb",
29
33
  "spec/active_file/base_spec.rb",
30
34
  "spec/active_hash/base_spec.rb",
31
35
  "spec/active_yaml/base_spec.rb",
32
- "spec/active_yaml/sample.yml",
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",
33
41
  "spec/spec_helper.rb"
34
42
  ]
35
43
  s.homepage = %q{http://github.com/zilkey/active_hash}
36
44
  s.rdoc_options = ["--charset=UTF-8"]
37
45
  s.require_paths = ["lib"]
38
- s.rubygems_version = %q{1.3.3}
46
+ s.rubygems_version = %q{1.3.5}
39
47
  s.summary = %q{An ActiveRecord-like model that uses a hash or file as a datasource}
40
48
  s.test_files = [
41
49
  "spec/active_file/base_spec.rb",
42
50
  "spec/active_hash/base_spec.rb",
43
51
  "spec/active_yaml/base_spec.rb",
52
+ "spec/associations/associations_spec.rb",
44
53
  "spec/spec_helper.rb"
45
54
  ]
46
55
 
@@ -9,3 +9,5 @@ gems:
9
9
  version: >= 1.2.8
10
10
  - name: fixjour
11
11
  version: 0.2.0
12
+ - name: jeweler
13
+ version: 1.2.1
@@ -2,3 +2,4 @@ require 'activesupport'
2
2
  require 'active_hash/base'
3
3
  require 'active_file/base'
4
4
  require 'active_yaml/base'
5
+ require 'associations/associations'
@@ -7,7 +7,12 @@ module ActiveHash
7
7
  def data=(array_of_hashes)
8
8
  @records = nil
9
9
  write_inheritable_attribute(:data, array_of_hashes)
10
- auto_assign_fields( array_of_hashes )
10
+ if array_of_hashes
11
+ auto_assign_fields( array_of_hashes )
12
+ array_of_hashes.each do |hash|
13
+ insert new(hash)
14
+ end
15
+ end
11
16
  end
12
17
 
13
18
  def insert(record)
@@ -25,24 +30,20 @@ module ActiveHash
25
30
  end
26
31
  end
27
32
 
28
- def create(attributes)
33
+ def create(attributes = {})
29
34
  record = new(attributes)
30
35
  record.save
31
36
  record
32
37
  end
33
38
 
34
- def create!(attributes)
39
+ def create!(attributes = {})
35
40
  record = new(attributes)
36
41
  record.save!
37
42
  record
38
43
  end
39
44
 
40
45
  def all
41
- unless @records
42
- records = read_inheritable_attribute(:data) || []
43
- @records = records.collect {|hash| new(hash)}
44
- end
45
- @records
46
+ @records || []
46
47
  end
47
48
 
48
49
  def count
@@ -55,6 +56,10 @@ module ActiveHash
55
56
 
56
57
  end
57
58
 
59
+ def delete_all
60
+ @records = []
61
+ end
62
+
58
63
  def find(id, *args)
59
64
  case id
60
65
  when :all
@@ -3,6 +3,14 @@ module ActiveYaml
3
3
  class Base < ActiveFile::Base
4
4
  class << self
5
5
  def load_file
6
+ if (data = raw_data).is_a?(Array)
7
+ data
8
+ else
9
+ data.values
10
+ end
11
+ end
12
+
13
+ def raw_data
6
14
  YAML.load_file(full_path)
7
15
  end
8
16
 
@@ -13,4 +21,4 @@ module ActiveYaml
13
21
  end
14
22
  end
15
23
 
16
- end
24
+ end
@@ -0,0 +1,40 @@
1
+ module ActiveHash
2
+ module Associations
3
+
4
+ def self.included(base)
5
+ base.send(:extend, ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def has_many(association_id, options = {})
10
+
11
+ define_method(association_id) do
12
+ options = {
13
+ :class_name => association_id.to_s.classify,
14
+ :foreign_key => self.class.to_s.foreign_key
15
+ }.merge(options)
16
+
17
+ options[:class_name].constantize.send("find_all_by_#{options[:foreign_key]}", id)
18
+ end
19
+
20
+ end
21
+
22
+ def belongs_to(association_id, options = {})
23
+
24
+ options = {
25
+ :class_name => association_id.to_s.classify,
26
+ :foreign_key => association_id.to_s.foreign_key
27
+ }.merge(options)
28
+
29
+ define_method(association_id) do
30
+ options[:class_name].constantize.find(send(options[:foreign_key]))
31
+ end
32
+
33
+ define_method("#{association_id}=") do |new_value|
34
+ attributes[ options[:foreign_key].to_sym ] = new_value.id
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -54,17 +54,17 @@ describe ActiveHash, "Base" do
54
54
  end
55
55
  end
56
56
 
57
- it "populates the object with data" do
57
+ it "populates the object with data and auto-assigns keys" do
58
58
  Country.data = [{:name => "US"}, {:name => "Canada"}]
59
- Country.data.should == [{:name => "US"}, {:name => "Canada"}]
59
+ Country.data.should == [{:name => "US", :id => 1}, {:name => "Canada", :id => 2}]
60
60
  end
61
61
 
62
62
  it "allows each of it's subclasses to have it's own data" do
63
63
  Country.data = [{:name => "US"}, {:name => "Canada"}]
64
64
  Region.data = [{:description => "A big region"}, {:description => "A remote region"}]
65
65
 
66
- Country.data.should == [{:name => "US"}, {:name => "Canada"}]
67
- Region.data.should == [{:description => "A big region"}, {:description => "A remote region"}]
66
+ Country.data.should == [{:name => "US", :id => 1}, {:name => "Canada", :id => 2}]
67
+ Region.data.should == [{:description => "A big region", :id => 1}, {:description => "A remote region", :id => 2}]
68
68
  end
69
69
  end
70
70
 
@@ -567,7 +567,7 @@ describe ActiveHash, "Base" do
567
567
  Country.all.should be_empty
568
568
  country = Country.new :id => 1, :name => "foo"
569
569
  country.save.should be_true
570
- Country.all.should == [country]
570
+ Country.all.should == [country]
571
571
  end
572
572
 
573
573
  it "adds the new object to the data collection" do
@@ -585,6 +585,12 @@ describe ActiveHash, "Base" do
585
585
  Country.field :name
586
586
  end
587
587
 
588
+ it "works with no args" do
589
+ Country.all.should be_empty
590
+ country = Country.create
591
+ country.id.should == 1
592
+ end
593
+
588
594
  it "adds the new object to the data collection" do
589
595
  Country.all.should be_empty
590
596
  country = Country.create :id => 1, :name => "foo"
@@ -648,7 +654,7 @@ describe ActiveHash, "Base" do
648
654
  it "returns false when the object is already part of the collection" do
649
655
  Country.new(:id => 1).should_not be_new_record
650
656
  end
651
-
657
+
652
658
  it "returns true when the object is not part of the collection" do
653
659
  Country.new(:id => 2).should be_new_record
654
660
  end
@@ -683,6 +689,18 @@ describe ActiveHash, "Base" do
683
689
 
684
690
  end
685
691
 
692
+ describe ".delete_all" do
693
+
694
+ it "clears out all record" do
695
+ country1 = Country.create
696
+ country2 = Country.create
697
+ Country.all.should == [country1, country2]
698
+ Country.delete_all
699
+ Country.all.should be_empty
700
+ end
701
+
702
+ end
703
+
686
704
  describe "using with Fixjour" do
687
705
 
688
706
  before do
@@ -2,19 +2,55 @@ require 'spec/spec_helper'
2
2
 
3
3
  describe ActiveYaml::Base do
4
4
 
5
- describe ".all" do
6
- it "loads the data from the yml file" do
7
- class SomeArbitraryClass < ActiveYaml::Base
8
- set_root_path File.dirname(__FILE__)
9
- set_filename "sample"
10
- field :name
5
+ before do
6
+ ActiveYaml::Base.set_root_path File.expand_path(File.dirname(__FILE__) + "/../fixtures")
7
+
8
+ class ArrayRow < ActiveYaml::Base
9
+ end
10
+
11
+ class City < ActiveYaml::Base
12
+ end
13
+
14
+ class State < ActiveYaml::Base
15
+ end
16
+ end
17
+
18
+ after do
19
+ Object.send :remove_const, :ArrayRow
20
+ Object.send :remove_const, :City
21
+ Object.send :remove_const, :State
22
+ end
23
+
24
+ describe ".raw_data" do
25
+
26
+ it "returns the raw hash data loaded from yaml hash-formatted files" do
27
+ City.raw_data.should be_kind_of(Hash)
28
+ City.raw_data.keys.should include("albany", "portland")
29
+ end
30
+
31
+ it "returns the raw array data loaded from yaml array-formatted files" do
32
+ ArrayRow.raw_data.should be_kind_of(Array)
33
+ end
34
+
35
+ end
36
+
37
+ describe ".load_file" do
38
+
39
+ describe "with array data" do
40
+ it "returns an array of hashes" do
41
+ ArrayRow.load_file.should be_kind_of(Array)
42
+ ArrayRow.load_file.should include({"name" => "Row 1", "id" => 1})
11
43
  end
44
+ end
12
45
 
13
- records = SomeArbitraryClass.all
14
- records.length.should == 3
15
- records.should =~ [SomeArbitraryClass.new(:id => 1), SomeArbitraryClass.new(:id => 2), SomeArbitraryClass.new(:id => 3)]
16
- records.first.name.should == "US"
46
+ describe "with hash data" do
47
+ it "returns an array of hashes" do
48
+ City.load_file.should be_kind_of(Array)
49
+ City.load_file.should include({"state" => :new_york, "name" => "Albany", "id" => 1})
50
+ City.all.should include( City.new(:id => 1) )
51
+ end
17
52
  end
53
+
18
54
  end
19
55
 
20
56
  end
@@ -0,0 +1,102 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ActiveHash::Base, "associations" do
4
+
5
+ before do
6
+ build_model :countries do
7
+ end
8
+
9
+ class City < ActiveHash::Base
10
+ field :country_id
11
+ include ActiveHash::Associations
12
+ end
13
+
14
+ class Author < ActiveHash::Base
15
+ include ActiveHash::Associations
16
+ field :publisher_id
17
+ field :city_id
18
+ end
19
+
20
+ build_model :books do
21
+ integer :author_id
22
+ end
23
+ end
24
+
25
+ after do
26
+ Object.send :remove_const, :Author
27
+ end
28
+
29
+ describe "#has_many" do
30
+
31
+ context "with ActiveRecord children" do
32
+ before do
33
+ @included_book_1 = Book.create! :author_id => 1
34
+ @included_book_2 = Book.create! :author_id => 1
35
+ @excluded_book = Book.create! :author_id => 2
36
+ end
37
+
38
+ it "find the correct records" do
39
+ Author.has_many :books
40
+ author = Author.create :id => 1
41
+ author.books.should =~ [@included_book_1, @included_book_2]
42
+ end
43
+ end
44
+
45
+ context "with ActiveHash children" do
46
+ before do
47
+ @included_author_1 = Author.create :city_id => 1
48
+ @included_author_2 = Author.create :city_id => 1
49
+ @excluded_author = Author.create :city_id => 2
50
+ end
51
+
52
+ it "find the correct records" do
53
+ City.has_many :authors
54
+ city = City.create :id => 1
55
+ city.authors.should =~ [@included_author_1, @included_author_2]
56
+ end
57
+
58
+ it "uses the correct class name when passed" do
59
+ City.has_many :writers, :class_name => "Author"
60
+ city = City.create :id => 1
61
+ city.writers.should =~ [@included_author_1, @included_author_2]
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ describe "#belongs_to" do
68
+
69
+ context "with an ActiveRecord parent" do
70
+ it "find the correct records" do
71
+ City.belongs_to :country
72
+ country = Country.create
73
+ city = City.create :country_id => country.id
74
+ city.country.should == country
75
+ end
76
+ end
77
+
78
+ context "with an ActiveHash parent" do
79
+ it "find the correct records" do
80
+ Author.belongs_to :city
81
+ city = City.create
82
+ author = Author.create :city_id => city.id
83
+ author.city.should == city
84
+ end
85
+ end
86
+
87
+ describe "#parent=" do
88
+ before do
89
+ Author.belongs_to :city
90
+ @city = City.create :id => 1
91
+ end
92
+
93
+ it "sets the underlying id of the parent" do
94
+ author = Author.new
95
+ author.city = @city
96
+ author.city_id.should == @city.id
97
+ end
98
+ end
99
+
100
+ end
101
+
102
+ end
@@ -0,0 +1,4 @@
1
+ - id: 1
2
+ name: Row 1
3
+ - id: 2
4
+ name: Row 2
@@ -0,0 +1,8 @@
1
+ albany:
2
+ id: 1
3
+ state: :new_york
4
+ name: Albany
5
+ portland:
6
+ id: 2
7
+ state: Oregon
8
+ name: Portland
@@ -0,0 +1,6 @@
1
+ new_york:
2
+ id: 1
3
+ name: New York
4
+ oregon:
5
+ id: 2
6
+ name: Oregon
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_hash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Dean
@@ -48,10 +48,15 @@ files:
48
48
  - lib/active_hash.rb
49
49
  - lib/active_hash/base.rb
50
50
  - lib/active_yaml/base.rb
51
+ - lib/associations/associations.rb
51
52
  - spec/active_file/base_spec.rb
52
53
  - spec/active_hash/base_spec.rb
53
54
  - spec/active_yaml/base_spec.rb
54
- - spec/active_yaml/sample.yml
55
+ - spec/associations/associations_spec.rb
56
+ - spec/fixtures/array_rows.yml
57
+ - spec/fixtures/cities.yml
58
+ - spec/fixtures/countries.yml
59
+ - spec/fixtures/states.yml
55
60
  - spec/spec_helper.rb
56
61
  has_rdoc: true
57
62
  homepage: http://github.com/zilkey/active_hash
@@ -77,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
82
  requirements: []
78
83
 
79
84
  rubyforge_project:
80
- rubygems_version: 1.3.3
85
+ rubygems_version: 1.3.5
81
86
  signing_key:
82
87
  specification_version: 3
83
88
  summary: An ActiveRecord-like model that uses a hash or file as a datasource
@@ -85,4 +90,5 @@ test_files:
85
90
  - spec/active_file/base_spec.rb
86
91
  - spec/active_hash/base_spec.rb
87
92
  - spec/active_yaml/base_spec.rb
93
+ - spec/associations/associations_spec.rb
88
94
  - spec/spec_helper.rb