son_jay 0.1.0.alpha

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MjdiYTdiZjkxOWE3ZDg0ZjkxNzY0ZjNkYzEwYzg1ZWM1ZWQ3NTM5Yg==
5
+ data.tar.gz: !binary |-
6
+ MjA4ZmQ1NDRkZDgwN2JmZDU1YmU4ZmFmMDM0NzhkZjAyZjczMzBiOQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZDkzNGQ4ZTk1ZjZhNjYyYTg0MDU0NzMwNTM2ZTM0MzJhNWQwNmY1OGIyMDY0
10
+ YjY2NThhMDJhNmMwODc2NjYyNGM4NDg5NzM3OTllZmJhMjVlMzdhMmMzY2Qw
11
+ ZjU5YzcwNTY3MTdhMWY1NTI0NDU2NGJiMGZkODZiMmVkOWVkYzk=
12
+ data.tar.gz: !binary |-
13
+ ZjI0N2FiMTE0MWNiYjVjM2IwZDQ2MGZkN2Y5MWUzNjg2NmVjYWRkZDQzMjU5
14
+ M2FlMzdjMzM3NmVmZWQxZWVlNzJkM2M1N2VmZGE5OTlhOTBmNzg2ZDI5MmJl
15
+ MjZmMjdmNTBmNGRiZDNjYTMwZWQ2YjRlZDUxZDFkMDkyNWQ0YzM=
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .ruby-version
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in son_jay.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Steve Jorgensen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # SonJay
2
+
3
+ Symmetrical transformation between structured data and JSON
4
+
5
+ Allows concrete object/array data model classes to be defined,
6
+ and then allows JSON serialization/parsing from/to an instance
7
+ of one of those classes.
8
+
9
+ This allows a single body of code to be used to define a JSON
10
+ API structure for a provider and its clients.
11
+
12
+ Instances of these models can also be used to help keep
13
+ automated tests simple and reliable. Attempts by test setup
14
+ code or code under test to produce incorrectly structured
15
+ data will generally fail in a fast, informative way.
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ gem 'son_jay'
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install son_jay
30
+
31
+ ## Usage
32
+
33
+ - [Serialization](features/json_serialization.feature)
34
+ - [Parsing](features/json_parsing.feature)
35
+
36
+ ## Contributing
37
+
38
+ 1. Fork it ( http://github.com/<my-github-username>/son_jay/fork )
39
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
40
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
41
+ 4. Push to the branch (`git push origin my-new-feature`)
42
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+
5
+ desc "Run all specs and features"
6
+ task all_tests: %w[spec cucumber cucumber:wip]
7
+
8
+ task :default => :all_tests
9
+
10
+
11
+ RSpec::Core::RakeTask.new(:spec)
12
+
13
+
14
+ require "cucumber"
15
+ require "cucumber/rake/task"
16
+
17
+ Cucumber::Rake::Task.new
18
+
19
+ namespace :cucumber do
20
+ Cucumber::Rake::Task.new :wip do |t|
21
+ t.profile = 'wip'
22
+ end
23
+ end
data/cucumber.yml ADDED
@@ -0,0 +1,2 @@
1
+ default: --tags ~@wip
2
+ wip: --wip --tags @wip
@@ -0,0 +1,227 @@
1
+ Feature: Parsing data from JSON
2
+
3
+ Scenario: Simple object data
4
+ Given an object model defined as:
5
+ """
6
+ class SimpleObjectModel < SonJay::ObjectModel
7
+ properties do
8
+ property :id
9
+ property :name
10
+ property :published
11
+ property :featured
12
+ property :owner
13
+ end
14
+ end
15
+ """
16
+ And JSON data defined as:
17
+ """
18
+ json = <<-JSON
19
+ {
20
+ "id" : 55 ,
21
+ "name" : "Polygon" ,
22
+ "published" : true ,
23
+ "featured" : false ,
24
+ "owner" : null
25
+ }
26
+ JSON
27
+ """
28
+ When the JSON is parsed to a model instance as:
29
+ """
30
+ instance = SimpleObjectModel.json_create( json )
31
+ """
32
+ Then the instance attributes are as follows:
33
+ | id | name | published | featured | owner |
34
+ | 55 | "Polygon" | true | false | nil |
35
+
36
+ Scenario: Composite object data
37
+ Given an object model defined as:
38
+ """
39
+ class ThingModel < SonJay::ObjectModel
40
+ properties do
41
+ property :name
42
+ property :details, :model => DetailModel
43
+ end
44
+ end
45
+ """
46
+ And an object model defined as:
47
+ """
48
+ class DetailModel < SonJay::ObjectModel
49
+ properties do
50
+ property :color
51
+ property :size
52
+ end
53
+ end
54
+ """
55
+ And JSON data defined as:
56
+ """
57
+ json = <<-JSON
58
+ {
59
+ "name" : "Cherry" ,
60
+ "details" :
61
+ {
62
+ "color" : "red" ,
63
+ "size" : "small"
64
+ }
65
+ }
66
+ JSON
67
+ """
68
+ When the JSON is parsed to a model instance as:
69
+ """
70
+ instance = ThingModel.json_create( json )
71
+ """
72
+ Then the instance attributes are as follows:
73
+ | name | details.color | details.size |
74
+ | "Cherry" | "red" | "small" |
75
+
76
+ Scenario: Object data with a value-array property
77
+ Given an object model defined as:
78
+ """
79
+ class ContestantModel < SonJay::ObjectModel
80
+ properties do
81
+ property :name
82
+ property :scores, model: []
83
+ end
84
+ end
85
+ """
86
+ And JSON data defined as:
87
+ """
88
+ json = <<-JSON
89
+ {
90
+ "name" : "Pat" ,
91
+ "scores" : [ 9, 5, 7 ]
92
+ }
93
+ JSON
94
+ """
95
+ When the JSON is parsed to a model instance as:
96
+ """
97
+ instance = ContestantModel.json_create( json )
98
+ """
99
+ Then the instance attributes are as follows:
100
+ | name | scores[0] | scores[1] | scores[2] |
101
+ | "Pat" | 9 | 5 | 7 |
102
+
103
+ Scenario: Object data with a nested value-array property
104
+ Given an object model defined as:
105
+ """
106
+ class TicTacToeModel < SonJay::ObjectModel
107
+ properties do
108
+ property :rows, model: [[]]
109
+ end
110
+ end
111
+ """
112
+ And a model instance defined as:
113
+ """
114
+ instance = TicTacToeModel.new
115
+ """
116
+ And JSON data defined as:
117
+ """
118
+ json = <<-JSON
119
+ {
120
+ "rows" : [
121
+ [ "X", "O", "X" ] ,
122
+ [ "O", "X", "X" ] ,
123
+ [ "X", "O", "O" ]
124
+ ]
125
+ }
126
+ JSON
127
+ """
128
+ When the JSON is parsed to a model instance as:
129
+ """
130
+ instance = TicTacToeModel.json_create( json )
131
+ """
132
+ Then the instance attributes are as follows:
133
+ | rows[0][0] | rows[0][1] | rows[0][2] |
134
+ | 'X' | 'O' | 'X' |
135
+ | rows[1][0] | rows[1][1] | rows[1][2] |
136
+ | 'O' | 'X' | 'X' |
137
+ | rows[2][0] | rows[2][1] | rows[2][2] |
138
+ | 'X' | 'O' | 'O' |
139
+
140
+ Scenario: Object data with an object-array property
141
+ Given an object model defined as:
142
+ """
143
+ class ListModel < SonJay::ObjectModel
144
+ properties do
145
+ property :name
146
+ property :items, :model => [ ItemModel ]
147
+ end
148
+ end
149
+ """
150
+ And an object model defined as:
151
+ """
152
+ class ItemModel < SonJay::ObjectModel
153
+ properties do
154
+ property :description
155
+ property :priority
156
+ end
157
+ end
158
+ """
159
+ And JSON data defined as:
160
+ """
161
+ json = <<-JSON
162
+ {
163
+ "name" : "Shopping" ,
164
+ "items" :
165
+ [
166
+ {
167
+ "description" : "Potato Chips" ,
168
+ "priority" : "Low"
169
+ } ,
170
+ {
171
+ "description" : "Ice Cream" ,
172
+ "priority" : "High"
173
+ }
174
+ ]
175
+ }
176
+ JSON
177
+ """
178
+ When the JSON is parsed to a model instance as:
179
+ """
180
+ instance = ListModel.json_create( json )
181
+ """
182
+ Then the instance attributes are as follows:
183
+ | name | items[0].description | items[0].priority | items[1].description | items[1].priority |
184
+ | "Shopping" | "Potato Chips" | "Low" | "Ice Cream" | "High" |
185
+
186
+ Scenario: Object data with a nested object-array property
187
+ Given an object model defined as:
188
+ """
189
+ class TableModel < SonJay::ObjectModel
190
+ properties do
191
+ property :rows, :model => [[ CellModel ]]
192
+ end
193
+ end
194
+ """
195
+ And an object model defined as:
196
+ """
197
+ class CellModel < SonJay::ObjectModel
198
+ properties do
199
+ property :is_head
200
+ property :value
201
+ end
202
+ end
203
+ """
204
+ And JSON data defined as:
205
+ """
206
+ json = <<-JSON
207
+ {
208
+ "rows" :
209
+ [
210
+ [ {"is_head": 1, "value": "Date"} , {"is_head": 1, "value": "Sightings"} ] ,
211
+ [ {"is_head": 1, "value": "Jan 1"} , {"is_head": 0, "value": 3} ] ,
212
+ [ {"is_head": 1, "value": "Jan 3"} , {"is_head": 0, "value": 2} ]
213
+ ]
214
+ }
215
+ JSON
216
+ """
217
+ When the JSON is parsed to a model instance as:
218
+ """
219
+ instance = TableModel.json_create( json )
220
+ """
221
+ Then the instance attributes are as follows:
222
+ | rows[0][0].is_head | rows[0][0].value | rows[0][1].is_head | rows[0][1].value |
223
+ | 1 | "Date" | 1 | "Sightings" |
224
+ | rows[1][0].is_head | rows[1][0].value | rows[1][1].is_head | rows[1][1].value |
225
+ | 1 | "Jan 1" | 0 | 3 |
226
+ | rows[2][0].is_head | rows[2][0].value | rows[2][1].is_head | rows[2][1].value |
227
+ | 1 | "Jan 3" | 0 | 2 |
@@ -0,0 +1,260 @@
1
+ Feature: Serializing data to JSON
2
+
3
+ Scenario: Simple object data
4
+ Given an object model defined as:
5
+ """
6
+ class SimpleObjectModel < SonJay::ObjectModel
7
+ properties do
8
+ property :id
9
+ property :name
10
+ property :published
11
+ property :featured
12
+ property :owner
13
+ end
14
+ end
15
+ """
16
+ And a model instance defined as:
17
+ """
18
+ instance = SimpleObjectModel.new
19
+ """
20
+ When the instance's property values are assigned as:
21
+ """
22
+ instance.id = 55
23
+ instance.name = "Polygon"
24
+ instance.published = true
25
+ instance.featured = false
26
+ instance.owner = nil
27
+ """
28
+ And the model is serialized to JSON as:
29
+ """
30
+ json = instance.to_json
31
+ """
32
+ Then the resulting JSON is equivalent to:
33
+ """
34
+ {
35
+ "id" : 55 ,
36
+ "name" : "Polygon" ,
37
+ "published" : true ,
38
+ "featured" : false ,
39
+ "owner" : null
40
+ }
41
+ """
42
+
43
+ Scenario: Composite object data
44
+ Given an object model defined as:
45
+ """
46
+ class ThingModel < SonJay::ObjectModel
47
+ properties do
48
+ property :name
49
+ property :details, :model => DetailModel
50
+ end
51
+ end
52
+ """
53
+ And an object model defined as:
54
+ """
55
+ class DetailModel < SonJay::ObjectModel
56
+ properties do
57
+ property :color
58
+ property :size
59
+ end
60
+ end
61
+ """
62
+ And a model instance defined as:
63
+ """
64
+ instance = ThingModel.new
65
+ """
66
+ When the instance's property values are assigned as:
67
+ """
68
+ instance.name = "Cherry"
69
+ instance.details.color = "red"
70
+ instance.details.size = "small"
71
+ """
72
+ And the model is serialized to JSON as:
73
+ """
74
+ json = instance.to_json
75
+ """
76
+ Then the resulting JSON is equivalent to:
77
+ """
78
+ {
79
+ "name" : "Cherry" ,
80
+ "details" :
81
+ {
82
+ "color" : "red" ,
83
+ "size" : "small"
84
+ }
85
+ }
86
+ """
87
+
88
+ Scenario: Object data with a value-array property
89
+ Given an object model defined as:
90
+ """
91
+ class ContestantModel < SonJay::ObjectModel
92
+ properties do
93
+ property :name
94
+ property :scores, model: []
95
+ end
96
+ end
97
+ """
98
+ And a model instance defined as:
99
+ """
100
+ instance = ContestantModel.new
101
+ """
102
+ When the instance's property values are assigned as:
103
+ """
104
+ instance.name = "Pat"
105
+ instance.scores = [ 9, 5, 7 ]
106
+ """
107
+ And the model is serialized to JSON as:
108
+ """
109
+ json = instance.to_json
110
+ """
111
+ Then the resulting JSON is equivalent to:
112
+ """
113
+ {
114
+ "name" : "Pat" ,
115
+ "scores" : [ 9, 5, 7 ]
116
+ }
117
+ """
118
+
119
+ Scenario: Object data with a nested value-array property
120
+ Given an object model defined as:
121
+ """
122
+ class TicTacToeModel < SonJay::ObjectModel
123
+ properties do
124
+ property :rows, model: [[]]
125
+ end
126
+ end
127
+ """
128
+ And a model instance defined as:
129
+ """
130
+ instance = TicTacToeModel.new
131
+ """
132
+ When the instance's property values are assigned as:
133
+ """
134
+ instance.rows.additional.concat %w[ X O X ]
135
+ instance.rows.additional.concat %w[ O X X ]
136
+ instance.rows.additional.concat %w[ X O O ]
137
+ """
138
+ And the model is serialized to JSON as:
139
+ """
140
+ json = instance.to_json
141
+ """
142
+ Then the resulting JSON is equivalent to:
143
+ """
144
+ {
145
+ "rows" : [
146
+ [ "X", "O", "X" ] ,
147
+ [ "O", "X", "X" ] ,
148
+ [ "X", "O", "O" ]
149
+ ]
150
+ }
151
+ """
152
+
153
+ Scenario: Object data with an object-array property
154
+ Given an object model defined as:
155
+ """
156
+ class ListModel < SonJay::ObjectModel
157
+ properties do
158
+ property :name
159
+ property :items, :model => [ ItemModel ]
160
+ end
161
+ end
162
+ """
163
+ And an object model defined as:
164
+ """
165
+ class ItemModel < SonJay::ObjectModel
166
+ properties do
167
+ property :description
168
+ property :priority
169
+ end
170
+ end
171
+ """
172
+ And a model instance defined as:
173
+ """
174
+ instance = ListModel.new
175
+ """
176
+ When the instance's property values are assigned as:
177
+ """
178
+ instance.name = "Shopping"
179
+
180
+ item = instance.items.additional
181
+ item.description = 'Potato Chips'
182
+ item.priority = 'Low'
183
+
184
+ item = instance.items.additional
185
+ item.description = 'Ice Cream'
186
+ item.priority = 'High'
187
+ """
188
+ And the model is serialized to JSON as:
189
+ """
190
+ json = instance.to_json
191
+ """
192
+ Then the resulting JSON is equivalent to:
193
+ """
194
+ {
195
+ "name" : "Shopping" ,
196
+ "items" :
197
+ [
198
+ {
199
+ "description" : "Potato Chips" ,
200
+ "priority" : "Low"
201
+ } ,
202
+ {
203
+ "description" : "Ice Cream" ,
204
+ "priority" : "High"
205
+ }
206
+ ]
207
+ }
208
+ """
209
+
210
+ Scenario: Object data with a nested object-array property
211
+ Given an object model defined as:
212
+ """
213
+ class TableModel < SonJay::ObjectModel
214
+ properties do
215
+ property :rows, :model => [[ CellModel ]]
216
+ end
217
+ end
218
+ """
219
+ And an object model defined as:
220
+ """
221
+ class CellModel < SonJay::ObjectModel
222
+ properties do
223
+ property :is_head
224
+ property :value
225
+ end
226
+ end
227
+ """
228
+ And a model instance defined as:
229
+ """
230
+ instance = TableModel.new
231
+ """
232
+ When the instance's property values are assigned as:
233
+ """
234
+ row_cells = instance.rows.additional
235
+ row_cells.additional.tap{ |c| c.is_head=1 ; c.value='Date' }
236
+ row_cells.additional.tap{ |c| c.is_head=1 ; c.value='Sightings' }
237
+
238
+ row_cells = instance.rows.additional
239
+ row_cells.additional.tap{ |c| c.is_head=1 ; c.value='Jan 1' }
240
+ row_cells.additional.tap{ |c| c.is_head=0 ; c.value=3 }
241
+
242
+ row_cells = instance.rows.additional
243
+ row_cells.additional.tap{ |c| c.is_head=1 ; c.value='Jan 3' }
244
+ row_cells.additional.tap{ |c| c.is_head=0 ; c.value=2 }
245
+ """
246
+ And the model is serialized to JSON as:
247
+ """
248
+ json = instance.to_json
249
+ """
250
+ Then the resulting JSON is equivalent to:
251
+ """
252
+ {
253
+ "rows" :
254
+ [
255
+ [ {"is_head": 1, "value": "Date"} , {"is_head": 1, "value": "Sightings"} ] ,
256
+ [ {"is_head": 1, "value": "Jan 1"} , {"is_head": 0, "value": 3} ] ,
257
+ [ {"is_head": 1, "value": "Jan 3"} , {"is_head": 0, "value": 2} ]
258
+ ]
259
+ }
260
+ """
@@ -0,0 +1,26 @@
1
+ Given(/^JSON data defined as:$/) do |code|
2
+ json = nil
3
+ context_module.module_eval code
4
+ context_data[:json] = json
5
+ end
6
+
7
+ When(/^the JSON is parsed to a model instance as:$/) do |code|
8
+ json = context_data[:json]
9
+ instance = nil
10
+ context_module.module_eval code
11
+ context_data[:instance] = instance
12
+ end
13
+
14
+ Then(/^the instance attributes are as follows:$/) do |table|
15
+ row_pairs = table.raw.each_slice(2)
16
+ attribute_exprs = row_pairs.map( &:first ).reduce( :+ )
17
+ expected_exprs = row_pairs.map( &:last ).reduce( :+ )
18
+
19
+ instance = context_data[:instance]
20
+ actuals = attribute_exprs .map{ |expr| eval( "instance.#{expr}" ) }
21
+ expecteds = expected_exprs .map{ |expr| eval( expr ) }
22
+
23
+ actual_hash = Hash[ attribute_exprs.zip( actuals ) ]
24
+ expected_hash = Hash[ attribute_exprs.zip( expecteds ) ]
25
+ expect( actual_hash ).to eq( expected_hash )
26
+ end
@@ -0,0 +1,23 @@
1
+ Given(/^a model instance defined as:$/) do |code|
2
+ instance = nil
3
+ context_module.module_eval code
4
+ context_data[:instance] = instance
5
+ end
6
+
7
+ When(/^the instance's property values are assigned as:$/) do |code|
8
+ instance = context_data[:instance]
9
+ context_module.module_eval code
10
+ end
11
+
12
+ When(/^the model is serialized to JSON as:$/) do |code|
13
+ instance = context_data[:instance]
14
+ json = nil
15
+ context_module.module_eval code
16
+ context_data[:json] = json
17
+ end
18
+
19
+ Then(/^the resulting JSON is equivalent to:$/) do |expected_json_equivalent|
20
+ expected_data = JSON.parse( expected_json_equivalent )
21
+ actual_data = JSON.parse( context_data[:json] )
22
+ expect( actual_data ).to eq( expected_data )
23
+ end
@@ -0,0 +1,3 @@
1
+ Given(/^an object model defined as:$/) do |code|
2
+ context_module.module_eval code
3
+ end