son_jay 0.1.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
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