representable 1.3.2 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -4
- data/CHANGES.textile +5 -0
- data/Gemfile +1 -1
- data/README.md +45 -1
- data/Rakefile +7 -3
- data/TODO +1 -1
- data/lib/representable.rb +10 -5
- data/lib/representable/binding.rb +18 -0
- data/lib/representable/bindings/yaml_bindings.rb +1 -1
- data/lib/representable/definition.rb +8 -0
- data/lib/representable/version.rb +1 -1
- data/representable.gemspec +1 -1
- data/test/definition_test.rb +27 -0
- data/test/representable_test.rb +48 -1
- data/test/test_helper.rb +1 -5
- metadata +4 -5
- data/gemfiles/Gemfile.mongoid-2.4 +0 -6
data/.travis.yml
CHANGED
data/CHANGES.textile
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -172,6 +172,34 @@ For XML we just include the `Representable::XML` module.
|
|
172
172
|
<composers>Sting</composers>
|
173
173
|
</song>
|
174
174
|
|
175
|
+
## Passing Options
|
176
|
+
|
177
|
+
You're free to pass an options hash into the rendering or parsing.
|
178
|
+
|
179
|
+
song.to_json(:append => "SOLD OUT!")
|
180
|
+
|
181
|
+
If you want to append the "SOLD OUT!" to the song's `title` when rendering, use the `:getter` option.
|
182
|
+
|
183
|
+
SongRepresenter
|
184
|
+
include Representable::JSON
|
185
|
+
|
186
|
+
property :title, :getter => lambda { |args| title + args[:append] }
|
187
|
+
end
|
188
|
+
|
189
|
+
Note that the block is executed in the represented model context which allows using accessors and instance variables.
|
190
|
+
|
191
|
+
|
192
|
+
The same works for parsing using the `:setter` method.
|
193
|
+
|
194
|
+
property :title, :setter => lambda { |val, args| self.title= val + args[:append] }
|
195
|
+
|
196
|
+
Here, the block retrieves two arguments: the parsed value and your user options.
|
197
|
+
|
198
|
+
You can also use the `:getter` option instead of writing a reader method. Even when you're not interested in user options you can still use this technique.
|
199
|
+
|
200
|
+
property :title, :getter => lambda { |*| @name }
|
201
|
+
|
202
|
+
This hash will also be available in the `:if` block, documented [here](https://github.com/apotonick/representable/#conditions) and will be passed to nested objects.
|
175
203
|
|
176
204
|
## Using Helpers
|
177
205
|
|
@@ -417,7 +445,6 @@ I do not recommend this approach as it bloats your domain classes with represent
|
|
417
445
|
|
418
446
|
Here's a quick overview about other available options for `#property` and its bro `#collection`.
|
419
447
|
|
420
|
-
|
421
448
|
### Read/Write Restrictions
|
422
449
|
|
423
450
|
Using the `:readable` and `:writeable` options access to properties can be restricted.
|
@@ -448,6 +475,14 @@ You can also define conditions on properties using `:if`, making them being cons
|
|
448
475
|
|
449
476
|
When rendering or parsing, the `track` property is considered only if track is valid. Note that the block is executed in instance context, giving you access to instance methods.
|
450
477
|
|
478
|
+
As always, the block retrieves your options. Given this render call
|
479
|
+
|
480
|
+
song.to_json(minimum_track: 2)
|
481
|
+
|
482
|
+
your `:if` may process the options.
|
483
|
+
|
484
|
+
property :track, if: lambda { |opts| track > opts[:minimum_track] }
|
485
|
+
|
451
486
|
|
452
487
|
### False and Nil Values
|
453
488
|
|
@@ -476,6 +511,15 @@ Use the `:type` option to specify the conversion target. Note that `:default` st
|
|
476
511
|
end
|
477
512
|
|
478
513
|
|
514
|
+
## Undocumented Features
|
515
|
+
|
516
|
+
(Please don't read this section!)
|
517
|
+
|
518
|
+
If you need a special binding for a property you're free to create it using the `:binding` option.
|
519
|
+
|
520
|
+
property :title, :binding => lambda { |*args| JSON::TitleBinding.new(*args) }
|
521
|
+
|
522
|
+
|
479
523
|
## Copyright
|
480
524
|
|
481
525
|
Representable started as a heavily simplified fork of the ROXML gem. Big thanks to Ben Woosley for his inspiring work.
|
data/Rakefile
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
require 'bundler'
|
2
|
-
Bundler::GemHelper.install_tasks
|
3
|
-
|
1
|
+
require 'bundler/setup'
|
4
2
|
require 'rake/testtask'
|
5
3
|
|
6
4
|
desc 'Test the representable gem.'
|
@@ -11,3 +9,9 @@ Rake::TestTask.new(:test) do |test|
|
|
11
9
|
test.test_files = FileList['test/*_test.rb']
|
12
10
|
test.verbose = true
|
13
11
|
end
|
12
|
+
|
13
|
+
Rake::TestTask.new(:test18) do |test|
|
14
|
+
test.libs << 'test'
|
15
|
+
test.test_files = FileList['test/*_test.rb'] - ['test/mongoid_test.rb', 'test/yaml_test.rb']
|
16
|
+
test.verbose = true
|
17
|
+
end
|
data/TODO
CHANGED
data/lib/representable.rb
CHANGED
@@ -81,13 +81,18 @@ private
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def skip_conditional_property?(binding)
|
84
|
+
# TODO: move to Binding.
|
84
85
|
return unless condition = binding.options[:if]
|
85
|
-
|
86
|
+
|
87
|
+
args = []
|
88
|
+
args << binding.user_options if condition.arity > 0 # TODO: remove arity check. users should know whether they pass options or not.
|
89
|
+
|
90
|
+
not instance_exec(*args, &condition)
|
86
91
|
end
|
87
92
|
|
88
93
|
# Retrieve value and write fragment to the doc.
|
89
94
|
def compile_fragment(bin, doc)
|
90
|
-
value =
|
95
|
+
value = bin.get
|
91
96
|
|
92
97
|
bin.write_fragment(doc, value)
|
93
98
|
end
|
@@ -95,17 +100,17 @@ private
|
|
95
100
|
# Parse value from doc and update the model property.
|
96
101
|
def uncompile_fragment(bin, doc)
|
97
102
|
bin.read_fragment(doc) do |value|
|
98
|
-
|
103
|
+
bin.set(value)
|
99
104
|
end
|
100
105
|
end
|
101
|
-
|
106
|
+
|
102
107
|
def representable_attrs
|
103
108
|
@representable_attrs ||= self.class.representable_attrs # DISCUSS: copy, or better not?
|
104
109
|
end
|
105
110
|
|
106
111
|
def representable_bindings_for(format, options)
|
107
112
|
options = cleanup_options(options) # FIXME: make representable-options and user-options two different hashes.
|
108
|
-
representable_attrs.map {|attr| format.
|
113
|
+
representable_attrs.map {|attr| format.build(attr, self, options) }
|
109
114
|
end
|
110
115
|
|
111
116
|
# Returns the wrapper for the representation. Mostly used in XML.
|
@@ -5,6 +5,12 @@ module Representable
|
|
5
5
|
class Binding < SimpleDelegator
|
6
6
|
class FragmentNotFound
|
7
7
|
end
|
8
|
+
|
9
|
+
def self.build(definition, *args)
|
10
|
+
# DISCUSS: move #create_binding to this class?
|
11
|
+
return definition.create_binding(*args) if definition.binding
|
12
|
+
build_for(definition, *args)
|
13
|
+
end
|
8
14
|
|
9
15
|
def definition # TODO: remove in 1.4.
|
10
16
|
raise "Binding#definition is no longer supported as all Definition methods are now delegated automatically."
|
@@ -15,6 +21,8 @@ module Representable
|
|
15
21
|
@represented = represented
|
16
22
|
@user_options = user_options
|
17
23
|
end
|
24
|
+
|
25
|
+
attr_reader :user_options, :represented # TODO: make private/remove.
|
18
26
|
|
19
27
|
# Main entry point for rendering/parsing a property object.
|
20
28
|
def serialize(value)
|
@@ -51,6 +59,16 @@ module Representable
|
|
51
59
|
def read_fragment_for(doc)
|
52
60
|
read(doc)
|
53
61
|
end
|
62
|
+
|
63
|
+
def get
|
64
|
+
return represented.instance_exec(user_options, &options[:getter]) if options[:getter]
|
65
|
+
represented.send(getter)
|
66
|
+
end
|
67
|
+
|
68
|
+
def set(value)
|
69
|
+
value = represented.instance_exec(value, user_options, &options[:setter]) if options[:setter]
|
70
|
+
represented.send(setter, value)
|
71
|
+
end
|
54
72
|
|
55
73
|
|
56
74
|
# Hooks into #serialize and #deserialize to extend typed properties
|
data/representable.gemspec
CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.add_development_dependency "rake"
|
26
26
|
s.add_development_dependency "test_xml"
|
27
27
|
s.add_development_dependency "minitest", ">= 2.8.1"
|
28
|
-
s.add_development_dependency "mocha"
|
28
|
+
s.add_development_dependency "mocha", ">= 0.13.0"
|
29
29
|
s.add_development_dependency "mongoid"
|
30
30
|
s.add_development_dependency "virtus", "~> 0.5.0"
|
31
31
|
end
|
data/test/definition_test.rb
CHANGED
@@ -181,6 +181,23 @@ class DefinitionTest < MiniTest::Spec
|
|
181
181
|
|
182
182
|
end
|
183
183
|
|
184
|
+
describe "#binding" do
|
185
|
+
it "returns true when :binding is set" do
|
186
|
+
assert Representable::Definition.new(:songs, :binding => Object).binding
|
187
|
+
end
|
188
|
+
|
189
|
+
it "returns false when :binding is not set" do
|
190
|
+
assert !Representable::Definition.new(:songs).binding
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "#create_binding" do
|
195
|
+
it "executes the block (without special context)" do
|
196
|
+
definition = Representable::Definition.new(:title, :binding => lambda { |*args| @binding = Representable::Binding.new(*args) })
|
197
|
+
definition.create_binding(object=Object.new).must_equal @binding
|
198
|
+
@binding.instance_variable_get(:@represented).must_equal object
|
199
|
+
end
|
200
|
+
end
|
184
201
|
|
185
202
|
describe ":collection => true" do
|
186
203
|
before do
|
@@ -232,4 +249,14 @@ class DefinitionTest < MiniTest::Spec
|
|
232
249
|
assert ! Representable::Definition.new(:songs).hash?
|
233
250
|
end
|
234
251
|
end
|
252
|
+
|
253
|
+
describe ":binding => Object" do
|
254
|
+
subject do
|
255
|
+
Representable::Definition.new(:songs, :binding => Object)
|
256
|
+
end
|
257
|
+
|
258
|
+
it "responds to #binding" do
|
259
|
+
assert_equal subject.binding, Object
|
260
|
+
end
|
261
|
+
end
|
235
262
|
end
|
data/test/representable_test.rb
CHANGED
@@ -444,7 +444,6 @@ class RepresentableTest < MiniTest::Spec
|
|
444
444
|
assert_equal nil, band.fame
|
445
445
|
end
|
446
446
|
|
447
|
-
|
448
447
|
it "executes block in instance context" do
|
449
448
|
@pop.class_eval { property :fame, :if => lambda { groupies } }
|
450
449
|
band = @pop.new
|
@@ -452,6 +451,39 @@ class RepresentableTest < MiniTest::Spec
|
|
452
451
|
band.update_properties_from({"fame"=>"oh yes"}, {}, Representable::Hash::PropertyBinding)
|
453
452
|
assert_equal "oh yes", band.fame
|
454
453
|
end
|
454
|
+
|
455
|
+
describe "propagating user options to the block" do
|
456
|
+
representer! do
|
457
|
+
property :name, :if => lambda { |opts| opts[:include_name] }
|
458
|
+
end
|
459
|
+
subject { OpenStruct.new(:name => "Outbound").extend(representer) }
|
460
|
+
|
461
|
+
it "works without specifying options" do
|
462
|
+
subject.to_hash.must_equal({})
|
463
|
+
end
|
464
|
+
|
465
|
+
it "passes user options to block" do
|
466
|
+
subject.to_hash(:include_name => true).must_equal({"name" => "Outbound"})
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
describe ":getter and :setter" do
|
472
|
+
representer! do
|
473
|
+
property :name,
|
474
|
+
:getter => lambda { |args| "#{args[:welcome]} #{name}" },
|
475
|
+
:setter => lambda { |val, args| self.name = "#{args[:welcome]} #{val}" }
|
476
|
+
end
|
477
|
+
|
478
|
+
subject { OpenStruct.new(:name => "Mony Mony").extend(representer) }
|
479
|
+
|
480
|
+
it "uses :getter when rendering" do
|
481
|
+
subject.to_hash(:welcome => "Hi").must_equal({"name" => "Hi Mony Mony"})
|
482
|
+
end
|
483
|
+
|
484
|
+
it "uses :setter when parsing" do
|
485
|
+
subject.from_hash({"name" => "Eyes Without A Face"}, :welcome => "Hello").name.must_equal "Hello Eyes Without A Face"
|
486
|
+
end
|
455
487
|
end
|
456
488
|
|
457
489
|
describe ":extend and :class" do
|
@@ -567,6 +599,21 @@ class RepresentableTest < MiniTest::Spec
|
|
567
599
|
end
|
568
600
|
end
|
569
601
|
end
|
602
|
+
|
603
|
+
describe ":binding" do
|
604
|
+
representer! do
|
605
|
+
class MyBinding < Representable::Binding
|
606
|
+
def write(doc, *args)
|
607
|
+
doc[:title] = @represented.title
|
608
|
+
end
|
609
|
+
end
|
610
|
+
property :title, :binding => lambda { |*args| MyBinding.new(*args) }
|
611
|
+
end
|
612
|
+
|
613
|
+
it "uses the specified binding instance" do
|
614
|
+
OpenStruct.new(:title => "Affliction").extend(representer).to_hash.must_equal({:title => "Affliction"})
|
615
|
+
end
|
616
|
+
end
|
570
617
|
|
571
618
|
end
|
572
619
|
|
data/test/test_helper.rb
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
require 'bundler'
|
2
|
-
Bundler.setup
|
3
|
-
|
4
|
-
gem 'minitest'
|
5
1
|
require 'representable'
|
6
2
|
require 'representable/json'
|
7
3
|
require 'representable/xml'
|
@@ -9,7 +5,7 @@ require 'test/unit'
|
|
9
5
|
require 'minitest/spec'
|
10
6
|
require 'minitest/autorun'
|
11
7
|
require 'test_xml/mini_test'
|
12
|
-
require 'mocha'
|
8
|
+
require 'mocha/setup'
|
13
9
|
|
14
10
|
class Album
|
15
11
|
attr_accessor :songs, :best_song
|
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.3.
|
4
|
+
version: 1.3.3
|
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: 2013-02-
|
12
|
+
date: 2013-02-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
@@ -98,7 +98,7 @@ dependencies:
|
|
98
98
|
requirements:
|
99
99
|
- - ! '>='
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version:
|
101
|
+
version: 0.13.0
|
102
102
|
type: :development
|
103
103
|
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -106,7 +106,7 @@ dependencies:
|
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
109
|
+
version: 0.13.0
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
111
|
name: mongoid
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -154,7 +154,6 @@ files:
|
|
154
154
|
- README.md
|
155
155
|
- Rakefile
|
156
156
|
- TODO
|
157
|
-
- gemfiles/Gemfile.mongoid-2.4
|
158
157
|
- lib/representable.rb
|
159
158
|
- lib/representable/binding.rb
|
160
159
|
- lib/representable/bindings/hash_bindings.rb
|