acts_has_many 0.1.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/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 igor04
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,135 @@
1
+ # acts_has_many
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ gem 'acts_has_many'
8
+
9
+ And then execute:
10
+
11
+ $ bundle
12
+
13
+ Or install it yourself as:
14
+
15
+ $ gem install acts_has_many
16
+
17
+ ## Usage
18
+ ```ruby
19
+ class User < ActiveRecord::Base
20
+ belongs_to :company, dependent: :destroy
21
+ end
22
+
23
+ class Company < ActiveRecord::Base
24
+ has_many :users
25
+
26
+ acts_has_many # after necessary relation
27
+ end
28
+
29
+ # OR
30
+
31
+ class Company < ActiveRecord::Base
32
+ acts_has_many relations: [:users] # array necessary relations
33
+
34
+ has_many :users
35
+ end
36
+
37
+ # acts_has_many options:
38
+ # :relations( array; default: has many relation which are written above) - necessary relations
39
+ # :compare( string or symbol; default: :title) - name column with unique elements in table
40
+ # :through( boolean; default: false) - if you use has_many :through
41
+
42
+
43
+ company = Company.create(:title => 'Microsoft')
44
+
45
+ company.actuale? # => false
46
+
47
+ user = User.create do |user|
48
+ user.company = company
49
+ # ...
50
+ end
51
+
52
+ company.actuale? # => true
53
+ company.actuale? relations: :users # => false ( exclude 1 record of current relation)
54
+
55
+ company.depend_relations # => ["users"]
56
+ company.model # => Company
57
+ company.compare # => :title
58
+
59
+ company.id # => 1
60
+ update_id, delete_id = company.has_many_update(data: { title: 'Google'}, relation: :users)
61
+ update_id # => 1
62
+ delete_id # => nil
63
+ company.title # => 'Google'
64
+
65
+ # ... update user with new company and other code
66
+
67
+ user2 = User.create do |user|
68
+ user.company = company
69
+ # ...
70
+ end
71
+
72
+ company.actuale? # => true
73
+ company.actuale? relations: :users # => true
74
+
75
+ # Suggestion: if you want to destroy user
76
+ user2.destroy # user will be destroyed, company will rollback (because company is used by other user)
77
+
78
+ company.id # => 1
79
+ update_id, delete_id = company.has_many_update(data: { title: 'Apple'}, relation: :users)
80
+ update_id # => 2
81
+ delete_id # => nil
82
+
83
+ user2.company = Company.find(update_id)
84
+ user2.save
85
+
86
+ # Suggestion: if you want to destroy user now
87
+ user2.destroy # user and company will be destroyed (because company is used only by user2)
88
+
89
+ Company.all # [#<Company id: 1, title: "Google">, #<Company id: 2, title: "Apple"]
90
+
91
+ company.destroy # => false
92
+
93
+ companu.id # => 1
94
+ update_id, delete_id = company.has_many_update(data: { title: 'Apple'}, relation: :users)
95
+ update_id # => 2
96
+ delete_id # => 1
97
+
98
+ # ... update user with company
99
+
100
+ company.destroy # => true
101
+
102
+ # this situation with delete_id best way is "company.delete" because you miss recheck actuality
103
+ ```
104
+
105
+ ### has_many_update_through used with has_many :through and get array parameters
106
+ has_many_update_through is helpful method for has_many_update and use it, you can use has_many_update
107
+ here too, and to do without has_many_update_through
108
+
109
+ For example:
110
+
111
+ ```ruby
112
+ class UserCompany < ActiveRecord::Base
113
+ belongs_to :user
114
+ belongs_to :company
115
+
116
+ class Company < ActiveRecord::Base
117
+ has_many :users, :through => :user_company
118
+ acts_has_many :through => true
119
+
120
+ has_many :user_company
121
+ end
122
+
123
+ class User < ActiveRecord::Base
124
+ has_many :user_company, :dependent => :destroy
125
+ has_many :companies, :through => :user_company
126
+ end
127
+
128
+ new_rows, delete_ids = Company.has_many_update_through( update: data, new: date, relation: :users)
129
+
130
+ user.companies = new_rows # update user companies
131
+
132
+ delete_ids.each do |id|
133
+ Company.finde(id).destroy # necessary for correct delete record ( will be fixed)
134
+ end
135
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env rake
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+
4
+ require 'acts_has_many/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+
8
+ gem.name = 'acts_has_many'
9
+ gem.version = ActsHasMany::VERSION
10
+ gem.summary = 'All records must be used, otherwise they will be deleted. Clear logic with has_many'
11
+ gem.description = 'This gem gives functional for update elements has_many relation'
12
+ gem.author = 'Igor IS04'
13
+ gem.email = 'igor.s04g@gmail.com'
14
+ gem.homepage = 'https://github.com/igor04/acts_has_many'
15
+
16
+ gem.files = `git ls-files`.split("\n")
17
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ gem.require_paths = ['lib']
20
+
21
+ gem.add_development_dependency 'bundler'
22
+ gem.add_development_dependency 'rspec'
23
+ gem.add_development_dependency 'sqlite3'
24
+ gem.add_development_dependency 'activerecord'
25
+ end
26
+
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ $:.unshift "#{File.dirname(__FILE__)}/lib"
2
+ require 'acts_has_many'
@@ -0,0 +1,190 @@
1
+ module ActiveRecord
2
+ module Acts #:nodoc:
3
+ module HasMany
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ #
9
+ # class methods +acts_has_many+ added:
10
+ #
11
+ # class method:
12
+ # +has_many_through_update+
13
+ #
14
+ # instance methods:
15
+ # +depend_relations+
16
+ # +model+
17
+ # +compare+
18
+ # +has_many_update+
19
+ # +actuale?+
20
+ #
21
+ # set +before_destroy+ callback;
22
+ #
23
+
24
+ module ClassMethods
25
+
26
+ #
27
+ # +acts_has_many+ - available method in all model and switch on
28
+ # extension functional in concrete model (need located this method
29
+ # after all relation or after relation which include in dependence)
30
+ # options
31
+ # :compare( symbol, string)- name column for compare with other element in table
32
+ # :relations( array) - concrete name of depended relation
33
+ # :through( boolean) - off or on has_many_through_update method
34
+ #
35
+
36
+ def acts_has_many(options = {})
37
+ depend_relations = []
38
+ options_default = { compare: :title, through: false }
39
+
40
+ options = options_default.merge options
41
+
42
+ options[:relations] = self.reflections
43
+ .select{ |k, v| v.macro == :has_many }
44
+ .map{|k, v| k} if options[:relations].nil?
45
+
46
+ options[:relations].each do |relation|
47
+ depend_relations << relation.to_s.tableize
48
+ end
49
+
50
+ #
51
+ # +has_many_through_update+ (return array) [ 1 - array objects records, 2 - array delete ids ]
52
+ # options
53
+ # :update( array) - data for update (id and data)
54
+ # :new( array) - data for create record (data)
55
+ #
56
+ # +for delete need use method destroy !!!+
57
+ #
58
+
59
+ has_many_through = ''
60
+ if(options[:through])
61
+ has_many_through = """
62
+ def self.has_many_through_update(options)
63
+ record_add = []
64
+ record_del = []
65
+
66
+ # update
67
+ options[:update].each do |id, data|
68
+ add, del = #{self}.find(id)
69
+ .has_many_update(:data => data, :relation => options[:relation])
70
+ record_add << add unless add.nil?
71
+ record_del << del unless del.nil?
72
+ end unless options[:update].nil?
73
+
74
+ # new
75
+ unless options[:new].nil?
76
+ options[:new].uniq!
77
+ options[:new].each do |data|
78
+ date = data.symbolize_keys
79
+ record_add << #{self}
80
+ .where('#{options[:compare]}' => data['#{options[:compare]}'.to_sym])
81
+ .first_or_create(data)
82
+ end
83
+ end
84
+
85
+ record_add = #{self}.where('id IN (?)', record_add) unless record_add.empty?
86
+ [record_add, record_del]
87
+ end """
88
+ end
89
+
90
+ class_eval <<-EOV
91
+ include ActiveRecord::Acts::HasMany::InstanceMethods
92
+ def depend_relations
93
+ #{depend_relations}
94
+ end
95
+ def compare
96
+ '#{options[:compare]}'.to_sym
97
+ end
98
+ def model
99
+ #{self}
100
+ end
101
+
102
+ #{has_many_through}
103
+
104
+ before_destroy :destroy_filter
105
+ EOV
106
+ end
107
+ end
108
+
109
+ module InstanceMethods
110
+
111
+ #
112
+ # +has_many_update+ ( return array) [new_id, del_id]
113
+ # options
114
+ # :data( type: hash) - data for updte
115
+ # :relation( type: str, symbol) - modifi with tableize
116
+ #
117
+
118
+ def has_many_update(options)
119
+ options_default = {
120
+ :data => { :title => ""}
121
+ }
122
+
123
+ options = options_default.merge options
124
+ options[:data] = options[:data].symbolize_keys
125
+ full_compare = { compare => options[:data][compare] }
126
+
127
+ object_id = id
128
+ delete_id = nil
129
+
130
+ if actuale? :relation => options[:relation]
131
+ # create new object and finish
132
+ object = model.where(full_compare).first_or_create(options[:data])
133
+ object_id = object.id
134
+ else
135
+ object_tmp = model.where(full_compare)[0]
136
+ unless object_tmp.nil?
137
+ # set new object and delete old
138
+ delete_id = (object_id == object_tmp.id) ? nil : object_id
139
+ object_id = object_tmp.id
140
+ else
141
+ # update old object
142
+ if object_id.nil?
143
+ object = model.where(full_compare).first_or_create(options[:data])
144
+ object_id = object.id
145
+ else
146
+ if options[:data][compare].empty?
147
+ delete_id = object_id
148
+ object_id = nil
149
+ else
150
+ update_attributes(options[:data])
151
+ end
152
+ end
153
+ end
154
+ end
155
+ [object_id, delete_id]
156
+ end
157
+
158
+ #
159
+ # +actuale?+ - check the acutuality of element in has_many table
160
+ # options
161
+ # :relation( string, symbol) - exclude current relation
162
+ #
163
+
164
+ def actuale? (options = { :relation => "" })
165
+ actuale = false
166
+
167
+ depend_relations.each do |relation|
168
+ tmp = self.send(relation)
169
+ if options[:relation].to_s.tableize == relation
170
+ actuale ||= tmp.all.size > 1
171
+ else
172
+ actuale ||= tmp.exists?
173
+ end
174
+ end
175
+
176
+ actuale
177
+ end
178
+ end
179
+
180
+ #
181
+ # +destroy_filter+ - method for before_destroy, check actuale record and
182
+ # return true for delete or false for leave
183
+ #
184
+
185
+ def destroy_filter
186
+ not actuale?
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,3 @@
1
+ module ActsHasMany
2
+ VERSION = "0.1.2"
3
+ end
@@ -0,0 +1,2 @@
1
+ require 'acts_has_many/active_record/acts/has_many'
2
+ ActiveRecord::Base.class_eval { include ActiveRecord::Acts::HasMany}
@@ -0,0 +1,70 @@
1
+ require 'helper'
2
+
3
+ describe 'acts_has_many' do
4
+ it 'update' do
5
+ location = Location.create :title => "italy"
6
+ experience = Experience.create :location => location, :title => "test experience1"
7
+
8
+ add_loc, del_loc = experience.location.has_many_update(
9
+ :data => {"title" => "ukraine"}, :relation => "experiences")
10
+ location = Location.find(add_loc)
11
+ experience.location = location
12
+ Location.delete(del_loc) unless del_loc.nil?
13
+
14
+ experience.location.title.should == "ukraine"
15
+ Location.all.size.should == 1
16
+
17
+ Experience.create :location => location, :title => "test experience2"
18
+
19
+ add_loc, del_loc = experience.location.has_many_update(
20
+ :data => {"title" => "italy"}, :relation => "experiences")
21
+ experience.location = Location.find(add_loc)
22
+ Location.delete(del_loc) unless del_loc.nil?
23
+
24
+ experience.location.id.should == add_loc
25
+ Location.all.size.should == 2
26
+
27
+ add_loc, del_loc = experience.location.has_many_update(
28
+ :data => {"title" => "ukraine"}, :relation => "experiences")
29
+ experience.location = Location.find(add_loc)
30
+ Location.delete(del_loc) unless del_loc.nil?
31
+
32
+ experience.location.should == location
33
+ Location.all.size.should == 1
34
+ end
35
+
36
+ it 'destroy' do
37
+ Location.delete_all
38
+ Experience.delete_all
39
+
40
+ location = Location.create(:title => "ukraine")
41
+
42
+ Experience.create( :location => location, :title => "test" )
43
+
44
+ location.destroy
45
+ Location.all.size.should == 1
46
+
47
+ Experience.all[0].destroy
48
+ location.destroy
49
+ Location.all.size.should == 0
50
+ end
51
+
52
+ it 'actuale?' do
53
+ Location.delete_all
54
+
55
+ location = Location.create(:title => "ukraine")
56
+
57
+ location.actuale?.should == false
58
+ location.actuale?(:relation => "experiences").should == false
59
+
60
+ Experience.create( :title => 'test', :location => location )
61
+
62
+ location.actuale?.should == true
63
+ location.actuale?(:relation => "experiences").should == false
64
+
65
+ Experience.create( :title => 'test', :location => location )
66
+
67
+ location.actuale?.should == true
68
+ location.actuale?(:relation => "experiences").should == true
69
+ end
70
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+ require 'bundler'
4
+ require "#{File.dirname(__FILE__)}/../init"
5
+
6
+ begin
7
+ Bundler.setup(:default, :development)
8
+ rescue Bundler::BundlerError => e
9
+ $stderr.puts e.message
10
+ $stderr.puts "Run `bundle install` to install missing gems"
11
+ exit e.status_code
12
+ end
13
+
14
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
15
+ ActiveRecord::Schema.verbose = false
16
+
17
+ ActiveRecord::Base.connection.schema_cache.clear!
18
+ ActiveRecord::Schema.define(:version => 1) do
19
+ create_table :experiences do |t|
20
+ t.string :title
21
+ t.references :location
22
+ end
23
+ create_table :locations do |t|
24
+ t.string :title
25
+ end
26
+ end
27
+
28
+ class Experience < ActiveRecord::Base
29
+ self.table_name = 'experiences'
30
+ belongs_to :location, :dependent => :destroy
31
+ end
32
+
33
+ class Location < ActiveRecord::Base
34
+ self.table_name = 'locations'
35
+ has_many :experiences
36
+ validates :title, :presence => true
37
+
38
+ acts_has_many
39
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_has_many
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Igor IS04
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: sqlite3
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: activerecord
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: This gem gives functional for update elements has_many relation
79
+ email: igor.s04g@gmail.com
80
+ executables: []
81
+ extensions: []
82
+ extra_rdoc_files: []
83
+ files:
84
+ - Gemfile
85
+ - LICENSE
86
+ - README.md
87
+ - Rakefile
88
+ - acts_has_many.gemspec
89
+ - init.rb
90
+ - lib/acts_has_many.rb
91
+ - lib/acts_has_many/active_record/acts/has_many.rb
92
+ - lib/acts_has_many/version.rb
93
+ - spec/has_many_spec.rb
94
+ - spec/helper.rb
95
+ homepage: https://github.com/igor04/acts_has_many
96
+ licenses: []
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 1.8.24
116
+ signing_key:
117
+ specification_version: 3
118
+ summary: All records must be used, otherwise they will be deleted. Clear logic with
119
+ has_many
120
+ test_files: []