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 CHANGED
@@ -1,34 +1,37 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 0.14.1
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.1
12
+ ## 0.13.0
10
13
 
11
14
  * remove static module
12
15
  * rename .key to .attribute
13
16
 
14
- # 0.12.1
17
+ # 0.12.0
15
18
 
16
19
  * add static module
17
20
 
18
- # 0.11.1
21
+ # 0.11.0
19
22
 
20
23
  * .key now emulates DataMapper.property
21
24
 
22
- # 0.10.1
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
@@ -1,6 +1,5 @@
1
1
  source :rubygems
2
2
 
3
3
  gem 'activesupport', '>= 3.0'
4
- gem 'json', :platform => [:mri_18, :jruby]
4
+ gem 'json', :platform => [:mri_18, :jruby, :rbx]
5
5
  gem 'rake'
6
- gem 'ruby-debug19', :platform => :mri_19, :require => 'ruby-debug'
data/README.md CHANGED
@@ -1,12 +1,10 @@
1
1
  # Structure
2
2
 
3
- Structure is a Struct-like key/value container.
3
+ [![travis](https://secure.travis-ci.org/hakanensari/structure.png)](http://travis-ci.org/hakanensari/structure)
4
4
 
5
- [![travis](https://secure.travis-ci.org/papercavalier/structure.png)](http://travis-ci.org/papercavalier/structure)
5
+ Structure is a Struct-like key/value container .
6
6
 
7
- ## Usage
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
@@ -1,3 +1,3 @@
1
1
  class Structure
2
- VERSION = '0.14.1'
2
+ VERSION = '0.15.0'
3
3
  end
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 :name
19
- # key :age, Integer
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
- # Structure supports the following types.
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+, +String+,
54
- # a +Structure+, or a subclass thereof. If none is specified, this defaults
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} is not a#{'n' if type.to_s.match(/^[AI]/)} #{type}"
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
- # Typecast value based on type.
83
- typecast =
84
- if type == Boolean
85
- lambda do |value|
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
- end
97
- elsif [Hash, Structure].include? type
98
- lambda do |value|
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
- end
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 : typecast.call(value)
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 is equal
172
- # to the other object when latter is of the same class and the two objects'
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
@@ -0,0 +1,5 @@
1
+ ---
2
+ - id: 1
3
+ name: New York
4
+ - id: 2
5
+ name: Tokyo
@@ -0,0 +1,7 @@
1
+ ---
2
+ - id: 1
3
+ name: New York
4
+ neighborhoods:
5
+ - name: Manhattan
6
+ - name: Brooklyn
7
+ - name: Queens
@@ -0,0 +1,4 @@
1
+ ---
2
+ - name: New York
3
+ - name: Tokyo
4
+ - name: Paris
data/test/helper.rb ADDED
@@ -0,0 +1,12 @@
1
+ $:.push File.expand_path('../../lib', __FILE__)
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'
@@ -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
@@ -1,15 +1,4 @@
1
- $:.push File.expand_path('../../lib', __FILE__)
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.14.1
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-11 00:00:00.000000000Z
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: -3339689548870644271
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