has_many_polymorphic 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007-2011 Collective Idea
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,45 @@
1
+ = HasManyPolymorphic
2
+
3
+ This mixin adds a has many polymorphic relationship to a model and creates all the relationships needed by rails to handle it.
4
+
5
+ == Params
6
+ - Name
7
+ - name of the relationship, there is a convention that whatever name you choose, the polymorphic table columns on your through table should match.
8
+
9
+ - Options
10
+ - through - the model that handles the through relationship
11
+ - models - models that should be included in this polymophic relationship
12
+
13
+
14
+ == Added methods
15
+
16
+ - {name param}
17
+ - the name of your relationship is used for the method name of this method. it will return an array of the models that are related via the has_many relationships
18
+
19
+ There is an after_save call back that will save the relationships when they are added or removed. If you want to remove a relationship the models need to be destroyed and this model reloaded.
20
+
21
+ == Example Usage
22
+
23
+ class PreferenceType < ActiveRecord::Base
24
+ has_many_polymorphic :preferenced_records,
25
+ :through => :valid_preference_types,
26
+ :models => [:desktops, :organizers]
27
+ end
28
+
29
+ this gives you the following
30
+
31
+ preferenceType = PreferenceType.first
32
+ preferenceType.desktops
33
+ preferenceType.organizers
34
+
35
+ and
36
+
37
+ preferenceType.preferenced_records
38
+
39
+ which is a concatentated array of the models. You also get the following
40
+
41
+ desktop = Desktop.first
42
+ desktop.preference_types
43
+
44
+ organizer = Organizer.first
45
+ organizer.preference_types
@@ -0,0 +1,7 @@
1
+ require 'has_many_polymorphic/has_many_polymorphic'
2
+ ActiveRecord::Base.send :include, RussellEdge::HasManyPolymorphic
3
+
4
+ #used to tie into Rails initialization load the models so the relationships are created
5
+ require 'has_many_polymorphic/autoload'
6
+
7
+
@@ -0,0 +1,50 @@
1
+ require 'initializer' unless defined? ::Rails::Initializer
2
+ require 'action_controller/dispatcher' unless defined? ::ActionController::Dispatcher
3
+
4
+ module RussellEdge
5
+ module HasManyPolymorphic
6
+
7
+ =begin rdoc
8
+ Searches for models that use <tt>has_many_polymorphic</tt> and makes sure that they get loaded during app initialization.
9
+ This ensures that helper methods are injected into the target classes.
10
+ =end
11
+
12
+ #define the models that use has_many_polymorphic. has_many_polymorphs combed the file system for models
13
+ #that had the has_many_polymorphs method. This is not as robust but more efficent. It can be set via
14
+ #
15
+ #RussellEdge::HasManyPolymorphic::DEFAULT_OPTIONS = { :models => %w(PreferenceType AnotherModel) }
16
+ #
17
+ DEFAULT_OPTIONS = {
18
+ :models => %w()
19
+ }
20
+
21
+ mattr_accessor :options
22
+ @@options = HashWithIndifferentAccess.new(DEFAULT_OPTIONS)
23
+
24
+ # Dispatcher callback to load polymorphic relationships
25
+ def self.autoload
26
+ options[:models].each do |model|
27
+ #try to load model if it exists.
28
+ begin
29
+ model.constantize
30
+ rescue=>e
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+
38
+ class Rails::Initializer #:nodoc:
39
+ # Make sure it gets loaded in the console, tests, and migrations
40
+ def after_initialize_with_autoload
41
+ after_initialize_without_autoload
42
+ RussellEdge::HasManyPolymorphic.autoload
43
+ end
44
+ alias_method_chain :after_initialize, :autoload
45
+ end
46
+
47
+ ActionController::Dispatcher.to_prepare(:morpheus_autoload) do
48
+ # Make sure it gets loaded in the app
49
+ RussellEdge::HasManyPolymorphic.autoload
50
+ end
@@ -0,0 +1,179 @@
1
+ module RussellEdge #:nodoc:
2
+ module HasManyPolymorphic #:nodoc:
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ ##
11
+ # HasManyPolymorphic
12
+ # This mixin adds a has many polymorphic relationship to a model and creates all the relationships needed
13
+ # by rails to handle it.
14
+ #
15
+ # Params
16
+ #
17
+ # Name - name of the relationship, there is a convention that whatever name you choose, the polymorphic
18
+ # table columns on your through table should match.
19
+ #
20
+ # Options
21
+ # - through - the model that handles the through relationship
22
+ # - models - models that should be included in this polymophic relationship
23
+ #
24
+ #
25
+ # One method is added for you to use
26
+ #
27
+ # - {name param}
28
+ # - the name of your relationship is used for the method name of this method. it will return
29
+ # an array of the models that are related via the has_many relationships
30
+ #
31
+ # There is an after_save call back that will save the relationships when they are added or removed
32
+ # If you want to remove a relationship the models need to be destroyed and this model reloaded.
33
+ #
34
+ # Example Usage
35
+ #
36
+ # class PreferenceType < ActiveRecord::Base
37
+ # has_morpheus :preferenced_records,
38
+ # :through => :valid_preference_types,
39
+ # :models => [:desktops, :organizers]
40
+ # end
41
+ #
42
+ # this gives you the following
43
+ #
44
+ # preferenceType = PreferenceType.first
45
+ # preferenceType.desktops
46
+ # preferenceType.organizers
47
+ #
48
+ # and
49
+ #
50
+ # preferenceType.preferenced_records
51
+ #
52
+ # which is a concatentated array of the models. You also get the following
53
+ #
54
+ # desktop = Desktop.first
55
+ # desktop.preference_types
56
+ #
57
+ # organizer = Organizer.first
58
+ # organizer.preference_types
59
+ ##
60
+
61
+ def has_many_polymorphic(name, options = {})
62
+ target_class_name = self.name
63
+ instance_array_name = "#{name}_array".to_sym
64
+
65
+ #declare array to related models
66
+ attr_accessor instance_array_name
67
+
68
+ #create the has_many relationship
69
+ has_many options[:through], :dependent => :destroy
70
+
71
+ #create the has_many relationship for each model
72
+ options[:models].each do |model|
73
+ has_many model, :through => options[:through], :source => model.to_s.singularize,
74
+ :conditions => ["#{options[:through]}.#{name.to_s.singularize}_type = ?", model.to_s.classify], :dependent => :destroy
75
+ end
76
+
77
+ #modify the through class to add the belongs to relationships
78
+ options[:through].to_s.classify.constantize.class_exec do
79
+ options[:models].each do |model|
80
+ belongs_to model.to_s.singularize.to_sym, :class_name => model.to_s.classify, :foreign_key => "#{name.to_s.singularize}_id"
81
+ end
82
+ end
83
+
84
+ #I want to keep the << and push methods of array so this helps to keep them.
85
+ define_method name do
86
+ #used the declared instance variable array
87
+ records = self.send(instance_array_name.to_s)
88
+ records = records || []
89
+ options[:models].each do |model|
90
+ records = records | self.send(model.to_s)
91
+ end
92
+
93
+ #set it back to the instance variable
94
+ self.send("#{instance_array_name.to_s}=", records)
95
+
96
+ records
97
+ end
98
+
99
+ #before we save this model make sure you save all the relationships.
100
+ before_save do |record|
101
+ record.send(name).each do |reln_record|
102
+ conditions = "#{name.to_s.singularize}_id = #{reln_record.id} and #{name.to_s.singularize}_type = '#{reln_record.class.name}'"
103
+ exisiting_record = record.send("#{options[:through]}").find(:first,
104
+ :conditions => conditions)
105
+
106
+ if exisiting_record.nil?
107
+ values_hash = {}
108
+ values_hash["#{record.class.name.underscore}_id"] = record.id
109
+ values_hash["#{name.to_s.singularize}_type"] = reln_record.class.name
110
+ values_hash["#{name.to_s.singularize}_id"] = reln_record.id
111
+
112
+ options[:through].to_s.classify.constantize.create(values_hash)
113
+ end
114
+ end
115
+ end
116
+
117
+ #include instance methods into this model
118
+ include RussellEdge::HasManyPolymorphic::InstanceMethods
119
+
120
+ #add the relationship to the models.
121
+ options[:models].each do |model|
122
+ model.to_s.classify.constantize.class_exec do
123
+ #build the has many polymorphic relationship via finder_sql
124
+ has_many target_class_name.underscore.pluralize.to_sym,
125
+ :class_name => "#{target_class_name}",
126
+ :finder_sql => 'SELECT DISTINCT target.* from '+target_class_name.tableize+' target join '+
127
+ options[:through].to_s+' join_table on join_table.'+target_class_name.underscore.singularize+'_id = target.id '+
128
+ 'and join_table.'+name.to_s.singularize+'_type = \'#{model_class_name}\' and '+
129
+ 'join_table.'+name.to_s.singularize+'_id = #{id}'
130
+
131
+ #we want to save the relantionships when the model is saved
132
+ before_save do |record|
133
+ record.send(target_class_name.underscore.pluralize).each do |reln_record|
134
+
135
+ db_result = ActiveRecord::Base.connection.select_all("SELECT count(*) as num_rows FROM #{options[:through]}
136
+ where #{name.to_s.singularize}_id = #{record.id}
137
+ and #{name.to_s.singularize}_type = '#{record.class.name}'
138
+ and #{target_class_name.underscore.singularize}_id = #{reln_record.id}")
139
+
140
+ #make sure that the relantionship does not already exist
141
+ num_rows = db_result[0]['num_rows'] unless db_result == -1
142
+ if num_rows.nil? || num_rows.to_i == 0
143
+ values_hash = {}
144
+ values_hash["#{reln_record.class.name.underscore}_id"] = reln_record.id
145
+ values_hash["#{name.to_s.singularize}_type"] = record.class.name
146
+ values_hash["#{name.to_s.singularize}_id"] = record.id
147
+ options[:through].to_s.classify.constantize.create(values_hash)
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ model.to_s.classify.constantize.class_eval do
154
+ #check if this is using STI if so use the type attribute else use the class name
155
+ def model_class_name
156
+ if attributes['type']
157
+ attributes['type']
158
+ else
159
+ self.class.to_s
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ end
166
+ end
167
+
168
+ module InstanceMethods
169
+ #clear array on reload
170
+ def reload(*args)
171
+ @records = []
172
+
173
+ super args
174
+ end
175
+
176
+ end
177
+
178
+ end
179
+ end
@@ -0,0 +1,3 @@
1
+ module HasManyPolymorphic
2
+ VERSION = '0.0.3' unless defined?(::HasManyPolymorphic::VERSION)
3
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_many_polymorphic
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 3
10
+ version: 0.0.3
11
+ platform: ruby
12
+ authors:
13
+ - Russell Holmes
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-07-26 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activerecord
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 9
30
+ segments:
31
+ - 2
32
+ - 3
33
+ - 5
34
+ version: 2.3.5
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: Simple replacement for has_many_polymorphs
38
+ email: rholmes@tnsolutionsinc.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files: []
44
+
45
+ files:
46
+ - lib/has_many_polymorphic/autoload.rb
47
+ - lib/has_many_polymorphic/has_many_polymorphic.rb
48
+ - lib/has_many_polymorphic/version.rb
49
+ - lib/has_many_polymorphic.rb
50
+ - MIT-LICENSE.txt
51
+ - README.rdoc
52
+ has_rdoc: true
53
+ homepage: https://github.com/russ1985/has_many_polymorphic
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options: []
58
+
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.3.7
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: Simple replacement for has_many_polymorphs
86
+ test_files: []
87
+