objectifier 0.2.0 → 0.3.0
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/CHANGELOG.md +16 -0
- data/README.md +9 -8
- data/TODO.md +17 -0
- data/example/money/example.rb +105 -0
- data/example/simple/example.rb +117 -0
- data/lib/objectifier/array_value.rb +0 -4
- data/lib/objectifier/map_value.rb +0 -12
- data/lib/objectifier/scalar_value.rb +0 -5
- data/lib/objectifier/transformer_factory.rb +30 -5
- data/lib/objectifier/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 815817635baa45e7a1ddf70cfbecd860b2b33e64
|
4
|
+
data.tar.gz: 64be5d2076c97859304f5ab810849ec98f08c1f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 584bd0a3fa9f96bb636acd0c7318bcdd276762e010f00f266a951596599a58b47e038a213266f02e0527a912cfc792756be4909af797837f691b44865166460b
|
7
|
+
data.tar.gz: 1d16c9f434d6167cd271cbd66a962af5763a705fbdad55ee0c2cda00a18c6a8a9b3c40bef5c62fb9d6fa4b25e2727aa55cb939e40f6987fbf4891781babf34d6
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## UNRELEASED
|
4
|
+
|
5
|
+
|
6
|
+
## 0.3.0 2017-03-07
|
7
|
+
|
8
|
+
### Added
|
9
|
+
Tests for built-in transformer functions
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
Floating point transformer to not silently convert strings to zero
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
Transformer type keys to be symbols instead of constants
|
16
|
+
|
17
|
+
|
3
18
|
## 0.2.0 2016-10-16
|
4
19
|
|
5
20
|
### Added
|
@@ -9,6 +24,7 @@ Change Log file.
|
|
9
24
|
Gem dependencies fixed for development and runtime to only require
|
10
25
|
MRI 1.9.3 and later.
|
11
26
|
|
27
|
+
|
12
28
|
## 0.1.0 2016-10-13
|
13
29
|
|
14
30
|
### Added
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Objectifier
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/objectifier)
|
3
4
|
[](https://travis-ci.org/wiggly/objectifier)
|
4
5
|
|
5
6
|
De-serialise plain hashes into objects.
|
@@ -31,27 +32,27 @@ Create custom transformers, create an objectifier and use it to de-serialise has
|
|
31
32
|
```
|
32
33
|
require 'objectifier'
|
33
34
|
require 'money'
|
34
|
-
|
35
|
+
|
35
36
|
# Add a custom tranformer for Money values
|
36
37
|
Objectifier.factories.add_type(
|
37
|
-
|
38
|
+
:money,
|
38
39
|
->(name, value) {
|
39
40
|
Objectifier::ValueResult.new(name, Money.new(value["amount"], value["code"]))
|
40
41
|
})
|
41
42
|
|
42
43
|
# define an objectifier that can be used to de-serialise a given format
|
43
44
|
obj = Objectifier.define do
|
44
|
-
item :name, type:
|
45
|
+
item :name, type: :string, required: false
|
45
46
|
|
46
|
-
items :accounts, type:
|
47
|
+
items :accounts, type: :integer
|
47
48
|
|
48
|
-
item :budget, type:
|
49
|
+
item :budget, type: :money
|
49
50
|
|
50
|
-
item :length, type:
|
51
|
+
item :length, type: :integer, required: false
|
51
52
|
|
52
53
|
map :activity do
|
53
|
-
item :clicks, type:
|
54
|
-
item :ctr, type:
|
54
|
+
item :clicks, type: :integer
|
55
|
+
item :ctr, type: :float
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
data/TODO.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# TODO
|
2
|
+
|
3
|
+
## Add nillable items
|
4
|
+
|
5
|
+
We should allow some things to be nil. The key is present but the value may be nil instead of either fully required or not required.
|
6
|
+
|
7
|
+
## Decide whether or not using class names for the transformer types is a good idea (probably not)
|
8
|
+
|
9
|
+
## Add Struct in addition to Map types
|
10
|
+
|
11
|
+
## Make transformer factory configurable on a per-objectifier basis
|
12
|
+
|
13
|
+
## Re-factor so that scalar/array/map also come from transformer factory?
|
14
|
+
|
15
|
+
## Add default values for optional items?
|
16
|
+
|
17
|
+
## Pass transformer factory through transformers so they can access transformers that exist when de-serializing objects
|
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift('../../lib')
|
3
|
+
require 'objectifier'
|
4
|
+
require 'pp'
|
5
|
+
require 'money'
|
6
|
+
|
7
|
+
#
|
8
|
+
# To run this example you will have to install the 'money' gem
|
9
|
+
#
|
10
|
+
|
11
|
+
#
|
12
|
+
# Money objectifier example to show how custom transformers can be created
|
13
|
+
# to deserialize types that Objectifier does not know about
|
14
|
+
#
|
15
|
+
# We have a structure representing a bank account. It contains;
|
16
|
+
#
|
17
|
+
# id - integer, required
|
18
|
+
#
|
19
|
+
# balance - money
|
20
|
+
#
|
21
|
+
# overdraft_limit - money
|
22
|
+
#
|
23
|
+
|
24
|
+
Objectifier.factories.add_type(
|
25
|
+
Money,
|
26
|
+
->(name, value) {
|
27
|
+
return Objectifier::ErrorResult.err(name, "'value' not present") unless value.key?("amount")
|
28
|
+
return Objectifier::ErrorResult.err(name, "'code' not present") unless value.key?("code")
|
29
|
+
|
30
|
+
Objectifier::ValueResult.new(name, Money.new(value["amount"], value["code"]))
|
31
|
+
})
|
32
|
+
|
33
|
+
obj = Objectifier.define do
|
34
|
+
item :id, type: Integer
|
35
|
+
item :balance, type: Money
|
36
|
+
item :credit_limit, type: Money, required: false
|
37
|
+
end
|
38
|
+
|
39
|
+
puts "Objectifier"
|
40
|
+
pp obj
|
41
|
+
|
42
|
+
# example 1 - complete data
|
43
|
+
parameters = {
|
44
|
+
"id" => 42,
|
45
|
+
"balance" => { "amount" => 100_00, "code" => "CAD" },
|
46
|
+
"credit_limit" => { "amount" => (-300_00), "code" => "CAD" },
|
47
|
+
}
|
48
|
+
|
49
|
+
puts "\nComplete Data Result"
|
50
|
+
result = obj.call(parameters)
|
51
|
+
|
52
|
+
if result.success?
|
53
|
+
pp result.value
|
54
|
+
else
|
55
|
+
pp result
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# example 2 - required data
|
60
|
+
parameters = {
|
61
|
+
"id" => 42,
|
62
|
+
"balance" => { "amount" => 100_00, "code" => "CAD" },
|
63
|
+
}
|
64
|
+
|
65
|
+
puts "\nRequired Data Result"
|
66
|
+
result = obj.call(parameters)
|
67
|
+
|
68
|
+
if result.success?
|
69
|
+
pp result.value
|
70
|
+
else
|
71
|
+
pp result
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# example 3 - missing a required field
|
76
|
+
parameters = {
|
77
|
+
"id" => 42,
|
78
|
+
"credit_limit" => { "amount" => (-300_00), "code" => "CAD" },
|
79
|
+
}
|
80
|
+
|
81
|
+
puts "\nIncomplete Data Result"
|
82
|
+
result = obj.call(parameters)
|
83
|
+
|
84
|
+
if result.success?
|
85
|
+
pp result.value
|
86
|
+
else
|
87
|
+
pp result
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
# example 4 - malformed data
|
92
|
+
parameters = {
|
93
|
+
"id" => 42,
|
94
|
+
"balance" => { "amount" => 100_00, "code" => "CAD" },
|
95
|
+
"credit_limit" => { "code" => "CAD" },
|
96
|
+
}
|
97
|
+
|
98
|
+
puts "\nMalformed Data Result"
|
99
|
+
result = obj.call(parameters)
|
100
|
+
|
101
|
+
if result.success?
|
102
|
+
pp result.value
|
103
|
+
else
|
104
|
+
pp result
|
105
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift('../../lib')
|
3
|
+
require 'objectifier'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
#
|
7
|
+
# Simple objectifier example
|
8
|
+
#
|
9
|
+
# We have a structure representing a person. It contains;
|
10
|
+
#
|
11
|
+
# name - string, required
|
12
|
+
#
|
13
|
+
# email - string required
|
14
|
+
#
|
15
|
+
# dob - string, optional
|
16
|
+
# If not present in the data provided it will not be present in the structure returned.
|
17
|
+
#
|
18
|
+
# location - map, required
|
19
|
+
# This map contains lattidue and longitude.
|
20
|
+
#
|
21
|
+
# It is required since at least one of its children is required.
|
22
|
+
#
|
23
|
+
#
|
24
|
+
|
25
|
+
obj = Objectifier.define do
|
26
|
+
item :name, type: String
|
27
|
+
item :email, type: String
|
28
|
+
item :dob, type: String, required: false # TODO: add date/time to the set of builtin transformers
|
29
|
+
map :location do
|
30
|
+
item :lat, type: Float
|
31
|
+
item :lng, type: Float
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
puts "Objectifier"
|
36
|
+
pp obj
|
37
|
+
|
38
|
+
# example 1 - complete data
|
39
|
+
parameters = {
|
40
|
+
"name" => "John Doe",
|
41
|
+
"email" => "john.doe@example.com",
|
42
|
+
"dob" => "1964-03-21",
|
43
|
+
"location" => {
|
44
|
+
"lat" => 51.5077,
|
45
|
+
"lng" => -0.1279,
|
46
|
+
},
|
47
|
+
}
|
48
|
+
|
49
|
+
puts "\nComplete Data Result"
|
50
|
+
result = obj.call(parameters)
|
51
|
+
|
52
|
+
if result.success?
|
53
|
+
pp result.value
|
54
|
+
else
|
55
|
+
pp result
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# example 2 - required data
|
60
|
+
parameters = {
|
61
|
+
"name" => "John Doe",
|
62
|
+
"email" => "john.doe@example.com",
|
63
|
+
"location" => {
|
64
|
+
"lat" => 51.5077,
|
65
|
+
"lng" => -0.1279,
|
66
|
+
},
|
67
|
+
}
|
68
|
+
|
69
|
+
puts "\nRequired Data Result"
|
70
|
+
result = obj.call(parameters)
|
71
|
+
|
72
|
+
if result.success?
|
73
|
+
pp result.value
|
74
|
+
else
|
75
|
+
pp result
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# example 3 - missing a required field
|
80
|
+
parameters = {
|
81
|
+
"name" => "John Doe",
|
82
|
+
"dob" => "1964-03-21",
|
83
|
+
"location" => {
|
84
|
+
"lat" => 51.5077,
|
85
|
+
"lng" => -0.1279,
|
86
|
+
},
|
87
|
+
}
|
88
|
+
|
89
|
+
puts "\nIncomplete Data Result"
|
90
|
+
result = obj.call(parameters)
|
91
|
+
|
92
|
+
if result.success?
|
93
|
+
pp result.value
|
94
|
+
else
|
95
|
+
pp result
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# example 4 - malformed data
|
100
|
+
parameters = {
|
101
|
+
"name" => "John Doe",
|
102
|
+
"email" => "john.doe@example.com",
|
103
|
+
"dob" => "1964-03-21",
|
104
|
+
"location" => {
|
105
|
+
"lat" => "fred",
|
106
|
+
"lng" => -0.1279,
|
107
|
+
},
|
108
|
+
}
|
109
|
+
|
110
|
+
puts "\nMalformed Data Result"
|
111
|
+
result = obj.call(parameters)
|
112
|
+
|
113
|
+
if result.success?
|
114
|
+
pp result.value
|
115
|
+
else
|
116
|
+
pp result
|
117
|
+
end
|
@@ -35,18 +35,6 @@ module Objectifier
|
|
35
35
|
true
|
36
36
|
end
|
37
37
|
|
38
|
-
def pp(indent = "")
|
39
|
-
next_indent = "#{indent} "
|
40
|
-
str = "#{indent} #{@scope} {\n"
|
41
|
-
str << @rules.values.map { |r| r.pp(next_indent) }.join("\n")
|
42
|
-
str << "\n#{indent}}\n"
|
43
|
-
str
|
44
|
-
end
|
45
|
-
|
46
|
-
def to_s
|
47
|
-
pp("")
|
48
|
-
end
|
49
|
-
|
50
38
|
# examine parameters and return a hash of massaged data or an error results
|
51
39
|
def call(parameters)
|
52
40
|
|
@@ -22,15 +22,10 @@ module Objectifier
|
|
22
22
|
elsif key_present(parameters)
|
23
23
|
transform(key_value(parameters))
|
24
24
|
else
|
25
|
-
# TODO: this is not a failure...
|
26
25
|
SuccessResult.new(@name)
|
27
26
|
end
|
28
27
|
end
|
29
28
|
|
30
|
-
def pp(indent = "")
|
31
|
-
"#{indent} #{to_s}"
|
32
|
-
end
|
33
|
-
|
34
29
|
def to_s
|
35
30
|
"scalar %s - type: %s - required: %s" % [ @name, @type.to_s, @required.to_s ]
|
36
31
|
end
|
@@ -4,16 +4,41 @@ module Objectifier
|
|
4
4
|
class TransformerFactory
|
5
5
|
def initialize
|
6
6
|
@t = Hash.new { |h,k| raise "unknown type #{@type}" }
|
7
|
-
|
8
|
-
@t[
|
7
|
+
|
8
|
+
@t[:boolean] = lambda do |name, value|
|
9
|
+
case value
|
10
|
+
when true, 'true', 1, '1'
|
11
|
+
ValueResult.new(name, true)
|
12
|
+
when false, 'false', 0, '0'
|
13
|
+
ValueResult.new(name, false)
|
14
|
+
else
|
15
|
+
ErrorResult.err(name, 'not boolean')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
@t[:symbol] = ->(name, value) { ValueResult.new(name, value.to_sym) }
|
20
|
+
|
21
|
+
@t[:string] = ->(name, value) { ValueResult.new(name, value.to_s) }
|
22
|
+
|
23
|
+
@t[:integer] = lambda do |name, value|
|
9
24
|
begin
|
10
25
|
ValueResult.new(name, Integer(value))
|
11
26
|
rescue => e
|
12
27
|
ErrorResult.err(name, e.message)
|
13
28
|
end
|
14
|
-
|
15
|
-
|
16
|
-
@t[
|
29
|
+
end
|
30
|
+
|
31
|
+
@t[:fixnum] = @t[:integer]
|
32
|
+
|
33
|
+
@t[:bignum] = @t[:integer]
|
34
|
+
|
35
|
+
@t[:float] = lambda do |name, value|
|
36
|
+
begin
|
37
|
+
ValueResult.new(name, Float(value))
|
38
|
+
rescue => e
|
39
|
+
ErrorResult.err(name, e.message)
|
40
|
+
end
|
41
|
+
end
|
17
42
|
end
|
18
43
|
|
19
44
|
def add_type(type, transformer)
|
data/lib/objectifier/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: objectifier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nigel Rantor
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -152,8 +152,11 @@ files:
|
|
152
152
|
- LICENSE.txt
|
153
153
|
- README.md
|
154
154
|
- Rakefile
|
155
|
+
- TODO.md
|
155
156
|
- bin/console
|
156
157
|
- bin/setup
|
158
|
+
- example/money/example.rb
|
159
|
+
- example/simple/example.rb
|
157
160
|
- lib/objectifier.rb
|
158
161
|
- lib/objectifier/array_value.rb
|
159
162
|
- lib/objectifier/map_value.rb
|
@@ -182,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
182
185
|
version: '0'
|
183
186
|
requirements: []
|
184
187
|
rubyforge_project:
|
185
|
-
rubygems_version: 2.
|
188
|
+
rubygems_version: 2.5.1
|
186
189
|
signing_key:
|
187
190
|
specification_version: 4
|
188
191
|
summary: De-serialize plain hashes into real object graphs
|