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 +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