simple_model 1.2.5 → 1.2.7

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -2,5 +2,4 @@ source "http://rubygems.org"
2
2
 
3
3
 
4
4
  # Specify your gem's dependencies in simple_model.gemspec
5
- gemspec
6
-
5
+ gemspec
@@ -1,13 +1,15 @@
1
- require 'simple_model/exceptions'
2
1
  module SimpleModel
3
2
  module Attributes
4
3
  include ExtendCore
5
4
  extend ActiveSupport::Concern
6
5
  include ActiveModel::AttributeMethods
7
-
6
+
8
7
  def initialize(*attrs)
9
8
  attrs = attrs.extract_options!
10
- set(attributes_with_for_init(attrs))
9
+ attrs = attributes_with_for_init(attrs)
10
+ attrs = self.class.before_initialize.call(self,attrs) if self.class.before_initialize
11
+ set(attrs)
12
+ self.class.after_initialize.call(self) if self.class.after_initialize
11
13
  end
12
14
 
13
15
  # Returns true if attribute has been initialized
@@ -59,6 +61,35 @@ module SimpleModel
59
61
  def allow_set_default?(d,k,v)
60
62
  (v[:default] && v[:initialize] && (d[k].blank? && (self.class.alias_attributes[k].blank? || d.key?(self.class.alias_attributes[k]) && d[self.class.alias_attributes[k]].blank?)))
61
63
  end
64
+
65
+ private
66
+
67
+ def allow_attribute_action?(obj,val,options)
68
+ return true if (options[:if].blank? && options[:unless].blank?)
69
+ b = true
70
+ if options[:if].is_a?(Symbol)
71
+ if options[:if] == :blank
72
+ b = (b && val.blank?)
73
+ else
74
+ b = (b && send(options[:if]))
75
+ end
76
+ end
77
+ b = (b && options[:if].call(obj,val)) if options[:if].is_a?(Proc)
78
+ if options[:unless].is_a?(Symbol)
79
+ if options[:unless] == :blank
80
+ b = (b && !val.blank?)
81
+ else
82
+ b = (b && !send(options[:unless]))
83
+ end
84
+ end
85
+ b = (b && !options[:unless].call(obj,val)) if options[:unless].is_a?(Proc)
86
+ b
87
+ end
88
+
89
+ # Rails 3.2 + required when searching for attributes in from inherited classes/cmodles
90
+ def attribute(name)
91
+ attributes[name.to_sym]
92
+ end
62
93
 
63
94
  module ClassMethods
64
95
  # Creates a new instance where the attributes store is set to object
@@ -123,8 +154,8 @@ module SimpleModel
123
154
  # re-run to occur ONLY IN RAILS 3.0.
124
155
  def add_defined_attribute(attr,options)
125
156
  self.defined_attributes[attr] = options
126
- @attribute_methods_generated = nil if (ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR == 0)
127
- define_attribute_methods self.defined_attributes.keys
157
+ @attribute_methods_generated = nil #if (ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR == 0)
158
+ define_attribute_methods(self.defined_attributes.keys)
128
159
  end
129
160
 
130
161
  # builds the setter and getter methods
@@ -141,10 +172,11 @@ module SimpleModel
141
172
  add_defined_attribute(attr,options)
142
173
  options = default_attribute_settings.merge(options) if options[:on_get].blank?
143
174
  define_method(attr) do
144
- if (options.key?(:default) && (!self.initialized?(attr) || (!options[:allow_blank] && self.attributes[attr].blank?)))
145
- self.attributes[attr] = fetch_default_value(options[:default])
175
+ val = self.attributes[attr]
176
+ if (options.key?(:default) && (!self.initialized?(attr) || (!options[:allow_blank] && val.blank?)))
177
+ val = self.attributes[attr] = fetch_default_value(options[:default])
146
178
  end
147
- options[:on_get].call(self,self.attributes[attr])
179
+ options[:on_get].call(self,val)
148
180
  end
149
181
  define_method("#{attr.to_s}?") do
150
182
  val = self.send(attr)
@@ -164,16 +196,14 @@ module SimpleModel
164
196
  add_defined_attribute(attr,options)
165
197
  options = default_attribute_settings.merge(options) if (options[:on_set].blank? || options[:after_set].blank?)
166
198
  define_method("#{attr.to_s}=") do |val|
167
- val = fetch_default_value(options[:default]) if (!options[:allow_blank] && options.key?(:default) && val.blank?)
168
- begin
169
- val = options[:on_set].call(self,val)
170
- rescue NoMethodError => e
171
- raise ArgumentError, "#{val} could not be set for #{attr}: #{e.message}"
199
+ if allow_attribute_action?(self,val,options)
200
+ val = fetch_default_value(options[:default]) if (!options[:allow_blank] && options.key?(:default) && val.blank?)
201
+ val = options[:on_set].call(self,val) unless (val.blank? && !options[:allow_blank] )
202
+ will_change = "#{attr}_will_change!".to_sym
203
+ self.send(will_change) if (initialized?(attr) && val != self.attributes[attr])
204
+ self.attributes[attr] = val
205
+ options[:after_set].call(self,val) if options[:after_set]
172
206
  end
173
- will_change = "#{attr}_will_change!".to_sym
174
- self.send(will_change) if (initialized?(attr) && val != self.attributes[attr])
175
- self.attributes[attr] = val
176
- options[:after_set].call(self,val) if options[:after_set]
177
207
  end
178
208
  end
179
209
 
@@ -181,14 +211,14 @@ module SimpleModel
181
211
  :has_attribute => {:alias => :has_attributes},
182
212
  :has_boolean => {:cast_to => :to_b, :alias => :has_booleans},
183
213
  :has_currency => {:cast_to => :to_d, :alias => :has_currencies},
184
- :has_date => {:cast_to => :to_date, :alias => :has_dates},
214
+ :has_date => {:cast_to => :to_date, :alias => :has_dates} ,
185
215
  :has_decimal => {:cast_to => :to_d, :alias => :has_decimals},
186
216
  :has_float => {:cast_to => :to_f, :alias => :has_floats},
187
217
  :has_int => {:cast_to => :to_i, :alias => :has_ints},
188
218
  :has_time => {:cast_to => :to_time, :alias => :has_times}
189
219
  }
190
220
 
191
- AVAILABLE_ATTRIBUTE_METHODS.each do |method,method_options|
221
+ AVAILABLE_ATTRIBUTE_METHODS.each do |method,method_options|
192
222
  define_method(method) do |*attributes|
193
223
  options = default_attribute_settings.merge(attributes.extract_options!)
194
224
  options[:on_set] = lambda {|obj,val| val.send(method_options[:cast_to]) } if method_options[:cast_to]
@@ -209,20 +239,57 @@ module SimpleModel
209
239
  end
210
240
  end
211
241
 
242
+ # A hook to perform actions on the pending attributes or the object before
243
+ # the pending attributes have been initialized.
244
+ # Expects an lambda that accept the object, the pending attributes hash and
245
+ # should return a hash to be set
246
+ # EX: lambda {|obj,attrs| attrs.select{|k,v| !v.blank?}}
247
+ def before_initialize
248
+ @before_initialize
249
+ end
250
+
251
+ # Expects an lambda that accept the object, the pending attributes hash and
252
+ # should return a hash to be set
253
+ # EX: lambda {|obj,attrs| attrs.select{|k,v| !v.blank?}}
254
+ def before_initialize=before_initialize
255
+ raise TypeError "before_initialize must be a lambda that accepts the attirbutes to be initialize" unless before_initialize.is_a?(Proc)
256
+ @before_initialize = before_initialize
257
+ end
258
+
259
+ # A hook to perform actions after all attributes have been initialized
260
+ # Expects an lambda that accept the object and the pending attributes hash
261
+ # EX: lambda{|obj| puts "initialized"}
262
+ def after_initialize
263
+ @after_initialize
264
+ end
265
+
266
+ # Expects an lambda that accept the object and the pending attributes hash
267
+ # EX: lambda{|obj| puts "initialized"}
268
+ def after_initialize=after_initialize
269
+ raise TypeError "after_initalize must be a Proc" unless after_initialize.is_a?(Proc)
270
+ @after_initialize = after_initialize
271
+ end
272
+
273
+
274
+
212
275
  # Must inherit super's defined_attributes and alias_attributes
213
276
  # Rails 3.0 does some weird stuff with ActiveModel::Dirty so we need a
214
277
  # hack to keep things working when a class in inherits from a super that
215
278
  # has ActiveModel::Dirty included
216
279
  def inherited(base)
217
280
  # Rails 3.0 Hack
218
- if (ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR == 0)
219
- base.send(:include, ActiveModel::Dirty)
281
+ if (ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR == 0)
220
282
  base.attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
221
283
  base.attribute_method_affix :prefix => 'reset_', :suffix => '!'
222
284
  end
285
+ base.send(:include, ActiveModel::Dirty)
286
+
287
+ self.defined_attributes.each do |attr,options|
288
+ base.create_attribute_methods([attr],options)
289
+ end
223
290
 
224
- base.defined_attributes = self.defined_attributes.merge(base.defined_attributes)
225
291
  base.alias_attributes = self.alias_attributes.merge(base.alias_attributes )
292
+ super
226
293
  end
227
294
  end
228
295
 
@@ -240,8 +307,8 @@ module SimpleModel
240
307
 
241
308
  # Rails 3.0 Hack
242
309
  if (ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR == 0)
243
- base.attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
244
- base.attribute_method_affix :prefix => 'reset_', :suffix => '!'
310
+ base.attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
311
+ base.attribute_method_affix :prefix => 'reset_', :suffix => '!'
245
312
  end
246
313
  end
247
314
  end
@@ -154,7 +154,7 @@ require 'active_support/core_ext/object/blank'
154
154
  default_options.merge!(methods.extract_options!)
155
155
  actions = [action,"#{action}!".to_sym]
156
156
  actions.each do |a|
157
- define_method(a) do |opts={}|
157
+ define_method(a) do |opts = {}|
158
158
  options = default_options.merge(opts)
159
159
  options[:raise_exception] = a.to_s.match(/\!$/)
160
160
  self.run_callbacks(action) do
@@ -1,5 +1,4 @@
1
1
  module SimpleModel
2
2
  class ActionError < StandardError; end
3
3
  class ValidationError < StandardError; end
4
- class ArgumentError < StandardError; end
5
4
  end
@@ -1,3 +1,3 @@
1
1
  module SimpleModel
2
- VERSION = "1.2.5"
2
+ VERSION = "1.2.7"
3
3
  end
@@ -9,7 +9,7 @@ describe SimpleModel::Attributes do
9
9
  @init = TestInit.new(:test1 => "1", :test2 => '2')
10
10
  end
11
11
 
12
- it "should set provided attributes on initialize" do
12
+ it "should set provided attributes on initialize" do
13
13
  @init.test1.should eql("1")
14
14
  @init.test2.should eql("2")
15
15
  end
@@ -19,7 +19,51 @@ describe SimpleModel::Attributes do
19
19
  @init.attributes[:test1].should eql("1")
20
20
  @init.attributes[:test2].should eql("2")
21
21
  end
22
-
22
+
23
+ context '#before_initialize' do
24
+ before(:all) do
25
+ class TestInit
26
+ include SimpleModel::Attributes
27
+ # Do not initalize blank attributes
28
+ self.before_initialize = lambda {|obj,attrs| attrs.select{|k,v| !v.blank?}}
29
+ has_attribute :far
30
+ end
31
+ end
32
+
33
+ it "should raise an exception if we try to set to something other than a Proc" do
34
+ lambda {TestInit.before_initialize = "bad stuff"}.should raise_error
35
+ end
36
+
37
+ it "should run the supplied lambda" do
38
+ t = TestInit.new(:far => "")
39
+ t.initialized?(:far).should be_false
40
+ t = TestInit.new(:far => "t")
41
+ t.initialized?(:far).should be_true
42
+ end
43
+
44
+ end
45
+
46
+ context '#after_initialize' do
47
+ before(:all) do
48
+ class TestInit
49
+ include SimpleModel::Attributes
50
+ # Do not initalize blank attributes
51
+ self.after_initialize = lambda { |obj| obj.car = "test" if obj.car.blank?}
52
+ has_attribute :car
53
+ end
54
+ end
55
+
56
+ it "should raise an exception if we try to set to something other than a Proc" do
57
+ lambda {TestInit.after_initialize = "bad stuff"}.should raise_error
58
+ end
59
+
60
+ it "should run the supplied lambda" do
61
+ t = TestInit.new(:far => "")
62
+ t.car.should eql("test")
63
+ end
64
+
65
+ end
66
+
23
67
  context '#new_with_store'do
24
68
  it "should use the provided object as the attribute store" do
25
69
  my_store = {:test1 => 1,:test2 => 2}
@@ -29,7 +73,7 @@ describe SimpleModel::Attributes do
29
73
  my_store[:test1].should eql(new.test1)
30
74
  end
31
75
  end
32
-
76
+
33
77
  context "AVAILABLE_ATTRIBUTE_METHODS" do
34
78
  SimpleModel::Attributes::ClassMethods::AVAILABLE_ATTRIBUTE_METHODS.each do |m,options|
35
79
  it "should respond to #{m}" do
@@ -55,14 +99,14 @@ describe SimpleModel::Attributes do
55
99
  def default_value
56
100
  "bar"
57
101
  end
58
-
102
+
59
103
  def default_hop
60
104
  "hop" if nap
61
105
  end
62
106
  end
63
-
107
+
64
108
  end
65
-
109
+
66
110
  before(:each) do
67
111
  @default = TestDefault.new
68
112
  end
@@ -70,11 +114,11 @@ describe SimpleModel::Attributes do
70
114
  it "should define setter method" do
71
115
  @default.respond_to?(:foo=).should be_true
72
116
  end
73
-
117
+
74
118
  it "should define reader/getter method" do
75
119
  @default.respond_to?(:foo).should be_true
76
120
  end
77
-
121
+
78
122
  context ':initialize => false' do
79
123
  it "should not initialize with the default value" do
80
124
  @default.attributes[:tip].should be_nil
@@ -87,30 +131,30 @@ describe SimpleModel::Attributes do
87
131
  end
88
132
  end
89
133
  end
90
-
91
- it "should call the method it describe by the default value if it exists" do
134
+
135
+ it "should call the method it describe by the default value if it exists" do
92
136
  @default.attributes[:bar].should eql("bar")
93
137
  end
94
-
95
- it "should set the defaul to the supplied symbol, if the method does not exist" do
138
+
139
+ it "should set the defaul to the supplied symbol, if the method does not exist" do
96
140
  @default.attributes[:fab].should eql(:some_symbol)
97
141
  end
98
-
142
+
99
143
  it "should allow default value to be an empty array" do
100
144
  @default.my_array.should eql([])
101
145
  end
102
-
146
+
103
147
  it "should create a boolean? method for each attribute" do
104
148
  @default.respond_to?(:foo?).should be_true
105
149
  end
106
-
150
+
107
151
  it "should return !blank?" do
108
152
  @default.my_array.should eql([]) # blank array
109
153
  @default.my_array?.should be_false
110
154
  @default.my_array << 1
111
155
  @default.my_array?.should be_true
112
156
  end
113
-
157
+
114
158
  it "should not allow blank if set" do
115
159
  @default.foo.should eql("foo")
116
160
  @default.foo = ""
@@ -118,51 +162,61 @@ describe SimpleModel::Attributes do
118
162
  @default.foo = "not blank"
119
163
  @default.foo.should eql("not blank")
120
164
  end
121
-
165
+
122
166
  it "should try for the default if its blank on get" do
123
167
  @default.hop.blank?.should be_true
124
168
  @default.nap = "yep"
125
169
  @default.hop.should eql("hop")
126
170
  end
127
-
128
-
129
171
  end
130
-
172
+
173
+ context 'options with conditional' do
174
+ before(:all) do
175
+ class WithConditional
176
+ include SimpleModel::Attributes
177
+ has_date :my_date, :if => lambda {|obj,val| !val.blank?}
178
+ has_date :my_other_date, :unless => :blank
179
+ end
180
+ end
181
+ it "should not raise error" do
182
+ new = WithConditional.new(:my_date => nil)
183
+ new.initialized?(:my_date).should be_false
184
+ end
185
+
186
+ it "should call blank on val if :blank is supplied" do
187
+ new = WithConditional.new(:my_other_date => nil)
188
+ new.initialized?(:my_other_date).should be_false
189
+ end
190
+ end
191
+
131
192
  context "on get" do
132
193
  it "should perform on_get when set" do
133
194
  class OnGet
134
195
  include SimpleModel::Attributes
135
196
  has_attribute :foo, :on_get => lambda{|obj,attr| (attr.blank? ? obj.send(:foo_default) : attr)}
136
-
197
+
137
198
  def foo_default
138
199
  "test"
139
200
  end
140
201
  end
141
-
202
+
142
203
  new = OnGet.new
143
204
  new.foo.should eql("test")
144
205
  new.foo = "foo"
145
206
  new.foo.should eql("foo")
146
207
  end
147
208
  end
148
-
209
+
149
210
  context 'if supplied value can be cast' do
150
- it "should throw an exception" do
151
- class TestThrow
211
+ before(:all) do
212
+ class TestAlias
152
213
  include SimpleModel::Attributes
153
- has_booleans :boo
154
- end
155
-
156
- lambda{TestThrow.new(:boo => [])}.should raise_error(SimpleModel::ArgumentError)
214
+ has_attribute :foo, :default => "bar"
215
+ alias_attribute(:bar,:foo)
216
+ end
157
217
  end
158
218
  context '#alias_attribute' do
159
219
  it "should create alias for attribute" do
160
- class TestAlias
161
- include SimpleModel::Attributes
162
- has_attribute :foo, :default => "bar"
163
- alias_attribute(:bar,:foo)
164
- end
165
-
166
220
  t = TestAlias.new(:bar => "foo")
167
221
  t.bar.should eql("foo")
168
222
  t.foo.should eql('foo')
@@ -170,29 +224,49 @@ describe SimpleModel::Attributes do
170
224
  t.bar.should eql("foo")
171
225
  t.foo.should eql('foo')
172
226
  end
173
- end
227
+ end
174
228
  end
175
-
229
+
176
230
  context "regression tests" do
177
- it "should merge defined attributes when class are inhereted" do
231
+ before(:all) do
178
232
  class MyBase
179
233
  include SimpleModel::Attributes
180
234
  has_boolean :bar
235
+ has_attribute :str
236
+ has_dates :some, :thing, :default => :fetch_date, :allow_blank => false, :initialize => false
237
+
238
+ def fetch_date
239
+ Date.today
240
+ end
181
241
  end
182
-
242
+
183
243
  class NewerBase < MyBase
184
244
  has_boolean :foo
245
+ has_int :str
185
246
  end
186
-
247
+ end
248
+ it "should merge defined attributes when class are inhereted" do
187
249
  NewerBase.defined_attributes[:bar].blank?.should be_false
188
250
  n = NewerBase.new
189
251
  n.respond_to?(:bar_will_change!).should be_true
190
252
  end
253
+
254
+ it "should defaults that were not initialized should work from parent class" do
255
+ n = NewerBase.new
256
+ n.some.should eql(Date.today)
257
+ n.thing.should eql(Date.today)
258
+ end
259
+
260
+ it "should allow redefining methods in child classes" do
261
+ n = NewerBase.new
262
+ n.str = '1'
263
+ n.str.should eql(1)
264
+ end
191
265
  end
192
-
266
+
193
267
  after(:all) do
194
- [:TestThrow,:OnGet,:TestDefault,:TestInit,:MyBase,:NewerBase].each do |test_klass|
195
- Object.send(:remove_const,test_klass)
268
+ [:OnGet,:TestDefault,:TestInit,:MyBase,:NewerBase].each do |test_klass|
269
+ Object.send(:remove_const,test_klass) if defined?(test_klass)
196
270
  end
197
271
  end
198
- end
272
+ end
@@ -204,15 +204,18 @@ describe SimpleModel do
204
204
  before(:each) do
205
205
  class TestStuff < SimpleModel::Base
206
206
  has_attribute :bar
207
+ validates_presence_of :bar
207
208
  end
208
209
 
209
- class OtherStuff < SimpleModel::Base
210
- has_attribute :bar
211
- end
212
-
213
210
  class NewTestStuff < TestStuff
214
211
  has_boolean :foo
215
212
  end
213
+
214
+ class OtherStuff < NewTestStuff
215
+ has_attribute :other
216
+ validates_numericality_of :other
217
+ end
218
+
216
219
  end
217
220
  it "should merge defined attributes when class are inhereted" do
218
221
  NewTestStuff.defined_attributes[:bar].blank?.should be_false
@@ -225,8 +228,16 @@ describe SimpleModel do
225
228
  NewTestStuff.new.respond_to?(:bar_will_change!).should be_true
226
229
  NewTestStuff.new.respond_to?(:foo_will_change!).should be_true
227
230
  end
231
+
232
+ it "should not throw exception method missing" do
233
+ o = OtherStuff.new
234
+ lambda { o.valid? }.should_not raise_error
235
+ end
236
+
228
237
  after(:each) do
229
- Object.send(:remove_const,:NewTestStuff)
238
+ [:OtherStuff,:NewTestStuff].each do |con|
239
+ Object.send(:remove_const,con)
240
+ end
230
241
  end
231
242
  end
232
243
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.5
4
+ version: 1.2.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-22 00:00:00.000000000 Z
12
+ date: 2013-02-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -127,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
127
  version: '0'
128
128
  requirements: []
129
129
  rubyforge_project: simple_model
130
- rubygems_version: 1.8.24
130
+ rubygems_version: 1.8.25
131
131
  signing_key:
132
132
  specification_version: 3
133
133
  summary: Simpifies building tableless models or models backed by webservices