structure 0.19.0 → 0.20.0

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