structure 0.14.1 → 0.15.0

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