Module: ActsAsJoinable::Core::ClassMethods

Defined in:
lib/acts_as_joinable/core.rb

Instance Method Summary collapse

Instance Method Details

#action_from_joined(action, model, conditions) ⇒ Object



254
255
256
257
258
259
260
# File 'lib/acts_as_joinable/core.rb', line 254

def action_from_joined(action, model, conditions)
  join_conditions(model, conditions.delete(:relationship) || {}) do |kind, source_type, relationship_conditions|
    ids = Relationship.select_attributes("#{kind}_id", relationship_conditions).uniq.compact.map(&:to_i)
    conditions.reverse_merge!(:id => ids)
    send(action, :conditions => conditions)
  end
end

#acts_as_joinable_on(*args) ⇒ Object



228
229
230
231
# File 'lib/acts_as_joinable/core.rb', line 228

def acts_as_joinable_on(*args)
  super(*args)
  initialize_acts_as_joinable_on_core
end

#add_around_filters_for_join(options, attribute, filter, filter_type) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/acts_as_joinable/core.rb', line 15

def add_around_filters_for_join(options, attribute, filter, filter_type)
  options[filter_type] = filter if filter
=begin
  if filter          
    options[filter_type]    = lambda { |parent, child|
      parent.write_join_attribute(attribute, child.id)
      filter.call(self)
    }
  else
    options[filter_type]    = lambda { |parent, child|
      parent.write_join_attribute(attribute, child.id)
    }
  end
=end
end

#count_from_joined(model, conditions) ⇒ Object



250
251
252
# File 'lib/acts_as_joinable/core.rb', line 250

def count_from_joined(model, conditions)
  action_from_joined(:count, model, conditions)
end

#find_from_joined(model, conditions = {}) ⇒ Object



246
247
248
# File 'lib/acts_as_joinable/core.rb', line 246

def find_from_joined(model, conditions = {})
  action_from_joined(:all, model, conditions)
end

#find_joined(model, conditions = {}) ⇒ Object



239
240
241
242
243
244
# File 'lib/acts_as_joinable/core.rb', line 239

def find_joined(model, conditions = {})
  join_conditions(model, conditions.delete(:relationship) || {}) do |kind, source_type, relationship_conditions|
    ids = Relationship.select_attributes("#{opposite_for(kind)}_id", relationship_conditions).uniq
    source_type.all(:conditions => {:id => ids}.merge(conditions))
  end
end

#find_joined_with_join(model, conditions = {}) ⇒ Object



233
234
235
236
237
# File 'lib/acts_as_joinable/core.rb', line 233

def find_joined_with_join(model, conditions = {})
  options = join_conditions(model, conditions.delete(:relationship) || {})
  options.merge!(conditions)
  source_type.all(options)
end

#initialize_acts_as_joinable_on_coreObject

custom parent, self, and child contexts Group…

acts_as_joinable_on :groups,
  :as => [:parent, :child],
  :context => :nested_groups,
  :child_classes => %w(pod store)
acts_as_joinable_on :pods, :as => :parent, :class_name => "Group"
acts_as_joinable_on :users,
  :as => :parent,
  :context => :membership
  :values => %w(owner developer admin consumer)
acts_as_joinable_on class|context|custom_alias, class, role, context, context values
acts_as_joinable_on :members, :class_name => "User", :as => :parent, :context => :memebership
acts_as_joinable_on :owner, :class_name => "User", :context => :memebership, :value => :owner
joins_one :owner, :class_name => "User", :context => :memebership, :value => :owner
joins_many :members, :class_name => "User", :context => :memebership
joins :user, :with => :role do
  has_many :board_of_directors
  has_one  :owner
end
has_many_parent :posts
has_many_child :assets
has_many_relationships :users, :through => :memberships
joins :user, :with => :membership do
  has_many :members
end

Office

acts_as_joinable_on :pods, :as => :child
acts_as_joinable_on :tenants, :as => :parent

User



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/acts_as_joinable/core.rb', line 60

def initialize_acts_as_joinable_on_core
  joins       = acts_as_joinable_config.dup
  block       = joins.pop
  opts        = joins.extract_options!
  if joins.empty?
    joins     = ActsAsJoinable.models
    relationships      = [:child]
  else
    relationships      = [opts[:as] || :parent].flatten.map(&:to_sym)
  end
  
  before_add  = opts[:before_add]
  after_add   = opts[:after_add]
  extension   = opts[:extend]
  
  opts[:class_name] ||= opts[:source].to_s.camelize if opts[:source]
  
  association_type = opts[:limit] == 1 ? :has_one : :has_many
  
  scope_name  = opts[:named_scope]
  
  autosave = opts[:autosave] != false
  
  joins.map!(&:to_sym)
  
  # class name of the model we're joining to self
  # otherwise it's retrieved from joins.each...
  class_name  = opts[:class_name] || nil
  sti         = (opts[:subclasses] || []).map { |i| i.to_s.camelize.constantize }
  # contexts defining the relationship between self and target
  contexts    = [opts[:context] || []].flatten.map(&:to_s)
  context     = contexts#.first
  # possible values of the context
  values      = [opts[:values] || opts[:value] || []].flatten.compact
  value       = values.first
  status      = opts[:status]
  
  sql         = opts[:conditions]
  
  nestable    = opts[:nestable] || false
  
  # parent, child, or contexts (both) for custom helper getters/setters
  
  has_many :parent_relationships, :class_name => 'Relationship', :as => :child, :foreign_key => "child_id", :uniq => true
  has_many :child_relationships,  :class_name => 'Relationship', :as => :parent, :foreign_key => "parent_id", :uniq => true
  
  after_destroy :destroy_relationships unless after_destroy.map(&:method).include?(:destroy_relationships)
  
  related_classes = (ancestors.reverse - included_modules + send(:subclasses) + sti).uniq
  wanted_classes = []
  while wanted_classes.push(related_classes.pop)
    break if wanted_classes.last == base_class.superclass
  end
  wanted_classes.pop
  
  joins.each do |type|
    if association_type == :has_one
      singular_type = type.to_s.to_sym
      plural_type   = type.to_s.pluralize.to_sym
    else
      singular_type = type.to_s.singularize.to_sym
      plural_type   = type.to_s.to_sym
    end
    class_name    = opts[:class_name] || type.to_s.classify
    source_type   = opts[:source_type] || class_name
    
    join_context = (context.blank? ? [singular_type] : Array(context)).map(&:to_s)
    
    options = {
      :through              => :relationships,
      :class_name           => class_name,
      :source               => :child,
      :source_type          => source_type,
      :conditions           => sql
    }
    options[:extend] = extension if extension
    
    add_around_filters_for_join(options, "#{singular_type.to_s}_id".to_sym, before_add, :before_add)
    add_around_filters_for_join(options, "#{singular_type.to_s}_id".to_sym, after_add, :after_add)
    
    relationship_table    = Relationship.quoted_table_name rescue nil
    # relationships == [:parent, :child]
    relationships.each do |relationship|
      # Post.joins :tags, :as => :parent
      # through_relationship = child_tag_relationships
      # relationship_table   = `relationships`
      relationship          = opposite_for(relationship)
      through_relationship  = "#{relationship.to_s}_#{singular_type}_relationships".to_sym
      
      options.merge!(:through => through_relationship, :source => relationship, :uniq => true)
      
      join_value            = value
      
      through_conditions = {
        "#{relationship}_type".to_sym => source_type
      }
      
      if join_context.include?(class_name.underscore)# && opposite_for(relationship).to_sym != :child
        #conditions            = [condition_string, [class_name]]
#              if join_value
#                condition_string      << "(#{relationship_table}.value = ?)"
#                conditions            = [condition_string, join_value.to_s]
#              end
        if status
          through_conditions = {:status => status}
        end
      else
        #through_conditions = {}
        through_conditions[:context] = join_context.dup
        through_conditions[:value] = join_value unless join_value.blank?
        through_conditions[:status] = status unless status.blank?
#              condition_string      << "(#{relationship_table}.context IN (?))"
#              condition_string      << " AND (#{relationship_table}.value = ?)" if join_value
#              join_contexts         = join_context.dup#[join_context, class_name.underscore].uniq
#              conditions            = [condition_string, join_contexts]
#              conditions            << join_value.to_s if join_value
      end
      
      through_options = {
        :class_name => "Relationship",
        :conditions => through_conditions,
        :as => opposite_for(relationship).to_sym
        # :select => "#{relationship}_id, #{relationship}_type, id, #{opposite_for(relationship)}_id"
      }

      if association_type == :has_one
        #options.delete(:after_add) 
        options.delete(:uniq)
      else
        through_options[:uniq] = true
      end
      
      if join_context.length > 1
        through_options[:readonly] = true
        options[:readonly] = true
      else
        through_options.delete(:readonly)
        options.delete(:readonly)
      end
      
      unless has_association?(through_relationship)
        has_many through_relationship, through_options
      end
                    
      add_association(relationship.to_s, plural_type, singular_type, options, join_context, join_value, status, &block)
      
      if association_type == :has_one
        add_has_one(singular_type, plural_type, through_relationship, class_name, join_context, join_value, options)
        add_class_relationship_method(singular_type)
      else
        add_class_relationship_method(plural_type)
      end
      
      if nestable
        accepts_nested_attributes_for plural_type.to_sym
        if association_type == :has_one
          define_method "#{singular_type}_attributes=" do |params|
            params = params.first if params.is_a?(Array)
            
            self.send("#{plural_type}_attributes=", [params])
          end
        end
      end
      
    end
  end
end

#join_conditions(model, conditions = {}, &block) ⇒ Object

end



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/acts_as_joinable/core.rb', line 266

def join_conditions(model, conditions = {}, &block)
  relationship = self.reflect_on_all_associations.detect {|a| a.name == model.to_sym}
  if relationship
    options = relationship.options
  else
    options = {:source => "parent", :source_type => model.to_s.camelize}
  end
  kind         = conditions.delete(:as) || options[:source].to_s
  source_type  = options[:source_type].constantize
  
  result = conditions.reverse_merge(
    "#{opposite_for(kind)}_type" => related_classes.map(&:name),
    "#{kind}_type" => source_type.related_classes.map(&:name)
  ).stringify_keys
  
  if block_given?
    yield(opposite_for(kind), source_type, result) 
  else
    result
  end
end