active_model_association_adapters 0.0.2

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/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ db/*
5
+ log/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mongoid_activerecord_adapter.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/Readme.md ADDED
@@ -0,0 +1,30 @@
1
+ ActiveModel Association Adapters
2
+ ================================
3
+ Simple association adapters for polyglot applications.
4
+
5
+ Summary
6
+ -------
7
+ Simple association adapters for ActiveModel-compliant models used in polyglot applications.
8
+ Use Mongoid and ActiveRecord together with simple macros.
9
+
10
+ Examples
11
+ --------
12
+
13
+ class Monkey < ActiveRecord::Base
14
+ include ActiveRecord::Adapters::Mongoid::Associations
15
+ has_one_document :banana
16
+ end
17
+
18
+ class Banana
19
+ include Mongoid::Document
20
+ field :monkey_id, :type => Integer
21
+ end
22
+
23
+ monkey.banana = Banana.create
24
+ #=> #<Banana id: 1, monkey_id: 2, created_at: "2011-06-13 21:24:39", updated_at: "2011-06-13 21:24:42">
25
+
26
+ Caveats
27
+ -------
28
+ No magic, no fancy stuff. Not built for performance. Terrible craftsmanship. Currently only supports ActiveRecord and Mongoid, and only a few macros. Will likely cause segfaults, server crashes, public unrest, and giant lizard attacks.
29
+
30
+ See RDoc for details (if there ever is one), and use at your own risk, if at all.
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "active_model_association_adapters/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "active_model_association_adapters"
7
+ s.version = ActiveModelAssociationAdapters::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Scott Burton"]
10
+ s.email = ["scott.burton@chaione.com"]
11
+ s.homepage = "http://www.chaione.com"
12
+ s.summary = %q{Simple association adapters for polyglot applications}
13
+ s.description = %q{Simple association adapters for ActiveModel-compliant models used in polyglot applications.}
14
+
15
+ s.rubyforge_project = "active_model_association_adapters"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveModelAssociationAdapters
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,16 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/dependencies/autoload'
3
+
4
+ module ActiveRecord
5
+ module Adapters
6
+ module Mongoid
7
+ extend ActiveSupport::Autoload
8
+ autoload :Associations
9
+ end
10
+ end
11
+ end
12
+
13
+ module ActiveModelAssociationAdapters
14
+ extend ActiveSupport::Autoload
15
+ autoload :Verion
16
+ end
@@ -0,0 +1,115 @@
1
+ module ActiveRecord
2
+ module Adapters
3
+ module Mongoid
4
+ module Associations
5
+ extend ActiveSupport::Concern
6
+
7
+ def save(*args)
8
+ super(*args)
9
+ self.class.send(:__has_one_documents).each do |document|
10
+ save_document(document)
11
+ end
12
+ self.class.send(:__has_many_documents).each do |document|
13
+ save_documents(document)
14
+ end
15
+ self
16
+ end
17
+
18
+ private
19
+
20
+ module InstanceMethods
21
+ def save_document(document)
22
+ send(document).save if send(document) && (send(document).changed? || send(document).new_record?)
23
+ instance_variable_get("@old_#{document}".to_sym).save if instance_variable_get("@old_#{document}".to_sym)
24
+ end
25
+
26
+ def save_documents(documents)
27
+ send(documents).each do |document|
28
+ document.save if document && (document.changed? || document.new_record?)
29
+ end
30
+ if instance_variable_get("@old_#{documents}".to_sym)
31
+ instance_variable_get("@old_#{documents}".to_sym).each do |old_document|
32
+ old_document.save
33
+ end
34
+ end
35
+ end
36
+
37
+ def method_missing(meth, *args, &block)
38
+ case meth
39
+ when /build_(.+)$/
40
+ self.send("#{$1}=".to_sym, ($1.classify.constantize).new(*args, &block)) rescue super(meth, *args, &block)
41
+ when /create_(.+)$/
42
+ self.send("#{$1}=".to_sym, ($1.classify.constantize).create(*args, &block)) rescue super(meth, *args, &block)
43
+ else
44
+ super(meth, *args, &block)
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ module ClassMethods
51
+
52
+ def has_one_document(name)
53
+ __has_one_documents.delete(name)
54
+ __has_one_documents.push(name)
55
+ add_has_one_accessors_for(name)
56
+ end
57
+
58
+ def has_many_documents(name)
59
+ __has_many_documents.delete(name)
60
+ __has_many_documents.push(name)
61
+ add_has_many_accessors_for(name)
62
+ end
63
+
64
+ private
65
+
66
+ def __has_one_documents
67
+ @__has_one_documents ||= Array.new
68
+ end
69
+
70
+ def __has_many_documents
71
+ @__has_many_documents ||= Array.new
72
+ end
73
+
74
+ def add_has_many_accessors_for(name)
75
+ class_eval %Q{
76
+ def #{name}
77
+ @#{name} ||= #{name.to_s.classify}.where( (self.class.to_s.underscore + "_id").to_sym => id)
78
+ end
79
+
80
+ def #{name}=(others)
81
+ if #{name}
82
+ @old_#{name} = #{name}
83
+ @old_#{name}.each do |doc|
84
+ doc.send((self.class.to_s.underscore + "_id=").to_sym, nil)
85
+ end
86
+ end
87
+ others.each do |doc|
88
+ doc.send((self.class.to_s.underscore + "_id=").to_sym, id)
89
+ end
90
+ @#{name} = others
91
+ end
92
+ }
93
+ end
94
+
95
+ def add_has_one_accessors_for(name)
96
+ class_eval %Q{
97
+ def #{name}
98
+ @#{name} ||= #{name.to_s.classify}.first(:conditions => {(self.class.to_s.underscore + "_id").to_sym => id})
99
+ end
100
+
101
+ def #{name}=(other)
102
+ if #{name}
103
+ @old_#{name} = #{name}
104
+ @old_#{name}.send((self.class.to_s.underscore + "_id=").to_sym, nil)
105
+ end
106
+ other.send((self.class.to_s.underscore + "_id=").to_sym, id)
107
+ @#{name} = other
108
+ end
109
+ }
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,263 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveModelAssociationAdapters do
4
+
5
+ describe "ActiveRecord to Mongoid adapters" do
6
+ describe "has_one_document" do
7
+ class Monkey < ActiveRecord::Base
8
+ include ActiveRecord::Adapters::Mongoid::Associations
9
+ has_one_document :banana
10
+ end
11
+
12
+ class Banana
13
+ include Mongoid::Document
14
+ field :monkey_id, :type => Integer
15
+ end
16
+
17
+ subject {Monkey.new}
18
+
19
+ it "should have :banana in its @__has_one_documents" do
20
+ subject.class.instance_variable_get(:@__has_one_documents).should eql([:banana])
21
+ end
22
+
23
+ it "should respond_to :banana" do
24
+ subject.should respond_to(:banana)
25
+ end
26
+
27
+ it "should respond_to :banana=" do
28
+ subject.should respond_to(:banana=)
29
+ end
30
+
31
+ its(:banana) {should be_nil}
32
+
33
+ describe "adding a banana" do
34
+ let(:banana_1) {Banana.new}
35
+ let(:banana_2) {Banana.new}
36
+
37
+ context "when unsaved" do
38
+ context "without an existing banana" do
39
+ it "should get a banana" do
40
+ subject.banana = banana_1
41
+ subject.banana.should eql banana_1
42
+ end
43
+ end
44
+
45
+ context "with an existing banana" do
46
+ before(:each) do
47
+ subject.banana = banana_1
48
+ end
49
+
50
+ it "swaps out the banana" do
51
+ subject.banana = banana_2
52
+ subject.banana.should eql banana_2
53
+ end
54
+ end
55
+ end
56
+
57
+ context "when saved" do
58
+ subject {Monkey.create}
59
+ context "without an existing banana" do
60
+ it "should get a banana" do
61
+ subject.banana = banana_1
62
+ subject.banana.should eql banana_1
63
+ end
64
+ end
65
+
66
+ context "with an existing banana" do
67
+ before(:each) do
68
+ subject.banana = banana_1
69
+ end
70
+ it "swaps out the banana" do
71
+ subject.banana = banana_2
72
+ subject.banana.should eql banana_2
73
+ end
74
+
75
+ it "modifies the new banana's monkey_id" do
76
+ subject.banana.monkey_id.should eql(subject.id)
77
+ end
78
+ end
79
+
80
+ describe "saving" do
81
+ context "when the banana is a new record" do
82
+ it "saves the banana" do
83
+ subject.banana = banana_1
84
+ subject.save
85
+ Banana.find(banana_1.id).monkey_id.should eql(subject.id)
86
+ end
87
+ end
88
+
89
+ context "when the banana is already saved" do
90
+ before(:each) do
91
+ banana_1.save
92
+ end
93
+
94
+ it "saves the banana's new foreign key" do
95
+ subject.banana = banana_1
96
+ subject.save
97
+ Banana.find(banana_1.id).monkey_id.should eql(subject.id)
98
+ end
99
+ end
100
+
101
+ context "when switching bananas" do
102
+ context "when the first banana is saved and the second is a new record" do
103
+ before(:each) do
104
+ banana_1.save
105
+ subject.banana = banana_1
106
+ subject.save
107
+ subject.banana = banana_2
108
+ subject.save
109
+ end
110
+
111
+ it "unsets the monkey_id for the first banana" do
112
+ Banana.find(banana_1.id).monkey_id.should be_nil
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "builder methods" do
121
+ its(:build_banana) {should be_an_instance_of(Banana)}
122
+ describe "create_banana" do
123
+ it "should be an instance of Banana" do
124
+ banana = subject.create_banana
125
+ banana.should be_an_instance_of(Banana)
126
+ banana.should_not be_new_record
127
+ end
128
+ end
129
+ end
130
+
131
+ end
132
+
133
+ describe "has_many_documents" do
134
+ class Monkey < ActiveRecord::Base
135
+ include ActiveRecord::Adapters::Mongoid::Associations
136
+ has_many_documents :bananas
137
+ end
138
+
139
+ class Banana
140
+ include Mongoid::Document
141
+ field :monkey_id, :type => Integer
142
+ end
143
+
144
+ subject {Monkey.new}
145
+
146
+ it "should have :bananas in its @__has_many_documents" do
147
+ subject.class.instance_variable_get(:@__has_many_documents).should eql([:bananas])
148
+ end
149
+
150
+ it "should respond_to :bananas" do
151
+ subject.should respond_to(:bananas)
152
+ end
153
+
154
+ it "should respond_to :banana=" do
155
+ subject.should respond_to(:bananas=)
156
+ end
157
+
158
+ its(:bananas) {should be_an_instance_of(Mongoid::Criteria)}
159
+
160
+ describe "adding a banana" do
161
+ let(:banana_1) {Banana.new}
162
+ let(:banana_2) {Banana.new}
163
+ let(:banana_3) {Banana.new}
164
+
165
+ context "when unsaved" do
166
+ context "without existing bananas" do
167
+ it "should get a banana array" do
168
+ subject.bananas = [banana_1]
169
+ subject.bananas.should eql [banana_1]
170
+ end
171
+ end
172
+
173
+ context "with existing bananas" do
174
+ before(:each) do
175
+ subject.bananas = [banana_1]
176
+ end
177
+
178
+ it "swaps out the bananas arrays" do
179
+ subject.bananas = [banana_2]
180
+ subject.bananas.should eql [banana_2]
181
+ end
182
+ end
183
+ end
184
+
185
+ context "when saved" do
186
+ subject {Monkey.create}
187
+ context "without existing bananas" do
188
+ it "should get a banana array" do
189
+ subject.bananas = [banana_1]
190
+ subject.bananas.should eql [banana_1]
191
+ end
192
+ end
193
+
194
+ context "with existing bananas" do
195
+ before(:each) do
196
+ subject.bananas = [banana_1]
197
+ end
198
+ it "swaps out the bananas arrays" do
199
+ subject.bananas = [banana_2]
200
+ subject.bananas.should eql [banana_2]
201
+ end
202
+
203
+ it "modifies the new bananas' monkey_ids" do
204
+ subject.bananas.each do |banana|
205
+ banana.monkey_id.should eql(subject.id)
206
+ end
207
+ end
208
+ end
209
+
210
+ describe "saving" do
211
+ context "when one of the bananas is a new record" do
212
+ it "saves the banana" do
213
+ subject.bananas = [banana_1]
214
+ subject.save
215
+ Banana.find(banana_1.id).monkey_id.should eql(subject.id)
216
+ end
217
+ end
218
+
219
+ context "when one of the bananas is already saved" do
220
+ before(:each) do
221
+ banana_1.save
222
+ end
223
+
224
+ it "saves the banana's new foreign key" do
225
+ subject.bananas = [banana_1]
226
+ subject.save
227
+ Banana.find(banana_1.id).monkey_id.should eql(subject.id)
228
+ end
229
+ end
230
+
231
+ context "when switching bananas" do
232
+ context "when the first banana is saved and the second is a new record" do
233
+ before(:each) do
234
+ [banana_1, banana_2].each {|banana| banana.save}
235
+ subject.bananas = [banana_1, banana_2]
236
+ subject.save
237
+ subject.bananas = [banana_2, banana_3]
238
+ subject.save
239
+ end
240
+
241
+ it "unsets the monkey_id for banana_1" do
242
+ Banana.find(banana_1.id).monkey_id.should be_nil
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ describe "builder methods" do
251
+ its(:build_banana) {should be_an_instance_of(Banana)}
252
+ describe "create_banana" do
253
+ it "should be an instance of Banana" do
254
+ banana = subject.create_banana
255
+ banana.should be_an_instance_of(Banana)
256
+ banana.should_not be_new_record
257
+ end
258
+ end
259
+ end
260
+ end
261
+ end
262
+
263
+ end
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ # require 'bson_ext'
3
+ require 'mongoid'
4
+ require 'database_cleaner'
5
+ require 'active_record'
6
+ require 'rspec'
7
+
8
+ require File.expand_path(File.join(__FILE__, "..", "..", "lib", "active_model_association_adapters"))
9
+
10
+ ActiveRecord::Base.establish_connection(
11
+ :database => "db/active_model_association_adapters_test",
12
+ :adapter => "sqlite3"
13
+ )
14
+
15
+ ar_tables = ["monkeys"]
16
+
17
+ ar_tables.each do |table_name|
18
+ ActiveRecord::Base.connection.create_table table_name, :force => true do |t|
19
+ t.string :name
20
+ end
21
+ end
22
+
23
+ Mongoid.database = Mongo::Connection.new.db("active_model_association_adapters_test")
24
+
25
+ include DatabaseCleaner
26
+
27
+ RSpec.configure do |config|
28
+ config.mock_with :rspec
29
+
30
+ config.before(:all) do
31
+ Mongoid.master.collections.reject { |c| c.name == 'system.indexes' }.each(&:drop)
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_model_association_adapters
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 2
9
+ version: 0.0.2
10
+ platform: ruby
11
+ authors:
12
+ - Scott Burton
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-06-14 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Simple association adapters for ActiveModel-compliant models used in polyglot applications.
22
+ email:
23
+ - scott.burton@chaione.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - Rakefile
34
+ - Readme.md
35
+ - active_model_association_adapters.gemspec
36
+ - lib/active_model_association_adapters.rb
37
+ - lib/active_model_association_adapters/version.rb
38
+ - lib/active_record/adapters/mongoid/associations.rb
39
+ - spec/mongoid_activerecord_adapter_spec.rb
40
+ - spec/spec_helper.rb
41
+ has_rdoc: true
42
+ homepage: http://www.chaione.com
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options: []
47
+
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ requirements: []
67
+
68
+ rubyforge_project: active_model_association_adapters
69
+ rubygems_version: 1.3.7
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Simple association adapters for polyglot applications
73
+ test_files:
74
+ - spec/mongoid_activerecord_adapter_spec.rb
75
+ - spec/spec_helper.rb