representable 2.3.0 → 2.4.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +47 -0
- data/Gemfile +5 -0
- data/README.md +33 -0
- data/lib/representable.rb +60 -73
- data/lib/representable/binding.rb +37 -194
- data/lib/representable/cached.rb +10 -46
- data/lib/representable/coercion.rb +8 -8
- data/lib/representable/config.rb +15 -75
- data/lib/representable/debug.rb +41 -59
- data/lib/representable/declarative.rb +34 -53
- data/lib/representable/decorator.rb +11 -40
- data/lib/representable/definition.rb +14 -15
- data/lib/representable/deprecations.rb +90 -0
- data/lib/representable/deserializer.rb +87 -82
- data/lib/representable/for_collection.rb +5 -3
- data/lib/representable/hash.rb +5 -3
- data/lib/representable/hash/binding.rb +6 -15
- data/lib/representable/hash/collection.rb +10 -6
- data/lib/representable/hash_methods.rb +5 -5
- data/lib/representable/insert.rb +31 -0
- data/lib/representable/json.rb +7 -3
- data/lib/representable/json/hash.rb +1 -1
- data/lib/representable/object/binding.rb +5 -5
- data/lib/representable/parse_strategies.rb +37 -3
- data/lib/representable/pipeline.rb +37 -5
- data/lib/representable/pipeline_factories.rb +88 -0
- data/lib/representable/serializer.rb +38 -44
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +4 -0
- data/lib/representable/xml/binding.rb +25 -31
- data/lib/representable/xml/collection.rb +5 -3
- data/lib/representable/xml/hash.rb +7 -2
- data/lib/representable/yaml.rb +6 -3
- data/lib/representable/yaml/binding.rb +4 -4
- data/representable.gemspec +3 -3
- data/test/---deserialize-pipeline_test.rb +37 -0
- data/test/binding_test.rb +7 -7
- data/test/cached_test.rb +31 -19
- data/test/coercion_test.rb +2 -2
- data/test/config/inherit_test.rb +13 -12
- data/test/config_test.rb +12 -67
- data/test/decorator_test.rb +4 -5
- data/test/default_test.rb +34 -0
- data/test/defaults_options_test.rb +93 -0
- data/test/definition_test.rb +19 -39
- data/test/exec_context_test.rb +1 -1
- data/test/filter_test.rb +18 -20
- data/test/getter_setter_test.rb +1 -8
- data/test/hash_bindings_test.rb +13 -13
- data/test/heritage_test.rb +62 -0
- data/test/if_test.rb +1 -0
- data/test/inherit_test.rb +5 -3
- data/test/instance_test.rb +3 -4
- data/test/json_test.rb +3 -59
- data/test/lonely_test.rb +47 -3
- data/test/nested_test.rb +8 -2
- data/test/pipeline_test.rb +259 -0
- data/test/populator_test.rb +76 -0
- data/test/realistic_benchmark.rb +39 -7
- data/test/render_nil_test.rb +21 -0
- data/test/represent_test.rb +2 -2
- data/test/representable_test.rb +33 -103
- data/test/schema_test.rb +5 -15
- data/test/serialize_deserialize_test.rb +2 -2
- data/test/skip_test.rb +1 -1
- data/test/test_helper.rb +6 -0
- data/test/uncategorized_test.rb +67 -0
- data/test/xml_bindings_test.rb +6 -6
- data/test/xml_test.rb +6 -6
- metadata +33 -13
- data/lib/representable/apply.rb +0 -13
- data/lib/representable/inheritable.rb +0 -71
- data/lib/representable/mapper.rb +0 -83
- data/lib/representable/populator.rb +0 -56
- data/test/inheritable_test.rb +0 -97
data/lib/representable/xml.rb
CHANGED
@@ -4,34 +4,34 @@ require 'representable/hash/binding.rb'
|
|
4
4
|
module Representable
|
5
5
|
module XML
|
6
6
|
class Binding < Representable::Binding
|
7
|
-
def self.build_for(definition
|
8
|
-
return Collection.new(definition
|
9
|
-
return Hash.new(definition
|
10
|
-
return AttributeHash.new(definition
|
11
|
-
return Attribute.new(definition
|
12
|
-
return Content.new(definition
|
13
|
-
new(definition
|
7
|
+
def self.build_for(definition)
|
8
|
+
return Collection.new(definition) if definition.array?
|
9
|
+
return Hash.new(definition) if definition.hash? and not definition[:use_attributes] # FIXME: hate this.
|
10
|
+
return AttributeHash.new(definition) if definition.hash? and definition[:use_attributes]
|
11
|
+
return Attribute.new(definition) if definition[:attribute]
|
12
|
+
return Content.new(definition) if definition[:content]
|
13
|
+
new(definition)
|
14
14
|
end
|
15
15
|
|
16
|
-
def write(parent, fragments)
|
16
|
+
def write(parent, fragments, as)
|
17
17
|
wrap_node = parent
|
18
18
|
|
19
19
|
if wrap = self[:wrap]
|
20
20
|
parent << wrap_node = node_for(parent, wrap)
|
21
21
|
end
|
22
22
|
|
23
|
-
wrap_node << serialize_for(fragments, parent)
|
23
|
+
wrap_node << serialize_for(fragments, parent, as)
|
24
24
|
end
|
25
25
|
|
26
|
-
def read(node)
|
27
|
-
nodes = find_nodes(node)
|
26
|
+
def read(node, as)
|
27
|
+
nodes = find_nodes(node, as)
|
28
28
|
return FragmentNotFound if nodes.size == 0 # TODO: write dedicated test!
|
29
29
|
|
30
30
|
deserialize_from(nodes)
|
31
31
|
end
|
32
32
|
|
33
33
|
# Creates wrapped node for the property.
|
34
|
-
def serialize_for(value, parent)
|
34
|
+
def serialize_for(value, parent, as)
|
35
35
|
node = node_for(parent, as)
|
36
36
|
serialize_node(node, value)
|
37
37
|
end
|
@@ -57,13 +57,9 @@ module Representable
|
|
57
57
|
end
|
58
58
|
|
59
59
|
private
|
60
|
-
def
|
61
|
-
as
|
62
|
-
|
63
|
-
|
64
|
-
def find_nodes(doc)
|
65
|
-
selector = xpath
|
66
|
-
selector = "#{self[:wrap]}/#{xpath}" if self[:wrap]
|
60
|
+
def find_nodes(doc, as)
|
61
|
+
selector = as
|
62
|
+
selector = "#{self[:wrap]}/#{as}" if self[:wrap]
|
67
63
|
nodes = doc.xpath(selector)
|
68
64
|
end
|
69
65
|
|
@@ -81,9 +77,9 @@ module Representable
|
|
81
77
|
class Collection < self
|
82
78
|
include Representable::Binding::Collection
|
83
79
|
|
84
|
-
def serialize_for(value, parent)
|
80
|
+
def serialize_for(value, parent, as)
|
85
81
|
# return NodeSet so << works.
|
86
|
-
set_for(parent, value.collect { |item| super(item, parent) })
|
82
|
+
set_for(parent, value.collect { |item| super(item, parent, as) })
|
87
83
|
end
|
88
84
|
|
89
85
|
def deserialize_from(nodes)
|
@@ -102,9 +98,7 @@ module Representable
|
|
102
98
|
|
103
99
|
|
104
100
|
class Hash < Collection
|
105
|
-
|
106
|
-
|
107
|
-
def serialize_for(value, parent)
|
101
|
+
def serialize_for(value, parent, as)
|
108
102
|
set_for(parent, value.collect do |k, v|
|
109
103
|
node = node_for(parent, k)
|
110
104
|
serialize_node(node, v)
|
@@ -123,7 +117,7 @@ module Representable
|
|
123
117
|
|
124
118
|
class AttributeHash < Collection
|
125
119
|
# DISCUSS: use AttributeBinding here?
|
126
|
-
def write(parent, value) # DISCUSS: is it correct overriding #write here?
|
120
|
+
def write(parent, value, as) # DISCUSS: is it correct overriding #write here?
|
127
121
|
value.collect do |k, v|
|
128
122
|
parent[k] = v.to_s
|
129
123
|
end
|
@@ -139,22 +133,22 @@ module Representable
|
|
139
133
|
|
140
134
|
# Represents a tag attribute. Currently this only works on the top-level tag.
|
141
135
|
class Attribute < self
|
142
|
-
def read(node)
|
136
|
+
def read(node, as)
|
143
137
|
node[as]
|
144
138
|
end
|
145
139
|
|
146
|
-
def serialize_for(value, parent)
|
140
|
+
def serialize_for(value, parent, as)
|
147
141
|
parent[as] = value.to_s
|
148
142
|
end
|
149
143
|
|
150
|
-
def write(parent, value)
|
151
|
-
serialize_for(value, parent)
|
144
|
+
def write(parent, value, as)
|
145
|
+
serialize_for(value, parent, as)
|
152
146
|
end
|
153
147
|
end
|
154
148
|
|
155
149
|
# Represents tag content.
|
156
150
|
class Content < self
|
157
|
-
def read(node)
|
151
|
+
def read(node, as)
|
158
152
|
node.content
|
159
153
|
end
|
160
154
|
|
@@ -162,7 +156,7 @@ module Representable
|
|
162
156
|
parent.content = value.to_s
|
163
157
|
end
|
164
158
|
|
165
|
-
def write(parent, value)
|
159
|
+
def write(parent, value, as)
|
166
160
|
serialize_for(value, parent)
|
167
161
|
end
|
168
162
|
end
|
@@ -1,15 +1,17 @@
|
|
1
1
|
module Representable::XML
|
2
2
|
module Collection
|
3
|
-
include Representable::XML
|
4
|
-
|
5
3
|
def self.included(base)
|
6
4
|
base.send :include, Representable::XML
|
7
5
|
base.send :include, Representable::Hash::Collection
|
8
6
|
base.send :include, Methods
|
9
7
|
end
|
10
8
|
|
11
|
-
|
12
9
|
module Methods
|
10
|
+
def create_representation_with(doc, options, format)
|
11
|
+
bin = representable_map(options, format).first
|
12
|
+
bin.write(doc, super, bin.name)
|
13
|
+
end
|
14
|
+
|
13
15
|
def update_properties_from(doc, *args)
|
14
16
|
super(doc.search("./*"), *args) # pass the list of collection items to Hash::Collection#update_properties_from.
|
15
17
|
end
|
@@ -10,7 +10,7 @@ module Representable::XML
|
|
10
10
|
base.class_eval do
|
11
11
|
include Representable
|
12
12
|
extend ClassMethods
|
13
|
-
|
13
|
+
property(:_self, hash: true, use_attributes: true)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -20,6 +20,11 @@ module Representable::XML
|
|
20
20
|
hash :_self, options.merge!(:use_attributes => true)
|
21
21
|
end
|
22
22
|
end
|
23
|
+
|
24
|
+
def create_representation_with(doc, options, format)
|
25
|
+
bin = representable_bindings_for(format, options).first
|
26
|
+
bin.write(doc, super, options)
|
27
|
+
end
|
23
28
|
end
|
24
29
|
|
25
30
|
module Hash
|
@@ -30,7 +35,7 @@ module Representable::XML
|
|
30
35
|
base.class_eval do
|
31
36
|
include Representable
|
32
37
|
extend ClassMethods
|
33
|
-
|
38
|
+
property(:_self, {:hash => true})
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
data/lib/representable/yaml.rb
CHANGED
@@ -8,11 +8,16 @@ module Representable
|
|
8
8
|
def self.included(base)
|
9
9
|
base.class_eval do
|
10
10
|
include Representable
|
11
|
-
#self.representation_wrap = true # let representable compute it.
|
12
11
|
register_feature Representable::YAML
|
12
|
+
extend ClassMethods
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
module ClassMethods
|
17
|
+
def format_engine
|
18
|
+
Representable::YAML
|
19
|
+
end
|
20
|
+
end
|
16
21
|
|
17
22
|
def from_yaml(doc, options={})
|
18
23
|
hash = Psych.load(doc)
|
@@ -21,8 +26,6 @@ module Representable
|
|
21
26
|
|
22
27
|
# Returns a Nokogiri::XML object representing this object.
|
23
28
|
def to_ast(options={})
|
24
|
-
#root_tag = options[:wrap] || representation_wrap
|
25
|
-
|
26
29
|
Psych::Nodes::Mapping.new.tap do |map|
|
27
30
|
create_representation_with(map, options, Binding)
|
28
31
|
end
|
@@ -3,12 +3,12 @@ require 'representable/hash/binding'
|
|
3
3
|
module Representable
|
4
4
|
module YAML
|
5
5
|
class Binding < Representable::Hash::Binding
|
6
|
-
def self.build_for(definition
|
7
|
-
return Collection.new(definition
|
8
|
-
new(definition
|
6
|
+
def self.build_for(definition)
|
7
|
+
return Collection.new(definition) if definition.array?
|
8
|
+
new(definition)
|
9
9
|
end
|
10
10
|
|
11
|
-
def write(map, fragment)
|
11
|
+
def write(map, fragment, as)
|
12
12
|
map.children << Psych::Nodes::Scalar.new(as)
|
13
13
|
map.children << node_for(fragment) # FIXME: should be serialize.
|
14
14
|
end
|
data/representable.gemspec
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
lib = File.expand_path('../lib/', __FILE__)
|
3
2
|
$:.unshift lib unless $:.include?(lib)
|
4
3
|
|
@@ -20,11 +19,12 @@ Gem::Specification.new do |s|
|
|
20
19
|
s.require_paths = ["lib"]
|
21
20
|
s.license = "MIT"
|
22
21
|
|
23
|
-
s.add_dependency "uber", "~> 0.0.
|
22
|
+
s.add_dependency "uber", "~> 0.0.15"
|
23
|
+
s.add_dependency "declarative", "~> 0.0.4"
|
24
24
|
|
25
25
|
s.add_development_dependency "rake"
|
26
26
|
s.add_development_dependency "test_xml", "0.1.6"
|
27
|
-
s.add_development_dependency "minitest"
|
27
|
+
s.add_development_dependency "minitest"
|
28
28
|
s.add_development_dependency "mongoid"
|
29
29
|
s.add_development_dependency "virtus"
|
30
30
|
s.add_development_dependency "json", '>= 1.7.7'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class DeserializePipelineTest < MiniTest::Spec
|
4
|
+
Album = Struct.new(:artist, :songs)
|
5
|
+
Artist = Struct.new(:email)
|
6
|
+
Song = Struct.new(:title)
|
7
|
+
|
8
|
+
# tests [Collect[Instance, Prepare, Deserialize], Setter]
|
9
|
+
class Representer < Representable::Decorator
|
10
|
+
include Representable::Hash
|
11
|
+
|
12
|
+
# property :artist, populator: Uber::Options::Value.new(ArtistPopulator.new), pass_options:true do
|
13
|
+
# property :email
|
14
|
+
# end
|
15
|
+
# DISCUSS: rename to populator_pipeline ?
|
16
|
+
collection :songs, parse_pipeline: ->(*) { [Collect[Instance, Prepare, Deserialize], Setter] }, instance: :instance!, exec_context: :decorator, pass_options: true do
|
17
|
+
property :title
|
18
|
+
end
|
19
|
+
|
20
|
+
def instance!(*options)
|
21
|
+
puts "@@@@@ #{options.inspect}"
|
22
|
+
Song.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def songs=(array)
|
26
|
+
represented.songs=array
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it do
|
31
|
+
skip "TODO: implement :parse_pipeline and :render_pipeline, and before/after/replace semantics"
|
32
|
+
album = Album.new
|
33
|
+
Representer.new(album).from_hash({"artist"=>{"email"=>"yo"}, "songs"=>[{"title"=>"Affliction"}, {"title"=>"Dream Beater"}]})
|
34
|
+
album.songs.must_equal([Song.new("Affliction"), Song.new("Dream Beater")])
|
35
|
+
puts album.inspect
|
36
|
+
end
|
37
|
+
end
|
data/test/binding_test.rb
CHANGED
@@ -5,7 +5,7 @@ class BindingTest < MiniTest::Spec
|
|
5
5
|
let (:render_nil_definition) { Representable::Definition.new(:song, :render_nil => true) }
|
6
6
|
|
7
7
|
describe "#skipable_empty_value?" do
|
8
|
-
let (:binding) { Binding.new(render_nil_definition
|
8
|
+
let (:binding) { Binding.new(render_nil_definition) }
|
9
9
|
|
10
10
|
# don't skip when present.
|
11
11
|
it { binding.skipable_empty_value?("Disconnect, Disconnect").must_equal false }
|
@@ -14,16 +14,16 @@ class BindingTest < MiniTest::Spec
|
|
14
14
|
it { binding.skipable_empty_value?(nil).must_equal false }
|
15
15
|
|
16
16
|
# skip when nil and :render_nil undefined.
|
17
|
-
it { Binding.new(Representable::Definition.new(:song)
|
17
|
+
it { Binding.new(Representable::Definition.new(:song)).skipable_empty_value?(nil).must_equal true }
|
18
18
|
|
19
19
|
# don't skip when nil and :render_nil undefined.
|
20
|
-
it { Binding.new(Representable::Definition.new(:song)
|
20
|
+
it { Binding.new(Representable::Definition.new(:song)).skipable_empty_value?("Fatal Flu").must_equal false }
|
21
21
|
end
|
22
22
|
|
23
23
|
|
24
24
|
describe "#default_for" do
|
25
25
|
let (:definition) { Representable::Definition.new(:song, :default => "Insider") }
|
26
|
-
let (:binding) { Binding.new(definition
|
26
|
+
let (:binding) { Binding.new(definition) }
|
27
27
|
|
28
28
|
# return value when value present.
|
29
29
|
it { binding.default_for("Black And Blue").must_equal "Black And Blue" }
|
@@ -35,12 +35,12 @@ class BindingTest < MiniTest::Spec
|
|
35
35
|
it { binding.default_for(nil).must_equal "Insider" }
|
36
36
|
|
37
37
|
# return nil when value nil and render_nil: true.
|
38
|
-
it { Binding.new(render_nil_definition
|
38
|
+
it { Binding.new(render_nil_definition).default_for(nil).must_equal nil }
|
39
39
|
|
40
40
|
# return nil when value nil and render_nil: true, even when :default is set" do
|
41
|
-
it { Binding.new(Representable::Definition.new(:song, :render_nil => true, :default => "The Quest")
|
41
|
+
it { Binding.new(Representable::Definition.new(:song, :render_nil => true, :default => "The Quest")).default_for(nil).must_equal nil }
|
42
42
|
|
43
43
|
# return nil if no :default
|
44
|
-
it { Binding.new(Representable::Definition.new(:song)
|
44
|
+
it { Binding.new(Representable::Definition.new(:song)).default_for(nil).must_equal nil }
|
45
45
|
end
|
46
46
|
end
|
data/test/cached_test.rb
CHANGED
@@ -14,7 +14,7 @@ class CachedTest < MiniTest::Spec
|
|
14
14
|
include Representable::Hash
|
15
15
|
feature Representable::Cached
|
16
16
|
|
17
|
-
property :title, render_filter: lambda { |
|
17
|
+
property :title, render_filter: lambda { |input, options| "#{input}:#{options[:user_options]}" }, pass_options: true
|
18
18
|
property :composer, class: Model::Artist do
|
19
19
|
property :name
|
20
20
|
end
|
@@ -45,14 +45,16 @@ class CachedTest < MiniTest::Spec
|
|
45
45
|
"songs"=>[{"title"=>"Jailbreak:{:volume=>9}"}, {"title"=>"Southbound:{:volume=>9}"}, {"title"=>"Emerald:{:volume=>9}"}]}) # called in Deserializer/Serializer
|
46
46
|
|
47
47
|
# representer becomes reusable as it is stateless.
|
48
|
-
representer.update!(album2)
|
48
|
+
# representer.update!(album2)
|
49
49
|
|
50
50
|
# makes sure options are passed correctly.
|
51
|
-
representer.to_hash(volume:10).must_equal(album_hash)
|
51
|
+
# representer.to_hash(volume:10).must_equal(album_hash)
|
52
52
|
end
|
53
53
|
|
54
54
|
# profiling
|
55
|
-
it
|
55
|
+
it do
|
56
|
+
representer.to_hash
|
57
|
+
|
56
58
|
RubyProf.start
|
57
59
|
representer.to_hash
|
58
60
|
res = RubyProf.stop
|
@@ -65,14 +67,18 @@ class CachedTest < MiniTest::Spec
|
|
65
67
|
|
66
68
|
printer.print(STDOUT)
|
67
69
|
|
68
|
-
#
|
69
|
-
data.must_match "
|
70
|
-
#
|
71
|
-
data.must_match "
|
70
|
+
# 3 songs get decorated.
|
71
|
+
data.must_match "3 Representable::Function::Decorate#call"
|
72
|
+
# 3 nested decorator is instantiated for 3 Songs, though.
|
73
|
+
data.must_match "3 <Class::Representable::Decorator>#prepare"
|
74
|
+
# no Binding is instantiated at runtime.
|
75
|
+
data.wont_match "Representable::Binding#initialize"
|
72
76
|
# 2 mappers for Album, Song
|
73
|
-
data.must_match "2 Representable::Mapper::Methods#initialize"
|
74
|
-
#
|
75
|
-
data.must_match "
|
77
|
+
# data.must_match "2 Representable::Mapper::Methods#initialize"
|
78
|
+
# title, songs, 3x title, composer
|
79
|
+
data.must_match "8 Representable::Binding#render_pipeline"
|
80
|
+
data.wont_match "render_functions"
|
81
|
+
data.wont_match "Representable::Binding::Factories#render_functions"
|
76
82
|
end
|
77
83
|
end
|
78
84
|
|
@@ -106,10 +112,12 @@ class CachedTest < MiniTest::Spec
|
|
106
112
|
# TODO: test options.
|
107
113
|
end
|
108
114
|
|
109
|
-
it do
|
115
|
+
it "xxx" do
|
110
116
|
representer = AlbumRepresenter.new(Model::Album.new)
|
117
|
+
representer.from_hash(album_hash)
|
111
118
|
|
112
119
|
RubyProf.start
|
120
|
+
# puts "#{representer.class.representable_attrs.get(:songs).representer_module.representable_attrs.inspect}"
|
113
121
|
representer.from_hash(album_hash)
|
114
122
|
res = RubyProf.stop
|
115
123
|
|
@@ -120,15 +128,19 @@ class CachedTest < MiniTest::Spec
|
|
120
128
|
data = data.string
|
121
129
|
|
122
130
|
# only 2 nested decorators are instantiated, Song, and Artist.
|
123
|
-
data.must_match "
|
131
|
+
data.must_match "5 <Class::Representable::Decorator>#prepare"
|
124
132
|
# a total of 5 properties in the object graph.
|
125
|
-
data.
|
133
|
+
data.wont_match "Representable::Binding#initialize"
|
134
|
+
|
135
|
+
|
136
|
+
data.wont_match "parse_functions" # no pipeline creation.
|
137
|
+
data.must_match "10 Representable::Binding#parse_pipeline"
|
126
138
|
# three mappers for Album, Song, composer
|
127
|
-
data.must_match "3 Representable::Mapper::Methods#initialize"
|
128
|
-
# 6 deserializers as the songs collection uses 2.
|
129
|
-
data.must_match "6 Representable::Deserializer#initialize"
|
130
|
-
# one populater for every property.
|
131
|
-
data.must_match "5 Representable::Populator#initialize"
|
139
|
+
# data.must_match "3 Representable::Mapper::Methods#initialize"
|
140
|
+
# # 6 deserializers as the songs collection uses 2.
|
141
|
+
# data.must_match "6 Representable::Deserializer#initialize"
|
142
|
+
# # one populater for every property.
|
143
|
+
# data.must_match "5 Representable::Populator#initialize"
|
132
144
|
# printer.print(STDOUT)
|
133
145
|
end
|
134
146
|
end
|
data/test/coercion_test.rb
CHANGED
@@ -40,8 +40,8 @@ class VirtusCoercionTest < MiniTest::Spec
|
|
40
40
|
include Representable::Coercion
|
41
41
|
|
42
42
|
property :length, :type => Float,
|
43
|
-
:parse_filter => lambda { |
|
44
|
-
:render_filter => lambda { |fragment
|
43
|
+
:parse_filter => lambda { |input, options| "#{input}.1" }, # happens BEFORE coercer.
|
44
|
+
:render_filter => lambda { |fragment,*| "#{fragment}.1" }
|
45
45
|
end
|
46
46
|
|
47
47
|
# user's :parse_filter(s) are run before coercion.
|