object_struct 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ ## 0.0.2 (September 22, 2010)
2
+
3
+ Protected ObjectStruct#convert method
1
4
 
2
5
  ## 0.0.1 (September 19, 2010)
3
6
 
@@ -0,0 +1,154 @@
1
+ # ---------------------------------------------------------------------------
2
+ # Copyright 2010, Quid, Inc.
3
+ #
4
+ # object_struct is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # object_struct is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with object_struct. If not, see <http://www.gnu.org/licenses/>.
16
+ #
17
+ # ---------------------------------------------------------------------------
18
+
19
+
20
+ # Inherit from ObjectStruct to allow object-style accesses to a hash parameter
21
+ # passed to {ObjectStruct#initialize initialize}. Properties are lazy-loaded.
22
+ #
23
+ # @see OpenStruct
24
+ #
25
+ # @abstract You probably want to subclass ObjectStruct, to reflect your business
26
+ # entities. See the README for example use.
27
+ class ObjectStruct < OpenStruct
28
+ VERSION = '0.0.2 '
29
+
30
+ # Delegated to Hash#each
31
+ def each &blk
32
+ to_h.each &blk
33
+ end
34
+
35
+ # Delegated to Hash#values
36
+ def values
37
+ to_h.values
38
+ end
39
+
40
+ # Specify the type of a particular property.
41
+ #
42
+ # @param [Hash] options The options hash. Pass any :property_name => ClassName
43
+ # to enforce that type.
44
+ #
45
+ # @option options [Boolean] :plural (false) Whether the property is plural,
46
+ # e.g. an Array of the class specified. (Yes, this means that if you have a
47
+ # property named :plural, you cannot specify its type.)
48
+ def self.property_type(options)
49
+ plural = options.delete(:plural)
50
+ property, type = *options.first
51
+ class_name = plural ? property.to_s.classify : property.to_s.camelize
52
+ Object.const_set class_name, Class.new(type)
53
+ end
54
+
55
+ # Create a new ObjectStruct.
56
+ #
57
+ # @param [Hash] data a Hash representing your data. Keys are used to identify
58
+ # the type of the property.
59
+ def initialize(data)
60
+ @table = Hash.new {|h, k| raise NoMethodError, "undefined property #{k} for #{self}"}
61
+ data = {'data' => data} unless data.respond_to? :each
62
+ for k,v in data
63
+ @table[k.to_sym] = self.class.value_maybe_promise(k, v)
64
+ new_ostruct_member(k)
65
+ end
66
+ end
67
+
68
+ # Provides has_property_name? methods. Intercepts messages matching
69
+ # /^has_[a-z_]+\?$/. If the method matches this regex, ObjectStruct will see
70
+ # if it responds to such a property. If that property responds to a count
71
+ # method, it will furthermore make sure #count is greater than zero.
72
+ # @return the property, if it exists, nil otherwise
73
+ def method_missing(name, *args)
74
+ if name =~ /^has_([a-z_]+)\?$/
75
+ if respond_to? (field = $1.to_sym)
76
+ result = (send field)
77
+ if result.respond_to?(:count) && result.method(:count).arity == 0
78
+ (result.count > 0)
79
+ else
80
+ result
81
+ end
82
+ end
83
+ else
84
+ super(name, *args)
85
+ end
86
+ end
87
+
88
+ # Specifically provide the object_id. If your objects have IDs, choose a more
89
+ # descriptive name.
90
+ def id
91
+ object_id
92
+ end
93
+
94
+ protected
95
+
96
+ def new_ostruct_member(name)
97
+ name = name.to_sym
98
+ unless self.respond_to?(name)
99
+ class << self; self; end.class_eval do
100
+ define_method(name) { ObjectStruct.demand @table[name] }
101
+ define_method("#{name}=") { |x| @table[name] = x }
102
+ end
103
+ end
104
+ name
105
+ end
106
+
107
+ # Recursively convert a value to an ObjectStruct (or a class matching the
108
+ # type, if it exists).
109
+ #
110
+ # @param [Array, Hash] value the value to convert. Arrays are converted
111
+ # recursively, each value to the requested type. Hashes are converted
112
+ # directly to the requested type.
113
+ #
114
+ # @param [String, #to_s] type the type of the value (or values in the array).
115
+ # This method falls back to ObjectStruct if it cannot find a class matching
116
+ # the type.
117
+ #
118
+ # @return [type/ObjectStruct, Array<type/ObjectStruct>, value] the type
119
+ # requested, or an ObjectStruct; or an Array thereof; or the value if it
120
+ # neither an Array nor Hash.
121
+ def self.convert(value, type = nil)
122
+ klass = ((value.is_a? Array) ? type.to_s.classify : type.to_s.camelize).constantize rescue ObjectStruct
123
+
124
+ case value
125
+ when Array
126
+ value.map{|e| convert(e, klass.to_s)}
127
+ when Hash
128
+ klass.new(value)
129
+ else
130
+ value
131
+ end
132
+ end
133
+
134
+ private
135
+
136
+ def to_h
137
+ result = {}
138
+ @table.each {|name, value| result[name.to_s] = value}
139
+ result
140
+ end
141
+
142
+ def self.value_maybe_promise(key, value)
143
+ case value
144
+ when Array, Hash
145
+ Lazy::Promise.new {convert(value, key)}
146
+ else
147
+ value
148
+ end
149
+ end
150
+
151
+ def self.demand value
152
+ (value.is_a? Lazy::Promise) ? value.__result__ : value
153
+ end
154
+ end
@@ -25,7 +25,7 @@
25
25
  # @abstract You probably want to subclass ObjectStruct, to reflect your business
26
26
  # entities. See the README for example use.
27
27
  class ObjectStruct < OpenStruct
28
- VERSION = '0.0.1'
28
+ VERSION = '0.0.2'
29
29
 
30
30
  # Delegated to Hash#each
31
31
  def each &blk
@@ -104,8 +104,6 @@ class ObjectStruct < OpenStruct
104
104
  name
105
105
  end
106
106
 
107
- private
108
-
109
107
  # Recursively convert a value to an ObjectStruct (or a class matching the
110
108
  # type, if it exists).
111
109
  #
@@ -133,6 +131,8 @@ class ObjectStruct < OpenStruct
133
131
  end
134
132
  end
135
133
 
134
+ private
135
+
136
136
  def to_h
137
137
  result = {}
138
138
  @table.each {|name, value| result[name.to_s] = value}
@@ -57,7 +57,7 @@ class ObjectStructTest < MiniTest::Unit::TestCase
57
57
  refute @widget.has_fake_property?
58
58
  end
59
59
 
60
- def has_property_name_empty
60
+ def test_has_property_name_empty
61
61
  refute @widget.has_empty_array?
62
62
  end
63
63
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 1
9
- version: 0.0.1
8
+ - 2
9
+ version: 0.0.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Quid, Inc.
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-09-19 00:00:00 -07:00
17
+ date: 2010-09-22 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -52,6 +52,7 @@ extra_rdoc_files: []
52
52
 
53
53
  files:
54
54
  - lib/object_struct/object_struct.rb
55
+ - lib/object_struct/#object_struct.rb#
55
56
  - lib/object_struct.rb
56
57
  - test/test_data.json
57
58
  - test/test_object_struct.rb