representable 2.3.0 → 2.4.0.rc1
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.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7d63369f6aa46d6611bd4f968a61ed24e91c1eb
|
4
|
+
data.tar.gz: f6708f841e1ada1e9e17cfd5a7c6bb43300d05d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9acd1431d96131b220f03bd15296c94a77fc362959a46daa0b41847cc6ef2d375295e546664e3f4e911ae3118447497cf260b8cdd301f2e1662c916b940e152c
|
7
|
+
data.tar.gz: c45744211c992ab797ea4f6d0488930414b4587e3be8db98ae74973d135557b7f1108a913e440f8e1e8808957240b89cf0dff60b4ee463c8de86773820af3fad
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,50 @@
|
|
1
|
+
# 2.4.0
|
2
|
+
|
3
|
+
* Breaking API change: `:parse_filter` and `:render_filter` have no deprecation as all the other, they receive one options.
|
4
|
+
render_filter: val, doc, options
|
5
|
+
* `Decorator` always needs a format engine included, e.g. `Representable::JSON` to build bindings at compile-time.
|
6
|
+
* Removed `Representable::Apply`. This is now done via `Schema`.
|
7
|
+
representer_class.representable_attrs is definitions
|
8
|
+
* Removed `:use_decorator` option. Use a decorator instead.
|
9
|
+
|
10
|
+
TODO: DEPRECATE 4-args? also, allow options[:fragment] instead of options[:result] ?
|
11
|
+
|
12
|
+
|
13
|
+
TODO: :representable should be removing Deserialize.
|
14
|
+
deprecate Coercion, it sucks
|
15
|
+
deprecate parse/render filter
|
16
|
+
|
17
|
+
pipeline
|
18
|
+
less ifs, instead: simply remove the "middleware"
|
19
|
+
read_fragment
|
20
|
+
represented.set instance.prepare.from_json
|
21
|
+
Populator.(fragment)
|
22
|
+
not_found?.default.deserialize.parse_filter.set
|
23
|
+
Deserializer.(fragment)
|
24
|
+
instance.class.prepare.deserialize=from_json
|
25
|
+
|
26
|
+
OverwriteOnNil vs. StopOnNil
|
27
|
+
[Instance, ->(){..}, Bla]
|
28
|
+
|
29
|
+
did you ever overwrite #create_object in a binding? you can now simply pass a callable object into the pipeline.
|
30
|
+
TODO: deprecate parse strategies in favour of :populator (analogue to Reform)
|
31
|
+
|
32
|
+
elegant, easier to understand, and extend
|
33
|
+
you know, the whole deserialize stack (same for rendering): you pass in the json, it finds the fragments for each property, passes it to the binding, the binding deserializes, builds/finds a model, assigns values to it, and so on
|
34
|
+
you can partially hook in with :instance or :deserialize or :parse_filter, but it's really awkward. i am now changing the architecture to a pipeline, where you plug in the features you want (e.g. "do not call :prepare, :instance, :class, etc. but run my own :populator")
|
35
|
+
and it suddenly is super simple to understand
|
36
|
+
|
37
|
+
* Added `Representable.deprecations = false` to disable slow and weird deprecation code.
|
38
|
+
|
39
|
+
* Removed `Binding#user_options`. This is now available via `->(options[:user_options])`.
|
40
|
+
* Removed `Binding#as`.
|
41
|
+
* Removed `Binding#parent_decorator`.
|
42
|
+
|
43
|
+
## Internal Changes
|
44
|
+
* Removed `Binding@represented` (which was never public anyway). Use `Binding#represented`.
|
45
|
+
* Changed signature: `Binding#get(represented:)`. In now needs a hash `{represented: ..}`.
|
46
|
+
|
47
|
+
|
1
48
|
# 2.3.0
|
2
49
|
|
3
50
|
* Remove dependency to Nokogiri and Multi_JSON. You have to add what you need to your `Gemfile`/`gemspec` now.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
Representable maps Ruby objects to documents and back.
|
4
4
|
|
5
|
+
[](https://gitter.im/trailblazer/chat)
|
6
|
+
[](https://travis-ci.org/apotonick/representable)
|
8
|
+
[](http://badge.fury.io/rb/representable)
|
9
|
+
|
5
10
|
In other words: Take an object and decorate it with a representer module. This will allow you to render a JSON, XML or YAML document from that object. But that's only half of it! You can also use representers to parse a document and create or populate an object.
|
6
11
|
|
7
12
|
Representable is helpful for all kind of mappings, rendering and parsing workflows. However, it is mostly useful in API code. Are you planning to write a real REST API with representable? Then check out the [Roar](http://github.com/apotonick/roar) gem first, save work and time and make the world a better place instead.
|
@@ -1362,6 +1367,34 @@ end
|
|
1362
1367
|
|
1363
1368
|
This is an implementation detail most people shouldn't worry about.
|
1364
1369
|
|
1370
|
+
|
1371
|
+
### Defaults
|
1372
|
+
|
1373
|
+
Declaring default options for `property`, `nested`, `collection` and `hash` is
|
1374
|
+
easy as:
|
1375
|
+
|
1376
|
+
```ruby
|
1377
|
+
defaults render_nil: true
|
1378
|
+
```
|
1379
|
+
|
1380
|
+
You can also pass a block for dynamic default options based on the property
|
1381
|
+
name or on the property options.
|
1382
|
+
|
1383
|
+
```ruby
|
1384
|
+
defaults render_nil: true do |name|
|
1385
|
+
{ as: name.to_s.camelize }
|
1386
|
+
end
|
1387
|
+
```
|
1388
|
+
|
1389
|
+
```ruby
|
1390
|
+
defaults render_nil: true do |name, options|
|
1391
|
+
{
|
1392
|
+
as: name.to_s.camelize,
|
1393
|
+
embedded: options.fetch(:hash, false)
|
1394
|
+
}
|
1395
|
+
end
|
1396
|
+
```
|
1397
|
+
|
1365
1398
|
## Symbol Keys vs. String Keys
|
1366
1399
|
|
1367
1400
|
When parsing representable reads properties from hashes by using their string keys.
|
data/lib/representable.rb
CHANGED
@@ -1,21 +1,18 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
require 'representable/inheritable'
|
4
|
-
require 'representable/config'
|
5
|
-
require 'representable/definition'
|
6
|
-
require 'representable/mapper'
|
7
|
-
require 'representable/for_collection'
|
8
|
-
require 'representable/represent'
|
9
|
-
require 'representable/declarative'
|
10
|
-
require 'representable/apply'
|
11
|
-
require "representable/populator"
|
12
|
-
require "representable/deserializer"
|
13
|
-
require "representable/serializer"
|
14
|
-
require "representable/cached"
|
1
|
+
require "uber/delegates"
|
15
2
|
|
3
|
+
require "declarative/schema"
|
16
4
|
|
17
|
-
require "
|
5
|
+
require "representable/config"
|
6
|
+
require "representable/definition"
|
7
|
+
require "representable/declarative"
|
8
|
+
require "representable/deserializer"
|
9
|
+
require "representable/serializer"
|
10
|
+
require "representable/binding"
|
18
11
|
require "representable/pipeline"
|
12
|
+
require "representable/insert" # Pipeline::Insert
|
13
|
+
require "representable/cached"
|
14
|
+
require "representable/for_collection"
|
15
|
+
require "representable/represent"
|
19
16
|
|
20
17
|
module Representable
|
21
18
|
attr_writer :representable_attrs
|
@@ -23,86 +20,90 @@ module Representable
|
|
23
20
|
def self.included(base)
|
24
21
|
base.class_eval do
|
25
22
|
extend Declarative
|
26
|
-
|
23
|
+
# make Representable horizontally and vertically inheritable.
|
24
|
+
extend ModuleExtensions, ::Declarative::Heritage::Inherited, ::Declarative::Heritage::Included
|
27
25
|
extend ClassMethods
|
28
|
-
extend Feature
|
29
26
|
extend ForCollection
|
30
27
|
extend Represent
|
31
|
-
extend Apply
|
32
|
-
# register_feature Representable # Representable gets included automatically when creating inline representer.
|
33
28
|
end
|
34
29
|
end
|
35
30
|
|
36
31
|
private
|
37
32
|
# Reads values from +doc+ and sets properties accordingly.
|
38
33
|
def update_properties_from(doc, options, format)
|
39
|
-
propagated_options
|
34
|
+
propagated_options = normalize_options(options)
|
40
35
|
|
41
|
-
|
36
|
+
representable_map!(doc, propagated_options, format, :uncompile_fragment)
|
37
|
+
represented
|
42
38
|
end
|
43
39
|
|
44
40
|
# Compiles the document going through all properties.
|
45
41
|
def create_representation_with(doc, options, format)
|
46
|
-
propagated_options
|
42
|
+
propagated_options = normalize_options(options) # {_private: {include: }, is_admin: true}
|
47
43
|
|
48
|
-
|
44
|
+
representable_map!(doc, propagated_options, format, :compile_fragment)
|
45
|
+
doc
|
49
46
|
end
|
50
47
|
|
51
|
-
|
52
|
-
|
48
|
+
class Binding::Map < Array
|
49
|
+
def call(method, options)
|
50
|
+
each do |bin|
|
51
|
+
options[:binding] = bin # this is so much faster than options.merge().
|
52
|
+
bin.send(method, options)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# TODO: Merge with Definitions.
|
57
|
+
def <<(binding) # can be slow. this is compile time code.
|
58
|
+
(existing = find { |bin| bin.name == binding.name }) ? self[index(existing)] = binding : super(binding)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def representable_map(options, format)
|
63
|
+
Binding::Map.new(representable_bindings_for(format, options))
|
64
|
+
end
|
65
|
+
|
66
|
+
def representable_map!(doc, propagated_options, format, method)
|
67
|
+
options = {doc: doc, _private: propagated_options[:_private], user_options: propagated_options, represented: represented, decorator: self}
|
68
|
+
|
69
|
+
representable_map(options, format).(method, options) # .(:uncompile_fragment, options)
|
53
70
|
end
|
54
71
|
|
55
|
-
def
|
56
|
-
format.build(definition
|
72
|
+
def representable_bindings_for(format, options)
|
73
|
+
representable_attrs.collect {|definition| format.build(definition) }
|
57
74
|
end
|
58
75
|
|
59
76
|
# Make sure we do not change original options. However, private options like :include or :wrap are
|
60
77
|
# not passed on to child representers.
|
61
|
-
def normalize_options
|
78
|
+
def normalize_options(options)
|
62
79
|
# here, we could also filter out local options e.g. like options[:band].
|
63
|
-
|
64
|
-
return [options, private_options] if options.size == 0
|
80
|
+
return options unless options.any?
|
65
81
|
|
66
82
|
propagated_options = options.dup
|
83
|
+
propagated_options.delete(:wrap) # FIXME.
|
84
|
+
propagated_options.delete(:_private)
|
67
85
|
|
86
|
+
private_options = {}
|
68
87
|
private_options[:include] = propagated_options.delete(:include) if options[:include]
|
69
88
|
private_options[:exclude] = propagated_options.delete(:exclude) if options[:exclude]
|
70
|
-
propagated_options.delete(:wrap) # FIXME.
|
71
89
|
|
72
|
-
[
|
73
|
-
end
|
90
|
+
propagated_options[:_private] = private_options if private_options.any?
|
74
91
|
|
75
|
-
|
76
|
-
@representable_attrs ||= self.class.representable_attrs # DISCUSS: copy, or better not? what about "freezing"?
|
92
|
+
propagated_options
|
77
93
|
end
|
78
94
|
|
79
|
-
def
|
80
|
-
|
81
|
-
Mapper.new(bindings)
|
95
|
+
def representable_attrs
|
96
|
+
@representable_attrs ||= self.class.definitions
|
82
97
|
end
|
83
98
|
|
84
99
|
def representation_wrap(*args)
|
85
|
-
representable_attrs.wrap_for(
|
100
|
+
representable_attrs.wrap_for(represented, *args)
|
86
101
|
end
|
87
102
|
|
88
103
|
def represented
|
89
104
|
self
|
90
105
|
end
|
91
106
|
|
92
|
-
module ClassInclusions
|
93
|
-
def included(base)
|
94
|
-
super
|
95
|
-
base.inherit_module!(self)
|
96
|
-
end
|
97
|
-
|
98
|
-
def inherited(subclass) # DISCUSS: this could be in Decorator? but then we couldn't do B < A(include X) for non-decorators, right?
|
99
|
-
super
|
100
|
-
# FIXME: subclass.representable_attrs is ALWAYS empty at this point.
|
101
|
-
subclass.representable_attrs.inherit!(representable_attrs) # this should be inherit_class!
|
102
|
-
# DISCUSS: this could also just be: subclass.inheritable_attr :representable_attrs --> superclass.representable_attrs.clone
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
107
|
module ModuleExtensions
|
107
108
|
# Copies the representable_attrs reference to the extended object.
|
108
109
|
# Note that changing attrs in the instance will affect the class configuration.
|
@@ -112,32 +113,18 @@ private
|
|
112
113
|
end
|
113
114
|
end
|
114
115
|
|
115
|
-
|
116
116
|
module ClassMethods
|
117
|
-
# Gets overridden by Decorator as inheriting representers via include in Decorator means a bit more work (manifesting).
|
118
|
-
def inherit_module!(parent)
|
119
|
-
representable_attrs.inherit!(parent.representable_attrs) # Module just inherits.
|
120
|
-
end
|
121
|
-
|
122
117
|
def prepare(represented)
|
123
118
|
represented.extend(self)
|
124
119
|
end
|
125
120
|
end
|
126
121
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
include mod
|
132
|
-
register_feature(mod)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
private
|
137
|
-
def register_feature(mod)
|
138
|
-
representable_attrs[:features][mod] = true
|
139
|
-
end
|
122
|
+
require "representable/deprecations"
|
123
|
+
def self.deprecations=(value)
|
124
|
+
evaluator = value==false ? Binding::EvaluateOption : Binding::Deprecation::EvaluateOption
|
125
|
+
::Representable::Binding.send :include, evaluator
|
140
126
|
end
|
127
|
+
self.deprecations = true # TODO: change to false in 2.5 or remove entirely.
|
141
128
|
end
|
142
129
|
|
143
|
-
require 'representable/autoload'
|
130
|
+
require 'representable/autoload'
|
@@ -1,143 +1,57 @@
|
|
1
1
|
module Representable
|
2
|
-
# The Binding
|
3
|
-
|
4
|
-
# The flow when parsing is Binding#read_fragment -> Populator -> Deserializer.
|
5
|
-
# Actual parsing the fragment from the document happens in Binding#read, everything after that is generic.
|
2
|
+
# The Binding provides methods to read/write the fragment for one property.
|
6
3
|
#
|
7
|
-
#
|
4
|
+
# Actually parsing the fragment from the document happens in Binding#read, everything after that is generic.
|
8
5
|
class Binding
|
9
6
|
class FragmentNotFound
|
10
7
|
end
|
11
8
|
|
12
|
-
def self.build(definition
|
13
|
-
|
14
|
-
|
15
|
-
build_for(definition, *args)
|
9
|
+
def self.build(definition)
|
10
|
+
return definition.create_binding if definition[:binding]
|
11
|
+
build_for(definition)
|
16
12
|
end
|
17
13
|
|
18
|
-
def initialize(definition
|
14
|
+
def initialize(definition)
|
19
15
|
@definition = definition
|
20
|
-
@parent_decorator = parent_decorator # DISCUSS: where's this needed?
|
21
|
-
|
22
|
-
# static options. do this once.
|
23
|
-
@representable = @definition.representable?
|
24
16
|
@name = @definition.name
|
25
|
-
@skip_filters = self[:readable]==false || self[:writeable]==false || self[:if] # Does this binding contain :if, :readable or :writeable settings?
|
26
17
|
@getter = @definition.getter
|
27
18
|
@setter = @definition.setter
|
28
|
-
@array = @definition.array?
|
29
|
-
@typed = @definition.typed?
|
30
|
-
@has_default = @definition.has_default?
|
31
|
-
end
|
32
|
-
|
33
|
-
attr_reader :user_options, :represented # TODO: make private/remove.
|
34
|
-
|
35
|
-
# DISCUSS: an overall strategy to speed up option reads will come around 3.0.
|
36
|
-
attr_reader :representable, :name, :getter, :setter, :array, :typed, :skip_filters, :has_default
|
37
|
-
alias_method :representable?, :representable
|
38
|
-
alias_method :array?, :array
|
39
|
-
alias_method :typed?, :typed
|
40
|
-
alias_method :skip_filters?, :skip_filters
|
41
|
-
alias_method :has_default?, :has_default
|
42
19
|
|
43
|
-
|
44
|
-
@as ||= evaluate_option(:as)
|
20
|
+
setup_exec_context!
|
45
21
|
end
|
46
22
|
|
23
|
+
attr_reader :name, :getter, :setter
|
24
|
+
extend Uber::Delegates
|
25
|
+
delegates :@definition, :has_default?, :representable?, :array?, :typed?
|
26
|
+
|
47
27
|
# Single entry points for rendering and parsing a property are #compile_fragment
|
48
28
|
# and #uncompile_fragment in Mapper.
|
49
|
-
#
|
50
|
-
# DISCUSS:
|
51
|
-
# currently, we need to call B#update! before compile_fragment/uncompile_fragment.
|
52
|
-
# this will change to B#renderer(represented, options).call
|
53
|
-
# B#parser (represented, options).call
|
54
|
-
# goal is to have two objects for 2 entirely different tasks.
|
55
29
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
write_fragment(doc, value)
|
30
|
+
module Deprecatable
|
31
|
+
# Retrieve value and write fragment to the doc.
|
32
|
+
def compile_fragment(options)
|
33
|
+
render_pipeline(nil, options).(nil, options)
|
61
34
|
end
|
62
|
-
end
|
63
35
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
read_fragment(doc)
|
36
|
+
# Parse value from doc and update the model property.
|
37
|
+
def uncompile_fragment(options)
|
38
|
+
parse_pipeline(options[:doc], options).(options[:doc], options)
|
68
39
|
end
|
69
40
|
end
|
41
|
+
include Deprecatable
|
70
42
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
render_fragment(value, doc)
|
77
|
-
end
|
78
|
-
|
79
|
-
def render_fragment(value, doc)
|
80
|
-
fragment = serialize(value) { return } # render fragments of hash, xml, yaml.
|
81
|
-
|
82
|
-
write(doc, fragment)
|
83
|
-
end
|
84
|
-
|
85
|
-
def read_fragment(doc)
|
86
|
-
fragment = read(doc) # scalar, Array, or Hash (abstract format) or un-deserialised fragment(s).
|
87
|
-
|
88
|
-
populator.call(fragment, doc)
|
89
|
-
end
|
90
|
-
|
91
|
-
def render_filter(value, doc)
|
92
|
-
evaluate_option(:render_filter, value, doc) { value }
|
93
|
-
end
|
94
|
-
|
95
|
-
def parse_filter(value, doc)
|
96
|
-
evaluate_option(:parse_filter, value, doc) { value }
|
97
|
-
end
|
98
|
-
|
99
|
-
def get
|
100
|
-
evaluate_option(:getter) do
|
101
|
-
exec_context.send(getter)
|
43
|
+
module EvaluateOption
|
44
|
+
def evaluate_option(name, input=nil, options={})
|
45
|
+
proc = self[name]
|
46
|
+
proc.(send(:exec_context, options), options) # from Uber::Options::Value. # NOTE: this can also be the Proc object if it's not wrapped by Uber:::Value.
|
102
47
|
end
|
103
48
|
end
|
104
|
-
|
105
|
-
def set(value)
|
106
|
-
evaluate_option(:setter, value) do
|
107
|
-
exec_context.send(setter, value)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# DISCUSS: do we really need that?
|
112
|
-
# 1.38 0.104 0.021 0.000 0.083 40001 Representable::Binding#representer_module_for
|
113
|
-
# 1.13 0.044 0.017 0.000 0.027 40001 Representable::Binding#representer_module_for (with memoize).
|
114
|
-
def representer_module_for(object, *args)
|
115
|
-
# TODO: cache this!
|
116
|
-
evaluate_option(:extend, object) # TODO: pass args? do we actually have args at the time this is called (compile-time)?
|
117
|
-
end
|
118
|
-
|
119
|
-
# Evaluate the option (either nil, static, a block or an instance method call) or
|
120
|
-
# executes passed block when option not defined.
|
121
|
-
def evaluate_option(name, *args)
|
122
|
-
unless proc = @definition[name] # TODO: this could dispatch directly to the @definition using #send?
|
123
|
-
return yield if block_given?
|
124
|
-
return
|
125
|
-
end
|
126
|
-
|
127
|
-
# TODO: it would be better if user_options was nil per default and then we just don't pass it into lambdas.
|
128
|
-
options = self[:pass_options] ? Options.new(self, user_options, represented, parent_decorator) : user_options
|
129
|
-
|
130
|
-
proc.evaluate(exec_context, *(args<<options)) # from Uber::Options::Value.
|
131
|
-
end
|
49
|
+
# include EvaluateOption
|
132
50
|
|
133
51
|
def [](name)
|
134
52
|
@definition[name]
|
135
53
|
end
|
136
54
|
|
137
|
-
# 1.55 0.031 0.022 0.000 0.009 60004 Representable::Binding#skipable_empty_value?
|
138
|
-
# 1.51 0.030 0.022 0.000 0.008 60004 Representable::Binding#skipable_empty_value?
|
139
|
-
# after polymorphism:
|
140
|
-
# 1.44 0.031 0.022 0.000 0.009 60002 Representable::Binding#skipable_empty_value?
|
141
55
|
def skipable_empty_value?(value)
|
142
56
|
value.nil? and not self[:render_nil]
|
143
57
|
end
|
@@ -147,112 +61,41 @@ module Representable
|
|
147
61
|
value
|
148
62
|
end
|
149
63
|
|
150
|
-
# Note: this method is experimental.
|
151
|
-
def update!(represented, user_options)
|
152
|
-
@represented = represented
|
153
|
-
|
154
|
-
setup_user_options!(user_options)
|
155
|
-
setup_exec_context!
|
156
|
-
end
|
157
|
-
|
158
64
|
attr_accessor :cached_representer
|
159
65
|
|
160
|
-
|
161
|
-
|
162
|
-
@user_options = user_options
|
163
|
-
# this is the propagated_options.
|
164
|
-
@user_options = user_options.merge(wrap: false) if self[:wrap] == false
|
66
|
+
require "representable/pipeline_factories"
|
67
|
+
include Factories
|
165
68
|
|
166
|
-
|
167
|
-
end
|
69
|
+
private
|
168
70
|
|
169
|
-
# 1.80 0.066 0.027 0.000 0.039 30002 Representable::Binding#setup_exec_context!
|
170
|
-
# 0.98 0.034 0.014 0.000 0.020 30002 Representable::Binding#setup_exec_context!
|
171
71
|
def setup_exec_context!
|
172
|
-
|
173
|
-
@exec_context = self
|
174
|
-
@exec_context =
|
72
|
+
@exec_context = ->(options) { options[:represented] } unless self[:exec_context]
|
73
|
+
@exec_context = ->(options) { self } if self[:exec_context] == :binding
|
74
|
+
@exec_context = ->(options) { options[:decorator] } if self[:exec_context] == :decorator
|
175
75
|
end
|
176
76
|
|
177
|
-
|
178
|
-
|
179
|
-
def serialize(object, &block)
|
180
|
-
serializer.call(object, &block)
|
77
|
+
def exec_context(options)
|
78
|
+
@exec_context.(options)
|
181
79
|
end
|
182
80
|
|
183
|
-
|
184
|
-
|
185
|
-
Serializer
|
186
|
-
end
|
187
|
-
|
188
|
-
def serializer
|
189
|
-
@serializer ||= serializer_class.new(self)
|
190
|
-
end
|
191
|
-
|
192
|
-
def populator
|
193
|
-
@populator ||= populator_class.new(self)
|
194
|
-
end
|
195
|
-
|
196
|
-
def populator_class
|
197
|
-
Populator
|
198
|
-
end
|
199
|
-
|
200
|
-
def deserializer_class
|
201
|
-
Deserializer
|
202
|
-
end
|
203
|
-
|
204
|
-
def deserializer
|
205
|
-
@deserializer ||= deserializer_class.new(self)
|
206
|
-
end
|
81
|
+
def parse_pipeline(input, options)
|
82
|
+
@parse_pipeline ||= pipeline_for(:parse_pipeline, input, options) { Pipeline[*parse_functions] }
|
207
83
|
end
|
208
|
-
include Factories
|
209
|
-
|
210
|
-
|
211
|
-
# Options instance gets passed to lambdas when pass_options: true.
|
212
|
-
# This is considered the new standard way and should be used everywhere for forward-compat.
|
213
|
-
Options = Struct.new(:binding, :user_options, :represented, :decorator)
|
214
84
|
|
85
|
+
def render_pipeline(input, options)
|
86
|
+
@render_pipeline ||= pipeline_for(:render_pipeline, input, options) { Pipeline[*render_functions] }
|
87
|
+
end
|
215
88
|
|
216
89
|
# generics for collection bindings.
|
217
90
|
module Collection
|
218
|
-
private
|
219
|
-
def populator_class
|
220
|
-
Populator::Collection
|
221
|
-
end
|
222
|
-
|
223
|
-
def serializer_class
|
224
|
-
Serializer::Collection
|
225
|
-
end
|
226
|
-
|
227
|
-
def deserializer_class
|
228
|
-
Deserializer::Collection
|
229
|
-
end
|
230
|
-
|
231
91
|
def skipable_empty_value?(value)
|
232
92
|
# TODO: this can be optimized, again.
|
233
93
|
return true if value.nil? and not self[:render_nil] # FIXME: test this without the "and"
|
234
94
|
return true if self[:render_empty] == false and value and value.size == 0 # TODO: change in 2.0, don't render emtpy.
|
235
95
|
end
|
236
96
|
end
|
237
|
-
|
238
|
-
# and the same for hashes.
|
239
|
-
module Hash
|
240
|
-
private
|
241
|
-
def populator_class
|
242
|
-
Populator::Hash
|
243
|
-
end
|
244
|
-
|
245
|
-
def serializer_class
|
246
|
-
Serializer::Hash
|
247
|
-
end
|
248
|
-
|
249
|
-
def deserializer_class
|
250
|
-
Deserializer::Hash
|
251
|
-
end
|
252
|
-
end
|
253
97
|
end
|
254
98
|
|
255
|
-
|
256
99
|
class DeserializeError < RuntimeError
|
257
100
|
end
|
258
101
|
end
|