activemodel_association_adapters 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -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,21 @@
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 = "activemodel_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 = "mongoid_activerecord_adapter"
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
+ 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,3 @@
1
+ module ActiveModelAssociationAdapters
2
+ VERSION = "0.0.1"
3
+ 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: activemodel_association_adapters
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Scott Burton
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-06-13 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
+ - activemodel_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: mongoid_activerecord_adapter
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