assignable_values 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -103,6 +103,18 @@ Defaults can be lambdas:
103
103
 
104
104
  The lambda will be evaluated in the context of the record instance.
105
105
 
106
+ You can also default a secondary default that is only set if the primary default value is not assignable:
107
+
108
+ class Song < ActiveRecord::Base
109
+ assignable_values_for :year, :default => 1999, :secondary_default => lambda { Date.today.year } do
110
+ (Date.today.year - 2) .. Date.today.year
111
+ end
112
+ end
113
+
114
+ If called in 2013 the code above will fall back to:
115
+
116
+ Song.new.year # => 2013
117
+
106
118
 
107
119
  ### Allowing blank values
108
120
 
@@ -3,7 +3,7 @@ module AssignableValues
3
3
  module Restriction
4
4
  class Base
5
5
 
6
- attr_reader :model, :property, :options, :values, :default
6
+ attr_reader :model, :property, :options, :values, :default, :secondary_default
7
7
 
8
8
  def initialize(model, property, options, &values)
9
9
  @model = model
@@ -11,7 +11,7 @@ module AssignableValues
11
11
  @options = options
12
12
  @values = values
13
13
  ensure_values_given
14
- setup_default if default?
14
+ setup_default
15
15
  define_assignable_values_method
16
16
  setup_validation
17
17
  end
@@ -20,8 +20,9 @@ module AssignableValues
20
20
  value = current_value(record)
21
21
  unless allow_blank? && value.blank?
22
22
  begin
23
- assignable_values = assignable_values(record)
24
- assignable_values.include?(value) or record.errors.add(property, not_included_error_message)
23
+ unless assignable_value?(record, value)
24
+ record.errors.add(property, not_included_error_message)
25
+ end
25
26
  rescue DelegateUnavailable
26
27
  # if the delegate is unavailable, the validation is skipped
27
28
  end
@@ -32,6 +33,10 @@ module AssignableValues
32
33
  I18n.t('errors.messages.inclusion', :default => 'is not included in the list')
33
34
  end
34
35
 
36
+ def assignable_value?(record, value)
37
+ assignable_values(record).include?(value)
38
+ end
39
+
35
40
  def assignable_values(record)
36
41
  assignable_values = []
37
42
  old_value = previously_saved_value(record)
@@ -43,8 +48,13 @@ module AssignableValues
43
48
 
44
49
  def set_default(record)
45
50
  if record.new_record? && record.send(property).nil?
46
- default_value = default
47
- default_value = record.instance_eval(&default_value) if default_value.is_a?(Proc)
51
+ default_value = evaluate_default(record, default)
52
+ if secondary_default? && !assignable_value?(record, default_value)
53
+ secondary_default_value = evaluate_default(record, secondary_default)
54
+ if assignable_value?(record, secondary_default_value)
55
+ default_value = secondary_default_value
56
+ end
57
+ end
48
58
  record.send("#{property}=", default_value)
49
59
  end
50
60
  true
@@ -52,6 +62,14 @@ module AssignableValues
52
62
 
53
63
  private
54
64
 
65
+ def evaluate_default(record, value_or_proc)
66
+ if value_or_proc.is_a?(Proc)
67
+ record.instance_eval(&value_or_proc)
68
+ else
69
+ value_or_proc
70
+ end
71
+ end
72
+
55
73
  def parse_values(values)
56
74
  values.to_a
57
75
  end
@@ -76,6 +94,10 @@ module AssignableValues
76
94
  @options.has_key?(:default)
77
95
  end
78
96
 
97
+ def secondary_default?
98
+ @options.has_key?(:secondary_default)
99
+ end
100
+
79
101
  def allow_blank?
80
102
  @options[:allow_blank]
81
103
  end
@@ -89,15 +111,20 @@ module AssignableValues
89
111
  end
90
112
 
91
113
  def setup_default
92
- ensure_after_initialize_callback_enabled
93
- @default = options[:default]
94
- restriction = self
95
- enhance_model do
96
- set_default_method = "set_default_#{restriction.property}"
97
- define_method set_default_method do
98
- restriction.set_default(self)
114
+ if default?
115
+ @default = options[:default] # for attr_reader
116
+ @secondary_default = options[:secondary_default] # for attr_reader
117
+ ensure_after_initialize_callback_enabled
118
+ restriction = self
119
+ enhance_model do
120
+ set_default_method = "set_default_#{restriction.property}"
121
+ define_method set_default_method do
122
+ restriction.set_default(self)
123
+ end
124
+ after_initialize set_default_method
99
125
  end
100
- after_initialize set_default_method
126
+ elsif secondary_default?
127
+ raise AssignableValues::NoDefault, "cannot use the :secondary_default option without a :default option"
101
128
  end
102
129
  end
103
130
 
@@ -2,4 +2,5 @@ module AssignableValues
2
2
  class Error < StandardError; end
3
3
  class DelegateUnavailable < Error; end
4
4
  class NoValuesGiven < Error; end
5
+ class NoDefault < Error; end
5
6
  end
@@ -1,3 +1,3 @@
1
1
  module AssignableValues
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -261,6 +261,79 @@ describe AssignableValues::ActiveRecord do
261
261
 
262
262
  end
263
263
 
264
+ context 'with :secondary_default option' do
265
+
266
+ it 'should set a secondary default value if the primary value is not assignable' do
267
+ klass = disposable_song_class do
268
+ assignable_values_for :genre, :default => 'techno', :secondary_default => 'rock' do
269
+ %w[pop rock]
270
+ end
271
+ end
272
+ klass.new.genre.should == 'rock'
273
+ end
274
+
275
+ it 'should not change the default value if the default value is assignable' do
276
+ klass = disposable_song_class do
277
+ assignable_values_for :genre, :default => 'pop', :secondary_default => 'rock' do
278
+ %w[pop rock]
279
+ end
280
+ end
281
+ klass.new.genre.should == 'pop'
282
+ end
283
+
284
+ it "should not change the primary default if the secondary default value isn't assignable either" do
285
+ klass = disposable_song_class do
286
+ assignable_values_for :genre, :default => 'techno', :secondary_default => 'jazz' do
287
+ %w[pop rock]
288
+ end
289
+ end
290
+ klass.new.genre.should == 'techno'
291
+ end
292
+
293
+ it 'should raise an error if used without a :default option' do
294
+ expect do
295
+ disposable_song_class do
296
+ assignable_values_for :genre, :secondary_default => 'pop' do
297
+ %w[pop rock]
298
+ end
299
+ end
300
+ end.to raise_error(AssignableValues::NoDefault)
301
+ end
302
+
303
+ it 'should allow to set a secondary default through a lambda' do
304
+ klass = disposable_song_class do
305
+ assignable_values_for :genre, :default => 'techno', :secondary_default => lambda { 'pop' } do
306
+ %w[pop rock]
307
+ end
308
+ end
309
+ klass.new.genre.should == 'pop'
310
+ end
311
+
312
+ it 'should evaluate a secondary lambda default in the context of the record instance' do
313
+ klass = disposable_song_class do
314
+ assignable_values_for :genre, :default => 'techno', :secondary_default => lambda { default_genre } do
315
+ %w[pop rock]
316
+ end
317
+ def default_genre
318
+ 'pop'
319
+ end
320
+ end
321
+ klass.new.genre.should == 'pop'
322
+ end
323
+
324
+ it 'should not cause the list of assignable values to be evaluated if the :secondary_default option is not used' do
325
+ klass = disposable_song_class do
326
+ assignable_values_for :genre, :default => 'techno' do
327
+ raise "block called!"
328
+ end
329
+ end
330
+ expect do
331
+ klass.new.genre.should == 'techno'
332
+ end.to_not raise_error
333
+ end
334
+
335
+ end
336
+
264
337
  context 'when generating methods to list assignable values' do
265
338
 
266
339
  it 'should generate an instance method returning a list of assignable values' do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: assignable_values
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 0.3.0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Henning Koch
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-07-31 00:00:00 +02:00
18
+ date: 2012-08-02 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency