traitorous 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ce24a9926d5a1a1473d4ecce829f5ae67753747e
4
- data.tar.gz: 935ffb3bb8ace183357d11db17ed1b9a45a07cd8
3
+ metadata.gz: 96b54f11016e53adedd46e355dda6e15df91ba0f
4
+ data.tar.gz: 16d6ae6e551d2a8a636667e05dae8a0187347539
5
5
  SHA512:
6
- metadata.gz: e4d983bdef166b6f527e01ab327d8e1ecdade88fec74f372bb0d534a31833ddbdf1921a23457cfe2b9fb16136f1547fe32dd1b92cf623c0853b6cd82906d5705
7
- data.tar.gz: 5df97df341451212529e99b632aa18267c4a74648aa4f074e6be9ee2184617b8a830c5d71b40b76151907ff18014e600a7aea30873aa8ba5c06e0839b2a73a38
6
+ metadata.gz: e167111bfa664852ed9a3ca79742196f3744b02464912dea3aa079c0468f8a5ef5b59a4caaab112760129eb03b8833aa47ef9b6c4fe22e33b8962cac444ab5fd
7
+ data.tar.gz: 28f02d34b627d998df66e5b01923b5925ce5f2a546c7b3f16f2018ab0183b3f5ec7c8b5aefb9f849dd5b894ee336f9b322dbb992fe42989c4a15bd4298e682e6
data/README.md CHANGED
@@ -16,8 +16,10 @@ The converters can be used to help (de)serialize, set default values, do
16
16
  validations and translations. The converter's job is to be able to import a
17
17
  value, instantiating into classes and assigning values.
18
18
 
19
- I took a lot of inspiration from [virtus](https://github.com/solnic/virtus).
19
+ No type information is included in a trait. Only the Converter that will
20
+ import and export it.
20
21
 
22
+ I took a lot of inspiration from [virtus](https://github.com/solnic/virtus).
21
23
 
22
24
  ## Installation
23
25
 
@@ -41,7 +43,9 @@ Or install it yourself as:
41
43
 
42
44
  # see spec/
43
45
  require 'traitorous'
44
-
46
+
47
+ # trait converters default to Convert.skip which is a converter that just
48
+ # passes through it's data unchanged.
45
49
  class Ruin
46
50
  include Traitorous
47
51
  include Traitorous::Equality
@@ -63,12 +67,16 @@ Or install it yourself as:
63
67
  puts Ruin.new(r.export) == r
64
68
  # true
65
69
 
70
+ # the Convert.default converter provides a default value if data is falsey
71
+ # the Convert.model converter instantiates the class given as a new object
72
+ # using data, if the 2nd arg == :array or [], each element of the array is
73
+ # instantiated.
66
74
  class Area
67
75
  include Traitorous
68
76
  include Traitorous::Equality
69
77
  trait :name
70
- trait :size, Converter::DefaultValueStatic.new('sub-continent')
71
- trait :ruins, Converter::UniformArray.new(Ruin)
78
+ trait :size, Convert.default('sub-continent')
79
+ trait :ruins, Convert.model(Ruin, :array) # or Convert.array(Convert.call_on(Ruin))
72
80
  end
73
81
 
74
82
  area = Area.new(
@@ -96,51 +104,113 @@ in a simple form ready to save.
96
104
  This system should be flexible enough to account for an large variety of data
97
105
  structures that can be read in and out of storage easily and in 1 tree.
98
106
 
99
- The 5 that are in the system should cover a lot of ground, anything trickier
100
- should just be implemented as it's own Converter, ensuring that it responds to
101
- `#do_import(data)` and `#do_export(data)`.
107
+ ### Basic Procs
108
+ Each converter is really just a thin wrapper around a pair of procs (or other
109
+ object that responds to .call(data).
110
+
111
+ Each proc takes data, and when called, converts that data into something else
112
+
113
+ #### StandardProcs.noop
114
+ The noop proc simply returns the value passed to it.
115
+ `# proc{|data| data}`
116
+
117
+ #### StandardProcs.default(default_value)
118
+ The default proc provides a default value if data is falsey.
119
+ `StandardProcs.default(:default_value) # proc{|data| data || :default_value }
120
+
121
+ #### StandardProcs.call_on_self(method_name)
122
+ The call_on_self proc calls method_name on data.
123
+ `StandardProcs.call_on_self(:intern) # proc{|data| data.send(:intern) }`
124
+ `StandardProcs.call_on_self(:intern) # proc{|data| data.send(:to_s) }`
125
+
126
+ #### StandardProcs.call_on(klass, with_method: :new)
127
+ The call_on proc uses data as params for a method call.
128
+ `StandardProcs.call_on(Pathname) # proc{|data| Pathname.send(:new,data)}`
129
+ `StandardProcs.call_on(URI, :parse) # proc{|data| URI.send(:parse,data)}`
130
+
131
+ #### StandardProcs.map(&block)
132
+ The map proc converts each element of Array(data) using block.
133
+ `StandardProcs.map{|data| URI.send(:parse,data)}`
134
+
135
+ #### StandardProcs.inject(memo_obj)(&block)
136
+ The map proc converts each element of Array(data) uses a memo_obj using block.
137
+ `StandardProcs.inject({}){|memo,data| memo[data.name] = data}`
138
+
139
+ #### Custom
140
+ A proc is very easy to create, creating simple procs to do various tasks can
141
+ simply many complex tasks
142
+ ```ruby
143
+ dt_proc = proc{|data| Time.now()}
144
+ uri_proc = proc{|data| URI.parse(data)}
145
+ uri_out_proc = proc{|data| data.to_s }
146
+ tag_proc = proc{|data| data.split(/,/)
147
+ tag_join_proc = proc{|data| data.join(',')}
148
+ ```
149
+
150
+ ## Basic Converters
151
+ The converters provided by the Convert module use various of the StandardProcs
152
+ to achieve commonly used patterns.
102
153
 
103
- Most of the converters wil have a pair of optional arguments: import_method
104
- (defaults to :new) and export_method (defaults to :export). Export is simple,
105
- it's the method called on the object, objects created using Traitorous
106
- automatically respond_to export. set export_method to :to_s in order to convert
107
- your Pathname object into a string. Set import_method to :parse, to create your
108
- object with URI.parse. This should provide a tremendous amount of versatility.
154
+ The Converter itself is a simple object that takes an importer, and an optional
155
+ exporter and the provides a thin wrapper for `.import(data)` and `.export(data)`
156
+
157
+ ### Convert.noop
158
+ This is the default converter for trait, both importer and exporter are
159
+ `StandardProcs.noop`. The primary purpose of this is so that data that doesn't
160
+ need conversion have a simple converter that conforms to the same API as other
161
+ converters.
162
+
163
+ ### Convert.default(default_value)
164
+ This provides a `StandardProcs.default(default_value)` importer, and a
165
+ `StandardProcs.noop` exporter.
166
+
167
+ Currently there isn't a way to skip nil or empty values on export, and so I've
168
+ never had to make a decision about a more complex `.export(data)` proc for this
169
+ converter. Changing the signature to `StandardProcs.default(default_value, :include_on_export)`
170
+ or it's logical opposite. But I'm not sure which default behaviour is better.
171
+
172
+ ### Convert.model(model_name, container = :scalar)
173
+ This is a pure convenience method that provides simple Object instantiaion
174
+ for single objects, arrays, and hash values. container may be :scaler, :array,
175
+ :hash. The container simply does a `proc{|data| model_name.new(data)}` on
176
+ the single scalar, on each element of an array, or on each value of a hash.
109
177
 
110
178
  ```ruby
111
- # Example of using the defaults for IMport_method and export_method
112
- # import_method defaults to :new
113
- # export_method defaults to :export
114
- batman_data = {name: 'Batman', secret_identity: 'Bruce Wayne'}
115
- converter = Model.new(Superhero)
116
- superhero = converter.do_import(batman_data)
117
- # does this
118
- Superhero.send(import_method, data)
119
- # and is the same result as
120
- Superhero.new(batman_data)
121
- # exports by
122
- converter.do_export(superhero)
123
- # does this
124
- superhero.send(export_method)
125
- # and is the same result as
126
- superhero.export
127
-
128
- # Example of using a custom import_method and export_method
129
- batman_data_uri = '/batman/current'
130
- converter = Model.new(Superhero, import_method: :parse, export_method: :to_s)
131
- superhero = converter.do_import(batman_data)
132
- # does this
133
- uri = URI.send(import_method, batman_data)
134
- # and is the same result as
135
- URI.parse(batman_data)
136
- # exports by
137
- converter.do_export(uri)
138
- # does this
139
- uri.send(export_method)
140
- # and is the same result as
141
- uri.to_s
179
+ # common uses
180
+ Convert.model(Pathname) # Pathname.new(data)
181
+ Convert.model(CustomClass, :array) # [CustomClass<#...>,CustomClass<#...>,...]
182
+ Convert.model(CustomClass, :hash) # {'orig_key_1' => CustomClass<#...>,'orig_key_2' => CustomClass<#...>...}
142
183
  ```
143
184
 
185
+ ### Convert.call_on_self(with_method, export_with: :itself)
186
+ This provides an `StandardProcs.call_on_self(with_method)` importer and
187
+ `StandardProcs.call_on_self(export_with)` exporter
188
+
189
+ Currently there is no way to pass additional arguments on the method call. It
190
+ should be easy enough to create a custom Converter if you need to do that.
191
+
192
+ ```ruby
193
+ # common uses
194
+ Convert.call_on_self(:intern, export_with: :to_s)
195
+ Convert.call_on_self(:to_i)
196
+ ```
197
+
198
+ ### Convert.call_on(klass, with: :new, export_with: :export)
199
+ This provides a `StandardProcs.call_on(klass, with_method: with)` importer
200
+ and a `StandardProcs.call_on_self(:export)` exporter.
201
+
202
+ Currently there is no way to pass additional arguments on the method call. It
203
+ should be easy enough to create a custom Converter if you need to do that.
204
+
205
+ ```
206
+ # common uses
207
+ c = Convert.call_on(Pathname) # Pathname.new(data)
208
+ c = Convert.call_on(URI, :parse) # URI.parse(data)
209
+ c = Convert.call_on(CustomClass, with: :import) # CustomClass.import(data)
210
+ ```
211
+
212
+ ### Convert.array(converter)
213
+
144
214
  ##### export
145
215
  My current design kind of places a lot of the responsibilities for the export
146
216
  sequence of the objects themselves. And I'm both satisied with this design and
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.1
1
+ 0.6.2
@@ -45,7 +45,7 @@ module Traitorous
45
45
  # be any object or module.
46
46
  #
47
47
  # defaults to Traitorous::Converter::DEFAULT
48
- def trait(attr_name, converter = Convert.skip)
48
+ def trait(attr_name, converter = Convert.noop)
49
49
  self.traits ||= HASH.new
50
50
  self.traits[attr_name.intern] = converter
51
51
  attr_accessor attr_name
@@ -6,7 +6,7 @@ module Traitorous
6
6
  # .skip provides the simplest converter. It does nothing to the value
7
7
  # except pass it through unchanged on both import and export
8
8
  # @return [Traitorous::Converter]
9
- def skip
9
+ def noop
10
10
  Converter.new(
11
11
  StandardProcs.noop,
12
12
  StandardProcs.noop
@@ -25,7 +25,6 @@ module Traitorous
25
25
  )
26
26
  end
27
27
 
28
-
29
28
  # .call_on creates a StandardProc.call_on proc for the importer, and a
30
29
  # StandardProc.call_on_self proc for the exporter.
31
30
  # @param method_name [Symbol, String] method to send data
@@ -39,13 +38,29 @@ module Traitorous
39
38
  )
40
39
  end
41
40
 
41
+ # .model(model_name) instantiates a model_name using model_name.new(data)
42
+ # @param model_name [Class,#new] the name of the model to be instantiated
43
+ # @return [Traitorous::Converter]
44
+ def model(model_name, container = :scalar)
45
+ case container
46
+ when :scalar
47
+ call_on(model_name)
48
+ when :array, []
49
+ self.array(self.call_on(model_name))
50
+ when :hash, {}
51
+ self.hash(self.call_on(model_name))
52
+ else
53
+ raise ArgumentError.new("No container of type: #{container}")
54
+ end
55
+ end
56
+
42
57
  # .call_on_self creates a StandardProc.call_on_self proc for the importer
43
58
  # with the with_method param, and a StandardProc.call_on_self with the
44
59
  # export_with param
45
60
  # @param with_method [Symbol, String] method to send data on import
46
61
  # @param export_with [Symbol, String] method to send data on export
47
62
  # @return [Traitorous::Converter]
48
- def call_on_self(with_method, export_with: :export)
63
+ def call_on_self(with_method, export_with: :itself)
49
64
  Converter.new(
50
65
  StandardProcs.call_on_self(with_method),
51
66
  StandardProcs.call_on_self(export_with)
@@ -60,8 +75,8 @@ module Traitorous
60
75
  # @return [Traitorous::Converter]
61
76
  def array(converter)
62
77
  Converter.new(
63
- proc { |data| Array(data).map { |entry| converter.importer.call(entry) } },
64
- proc { |data| Array(data).map { |entry| converter.exporter.call(entry) } }
78
+ StandardProcs.map { |entry| converter.importer.call(entry) },
79
+ StandardProcs.map { |entry| converter.exporter.call(entry) }
65
80
  )
66
81
  end
67
82
 
@@ -71,8 +86,8 @@ module Traitorous
71
86
  # @return [Traitorous::Converter]
72
87
  def hash(value_converter)
73
88
  Converter.new(
74
- proc { |data| data.inject({}) { |h, (k, v)| h[k] = value_converter.importer.call(v); h } },
75
- proc { |data| data.inject({}) { |h, (k, v)| h[k] = value_converter.exporter.call(v); h } },
89
+ StandardProcs.inject({}) { |h, (k, v)| h[k] = value_converter.importer.call(v) },
90
+ StandardProcs.inject({}) { |h, (k, v)| h[k] = value_converter.exporter.call(v) },
76
91
  )
77
92
  end
78
93
 
@@ -90,13 +105,11 @@ module Traitorous
90
105
  # @return [Traitorous::Converter]
91
106
  def array_to_hash(key_converter, value_converter)
92
107
  Converter.new(
93
- proc do |data_arr|
94
- data_arr.inject({}) do |h, d|
95
- v = value_converter.importer.call(d)
96
- k = key_converter.importer.call(v)
97
- h[k] = v
98
- h
99
- end
108
+ StandardProcs.inject({}) do |memo_obj, data|
109
+ value = value_converter.importer.call(data)
110
+ key = key_converter.importer.call(value, data)
111
+ memo_obj[key] = value
112
+ memo_obj
100
113
  end,
101
114
  proc { |data_hsh| data_hsh.values.map { |value| value_converter.exporter.call(value) } }
102
115
  )
@@ -12,14 +12,14 @@ module Traitorous
12
12
  # otherwise it returns it's default value
13
13
  # @param default_value [Object] the default value to use with data is
14
14
  # falsey
15
- # @return [Object] returns data || default_value
15
+ # @return [Proc]
16
16
  def default(default_value)
17
17
  proc { |data| data || default_value }
18
18
  end
19
19
 
20
20
  # This proc calls method_name on data.
21
21
  # @param method_name [Symbol, String] method to send data
22
- # @return [Object] result of calling method_name on data
22
+ # @return [Proc]
23
23
  def call_on_self(method_name)
24
24
  proc { |data| data.send(method_name) }
25
25
  end
@@ -27,8 +27,32 @@ module Traitorous
27
27
  # This proc calls klass with :with and passes data as params
28
28
  # @param klass [Class, Module, Object] obj to call
29
29
  # @param with_method: [Symbol, String] method to send klass with data as params
30
+ # @return [Proc]
30
31
  def call_on(klass, with_method: :new)
31
- proc {|data| klass.send(with_method, data)}
32
+ proc { |data| klass.send(with_method, data) }
33
+ end
34
+
35
+ # This proc calls map on data, and calls the block for each element.
36
+ # @param block [Class, Module, Object] obj to call
37
+ # @return [Proc]
38
+ def map(&block)
39
+ raise ArgumentError.new("Must be called with block") unless block_given?
40
+ proc { |data_arr| Array(data_arr).map { |data| block.call(data) } }
41
+ end
42
+
43
+ # This proc calls map on data, and calls the block for each element.
44
+ # @param memo_obj [Array,Hash,#dup] dup called on this and used as the
45
+ # seed object for the inject call.
46
+ # @param block [Class, Module, Object] obj to call
47
+ # @return [Proc]
48
+ def inject(memo_obj, &block)
49
+ raise ArgumentError.new("Must be called with block") unless block_given?
50
+ proc do |data_arr|
51
+ Array(data_arr).inject(memo_obj.dup) do |memo_obj, data|
52
+ block.call(memo_obj, data)
53
+ memo_obj
54
+ end
55
+ end
32
56
  end
33
57
  end
34
58
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: traitorous
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - scott m parrish
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-10-10 00:00:00.000000000 Z
11
+ date: 2015-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport