representable 1.1.7 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,9 @@
1
- h2. 1.1.7
1
+ h2. 1.2.0
2
+
3
+ * Deprecated @:except@ in favor of @:exclude@.
4
+ * A property with @false@ value will now be included in the rendered representation. Same applies to parsing, @false@ values will now be included. That particularly means properties that used to be unset (i.e. @nil@) after parsing might be @false@ now.
5
+ * You can include @nil@ values now in your representations since @#property@ respects @:represent_nil => true@.
2
6
 
3
- * Added support for coercion using virtus. Note that this presently works on class level with inline representers, only.
4
-
5
7
  h2. 1.1.6
6
8
 
7
9
  * Added @:if@ option to @property@.
@@ -247,7 +247,15 @@ You can also define conditions on properties on the class layer.
247
247
  property :friends, :if => lambda { forename == "Peter" }
248
248
  end
249
249
 
250
- When rendering or parsing, the +friends+ property is considered only if the condition block evals to true. Note that the block is executed in instance context, giving you access to instance methods.
250
+ When rendering or parsing, the +friends+ property is considered only if the condition block evals to true. Note that the block is executed in instance context, giving you access to instance methods.
251
+
252
+ === False and Nil Values
253
+
254
+ Since 1.2 +false+ values are considered when parsing and rendering. That particularly means properties that used to be unset (i.e. @nil@) after parsing might be @false@ now. Vice versa, +false+ values that weren't included in the rendered document will be visible now.
255
+
256
+ If you want +nil+ values to be included when rendering, use the +:represent_nil+ option.
257
+
258
+ property :surename, :represent_nil => true
251
259
 
252
260
  == DCI
253
261
 
@@ -1,4 +1,5 @@
1
1
  require 'representable/definition'
2
+ require 'representable/deprecations'
2
3
 
3
4
  # Representable can be used in two ways.
4
5
  #
@@ -29,6 +30,7 @@ module Representable
29
30
 
30
31
  def self.included(base)
31
32
  base.class_eval do
33
+ include Deprecations
32
34
  extend ClassMethods
33
35
  extend ClassMethods::Declarations
34
36
  extend ClassMethods::Accessors
@@ -67,14 +69,13 @@ private
67
69
 
68
70
  # Checks and returns if the property should be included.
69
71
  def skip_property?(binding, options)
70
- return true if skip_excluded_property?(binding, options) # no need for further evaluation when :except'ed
72
+ return true if skip_excluded_property?(binding, options) # no need for further evaluation when :exclude'ed
71
73
 
72
74
  skip_conditional_property?(binding)
73
75
  end
74
76
 
75
77
  def skip_excluded_property?(binding, options)
76
- return unless props = options[:except] || options[:include]
77
- props = options[:except] || options[:include]
78
+ return unless props = options[:exclude] || options[:include]
78
79
  res = props.include?(binding.definition.name.to_sym)
79
80
  options[:include] ? !res : res
80
81
  end
@@ -86,18 +87,22 @@ private
86
87
 
87
88
  # Retrieve value and write fragment to the doc.
88
89
  def compile_fragment(bin, doc)
89
- value = send(bin.definition.getter) || bin.definition.default # DISCUSS: eventually move back to Ref.
90
+ value = send(bin.definition.getter)
91
+ value = bin.definition.default_for(value)
92
+
90
93
  write_fragment_for(bin, value, doc)
91
94
  end
92
95
 
93
96
  # Parse value from doc and update the model property.
94
97
  def uncompile_fragment(bin, doc)
95
- value = read_fragment_for(bin, doc) || bin.definition.default
98
+ value = read_fragment_for(bin, doc)
99
+ value = bin.definition.default_for(value)
100
+
96
101
  send(bin.definition.setter, value)
97
102
  end
98
103
 
99
104
  def write_fragment_for(bin, value, doc) # DISCUSS: move to Binding?
100
- return unless value
105
+ return if bin.definition.skipable_nil_value?(value)
101
106
  bin.write(doc, value)
102
107
  end
103
108
 
@@ -140,6 +145,7 @@ private
140
145
  # property :name, :from => :title
141
146
  # property :name, :class => Name
142
147
  # property :name, :default => "Mike"
148
+ # property :name, :include_nil => true
143
149
  def property(name, options={})
144
150
  representable_attrs << definition_class.new(name, options)
145
151
  end
@@ -1,7 +1,7 @@
1
1
  module Representable
2
2
  class Binding
3
- attr_reader :definition
4
-
3
+ attr_reader :definition # TODO: merge Binding and Definition.
4
+
5
5
  def initialize(definition)
6
6
  @definition = definition
7
7
  end
@@ -37,9 +37,9 @@ module Representable
37
37
  (options[:from] || name).to_s
38
38
  end
39
39
 
40
- def default
41
- options[:default] ||= [] if array? # FIXME: move to CollectionBinding!
42
- options[:default]
40
+ def default_for(value)
41
+ return default if skipable_nil_value?(value)
42
+ value
43
43
  end
44
44
 
45
45
  def representer_module
@@ -49,5 +49,15 @@ module Representable
49
49
  def attribute
50
50
  options[:attribute]
51
51
  end
52
+
53
+ def skipable_nil_value?(value)
54
+ value.nil? and not options[:represent_nil]
55
+ end
56
+
57
+ private
58
+ def default
59
+ options[:default] ||= [] if array? # FIXME: move to CollectionBinding!
60
+ options[:default]
61
+ end
52
62
  end
53
63
  end
@@ -0,0 +1,9 @@
1
+ module Representable::Deprecations
2
+ def skip_excluded_property?(binding, options) # TODO: remove with 1.3.
3
+ if options[:except]
4
+ options[:exclude] = options[:except]
5
+ warn "The :except option is deprecated and will be removed in 1.3. Please use :exclude."
6
+ end # i wanted a one-liner but failed :)
7
+ super
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "1.1.7"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -55,6 +55,62 @@ class DefinitionTest < MiniTest::Spec
55
55
  end
56
56
  end
57
57
 
58
+ describe "#skipable_nil_value?" do
59
+ # default if skipable_nil_value?
60
+ before do
61
+ @def = Representable::Definition.new(:song, :represent_nil => true)
62
+ end
63
+
64
+ it "returns false when not nil" do
65
+ assert_equal false, @def.skipable_nil_value?("Disconnect, Disconnect")
66
+ end
67
+
68
+ it "returns false when nil and :represent_nil => true" do
69
+ assert_equal false, @def.skipable_nil_value?(nil)
70
+ end
71
+
72
+ it "returns true when nil and :represent_nil => false" do
73
+ assert_equal true, Representable::Definition.new(:song).skipable_nil_value?(nil)
74
+ end
75
+
76
+ it "returns false when not nil and :represent_nil => false" do
77
+ assert_equal false, Representable::Definition.new(:song).skipable_nil_value?("Fatal Flu")
78
+ end
79
+ end
80
+
81
+
82
+ describe "#default_for" do
83
+ before do
84
+ @def = Representable::Definition.new(:song, :default => "Insider")
85
+ end
86
+
87
+ it "always returns value when value not nil" do
88
+ assert_equal "Black And Blue", @def.default_for("Black And Blue")
89
+ end
90
+
91
+ it "returns false when value false" do
92
+ assert_equal false, @def.default_for(false)
93
+ end
94
+
95
+ it "returns default when value nil" do
96
+ assert_equal "Insider", @def.default_for(nil)
97
+ end
98
+
99
+ it "returns nil when value nil and :represent_nil true" do
100
+ @def = Representable::Definition.new(:song, :represent_nil => true)
101
+ assert_equal nil, @def.default_for(nil)
102
+ end
103
+
104
+ it "returns nil when value nil and :represent_nil true even when :default is set" do
105
+ @def = Representable::Definition.new(:song, :represent_nil => true, :default => "The Quest")
106
+ assert_equal nil, @def.default_for(nil)
107
+ end
108
+
109
+ it "returns nil if no :default" do
110
+ @def = Representable::Definition.new(:song)
111
+ assert_equal nil, @def.default_for(nil)
112
+ end
113
+ end
58
114
 
59
115
  describe ":collection => true" do
60
116
  before do
@@ -70,7 +126,7 @@ class DefinitionTest < MiniTest::Spec
70
126
  end
71
127
 
72
128
  it "responds to #default" do
73
- assert_equal [], @def.default
129
+ assert_equal [], @def.send(:default)
74
130
  end
75
131
  end
76
132
 
@@ -87,12 +143,12 @@ class DefinitionTest < MiniTest::Spec
87
143
  describe ":default => value" do
88
144
  it "responds to #default" do
89
145
  @def = Representable::Definition.new(:song)
90
- assert_equal nil, @def.default
146
+ assert_equal nil, @def.send(:default)
91
147
  end
92
148
 
93
149
  it "accepts a default value" do
94
150
  @def = Representable::Definition.new(:song, :default => "Atheist Peace")
95
- assert_equal "Atheist Peace", @def.default
151
+ assert_equal "Atheist Peace", @def.send(:default)
96
152
  end
97
153
  end
98
154
 
@@ -229,12 +229,16 @@ class RepresentableTest < MiniTest::Spec
229
229
  assert_equal 2, @band.groupies
230
230
  end
231
231
 
232
- it "accepts :except option" do
233
- @band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:except => [:groupies]}, Representable::JSON)
232
+ it "accepts :exclude option" do
233
+ @band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:exclude => [:groupies]}, Representable::JSON)
234
234
  assert_equal "No One's Choice", @band.name
235
235
  assert_equal nil, @band.groupies
236
236
  end
237
237
 
238
+ it "still accepts deprecated :except option" do # FIXME: remove :except option.
239
+ assert_equal @band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:except => [:groupies]}, Representable::JSON), @band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:exclude => [:groupies]}, Representable::JSON)
240
+ end
241
+
238
242
  it "accepts :include option" do
239
243
  @band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:include => [:groupies]}, Representable::JSON)
240
244
  assert_equal 2, @band.groupies
@@ -245,47 +249,10 @@ class RepresentableTest < MiniTest::Spec
245
249
  assert_equal @band, @band.update_properties_from({"name"=>"Nofx"}, {}, Representable::JSON)
246
250
  end
247
251
 
248
- describe ":if" do
249
- before do
250
- @pop = Class.new(PopBand) { attr_accessor :fame }
251
- end
252
-
253
- it "respects property when condition true" do
254
- @pop.class_eval { property :fame, :if => lambda { true } }
255
- band = @pop.new
256
- band.update_properties_from({"fame"=>"oh yes"}, {}, Representable::JSON)
257
- assert_equal "oh yes", band.fame
258
- end
259
-
260
- it "ignores property when condition false" do
261
- @pop.class_eval { property :fame, :if => lambda { false } }
262
- band = @pop.new
263
- band.update_properties_from({"fame"=>"oh yes"}, {}, Representable::JSON)
264
- assert_equal nil, band.fame
265
- end
266
-
267
- it "ignores property when :except'ed even when condition is true" do
268
- @pop.class_eval { property :fame, :if => lambda { true } }
269
- band = @pop.new
270
- band.update_properties_from({"fame"=>"oh yes"}, {:except => [:fame]}, Representable::JSON)
271
- assert_equal nil, band.fame
272
- end
273
-
274
-
275
- it "executes block in instance context" do
276
- @pop.class_eval { property :fame, :if => lambda { groupies } }
277
- band = @pop.new
278
- band.groupies = true
279
- band.update_properties_from({"fame"=>"oh yes"}, {}, Representable::JSON)
280
- assert_equal "oh yes", band.fame
281
- end
282
-
283
- end
284
-
285
- it "what" do
286
-
252
+ it "includes false attributes" do
253
+ @band.update_properties_from({"groupies"=>false}, {}, Representable::JSON)
254
+ assert_equal false, @band.groupies
287
255
  end
288
-
289
256
  end
290
257
 
291
258
  describe "#create_representation_with" do
@@ -299,17 +266,79 @@ class RepresentableTest < MiniTest::Spec
299
266
  assert_equal({"name"=>"No One's Choice", "groupies"=>2}, @band.send(:create_representation_with, {}, {}, Representable::JSON))
300
267
  end
301
268
 
302
- it "accepts :except option" do
303
- hash = @band.send(:create_representation_with, {}, {:except => [:groupies]}, Representable::JSON)
269
+ it "accepts :exclude option" do
270
+ hash = @band.send(:create_representation_with, {}, {:exclude => [:groupies]}, Representable::JSON)
304
271
  assert_equal({"name"=>"No One's Choice"}, hash)
305
272
  end
306
273
 
274
+ it "still accepts deprecated :except option" do # FIXME: remove :except option.
275
+ assert_equal @band.send(:create_representation_with, {}, {:except => [:groupies]}, Representable::JSON), @band.send(:create_representation_with, {}, {:exclude => [:groupies]}, Representable::JSON)
276
+ end
277
+
307
278
  it "accepts :include option" do
308
279
  hash = @band.send(:create_representation_with, {}, {:include => [:groupies]}, Representable::JSON)
309
280
  assert_equal({"groupies"=>2}, hash)
310
281
  end
282
+
283
+ it "does not write nil attributes" do
284
+ @band.groupies = nil
285
+ assert_equal({"name"=>"No One's Choice"}, @band.send(:create_representation_with, {}, {}, Representable::JSON))
286
+ end
287
+
288
+ it "writes false attributes" do
289
+ @band.groupies = false
290
+ assert_equal({"name"=>"No One's Choice","groupies"=>false}, @band.send(:create_representation_with, {}, {}, Representable::JSON))
291
+ end
292
+
293
+ it "includes nil attribute when :represent_nil is true" do
294
+ mod = Module.new do
295
+ include Representable::JSON
296
+ property :name
297
+ property :groupies, :represent_nil => true
298
+ end
299
+
300
+ @band.extend(mod) # FIXME: use clean object.
301
+ @band.groupies = nil
302
+ hash = @band.send(:create_representation_with, {}, {}, Representable::JSON)
303
+ assert_equal({"name"=>"No One's Choice", "groupies" => nil}, hash)
304
+ end
311
305
  end
312
306
 
307
+ describe ":if" do
308
+ before do
309
+ @pop = Class.new(PopBand) { attr_accessor :fame }
310
+ end
311
+
312
+ it "respects property when condition true" do
313
+ @pop.class_eval { property :fame, :if => lambda { true } }
314
+ band = @pop.new
315
+ band.update_properties_from({"fame"=>"oh yes"}, {}, Representable::JSON)
316
+ assert_equal "oh yes", band.fame
317
+ end
318
+
319
+ it "ignores property when condition false" do
320
+ @pop.class_eval { property :fame, :if => lambda { false } }
321
+ band = @pop.new
322
+ band.update_properties_from({"fame"=>"oh yes"}, {}, Representable::JSON)
323
+ assert_equal nil, band.fame
324
+ end
325
+
326
+ it "ignores property when :exclude'ed even when condition is true" do
327
+ @pop.class_eval { property :fame, :if => lambda { true } }
328
+ band = @pop.new
329
+ band.update_properties_from({"fame"=>"oh yes"}, {:exclude => [:fame]}, Representable::JSON)
330
+ assert_equal nil, band.fame
331
+ end
332
+
333
+
334
+ it "executes block in instance context" do
335
+ @pop.class_eval { property :fame, :if => lambda { groupies } }
336
+ band = @pop.new
337
+ band.groupies = true
338
+ band.update_properties_from({"fame"=>"oh yes"}, {}, Representable::JSON)
339
+ assert_equal "oh yes", band.fame
340
+ end
341
+ end
313
342
 
314
343
  describe "Config" do
315
344
  before do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: representable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.7
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-13 00:00:00.000000000 Z
12
+ date: 2012-05-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
16
- requirement: &80366830 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *80366830
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: json
27
- requirement: &80366420 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '0'
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *80366420
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: rake
38
- requirement: &80365840 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: '0'
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *80365840
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: test_xml
49
- requirement: &80365210 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,10 +69,15 @@ dependencies:
54
69
  version: '0'
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *80365210
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
58
78
  - !ruby/object:Gem::Dependency
59
79
  name: minitest
60
- requirement: &80364570 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
83
  - - ! '>='
@@ -65,10 +85,15 @@ dependencies:
65
85
  version: 2.8.1
66
86
  type: :development
67
87
  prerelease: false
68
- version_requirements: *80364570
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 2.8.1
69
94
  - !ruby/object:Gem::Dependency
70
95
  name: mocha
71
- requirement: &80364280 !ruby/object:Gem::Requirement
96
+ requirement: !ruby/object:Gem::Requirement
72
97
  none: false
73
98
  requirements:
74
99
  - - ! '>='
@@ -76,10 +101,15 @@ dependencies:
76
101
  version: '0'
77
102
  type: :development
78
103
  prerelease: false
79
- version_requirements: *80364280
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
80
110
  - !ruby/object:Gem::Dependency
81
111
  name: mongoid
82
- requirement: &80363910 !ruby/object:Gem::Requirement
112
+ requirement: !ruby/object:Gem::Requirement
83
113
  none: false
84
114
  requirements:
85
115
  - - ! '>='
@@ -87,10 +117,15 @@ dependencies:
87
117
  version: '0'
88
118
  type: :development
89
119
  prerelease: false
90
- version_requirements: *80363910
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
91
126
  - !ruby/object:Gem::Dependency
92
127
  name: virtus
93
- requirement: &80363490 !ruby/object:Gem::Requirement
128
+ requirement: !ruby/object:Gem::Requirement
94
129
  none: false
95
130
  requirements:
96
131
  - - ! '>='
@@ -98,7 +133,12 @@ dependencies:
98
133
  version: '0'
99
134
  type: :development
100
135
  prerelease: false
101
- version_requirements: *80363490
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
102
142
  description: Maps representation documents from and to Ruby objects. Includes XML
103
143
  and JSON support, plain properties, collections and compositions.
104
144
  email:
@@ -121,6 +161,7 @@ files:
121
161
  - lib/representable/bindings/xml_bindings.rb
122
162
  - lib/representable/coercion.rb
123
163
  - lib/representable/definition.rb
164
+ - lib/representable/deprecations.rb
124
165
  - lib/representable/json.rb
125
166
  - lib/representable/json/collection.rb
126
167
  - lib/representable/json/hash.rb
@@ -159,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
200
  version: '0'
160
201
  requirements: []
161
202
  rubyforge_project:
162
- rubygems_version: 1.8.10
203
+ rubygems_version: 1.8.24
163
204
  signing_key:
164
205
  specification_version: 3
165
206
  summary: Maps representation documents from and to Ruby objects. Includes XML and