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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YTEwY2YzOGQ2MTRkMzUxNTliNGQ3NWUzMWY1ZGVjNDJiMzVkN2FmZg==
4
+ MTRiYjYyNWM4NGRkZGMxZmRjNGEyYTNlMzU0MzdhMzZjZjZhYTEzMQ==
5
5
  data.tar.gz: !binary |-
6
- NjVkMGQ5MDNmYmQyZDQ1MTU0NmQ1ZDgzZDczZmZkMWFiNzUxN2NhOA==
6
+ MTBjNDdmNDcxZDg0MjExY2RlNzEwYzA4MTlkZmMyZTY2NTFjYTZkOQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YjIxZWJjODE3MDEzNGViMTJlMzQxYTRjODc5M2IxMmJlYTE0OWVjYzMxYjky
10
- NThkMzMxYTQ4ZDBkZWE0NzlmYjhkNDY0NDgyNmVlZTI3MWUxYWQxYzUwN2Y1
11
- ZTI5NTQyNmMxMmE4YmRhYjNhMTBjZTc0MzUxNDVjZDExMWEwZmM=
9
+ NjA5NWU3MzMyNDZmMzFkMDBlNTdmNmIxODdlNmFiYjczMjg0OTBlYTRhYjZk
10
+ ZWZmYWNhMzAyMjJiNDM2MWY3OTJlZGMxNGFlMWQxYmZlMTM5OTA2MDEyZjhj
11
+ MTIyY2JhODAyNDViNTQ1Njc1YWNlMGQyNjNlMTM3Mjk5MGFjM2U=
12
12
  data.tar.gz: !binary |-
13
- M2I5Y2ZmYjk3ZTA4NjBiNWFhNmRhNDNkMjgwYjEyY2YxZGM0MDAzNDEyODg3
14
- YzM1ZGRhNWNmOTdhNTU1YjljMDQ2ZWY2Nzc3NWVhOGM5Y2ViODc0NzBmNGZk
15
- NzA4YzNiNWY0MjRlNTEwZGNmYjdiYTQ0ODEyNjZiOTAwMzNmZjc=
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
- * Enhamcements
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.sonj_content.load_data data
18
+ instance.model_content.load_data data
14
19
  instance
15
20
  end
16
21
 
@@ -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 sonj_content
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.sonj_content.load_data entry_data
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 Properties
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
- property_definitions.each do |d|
17
- is_model_property = !! d.model_class
18
- @data[d.name] = is_model_property ? d.model_class.new : nil
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 ].sonj_content.load_data value
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 Properties
3
+ module Content
4
4
 
5
- class PropertiesWithoutExtra < Abstract
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
@@ -1,25 +1,29 @@
1
1
  require 'set'
2
- require 'son_jay/object_model/properties'
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 :sonj_content
12
+ attr_reader :model_content
13
13
 
14
14
  def initialize
15
15
  definitions = self.class.property_definitions
16
- @sonj_content = ObjectModel::Properties.new(
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
- sonj_content.to_json( *args )
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
- sonj_content.fetch( name )
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 = sonj_content
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 = sonj_content.extra
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
- @property_definitions = PropertyDefinitions.from_initializations(
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} ; sonj_content[#{name.inspect}] ; end
92
- def #{name}=(value) ; sonj_content[#{name.inspect}] = value ; end
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
@@ -2,7 +2,7 @@ module SonJay
2
2
  class ValueArray < ::Array
3
3
  include ActsAsModel
4
4
 
5
- def sonj_content
5
+ def model_content
6
6
  self
7
7
  end
8
8
 
@@ -1,3 +1,3 @@
1
1
  module SonJay
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -15,16 +15,16 @@ describe SonJay::ActsAsModel do
15
15
  end
16
16
  end
17
17
 
18
- def sonj_content
19
- @sonj_content ||= Content.new
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 #sonj_content object" do
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.sonj_content.loaded_data
27
+ loaded_data = instance.model_content.loaded_data
28
28
  expect( loaded_data ).to eq( {'hello' => 'world'} )
29
29
  end
30
30
  end