meek 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +36 -0
- data/Rakefile +44 -0
- data/lib/meek.rb +7 -0
- data/lib/meek/active_concern.rb +13 -0
- data/lib/meek/collection_proxy.rb +58 -0
- data/lib/meek/concern.rb +52 -0
- data/lib/meek/errors.rb +3 -0
- data/lib/meek/proxy_association.rb +51 -0
- data/lib/meek/version.rb +3 -0
- data/spec/meek/active_concern_spec.rb +75 -0
- data/spec/meek/concern_spec.rb +47 -0
- data/spec/models/dummy.rb +49 -0
- data/spec/spec_helper.rb +14 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2c750b14616a96016a9c9a1b3a657f8ef45187fa
|
4
|
+
data.tar.gz: df954f83e9a2da3fa618f16f8cc46e7e3a0a06d3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 34365cb8d616720e157305f4661f44c62333c9b500512a2c3ca1f13c710e94a878c26505ce06cfaa8debc9815f677e3643415d732a5a5351fe6ed199e5eab342
|
7
|
+
data.tar.gz: 3d9f2b0b45325fb39b78b7a2510ccbdd7ea0c6f84aaa101f7cfb4e5ea72872fe51bee2a2228f5259a6d7bc1ac51ace6b40008518bb735e14e6952c14ae622934
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# SilverSpoon
|
2
|
+
In this world there are the has, and the has nots. ActiveRecord can `has_many` other ActiveRecords, but what about the rest of us? SilverSpoon lets ActiveRecord objects `has_many` Non-ActiveRecord objects, or for Non-ActiveRecord objects to `has_many` other Non-ActiveRecord objects.
|
3
|
+
|
4
|
+
For the people!
|
5
|
+
|
6
|
+
## Suppose you have a plain old ActiveModel
|
7
|
+
|
8
|
+
class BankAccount
|
9
|
+
class << self
|
10
|
+
def find(args)
|
11
|
+
HTTP Request...
|
12
|
+
an arbitrary service...
|
13
|
+
or some awful local Modeling logic...
|
14
|
+
...
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
## And another Object that bares an association to it
|
20
|
+
|
21
|
+
class User < ActiveRecord::Base
|
22
|
+
extend SilverSpoon::ActiveConcern
|
23
|
+
|
24
|
+
has_many :bank_accounts
|
25
|
+
end
|
26
|
+
|
27
|
+
## Now make calls a la ActiveRecord Association
|
28
|
+
|
29
|
+
user = User.new
|
30
|
+
|
31
|
+
user.bank_accounts
|
32
|
+
|
33
|
+
The referencing class does not have to be an ActiveRecord object. It simply needs to respond to a `find` method as you would expect. Having an `@id` or `to_params` would be useful as well in most cases. It would also be nice if there was a `save` method, if you're going to be doing something like that.
|
34
|
+
|
35
|
+
The `find` method may receive the id from the referring object to associate the two.
|
36
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
lib_path = File.expand_path('../../lib', __FILE__)
|
2
|
+
#ENV['RACK_ENV'] = ENV['RAILS_ENV'] = ENV['ODDZ_ENV'] ||= "development"
|
3
|
+
#require 'config/boot'
|
4
|
+
#Bundler::GemHelper.install_tasks if defined?(Bundler)
|
5
|
+
#require 'active_record'
|
6
|
+
#
|
7
|
+
require 'rspec/core'
|
8
|
+
require 'rspec/core/rake_task'
|
9
|
+
#require 'rspec/core/rake_task'
|
10
|
+
require 'active_record'
|
11
|
+
#
|
12
|
+
#Dir[File.join(File.dirname(__FILE__), 'lib/tasks/*.rake')].each {|f| load f }
|
13
|
+
#Dir[File.join(File.dirname(__FILE__), 'lib/tasks/**/*.rake')].each {|f| load f }
|
14
|
+
#include ActiveRecord::Tasks
|
15
|
+
#
|
16
|
+
##Database
|
17
|
+
#dbconfig ||= YAML::load(ERB.new(File.read('config/database.yml')).result)
|
18
|
+
#dbenv = dbconfig[ENV["ODDZ_ENV"]]
|
19
|
+
#ENV["DATABASE_URL"] ||= "postgres://#{dbenv["username"]}:#{dbenv["password"]}@#{dbenv["host"]}:#{dbenv["port"]}/#{dbenv["database"]}"
|
20
|
+
#
|
21
|
+
#DatabaseTasks.env = ENV["ODDZ_ENV"]
|
22
|
+
#DatabaseTasks.seed_loader = SeedLoader.new
|
23
|
+
#DatabaseTasks.database_configuration = dbconfig
|
24
|
+
#DatabaseTasks.db_dir = 'db'
|
25
|
+
#DatabaseTasks.migrations_paths = 'db/migrate'
|
26
|
+
#DatabaseTasks.root = File.dirname(__FILE__)
|
27
|
+
#load 'active_record/railties/databases.rake'
|
28
|
+
#
|
29
|
+
desc "create database"
|
30
|
+
task :create_db do
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
#intentionally nothing
|
35
|
+
end
|
36
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
37
|
+
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
|
38
|
+
task :default => :spec
|
39
|
+
|
40
|
+
#ActiveRecord::Base.configurations = dbconfig
|
41
|
+
#ActiveRecord::Base.establish_connection(
|
42
|
+
# :adapter => "sqlite3",
|
43
|
+
# :database => "activeworkaround.db"
|
44
|
+
#)
|
data/lib/meek.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
module Meek
|
2
|
+
|
3
|
+
class CollectionProxy
|
4
|
+
attr_accessor :owner, :model, :proxy_association
|
5
|
+
|
6
|
+
def initialize model, association
|
7
|
+
@proxy_association = Meek::ProxyAssociation.new(model, association)
|
8
|
+
end
|
9
|
+
|
10
|
+
delegate :[], :first, :second, :third, :fourth, :fifth, :forty_two,
|
11
|
+
:find, :select,
|
12
|
+
:last, :take, :count, :size, :length, :empty?, :any?, :many?, :include?,
|
13
|
+
:create, :create!, :build, :new,
|
14
|
+
to: :proxy_association
|
15
|
+
|
16
|
+
#replace, delete_all, destroy_all, delete, destroy, distinct, uniq,
|
17
|
+
|
18
|
+
def concat new
|
19
|
+
proxy_association.concat(new)
|
20
|
+
end
|
21
|
+
|
22
|
+
def <<(*records)
|
23
|
+
records.map {|record| record.save }
|
24
|
+
proxy_association.concat(records) && self
|
25
|
+
end
|
26
|
+
alias_method :push, :<<
|
27
|
+
alias_method :append, :<<
|
28
|
+
|
29
|
+
def prepend(*args)
|
30
|
+
raise NoMethodError, "prepend on association is not defined. Please use << or append"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Equivalent to +delete_all+. The difference is that returns +self+, instead
|
34
|
+
# of an array with the deleted objects, so methods can be chained. See
|
35
|
+
# +delete_all+ for more information.
|
36
|
+
# Note that because +delete_all+ removes records by directly
|
37
|
+
# running an SQL query into the database, the +updated_at+ column of
|
38
|
+
# the object is not changed.
|
39
|
+
def clear
|
40
|
+
delete_all
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
# Reloads the collection from the database. Returns +self+.
|
45
|
+
def reload
|
46
|
+
proxy_association.reload
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Unloads the association. Returns +self+.
|
51
|
+
def reset
|
52
|
+
proxy_association.reset
|
53
|
+
proxy_association.reset_scope
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/lib/meek/concern.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# presume it's an active record compliant object,
|
2
|
+
# abstract
|
3
|
+
module Meek
|
4
|
+
module Concern
|
5
|
+
|
6
|
+
def has_many(name, args={})
|
7
|
+
@@active_workaround_args ||= {}
|
8
|
+
@@active_workaround_args[:has_many] ||= {}
|
9
|
+
@@active_workaround_args[:has_many][name] = args
|
10
|
+
@@active_workaround_args[:has_many][name][:_attribute_name] = name
|
11
|
+
|
12
|
+
define_method(name) do
|
13
|
+
#return instance_variable_get(name) if does_cache && !instance_variable_get(name).nil?
|
14
|
+
|
15
|
+
args= @@active_workaround_args[:has_many][name]
|
16
|
+
msg = {}
|
17
|
+
|
18
|
+
foreign_key = args[:foreign_key] || ActiveSupport::Inflector.foreign_key(self.class.name)
|
19
|
+
msg[foreign_key] = respond_to?(:to_param) ? to_param : @id
|
20
|
+
|
21
|
+
klass_title = ActiveSupport::Inflector.classify( msg[:class_name] || name )
|
22
|
+
klass = args[:class] || ActiveSupport::Inflector.constantize(klass_title)
|
23
|
+
|
24
|
+
response = klass.find(:all, msg)
|
25
|
+
#instance_variable_set(name, response) if does_cache
|
26
|
+
response
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def belongs_to(name, args={})
|
31
|
+
@@active_workaround_args ||= {}
|
32
|
+
@@active_workaround_args[:belongs_to] ||= {}
|
33
|
+
@@active_workaround_args[:belongs_to][name] = args
|
34
|
+
@@active_workaround_args[:belongs_to][name][:_attribute_name] = name
|
35
|
+
|
36
|
+
define_method(name) do
|
37
|
+
|
38
|
+
args= @@active_workaround_args[:belongs_to][name]
|
39
|
+
msg = {}
|
40
|
+
|
41
|
+
foreign_key = args[:foreign_key] || :id
|
42
|
+
msg[foreign_key] = respond_to?(:to_param) ? to_param : @id
|
43
|
+
|
44
|
+
klass_title = ActiveSupport::Inflector.classify(name)
|
45
|
+
klass = args[:class] || ActiveSupport::Inflector.constantize(klass_title)
|
46
|
+
|
47
|
+
klass.find(msg)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
data/lib/meek/errors.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module Meek
|
2
|
+
|
3
|
+
class ProxyAssociation
|
4
|
+
|
5
|
+
attr_accessor :items
|
6
|
+
|
7
|
+
delegate :[], :first, :second, :third, :fourth, :fifth, :forty_two,
|
8
|
+
:find, :select,
|
9
|
+
:last, :take, :count, :size, :length, :empty?, :any?, :many?, :include?,
|
10
|
+
to: :items
|
11
|
+
|
12
|
+
def initialize( model, association )
|
13
|
+
@owner = association.owner
|
14
|
+
@model = model
|
15
|
+
reload
|
16
|
+
end
|
17
|
+
|
18
|
+
def create(args)
|
19
|
+
args[foriegn_key(@owner)] = @owner.to_param
|
20
|
+
@model.create(args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def create!(args)
|
24
|
+
args[foriegn_key(@owner)] = @owner.to_param
|
25
|
+
@model.create!(args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def build(args)
|
29
|
+
args[foriegn_key(@owner)] = @owner.to_param
|
30
|
+
@model.build(args)
|
31
|
+
end
|
32
|
+
alias_method :new, :build
|
33
|
+
|
34
|
+
def concat(new)
|
35
|
+
@items.concat(new)
|
36
|
+
end
|
37
|
+
|
38
|
+
def reload
|
39
|
+
@items = @model.find(:all, foriegn_key(@owner) => @owner.to_param)
|
40
|
+
end
|
41
|
+
|
42
|
+
#def reset
|
43
|
+
#end
|
44
|
+
|
45
|
+
#def reset_scope
|
46
|
+
#end
|
47
|
+
|
48
|
+
def foriegn_key(klass); ActiveSupport::Inflector.foreign_key(klass.class.name); end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
data/lib/meek/version.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe(Meek::ActiveConcern) do
|
4
|
+
|
5
|
+
describe("has_many") do
|
6
|
+
|
7
|
+
it("creates an object that responds to association") {
|
8
|
+
dummy = User.create(name: "bob")
|
9
|
+
expect(FundingInstrument).to receive(:find).with(:all, "user_id"=>dummy.id.to_s)
|
10
|
+
dummy.funding_instruments
|
11
|
+
}
|
12
|
+
|
13
|
+
it("returns as expected") {
|
14
|
+
dummy = User.create(name: "bob")
|
15
|
+
expect(dummy.funding_instruments).to be_a(Meek::CollectionProxy)
|
16
|
+
expect(dummy.funding_instruments[0]).to be(:a)
|
17
|
+
|
18
|
+
expect(dummy.funding_instruments.first).to be(:a)
|
19
|
+
|
20
|
+
[:size, :length, :count].each do |method|
|
21
|
+
expect(dummy.funding_instruments.send(method)).to be(2)
|
22
|
+
end
|
23
|
+
|
24
|
+
}
|
25
|
+
|
26
|
+
describe("new") do
|
27
|
+
|
28
|
+
it("builds") {
|
29
|
+
args = {:a => :b}
|
30
|
+
dummy = User.create(name: "bob")
|
31
|
+
expect(FundingInstrument).to receive(:build).with(args)
|
32
|
+
dummy.funding_instruments.build(args)
|
33
|
+
}
|
34
|
+
|
35
|
+
it("creates") {
|
36
|
+
args = {:a => :b}
|
37
|
+
dummy = User.create(name: "bob")
|
38
|
+
expect(FundingInstrument).to receive(:create).with(args)
|
39
|
+
dummy.funding_instruments.create(args)
|
40
|
+
}
|
41
|
+
|
42
|
+
it("creates!") {
|
43
|
+
args = {:a => :b}
|
44
|
+
dummy = User.create(name: "bob")
|
45
|
+
expect(FundingInstrument).to receive(:create!).with(args)
|
46
|
+
dummy.funding_instruments.create!(args)
|
47
|
+
}
|
48
|
+
|
49
|
+
it("concats") {
|
50
|
+
dummy = User.create(name: "bob")
|
51
|
+
|
52
|
+
child = Array.wrap( FundingInstrument.new() )
|
53
|
+
expect(dummy.funding_instruments.size).to eq(2)
|
54
|
+
dummy.funding_instruments.concat( child )
|
55
|
+
expect(dummy.funding_instruments.size).to eq(3)
|
56
|
+
}
|
57
|
+
|
58
|
+
it("pushes") {
|
59
|
+
dummy = User.create(name: "bob")
|
60
|
+
|
61
|
+
child = FundingInstrument.new()
|
62
|
+
expect(dummy.funding_instruments.size).to eq(2)
|
63
|
+
dummy.funding_instruments << child
|
64
|
+
expect(dummy.funding_instruments.size).to eq(3)
|
65
|
+
dummy.funding_instruments.push child
|
66
|
+
expect(dummy.funding_instruments.size).to eq(4)
|
67
|
+
dummy.funding_instruments.append child
|
68
|
+
expect(dummy.funding_instruments.size).to eq(5)
|
69
|
+
}
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe(Meek::Concern) do
|
4
|
+
|
5
|
+
describe("has_many") do
|
6
|
+
|
7
|
+
describe("finds a foreign_id") do
|
8
|
+
|
9
|
+
#it("prefers to_param") {
|
10
|
+
# dummy = User.new
|
11
|
+
# expect(FundingInstrument).to receive(:find).with(:all, "dummy_id"=>1)
|
12
|
+
# dummy.funding_instruments
|
13
|
+
#}
|
14
|
+
|
15
|
+
#it("defaults to @id") {
|
16
|
+
# dummy = UserWithoutToParam.new
|
17
|
+
|
18
|
+
# #expect(FundingInstrument).to receive(:find).with(:all, "dummy_without_to_param_id"=>1)
|
19
|
+
# #dummy.funding_instruments
|
20
|
+
#}
|
21
|
+
|
22
|
+
it("doesn't croak on no id") { skip("I'm lazy, put an id on it")
|
23
|
+
dummy = HorribleObject.new
|
24
|
+
#expect(FundingInstrument).to receive(:find).with(:all, {})
|
25
|
+
#dummy.funding_instruments
|
26
|
+
}
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe("belongs_to") do
|
33
|
+
|
34
|
+
it("creates an object that responds to association") {
|
35
|
+
dummy = SubservientDummy.new
|
36
|
+
#expect(ParentObject).to receive(:find).with(id:1)
|
37
|
+
dummy.parent_object
|
38
|
+
}
|
39
|
+
|
40
|
+
it("returns as expected") {
|
41
|
+
dummy = SubservientDummy.new
|
42
|
+
#expect(dummy.parent_object).to eq([:a, :b, :c])
|
43
|
+
}
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class FundingInstrument
|
2
|
+
# I'm a fake ActiveModel class!
|
3
|
+
def save
|
4
|
+
@persisted = true
|
5
|
+
end
|
6
|
+
|
7
|
+
# I should minimally respond to the #find method
|
8
|
+
class << self
|
9
|
+
def find(*args)
|
10
|
+
[:a, :b]
|
11
|
+
end
|
12
|
+
|
13
|
+
def relation_delegate_class(_args)
|
14
|
+
Meek::CollectionProxy
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ParentObject
|
20
|
+
extend Meek::ActiveConcern
|
21
|
+
def self.find(args)
|
22
|
+
[:a, :b, :c]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class User < ActiveRecord::Base
|
27
|
+
|
28
|
+
has_many :funding_instruments
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class UserWithoutToParam < User
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
class HorribleObject < User
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
class SubservientDummy
|
41
|
+
extend Meek::Concern
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@id = 1
|
45
|
+
end
|
46
|
+
|
47
|
+
belongs_to :parent_object
|
48
|
+
|
49
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
lib_path = File.expand_path('../../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
|
3
|
+
|
4
|
+
require 'active_record'
|
5
|
+
require 'meek'
|
6
|
+
|
7
|
+
ActiveRecord::Base.establish_connection(
|
8
|
+
:adapter => "sqlite3",
|
9
|
+
:database => "./db/test.sqlite3"
|
10
|
+
)
|
11
|
+
|
12
|
+
# in spec/support/ and its subdirectories.
|
13
|
+
Dir.glob(File.join(File.dirname(__FILE__), './models', '*.rb')).each { |f| require f}
|
14
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'support', '*.rb')).each { |f| require f}
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: meek
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Trevor Grayson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - '>='
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '0'
|
25
|
+
prerelease: false
|
26
|
+
type: :runtime
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.0.0
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 2.0.0
|
39
|
+
prerelease: false
|
40
|
+
type: :development
|
41
|
+
description: This may go from ActiveRecord to Object or Object to ActiveRecord
|
42
|
+
email:
|
43
|
+
- trevor@trevorgrayson.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- README.md
|
49
|
+
- Rakefile
|
50
|
+
- lib/meek.rb
|
51
|
+
- lib/meek/active_concern.rb
|
52
|
+
- lib/meek/collection_proxy.rb
|
53
|
+
- lib/meek/concern.rb
|
54
|
+
- lib/meek/errors.rb
|
55
|
+
- lib/meek/proxy_association.rb
|
56
|
+
- lib/meek/version.rb
|
57
|
+
- spec/meek/active_concern_spec.rb
|
58
|
+
- spec/meek/concern_spec.rb
|
59
|
+
- spec/models/dummy.rb
|
60
|
+
- spec/spec_helper.rb
|
61
|
+
homepage: ''
|
62
|
+
licenses: []
|
63
|
+
metadata: {}
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 2.2.2
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Add has_many methods for non ActiveRecord associations.
|
84
|
+
test_files: []
|
85
|
+
has_rdoc:
|