meek 0.0.6
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.
- 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:
|