structure 0.19.0 → 0.20.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.
- data/Gemfile +1 -7
- data/README.md +4 -4
- data/lib/structure.rb +77 -47
- data/lib/structure/{rails.rb → active_support.rb} +0 -0
- data/lib/structure/version.rb +1 -1
- data/test/structure_test.rb +37 -41
- metadata +5 -5
data/Gemfile
CHANGED
@@ -1,12 +1,6 @@
|
|
1
1
|
source :rubygems
|
2
|
-
|
3
2
|
gemspec
|
4
|
-
|
5
3
|
gem 'activesupport', '~> 3.0'
|
6
4
|
gem 'json', :platform => [:mri_18, :jruby, :rbx]
|
5
|
+
gem 'pry' unless ENV['CI']
|
7
6
|
gem 'rake'
|
8
|
-
|
9
|
-
unless ENV['CI']
|
10
|
-
gem 'ruby-debug', :platforms => :mri_18
|
11
|
-
gem 'ruby-debug19', :platforms => :mri_19
|
12
|
-
end
|
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# Structure
|
2
2
|
|
3
|
-
[](http://travis-ci.org/hakanensari/structure)
|
3
|
+
[](http://travis-ci.org/hakanensari/structure)
|
4
4
|
|
5
5
|
Structure is a typed, nestable key/value container.
|
6
6
|
|
7
7
|
class Person < Structure
|
8
|
-
key :name
|
8
|
+
key :name
|
9
9
|
many :friends
|
10
10
|
end
|
11
11
|
|
12
|
-
Please see
|
12
|
+
Please see the [wiki] [1] for more detail.
|
13
13
|
|
14
|
-
[1]:
|
14
|
+
[1]: https://github.com/hakanensari/structure/wiki/
|
data/lib/structure.rb
CHANGED
@@ -1,94 +1,124 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
begin
|
2
3
|
JSON::JSON_LOADED
|
3
4
|
rescue NameError
|
4
5
|
require 'json'
|
5
6
|
end
|
6
7
|
|
7
|
-
#
|
8
|
+
# Structure is a nestable, typed key/value container.
|
8
9
|
#
|
9
10
|
# class Person < Structure
|
10
11
|
# key :name
|
11
|
-
# key :age, Integer
|
12
12
|
# many :friends
|
13
13
|
# end
|
14
14
|
#
|
15
15
|
class Structure
|
16
|
+
extend Forwardable
|
16
17
|
include Enumerable
|
17
18
|
|
19
|
+
# A namespaced basic object.
|
20
|
+
#
|
21
|
+
# If running a legacy Ruby version, we either quote Builder's or
|
22
|
+
# fabricate ourselves.
|
23
|
+
if defined?(BasicObject)
|
24
|
+
BasicObject = ::BasicObject
|
25
|
+
elsif defined?(BlankSlate)
|
26
|
+
BasicObject = ::BlankSlate
|
27
|
+
else
|
28
|
+
class BasicObject
|
29
|
+
instance_methods.each do |mth|
|
30
|
+
undef_method(mth) unless mth =~ /\A(__|instance_eval)/
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# A double that stands in for a yet-to-be-defined class. Otherwise
|
36
|
+
# known as "lazy evaluation."
|
37
|
+
#
|
38
|
+
# Idea lifted from:
|
39
|
+
# http://github.com/soveran/ohm/
|
40
|
+
class Double < BasicObject
|
41
|
+
def initialize(name)
|
42
|
+
@name = name
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
@name.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def method_missing(mth, *args, &block)
|
50
|
+
@unwrapped ? super : @unwrapped = true
|
51
|
+
::Kernel.const_get(@name).send(mth, *args, &block)
|
52
|
+
ensure
|
53
|
+
@unwrapped = false
|
54
|
+
end; private :method_missing
|
55
|
+
end
|
56
|
+
|
18
57
|
class << self
|
19
58
|
# Returns attribute keys and their default values.
|
20
59
|
def defaults
|
21
60
|
@defaults ||= {}
|
22
61
|
end
|
23
62
|
|
24
|
-
#
|
25
|
-
# structure.
|
63
|
+
# Recreates a structure out of its JSON representation.
|
26
64
|
def json_create(hsh)
|
27
|
-
hsh.delete
|
28
|
-
new
|
65
|
+
hsh.delete('json_class')
|
66
|
+
new(hsh)
|
29
67
|
end
|
30
68
|
|
31
69
|
# Defines an attribute.
|
32
70
|
#
|
33
|
-
# Takes a name and, optionally, a type and
|
34
|
-
|
35
|
-
|
36
|
-
#
|
37
|
-
# Available options are:
|
38
|
-
#
|
39
|
-
# * +:default+, which specifies a default value for the attribute.
|
40
|
-
def key(name, *args)
|
41
|
-
name = name.to_sym
|
42
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
43
|
-
type = args.shift
|
44
|
-
default = options[:default]
|
71
|
+
# Takes a name and, optionally, a type and default value.
|
72
|
+
def key(name, type = nil, default = nil)
|
73
|
+
name = name.to_sym
|
45
74
|
|
46
75
|
if method_defined?(name)
|
47
76
|
raise NameError, "#{name} is taken"
|
48
77
|
end
|
49
78
|
|
50
|
-
if default
|
79
|
+
if !default || !type || default.is_a?(type)
|
51
80
|
defaults[name] = default
|
52
81
|
else
|
82
|
+
binding.pry
|
53
83
|
raise TypeError, "#{default} isn't a #{type}"
|
54
84
|
end
|
55
85
|
|
56
86
|
define_method(name) { attributes[name] }
|
57
87
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
-
end
|
69
|
-
else
|
70
|
-
define_method("#{name}=") do |val|
|
71
|
-
attributes[name] =
|
72
|
-
if val.nil? || val.is_a?(type)
|
73
|
-
val
|
74
|
-
else
|
75
|
-
raise TypeError, "#{val} isn't a #{type}"
|
76
|
-
end
|
77
|
-
end
|
88
|
+
define_method("#{name}=") do |val|
|
89
|
+
attributes[name] =
|
90
|
+
if type.nil? || val.nil? || val.is_a?(type)
|
91
|
+
val.dup rescue val
|
92
|
+
elsif Kernel.respond_to?(type.to_s)
|
93
|
+
Kernel.send(type.to_s, val)
|
94
|
+
else
|
95
|
+
raise TypeError, "#{val} isn't a #{type}"
|
96
|
+
end
|
78
97
|
end
|
79
98
|
end
|
80
99
|
|
81
|
-
#
|
100
|
+
# Defines an attribute that is an array and defaults to an empty
|
101
|
+
# one.
|
82
102
|
def many(name)
|
83
|
-
key name, Array,
|
103
|
+
key name, Array, []
|
84
104
|
end
|
105
|
+
|
106
|
+
# Lazy-eval undefined constants, typically other structures,
|
107
|
+
# assuming they will be defined later in the text.
|
108
|
+
def const_missing(name)
|
109
|
+
Double.new(name)
|
110
|
+
end; private :const_missing
|
111
|
+
|
112
|
+
def inherited(child)
|
113
|
+
child.def_delegator child, :defaults
|
114
|
+
end; private :inherited
|
85
115
|
end
|
86
116
|
|
87
117
|
# Creates a new structure.
|
88
118
|
#
|
89
|
-
# A hash, if provided,
|
119
|
+
# A hash, if provided, seeds the attributes.
|
90
120
|
def initialize(hsh = {})
|
91
|
-
@attributes =
|
121
|
+
@attributes = defaults.inject({}) do |a, (k, v)|
|
92
122
|
a[k] = v.is_a?(Array) ? v.dup : v
|
93
123
|
a
|
94
124
|
end
|
@@ -109,9 +139,9 @@ class Structure
|
|
109
139
|
def to_hash
|
110
140
|
attributes.inject({}) do |a, (k, v)|
|
111
141
|
a[k] =
|
112
|
-
if v.respond_to?
|
142
|
+
if v.respond_to?(:to_hash)
|
113
143
|
v.to_hash
|
114
|
-
elsif v.is_a?
|
144
|
+
elsif v.is_a?(Array)
|
115
145
|
v.map { |e| e.respond_to?(:to_hash) ? e.to_hash : e }
|
116
146
|
else
|
117
147
|
v
|
@@ -121,7 +151,7 @@ class Structure
|
|
121
151
|
end
|
122
152
|
end
|
123
153
|
|
124
|
-
# Converts structure to
|
154
|
+
# Converts structure to its JSON representation.
|
125
155
|
def to_json(*args)
|
126
156
|
{ JSON.create_id => self.class.name }.
|
127
157
|
merge(attributes).
|
@@ -136,4 +166,4 @@ class Structure
|
|
136
166
|
end
|
137
167
|
end
|
138
168
|
|
139
|
-
require 'structure/
|
169
|
+
require 'structure/active_support' if defined?(Rails)
|
File without changes
|
data/lib/structure/version.rb
CHANGED
data/test/structure_test.rb
CHANGED
@@ -1,60 +1,58 @@
|
|
1
|
-
$:.push File.expand_path('../../lib', __FILE__)
|
2
|
-
|
3
1
|
require 'bundler/setup'
|
4
|
-
|
2
|
+
require 'test/unit'
|
5
3
|
begin
|
6
|
-
require '
|
4
|
+
require 'pry'
|
7
5
|
rescue LoadError
|
8
6
|
end
|
9
|
-
|
10
|
-
require 'structure'
|
11
|
-
require 'test/unit'
|
12
|
-
|
13
|
-
class Test::Unit::TestCase
|
14
|
-
def self.test(name, &block)
|
15
|
-
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
|
16
|
-
if method_defined? test_name
|
17
|
-
raise "#{test_name} is already defined in #{self}"
|
18
|
-
end
|
19
|
-
define_method test_name, &block
|
20
|
-
end
|
21
|
-
end
|
7
|
+
require File.expand_path('../../lib/structure', __FILE__)
|
22
8
|
|
23
9
|
class Person < Structure
|
24
10
|
key :name
|
25
|
-
key :
|
11
|
+
key :location, Location
|
26
12
|
many :friends
|
27
13
|
end
|
28
14
|
|
15
|
+
class Location < Structure
|
16
|
+
key :lon, Float
|
17
|
+
key :lat, Float
|
18
|
+
end
|
19
|
+
|
29
20
|
class TestStructure < Test::Unit::TestCase
|
30
|
-
|
21
|
+
def test_double
|
22
|
+
double = Structure::Double.new(:Foo)
|
23
|
+
assert_equal 'Foo', "#{double}"
|
24
|
+
assert_raise(NameError) { double.class }
|
25
|
+
::Kernel.const_set(:Foo, 1)
|
26
|
+
assert_kind_of Fixnum, double
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_enumeration
|
31
30
|
assert_respond_to Person.new, :map
|
32
31
|
end
|
33
32
|
|
34
|
-
|
33
|
+
def test_accessors
|
35
34
|
assert_respond_to Person.new, :name
|
36
35
|
assert_respond_to Person.new, :name=
|
37
36
|
end
|
38
37
|
|
39
|
-
|
38
|
+
def test_key_errors
|
40
39
|
assert_raise(NameError) { Person.key :class }
|
41
|
-
assert_raise(TypeError) { Person.key :foo, String, :default => 1 }
|
42
40
|
end
|
43
41
|
|
44
|
-
|
42
|
+
def test_key_defaults
|
45
43
|
assert_equal [], Person.new.friends
|
46
44
|
end
|
47
45
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
def test_typechecking
|
47
|
+
loc = Location.new
|
48
|
+
loc.lon = "1"
|
49
|
+
assert_kind_of Float, loc.lon
|
52
50
|
|
53
|
-
|
54
|
-
assert_nil
|
51
|
+
loc.lon = nil
|
52
|
+
assert_nil loc.lon
|
55
53
|
end
|
56
54
|
|
57
|
-
|
55
|
+
def test_array_type
|
58
56
|
person = Person.new
|
59
57
|
assert_equal [], person.friends
|
60
58
|
|
@@ -63,28 +61,26 @@ class TestStructure < Test::Unit::TestCase
|
|
63
61
|
assert_equal 0, person.friends.first.friends.size
|
64
62
|
end
|
65
63
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
64
|
+
# def test_to_hash
|
65
|
+
# person = Person.new(:name => 'John')
|
66
|
+
# person.friends << Person.new(:name => 'Jane')
|
67
|
+
# assert_equal 'John', person.to_hash[:name]
|
68
|
+
# assert_equal 'Jane', person.to_hash[:friends].first[:name]
|
69
|
+
# end
|
72
70
|
|
73
|
-
|
71
|
+
def test_json
|
74
72
|
person = Person.new
|
75
73
|
person.friends << Person.new
|
76
74
|
json = person.to_json
|
77
75
|
assert_kind_of Person, JSON.parse(json)
|
78
76
|
assert_kind_of Person, JSON.parse(json).friends.first
|
79
|
-
end
|
80
77
|
|
81
|
-
test "should translate to JSON in a Rails app" do
|
82
|
-
person = Person.new
|
83
78
|
assert_equal false, person.respond_to?(:as_json)
|
84
79
|
|
85
80
|
require 'active_support/ordered_hash'
|
86
81
|
require 'active_support/json'
|
87
|
-
require 'structure/
|
82
|
+
require 'structure/active_support'
|
83
|
+
|
88
84
|
assert_equal true, person.as_json(:only => :name).has_key?(:name)
|
89
85
|
assert_equal false, person.as_json(:except => :name).has_key?(:name)
|
90
86
|
end
|
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.
|
4
|
+
version: 0.20.0
|
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-
|
12
|
+
date: 2011-09-06 00:00:00.000000000Z
|
13
13
|
dependencies: []
|
14
14
|
description: Structure is a typed, nestable key/value container.
|
15
15
|
email:
|
@@ -25,7 +25,7 @@ files:
|
|
25
25
|
- README.md
|
26
26
|
- Rakefile
|
27
27
|
- lib/structure.rb
|
28
|
-
- lib/structure/
|
28
|
+
- lib/structure/active_support.rb
|
29
29
|
- lib/structure/version.rb
|
30
30
|
- structure.gemspec
|
31
31
|
- test/structure_test.rb
|
@@ -43,7 +43,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
43
|
version: '0'
|
44
44
|
segments:
|
45
45
|
- 0
|
46
|
-
hash:
|
46
|
+
hash: -506799676672156126
|
47
47
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
48
|
none: false
|
49
49
|
requirements:
|
@@ -52,7 +52,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
52
|
version: '0'
|
53
53
|
segments:
|
54
54
|
- 0
|
55
|
-
hash:
|
55
|
+
hash: -506799676672156126
|
56
56
|
requirements: []
|
57
57
|
rubyforge_project: structure
|
58
58
|
rubygems_version: 1.8.6
|