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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +47 -0
  3. data/Gemfile +5 -0
  4. data/README.md +33 -0
  5. data/lib/representable.rb +60 -73
  6. data/lib/representable/binding.rb +37 -194
  7. data/lib/representable/cached.rb +10 -46
  8. data/lib/representable/coercion.rb +8 -8
  9. data/lib/representable/config.rb +15 -75
  10. data/lib/representable/debug.rb +41 -59
  11. data/lib/representable/declarative.rb +34 -53
  12. data/lib/representable/decorator.rb +11 -40
  13. data/lib/representable/definition.rb +14 -15
  14. data/lib/representable/deprecations.rb +90 -0
  15. data/lib/representable/deserializer.rb +87 -82
  16. data/lib/representable/for_collection.rb +5 -3
  17. data/lib/representable/hash.rb +5 -3
  18. data/lib/representable/hash/binding.rb +6 -15
  19. data/lib/representable/hash/collection.rb +10 -6
  20. data/lib/representable/hash_methods.rb +5 -5
  21. data/lib/representable/insert.rb +31 -0
  22. data/lib/representable/json.rb +7 -3
  23. data/lib/representable/json/hash.rb +1 -1
  24. data/lib/representable/object/binding.rb +5 -5
  25. data/lib/representable/parse_strategies.rb +37 -3
  26. data/lib/representable/pipeline.rb +37 -5
  27. data/lib/representable/pipeline_factories.rb +88 -0
  28. data/lib/representable/serializer.rb +38 -44
  29. data/lib/representable/version.rb +1 -1
  30. data/lib/representable/xml.rb +4 -0
  31. data/lib/representable/xml/binding.rb +25 -31
  32. data/lib/representable/xml/collection.rb +5 -3
  33. data/lib/representable/xml/hash.rb +7 -2
  34. data/lib/representable/yaml.rb +6 -3
  35. data/lib/representable/yaml/binding.rb +4 -4
  36. data/representable.gemspec +3 -3
  37. data/test/---deserialize-pipeline_test.rb +37 -0
  38. data/test/binding_test.rb +7 -7
  39. data/test/cached_test.rb +31 -19
  40. data/test/coercion_test.rb +2 -2
  41. data/test/config/inherit_test.rb +13 -12
  42. data/test/config_test.rb +12 -67
  43. data/test/decorator_test.rb +4 -5
  44. data/test/default_test.rb +34 -0
  45. data/test/defaults_options_test.rb +93 -0
  46. data/test/definition_test.rb +19 -39
  47. data/test/exec_context_test.rb +1 -1
  48. data/test/filter_test.rb +18 -20
  49. data/test/getter_setter_test.rb +1 -8
  50. data/test/hash_bindings_test.rb +13 -13
  51. data/test/heritage_test.rb +62 -0
  52. data/test/if_test.rb +1 -0
  53. data/test/inherit_test.rb +5 -3
  54. data/test/instance_test.rb +3 -4
  55. data/test/json_test.rb +3 -59
  56. data/test/lonely_test.rb +47 -3
  57. data/test/nested_test.rb +8 -2
  58. data/test/pipeline_test.rb +259 -0
  59. data/test/populator_test.rb +76 -0
  60. data/test/realistic_benchmark.rb +39 -7
  61. data/test/render_nil_test.rb +21 -0
  62. data/test/represent_test.rb +2 -2
  63. data/test/representable_test.rb +33 -103
  64. data/test/schema_test.rb +5 -15
  65. data/test/serialize_deserialize_test.rb +2 -2
  66. data/test/skip_test.rb +1 -1
  67. data/test/test_helper.rb +6 -0
  68. data/test/uncategorized_test.rb +67 -0
  69. data/test/xml_bindings_test.rb +6 -6
  70. data/test/xml_test.rb +6 -6
  71. metadata +33 -13
  72. data/lib/representable/apply.rb +0 -13
  73. data/lib/representable/inheritable.rb +0 -71
  74. data/lib/representable/mapper.rb +0 -83
  75. data/lib/representable/populator.rb +0 -56
  76. data/test/inheritable_test.rb +0 -97
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 304ec8f66cc93beff15aba84d2d4b5d5214fa5d1
4
- data.tar.gz: 83a928ef451bd032509abb6c41d8ababda744a97
3
+ metadata.gz: a7d63369f6aa46d6611bd4f968a61ed24e91c1eb
4
+ data.tar.gz: f6708f841e1ada1e9e17cfd5a7c6bb43300d05d1
5
5
  SHA512:
6
- metadata.gz: 5eb021bc55145ff44e467db742d2842c8971413b44f084df9046f9eb6ee66e5250fa523fae0f8d0f3f0f3938c2e38339ca08876bcd04f6fb291ab8fb4368b0b7
7
- data.tar.gz: fc426cbb854f6aef32a99b5bdec3e0f364176cf9ece0d35f19c43200434db12a2d8f545430f139cc8f452baf3598183ba8bd1ab53b6ce6d944512a5ac31c1fa5
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
@@ -11,4 +11,9 @@ end
11
11
  group :test do
12
12
  gem "nokogiri", require: false
13
13
  gem "multi_json", require: false
14
+ gem "minitest-line"
15
+ gem "pry"
14
16
  end
17
+
18
+ # gem "declarative", path: "../declarative"
19
+ # gem "declarative", github: "apotonick/declarative"
data/README.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  Representable maps Ruby objects to documents and back.
4
4
 
5
+ [![Gitter Chat](https://badges.gitter.im/trailblazer/chat.svg)](https://gitter.im/trailblazer/chat)
6
+ [![Build
7
+ Status](https://travis-ci.org/apotonick/representable.svg)](https://travis-ci.org/apotonick/representable)
8
+ [![Gem Version](https://badge.fury.io/rb/representable.svg)](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.
@@ -1,21 +1,18 @@
1
- require 'forwardable'
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 "uber/callable"
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
- extend ClassInclusions, ModuleExtensions
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, private_options = normalize_options!(options)
34
+ propagated_options = normalize_options(options)
40
35
 
41
- representable_mapper(format, propagated_options).deserialize(represented, doc, propagated_options, private_options)
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, private_options = normalize_options!(options)
42
+ propagated_options = normalize_options(options) # {_private: {include: }, is_admin: true}
47
43
 
48
- representable_mapper(format, propagated_options).serialize(represented, doc, propagated_options, private_options)
44
+ representable_map!(doc, propagated_options, format, :compile_fragment)
45
+ doc
49
46
  end
50
47
 
51
- def representable_bindings_for(format, options)
52
- representable_attrs.collect {|definition| representable_binding_for(definition, format, options) }
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 representable_binding_for(definition, format, options)
56
- format.build(definition, self)
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!(options)
78
+ def normalize_options(options)
62
79
  # here, we could also filter out local options e.g. like options[:band].
63
- private_options = {}
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
- [propagated_options, private_options]
73
- end
90
+ propagated_options[:_private] = private_options if private_options.any?
74
91
 
75
- def representable_attrs
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 representable_mapper(format, options)
80
- bindings = representable_bindings_for(format, options)
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(nil, represented, *args) { self.class.name }
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
- module Feature
129
- def feature(*mods)
130
- mods.each do |mod|
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 wraps the Definition instance for this property and provides methods to read/write fragments.
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
- # Serialization: Serializer -> {frag}/[frag]/frag -> Binding#write
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, *args)
13
- # DISCUSS: move #create_binding to this class?
14
- return definition.create_binding(*args) if definition[:binding]
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, parent_decorator)
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
- def as # DISCUSS: private?
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
- # Retrieve value and write fragment to the doc.
57
- def compile_fragment(doc)
58
- evaluate_option(:writer, doc) do
59
- value = render_filter(get, doc)
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
- # Parse value from doc and update the model property.
65
- def uncompile_fragment(doc)
66
- evaluate_option(:reader, doc) do
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
- def write_fragment(doc, value)
72
- value = default_for(value)
73
-
74
- return if skipable_empty_value?(value)
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
- private
161
- def setup_user_options!(user_options)
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
- # @user_options = user_options.merge(wrap: self[:wrap]) if self[:wrap]
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
- return @exec_context = @represented unless self[:exec_context]
173
- @exec_context = self if self[:exec_context] == :binding
174
- @exec_context = parent_decorator if self[:exec_context] == :decorator
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
- attr_reader :exec_context, :parent_decorator
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
- module Factories
184
- def serializer_class
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