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 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
- [![travis](https://secure.travis-ci.org/hakanensari/structure.png)](http://travis-ci.org/hakanensari/structure)
3
+ [![travis](https://secure.travis-ci.org/hakanensari/structure.png?branch=master)](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, String
8
+ key :name
9
9
  many :friends
10
10
  end
11
11
 
12
- Please see [the project page] [1] for more detailed info.
12
+ Please see the [wiki] [1] for more detail.
13
13
 
14
- [1]: http://code.papercavalier.com/structure/
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
- # A structure is a nestable key/value container.
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
- # Builds a structure out of the JSON representation of a
25
- # structure.
63
+ # Recreates a structure out of its JSON representation.
26
64
  def json_create(hsh)
27
- hsh.delete 'json_class'
28
- new hsh
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 options hash.
34
- #
35
- # The type should be a Ruby class.
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.nil? || type.nil? || default.is_a?(type)
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
- if type.nil?
59
- define_method("#{name}=") { |val| attributes[name] = val }
60
- elsif Kernel.respond_to? type.to_s
61
- define_method("#{name}=") do |val|
62
- attributes[name] =
63
- if val.nil? || val.is_a?(type)
64
- val
65
- else
66
- Kernel.send(type.to_s, val)
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
- # A shorthand that defines an attribute that is an array.
100
+ # Defines an attribute that is an array and defaults to an empty
101
+ # one.
82
102
  def many(name)
83
- key name, Array, :default => []
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, will seed the attributes.
119
+ # A hash, if provided, seeds the attributes.
90
120
  def initialize(hsh = {})
91
- @attributes = self.class.defaults.inject({}) do |a, (k, v)|
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? :to_hash
142
+ if v.respond_to?(:to_hash)
113
143
  v.to_hash
114
- elsif v.is_a? Array
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 a JSON representation.
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/rails' if defined?(Rails)
169
+ require 'structure/active_support' if defined?(Rails)
File without changes
@@ -1,3 +1,3 @@
1
1
  class Structure
2
- VERSION = '0.19.0'
2
+ VERSION = '0.20.0'
3
3
  end
@@ -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 'ruby-debug'
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 :age, Integer
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
- test "should enumerate" do
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
- test "should define accessors" do
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
- test "should raise errors" do
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
- test "should store defaults" do
42
+ def test_key_defaults
45
43
  assert_equal [], Person.new.friends
46
44
  end
47
45
 
48
- test "should typecheck" do
49
- person = Person.new
50
- person.age = '18'
51
- assert_equal 18, person.age
46
+ def test_typechecking
47
+ loc = Location.new
48
+ loc.lon = "1"
49
+ assert_kind_of Float, loc.lon
52
50
 
53
- person.age = nil
54
- assert_nil person.age
51
+ loc.lon = nil
52
+ assert_nil loc.lon
55
53
  end
56
54
 
57
- test "should handle arrays" do
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
- test "should translate to hash" do
67
- person = Person.new(:name => 'John')
68
- person.friends << Person.new(:name => 'Jane')
69
- assert_equal 'John', person.to_hash[:name]
70
- assert_equal 'Jane', person.to_hash[:friends].first[:name]
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
- test "should translate to JSON" do
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/rails'
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.19.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-08-27 00:00:00.000000000Z
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/rails.rb
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: 4009138194620496744
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: 4009138194620496744
55
+ hash: -506799676672156126
56
56
  requirements: []
57
57
  rubyforge_project: structure
58
58
  rubygems_version: 1.8.6