son_jay 0.4.1 → 0.5.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 +8 -8
- data/CHANGELOG.md +13 -1
- data/lib/son_jay/acts_as_model.rb +6 -1
- data/lib/son_jay/model_array.rb +43 -2
- data/lib/son_jay/object_model/{properties → content}/abstract.rb +44 -8
- data/lib/son_jay/object_model/content/content_with_extra.rb +63 -0
- data/lib/son_jay/object_model/{properties/properties_without_extra.rb → content/content_without_extra.rb} +12 -2
- data/lib/son_jay/object_model/content.rb +20 -0
- data/lib/son_jay/object_model/content_data.rb +74 -0
- data/lib/son_jay/object_model/property_definitions.rb +12 -0
- data/lib/son_jay/object_model.rb +33 -13
- data/lib/son_jay/value_array.rb +1 -1
- data/lib/son_jay/version.rb +1 -1
- data/spec/acts_as_model_spec.rb +4 -4
- data/spec/model_array_spec.rb +86 -25
- data/spec/object_model/content/content_with_extra_spec.rb +201 -0
- data/spec/object_model/content/content_without_extra_spec.rb +180 -0
- data/spec/object_model/content_data_spec.rb +81 -0
- data/spec/object_model_spec.rb +106 -92
- data/spec/son_jay_spec.rb +1 -1
- data/spec/value_array_spec.rb +2 -2
- metadata +13 -9
- data/lib/son_jay/object_model/extra_data.rb +0 -36
- data/lib/son_jay/object_model/properties/properties_with_extra.rb +0 -27
- data/lib/son_jay/object_model/properties.rb +0 -20
- data/spec/object_model/extra_data_spec.rb +0 -37
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MTRiYjYyNWM4NGRkZGMxZmRjNGEyYTNlMzU0MzdhMzZjZjZhYTEzMQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MTBjNDdmNDcxZDg0MjExY2RlNzEwYzA4MTlkZmMyZTY2NTFjYTZkOQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NjA5NWU3MzMyNDZmMzFkMDBlNTdmNmIxODdlNmFiYjczMjg0OTBlYTRhYjZk
|
10
|
+
ZWZmYWNhMzAyMjJiNDM2MWY3OTJlZGMxNGFlMWQxYmZlMTM5OTA2MDEyZjhj
|
11
|
+
MTIyY2JhODAyNDViNTQ1Njc1YWNlMGQyNjNlMTM3Mjk5MGFjM2U=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZTRjY2M4ZGExOGQ1MjlhOWE0YmU1ZjM5YWExNDIzOTgwOTQzNjIzMDhkNzY4
|
14
|
+
ZjI5NDE2NzZlNGVmNDAwN2IwZjFmZjQ5ODU3NmM0MWVmMTRmMWI1MDRjYmM5
|
15
|
+
MDI1ODU4ZGU3YTkzNjI4NGRkMjg1OGY5Y2VlMDUwOTFlNjUxMTg=
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
|
+
### 0.5.0
|
2
|
+
* Enhancements
|
3
|
+
* Allow subclassing/extending model classes
|
4
|
+
* Access content of model instances using #model_content
|
5
|
+
instead of #sonj_content
|
6
|
+
* Implement well-behaved #freeze, #dup, and #clone for model
|
7
|
+
instances
|
8
|
+
* Expose additional Array/Enumerable behavior for model-array
|
9
|
+
instances
|
10
|
+
* Implement #to_a for model-array instances
|
11
|
+
* Implement #to_h for object-model instances
|
12
|
+
|
1
13
|
### 0.4.1
|
2
|
-
*
|
14
|
+
* Enhancements
|
3
15
|
* Add this CHANGELOG.md file
|
4
16
|
* Made feature scenarios more informative regarding bracket
|
5
17
|
operator behavior for defined properties when extra
|
@@ -5,12 +5,17 @@ module SonJay
|
|
5
5
|
other.extend ClassBehavior
|
6
6
|
end
|
7
7
|
|
8
|
+
# Deprecated
|
9
|
+
def sonj_content
|
10
|
+
model_content
|
11
|
+
end
|
12
|
+
|
8
13
|
module ClassBehavior
|
9
14
|
|
10
15
|
def parse_json(json)
|
11
16
|
data = JSON.parse( json )
|
12
17
|
instance = new
|
13
|
-
instance.
|
18
|
+
instance.model_content.load_data data
|
14
19
|
instance
|
15
20
|
end
|
16
21
|
|
data/lib/son_jay/model_array.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
|
3
3
|
module SonJay
|
4
|
+
include Enumerable
|
4
5
|
|
5
6
|
def self.ModelArray(entry_class)
|
6
7
|
Class.new(ModelArray).tap{ |c|
|
@@ -21,7 +22,7 @@ module SonJay
|
|
21
22
|
@entries = []
|
22
23
|
end
|
23
24
|
|
24
|
-
def
|
25
|
+
def model_content
|
25
26
|
self
|
26
27
|
end
|
27
28
|
|
@@ -33,17 +34,57 @@ module SonJay
|
|
33
34
|
|
34
35
|
def load_data(data)
|
35
36
|
data.each do |entry_data|
|
36
|
-
additional.
|
37
|
+
additional.model_content.load_data entry_data
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
41
|
+
def freeze
|
42
|
+
super
|
43
|
+
@entries.freeze
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def dup
|
48
|
+
new_copy = super
|
49
|
+
new_copy.instance_variable_set :@entries, @entries.dup
|
50
|
+
new_copy
|
51
|
+
end
|
52
|
+
|
53
|
+
def clone
|
54
|
+
new_copy = super
|
55
|
+
new_copy.instance_variable_set :@entries, @entries.clone unless new_copy.frozen?
|
56
|
+
new_copy
|
57
|
+
end
|
58
|
+
|
40
59
|
def_delegators :@entries, *[
|
41
60
|
:[] ,
|
61
|
+
:at ,
|
62
|
+
:choice,
|
63
|
+
:collect,
|
64
|
+
:count ,
|
65
|
+
:cycle ,
|
66
|
+
:drop ,
|
67
|
+
:drop_while ,
|
68
|
+
:each ,
|
69
|
+
:each_index,
|
42
70
|
:empty? ,
|
43
71
|
:entries ,
|
72
|
+
:fetch ,
|
73
|
+
:find_index ,
|
74
|
+
:first ,
|
75
|
+
:include? ,
|
76
|
+
:index ,
|
77
|
+
:last ,
|
44
78
|
:length ,
|
79
|
+
:map ,
|
80
|
+
:product ,
|
81
|
+
:size ,
|
45
82
|
:to_json ,
|
83
|
+
:to_yaml ,
|
84
|
+
:zip
|
46
85
|
]
|
47
86
|
|
87
|
+
alias to_a entries
|
88
|
+
|
48
89
|
end
|
49
90
|
end
|
@@ -2,22 +2,21 @@ require 'forwardable'
|
|
2
2
|
|
3
3
|
module SonJay
|
4
4
|
class ObjectModel
|
5
|
-
module
|
5
|
+
module Content
|
6
6
|
|
7
7
|
class Abstract
|
8
8
|
extend Forwardable
|
9
|
+
include Enumerable
|
9
10
|
|
10
11
|
attr_reader :model_properties
|
11
12
|
|
12
13
|
def initialize(property_definitions)
|
13
14
|
@property_definitions = property_definitions
|
14
|
-
@data =
|
15
|
+
@data = ObjectModel::ContentData.new
|
15
16
|
@model_properties = Set.new
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@model_properties << d.name if is_model_property
|
20
|
-
end
|
17
|
+
|
18
|
+
init_properties \
|
19
|
+
*property_definitions.partition { |md| !! md.model_class }
|
21
20
|
end
|
22
21
|
|
23
22
|
def_delegators :@data, *[
|
@@ -67,11 +66,48 @@ module SonJay
|
|
67
66
|
hash_for_json.to_json( options )
|
68
67
|
end
|
69
68
|
|
69
|
+
def each
|
70
|
+
raise NotImplementedError, "Subclass responsibility"
|
71
|
+
end
|
72
|
+
|
73
|
+
def freeze
|
74
|
+
super
|
75
|
+
@data.freeze
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def dup
|
80
|
+
new_copy = super
|
81
|
+
new_copy.instance_variable_set :@data, @data.dup
|
82
|
+
new_copy.instance_variable_set :@model_properties, @model_properties.dup
|
83
|
+
new_copy
|
84
|
+
end
|
85
|
+
|
86
|
+
def clone
|
87
|
+
new_copy = super
|
88
|
+
unless new_copy.frozen?
|
89
|
+
new_copy.instance_variable_set :@data, @data.clone
|
90
|
+
new_copy.instance_variable_set :@model_properties, @model_properties.clone
|
91
|
+
end
|
92
|
+
new_copy
|
93
|
+
end
|
94
|
+
|
70
95
|
private
|
71
96
|
|
97
|
+
def init_properties(model_property_defs, value_property_defs)
|
98
|
+
value_property_defs.each do |d|
|
99
|
+
@data[d.name] = nil
|
100
|
+
end
|
101
|
+
|
102
|
+
model_property_defs.each do |d|
|
103
|
+
@data[d.name] = d.model_class.new
|
104
|
+
@model_properties << d.name
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
72
108
|
def load_defined_property(name_string, value)
|
73
109
|
if @model_properties.include?( name_string )
|
74
|
-
@data[ name_string ].
|
110
|
+
@data[ name_string ].model_content.load_data value
|
75
111
|
else
|
76
112
|
@data[ name_string ] = value
|
77
113
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module SonJay
|
2
|
+
class ObjectModel
|
3
|
+
module Content
|
4
|
+
|
5
|
+
class ContentWithExtra < Abstract
|
6
|
+
|
7
|
+
def extra
|
8
|
+
@extra ||= ObjectModel::ContentData.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def each
|
12
|
+
@data.each do |(name, value)|
|
13
|
+
yield name, value
|
14
|
+
end
|
15
|
+
@extra.each do |(name, value)|
|
16
|
+
yield name, value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def freeze
|
21
|
+
super
|
22
|
+
@extra.freeze
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def dup
|
27
|
+
new_copy = super
|
28
|
+
new_copy.instance_variable_set :@extra, @extra.dup if defined? @extra
|
29
|
+
new_copy
|
30
|
+
end
|
31
|
+
|
32
|
+
def clone
|
33
|
+
new_copy = super
|
34
|
+
if (defined? @extra) && (! new_copy.frozen?)
|
35
|
+
p new_copy.frozen?
|
36
|
+
new_copy.instance_variable_set :@extra, @extra.clone
|
37
|
+
end
|
38
|
+
new_copy
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_h
|
42
|
+
extra.empty? ?
|
43
|
+
@data.dup :
|
44
|
+
extra.hash_merge( @data )
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def load_extra_property(name_string, value)
|
50
|
+
extra[ name_string ] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
def hash_for_json
|
54
|
+
extra.empty? ?
|
55
|
+
@data :
|
56
|
+
extra.hash_merge( @data )
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -1,13 +1,23 @@
|
|
1
1
|
module SonJay
|
2
2
|
class ObjectModel
|
3
|
-
module
|
3
|
+
module Content
|
4
4
|
|
5
|
-
class
|
5
|
+
class ContentWithoutExtra < Abstract
|
6
6
|
|
7
7
|
def extra
|
8
8
|
raise SonJay::DisabledMethodError
|
9
9
|
end
|
10
10
|
|
11
|
+
def each
|
12
|
+
@data.each do |(name, value)|
|
13
|
+
yield name, value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_h
|
18
|
+
@data.dup
|
19
|
+
end
|
20
|
+
|
11
21
|
private
|
12
22
|
|
13
23
|
def load_extra_property(name_string, value)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'son_jay/object_model/content/abstract'
|
2
|
+
require 'son_jay/object_model/content/content_without_extra'
|
3
|
+
require 'son_jay/object_model/content/content_with_extra'
|
4
|
+
|
5
|
+
module SonJay
|
6
|
+
class ObjectModel
|
7
|
+
|
8
|
+
module Content
|
9
|
+
|
10
|
+
def self.new(property_definitions, allow_extra)
|
11
|
+
klass = allow_extra ?
|
12
|
+
self::ContentWithExtra :
|
13
|
+
self::ContentWithoutExtra
|
14
|
+
klass.new( property_definitions )
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module SonJay
|
4
|
+
class ObjectModel
|
5
|
+
|
6
|
+
class ContentData
|
7
|
+
extend Forwardable
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
def initialize(data = {})
|
11
|
+
@data = data.to_hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def []=(name, value)
|
15
|
+
name = "#{name}" unless String === name
|
16
|
+
@data[name] = value
|
17
|
+
end
|
18
|
+
|
19
|
+
def fetch(name, *args)
|
20
|
+
name = "#{name}" unless String === name
|
21
|
+
block_given? ?
|
22
|
+
@data.fetch(name, *args) { |*args| yield *args } :
|
23
|
+
@data.fetch(name, *args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](name)
|
27
|
+
name = "#{name}" unless String === name
|
28
|
+
@data[name]
|
29
|
+
end
|
30
|
+
|
31
|
+
def hash_merge(other)
|
32
|
+
@data.dup.tap { |result|
|
33
|
+
other.each do |name, value|
|
34
|
+
name = "#{name}" unless String === name
|
35
|
+
result[name] = value
|
36
|
+
end
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def hash_merge(other)
|
41
|
+
@data.dup.tap { |result|
|
42
|
+
other.each do |name, value|
|
43
|
+
result[name] = value
|
44
|
+
end
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def freeze
|
49
|
+
@data.freeze
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_h
|
54
|
+
@data.dup
|
55
|
+
end
|
56
|
+
|
57
|
+
def dup
|
58
|
+
self.class.new( @data.dup )
|
59
|
+
end
|
60
|
+
|
61
|
+
def clone
|
62
|
+
new_copy = super
|
63
|
+
unless new_copy.frozen?
|
64
|
+
new_copy.instance_variable_set :@data, @data.clone
|
65
|
+
end
|
66
|
+
new_copy
|
67
|
+
end
|
68
|
+
|
69
|
+
def_delegators :@data, :each, :length, :keys, :has_key?, :empty?, :to_json
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -22,11 +22,23 @@ module SonJay
|
|
22
22
|
@name_symbol_to_string_map = {}
|
23
23
|
end
|
24
24
|
|
25
|
+
def +(other)
|
26
|
+
sum = self.class.new
|
27
|
+
each do |property_definition|
|
28
|
+
sum << property_definition
|
29
|
+
end
|
30
|
+
other.each do |property_definition|
|
31
|
+
sum << property_definition
|
32
|
+
end
|
33
|
+
sum
|
34
|
+
end
|
35
|
+
|
25
36
|
def <<(definition)
|
26
37
|
@definitions << definition
|
27
38
|
name = definition.name
|
28
39
|
@names << name
|
29
40
|
@name_symbol_to_string_map[name.to_sym] = name
|
41
|
+
self
|
30
42
|
end
|
31
43
|
|
32
44
|
def_delegators :@definitions, :each
|
data/lib/son_jay/object_model.rb
CHANGED
@@ -1,25 +1,29 @@
|
|
1
1
|
require 'set'
|
2
|
-
require 'son_jay/object_model/
|
2
|
+
require 'son_jay/object_model/content_data'
|
3
|
+
require 'son_jay/object_model/content'
|
3
4
|
require 'son_jay/object_model/property_definition'
|
4
5
|
require 'son_jay/object_model/property_definitions'
|
5
6
|
require 'son_jay/object_model/properties_definer'
|
6
|
-
require 'son_jay/object_model/extra_data'
|
7
7
|
|
8
8
|
module SonJay
|
9
9
|
class ObjectModel
|
10
10
|
include ActsAsModel
|
11
11
|
|
12
|
-
attr_reader :
|
12
|
+
attr_reader :model_content
|
13
13
|
|
14
14
|
def initialize
|
15
15
|
definitions = self.class.property_definitions
|
16
|
-
@
|
16
|
+
@model_content = ObjectModel::Content.new(
|
17
17
|
definitions, self.class.extras_allowed?
|
18
18
|
)
|
19
19
|
end
|
20
20
|
|
21
21
|
def to_json(*args)
|
22
|
-
|
22
|
+
model_content.to_json( *args )
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_h
|
26
|
+
model_content.to_h
|
23
27
|
end
|
24
28
|
|
25
29
|
def []=(name, value)
|
@@ -35,18 +39,18 @@ module SonJay
|
|
35
39
|
end
|
36
40
|
|
37
41
|
def fetch(name)
|
38
|
-
|
42
|
+
model_content.fetch( name )
|
39
43
|
end
|
40
44
|
|
41
45
|
private
|
42
46
|
|
43
47
|
def property_store_for(name_string)
|
44
|
-
store =
|
48
|
+
store = model_content
|
45
49
|
if (
|
46
50
|
self.class.extras_allowed? &&
|
47
51
|
(! self.class.property_definitions.include_name?(name_string) )
|
48
52
|
) then
|
49
|
-
store =
|
53
|
+
store = model_content.extra
|
50
54
|
end
|
51
55
|
store
|
52
56
|
end
|
@@ -72,11 +76,27 @@ module SonJay
|
|
72
76
|
end
|
73
77
|
|
74
78
|
def _evaluate_property_definitions
|
75
|
-
|
76
|
-
_property_initializations
|
77
|
-
)
|
79
|
+
_populate_property_definitions
|
78
80
|
_validate_model_dependencies!
|
79
81
|
_apply_property_definitions property_definitions
|
82
|
+
_property_definitions
|
83
|
+
end
|
84
|
+
|
85
|
+
def _populate_property_definitions
|
86
|
+
@property_definitions =
|
87
|
+
_inherited_property_definitions +
|
88
|
+
PropertyDefinitions.from_initializations(
|
89
|
+
_property_initializations
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
def _inherited_property_definitions
|
94
|
+
self == SonJay::ObjectModel ?
|
95
|
+
PropertyDefinitions.new :
|
96
|
+
superclass.property_definitions
|
97
|
+
end
|
98
|
+
|
99
|
+
def _property_definitions
|
80
100
|
@property_definitions
|
81
101
|
end
|
82
102
|
|
@@ -88,8 +108,8 @@ module SonJay
|
|
88
108
|
definitions.each do |d|
|
89
109
|
name = d.name
|
90
110
|
class_eval <<-CODE
|
91
|
-
def #{name} ;
|
92
|
-
def #{name}=(value) ;
|
111
|
+
def #{name} ; model_content[#{name.inspect}] ; end
|
112
|
+
def #{name}=(value) ; model_content[#{name.inspect}] = value ; end
|
93
113
|
CODE
|
94
114
|
end
|
95
115
|
end
|
data/lib/son_jay/value_array.rb
CHANGED
data/lib/son_jay/version.rb
CHANGED
data/spec/acts_as_model_spec.rb
CHANGED
@@ -15,16 +15,16 @@ describe SonJay::ActsAsModel do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
@
|
18
|
+
def model_content
|
19
|
+
@model_content ||= Content.new
|
20
20
|
end
|
21
21
|
end
|
22
22
|
}
|
23
23
|
|
24
24
|
describe '::parse_json' do
|
25
|
-
it "returns a new instance with parsed JSON data loaded into its #
|
25
|
+
it "returns a new instance with parsed JSON data loaded into its #model_content object" do
|
26
26
|
instance = klass.parse_json( '{"hello": "world"}' )
|
27
|
-
loaded_data = instance.
|
27
|
+
loaded_data = instance.model_content.loaded_data
|
28
28
|
expect( loaded_data ).to eq( {'hello' => 'world'} )
|
29
29
|
end
|
30
30
|
end
|