structure 0.22.0 → 0.22.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/structure.rb +36 -37
- data/lib/structure/json.rb +22 -1
- data/lib/structure/version.rb +1 -1
- data/test/structure_test.rb +15 -4
- metadata +2 -8
data/lib/structure.rb
CHANGED
@@ -7,7 +7,6 @@
|
|
7
7
|
# end
|
8
8
|
#
|
9
9
|
class Structure
|
10
|
-
# Summon a Basic Object.
|
11
10
|
unless defined? BasicObject
|
12
11
|
if defined? BlankSlate
|
13
12
|
BasicObject = BlankSlate
|
@@ -22,12 +21,12 @@ class Structure
|
|
22
21
|
|
23
22
|
# A wrapper for lazy-evaluating undeclared classes
|
24
23
|
#
|
25
|
-
# @note
|
24
|
+
# @note Somewhat borrowed from the same-named class in Ohm
|
26
25
|
class Wrapper < BasicObject
|
27
26
|
# Wraps specified class in a wrapper if it is not already wrapped
|
28
27
|
#
|
29
|
-
# @param [Class] klass
|
30
|
-
# @return [Wrapper]
|
28
|
+
# @param [Class] klass a class, which may already be wrapped
|
29
|
+
# @return [Wrapper] the wrapped class
|
31
30
|
def self.wrap(klass)
|
32
31
|
klass.class == self ? klass : new(klass.to_s)
|
33
32
|
end
|
@@ -53,8 +52,6 @@ class Structure
|
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
56
|
-
private
|
57
|
-
|
58
55
|
def method_missing(mth, *args, &block)
|
59
56
|
@unwrapped ? super : @unwrapped = true
|
60
57
|
::Kernel.const_get(@name).send(mth, *args, &block)
|
@@ -71,7 +68,7 @@ class Structure
|
|
71
68
|
# @param [Object] default an optional default value
|
72
69
|
def initialize(type, default = nil)
|
73
70
|
@wrapper = Wrapper.wrap(type)
|
74
|
-
@default = default
|
71
|
+
@default = typecast(default)
|
75
72
|
end
|
76
73
|
|
77
74
|
# @return the default value for the key
|
@@ -88,7 +85,9 @@ class Structure
|
|
88
85
|
# @raise [TypeError] value isn't a type
|
89
86
|
# @return [Object] a typecast value
|
90
87
|
def typecast(val)
|
91
|
-
if val.nil?
|
88
|
+
if val.nil?
|
89
|
+
nil
|
90
|
+
elsif val.is_a?(type)
|
92
91
|
val.dup rescue val
|
93
92
|
elsif Kernel.respond_to?(type.to_s)
|
94
93
|
Kernel.send(type.to_s, val)
|
@@ -113,32 +112,17 @@ class Structure
|
|
113
112
|
# @param [Object] default an optional default value
|
114
113
|
# @raise [NameError] name is already taken
|
115
114
|
def key(name, type = String, default = nil)
|
116
|
-
name = name.to_sym
|
117
|
-
|
118
|
-
if method_defined?(name)
|
115
|
+
if method_defined?(name = name.to_sym)
|
119
116
|
raise NameError, "#{name} is taken"
|
120
117
|
end
|
121
118
|
|
122
|
-
if default && !default.is_a?(type)
|
123
|
-
raise TypeError, "#{default} isn't a #{type}"
|
124
|
-
end
|
125
|
-
|
126
|
-
# Add key to blueprint.
|
127
119
|
blueprint[name] = Definition.new(type, default)
|
128
|
-
|
129
|
-
# Define getter.
|
130
|
-
define_method(name) do
|
131
|
-
@attributes[name]
|
132
|
-
end
|
133
|
-
|
134
|
-
# Define setter.
|
120
|
+
define_method(name) { @attributes[name] }
|
135
121
|
define_method("#{name}=") do |val|
|
136
|
-
|
122
|
+
modifiable[name] = blueprint[name].typecast(val)
|
137
123
|
end
|
138
124
|
end
|
139
125
|
|
140
|
-
private
|
141
|
-
|
142
126
|
def const_missing(name)
|
143
127
|
Wrapper.new(name)
|
144
128
|
end
|
@@ -158,18 +142,19 @@ class Structure
|
|
158
142
|
|
159
143
|
# @return [Hash] a hash representation of the structure
|
160
144
|
def to_hash
|
161
|
-
@attributes.inject({})
|
162
|
-
|
163
|
-
v.to_hash
|
164
|
-
elsif v.is_a?(Array)
|
165
|
-
v.map { |e| e.respond_to?(:to_hash) ? e.to_hash : e }
|
166
|
-
else
|
167
|
-
v
|
168
|
-
end
|
145
|
+
@attributes.inject({}) { |a, (k, v)| a.merge k => hashify(v) }
|
146
|
+
end
|
169
147
|
|
170
|
-
|
148
|
+
def hashify(obj)
|
149
|
+
if obj.respond_to? :to_hash
|
150
|
+
obj.to_hash
|
151
|
+
elsif obj.is_a? Array
|
152
|
+
obj.map { |e| hashify(e) }
|
153
|
+
else
|
154
|
+
obj
|
171
155
|
end
|
172
156
|
end
|
157
|
+
private :hashify
|
173
158
|
|
174
159
|
# Compares this object with another object for equality
|
175
160
|
#
|
@@ -182,9 +167,23 @@ class Structure
|
|
182
167
|
other.is_a?(self.class) && attributes == other.attributes
|
183
168
|
end
|
184
169
|
|
185
|
-
private
|
186
|
-
|
187
170
|
def blueprint
|
188
171
|
self.class.blueprint
|
189
172
|
end
|
173
|
+
private :blueprint
|
174
|
+
|
175
|
+
# Used internally to check if the structure is frozen or not before
|
176
|
+
# updating a value
|
177
|
+
#
|
178
|
+
# @note Borrowed from OpenStruct
|
179
|
+
def modifiable
|
180
|
+
begin
|
181
|
+
@modifiable = true
|
182
|
+
rescue
|
183
|
+
raise TypeError, "can't modify frozen #{self.class}", caller(3)
|
184
|
+
end
|
185
|
+
|
186
|
+
@attributes
|
187
|
+
end
|
188
|
+
private :modifiable
|
190
189
|
end
|
data/lib/structure/json.rb
CHANGED
@@ -5,6 +5,27 @@ rescue NameError
|
|
5
5
|
end
|
6
6
|
|
7
7
|
class Structure
|
8
|
+
# JSON methods for a structure
|
9
|
+
#
|
10
|
+
# Include this in your structure if you need to cast it to JSON and
|
11
|
+
# vice versa.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# class Point < Structure
|
15
|
+
# include JSON
|
16
|
+
#
|
17
|
+
# key :x, Integer
|
18
|
+
# key :y, Integer
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Alternatively, include it in the parent class if you have more than
|
22
|
+
# one structure.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# class Structure
|
26
|
+
# include JSON
|
27
|
+
# end
|
28
|
+
#
|
8
29
|
module JSON
|
9
30
|
def self.included(base)
|
10
31
|
base.extend ClassMethods
|
@@ -46,7 +67,7 @@ class Structure
|
|
46
67
|
module ClassMethods
|
47
68
|
# Builds a structure out of its JSON representation
|
48
69
|
#
|
49
|
-
# @param [Hash] hsh a
|
70
|
+
# @param [Hash] hsh a hash representation of a JSON
|
50
71
|
# @return [Structure] a structure
|
51
72
|
def json_create(hsh)
|
52
73
|
hsh.delete('json_class')
|
data/lib/structure/version.rb
CHANGED
data/test/structure_test.rb
CHANGED
@@ -70,13 +70,24 @@ class TestStructure < MiniTest::Unit::TestCase
|
|
70
70
|
assert_equal 'John', other.friends.first.name
|
71
71
|
end
|
72
72
|
|
73
|
+
def test_cant_change_sex_when_frozen
|
74
|
+
person = Person.new(:name => 'John')
|
75
|
+
person.freeze
|
76
|
+
assert_raises(TypeError) { person.name = 'Jane' }
|
77
|
+
end
|
78
|
+
|
73
79
|
def test_to_hash
|
74
80
|
person = Person.new(:name => 'John')
|
75
|
-
|
76
|
-
|
81
|
+
friend = Person.new(:name => 'Jane')
|
82
|
+
person.friends << friend
|
83
|
+
hsh = person.to_hash
|
84
|
+
|
85
|
+
assert_equal person.name, hsh[:name]
|
86
|
+
assert_equal friend.name, hsh[:friends].first[:name]
|
77
87
|
|
78
|
-
|
79
|
-
|
88
|
+
person.friends = [[friend]]
|
89
|
+
hsh = person.to_hash
|
90
|
+
assert_equal friend.name, hsh[:friends].first.first[:name]
|
80
91
|
end
|
81
92
|
|
82
93
|
def test_json
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: structure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.22.
|
4
|
+
version: 0.22.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-27 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Structure is a typed key/value container.
|
15
15
|
email:
|
@@ -41,18 +41,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
41
|
- - ! '>='
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: '0'
|
44
|
-
segments:
|
45
|
-
- 0
|
46
|
-
hash: 2031698207987898845
|
47
44
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
45
|
none: false
|
49
46
|
requirements:
|
50
47
|
- - ! '>='
|
51
48
|
- !ruby/object:Gem::Version
|
52
49
|
version: '0'
|
53
|
-
segments:
|
54
|
-
- 0
|
55
|
-
hash: 2031698207987898845
|
56
50
|
requirements: []
|
57
51
|
rubyforge_project: structure
|
58
52
|
rubygems_version: 1.8.10
|