structure 0.14.1 → 0.15.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/CHANGELOG.md +10 -7
- data/Gemfile +1 -2
- data/README.md +3 -13
- data/lib/structure/static.rb +61 -0
- data/lib/structure/version.rb +1 -1
- data/lib/structure.rb +35 -23
- data/test/fixtures/cities.yml +5 -0
- data/test/fixtures/cities_with_neighborhoods.yml +7 -0
- data/test/fixtures/cities_without_ids.yml +4 -0
- data/test/helper.rb +12 -0
- data/test/static_test.rb +62 -0
- data/test/structure_test.rb +7 -12
- metadata +14 -3
data/CHANGELOG.md
CHANGED
@@ -1,34 +1,37 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
-
## 0.
|
3
|
+
## 0.15.0
|
4
|
+
* bring back static module
|
5
|
+
|
6
|
+
## 0.14.0
|
4
7
|
* rename .attribute back to .key
|
5
8
|
* shorten .embeds_many and .embeds_one to .many and .one
|
6
9
|
* remove presence methods
|
7
10
|
* add options to .many
|
8
11
|
|
9
|
-
## 0.13.
|
12
|
+
## 0.13.0
|
10
13
|
|
11
14
|
* remove static module
|
12
15
|
* rename .key to .attribute
|
13
16
|
|
14
|
-
# 0.12.
|
17
|
+
# 0.12.0
|
15
18
|
|
16
19
|
* add static module
|
17
20
|
|
18
|
-
# 0.11.
|
21
|
+
# 0.11.0
|
19
22
|
|
20
23
|
* .key now emulates DataMapper.property
|
21
24
|
|
22
|
-
# 0.10.
|
25
|
+
# 0.10.0
|
23
26
|
|
24
27
|
* rename .has_one and .has_many to .embeds_one and .embeds_many to make room
|
25
28
|
for associations
|
26
29
|
|
27
|
-
# 0.9
|
30
|
+
# 0.9.0
|
28
31
|
|
29
32
|
* add presence method
|
30
33
|
|
31
|
-
# 0.8
|
34
|
+
# 0.8.0
|
32
35
|
|
33
36
|
* make JSON patch compatible with Active Support
|
34
37
|
* remove URI from list of types
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
# Structure
|
2
2
|
|
3
|
-
|
3
|
+
[](http://travis-ci.org/hakanensari/structure)
|
4
4
|
|
5
|
-
|
5
|
+
Structure is a Struct-like key/value container .
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
Set up a model:
|
7
|
+
It will shine in the ephemeral landscape of API-backed data.
|
10
8
|
|
11
9
|
require 'structure'
|
12
10
|
|
@@ -15,14 +13,6 @@ Set up a model:
|
|
15
13
|
many :friends
|
16
14
|
end
|
17
15
|
|
18
|
-
Do things with it:
|
19
|
-
|
20
|
-
person = Person.new
|
21
|
-
friend = Person.new
|
22
|
-
person.friends << friend
|
23
|
-
puts person.to_json
|
24
|
-
=> {"json_class":"Person","name":null,"friends":[{"json_class":"Person","name":null,"friends":[]}]}
|
25
|
-
|
26
16
|
Please see [the project page] [1] for more detailed info.
|
27
17
|
|
28
18
|
[1]: http://code.papercavalier.com/structure/
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# When included in a structure, this module turns it into a static
|
2
|
+
# model, the data of which is sourced from a yaml file.
|
3
|
+
#
|
4
|
+
# This is a basic implementation and does not handle nested structures.
|
5
|
+
# See test.
|
6
|
+
class Structure
|
7
|
+
module Static
|
8
|
+
def self.included(base)
|
9
|
+
base.key(:id, Integer)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
include Enumerable
|
15
|
+
|
16
|
+
# The path for the data file.
|
17
|
+
#
|
18
|
+
# This file should contain a YAML representation of the records.
|
19
|
+
#
|
20
|
+
# Overwrite this reader with an opiniated location to dry.
|
21
|
+
attr :data_path
|
22
|
+
|
23
|
+
# Returns all records.
|
24
|
+
def all
|
25
|
+
@records ||= data.map do |record|
|
26
|
+
record["id"] ||= increment_id
|
27
|
+
new(record)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Yields each record to given block.
|
32
|
+
#
|
33
|
+
# Other enumerators will be made available by the Enumerable
|
34
|
+
# module.
|
35
|
+
def each(&block)
|
36
|
+
all.each { |record| block.call(record) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Finds a record by its ID.
|
40
|
+
def find(id)
|
41
|
+
detect { |record| record.id == id }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Sets the path for the data file.
|
45
|
+
def set_data_path(data_path)
|
46
|
+
@data_path = data_path
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def data
|
52
|
+
YAML.load_file(@data_path)
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def increment_id
|
57
|
+
@increment_id = @increment_id.to_i + 1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/structure/version.rb
CHANGED
data/lib/structure.rb
CHANGED
@@ -15,18 +15,16 @@ end
|
|
15
15
|
# Structure is a Struct-like key/value container.
|
16
16
|
#
|
17
17
|
# class Person < Structure
|
18
|
-
# key
|
19
|
-
#
|
18
|
+
# key :name
|
19
|
+
# many :friends
|
20
20
|
# end
|
21
21
|
#
|
22
|
-
# person = Person.new(:name => "John")
|
23
|
-
# person.name
|
24
|
-
# => "John"
|
25
|
-
#
|
26
22
|
class Structure
|
27
23
|
include Enumerable
|
28
24
|
|
29
|
-
|
25
|
+
autoload :Static, 'structure/static'
|
26
|
+
|
27
|
+
# Available data type.
|
30
28
|
TYPES = [Array, Boolean, Float, Hash, Integer, String, Structure]
|
31
29
|
|
32
30
|
class << self
|
@@ -50,9 +48,9 @@ class Structure
|
|
50
48
|
#
|
51
49
|
# Takes a name, an optional type, and an optional hash of options.
|
52
50
|
#
|
53
|
-
# The type can be +Array+, +Boolean+, +Float+, +Hash+, +Integer+,
|
54
|
-
# a +Structure+, or a subclass thereof. If none is
|
55
|
-
# to +String+.
|
51
|
+
# The type can be +Array+, +Boolean+, +Float+, +Hash+, +Integer+,
|
52
|
+
# +String+, a +Structure+, or a subclass thereof. If none is
|
53
|
+
# specified, this defaults to +String+.
|
56
54
|
#
|
57
55
|
# Available options are:
|
58
56
|
#
|
@@ -74,15 +72,26 @@ class Structure
|
|
74
72
|
if default.nil? || default.is_a?(type)
|
75
73
|
default_attributes[name] = default
|
76
74
|
else
|
77
|
-
msg = "#{default}
|
75
|
+
msg = "#{default} isn't a#{'n' if type.name.match(/^[AI]/)} #{type}"
|
78
76
|
raise TypeError, msg
|
79
77
|
end
|
80
78
|
|
81
79
|
module_eval do
|
82
|
-
#
|
83
|
-
|
84
|
-
|
85
|
-
|
80
|
+
# A proc that typecasts value based on type.
|
81
|
+
typecaster =
|
82
|
+
case type.name
|
83
|
+
when 'Boolean'
|
84
|
+
lambda { |value|
|
85
|
+
# This should take care of the different representations
|
86
|
+
# of truth we might be feeding into the model.
|
87
|
+
#
|
88
|
+
# Any string other than "0" or "false" will evaluate to
|
89
|
+
# true.
|
90
|
+
#
|
91
|
+
# Any integer other than 0 will evaluate to true.
|
92
|
+
#
|
93
|
+
# Otherwise, we do the double-bang trick to non-boolean
|
94
|
+
# values.
|
86
95
|
case value
|
87
96
|
when Boolean
|
88
97
|
value
|
@@ -93,14 +102,17 @@ class Structure
|
|
93
102
|
else
|
94
103
|
!!value
|
95
104
|
end
|
96
|
-
|
97
|
-
|
98
|
-
|
105
|
+
}
|
106
|
+
when /Hash|Structure/
|
107
|
+
# We could possibly check if the value responds to #to_hash
|
108
|
+
# and cast to hash if it does, but I don't see any use case
|
109
|
+
# for this right now.
|
110
|
+
lambda { |value|
|
99
111
|
unless value.is_a? type
|
100
112
|
raise TypeError, "#{value} is not a #{type}"
|
101
113
|
end
|
102
114
|
value
|
103
|
-
|
115
|
+
}
|
104
116
|
else
|
105
117
|
lambda { |value| Kernel.send(type.to_s, value) }
|
106
118
|
end
|
@@ -109,7 +121,7 @@ class Structure
|
|
109
121
|
define_method(name) { @attributes[name] }
|
110
122
|
|
111
123
|
define_method("#{name}=") do |value|
|
112
|
-
@attributes[name] = value.nil? ? nil :
|
124
|
+
@attributes[name] = value.nil? ? nil : typecaster.call(value)
|
113
125
|
end
|
114
126
|
end
|
115
127
|
end
|
@@ -168,9 +180,9 @@ class Structure
|
|
168
180
|
to_json(*args)
|
169
181
|
end
|
170
182
|
|
171
|
-
# Compares this object with another object for equality. A Structure
|
172
|
-
# to the other object when latter is of the same class and
|
173
|
-
# attributes are the same.
|
183
|
+
# Compares this object with another object for equality. A Structure
|
184
|
+
# is equal to the other object when latter is of the same class and
|
185
|
+
# the two objects' attributes are the same.
|
174
186
|
def ==(other)
|
175
187
|
other.is_a?(self.class) && @attributes == other.attributes
|
176
188
|
end
|
data/test/helper.rb
ADDED
data/test/static_test.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.expand_path('../helper.rb', __FILE__)
|
2
|
+
|
3
|
+
class City < Structure
|
4
|
+
include Static
|
5
|
+
|
6
|
+
key :name
|
7
|
+
many :neighborhoods
|
8
|
+
end
|
9
|
+
|
10
|
+
class Neighborhood < Structure
|
11
|
+
key :name
|
12
|
+
end
|
13
|
+
|
14
|
+
class Dummy < Structure
|
15
|
+
include Static
|
16
|
+
|
17
|
+
key :name
|
18
|
+
end
|
19
|
+
|
20
|
+
class TestStatic < Test::Unit::TestCase
|
21
|
+
def fixture(klass, path)
|
22
|
+
klass.instance_variable_set(:@records, nil)
|
23
|
+
klass.instance_variable_set(:@increment_id, nil)
|
24
|
+
fixture = File.expand_path("../fixtures/#{path}.yml", __FILE__)
|
25
|
+
klass.set_data_path(fixture)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_class_enumeration
|
29
|
+
assert_respond_to City, :map
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_all
|
33
|
+
fixture City, 'cities'
|
34
|
+
cities = City.all
|
35
|
+
assert_kind_of City, cities.first
|
36
|
+
assert_equal 2, cities.size
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_find
|
40
|
+
fixture City, 'cities'
|
41
|
+
assert 'New York', City.find(1).name
|
42
|
+
assert_nil City.find(4)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_data_without_ids
|
46
|
+
fixture City, 'cities_without_ids'
|
47
|
+
assert_equal 'New York', City.find(1).name
|
48
|
+
assert_equal 'Paris', City.find(3).name
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_auto_increment
|
52
|
+
fixture City, 'cities_without_ids'
|
53
|
+
fixture Dummy, 'cities_without_ids'
|
54
|
+
assert_equal 'New York', City.find(1).name
|
55
|
+
assert_equal 'New York', Dummy.find(1).name
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_nesting
|
59
|
+
fixture City, 'cities_with_neighborhoods'
|
60
|
+
# assert_kind_of Neighborhood, City.first.neighborhoods.first
|
61
|
+
end
|
62
|
+
end
|
data/test/structure_test.rb
CHANGED
@@ -1,15 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'rubygems'
|
4
|
-
require 'bundler/setup'
|
5
|
-
|
6
|
-
begin
|
7
|
-
require 'ruby-debug'
|
8
|
-
rescue LoadError
|
9
|
-
end
|
10
|
-
|
11
|
-
require 'structure'
|
12
|
-
require 'test/unit'
|
1
|
+
require File.expand_path('../helper.rb', __FILE__)
|
13
2
|
|
14
3
|
class Book < Structure
|
15
4
|
key :title
|
@@ -21,6 +10,7 @@ class Person < Structure
|
|
21
10
|
key :name
|
22
11
|
one :partner
|
23
12
|
many :friends
|
13
|
+
many :parents, :default => 2.times.map { Person.new }
|
24
14
|
end
|
25
15
|
|
26
16
|
class TestStructure < Test::Unit::TestCase
|
@@ -103,6 +93,11 @@ class TestStructure < Test::Unit::TestCase
|
|
103
93
|
assert_equal 0, friend.friends.count
|
104
94
|
end
|
105
95
|
|
96
|
+
def test_many
|
97
|
+
person = Person.new
|
98
|
+
assert_equal 2, person.parents.size
|
99
|
+
end
|
100
|
+
|
106
101
|
def test_json
|
107
102
|
book = Book.new(:title => 'Foo')
|
108
103
|
json = book.to_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.
|
4
|
+
version: 0.15.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-
|
12
|
+
date: 2011-08-12 00:00:00.000000000Z
|
13
13
|
dependencies: []
|
14
14
|
description: Structure is a Struct-like key/value container.
|
15
15
|
email:
|
@@ -26,8 +26,14 @@ files:
|
|
26
26
|
- README.md
|
27
27
|
- Rakefile
|
28
28
|
- lib/structure.rb
|
29
|
+
- lib/structure/static.rb
|
29
30
|
- lib/structure/version.rb
|
30
31
|
- structure.gemspec
|
32
|
+
- test/fixtures/cities.yml
|
33
|
+
- test/fixtures/cities_with_neighborhoods.yml
|
34
|
+
- test/fixtures/cities_without_ids.yml
|
35
|
+
- test/helper.rb
|
36
|
+
- test/static_test.rb
|
31
37
|
- test/structure_test.rb
|
32
38
|
homepage: http://code.papercavalier.com/structure
|
33
39
|
licenses: []
|
@@ -43,7 +49,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
49
|
version: '0'
|
44
50
|
segments:
|
45
51
|
- 0
|
46
|
-
hash: -
|
52
|
+
hash: -1085682960934054294
|
47
53
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
54
|
none: false
|
49
55
|
requirements:
|
@@ -57,4 +63,9 @@ signing_key:
|
|
57
63
|
specification_version: 3
|
58
64
|
summary: A Struct-like key/value container
|
59
65
|
test_files:
|
66
|
+
- test/fixtures/cities.yml
|
67
|
+
- test/fixtures/cities_with_neighborhoods.yml
|
68
|
+
- test/fixtures/cities_without_ids.yml
|
69
|
+
- test/helper.rb
|
70
|
+
- test/static_test.rb
|
60
71
|
- test/structure_test.rb
|