Class: Searchgasm::Conditions::Base

Inherits:
Object
  • Object
show all
Includes:
Protection, Shared::Utilities, Shared::VirtualClasses
Defined in:
lib/searchgasm/conditions/base.rb,
lib/searchgasm.rb

Overview

Conditions

Represents a collection of conditions and performs various tasks on that collection. For information on each condition see Searchgasm::Condition. Each condition has its own file and class and the source for each condition is pretty self explanatory.

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Shared::VirtualClasses

included

Methods included from Protection

#conditions_with_protection=, included, #protect=, #protect?

Constructor Details

#initialize(init_conditions = {}) ⇒ Base

Returns a new instance of Base.



150
151
152
153
154
# File 'lib/searchgasm/conditions/base.rb', line 150

def initialize(init_conditions = {})
  add_associations!
  add_column_equals_conditions!
  self.conditions = init_conditions
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object (private)



350
351
352
353
354
355
356
# File 'lib/searchgasm/conditions/base.rb', line 350

def method_missing(name, *args, &block)
  if setup_condition(name)
    send(name, *args, &block)
  else
    super
  end
end

Class Attribute Details

.added_associationsObject

Returns the value of attribute added_associations.



14
15
16
# File 'lib/searchgasm/conditions/base.rb', line 14

def added_associations
  @added_associations
end

.added_column_equals_conditionsObject

Returns the value of attribute added_column_equals_conditions.



14
15
16
# File 'lib/searchgasm/conditions/base.rb', line 14

def added_column_equals_conditions
  @added_column_equals_conditions
end

.added_klass_conditionsObject

Returns the value of attribute added_klass_conditions.



14
15
16
# File 'lib/searchgasm/conditions/base.rb', line 14

def added_klass_conditions
  @added_klass_conditions
end

Instance Attribute Details

#anyObject

:nodoc:



167
168
169
# File 'lib/searchgasm/conditions/base.rb', line 167

def any
  @any
end

#relationship_nameObject

Returns the value of attribute relationship_name.



11
12
13
# File 'lib/searchgasm/conditions/base.rb', line 11

def relationship_name
  @relationship_name
end

Class Method Details

.association_namesObject

A list of all associations created, used for caching and performance



123
124
125
# File 'lib/searchgasm/conditions/base.rb', line 123

def association_names
  @association_names ||= []
end

.column_detailsObject

:nodoc:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/searchgasm/conditions/base.rb', line 16

def column_details # :nodoc:
  return @column_details if @column_details
  
  @column_details = []
  
  klass.columns.each do |column|
    column_detail = {:column => column}
    column_detail[:aliases] = case column.type
    when :datetime, :time, :timestamp
      [column.name.gsub(/_at$/, "")]
    when :date
      [column.name.gsub(/_at$/, "")]
    else
      []
    end
    
    @column_details << column_detail
  end
  
  @column_details
end

.condition_namesObject

A list of all conditions available, users for caching and performance



128
129
130
# File 'lib/searchgasm/conditions/base.rb', line 128

def condition_names
  @condition_names ||= []
end

.conditionsObject

A list of available condition type classes



67
68
69
# File 'lib/searchgasm/conditions/base.rb', line 67

def conditions
  @@conditions ||= []
end

.modifiersObject

A list of available modifier classes



118
119
120
# File 'lib/searchgasm/conditions/base.rb', line 118

def modifiers
  @@modifiers ||= []
end

.needed?(model_class, conditions) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/searchgasm/conditions/base.rb', line 132

def needed?(model_class, conditions) # :nodoc:
  return false if conditions.blank?
  
  if conditions.is_a?(Hash)
    return true if conditions[:any]
    stringified_conditions = conditions.stringify_keys
    stringified_conditions.keys.each { |condition| return false if condition.include?(".") } # setting conditions on associations, which is just another way of writing SQL, and we ignore SQL
    
    column_names = model_class.column_names
    stringified_conditions.keys.each do |condition|
      return true unless column_names.include?(condition)
    end
  end
  
  false
end

.register_condition(condition_class) ⇒ Object

Registers a condition as an available condition for a column or a class. MySQL supports a “sounds like” function. I want to use it, so let’s add it.

Example

# config/initializers/searchgasm.rb
# Actual function for MySQL databases only
class SoundsLike < Searchgasm::Condition::Base
  # The name of the conditions. By default its the name of the class, if you want alternate or alias conditions just add them on.
  # If you don't want to add aliases you don't even need to define this method
  def self.name_for_column(column)
    super
  end

  # You can return an array or a string. NOT a hash, because all of these conditions
  # need to eventually get merged together. The array or string can be anything you would put in
  # the :conditions option for ActiveRecord::Base.find(). Also notice the column_sql variable. This is essentail
  # for applying modifiers and should be used in your conditions wherever you want the column.
  def to_conditions(value)
    ["#{column_sql} SOUNDS LIKE ?", value]
  end
end

Searchgasm::Seearch::Conditions.register_condition(SoundsLike)

Raises:

  • (ArgumentError)


61
62
63
64
# File 'lib/searchgasm/conditions/base.rb', line 61

def register_condition(condition_class)
  raise(ArgumentError, "You can only register conditions that extend Searchgasm::Condition::Base") unless condition_class.ancestors.include?(Searchgasm::Condition::Base)
  conditions << condition_class unless conditions.include?(condition_class)
end

.register_modifier(modifier_class) ⇒ Object

Registers a modifier as an available modifier for each column.

Example

# config/initializers/searchgasm.rb
class Ceil < Searchgasm::Modifiers::Base
  # The name of the modifier. By default its the name of the class, if you want alternate or alias modifiers just add them on.
  # If you don't want to add aliases you don't even need to define this method
  def self.modifier_names
    super + ["round_up"]
  end

  # The name of the method in the connection adapters (see below). By default its the name of your class suffixed with "_sql".
  # So in this example it would be "ceil_sql". Unless you want to change that you don't need to define this method.
  def self.adapter_method_name
    super
  end

  # This is the type of value returned from the modifier. This is neccessary for typcasting values for the modifier when
  # applied to a column
  def self.return_type
    :integer
  end
end

Searchgasm::Seearch::Conditions.register_modifiers(Ceil)

Now here’s the fun part, applying this modifier to each connection adapter. Some databases call modifiers differently. If they all apply them the same you can add in the function to ActiveRecord::ConnectionAdapters::AbstractAdapter, otherwise you need to add them to each individually: ActiveRecord::ConnectionAdapters::MysqlAdapter, ActiveRecord::ConnectionAdapters::PostgreSQLAdapter, ActiveRecord::ConnectionAdapters::SQLiteAdapter

Do this by includine a model with your method. The name of your method, by default, is: #modifier_name_sql. So in the example above it would be “ceil_sql”

module CeilAdapterMethod
  def ceil_sql(column_name)
    "CEIL(#{column_name})"
  end
end

ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:include, CeilAdapterMethod)
# ... include for the rest of the adapters

Raises:

  • (ArgumentError)


112
113
114
115
# File 'lib/searchgasm/conditions/base.rb', line 112

def register_modifier(modifier_class)
  raise(ArgumentError, "You can only register conditions that extend Searchgasm::Modifiers::Base") unless modifier_class.ancestors.include?(Searchgasm::Modifiers::Base)
  modifiers << modifier_class unless modifiers.include?(modifier_class)
end

Instance Method Details

#any?Boolean

Convenience method for determining if we should join the conditions with “AND” or “OR”.

Returns:

  • (Boolean)


172
173
174
# File 'lib/searchgasm/conditions/base.rb', line 172

def any?
  @any == true || @any == "true" || @any == "1" || @any == "yes"
end

#auto_joinsObject

A list of joins to use when searching, includes relationships



177
178
179
180
181
182
183
184
185
# File 'lib/searchgasm/conditions/base.rb', line 177

def auto_joins
  j = []
  associations.each do |name, association|
    next if association.conditions.blank?
    association_joins = association.auto_joins
    j << (association_joins.blank? ? name : {name => association_joins})
  end
  j.blank? ? nil : (j.size == 1 ? j.first : j)
end

#conditionsObject

All of the active conditions (conditions that have been set)



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/searchgasm/conditions/base.rb', line 227

def conditions
  return @conditions if @conditions
  return if objects.blank?
  
  conditions_hash = {}
  objects.each do |name, object|
    if object.class < Searchgasm::Conditions::Base
      relationship_conditions = object.conditions
      next if relationship_conditions.blank?
      conditions_hash[name] = relationship_conditions
    else
      next if object.value_is_meaningless?
      conditions_hash[name] = object.value
    end
  end
  conditions_hash
end

#conditions=(value) ⇒ Object

Allows you to set the conditions via a hash.



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
# File 'lib/searchgasm/conditions/base.rb', line 198

def conditions=(value)
  case value
  when Hash
    assert_valid_conditions(value)
    remove_conditions_from_protected_assignement(value).each do |condition, condition_value|
      
      # delete all blanks from mass assignments, forms submit blanks, blanks are meaningless
      # equals condition thinks everything is meaningful, and arrays can be pased
      new_condition_value = nil
      case condition_value
      when Array
        new_condition_value = []
        condition_value.each { |v| new_condition_value << v unless v == "" }
        next if new_condition_value.size == 0
        new_condition_value = new_condition_value.first if new_condition_value.size == 1
      else
        next if condition_value == ""
        new_condition_value = condition_value
      end
      
      send("#{condition}=", new_condition_value)
    end
  else
    reset_objects!
    @conditions = value
  end
end

#inspectObject



187
188
189
# File 'lib/searchgasm/conditions/base.rb', line 187

def inspect
  "#<#{klass}Conditions#{conditions.blank? ? "" : " #{conditions.inspect}"}>"
end

#sanitizeObject

Sanitizes the conditions down into conditions that ActiveRecord::Base.find can understand.



192
193
194
195
# File 'lib/searchgasm/conditions/base.rb', line 192

def sanitize
  return @conditions if @conditions
  merge_conditions(*(objects.collect { |name, object| object.sanitize } << {:any => any}))
end