-
Notifications
You must be signed in to change notification settings - Fork 133
/
base.rb
163 lines (135 loc) · 5.87 KB
/
base.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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
module Searchlogic
module Condition # :nodoc:
# = Conditions condition
#
# The base class for creating a condition. Your custom conditions should extend this class.
# See Searchlogic::Conditions::Base.register_condition on how to write your own condition.
class Base
include Shared::Utilities
attr_accessor :any, :column, :column_for_type_cast, :column_sql, :column_sql_format, :klass, :object_name, :table_name
class_inheritable_accessor :handle_array_value, :ignore_meaningless_value, :join_arrays_with_or, :value_type
self.ignore_meaningless_value = true
class << self
# Name of the condition type inferred from the class name
def condition_type_name
name.split("::").last.underscore
end
def handle_array_value?
handle_array_value == true
end
def ignore_meaningless_value? # :nodoc:
ignore_meaningless_value == true
end
def join_arrays_with_or?
join_arrays_with_or == true
end
# Determines what to call the condition for the model
#
# Searchlogic tries to create conditions on each model. Before it does this it passes the model to this method to see what to call the condition. If the condition type doesnt want to create a condition on
# a model it will just return nil and Searchlogic will skip over it.
def condition_names_for_model
[]
end
# Same as condition_name_for_model, but for a model's column obj
def condition_names_for_column
[condition_type_name]
end
end
def initialize(klass, options = {})
self.klass = klass
self.table_name = options[:table_name] || klass.table_name
if options[:column]
self.column = options[:column].class < ::ActiveRecord::ConnectionAdapters::Column ? options[:column] : klass.columns_hash[options[:column].to_s]
if options[:column_for_type_cast]
self.column_for_type_cast = options[:column_for_type_cast]
else
type = (!self.class.value_type.blank? && self.class.value_type.to_s) || (!options[:column_type].blank? && options[:column_type].to_s) || column.sql_type
self.column_for_type_cast = column.class.new(column.name, column.default.to_s, type, column.null)
end
self.column_sql_format = options[:column_sql_format] || "{table}.{column}"
end
end
def any? # :nodoc:
any == true
end
# Substitutes string vars with table and column name. Allows us to switch the column and table on the fly and have the condition update appropriately.
# The table name could be variable depending on the condition. Take STI and more than one child model is used in the condition, the first gets the parent table name, the rest get aliases.
def column_sql
column_sql_format.gsub("{table}", quoted_table_name).gsub("{column}", quoted_column_name)
end
# Allows nils to be meaninful values
def explicitly_set_value=(value)
@explicitly_set_value = value
end
# Need this if someone wants to actually use nil in a meaningful way
def explicitly_set_value?
@explicitly_set_value == true
end
def options
{:table_name => table_name, :column => column, :column_for_type_cast => column_for_type_cast, :column_sql_format => column_sql_format}
end
# You should refrain from overwriting this method, it performs various tasks before callign your to_conditions method, allowing you to keep to_conditions simple.
def sanitize(alt_value = nil) # :nodoc:
return if value_is_meaningless?
v = alt_value || value
if v.is_a?(Array) && !self.class.handle_array_value?
merge_conditions(*v.collect { |i| sanitize(i) } << {:any => self.class.join_arrays_with_or?})
else
v = v.utc if column && v.respond_to?(:utc) && [:time, :timestamp, :datetime].include?(column.type) && klass.time_zone_aware_attributes && !klass.skip_time_zone_conversion_for_attributes.include?(column.name.to_sym)
to_conditions(v)
end
end
# The value for the condition
def value
@casted_value ||= type_cast_value(@value)
end
# Sets the value for the condition
def value=(v)
self.explicitly_set_value = true
@casted_value = nil
@value = v
end
def value_is_meaningless? # :nodoc:
meaningless?(@value)
end
private
def like_condition_name
@like_condition_name ||= klass.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE"
end
def meaningless?(v)
case v
when Array
v.each { |i| return false unless meaningless?(i) }
true
else
!explicitly_set_value? || (self.class.ignore_meaningless_value? && v != false && v.blank?)
end
end
def meaningful?(v)
!meaningless?(v)
end
def quote_column_name(column_name)
klass.connection.quote_column_name(column_name)
end
def quoted_column_name
quote_column_name(column.name)
end
def quote_table_name(table_name)
klass.connection.quote_table_name(table_name)
end
def quoted_table_name
quote_table_name(table_name)
end
def type_cast_value(v)
case v
when Array
v.collect { |i| type_cast_value(i) }.compact
else
return if meaningless?(v)
return v if !column_for_type_cast || !v.is_a?(String)
column_for_type_cast.type_cast(v)
end
end
end
end
end