activemodel-associations 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 78d28b409b8b4a0658df2eb0729e3737a7cf5fe6
4
+ data.tar.gz: 3b84b0cbff584770cd0e8c247b036111d9a7a300
5
+ SHA512:
6
+ metadata.gz: 56f110e11d873c9916cb51325753d95f250957b225739df87b6812e33c62a01df41e8f1768f0f6fe08a6e74a5ed8ae429a10658f23a759740fda8c299e3bb898
7
+ data.tar.gz: 1e83f7e627843b46949b8d6d1c7f48938c09513ccf47d291580df107d3fc84c8a20248c670ebc5c15484818bf9786eba0b7235456e3a838fd7e014ca1e509b48
data/.gitignore ADDED
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+
24
+ gemfiles/*.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ - 2.0.0
5
+ gemfile:
6
+ - gemfiles/activerecord-40.gemfile
7
+ - gemfiles/activerecord-41.gemfile
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in activemodel-association.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 joker1007
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # Activemodel::Association
2
+ [![Build Status](https://travis-ci.org/joker1007/activemodel-associations.svg?branch=master)](https://travis-ci.org/joker1007/activemodel-associations)
3
+ [![Coverage Status](https://coveralls.io/repos/joker1007/activemodel-associations/badge.png)](https://coveralls.io/r/joker1007/activemodel-associations)
4
+
5
+ `has_many` and `belongs_to` macro for Plain Ruby Object.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'activemodel-association'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install activemodel-association
20
+
21
+ ## Usage
22
+
23
+ ### belongs\_to
24
+
25
+ ```ruby
26
+ class User < ActiveRecord::Base; end
27
+
28
+ class Comment
29
+ include ActiveModel::Model # need ActiveModel::Model
30
+ include ActiveModel::Associations # include this
31
+
32
+ attr_accessor :body, :user_id # belongs_to association need foreign_key attribute
33
+
34
+ belongs_to :user
35
+
36
+ # need hash like accessor, used internal Rails
37
+ def [](attr)
38
+ self.send(attr)
39
+ end
40
+
41
+ # need hash like accessor, used internal Rails
42
+ def []=(attr, value)
43
+ self.send("#{attr}=", value)
44
+ end
45
+ end
46
+
47
+ user = User.create(name: "joker1007")
48
+ comment = Comment.new(user_id: user.id)
49
+ comment.user # => <User {name: "joker1007"}>
50
+ ```
51
+
52
+ ### Polymorphic belongs\_to
53
+
54
+ ```ruby
55
+ class User < ActiveRecord::Base; end
56
+
57
+ class Comment
58
+ include ActiveModel::Model # need ActiveModel::Model
59
+ include ActiveModel::Associations # include this
60
+
61
+ attr_accessor :body, :commenter_id, :commenter_type
62
+
63
+ belongs_to :commenter, polymorphic: true
64
+
65
+ # need hash like accessor, used internal Rails
66
+ def [](attr)
67
+ self.send(attr)
68
+ end
69
+
70
+ # need hash like accessor, used internal Rails
71
+ def []=(attr, value)
72
+ self.send("#{attr}=", value)
73
+ end
74
+ end
75
+
76
+ user = User.create(name: "joker1007")
77
+ comment = Comment.new(commenter_id: user.id, commenter_type: "User")
78
+ comment.commenter # => <User {name: "joker1007"}>
79
+ ```
80
+
81
+ ### has\_many
82
+
83
+ ```ruby
84
+ class User < ActiveRecord::Base; end
85
+
86
+ class Group
87
+ include ActiveModel::Model
88
+ include ActiveModel::Associations
89
+
90
+ attr_accessor :name
91
+ attr_reader :user_ids
92
+
93
+ has_many :users
94
+
95
+ def [](attr)
96
+ self.send(attr)
97
+ end
98
+
99
+ def []=(attr, value)
100
+ self.send("#{attr}=", value)
101
+ end
102
+ end
103
+
104
+ user = User.create(name: "joker1007")
105
+ group = Group.new(user_ids: [user.id])
106
+ group.users # => ActiveRecord::Relation (SELECT * from users WHERE id IN (#{user.id}))
107
+ group.users.find_by(name: "none") # => []
108
+ ```
109
+
110
+ ## Limitation
111
+ Support associations is only `belongs_to` and simple `has_many`
112
+ `has_many` options is limited. following options is unsupported.
113
+
114
+ `:through :dependent :source :source_type :counter_cache :as`
115
+
116
+ ## Contributing
117
+
118
+ 1. Fork it ( https://github.com/[my-github-username]/activemodel-association/fork )
119
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
120
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
121
+ 4. Push to the branch (`git push origin my-new-feature`)
122
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ namespace :spec do
9
+ %w(activerecord-40 activerecord-41).each do |gemfile|
10
+ desc "Run Tests by #{gemfile}.gemfile"
11
+ task gemfile do
12
+ sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle install --path .bundle"
13
+ sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle exec rake -t spec"
14
+ end
15
+ end
16
+
17
+ desc "Run All Tests"
18
+ task :all do
19
+ %w(activerecord-40 activerecord-41).each do |gemfile|
20
+ Rake::Task["spec:#{gemfile}"].invoke
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_model/associations/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "activemodel-associations"
8
+ spec.version = ActiveModel::Associations::VERSION
9
+ spec.authors = ["joker1007"]
10
+ spec.email = ["kakyoin.hierophant@gmail.com"]
11
+ spec.summary = %q{ActiveRecord Association Helper for PORO}
12
+ spec.description = %q{ActiveRecord Association Helper for PORO}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "activerecord", "< 5"
22
+ spec.add_runtime_dependency "activemodel", "< 5"
23
+ spec.add_runtime_dependency "activesupport", "< 5"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.6"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "tapp"
29
+ spec.add_development_dependency "sqlite3"
30
+ spec.add_development_dependency "database_cleaner"
31
+ spec.add_development_dependency "coveralls"
32
+ end
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "activerecord", "~> 4.0.0"
4
+ gem "activemodel", "~> 4.0.0"
5
+ gem "activesupport", "~> 4.0.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "activerecord", "~> 4.1.0"
4
+ gem "activemodel", "~> 4.1.0"
5
+ gem "activesupport", "~> 4.1.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,51 @@
1
+ require 'active_model/associations/initialize_extension'
2
+ require 'active_model/associations/active_record_reflection'
3
+ require 'active_model/associations/autosave_association'
4
+ require 'active_model/associations/override_methods'
5
+ require 'active_record/associations/builder/has_many_for_active_model'
6
+ require 'active_record/associations/has_many_for_active_model_association'
7
+
8
+ module ActiveModel
9
+ module Associations
10
+ extend ActiveSupport::Concern
11
+
12
+ include InitializeExtension
13
+ include AutosaveAssociation
14
+ include ActiveRecordReflection
15
+ include OverrideMethods
16
+
17
+ module ClassMethods
18
+ # define association like ActiveRecord
19
+ def belongs_to(name, scope = nil, options = {})
20
+ reflection = ActiveRecord::Associations::Builder::BelongsTo.build(self, name, scope, options)
21
+ if ActiveRecord.version.to_s >= "4.1"
22
+ ActiveRecord::Reflection.add_reflection self, name, reflection
23
+ end
24
+ end
25
+
26
+ # define association like ActiveRecord
27
+ def has_many(name, scope = nil, options = {}, &extension)
28
+ options.reverse_merge!(active_model: true, target_ids: "#{name.to_s.singularize}_ids")
29
+ if scope.is_a?(Hash)
30
+ options.merge!(scope)
31
+ scope = nil
32
+ end
33
+
34
+ reflection = ActiveRecord::Associations::Builder::HasManyForActiveModel.build(self, name, scope, options, &extension)
35
+ if ActiveRecord.version.to_s >= "4.1"
36
+ ActiveRecord::Reflection.add_reflection self, name, reflection
37
+ end
38
+
39
+ mixin = generated_association_methods
40
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
41
+ def #{options[:target_ids]}=(other_ids)
42
+ @#{options[:target_ids]} = other_ids
43
+ association(:#{name}).reset
44
+ association(:#{name}).reset_scope
45
+ @#{options[:target_ids]}
46
+ end
47
+ CODE
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ module ActiveModel::Associations
2
+ module ActiveRecordReflection
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :reflections
7
+ self.reflections = {}
8
+ end
9
+
10
+ module ClassMethods
11
+ if ActiveRecord.version.to_s < "4.1"
12
+ def create_reflection(macro, name, scope, options, active_record)
13
+ case macro
14
+ when :has_many, :belongs_to
15
+ klass = ActiveRecord::Reflection::AssociationReflection
16
+ reflection = klass.new(macro, name, scope, options, active_record)
17
+ end
18
+
19
+ self.reflections = self.reflections.merge(name => reflection)
20
+ reflection
21
+ end
22
+ end
23
+
24
+ def reflect_on_association(association)
25
+ reflections[association]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveModel::Associations
2
+ module AssociationScopeExtension
3
+ if ActiveRecord.version.to_s < "4.1"
4
+ def add_constraints(scope)
5
+ if reflection.options[:active_model]
6
+ target_ids = reflection.options[:target_ids]
7
+ return scope.where(id: owner[target_ids])
8
+ end
9
+
10
+ super
11
+ end
12
+ else
13
+ def add_constraints(scope, owner, assoc_klass, refl, tracker)
14
+ if refl.options[:active_model]
15
+ target_ids = refl.options[:target_ids]
16
+ return scope.where(id: owner[target_ids])
17
+ end
18
+
19
+ super
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ module ActiveModel::Associations
2
+ module AutosaveAssociation
3
+ extend ActiveSupport::Concern
4
+
5
+ include ActiveRecord::AutosaveAssociation
6
+
7
+ included do
8
+ extend ActiveModel::Callbacks
9
+ define_model_callbacks :save, :create, :update
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveModel::Associations
2
+ module Hooks
3
+ def self.init
4
+ ActiveSupport.on_load(:active_record) do
5
+ require 'active_model/associations/association_scope_extension'
6
+ ActiveRecord::Associations::AssociationScope.send(:prepend, ActiveModel::Associations::AssociationScopeExtension)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module ActiveModel::Associations
2
+ module InitializeExtension
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ prepend WithAssociationCache
7
+ end
8
+
9
+ module WithAssociationCache
10
+ def initialize(*args)
11
+ @association_cache = {}
12
+ super
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,76 @@
1
+ module ActiveModel::Associations
2
+ module OverrideMethods
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ # borrow method definition from ActiveRecord::Inheritance
7
+ # use in Rails internal
8
+ mod = Module.new do
9
+ unbound = ActiveRecord::Inheritance::ClassMethods.instance_method(:compute_type)
10
+ define_method(:compute_type, unbound)
11
+ protected :compute_type
12
+ end
13
+ extend mod
14
+ end
15
+
16
+ module ClassMethods
17
+ def generated_association_methods
18
+ @generated_association_methods ||= begin
19
+ mod = const_set(:GeneratedAssociationMethods, Module.new)
20
+ include mod
21
+ mod
22
+ end
23
+ end
24
+ alias :generated_feature_methods :generated_association_methods \
25
+ if ActiveRecord.version.to_s < "4.1"
26
+
27
+ # override
28
+ def dangerous_attribute_method?(name)
29
+ false
30
+ end
31
+
32
+ # dummy table name
33
+ def pluralize_table_names
34
+ self.to_s.pluralize
35
+ end
36
+ end
37
+
38
+ # use by association accessor
39
+ def association(name) #:nodoc:
40
+ association = association_instance_get(name)
41
+
42
+ if association.nil?
43
+ reflection = self.class.reflect_on_association(name)
44
+ if reflection.options[:active_model]
45
+ association = ActiveRecord::Associations::HasManyForActiveModelAssociation.new(self, reflection)
46
+ else
47
+ association = reflection.association_class.new(self, reflection)
48
+ end
49
+ association_instance_set(name, association)
50
+ end
51
+
52
+ association
53
+ end
54
+
55
+ private
56
+
57
+ # override
58
+ def validate_collection_association(reflection)
59
+ if association = association_instance_get(reflection.name)
60
+ if records = associated_records_to_validate_or_save(association, false, reflection.options[:autosave])
61
+ records.each { |record| association_valid?(reflection, record) }
62
+ end
63
+ end
64
+ end
65
+
66
+ # use in Rails internal
67
+ def association_instance_get(name)
68
+ @association_cache[name]
69
+ end
70
+
71
+ # use in Rails internal
72
+ def association_instance_set(name, association)
73
+ @association_cache[name] = association
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,7 @@
1
+ module ActiveModel::Associations
2
+ class Railtie < ::Rails::Railtie #:nodoc:
3
+ initializer 'activemodel-associations' do |_|
4
+ ActiveModel::Associations::Hooks.init
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveModel
2
+ module Associations
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module ActiveRecord::Associations::Builder
2
+ class HasManyForActiveModel < HasMany
3
+ def valid_options
4
+ super + [:active_model, :target_ids] - [:through, :dependent, :source, :source_type, :counter_cache, :as]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveRecord::Associations
2
+ class HasManyForActiveModelAssociation < HasManyAssociation
3
+ # remove conditions: owner.new_record?, foreign_key_present?
4
+ def find_target?
5
+ !loaded? && klass
6
+ end
7
+
8
+ # no dependent action
9
+ def null_scope?
10
+ false
11
+ end
12
+
13
+ # full replace simplely
14
+ def replace(other_array)
15
+ other_array.each { |val| raise_on_type_mismatch!(val) }
16
+ target_ids = reflection.options[:target_ids]
17
+ owner[target_ids] = other_array.map(&:id)
18
+ end
19
+
20
+ # no need load_target, and transaction
21
+ def concat(*records)
22
+ flatten_records = records.flatten
23
+ flatten_records.each { |val| raise_on_type_mismatch!(val) }
24
+ target_ids = reflection.options[:target_ids]
25
+ owner[target_ids] ||= []
26
+ owner[target_ids].concat(flatten_records.map(&:id))
27
+ reset
28
+ reset_scope
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ require "active_model"
2
+ require "active_record"
3
+ require "active_support"
4
+ require "active_model/associations"
5
+ require "active_model/associations/hooks"
6
+
7
+ # Load Railtie
8
+ begin
9
+ require "rails"
10
+ rescue LoadError
11
+ end
12
+
13
+ if defined?(Rails)
14
+ require "active_model/associations/railtie"
15
+ end
@@ -0,0 +1,9 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users do |t|
4
+ t.string :name
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,223 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveModel::Associations do
4
+ context "When included Comment class" do
5
+ class Comment
6
+ include ActiveModel::Model
7
+ include ActiveModel::Associations
8
+
9
+ attr_accessor :body, :user_id
10
+
11
+ belongs_to :user
12
+
13
+ def [](attr)
14
+ self.send(attr)
15
+ end
16
+
17
+ def []=(attr, value)
18
+ self.send("#{attr}=", value)
19
+ end
20
+ end
21
+
22
+ it "extends constructor" do
23
+ comment = Comment.new(body: "foo")
24
+ expect(comment.body).to eq "foo"
25
+ expect(comment.instance_variable_get("@association_cache")).to eq({})
26
+ end
27
+
28
+ describe "belongs_to" do
29
+ it "Add belongs_to macro" do
30
+ expect(Comment).to be_respond_to(:belongs_to)
31
+ end
32
+
33
+ describe ".belongs_to" do
34
+ let(:comment) { Comment.new }
35
+
36
+ it "defines association accessor" do
37
+ expect(comment).to be_respond_to(:user)
38
+ expect(comment).to be_respond_to(:user=)
39
+ end
40
+
41
+ describe "defined accessor" do
42
+ let(:user) { User.create(name: "joker1007") }
43
+ let(:comment) { Comment.new(user_id: user.id) }
44
+
45
+ it "defined accessor loads target ActiveRecord instance" do
46
+ expect(comment.user).to eq user
47
+ end
48
+
49
+ it "receives target ActiveRecord instance, and set foreign_key attributes" do
50
+ other_user = User.create(name: "kakyoin")
51
+ expect { comment.user = other_user }.to change { [comment.user, comment.user_id] }
52
+ .from([user, user.id]).to([other_user, other_user.id])
53
+ end
54
+
55
+ it "can validate" do
56
+ expect(comment.valid?).to be_true
57
+ end
58
+ end
59
+
60
+ describe "defined builder" do
61
+ it "sets foreign_key" do
62
+ comment.create_user(name: "joker1007")
63
+ expect(comment.user).to be_a(User)
64
+ expect(comment.user).to be_persisted
65
+ expect(comment.user_id).not_to be_nil
66
+ end
67
+ end
68
+
69
+ context "When set foreign_key manually" do
70
+ let!(:user) { User.create(name: "joker1007") }
71
+ let(:comment) { Comment.new }
72
+
73
+ it "can access target ActiveRecord instance" do
74
+ expect { comment.user_id = user.id }.to change { comment.user }
75
+ .from(nil).to(user)
76
+ end
77
+ end
78
+
79
+ it "can define polymorphic association" do
80
+ class PolymorhicBelongsToComment < Comment
81
+ attr_accessor :commenter_id, :commenter_type
82
+ belongs_to :commenter, polymorphic: true
83
+ end
84
+
85
+ user = User.create(name: "joker1007")
86
+ comment = PolymorhicBelongsToComment.new(commenter_id: user.id, commenter_type: "User")
87
+ expect(comment.commenter).to eq user
88
+ end
89
+
90
+ it "can define different class_name association" do
91
+ class DiffClassNameBelongsToComment < Comment
92
+ attr_accessor :commenter_id
93
+ belongs_to :commenter, class_name: "User"
94
+ end
95
+
96
+ user = User.create(name: "joker1007")
97
+ comment = DiffClassNameBelongsToComment.new(commenter: user)
98
+ expect(comment.commenter_id).to eq user.id
99
+ end
100
+ end
101
+ end
102
+
103
+ describe "has_many" do
104
+ class Group
105
+ include ActiveModel::Model
106
+ include ActiveModel::Associations
107
+
108
+ attr_accessor :name
109
+ attr_reader :user_ids
110
+
111
+ has_many :users
112
+
113
+ def [](attr)
114
+ self.send(attr)
115
+ end
116
+
117
+ def []=(attr, value)
118
+ self.send("#{attr}=", value)
119
+ end
120
+ end
121
+
122
+ let(:group) { Group.new }
123
+
124
+ it "Add has_many macro" do
125
+ expect(Group).to be_respond_to(:has_many)
126
+ end
127
+
128
+ describe ".has_many" do
129
+ it "defines association accessor" do
130
+ expect(group).to be_respond_to(:users)
131
+ expect(group).to be_respond_to(:users=)
132
+ end
133
+
134
+ describe "defined accessor" do
135
+ let(:user1) { User.create(name: "joker1007") }
136
+ let(:user2) { User.create(name: "kakyoin") }
137
+ let(:group) { Group.new(user_ids: [user1.id, user2.id]) }
138
+
139
+ it "returns ActiveRecord CollectionProxy of target class" do
140
+ expect(group.users).to eq [user1, user2]
141
+ expect(group.users.find_by(id: user1.id)).to eq user1
142
+ end
143
+
144
+ it "receives target ActiveRecord instance Array, and set target_ids attributes" do
145
+ group = Group.new
146
+ expect(group.users).to be_empty
147
+ group.users = [user1, user2]
148
+ expect(group.users).to eq [user1, user2]
149
+ end
150
+
151
+ it "can replace having association" do
152
+ user3 = User.create(name: "jotaro")
153
+ group = Group.new
154
+ expect(group.users).to be_empty
155
+ group.users = [user1, user2]
156
+ group.users = [user3]
157
+ expect(group.users).to eq [user3]
158
+ end
159
+
160
+ it "can concat records" do
161
+ expect(group.users).to eq [user1, user2]
162
+ user3 = User.create(name: "jotaro")
163
+ group.users << user3
164
+ expect(group.users).to eq [user1, user2, user3]
165
+ end
166
+
167
+ it "can validate" do
168
+ expect(group.valid?).to be_true
169
+ end
170
+ end
171
+
172
+ context "When set target_ids manually" do
173
+ let!(:user) { User.create(name: "joker1007") }
174
+ let(:group) { Group.new }
175
+
176
+ it "can access target ActiveRecord instance" do
177
+ expect(group.users).to be_empty
178
+ group.user_ids = [user.id]
179
+ expect(group.users).to eq [user]
180
+ end
181
+ end
182
+
183
+ context "When ids attribute is nil" do
184
+ let!(:user) { User.create(name: "joker1007") }
185
+ let(:group) { Group.new }
186
+
187
+ it "can concat" do
188
+ expect(group.users).to be_empty
189
+ group.users << user
190
+ expect(group.users).to eq [user]
191
+ end
192
+ end
193
+
194
+ it "can define different class_name association" do
195
+ class DiffClassNameHasManyGroup < Group
196
+ attr_reader :member_ids
197
+ has_many :members, class_name: "User"
198
+ end
199
+
200
+ user = User.create(name: "joker1007")
201
+ group = DiffClassNameHasManyGroup.new(members: [user])
202
+ expect(group.member_ids).to eq [user.id]
203
+ expect(group.members).to eq [user]
204
+ end
205
+
206
+ %i(through dependent source source_type counter_cache as).each do |option_name|
207
+ it "#{option_name} option is unsupported" do
208
+ expect {
209
+ eval <<-CODE
210
+ class #{option_name.to_s.classify}Group
211
+ include ActiveModel::Model
212
+ include ActiveModel::Associations
213
+
214
+ has_many :users, #{option_name}: true
215
+ end
216
+ CODE
217
+ }.to raise_error(ArgumentError)
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,36 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'coveralls'
3
+ Coveralls.wear!
4
+
5
+ require 'activemodel/associations'
6
+ ActiveModel::Associations::Hooks.init
7
+
8
+ ActiveRecord::Base.establish_connection(
9
+ adapter: "sqlite3",
10
+ database: ":memory:"
11
+ )
12
+
13
+ # Test Class
14
+ class User < ActiveRecord::Base; end
15
+
16
+ ActiveRecord::Migration.verbose = false
17
+ ActiveRecord::Migrator.migrate File.expand_path("../db/migrate", __FILE__), nil
18
+
19
+ require 'database_cleaner'
20
+
21
+ RSpec.configure do |config|
22
+ config.order = :random
23
+
24
+ config.before(:suite) do
25
+ DatabaseCleaner.strategy = :transaction
26
+ DatabaseCleaner.clean_with(:truncation)
27
+ end
28
+
29
+ config.before(:each) do
30
+ DatabaseCleaner.start
31
+ end
32
+
33
+ config.after(:each) do
34
+ DatabaseCleaner.clean
35
+ end
36
+ end
metadata ADDED
@@ -0,0 +1,212 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activemodel-associations
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - joker1007
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "<"
18
+ - !ruby/object:Gem::Version
19
+ version: '5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "<"
25
+ - !ruby/object:Gem::Version
26
+ version: '5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activemodel
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "<"
39
+ - !ruby/object:Gem::Version
40
+ version: '5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "<"
46
+ - !ruby/object:Gem::Version
47
+ version: '5'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "<"
53
+ - !ruby/object:Gem::Version
54
+ version: '5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: tapp
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: database_cleaner
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: coveralls
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: ActiveRecord Association Helper for PORO
154
+ email:
155
+ - kakyoin.hierophant@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".gitignore"
161
+ - ".rspec"
162
+ - ".travis.yml"
163
+ - Gemfile
164
+ - LICENSE.txt
165
+ - README.md
166
+ - Rakefile
167
+ - activemodel-associations.gemspec
168
+ - gemfiles/activerecord-40.gemfile
169
+ - gemfiles/activerecord-41.gemfile
170
+ - lib/active_model/associations.rb
171
+ - lib/active_model/associations/active_record_reflection.rb
172
+ - lib/active_model/associations/association_scope_extension.rb
173
+ - lib/active_model/associations/autosave_association.rb
174
+ - lib/active_model/associations/hooks.rb
175
+ - lib/active_model/associations/initialize_extension.rb
176
+ - lib/active_model/associations/override_methods.rb
177
+ - lib/active_model/associations/railtie.rb
178
+ - lib/active_model/associations/version.rb
179
+ - lib/active_record/associations/builder/has_many_for_active_model.rb
180
+ - lib/active_record/associations/has_many_for_active_model_association.rb
181
+ - lib/activemodel/associations.rb
182
+ - spec/db/migrate/10_create_users.rb
183
+ - spec/lib/active_model/association_spec.rb
184
+ - spec/spec_helper.rb
185
+ homepage: ''
186
+ licenses:
187
+ - MIT
188
+ metadata: {}
189
+ post_install_message:
190
+ rdoc_options: []
191
+ require_paths:
192
+ - lib
193
+ required_ruby_version: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - ">="
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ required_rubygems_version: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
203
+ requirements: []
204
+ rubyforge_project:
205
+ rubygems_version: 2.2.2
206
+ signing_key:
207
+ specification_version: 4
208
+ summary: ActiveRecord Association Helper for PORO
209
+ test_files:
210
+ - spec/db/migrate/10_create_users.rb
211
+ - spec/lib/active_model/association_spec.rb
212
+ - spec/spec_helper.rb