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 +5 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/Readme.md +30 -0
- data/active_model_association_adapters.gemspec +22 -0
- data/lib/active_model_association_adapters/version.rb +3 -0
- data/lib/active_model_association_adapters.rb +16 -0
- data/lib/active_record/adapters/mongoid/associations.rb +115 -0
- data/spec/mongoid_activerecord_adapter_spec.rb +263 -0
- data/spec/spec_helper.rb +33 -0
- metadata +75 -0
data/Gemfile
ADDED
data/Rakefile
ADDED
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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|