representable 1.3.2 → 1.3.3
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/.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
|