acts_as_lookup 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -45,3 +45,9 @@ Some work is yet to be done to support general classes using acts_as_lookup
45
45
  == Configuration acts_as_lookup
46
46
 
47
47
  TODO: go through the options (enabled and FUTURE)
48
+
49
+ == Development
50
+
51
+ === Testing
52
+
53
+ Run rake spec to run the (rspec) tests. You should add tests for anything you touch.
data/TODO CHANGED
@@ -1,22 +1,17 @@
1
1
  Things to work on:
2
2
 
3
3
 
4
- * find a better workaround for the cached instance problem (see https://rails.lighthouseapp.com/projects/8994/tickets/1339, for example) than explicit requiring of lookup classes (see acts_as_lookup.rb : ActsAsLookupHasLookupClassMethods#has_lookup)
5
4
  * for now, it's required to include :id in the values hashes; enhance to allow leaving :id off (by specifying :uniqueness_column) and fetch them from db (option will only be available for :sync_with_db == true)
6
5
  * allow gem to be used outside Rails by specifying alternative implementations for db-hitting methods....e.g. add configuration values that specify method names to call to load/write/remove from db. see +acts_as_lookup_fetch_values+ method comment.
7
6
  * add the following configuration options:
8
- ** sync_with_db: if false, no inserts/retrievals of db will be performed at all. this means the model will operate as an enum-like thing...a lot like rapleaf_enum where the class just defines the mapping and when associations are formed, the id gets inserted.
9
- *** plan for this to work even if there is no lookup table in db at all...eg if we didn't care about foreign keys and just wanted to store some ids instead of strings....just need to make sure that associations work properly...like user.status = UserAccountStatus.active needs to set user.status_id = UserAccountStatus.active
10
- *** this might be kind of tricky to do without mucking with the active record association methods (belongs_to() etc.)
11
- *** if false, don't have the requirement that there's an :id column specified
12
- *** so maybe this no-table business will be FUTURE only...
13
- *** default will be true
14
- ** write_to_db: if true will insert values specified in model into db table if the row doesn't exist (based on id)
15
- *** default will be true
16
- ** remove_from_db: if there is an id in the table not specified in the model, will remove that row (removal will happen before any inserts)
17
- *** allows for there to be values in lookup table that aren't relevant for the rails app (e.g. something that only some other process needs)
18
- *** default will be false
19
- ** indexed_columns: which columns can be used in lookup_by_column methods
7
+ * if :sync_with_db false, don't have the requirement that there's an :id column specified
8
+ ** so maybe this no-table business will be FUTURE only...
9
+ ** default will be true
10
+ * write_to_db: if true will insert values specified in model into db table if the row doesn't exist (based on id)
11
+ ** default will be true
12
+ * remove_from_db: if there is an id in the table not specified in the model, will remove that row (removal will happen before any inserts)
13
+ ** allows for there to be values in lookup table that aren't relevant for the rails app (e.g. something that only some other process needs)
14
+ ** default will be false
20
15
  *** allows for specifying other values in the model that don't need to be lookupable columns, e.g. if there are columns with non-unique values, foreign keys to other tables etc.
21
16
  *** default will be to use all columns specified in the model
22
17
  *** probably store the lookup caches in one giant hash, each entry is a hash :column => lookup_hash_for_that_column
@@ -5,18 +5,18 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{acts_as_lookup}
8
- s.version = "0.0.3"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Brian Percival"]
12
- s.date = %q{2010-05-04}
12
+ s.date = %q{2011-02-23}
13
13
  s.description = %q{Provides an easy means for creating models that act like enumerations or lookup
14
14
  tables. You can specify the lookup values in your Rails models and can lazily
15
15
  push these to the associated db tables (or not). Also dynamically adds helpful
16
16
  class-level methods to access singleton instances of each value in your lookup
17
17
  table.
18
18
  }
19
- s.email = %q{percivalatumamibuddotcom}
19
+ s.email = %q{percivalatdiscovereadsdotcom}
20
20
  s.extra_rdoc_files = [
21
21
  "LICENSE",
22
22
  "README.rdoc",
@@ -30,14 +30,18 @@ table.
30
30
  s.homepage = %q{http://github.com/bmpercy/acts_as_lookup}
31
31
  s.rdoc_options = ["--charset=UTF-8"]
32
32
  s.require_paths = ["lib"]
33
- s.rubygems_version = %q{1.3.5}
33
+ s.rubygems_version = %q{1.3.7}
34
34
  s.summary = %q{Helpful for creating lookup-table-like models}
35
+ s.test_files = [
36
+ "spec/acts_as_lookup_spec.rb",
37
+ "spec/spec_helper.rb"
38
+ ]
35
39
 
36
40
  if s.respond_to? :specification_version then
37
41
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
38
42
  s.specification_version = 3
39
43
 
40
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
41
45
  else
42
46
  end
43
47
  else
@@ -2,34 +2,36 @@
2
2
  # method
3
3
  #-------------------------------------------------------------------------------
4
4
  module ActsAsLookupClassMethods
5
- def acts_as_lookup_options=(options)
6
- @@acts_as_lookup_options = options
5
+ def self.extended(klass)
6
+ klass.send(:instance_variable_set, :@acts_as_lookup_options, nil)
7
+ klass.send(:instance_variable_set, :@acts_as_lookup_by_id, {})
8
+ klass.send(:instance_variable_set, :@acts_as_lookup_by_name, {})
9
+ klass.send(:instance_variable_set, :@acts_as_lookup_initialized, false)
10
+ klass.send(:instance_variable_set, :@acts_as_lookup_values, [])
7
11
  end
8
12
 
13
+ def acts_as_lookup_options=(options)
14
+ @acts_as_lookup_options = options
15
+ end
9
16
  def acts_as_lookup_options
10
- @@acts_as_lookup_options
17
+ @acts_as_lookup_options
11
18
  end
12
19
 
13
-
14
20
  # FUTURE: allow for dynamically specifying which columns can be used for
15
21
  # cache lookups
16
22
  #-----------------------------------------------------------------------------
17
23
  def lookup_by_id(id)
18
- @@acts_as_lookup_by_id[id]
24
+ @acts_as_lookup_by_id[id]
19
25
  end
20
26
  def lookup_by_name(name)
21
- @@acts_as_lookup_by_name[name]
27
+ @acts_as_lookup_by_name[name]
22
28
  end
23
29
 
24
30
 
25
31
  # check if the current lookup class has been initialized for lookup use
26
32
  #-----------------------------------------------------------------------------
27
33
  def acts_as_lookup_initialized?
28
- if !defined?(@@acts_as_lookup_initialized)
29
- @@acts_as_lookup_initialized = false
30
- end
31
-
32
- @@acts_as_lookup_initialized
34
+ @acts_as_lookup_initialized
33
35
  end
34
36
 
35
37
 
@@ -42,25 +44,22 @@ module ActsAsLookupClassMethods
42
44
  Thread.exclusive do
43
45
  # double-check in case of race condition in calling code
44
46
  unless self.acts_as_lookup_initialized?
45
- @@acts_as_lookup_by_id = {}
46
- @@acts_as_lookup_by_name = {}
47
-
48
- if @@acts_as_lookup_options[:sync_with_db]
47
+ if acts_as_lookup_options[:sync_with_db]
49
48
  acts_as_lookup_fetch_values
50
- if @@acts_as_lookup_options[:write_to_db]
49
+ if acts_as_lookup_options[:write_to_db]
51
50
  acts_as_lookup_write_missing_values
52
51
  end
53
52
  else
54
- @@acts_as_lookup_values = @acts_as_lookup_options[:values]
53
+ @acts_as_lookup_values = acts_as_lookup_options[:values]
55
54
  self.acts_as_lookup_refresh_caches
56
55
  end
57
56
 
58
- @@acts_as_lookup_initialized = true
57
+ @acts_as_lookup_initialized = true
59
58
  end
60
59
 
61
60
  # FUTURE: allow for a different column to be used for generating class
62
61
  # accessor methods
63
- @@acts_as_lookup_by_name.each_pair do |name,val|
62
+ @acts_as_lookup_by_name.each_pair do |name,val|
64
63
  self.acts_as_lookup_add_shortcut name
65
64
  end
66
65
 
@@ -69,6 +68,7 @@ module ActsAsLookupClassMethods
69
68
 
70
69
 
71
70
  # fetches existing records from the db and merges them into the cached values
71
+ # only called if :sync_with_db option is true
72
72
  #
73
73
  # FUTURE: if this gem is to be used outside of a Rails' ActiveRecord::Base
74
74
  # descendant, will need to allow calling class to specify an alternative
@@ -76,23 +76,23 @@ module ActsAsLookupClassMethods
76
76
  # call)
77
77
  #-----------------------------------------------------------------------------
78
78
  def acts_as_lookup_fetch_values
79
- @@acts_as_lookup_values = self.all
79
+ @acts_as_lookup_values = self.all
80
80
  self.acts_as_lookup_refresh_caches
81
81
  end
82
82
 
83
83
 
84
84
  # writes any missing values to the db
85
+ # only called if :sync_with_db and :write_to_db options are both true
85
86
  #
86
87
  # NOTE: does NOT overwrite any values found in the db, so it is possible for
87
88
  # the values specified in the class to be superceded by values in the
88
89
  # database
89
90
  #-----------------------------------------------------------------------------
90
91
  def acts_as_lookup_write_missing_values
91
-
92
92
  # FUTURE: if :ids aren't provided, use the uniqueness_column to determine
93
93
  # which values are missing from existing caches
94
- @@acts_as_lookup_options[:values].each do |val|
95
- next if @@acts_as_lookup_by_id.include?(val[:id])
94
+ acts_as_lookup_options[:values].each do |val|
95
+ next if @acts_as_lookup_by_id.include?(val[:id])
96
96
 
97
97
  # allow for attr_accessible protection, assign attributes one-by-one
98
98
  new_val = self.new
@@ -101,7 +101,7 @@ module ActsAsLookupClassMethods
101
101
  end
102
102
  new_val.save!
103
103
 
104
- @@acts_as_lookup_values << new_val
104
+ @acts_as_lookup_values << new_val
105
105
  end
106
106
 
107
107
  self.acts_as_lookup_refresh_caches
@@ -113,9 +113,9 @@ module ActsAsLookupClassMethods
113
113
  def acts_as_lookup_refresh_caches
114
114
  # FUTURE: this will get cleaned up, and will dynamically select which
115
115
  # columns to establish lookup caches for.
116
- @@acts_as_lookup_values.each do |val|
117
- @@acts_as_lookup_by_id.reverse_merge! val.id => val
118
- @@acts_as_lookup_by_name.reverse_merge! val.name => val
116
+ @acts_as_lookup_values.each do |val|
117
+ @acts_as_lookup_by_id.merge!(val.id => val) unless @acts_as_lookup_by_id.include?(val.id)
118
+ @acts_as_lookup_by_name.merge!(val.name => val) unless @acts_as_lookup_by_name.include?(val.name)
119
119
  end
120
120
  end
121
121
 
@@ -134,7 +134,6 @@ module ActsAsLookupClassMethods
134
134
  # to lookup by name
135
135
  #-----------------------------------------------------------------------------
136
136
  def acts_as_lookup_add_shortcut(name)
137
-
138
137
  method_name = name.gsub(/ /, '_')
139
138
  unless method_name.upcase == method_name
140
139
  method_name.downcase!
@@ -146,7 +145,6 @@ module ActsAsLookupClassMethods
146
145
 
147
146
  instance_eval "def #{method_name}; self.lookup_by_name '#{name}'; end"
148
147
  end
149
-
150
148
  end
151
149
 
152
150
  # modify object to allow any class to act like a lookup class
@@ -161,8 +159,8 @@ class Object
161
159
  def self.acts_as_lookup(options = {})
162
160
  self.extend ActsAsLookupClassMethods
163
161
 
164
- options.reverse_merge! :sync_with_db => true,
165
- :write_to_db => true #,
162
+ options.merge!(:sync_with_db => true) unless options.include?(:sync_with_db)
163
+ options.merge!(:write_to_db => true) unless options.include?(:write_to_db)
166
164
  # FUTURE:
167
165
  # :remove_from_db => false,
168
166
  # :shortcut_method_column => :name
@@ -189,21 +187,14 @@ if defined?(ActiveRecord)
189
187
  # classname (in CamelCase) for the association.
190
188
  #---------------------------------------------------------------------------
191
189
  def has_lookup(association_name, options = {})
190
+ cname = options[:class_name] || association_name.to_s.camelize
192
191
 
193
- class_name = options[:class_name] || association_name.to_s.camelize
194
-
195
- # this is a hack that is not at all pretty but seems to get around the
196
- # double-class loading problems that arise in rails: see for example
197
- # https://rails.lighthouseapp.com/projects/8994/tickets/1339
198
- # it may create other problems though, so be careful....
199
- if !Object.const_defined?(class_name)
200
- require File.join(RAILS_ROOT, 'app', 'models', class_name.underscore)
201
- end
192
+ force_class_load cname
202
193
 
203
194
  # this is inspired/borrowed from Rapleaf's has_rap_enum
204
- klass = Kernel.const_get(class_name)
195
+ klass = Kernel.const_get(cname)
205
196
  unless(klass && klass.is_a?(ActsAsLookupClassMethods))
206
- raise "#{class_name.to_s.camelize} is not an acts_as_lookup class"
197
+ raise "#{cname.to_s.camelize} is not an acts_as_lookup class"
207
198
  end
208
199
 
209
200
  # create the reader method for the lookup association
@@ -220,6 +211,18 @@ if defined?(ActiveRecord)
220
211
  end
221
212
  end
222
213
 
214
+ # separate the logic for forcing a class load to isolate it, as well as
215
+ # making testing easier
216
+ #---------------------------------------------------------------------------
217
+ def force_class_load(cname)
218
+ # this is a hack that is not at all pretty but seems to get around the
219
+ # double-class loading problems that arise in rails: see for example
220
+ # https://rails.lighthouseapp.com/projects/8994/tickets/1339
221
+ # it may create other problems though, so be careful....
222
+ if !Object.const_defined?(cname)
223
+ require File.join(RAILS_ROOT, 'app', 'models', cname.underscore)
224
+ end
225
+ end
223
226
  end
224
227
 
225
228
  ActiveRecord::Base.extend ActsAsLookupHasLookupClassMethods
@@ -0,0 +1,436 @@
1
+ require 'spec_helper'
2
+
3
+ module ActiveRecord
4
+ class Base
5
+
6
+ end
7
+ end
8
+
9
+ # add a helper method that's defined in rails...the gem only uses it if
10
+ # ActiveRecord is defined
11
+ class String
12
+ def camelize
13
+ splits = self.split("_")
14
+ splits.map! do |s|
15
+ s.downcase!
16
+ s[0] = s[0].upcase
17
+ s
18
+ end
19
+ splits.join
20
+ end
21
+ end
22
+
23
+ # a dummy lookup class (see has_lookup tests)
24
+ class DummyLookup < Struct.new(:id, :name)
25
+ def self.one_instance
26
+ self.new(1,"dummy one")
27
+ end
28
+
29
+ def self.another_instance
30
+ self.new(2,"dummy two")
31
+ end
32
+
33
+ def self.yet_another_instance
34
+ self.new(3,"dummy three")
35
+ end
36
+ end
37
+
38
+ describe "ActsAsLookup" do
39
+ # this is kinda hacky, but what we're doing here is trying to clear out all
40
+ # entries in namespace for acts as lookup between each run so that we don't
41
+ # see any bugs that are tied to double-loading the acts as lookup
42
+ # infrastructure
43
+ # so we:
44
+ # - unload all classes that are either part of gem or are test classes
45
+ # - reload the acts as lookup lib file
46
+ # - re-declare each of the test classes we're using
47
+ #
48
+ # (alternative would be to generate new classes for each test but that's
49
+ # likely equally messy with dynamically defined class names etc.)
50
+ #-----------------------------------------------------------------------------
51
+ before :each do
52
+ Object.constants.each do |klass|
53
+ if (klass.to_s =~ /LookupClass/) && (Object.const_defined?(klass.to_s))
54
+ Object.send(:remove_const,klass.to_s)
55
+ end
56
+ end
57
+ load File.join(File.dirname(__FILE__), '../lib/acts_as_lookup.rb')
58
+
59
+ class ClassOnlyLookupClass < Struct.new(:id, :name); end
60
+
61
+ # another one to detect shared var conflict
62
+ class SecondClassOnlyLookupClass < Struct.new(:id, :name); end
63
+
64
+ # all values are in db and in class specification
65
+ class ActiveRecordLookupClassWithAllValuesInDbAndClass < Struct.new(:id, :name)
66
+ ALL_VALUES = [self.new(1, "one"), self.new(2, "two")]
67
+
68
+ def self.all_values
69
+ ALL_VALUES
70
+ end
71
+
72
+ def self.class_vals
73
+ all_values
74
+ end
75
+
76
+ def self.all
77
+ all_values
78
+ end
79
+ end
80
+ # some values are in db; the rest are in class specification
81
+ class ActiveRecordLookupClassWithSomeValuesInDb < Struct.new(:id, :name)
82
+ ALL_VALUES = [self.new(1, "one"), self.new(2, "two"), self.new(3, "three")]
83
+
84
+ def self.all_values
85
+ ALL_VALUES
86
+ end
87
+
88
+ def self.db_missing_val
89
+ all_values[1]
90
+ end
91
+
92
+ def self.class_vals
93
+ all_values
94
+ end
95
+
96
+ # don't return all vals in query
97
+ def self.all
98
+ all_values.reject { |v| v.id == db_missing_val.id }
99
+ end
100
+
101
+ # a method for checking what's saved when save! is called
102
+ def self.created_val(val)
103
+ # doesn't need to do anything
104
+ end
105
+
106
+ # "active record" method for creating new lookup values
107
+ def save!
108
+ self.class.created_val(self)
109
+ end
110
+ end
111
+ # all values in db; some are in class specification
112
+ class ActiveRecordLookupClassWithSomeValuesInClass < Struct.new(:id, :name)
113
+ ALL_VALUES = [self.new(1, "one"), self.new(2, "two"), self.new(3, "three")]
114
+
115
+ def self.all_values
116
+ ALL_VALUES
117
+ end
118
+
119
+ def self.class_missing_val
120
+ all_values[1]
121
+ end
122
+
123
+ def self.class_vals
124
+ all_values.reject { |v| v.id == class_missing_val.id }
125
+ end
126
+
127
+ # return all vals in query
128
+ def self.all
129
+ all_values
130
+ end
131
+ end
132
+ class ClassWithLookupClass < ActiveRecord::Base
133
+ attr_accessor :dummy_lookup_id
134
+ attr_accessor :other_lookup_id
135
+ end
136
+
137
+ end
138
+
139
+ #-----------------------------------------------------------------------------
140
+ describe "class-only (non ActiveRecord) lookup classes" do
141
+ it "should not insert any new values if :write_to_db is false" do
142
+ klass = ClassOnlyLookupClass
143
+ klass.should_not_receive :acts_as_lookup_write_missing_values
144
+ instances = [klass.new(1, "one"), klass.new(2, "two")]
145
+
146
+ klass.acts_as_lookup(
147
+ :sync_with_db => false,
148
+ :values => instances
149
+ )
150
+ end
151
+
152
+ it "should not query values if :sync_with_db is false" do
153
+ klass = ClassOnlyLookupClass
154
+ klass.should_not_receive :all
155
+ instances = [klass.new(1, "one"), klass.new(2, "two")]
156
+
157
+ klass.acts_as_lookup(
158
+ :sync_with_db => false,
159
+ :values => instances
160
+ )
161
+ end
162
+ end
163
+
164
+ # "active record" tests
165
+ # note: these test classes aren't really active record classes (to enable
166
+ # testing in absence of active record), but mimic enough of the
167
+ # interface for the tests
168
+
169
+ #-----------------------------------------------------------------------------
170
+ describe "when active record lookup class specifies all values and db has all values" do
171
+ it "should run select query if :sync_with_db is true" do
172
+ klass = ActiveRecordLookupClassWithAllValuesInDbAndClass
173
+
174
+ klass.should_receive(:all).and_return klass.all_values
175
+
176
+ klass.acts_as_lookup(
177
+ :values => klass.class_vals
178
+ )
179
+ end
180
+
181
+ it "should not insert any new values even if :write_to_db is true" do
182
+ klass = ActiveRecordLookupClassWithAllValuesInDbAndClass
183
+
184
+ klass.should_not_receive(:new)
185
+
186
+ klass.acts_as_lookup(
187
+ :write_to_db => true,
188
+ :values => klass.class_vals
189
+ )
190
+ end
191
+
192
+ it "should return correct value accessed by lookup_by_id" do
193
+ klass = ActiveRecordLookupClassWithAllValuesInDbAndClass
194
+ klass.acts_as_lookup(
195
+ :write_to_db => true,
196
+ :values => klass.class_vals
197
+ )
198
+
199
+ klass.lookup_by_id(klass.all_values.first.id).should == klass.all_values.first
200
+ end
201
+
202
+ it "should return correct value accessed by lookup_by_name" do
203
+ klass = ActiveRecordLookupClassWithAllValuesInDbAndClass
204
+ klass.acts_as_lookup(
205
+ :write_to_db => true,
206
+ :values => klass.class_vals
207
+ )
208
+
209
+ klass.lookup_by_name(klass.all_values.last.name).should == klass.all_values.last
210
+ end
211
+ end
212
+
213
+ #-----------------------------------------------------------------------------
214
+ describe "when lookup class specifies all lookup values but db only specifies some" do
215
+ before :each do
216
+ @klass = ActiveRecordLookupClassWithSomeValuesInDb
217
+ end
218
+
219
+ it "should run select query if :sync_with_db is true" do
220
+ all_result = @klass.all
221
+
222
+ @klass.should_receive(:all).and_return all_result
223
+
224
+ @klass.acts_as_lookup(
225
+ :values => @klass.class_vals
226
+ )
227
+ end
228
+
229
+ it "should insert any new values when :write_to_db is true" do
230
+ # this is a dummy class method that gets called when instance method
231
+ # save! is called, so we can check what was created
232
+ @klass.should_receive(:created_val) { |val|
233
+ val.id.should == @klass.db_missing_val.id
234
+ val.name.should == @klass.db_missing_val.name
235
+ }
236
+
237
+ @klass.acts_as_lookup(
238
+ :write_to_db => true,
239
+ :values => @klass.class_vals
240
+ )
241
+ end
242
+
243
+ it "should return correct value accessed by lookup_by_id" do
244
+ @klass.acts_as_lookup(
245
+ :write_to_db => true,
246
+ :values => @klass.class_vals
247
+ )
248
+
249
+ @klass.lookup_by_id(@klass.db_missing_val.id).should == @klass.db_missing_val
250
+ end
251
+
252
+ it "should return correct value accessed by lookup_by_name" do
253
+ @klass.acts_as_lookup(
254
+ :write_to_db => true,
255
+ :values => @klass.class_vals
256
+ )
257
+
258
+ @klass.lookup_by_name(@klass.db_missing_val.name).should == @klass.db_missing_val
259
+ end
260
+ end
261
+
262
+ #-----------------------------------------------------------------------------
263
+ describe "when lookup class doesn't specify all lookup values but db does" do
264
+ before :each do
265
+ @klass = ActiveRecordLookupClassWithSomeValuesInClass
266
+ end
267
+
268
+ it "should run select query if :sync_with_db is true" do
269
+ all_result = @klass.all
270
+
271
+ @klass.should_receive(:all).and_return all_result
272
+
273
+ @klass.acts_as_lookup(
274
+ :values => @klass.class_vals
275
+ )
276
+ end
277
+
278
+ it "shouldn't insert any new values even if :write_to_db is true" do
279
+ @klass.should_not_receive(:new)
280
+
281
+ @klass.acts_as_lookup(
282
+ :write_to_db => true,
283
+ :values => @klass.class_vals
284
+ )
285
+ end
286
+
287
+ it "should return correct value accessed by lookup_by_id" do
288
+ @klass.acts_as_lookup(
289
+ :write_to_db => true,
290
+ :values => @klass.class_vals
291
+ )
292
+
293
+ @klass.lookup_by_id(@klass.class_missing_val.id).should == @klass.class_missing_val
294
+ end
295
+
296
+ it "should return correct value accessed by lookup_by_name" do
297
+ @klass.acts_as_lookup(
298
+ :write_to_db => true,
299
+ :values => @klass.class_vals
300
+ )
301
+
302
+ @klass.lookup_by_name(@klass.class_missing_val.name).should == @klass.class_missing_val
303
+ end
304
+ end
305
+
306
+ #-----------------------------------------------------------------------------
307
+ describe "single-lookup-class scenarios" do
308
+ before :each do
309
+ @klass = ActiveRecordLookupClassWithSomeValuesInClass
310
+ @klass.acts_as_lookup(:values => @klass.class_vals)
311
+ end
312
+
313
+ it "should not query values on calls to lookup_by_id" do
314
+ @klass.should_not_receive(:find)
315
+ @klass.should_not_receive(:find_by_id)
316
+
317
+ @klass.lookup_by_id(1)
318
+ end
319
+
320
+ it "should not query values calls to lookup_by_name" do
321
+ @klass.should_not_receive(:find)
322
+ @klass.should_not_receive(:find_by_name)
323
+
324
+ @klass.lookup_by_name('one')
325
+ end
326
+
327
+ it "should dynamically add specific methods to access lookup value by name" do
328
+ @klass.all_values.each do |val|
329
+ @klass.send(val.name.to_sym).should == val
330
+ end
331
+ end
332
+ end
333
+
334
+ describe "two-lookup-class scenarios" do
335
+ it "should return the correct object (type) even if ids overlap between two lookup classes" do
336
+ klass1 = ClassOnlyLookupClass
337
+ klass2 = SecondClassOnlyLookupClass
338
+ instances1 = [klass1.new(1, "one"), klass1.new(2, "two")]
339
+ instances2 = [klass2.new(1, "class two one"), klass2.new(2, "class two two")]
340
+ klass1.acts_as_lookup(
341
+ :sync_with_db => false,
342
+ :values => instances1
343
+ )
344
+ klass2.acts_as_lookup(
345
+ :sync_with_db => false,
346
+ :values => instances2
347
+ )
348
+
349
+ klass1.lookup_by_id(1).should == instances1.first
350
+ klass2.lookup_by_id(1).should == instances2.first
351
+ end
352
+
353
+ it "should return the correct object (type) even if names overlap between two lookup classes" do
354
+ klass1 = ClassOnlyLookupClass
355
+ klass2 = SecondClassOnlyLookupClass
356
+ instances1 = [klass1.new(3, "three"), klass1.new(4, "four")]
357
+ instances2 = [klass2.new(33, "three"), klass2.new(44, "four")]
358
+ klass1.acts_as_lookup(
359
+ :sync_with_db => false,
360
+ :values => instances1
361
+ )
362
+ klass2.acts_as_lookup(
363
+ :sync_with_db => false,
364
+ :values => instances2
365
+ )
366
+
367
+ klass1.lookup_by_name('four').should == instances1.last
368
+ klass2.lookup_by_name('four').should == instances2.last
369
+ end
370
+
371
+ it "should keep acts as lookup options separate for different lookup classes" do
372
+ klass1 = ClassOnlyLookupClass
373
+ klass2 = SecondClassOnlyLookupClass
374
+ instances1 = [klass1.new(1, "one"), klass1.new(2, "two")]
375
+ instances2 = [klass2.new(3, "three"), klass2.new(4, "four")]
376
+ klass1.acts_as_lookup(
377
+ :sync_with_db => false,
378
+ :values => instances1
379
+ )
380
+ klass2.acts_as_lookup(
381
+ :sync_with_db => false,
382
+ :values => instances2
383
+ )
384
+
385
+ klass1.acts_as_lookup_options[:values].should == instances1
386
+ klass2.acts_as_lookup_options[:values].should == instances2
387
+ end
388
+ end
389
+
390
+ describe "when using has_lookup to associate classes" do
391
+ it "should not make a db hit to look up a single record when accessing a has_lookup accessor" do
392
+ DummyLookup.acts_as_lookup(
393
+ :values => [DummyLookup.one_instance],
394
+ :sync_with_db => false
395
+ )
396
+ klass = ClassWithLookupClass
397
+ klass.has_lookup :dummy_lookup
398
+ lookup_instance = DummyLookup.one_instance
399
+
400
+ instance = klass.new
401
+ instance.dummy_lookup_id = lookup_instance.id
402
+
403
+ instance.dummy_lookup.should == lookup_instance
404
+ end
405
+
406
+ it "should set the id attribute of an object that has_lookup when the whole-object setter is used" do
407
+ DummyLookup.acts_as_lookup(
408
+ :values => [DummyLookup.another_instance],
409
+ :sync_with_db => false
410
+ )
411
+ klass = ClassWithLookupClass
412
+ klass.has_lookup :dummy_lookup
413
+ lookup_instance = DummyLookup.one_instance
414
+
415
+ instance = klass.new
416
+ instance.dummy_lookup = lookup_instance
417
+
418
+ instance.dummy_lookup_id.should == lookup_instance.id
419
+ end
420
+
421
+ it "should allow overriding the class name on a has_lookup association" do
422
+ DummyLookup.acts_as_lookup(
423
+ :values => [DummyLookup.yet_another_instance],
424
+ :sync_with_db => false
425
+ )
426
+ klass = ClassWithLookupClass
427
+ klass.has_lookup :other_lookup, :class_name => 'DummyLookup'
428
+ lookup_instance = DummyLookup.one_instance
429
+
430
+ instance = klass.new
431
+ instance.other_lookup = lookup_instance
432
+
433
+ instance.other_lookup_id.should == lookup_instance.id
434
+ end
435
+ end
436
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), '../lib/acts_as_lookup')
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts_as_lookup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
5
11
  platform: ruby
6
12
  authors:
7
13
  - Brian Percival
@@ -9,7 +15,7 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-05-04 00:00:00 -07:00
18
+ date: 2011-02-23 00:00:00 -08:00
13
19
  default_executable:
14
20
  dependencies: []
15
21
 
@@ -20,7 +26,7 @@ description: |
20
26
  class-level methods to access singleton instances of each value in your lookup
21
27
  table.
22
28
 
23
- email: percivalatumamibuddotcom
29
+ email: percivalatdiscovereadsdotcom
24
30
  executables: []
25
31
 
26
32
  extensions: []
@@ -35,6 +41,8 @@ files:
35
41
  - lib/acts_as_lookup.rb
36
42
  - LICENSE
37
43
  - TODO
44
+ - spec/acts_as_lookup_spec.rb
45
+ - spec/spec_helper.rb
38
46
  has_rdoc: true
39
47
  homepage: http://github.com/bmpercy/acts_as_lookup
40
48
  licenses: []
@@ -45,23 +53,30 @@ rdoc_options:
45
53
  require_paths:
46
54
  - lib
47
55
  required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
48
57
  requirements:
49
58
  - - ">="
50
59
  - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
51
63
  version: "0"
52
- version:
53
64
  required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
54
66
  requirements:
55
67
  - - ">="
56
68
  - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
57
72
  version: "0"
58
- version:
59
73
  requirements: []
60
74
 
61
75
  rubyforge_project:
62
- rubygems_version: 1.3.5
76
+ rubygems_version: 1.3.7
63
77
  signing_key:
64
78
  specification_version: 3
65
79
  summary: Helpful for creating lookup-table-like models
66
- test_files: []
67
-
80
+ test_files:
81
+ - spec/acts_as_lookup_spec.rb
82
+ - spec/spec_helper.rb