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 +15 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +42 -0
- data/Rakefile +23 -0
- data/cucumber.yml +2 -0
- data/features/json_parsing.feature +227 -0
- data/features/json_serialization.feature +260 -0
- data/features/step_definitions/json_parsing_steps.rb +26 -0
- data/features/step_definitions/json_serialization_steps.rb +23 -0
- data/features/step_definitions/model_steps.rb +3 -0
- data/features/support/env.rb +13 -0
- data/lib/son_jay/model_array.rb +55 -0
- data/lib/son_jay/object_model/properties.rb +70 -0
- data/lib/son_jay/object_model/property_definition.rb +38 -0
- data/lib/son_jay/object_model.rb +71 -0
- data/lib/son_jay/value_array.rb +17 -0
- data/lib/son_jay/version.rb +3 -0
- data/lib/son_jay.rb +8 -0
- data/son_jay.gemspec +25 -0
- data/spec/model_array_spec.rb +70 -0
- data/spec/object_model/properties_spec.rb +125 -0
- data/spec/object_model/property_definition_spec.rb +31 -0
- data/spec/object_model_spec.rb +136 -0
- data/spec/son_jay_spec.rb +7 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/value_array_spec.rb +21 -0
- metadata +142 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module SonJay
|
4
|
+
|
5
|
+
def self.ModelArray(entry_class)
|
6
|
+
Class.new(ModelArray).tap{ |c|
|
7
|
+
c.send :entry_class=, entry_class
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
class ModelArray
|
12
|
+
extend Forwardable
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_accessor :entry_class
|
16
|
+
|
17
|
+
def array_class
|
18
|
+
#TODO: Factor out duplication w/ ObjectModel::array_class
|
19
|
+
@array_class ||= begin
|
20
|
+
klass = SonJay::ModelArray(self)
|
21
|
+
const_set :Array, klass
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@entries = []
|
28
|
+
end
|
29
|
+
|
30
|
+
def sonj_content
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def additional
|
35
|
+
entry = self.class.entry_class.new
|
36
|
+
@entries << entry
|
37
|
+
entry
|
38
|
+
end
|
39
|
+
|
40
|
+
def load_data(data)
|
41
|
+
data.each do |entry_data|
|
42
|
+
additional.sonj_content.load_data entry_data
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def_delegators :@entries, *[
|
47
|
+
:[] ,
|
48
|
+
:empty? ,
|
49
|
+
:entries ,
|
50
|
+
:length ,
|
51
|
+
:to_json ,
|
52
|
+
]
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module SonJay
|
4
|
+
class ObjectModel
|
5
|
+
class Properties
|
6
|
+
class NameError < KeyError
|
7
|
+
def initialize(name)
|
8
|
+
super "No such property name as %s" % name.inspect
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
extend Forwardable
|
13
|
+
|
14
|
+
def initialize(property_definitions)
|
15
|
+
@data = {}
|
16
|
+
@model_properties = Set.new
|
17
|
+
property_definitions.each do |d|
|
18
|
+
is_model_property = !! d.model_class
|
19
|
+
@data[d.name] = is_model_property ? d.model_class.new : nil
|
20
|
+
@model_properties << d.name if is_model_property
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def_delegators :@data, *[
|
25
|
+
:length ,
|
26
|
+
:values ,
|
27
|
+
]
|
28
|
+
|
29
|
+
def_delegator :@data, :has_key?, :has_name?
|
30
|
+
|
31
|
+
def [](name)
|
32
|
+
name = "#{name}"
|
33
|
+
@data.fetch(name)
|
34
|
+
rescue KeyError
|
35
|
+
raise NameError.new(name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def []=(name, value)
|
39
|
+
name = "#{name}"
|
40
|
+
raise NameError.new(name) unless @data.has_key?(name)
|
41
|
+
@data[name] = value
|
42
|
+
end
|
43
|
+
|
44
|
+
def load_data(data)
|
45
|
+
data.each_pair do |name, value|
|
46
|
+
load_property name, value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def load_property(name, value)
|
51
|
+
name = "#{name}"
|
52
|
+
return unless @data.has_key?(name)
|
53
|
+
if @model_properties.include?(name)
|
54
|
+
@data[name].sonj_content.load_data value
|
55
|
+
else
|
56
|
+
@data[name] = value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def has_name?(name)
|
61
|
+
@data.has_key?( "#{name}" )
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_json(*args)
|
65
|
+
@data.to_json(*args)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module SonJay
|
2
|
+
class ObjectModel
|
3
|
+
|
4
|
+
class PropertyDefinition
|
5
|
+
attr_reader :name, :model_class
|
6
|
+
|
7
|
+
def initialize(name, instruction = nil)
|
8
|
+
@name = name
|
9
|
+
@model_class = model_class_for_instruction(instruction)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def model_class_for_instruction(instruction)
|
15
|
+
if instruction.nil?
|
16
|
+
nil
|
17
|
+
elsif instruction.respond_to?(:to_ary)
|
18
|
+
array_model_class(instruction)
|
19
|
+
elsif instruction.respond_to?( :new )
|
20
|
+
instruction
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def array_model_class(instruction)
|
27
|
+
return instruction unless instruction.respond_to?(:to_ary)
|
28
|
+
return SonJay::ValueArray if instruction == []
|
29
|
+
|
30
|
+
sub_instruction = instruction.first
|
31
|
+
sub_model_class = array_model_class( sub_instruction )
|
32
|
+
sub_model_class.array_class
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'son_jay/object_model/properties'
|
2
|
+
require 'son_jay/object_model/property_definition'
|
3
|
+
|
4
|
+
module SonJay
|
5
|
+
class ObjectModel
|
6
|
+
|
7
|
+
class PropertiesDefiner
|
8
|
+
|
9
|
+
def initialize(property_definitions)
|
10
|
+
@property_definitions = property_definitions
|
11
|
+
end
|
12
|
+
|
13
|
+
def property(name, options={})
|
14
|
+
name = "#{name}"
|
15
|
+
@property_definitions << PropertyDefinition.new( name, options[:model] )
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
|
22
|
+
def json_create(json)
|
23
|
+
data = JSON.parse( json )
|
24
|
+
instance = new
|
25
|
+
instance.sonj_content.load_data data
|
26
|
+
instance
|
27
|
+
end
|
28
|
+
|
29
|
+
def properties(&property_initializations)
|
30
|
+
@property_initializations = property_initializations
|
31
|
+
end
|
32
|
+
|
33
|
+
def property_definitions
|
34
|
+
@property_definitions ||= begin
|
35
|
+
definitions = []
|
36
|
+
|
37
|
+
definer = PropertiesDefiner.new(definitions)
|
38
|
+
definer.instance_eval &@property_initializations
|
39
|
+
definitions.each do |d|
|
40
|
+
name = d.name
|
41
|
+
class_eval <<-CODE
|
42
|
+
def #{name} ; sonj_content[#{name.inspect}] ; end
|
43
|
+
def #{name}=(value) ; sonj_content[#{name.inspect}] = value ; end
|
44
|
+
CODE
|
45
|
+
end
|
46
|
+
definitions
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def array_class
|
51
|
+
@array_class ||= begin
|
52
|
+
klass = SonJay::ModelArray(self)
|
53
|
+
const_set :Array, klass
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_reader :sonj_content
|
60
|
+
|
61
|
+
def initialize
|
62
|
+
definitions = self.class.property_definitions
|
63
|
+
@sonj_content = ObjectModel::Properties.new( definitions )
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_json(*args)
|
67
|
+
sonj_content.to_json(*args)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
data/lib/son_jay.rb
ADDED
data/son_jay.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'son_jay/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "son_jay"
|
8
|
+
spec.version = SonJay::VERSION
|
9
|
+
spec.authors = ["Steve Jorgensen"]
|
10
|
+
spec.email = ["stevej@stevej.name"]
|
11
|
+
spec.summary = %q{Symmetrical transformation between structured data and JSON}
|
12
|
+
spec.description = %q{Symmetrical transformation between structured data and JSON}
|
13
|
+
spec.homepage = "https://github.com/stevecj/son_jay"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "cucumber"
|
25
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'SonJay::ModelArray()' do
|
4
|
+
it "returns a subclass of ModelArray for entries of given type" do
|
5
|
+
entry_class_1 = Class.new
|
6
|
+
entry_class_2 = Class.new
|
7
|
+
|
8
|
+
array_model_subclass_1 = SonJay::ModelArray( entry_class_1 )
|
9
|
+
array_model_subclass_2 = SonJay::ModelArray( entry_class_2 )
|
10
|
+
|
11
|
+
expect( array_model_subclass_1.entry_class ).to eq( entry_class_1 )
|
12
|
+
expect( array_model_subclass_2.entry_class ).to eq( entry_class_2 )
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe SonJay::ModelArray do
|
17
|
+
describe "a subclass for entries of a modeled type" do
|
18
|
+
let( :subclass ) { Class.new(described_class).tap{ |c|
|
19
|
+
c.send :entry_class=, entry_class
|
20
|
+
} }
|
21
|
+
let( :entry_class ) { Class.new do
|
22
|
+
class Content
|
23
|
+
def initialize( entry ) ; @entry = entry ; end
|
24
|
+
def load_data(data) ; @entry.loaded_data = data ; end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_accessor :loaded_data
|
28
|
+
def sonj_content ; Content.new(self) ; end
|
29
|
+
end }
|
30
|
+
|
31
|
+
describe '::array_class' do
|
32
|
+
it "returns a model-array subclass for entries of this model-array type (nested array)" do
|
33
|
+
array_class = subclass.array_class
|
34
|
+
puts array_class.ancestors
|
35
|
+
expect( array_class.ancestors ).to include( SonJay::ModelArray )
|
36
|
+
expect( array_class.entry_class ).to eq( subclass )
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "produces instances that are initially empty" do
|
41
|
+
instance = subclass.new
|
42
|
+
expect( instance ).to be_empty
|
43
|
+
expect( instance.length ).to eq( 0 )
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#additional" do
|
47
|
+
it "adds a new entry of the modeled type, and returns the entry" do
|
48
|
+
instance = subclass.new
|
49
|
+
|
50
|
+
entry_0 = instance.additional
|
51
|
+
entry_1 = instance.additional
|
52
|
+
|
53
|
+
expect( entry_0 ).to be_kind_of( entry_class )
|
54
|
+
expect( instance.entries ).to eq( [entry_0, entry_1] )
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#load_data' do
|
59
|
+
it "loads entries in the given enumerable into its own model instance entries" do
|
60
|
+
instance = subclass.new
|
61
|
+
|
62
|
+
instance.load_data( ['entry 0 data', 'entry 1 data'] )
|
63
|
+
|
64
|
+
expect( instance.length ).to eq( 2 )
|
65
|
+
expect( instance[0].loaded_data ).to eq( 'entry 0 data' )
|
66
|
+
expect( instance[1].loaded_data ).to eq( 'entry 1 data' )
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SonJay::ObjectModel::Properties do
|
4
|
+
subject{
|
5
|
+
described_class.new( [
|
6
|
+
SonJay::ObjectModel::PropertyDefinition.new( 'aaa' ) ,
|
7
|
+
SonJay::ObjectModel::PropertyDefinition.new( 'bbb' ) ,
|
8
|
+
SonJay::ObjectModel::PropertyDefinition.new( 'ccc' ) ,
|
9
|
+
SonJay::ObjectModel::PropertyDefinition.new( 'ddd', ddd_model_class ) ,
|
10
|
+
] )
|
11
|
+
}
|
12
|
+
|
13
|
+
let( :ddd_model_class ) { Class.new do
|
14
|
+
class Content
|
15
|
+
def initialize(model) ; @model = model ; end
|
16
|
+
def load_data(data) ; @model.class.loaded_data = data ; end
|
17
|
+
end
|
18
|
+
class << self ; attr_accessor :loaded_data ; end
|
19
|
+
def to_json(*) ; '"ddd..."' ; end
|
20
|
+
def sonj_content ; Content.new(self) ; end
|
21
|
+
end }
|
22
|
+
|
23
|
+
it "has an entry for each property name specified during initialization" do
|
24
|
+
expect( subject.length ).to eq( 4 )
|
25
|
+
expect( subject ).to have_name('aaa')
|
26
|
+
expect( subject ).to have_name('bbb')
|
27
|
+
expect( subject ).to have_name('ccc')
|
28
|
+
expect( subject ).to have_name('ddd')
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "property value access by name" do
|
32
|
+
it "reads nil by default for an existing value property" do
|
33
|
+
expect( subject['aaa'] ).to be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "writes and reads existing value properties" do
|
37
|
+
subject['bbb'] = 10
|
38
|
+
subject['aaa'] = 11
|
39
|
+
|
40
|
+
expect( subject['bbb'] ).to eq( 10 )
|
41
|
+
expect( subject['aaa'] ).to eq( 11 )
|
42
|
+
end
|
43
|
+
|
44
|
+
it "refuses to read a non-existent property" do
|
45
|
+
expect{ subject['abc'] }.to raise_exception( described_class::NameError )
|
46
|
+
end
|
47
|
+
|
48
|
+
it "refuses to write a non-existent property" do
|
49
|
+
expect{ subject['abc'] = 1 }.to raise_exception( described_class::NameError )
|
50
|
+
end
|
51
|
+
|
52
|
+
it "allows reading/writing by symbol or string for property name" do
|
53
|
+
subject['aaa'] = 10
|
54
|
+
subject[:bbb] = 11
|
55
|
+
|
56
|
+
expect( subject[:aaa] ).to eq( 10 )
|
57
|
+
expect( subject['bbb'] ).to eq( 11 )
|
58
|
+
end
|
59
|
+
|
60
|
+
it "reads an instance of the model for a modeled attribute" do
|
61
|
+
expect( subject[:ddd] ).to be_kind_of( ddd_model_class )
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#to_json" do
|
66
|
+
it "returns a JSON object representation with attribute values" do
|
67
|
+
subject['aaa'] = 'abc'
|
68
|
+
subject['ccc'] = true
|
69
|
+
|
70
|
+
actual_json = subject.to_json
|
71
|
+
|
72
|
+
actual_data = JSON.parse( actual_json )
|
73
|
+
expected_data = {
|
74
|
+
'aaa' => 'abc' ,
|
75
|
+
'bbb' => nil ,
|
76
|
+
'ccc' => true ,
|
77
|
+
'ddd' => "ddd..." ,
|
78
|
+
}
|
79
|
+
expect( actual_data ).to eq( expected_data )
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "load_property" do
|
84
|
+
it "writes to existing value properties" do
|
85
|
+
subject.load_property( 'bbb', 11 )
|
86
|
+
subject.load_property( 'ccc', 12 )
|
87
|
+
|
88
|
+
expect( subject['bbb'] ).to eq( 11 )
|
89
|
+
expect( subject['ccc'] ).to eq( 12 )
|
90
|
+
end
|
91
|
+
|
92
|
+
it "loads data into existing modeled properties" do
|
93
|
+
subject.load_property( 'ddd', 'some data' )
|
94
|
+
expect( ddd_model_class.loaded_data ).to eq( 'some data' )
|
95
|
+
end
|
96
|
+
|
97
|
+
it "ignores attempts to write to non-existent properties" do
|
98
|
+
expect{ subject.load_property('xx', 10) }.
|
99
|
+
not_to change{ subject.length }
|
100
|
+
end
|
101
|
+
|
102
|
+
it "allows string or symbol for property name" do
|
103
|
+
subject.load_property( 'aaa' , 888 )
|
104
|
+
subject.load_property( :ccc , 999 )
|
105
|
+
|
106
|
+
expect( subject['aaa'] ).to eq( 888 )
|
107
|
+
expect( subject['ccc'] ).to eq( 999 )
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "load_data" do
|
112
|
+
it "populates property values from hash entries" do
|
113
|
+
subject.load_data({
|
114
|
+
'bbb' => 'abc' ,
|
115
|
+
'ccc' => false ,
|
116
|
+
'ddd' => 'something...' ,
|
117
|
+
})
|
118
|
+
expect( subject['aaa'] ).to be_nil
|
119
|
+
expect( subject['bbb'] ).to eq( 'abc' )
|
120
|
+
expect( subject['ccc'] ).to eq( false )
|
121
|
+
expect( ddd_model_class.loaded_data ).to eq( 'something...' )
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SonJay::ObjectModel::PropertyDefinition do
|
4
|
+
|
5
|
+
it "has a nil model class for a nil instruction" do
|
6
|
+
instance = described_class.new( :a, nil )
|
7
|
+
expect( instance.model_class ).to be_nil
|
8
|
+
end
|
9
|
+
|
10
|
+
it "has a model class for a model class instruction" do
|
11
|
+
model_class = Class.new
|
12
|
+
|
13
|
+
instance = described_class.new( :a, model_class )
|
14
|
+
expect( instance.model_class ).to eq( model_class )
|
15
|
+
end
|
16
|
+
|
17
|
+
it "has a value-array class for an empty-array instruction" do
|
18
|
+
instance = described_class.new( :a, [] )
|
19
|
+
expect( instance.model_class ).to eq( SonJay::ValueArray )
|
20
|
+
end
|
21
|
+
|
22
|
+
it "has a model-array class for an array w/ model class entry" do
|
23
|
+
entry_model_class = Class.new do
|
24
|
+
class Array ; end
|
25
|
+
def self.array_class ; Array ; end
|
26
|
+
end
|
27
|
+
|
28
|
+
instance = described_class.new( :a, [ entry_model_class ] )
|
29
|
+
expect( instance.model_class ).to eq( entry_model_class::Array )
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SonJay::ObjectModel do
|
4
|
+
|
5
|
+
describe "a subclass that defines value properties" do
|
6
|
+
|
7
|
+
describe '::array_class' do
|
8
|
+
it "returns a model-array subclass for entries of this type" do
|
9
|
+
array_class = subclass.array_class
|
10
|
+
puts array_class.ancestors
|
11
|
+
expect( array_class.ancestors ).to include( SonJay::ModelArray )
|
12
|
+
expect( array_class.entry_class ).to eq( subclass )
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let( :subclass ) {
|
17
|
+
Class.new(described_class) do
|
18
|
+
properties do
|
19
|
+
property :abc
|
20
|
+
property :xyz
|
21
|
+
end
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
describe "#sonj_content" do
|
26
|
+
it "has name-indexed settable/gettable values for defined properties" do
|
27
|
+
instance = subclass.new
|
28
|
+
|
29
|
+
content = instance.sonj_content
|
30
|
+
expect( content.length ).to eq( 2 )
|
31
|
+
|
32
|
+
content[:abc] = 1
|
33
|
+
content[:xyz] = 'XYZ'
|
34
|
+
|
35
|
+
expect( content[:abc] ).to eq( 1 )
|
36
|
+
expect( content[:xyz] ).to eq( 'XYZ' )
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "has direct property accessor methods for each property" do
|
41
|
+
instance = subclass.new
|
42
|
+
instance.abc, instance.xyz = 11, 22
|
43
|
+
|
44
|
+
expect( [instance.abc, instance.xyz] ).to eq( [11, 22] )
|
45
|
+
end
|
46
|
+
|
47
|
+
it "serializes to a JSON object representation w/ property values" do
|
48
|
+
instance = subclass.new
|
49
|
+
instance.abc, instance.xyz = 'ABC', nil
|
50
|
+
|
51
|
+
actual_json = instance.to_json
|
52
|
+
|
53
|
+
actual_data = JSON.parse( actual_json)
|
54
|
+
expected_data = {'abc' => 'ABC', 'xyz' => nil}
|
55
|
+
expect( actual_data ).to eq( expected_data )
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '::json_create' do
|
59
|
+
it "creates an instance with properties filled in from parsed JSON" do
|
60
|
+
json = <<-JSON
|
61
|
+
{
|
62
|
+
"abc": 123 ,
|
63
|
+
"xyz": "XYZ"
|
64
|
+
}
|
65
|
+
JSON
|
66
|
+
|
67
|
+
instance = subclass.json_create( json )
|
68
|
+
|
69
|
+
expect( instance.abc ).to eq( 123 )
|
70
|
+
expect( instance.xyz ).to eq( 'XYZ' )
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "a subclass that defines value and modeled-object properties" do
|
77
|
+
let( :subclass ) {
|
78
|
+
dmc_1, dmc_2 = detail_model_class_1, detail_model_class_2
|
79
|
+
pbcs = property_block_calls
|
80
|
+
Class.new(described_class) do
|
81
|
+
properties do
|
82
|
+
pbcs << 1
|
83
|
+
property :a
|
84
|
+
property :obj_1, model: dmc_1
|
85
|
+
property :obj_2, model: dmc_2
|
86
|
+
end
|
87
|
+
end
|
88
|
+
}
|
89
|
+
|
90
|
+
let( :property_block_calls ) { [] }
|
91
|
+
|
92
|
+
let( :detail_model_class_1 ) {
|
93
|
+
Class.new(described_class) do
|
94
|
+
properties do
|
95
|
+
property :aaa
|
96
|
+
property :bbb
|
97
|
+
end
|
98
|
+
end
|
99
|
+
}
|
100
|
+
|
101
|
+
let( :detail_model_class_2 ) {
|
102
|
+
Class.new(described_class) do
|
103
|
+
properties do
|
104
|
+
property :ccc
|
105
|
+
end
|
106
|
+
end
|
107
|
+
}
|
108
|
+
|
109
|
+
it "does not immediately invoke its properties block when declared" do
|
110
|
+
_ = subclass
|
111
|
+
expect( property_block_calls ).to be_empty
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#sonj_content" do
|
115
|
+
it "has an entry for each defined property" do
|
116
|
+
content = subclass.new.sonj_content
|
117
|
+
expect( content.length ).to eq( 3 )
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "#sonj_content" do
|
122
|
+
it "has name-indexed settable/gettable values for defined value properties" do
|
123
|
+
content = subclass.new.sonj_content
|
124
|
+
content[:a] = 1
|
125
|
+
expect( content[:a] ).to eq( 1 )
|
126
|
+
end
|
127
|
+
|
128
|
+
it "has name-indexed gettable values for defined modeled-object properties" do
|
129
|
+
content = subclass.new.sonj_content
|
130
|
+
expect( content[:obj_1] ).to be_kind_of( detail_model_class_1 )
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
data/spec/spec_helper.rb
ADDED