son_jay 0.4.1 → 0.5.0

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