ripple-anaf 0.8.0.beta1

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+ .bundle
21
+
22
+ ## PROJECT::SPECIFIC
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org/"
2
+
3
+ gem 'bundler', '1.0.0'
4
+ gem 'jeweler'
5
+ gem 'ripple', '0.8.0'
6
+ gem 'activemodel', '3.0.0'
7
+ gem 'curb', '>0.6'
8
+ gem 'rspec', '~>2.0.0.beta.19'
9
+ gem 'rake', '0.8.7'
10
+ gem 'ruby-debug19', '0.11.6'
data/Gemfile.lock ADDED
@@ -0,0 +1,64 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.0.0)
5
+ activesupport (= 3.0.0)
6
+ builder (~> 2.1.2)
7
+ i18n (~> 0.4.1)
8
+ activesupport (3.0.0)
9
+ archive-tar-minitar (0.5.2)
10
+ builder (2.1.2)
11
+ columnize (0.3.1)
12
+ curb (0.7.8)
13
+ diff-lcs (1.1.2)
14
+ gemcutter (0.6.1)
15
+ git (1.2.5)
16
+ i18n (0.4.1)
17
+ jeweler (1.4.0)
18
+ gemcutter (>= 0.1.0)
19
+ git (>= 1.2.5)
20
+ rubyforge (>= 2.0.0)
21
+ json_pure (1.4.6)
22
+ linecache19 (0.5.11)
23
+ ruby_core_source (>= 0.1.4)
24
+ rake (0.8.7)
25
+ riak-client (0.8.0)
26
+ activesupport (>= 2.3.5)
27
+ i18n (~> 0.4.0)
28
+ ripple (0.8.0)
29
+ activemodel (= 3.0.0)
30
+ activesupport (= 3.0.0)
31
+ riak-client (= 0.8.0)
32
+ rspec (2.0.0.beta.20)
33
+ rspec-core (= 2.0.0.beta.20)
34
+ rspec-expectations (= 2.0.0.beta.20)
35
+ rspec-mocks (= 2.0.0.beta.20)
36
+ rspec-core (2.0.0.beta.20)
37
+ rspec-expectations (2.0.0.beta.20)
38
+ diff-lcs (>= 1.1.2)
39
+ rspec-mocks (2.0.0.beta.20)
40
+ ruby-debug-base19 (0.11.24)
41
+ columnize (>= 0.3.1)
42
+ linecache19 (>= 0.5.11)
43
+ ruby_core_source (>= 0.1.4)
44
+ ruby-debug19 (0.11.6)
45
+ columnize (>= 0.3.1)
46
+ linecache19 (>= 0.5.11)
47
+ ruby-debug-base19 (>= 0.11.19)
48
+ ruby_core_source (0.1.4)
49
+ archive-tar-minitar (>= 0.5.2)
50
+ rubyforge (2.0.4)
51
+ json_pure (>= 1.1.7)
52
+
53
+ PLATFORMS
54
+ ruby
55
+
56
+ DEPENDENCIES
57
+ activemodel (= 3.0.0)
58
+ bundler (= 1.0.0)
59
+ curb (> 0.6)
60
+ jeweler
61
+ rake (= 0.8.7)
62
+ ripple (= 0.8.0)
63
+ rspec (~> 2.0.0.beta.19)
64
+ ruby-debug19 (= 0.11.6)
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Brian Kaney
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,61 @@
1
+ = ripple-anaf
2
+
3
+ This is inspired and similar implementation to the
4
+ `accepts_nested_attributes` functionality found in AR. This allows
5
+ the use update attributes and create new child records through the
6
+ parent. It also allows the use of the `fields_for` form view helper,
7
+ using a presenter pattern.
8
+
9
+ To enable in the model, call the class method, using the same
10
+ relationship as defined in the `one` or `many`.
11
+
12
+ class Shipment
13
+ include Ripple::Document
14
+ property :identifier, String
15
+
16
+ one :box
17
+ many :addresses
18
+ many :checkpoints
19
+
20
+ accepts_nested_attributes_for :box, :addresses
21
+ accepts_nested_attributes_for :checkpoints, :allow_destroy => true, :reject_if => proc{|attrs| attrs['signed_off'].blank? }
22
+ end
23
+
24
+ Now, you can do things like this:
25
+
26
+ Shipment.create(
27
+ :identifier => '1Z0001',
28
+ :box_attributes => { :weight => 1.4 },
29
+ :addresses_attributes => [ { :street => "Main Street" } ]
30
+ )
31
+
32
+ And in your views you can use fields_for:
33
+
34
+ <%= form_for(@shipment) do |f| %>
35
+
36
+ <%= f.text_field :identifier %>
37
+
38
+ <%= f.fields_for :box do |ff| %>
39
+ <%= ff.text_field :weight %>
40
+ <% end %>
41
+
42
+ <% end %>
43
+
44
+ == Supported Features
45
+
46
+ * Same DSL for linked and embedded documents (except embedded many)
47
+ * :reject_if
48
+ * :allow_destroy
49
+ * :update_only (soon)
50
+
51
+ == Note on Patches/Pull Requests
52
+
53
+ * Fork the project.
54
+ * Make your feature addition or bug fix.
55
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
56
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
57
+ * Send me a pull request. Bonus points for topic branches.
58
+
59
+ == Copyright
60
+
61
+ Copyright (c) 2010 Brian Kaney. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "ripple-anaf"
8
+ gem.summary = %Q{Accepts-nested-attributes-for support for Ripple}
9
+ gem.description = %Q{Adds the DSL for ANAF for Ripple, an ORM for Riak}
10
+ gem.email = "brian@vermonster.com"
11
+ gem.homepage = "http://github.com/bkaney/ripple-anaf"
12
+ gem.authors = ["Brian Kaney"]
13
+ gem.add_development_dependency "rspec", "~>2.0.0.beta.19"
14
+ gem.add_development_dependency "bundler", "~>1.0.0"
15
+ gem.add_dependency "ripple", "0.8.0"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+
24
+ require 'rspec/core'
25
+ require 'rspec/core/rake_task'
26
+
27
+ desc "Run Specs"
28
+ Rspec::Core::RakeTask.new(:spec) do |spec|
29
+ spec.pattern = "spec/lib/**/*_spec.rb"
30
+ end
31
+
32
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.8.0.beta1
@@ -0,0 +1,32 @@
1
+ class Hash
2
+
3
+ # Return a new hash with all keys converted to strings.
4
+ def stringify_keys
5
+ inject({}) do |options, (key, value)|
6
+ options[key.to_s] = value
7
+ options
8
+ end
9
+ end
10
+
11
+ # Destructively convert all keys to strings.
12
+ def stringify_keys!
13
+ keys.each do |key|
14
+ self[key.to_s] = delete(key)
15
+ end
16
+ self
17
+ end
18
+
19
+ # Return a new hash with all keys converted to symbols.
20
+ def symbolize_keys
21
+ inject({}) do |options, (key, value)|
22
+ options[(key.to_sym rescue key) || key] = value
23
+ options
24
+ end
25
+ end
26
+
27
+ # Destructively convert all keys to symbols.
28
+ def symbolize_keys!
29
+ self.replace(self.symbolize_keys)
30
+ end
31
+
32
+ end
@@ -0,0 +1 @@
1
+ require 'ripple-anaf/core_ext/hash'
@@ -0,0 +1,44 @@
1
+ require 'ripple'
2
+
3
+ module Ripple
4
+ module Document
5
+ module Key
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ # Defines the key to be derived from a property.
10
+ # @param [String,Symbol] prop the property to derive the key from
11
+ def key_on(prop)
12
+ class_eval <<-CODE
13
+ def key
14
+ #{prop}.to_s
15
+ end
16
+ def key=(value)
17
+ self.#{prop} = value
18
+ end
19
+ def key_attr
20
+ :#{prop}
21
+ end
22
+ CODE
23
+ end
24
+ end
25
+
26
+ module InstanceMethods
27
+ # Reads the key for this Document.
28
+ def key
29
+ @key
30
+ end
31
+
32
+ # Sets the key for this Document.
33
+ def key=(value)
34
+ @key = value.to_s
35
+ end
36
+
37
+ def key_attr
38
+ :key
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,232 @@
1
+ require 'ripple'
2
+ module Ripple
3
+ module NestedAttributes #:nodoc:
4
+ extend ActiveSupport::Concern
5
+
6
+ UNASSIGNABLE_KEYS = %w{ _destroy }
7
+ TRUE_VALUES = [ true, "true", 1, "1", "yes", "ok", "y" ]
8
+
9
+ included do
10
+ class_inheritable_accessor :nested_attributes_options, :instance_writer => false
11
+ self.nested_attributes_options = {}
12
+ end
13
+
14
+ # = Nested Attributes
15
+ #
16
+ # This is similar to the `accepts_nested_attributes` functionality
17
+ # as found in AR. This allows the use update attributes and create
18
+ # new child records through the parent. It also allows the use of
19
+ # the `fields_for` form view helper, using a presenter pattern.
20
+ #
21
+ # To enable in the model, call the class method, using the same
22
+ # relationship as defined in the `one` or `many`.
23
+ #
24
+ # class Shipment
25
+ # include Ripple::Document
26
+ # one :box
27
+ # many :addresses
28
+ # accepts_nested_attributes_for :box, :addresses
29
+ # end
30
+ #
31
+ # == One
32
+ #
33
+ # Given this model:
34
+ #
35
+ # class Shipment
36
+ # include Ripple::Document
37
+ # one :box
38
+ # accepts_nested_attributes_for :box
39
+ # end
40
+ #
41
+ # This allows creating a box child during creation:
42
+ #
43
+ # shipment = Shipment.create(:box_attributes => { :shape => 'square' })
44
+ # shipment.box.shape # => 'square'
45
+ #
46
+ # This also allows updating box attributes:
47
+ #
48
+ # shipment.update_attributes(:box_attributes => { :key => 'xxx', :shape => 'triangle' })
49
+ # shipment.box.shape # => 'triangle'
50
+ #
51
+ # == Many
52
+ #
53
+ # Given this model
54
+ #
55
+ # class Manifest
56
+ # include Ripple::Document
57
+ # many :shipments
58
+ # accepts_nested_attributes_for :shipments
59
+ # end
60
+ #
61
+ # This allows creating several shipments during manifest creation:
62
+ #
63
+ # manifest = Manifest.create(:shipments_attributes => [ { :reference => "foo1" }, { :reference => "foo2" } ])
64
+ # manifest.shipments.size # => 2
65
+ # manifest.shipments.first.reference # => foo1
66
+ # manifest.shipments.second.reference # => foo2
67
+ #
68
+ # And updating shipment attributes:
69
+ #
70
+ # manifest.update_attributes(:shipment_attributes => [ { :key => 'xxx', :reference => 'updated foo1' },
71
+ # { :key => 'yyy', :reference => 'updated foo2' } ])
72
+ # manifest.shipments.first.reference # => updated foo1
73
+ # manifest.shipments.second.reference # => updated foo2
74
+ #
75
+ module ClassMethods
76
+
77
+ def accepts_nested_attributes_for(*attr_names)
78
+ options = { :allow_destroy => false }
79
+ options.update(attr_names.extract_options!)
80
+
81
+ attr_names.each do |association_name|
82
+ if association = self.associations[association_name]
83
+ nested_attributes_options[association_name.to_sym] = options
84
+
85
+ class_eval %{
86
+ def #{association_name}_attributes=(attributes)
87
+ assign_nested_attributes_for_#{association.type}_association(:#{association_name}, attributes)
88
+ end
89
+
90
+ before_save :autosave_nested_attributes_for_#{association_name}
91
+ before_save :destroy_marked_for_destruction
92
+
93
+ private
94
+
95
+ def autosave_nested_attributes_for_#{association_name}
96
+ save_nested_attributes_for_#{association.type}_association(:#{association_name}) if self.autosave[:#{association_name}]
97
+ end
98
+ }, __FILE__, __LINE__
99
+ else
100
+ raise ArgumentError, "Association #{association_name} not found!"
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ module InstanceMethods
107
+
108
+ protected
109
+
110
+ def autosave
111
+ @autosave_nested_attributes_for ||= {}
112
+ end
113
+
114
+ def marked_for_destruction
115
+ @marked_for_destruction ||= {}
116
+ end
117
+
118
+ private
119
+
120
+ def save_nested_attributes_for_one_association(association_name)
121
+ send(association_name).save
122
+ end
123
+
124
+ def save_nested_attributes_for_many_association(association_name)
125
+ send(association_name).map(&:save)
126
+ end
127
+
128
+ def destroy_marked_for_destruction
129
+ self.marked_for_destruction.each_pair do |association_name, resources|
130
+ resources.map(&:destroy)
131
+ send(association_name).reload
132
+ end
133
+ end
134
+
135
+ def destroy_nested_many_association(association_name)
136
+ send(association_name).map(&:destroy)
137
+ end
138
+
139
+ def assign_nested_attributes_for_one_association(association_name, attributes)
140
+ association = self.class.associations[association_name]
141
+ if association.embeddable?
142
+ assign_nested_attributes_for_one_embedded_association(association_name, attributes)
143
+ else
144
+ self.autosave[association_name] = true
145
+ assign_nested_attributes_for_one_linked_association(association_name, attributes)
146
+ end
147
+ end
148
+
149
+ def assign_nested_attributes_for_one_embedded_association(association_name, attributes)
150
+ send(association_name).build(attributes.except(*UNASSIGNABLE_KEYS))
151
+ end
152
+
153
+ def assign_nested_attributes_for_one_linked_association(association_name, attributes)
154
+ attributes = attributes.stringify_keys
155
+ options = nested_attributes_options[association_name]
156
+
157
+ if attributes[key_attr.to_s].blank? && !reject_new_record?(association_name, attributes)
158
+ send(association_name).build(attributes.except(*UNASSIGNABLE_KEYS))
159
+ else
160
+ if ((existing_record = send(association_name)).key.to_s == attributes[key_attr.to_s].to_s)
161
+ assign_to_or_mark_for_destruction(existing_record, attributes, association_name, options[:allow_destroy])
162
+ else
163
+ raise ArgumentError, "Attempting to update a child that isn't already associated to the parent."
164
+ end
165
+ end
166
+ end
167
+
168
+ def assign_nested_attributes_for_many_association(association_name, attributes_collection)
169
+ unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
170
+ raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
171
+ end
172
+
173
+ if attributes_collection.is_a? Hash
174
+ attributes_collection = attributes_collection.sort_by { |index, _| index.to_i }.map { |_, attributes| attributes }
175
+ end
176
+
177
+ association = self.class.associations[association_name]
178
+ if association.embeddable?
179
+ assign_nested_attributes_for_many_embedded_association(association_name, attributes_collection)
180
+ else
181
+ self.autosave[association_name] = true
182
+ assign_nested_attributes_for_many_linked_association(association_name, attributes_collection)
183
+ end
184
+ end
185
+
186
+ def assign_nested_attributes_for_many_embedded_association(association_name, attributes_collection)
187
+ raise NotImplementedError, "Unclear how to identify which embedded document to update without a key"
188
+ end
189
+
190
+ def assign_nested_attributes_for_many_linked_association(association_name, attributes_collection)
191
+ options = nested_attributes_options[association_name]
192
+ attributes_collection.each do |attributes|
193
+ attributes = attributes.stringify_keys
194
+
195
+ if attributes[key_attr.to_s].blank? && !reject_new_record?(association_name, attributes)
196
+ send(association_name).build(attributes.except(*UNASSIGNABLE_KEYS))
197
+ elsif existing_record = send(association_name).detect { |record| record.key.to_s == attributes[key_attr.to_s].to_s }
198
+ assign_to_or_mark_for_destruction(existing_record, attributes, association_name, options[:allow_destroy])
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ def assign_to_or_mark_for_destruction(record, attributes, association_name, allow_destroy)
205
+ if has_destroy_flag?(attributes) && allow_destroy
206
+ (self.marked_for_destruction[association_name] ||= []) << record
207
+ else
208
+ record.attributes = attributes.except(*UNASSIGNABLE_KEYS)
209
+ end
210
+ end
211
+
212
+ def has_destroy_flag?(hash)
213
+ TRUE_VALUES.include?(hash.stringify_keys['_destroy'])
214
+ end
215
+
216
+ def reject_new_record?(association_name, attributes)
217
+ has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
218
+ end
219
+
220
+ def call_reject_if(association_name, attributes)
221
+ attributes = attributes.stringify_keys
222
+ case callback = nested_attributes_options[association_name][:reject_if]
223
+ when Symbol
224
+ method(callback).arity == 0 ? send(callback) : send(callback, attributes)
225
+ when Proc
226
+ callback.call(attributes)
227
+ end
228
+ end
229
+
230
+ end
231
+
232
+ end
@@ -0,0 +1,20 @@
1
+ require 'ripple'
2
+ require 'active_support/all'
3
+ require 'active_model'
4
+ require 'ripple-anaf/core_ext'
5
+
6
+ module Ripple
7
+ extend ActiveSupport::Autoload
8
+ autoload :NestedAttributes, 'ripple-anaf/nested_attributes'
9
+
10
+ module Document
11
+ extend ActiveSupport::Concern
12
+ extend ActiveSupport::Autoload
13
+
14
+ autoload :Key, 'ripple-anaf/document/key'
15
+
16
+ include Ripple::NestedAttributes
17
+ include Ripple::Document::Key
18
+
19
+ end
20
+ end
@@ -0,0 +1,79 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ripple-anaf}
8
+ s.version = "0.8.0.beta1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Brian Kaney"]
12
+ s.date = %q{2010-09-05}
13
+ s.description = %q{Adds the DSL for ANAF for Ripple, an ORM for Riak}
14
+ s.email = %q{brian@vermonster.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ ".rspec",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "lib/ripple-anaf.rb",
30
+ "lib/ripple-anaf/core_ext.rb",
31
+ "lib/ripple-anaf/core_ext/hash.rb",
32
+ "lib/ripple-anaf/document/key.rb",
33
+ "lib/ripple-anaf/nested_attributes.rb",
34
+ "ripple-anaf.gemspec",
35
+ "spec/lib/nested_attributes_spec.rb",
36
+ "spec/spec_helper.rb",
37
+ "spec/support/models/car.rb",
38
+ "spec/support/models/driver.rb",
39
+ "spec/support/models/engine.rb",
40
+ "spec/support/models/passenger.rb",
41
+ "spec/support/models/seat.rb",
42
+ "spec/support/models/wheel.rb"
43
+ ]
44
+ s.homepage = %q{http://github.com/bkaney/ripple-anaf}
45
+ s.rdoc_options = ["--charset=UTF-8"]
46
+ s.require_paths = ["lib"]
47
+ s.rubygems_version = %q{1.3.7}
48
+ s.summary = %q{Accepts-nested-attributes-for support for Ripple}
49
+ s.test_files = [
50
+ "spec/lib/nested_attributes_spec.rb",
51
+ "spec/spec_helper.rb",
52
+ "spec/support/models/car.rb",
53
+ "spec/support/models/driver.rb",
54
+ "spec/support/models/engine.rb",
55
+ "spec/support/models/passenger.rb",
56
+ "spec/support/models/seat.rb",
57
+ "spec/support/models/wheel.rb"
58
+ ]
59
+
60
+ if s.respond_to? :specification_version then
61
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
62
+ s.specification_version = 3
63
+
64
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
65
+ s.add_development_dependency(%q<rspec>, ["~> 2.0.0.beta.19"])
66
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
67
+ s.add_runtime_dependency(%q<ripple>, ["= 0.8.0"])
68
+ else
69
+ s.add_dependency(%q<rspec>, ["~> 2.0.0.beta.19"])
70
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
71
+ s.add_dependency(%q<ripple>, ["= 0.8.0"])
72
+ end
73
+ else
74
+ s.add_dependency(%q<rspec>, ["~> 2.0.0.beta.19"])
75
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
76
+ s.add_dependency(%q<ripple>, ["= 0.8.0"])
77
+ end
78
+ end
79
+
@@ -0,0 +1,224 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Ripple::NestedAttributes do
4
+ require 'support/models/car'
5
+ require 'support/models/driver'
6
+ require 'support/models/passenger'
7
+ require 'support/models/engine'
8
+ require 'support/models/seat'
9
+ require 'support/models/wheel'
10
+
11
+ context "one :driver (link)" do
12
+ subject { Car.new }
13
+
14
+ it { should respond_to(:driver_attributes=) }
15
+
16
+ it "should not have a driver" do
17
+ subject.driver.should be_nil
18
+ end
19
+
20
+ describe "creation" do
21
+ subject { Car.new(:make => 'VW', :model => 'Rabbit', :driver_attributes => { :name => 'Speed Racer' }) }
22
+
23
+ it "should have a driver of class Driver" do
24
+ subject.driver.should be_a(Driver)
25
+ end
26
+
27
+ it "should have a driver with name 'Speed Racer'" do
28
+ subject.driver.name.should == 'Speed Racer'
29
+ end
30
+
31
+ it "should save the child when saving the parent" do
32
+ subject.driver.should_receive(:save)
33
+ subject.save
34
+ end
35
+ end
36
+
37
+ describe "update" do
38
+ let(:driver) { Driver.create(:name => 'Slow Racer') }
39
+
40
+ before do
41
+ subject.driver = driver
42
+ subject.save
43
+ end
44
+
45
+ it "should have a driver" do
46
+ subject.driver.should == driver
47
+ end
48
+
49
+ it "should update attributes" do
50
+ subject.driver_attributes = { :name => 'Speed Racer' }
51
+ subject.driver.name.should == 'Speed Racer'
52
+ end
53
+
54
+ it "should not save the child if attributes haven't been updated" do
55
+ subject.driver.should_not_receive(:save)
56
+ subject.save
57
+ end
58
+
59
+ it "should save the child when saving the parent" do
60
+ subject.driver_attributes = { :name => 'Speed Racer' }
61
+ subject.driver.should_receive(:save)
62
+ subject.save
63
+ end
64
+ end
65
+ end
66
+
67
+ context "many :passengers (link)" do
68
+ subject { Car.new }
69
+
70
+ it { should respond_to(:passengers_attributes=) }
71
+
72
+ it "should not have passengers" do
73
+ subject.passengers.should == []
74
+ end
75
+
76
+ describe "creation" do
77
+ subject { Car.new(:make => 'VW',
78
+ :model => 'Rabbit',
79
+ :passengers_attributes => [ { :name => 'Joe' },
80
+ { :name => 'Sue' },
81
+ { :name => 'Pat' } ] ) }
82
+
83
+ it "should have 3 passengers" do
84
+ subject.passengers.size.should == 3
85
+ end
86
+
87
+ it "should have 3 passengers with specified names" do
88
+ subject.passengers.first.name.should == 'Joe'
89
+ subject.passengers.second.name.should == 'Sue'
90
+ subject.passengers.third.name.should == 'Pat'
91
+ end
92
+
93
+ it "should save the children when saving the parent" do
94
+ subject.passengers.each do |passenger|
95
+ passenger.should_receive(:save)
96
+ end
97
+ subject.save
98
+ end
99
+ end
100
+
101
+ describe "update" do
102
+ let(:passenger1) { Passenger.create(:name => 'One') }
103
+ let(:passenger2) { Passenger.create(:name => 'Two') }
104
+ let(:passenger3) { Passenger.create(:name => 'Three') }
105
+
106
+ before do
107
+ subject.passengers << passenger1
108
+ subject.passengers << passenger2
109
+ subject.passengers << passenger3
110
+ subject.save
111
+ end
112
+
113
+ it "should have 3 passengers" do
114
+ subject.passengers.size.should == 3
115
+ end
116
+
117
+ it "should update attributes" do
118
+ subject.passengers_attributes = [ { :key => passenger1.key, :name => 'UPDATED One' },
119
+ { :key => passenger2.key, :name => 'UPDATED Two' },
120
+ { :key => passenger3.key, :name => 'UPDATED Three' } ]
121
+ subject.passengers.first.name.should == 'UPDATED One'
122
+ subject.passengers.second.name.should == 'UPDATED Two'
123
+ subject.passengers.third.name.should == 'UPDATED Three'
124
+ end
125
+
126
+ it "should not save the child if attributes haven't been updated" do
127
+ subject.passengers.each do |passenger|
128
+ passenger.should_not_receive(:save)
129
+ end
130
+ subject.save
131
+ end
132
+
133
+ it "should save the child when saving the parent" do
134
+ subject.passengers_attributes = [ { :key => passenger1.key, :name => 'UPDATED One' },
135
+ { :key => passenger1.key, :name => 'UPDATED Two' },
136
+ { :key => passenger1.key, :name => 'UPDATED Three' } ]
137
+ subject.passengers.each do |passenger|
138
+ passenger.should_receive(:save)
139
+ end
140
+ subject.save
141
+ end
142
+ end
143
+ end
144
+
145
+ context ":one engine (embedded)" do
146
+ subject { Car.new }
147
+
148
+ it { should respond_to(:engine_attributes=) }
149
+
150
+ it "should not have an engine" do
151
+ subject.engine.should be_nil
152
+ end
153
+
154
+ describe "creation" do
155
+ subject { Car.new(:make => 'VW', :model => 'Rabbit', :engine_attributes => { :displacement => '2.5L' }) }
156
+
157
+ it "should have an engine of class Engine" do
158
+ subject.engine.should be_a(Engine)
159
+ end
160
+
161
+ it "should have a engine with displacement '2.5L'" do
162
+ subject.engine.displacement.should == '2.5L'
163
+ end
164
+
165
+ it "should save the child when saving the parent" do
166
+ subject.engine.should_not_receive(:save)
167
+ subject.save
168
+ end
169
+ end
170
+
171
+ describe "update" do
172
+ before do
173
+ subject.engine.build(:displacement => '3.6L')
174
+ subject.save
175
+ end
176
+
177
+ it "should have a specified engine" do
178
+ subject.engine.displacement.should == '3.6L'
179
+ end
180
+
181
+ it "should update attributes" do
182
+ subject.engine_attributes = { :displacement => 'UPDATED 3.6L' }
183
+ subject.engine.displacement.should == 'UPDATED 3.6L'
184
+ end
185
+
186
+ it "should not save the child if attributes haven't been updated" do
187
+ subject.engine.should_not_receive(:save)
188
+ subject.save
189
+ end
190
+
191
+ it "should not save the child when saving the parent" do
192
+ subject.engine_attributes = { :displacement => 'UPDATED 3.6L' }
193
+ subject.engine.should_not_receive(:save)
194
+ subject.save
195
+ end
196
+ end
197
+ end
198
+
199
+ context ":reject_if" do
200
+ it "should not create a wheel" do
201
+ car = Car.new(:wheels_attributes => [ { :diameter => 10 } ])
202
+ car.wheels.should == []
203
+ end
204
+
205
+ it "should create a wheel" do
206
+ car = Car.new(:wheels_attributes => [ { :diameter => 16 } ])
207
+ car.wheels.size.should == 1
208
+ car.wheels.first.diameter.should == 16
209
+ end
210
+ end
211
+
212
+ context ":allow_delete" do
213
+ let(:wheel) { Wheel.create(:diameter => 17) }
214
+ subject { Car.create(:wheels => [ wheel ] ) }
215
+
216
+ it "should allow us to delete the wheel" do
217
+ subject.wheels_attributes = [ { :key => wheel.key, :_destroy => "1" } ]
218
+ subject.save
219
+ subject.wheels.should == []
220
+ end
221
+
222
+ end
223
+
224
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'ripple-anaf'
5
+ require 'rspec/autorun'
6
+ require 'ruby-debug'
7
+
8
+ Dir[File.join(File.dirname(__FILE__), "support", "*.rb")].each {|f| require f }
9
+
10
+ Rspec.configure do |config|
11
+ config.mock_with :rspec
12
+ end
@@ -0,0 +1,15 @@
1
+ class Car
2
+ include Ripple::Document
3
+
4
+ property :make, String
5
+ property :model, String
6
+
7
+ one :driver # linked, key_on :name
8
+ many :passengers # linked, standard :key
9
+ one :engine # embedded
10
+ many :seats # embedded
11
+ many :wheels
12
+
13
+ accepts_nested_attributes_for :driver, :passengers, :engine, :seats
14
+ accepts_nested_attributes_for :wheels, :reject_if => proc{|attrs| attrs['diameter'] < 12 }, :allow_destroy => true
15
+ end
@@ -0,0 +1,5 @@
1
+ class Driver
2
+ include Ripple::Document
3
+ property :name, String
4
+ key_on :name
5
+ end
@@ -0,0 +1,4 @@
1
+ class Engine
2
+ include Ripple::EmbeddedDocument
3
+ property :displacement, String
4
+ end
@@ -0,0 +1,5 @@
1
+ class Passenger
2
+ include Ripple::Document
3
+ property :name, String
4
+
5
+ end
@@ -0,0 +1,4 @@
1
+ class Seat
2
+ include Ripple::EmbeddedDocument
3
+ property :color, String
4
+ end
@@ -0,0 +1,5 @@
1
+ class Wheel
2
+ include Ripple::Document
3
+ property :diameter, Integer
4
+
5
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ripple-anaf
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: true
5
+ segments:
6
+ - 0
7
+ - 8
8
+ - 0
9
+ - beta1
10
+ version: 0.8.0.beta1
11
+ platform: ruby
12
+ authors:
13
+ - Brian Kaney
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-05 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 2
30
+ - 0
31
+ - 0
32
+ - beta
33
+ - 19
34
+ version: 2.0.0.beta.19
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *id001
38
+ - !ruby/object:Gem::Dependency
39
+ name: bundler
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ segments:
46
+ - 1
47
+ - 0
48
+ - 0
49
+ version: 1.0.0
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: ripple
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - "="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ - 8
63
+ - 0
64
+ version: 0.8.0
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: *id003
68
+ description: Adds the DSL for ANAF for Ripple, an ORM for Riak
69
+ email: brian@vermonster.com
70
+ executables: []
71
+
72
+ extensions: []
73
+
74
+ extra_rdoc_files:
75
+ - LICENSE
76
+ - README.rdoc
77
+ files:
78
+ - .document
79
+ - .gitignore
80
+ - .rspec
81
+ - Gemfile
82
+ - Gemfile.lock
83
+ - LICENSE
84
+ - README.rdoc
85
+ - Rakefile
86
+ - VERSION
87
+ - lib/ripple-anaf.rb
88
+ - lib/ripple-anaf/core_ext.rb
89
+ - lib/ripple-anaf/core_ext/hash.rb
90
+ - lib/ripple-anaf/document/key.rb
91
+ - lib/ripple-anaf/nested_attributes.rb
92
+ - ripple-anaf.gemspec
93
+ - spec/lib/nested_attributes_spec.rb
94
+ - spec/spec_helper.rb
95
+ - spec/support/models/car.rb
96
+ - spec/support/models/driver.rb
97
+ - spec/support/models/engine.rb
98
+ - spec/support/models/passenger.rb
99
+ - spec/support/models/seat.rb
100
+ - spec/support/models/wheel.rb
101
+ has_rdoc: true
102
+ homepage: http://github.com/bkaney/ripple-anaf
103
+ licenses: []
104
+
105
+ post_install_message:
106
+ rdoc_options:
107
+ - --charset=UTF-8
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 2319011372961580373
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">"
123
+ - !ruby/object:Gem::Version
124
+ segments:
125
+ - 1
126
+ - 3
127
+ - 1
128
+ version: 1.3.1
129
+ requirements: []
130
+
131
+ rubyforge_project:
132
+ rubygems_version: 1.3.7
133
+ signing_key:
134
+ specification_version: 3
135
+ summary: Accepts-nested-attributes-for support for Ripple
136
+ test_files:
137
+ - spec/lib/nested_attributes_spec.rb
138
+ - spec/spec_helper.rb
139
+ - spec/support/models/car.rb
140
+ - spec/support/models/driver.rb
141
+ - spec/support/models/engine.rb
142
+ - spec/support/models/passenger.rb
143
+ - spec/support/models/seat.rb
144
+ - spec/support/models/wheel.rb