searchlogic 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/CHANGELOG.rdoc +228 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Manifest +123 -0
  4. data/README.rdoc +383 -0
  5. data/Rakefile +15 -0
  6. data/TODO.rdoc +6 -0
  7. data/examples/README.rdoc +4 -0
  8. data/init.rb +1 -0
  9. data/lib/searchlogic.rb +89 -0
  10. data/lib/searchlogic/active_record/associations.rb +52 -0
  11. data/lib/searchlogic/active_record/base.rb +218 -0
  12. data/lib/searchlogic/active_record/connection_adapters/mysql_adapter.rb +172 -0
  13. data/lib/searchlogic/active_record/connection_adapters/postgresql_adapter.rb +168 -0
  14. data/lib/searchlogic/active_record/connection_adapters/sqlite_adapter.rb +75 -0
  15. data/lib/searchlogic/condition/base.rb +159 -0
  16. data/lib/searchlogic/condition/begins_with.rb +17 -0
  17. data/lib/searchlogic/condition/blank.rb +21 -0
  18. data/lib/searchlogic/condition/child_of.rb +11 -0
  19. data/lib/searchlogic/condition/descendant_of.rb +24 -0
  20. data/lib/searchlogic/condition/ends_with.rb +17 -0
  21. data/lib/searchlogic/condition/equals.rb +27 -0
  22. data/lib/searchlogic/condition/greater_than.rb +15 -0
  23. data/lib/searchlogic/condition/greater_than_or_equal_to.rb +15 -0
  24. data/lib/searchlogic/condition/inclusive_descendant_of.rb +11 -0
  25. data/lib/searchlogic/condition/keywords.rb +47 -0
  26. data/lib/searchlogic/condition/less_than.rb +15 -0
  27. data/lib/searchlogic/condition/less_than_or_equal_to.rb +15 -0
  28. data/lib/searchlogic/condition/like.rb +15 -0
  29. data/lib/searchlogic/condition/nil.rb +21 -0
  30. data/lib/searchlogic/condition/not_begin_with.rb +20 -0
  31. data/lib/searchlogic/condition/not_blank.rb +19 -0
  32. data/lib/searchlogic/condition/not_end_with.rb +20 -0
  33. data/lib/searchlogic/condition/not_equal.rb +26 -0
  34. data/lib/searchlogic/condition/not_have_keywords.rb +20 -0
  35. data/lib/searchlogic/condition/not_like.rb +20 -0
  36. data/lib/searchlogic/condition/not_nil.rb +19 -0
  37. data/lib/searchlogic/condition/sibling_of.rb +14 -0
  38. data/lib/searchlogic/condition/tree.rb +17 -0
  39. data/lib/searchlogic/conditions/base.rb +484 -0
  40. data/lib/searchlogic/conditions/protection.rb +36 -0
  41. data/lib/searchlogic/config.rb +31 -0
  42. data/lib/searchlogic/config/helpers.rb +289 -0
  43. data/lib/searchlogic/config/search.rb +53 -0
  44. data/lib/searchlogic/core_ext/hash.rb +75 -0
  45. data/lib/searchlogic/helpers/control_types/link.rb +310 -0
  46. data/lib/searchlogic/helpers/control_types/links.rb +241 -0
  47. data/lib/searchlogic/helpers/control_types/remote_link.rb +87 -0
  48. data/lib/searchlogic/helpers/control_types/remote_links.rb +72 -0
  49. data/lib/searchlogic/helpers/control_types/remote_select.rb +36 -0
  50. data/lib/searchlogic/helpers/control_types/select.rb +82 -0
  51. data/lib/searchlogic/helpers/form.rb +208 -0
  52. data/lib/searchlogic/helpers/utilities.rb +197 -0
  53. data/lib/searchlogic/modifiers/absolute.rb +15 -0
  54. data/lib/searchlogic/modifiers/acos.rb +11 -0
  55. data/lib/searchlogic/modifiers/asin.rb +11 -0
  56. data/lib/searchlogic/modifiers/atan.rb +11 -0
  57. data/lib/searchlogic/modifiers/base.rb +27 -0
  58. data/lib/searchlogic/modifiers/ceil.rb +15 -0
  59. data/lib/searchlogic/modifiers/char_length.rb +15 -0
  60. data/lib/searchlogic/modifiers/cos.rb +15 -0
  61. data/lib/searchlogic/modifiers/cot.rb +15 -0
  62. data/lib/searchlogic/modifiers/day_of_month.rb +15 -0
  63. data/lib/searchlogic/modifiers/day_of_week.rb +15 -0
  64. data/lib/searchlogic/modifiers/day_of_year.rb +15 -0
  65. data/lib/searchlogic/modifiers/degrees.rb +11 -0
  66. data/lib/searchlogic/modifiers/exp.rb +15 -0
  67. data/lib/searchlogic/modifiers/floor.rb +15 -0
  68. data/lib/searchlogic/modifiers/hex.rb +11 -0
  69. data/lib/searchlogic/modifiers/hour.rb +11 -0
  70. data/lib/searchlogic/modifiers/log.rb +15 -0
  71. data/lib/searchlogic/modifiers/log10.rb +11 -0
  72. data/lib/searchlogic/modifiers/log2.rb +11 -0
  73. data/lib/searchlogic/modifiers/lower.rb +15 -0
  74. data/lib/searchlogic/modifiers/ltrim.rb +15 -0
  75. data/lib/searchlogic/modifiers/md5.rb +11 -0
  76. data/lib/searchlogic/modifiers/microseconds.rb +11 -0
  77. data/lib/searchlogic/modifiers/milliseconds.rb +11 -0
  78. data/lib/searchlogic/modifiers/minute.rb +15 -0
  79. data/lib/searchlogic/modifiers/month.rb +15 -0
  80. data/lib/searchlogic/modifiers/octal.rb +15 -0
  81. data/lib/searchlogic/modifiers/radians.rb +11 -0
  82. data/lib/searchlogic/modifiers/round.rb +11 -0
  83. data/lib/searchlogic/modifiers/rtrim.rb +15 -0
  84. data/lib/searchlogic/modifiers/second.rb +15 -0
  85. data/lib/searchlogic/modifiers/sign.rb +11 -0
  86. data/lib/searchlogic/modifiers/sin.rb +11 -0
  87. data/lib/searchlogic/modifiers/square_root.rb +15 -0
  88. data/lib/searchlogic/modifiers/tan.rb +15 -0
  89. data/lib/searchlogic/modifiers/trim.rb +15 -0
  90. data/lib/searchlogic/modifiers/upper.rb +15 -0
  91. data/lib/searchlogic/modifiers/week.rb +11 -0
  92. data/lib/searchlogic/modifiers/year.rb +11 -0
  93. data/lib/searchlogic/search/base.rb +148 -0
  94. data/lib/searchlogic/search/conditions.rb +53 -0
  95. data/lib/searchlogic/search/ordering.rb +244 -0
  96. data/lib/searchlogic/search/pagination.rb +121 -0
  97. data/lib/searchlogic/search/protection.rb +89 -0
  98. data/lib/searchlogic/search/searching.rb +31 -0
  99. data/lib/searchlogic/shared/utilities.rb +50 -0
  100. data/lib/searchlogic/shared/virtual_classes.rb +39 -0
  101. data/lib/searchlogic/version.rb +79 -0
  102. data/searchlogic.gemspec +39 -0
  103. data/test/fixtures/accounts.yml +15 -0
  104. data/test/fixtures/cats.yml +3 -0
  105. data/test/fixtures/dogs.yml +3 -0
  106. data/test/fixtures/orders.yml +14 -0
  107. data/test/fixtures/user_groups.yml +13 -0
  108. data/test/fixtures/users.yml +36 -0
  109. data/test/test_active_record_associations.rb +81 -0
  110. data/test/test_active_record_base.rb +93 -0
  111. data/test/test_condition_base.rb +52 -0
  112. data/test/test_condition_types.rb +143 -0
  113. data/test/test_conditions_base.rb +242 -0
  114. data/test/test_conditions_protection.rb +16 -0
  115. data/test/test_config.rb +23 -0
  116. data/test/test_helper.rb +134 -0
  117. data/test/test_search_base.rb +227 -0
  118. data/test/test_search_conditions.rb +19 -0
  119. data/test/test_search_ordering.rb +165 -0
  120. data/test/test_search_pagination.rb +72 -0
  121. data/test/test_search_protection.rb +24 -0
  122. data/test_libs/acts_as_tree.rb +98 -0
  123. data/test_libs/ordered_hash.rb +9 -0
  124. data/test_libs/rexml_fix.rb +14 -0
  125. metadata +317 -0
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'echoe'
3
+
4
+ require File.dirname(__FILE__) << "/lib/searchlogic/version"
5
+
6
+ Echoe.new 'searchlogic' do |p|
7
+ p.version = Searchlogic::Version::STRING
8
+ p.author = "Ben Johnson of Binary Logic"
9
+ p.email = 'bjohnson@binarylogic.com'
10
+ p.project = 'searchlogic'
11
+ p.summary = "Object based ActiveRecord searching, ordering, pagination, and more!"
12
+ p.url = "http://github.com/binarylogic/searchlogic"
13
+ p.dependencies = %w(activerecord activesupport)
14
+ p.include_rakefile = true
15
+ end
data/TODO.rdoc ADDED
@@ -0,0 +1,6 @@
1
+ = To Do
2
+
3
+ 1. Perform "more efficient" checks: year_of_created_at = 2008 and month_of_created_at = 8. Should result in a "BETWEEN" statement utilizing the column indexes. Thanks Georg for letting me know about this.
4
+ 2. Solve conflicts between scope joins and joins in the search (for old versions of AR). Also solve conflicts between joins and includes.
5
+ 3. Add configuration to change the "english" words
6
+ 4. Re-add the distinct option
@@ -0,0 +1,4 @@
1
+ === Live example
2
+
3
+ * Please see the live example: http://searchlogic_example.binarylogic.com
4
+ * Source / Github project for the example: http://github.com/binarylogic/searchlogic_example.binarylogic.com
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) << "/lib/searchlogic"
@@ -0,0 +1,89 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require "active_support"
4
+ require "active_record"
5
+ require "active_record/version"
6
+
7
+ ["mysql", "postgresql", "sqlite"].each do |adapter_name|
8
+ begin
9
+ require "active_record/connection_adapters/#{adapter_name}_adapter"
10
+ require "searchlogic/active_record/connection_adapters/#{adapter_name}_adapter"
11
+ rescue Exception
12
+ end
13
+ end
14
+
15
+ # Core Ext
16
+ require "searchlogic/core_ext/hash"
17
+
18
+ # Shared
19
+ require "searchlogic/shared/utilities"
20
+ require "searchlogic/shared/virtual_classes"
21
+
22
+ # Base classes
23
+ require "searchlogic/version"
24
+ require "searchlogic/config/helpers"
25
+ require "searchlogic/config/search"
26
+ require "searchlogic/config"
27
+
28
+ # ActiveRecord
29
+ require "searchlogic/active_record/base"
30
+ require "searchlogic/active_record/associations"
31
+
32
+ # Search
33
+ require "searchlogic/search/ordering"
34
+ require "searchlogic/search/pagination"
35
+ require "searchlogic/search/conditions"
36
+ require "searchlogic/search/searching"
37
+ require "searchlogic/search/base"
38
+ require "searchlogic/search/protection"
39
+
40
+ # Conditions
41
+ require "searchlogic/conditions/protection"
42
+ require "searchlogic/conditions/base"
43
+
44
+ # Condition
45
+ require "searchlogic/condition/base"
46
+ require "searchlogic/condition/tree"
47
+ SEARCHGASM_CONDITIONS = [:begins_with, :blank, :child_of, :descendant_of, :ends_with, :equals, :greater_than, :greater_than_or_equal_to, :inclusive_descendant_of, :like, :nil, :not_begin_with, :not_blank, :not_end_with, :not_equal, :not_have_keywords, :not_nil, :keywords, :less_than, :less_than_or_equal_to, :sibling_of]
48
+ SEARCHGASM_CONDITIONS.each { |condition| require "searchlogic/condition/#{condition}" }
49
+
50
+ # Modifiers
51
+ require "searchlogic/modifiers/base"
52
+ SEARCHGASM_MODIFIERS = [:absolute, :acos, :asin, :atan, :ceil, :char_length, :cos, :cot, :day_of_month, :day_of_week, :day_of_year, :degrees, :exp, :floor, :hex, :hour, :log, :log10, :log2, :lower, :ltrim, :md5, :microseconds, :milliseconds, :minute, :month, :octal, :radians, :round, :rtrim, :second, :sign, :sin, :square_root, :tan, :trim, :upper, :week, :year]
53
+ SEARCHGASM_MODIFIERS.each { |modifier| require "searchlogic/modifiers/#{modifier}" }
54
+
55
+ # Helpers
56
+ require "searchlogic/helpers/utilities"
57
+ require "searchlogic/helpers/form"
58
+ require "searchlogic/helpers/control_types/link"
59
+ require "searchlogic/helpers/control_types/links"
60
+ require "searchlogic/helpers/control_types/select"
61
+ require "searchlogic/helpers/control_types/remote_link"
62
+ require "searchlogic/helpers/control_types/remote_links"
63
+ require "searchlogic/helpers/control_types/remote_select"
64
+
65
+ # Lets do it!
66
+ module Searchlogic
67
+ module Search
68
+ class Base
69
+ include Conditions
70
+ include Ordering
71
+ include Protection
72
+ include Pagination
73
+ include Searching
74
+ end
75
+ end
76
+
77
+ module Conditions
78
+ class Base
79
+ include Protection
80
+ end
81
+
82
+ SEARCHGASM_CONDITIONS.each { |condition| Base.register_condition("Searchlogic::Condition::#{condition.to_s.camelize}".constantize) }
83
+ SEARCHGASM_MODIFIERS.each { |modifier| Base.register_modifier("Searchlogic::Modifiers::#{modifier.to_s.camelize}".constantize) }
84
+ end
85
+
86
+ # The namespace I put all cached search classes.
87
+ module Cache
88
+ end
89
+ end
@@ -0,0 +1,52 @@
1
+ module Searchlogic
2
+ module ActiveRecord
3
+ # = Searchlogic ActiveRecord Associations
4
+ #
5
+ # These methods hook into ActiveRecords association methods and add in searchlogic functionality.
6
+ module Associations
7
+ module AssociationCollection
8
+ # This needs to be implemented because AR doesn't leverage scopes with this method like it probably should
9
+ def find_with_searchlogic(*args)
10
+ options = args.extract_options!
11
+ args << filter_options_with_searchlogic(options)
12
+ find_without_searchlogic(*args)
13
+ end
14
+ end
15
+
16
+ module HasManyAssociation
17
+ def count_with_searchlogic(*args)
18
+ options = args.extract_options!
19
+ args << filter_options_with_searchlogic(options)
20
+ count_without_searchlogic(*args)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ ActiveRecord::Associations::AssociationCollection.class_eval do
28
+ if respond_to?(:find)
29
+ include Searchlogic::ActiveRecord::Associations::AssociationCollection
30
+ alias_method_chain :find, :searchlogic
31
+ end
32
+ end
33
+
34
+ ActiveRecord::Associations::HasManyAssociation.class_eval do
35
+ include Searchlogic::ActiveRecord::Associations::HasManyAssociation
36
+ alias_method_chain :count, :searchlogic
37
+
38
+ # Older versions of AR have find in here, not in AssociationCollection
39
+ include Searchlogic::ActiveRecord::Associations::AssociationCollection
40
+ alias_method_chain :find, :searchlogic
41
+ end
42
+
43
+ ActiveRecord::Associations::ClassMethods::InnerJoinDependency::InnerJoinAssociation.class_eval do
44
+ private
45
+ # Inner joins impose limitations on queries. They can be quicker but you can't do OR conditions when conditions
46
+ # overlap from the base model to any of its associations. Also, inner joins won't allow you to order by an association
47
+ # attribute. What if the association is optional? All of those records are ommitted. It just doesn't make sense to default
48
+ # to inner joins when providing this as a "convenience" when searching. So let's change it.
49
+ def join_type
50
+ "LEFT OUTER JOIN"
51
+ end
52
+ end
@@ -0,0 +1,218 @@
1
+ module Searchlogic
2
+ # == Searchlogic ActiveRecord
3
+ #
4
+ # Hooks into ActiveRecord to add all of the searchlogic functionality into your models. Only uses what is publically available, doesn't dig into internals, and
5
+ # searchlogic only gets involved when needed.
6
+ module ActiveRecord
7
+ # = Searchlogic ActiveRecord Base
8
+ # Adds in base level functionality to ActiveRecord
9
+ module Base
10
+ # This is an alias method chain. It hook into ActiveRecord's "calculate" method and checks to see if Searchlogic should get involved.
11
+ def calculate_with_searchlogic(*args)
12
+ options = args.extract_options!
13
+ options = filter_options_with_searchlogic(options, false)
14
+ args << options
15
+ calculate_without_searchlogic(*args)
16
+ end
17
+
18
+ # This is an alias method chain. It hooks into ActiveRecord's "find" method and checks to see if Searchlogic should get involved.
19
+ def find_with_searchlogic(*args)
20
+ options = args.extract_options!
21
+ options = filter_options_with_searchlogic(options)
22
+ args << options
23
+ find_without_searchlogic(*args)
24
+ end
25
+
26
+ # This is an alias method chain. It hooks into ActiveRecord's scopes and checks to see if Searchlogic should get involved. Allowing you to use all of Searchlogics conditions and tools
27
+ # in scopes as well.
28
+ #
29
+ # === Examples
30
+ #
31
+ # Named scopes:
32
+ #
33
+ # named_scope :top_expensive, :conditions => {:total_gt => 1_000_000}, :per_page => 10
34
+ # named_scope :top_expensive_ordered, :conditions => {:total_gt => 1_000_000}, :per_page => 10, :order_by => {:user => :first_name}
35
+ #
36
+ # Good ole' regular scopes:
37
+ #
38
+ # with_scope(:find => {:conditions => {:total_gt => 1_000_000}, :per_page => 10}) do
39
+ # find(:all)
40
+ # end
41
+ #
42
+ # with_scope(:find => {:conditions => {:total_gt => 1_000_000}, :per_page => 10}) do
43
+ # build_search
44
+ # end
45
+ def with_scope_with_searchlogic(method_scoping = {}, action = :merge, &block)
46
+ method_scoping[:find] = filter_options_with_searchlogic(method_scoping[:find]) if method_scoping[:find]
47
+ with_scope_without_searchlogic(method_scoping, action, &block)
48
+ end
49
+
50
+ # This is a special method that Searchlogic adds in. It returns a new search object on the model. So you can search via an object.
51
+ #
52
+ # <b>This method is "protected". Meaning it checks the passed options for SQL injections. So trying to write raw SQL in *any* of the option will result in a raised exception. It's safe to pass a params object when instantiating.</b>
53
+ #
54
+ # This method has an alias "new_search"
55
+ #
56
+ # === Examples
57
+ #
58
+ # search = User.new_search
59
+ # search.conditions.first_name_contains = "Ben"
60
+ # search.per_page = 20
61
+ # search.page = 2
62
+ # search.order_by = {:user_group => :name}
63
+ # search.all # can call any search method: first, find(:all), find(:first), sum("id"), etc...
64
+ def build_search(options = {}, &block)
65
+ search = searchlogic_search
66
+ search.protect = true
67
+ search.options = options
68
+ yield search if block_given?
69
+ search
70
+ end
71
+
72
+ # See build_search. This is the same method but *without* protection. Do *NOT* pass in a params object to this method.
73
+ #
74
+ # This also has an alias "new_search!"
75
+ def build_search!(options = {}, &block)
76
+ search = searchlogic_search(options)
77
+ yield search if block_given?
78
+ search
79
+ end
80
+
81
+ # Similar to ActiveRecord's attr_protected, but for conditions. It will block any conditions in this array that are being mass assigned. Mass assignments are:
82
+ #
83
+ # === Examples
84
+ #
85
+ # search = User.new_search(:conditions => {:first_name_like => "Ben", :email_contains => "binarylogic.com"})
86
+ # search.options = {:conditions => {:first_name_like => "Ben", :email_contains => "binarylogic.com"}}
87
+ #
88
+ # If first_name_like is in the list of conditions_protected then it will be removed from the hash.
89
+ def conditions_protected(*conditions)
90
+ write_inheritable_attribute(:conditions_protected, Set.new(conditions.map(&:to_s)) + (protected_conditions || []))
91
+ end
92
+
93
+ def protected_conditions # :nodoc:
94
+ read_inheritable_attribute(:conditions_protected)
95
+ end
96
+
97
+ # This is the reverse of conditions_protected. You can specify conditions here and *only* these conditions will be allowed in mass assignment. Any condition not specified here will be blocked.
98
+ def conditions_accessible(*conditions)
99
+ write_inheritable_attribute(:conditions_accessible, Set.new(conditions.map(&:to_s)) + (accessible_conditions || []))
100
+ end
101
+
102
+ def accessible_conditions # :nodoc:
103
+ read_inheritable_attribute(:conditions_accessible)
104
+ end
105
+
106
+ private
107
+ def filter_options_with_searchlogic(options = {}, searching = true)
108
+ return options unless Searchlogic::Search::Base.needed?(self, options)
109
+ search = Searchlogic::Search::Base.create_virtual_class(self).new # call explicitly to avoid merging the scopes into the search
110
+ search.acting_as_filter = true
111
+ search.scope = scope(:find)
112
+ conditions = options.delete(:conditions) || options.delete("conditions") || {}
113
+ if conditions
114
+ case conditions
115
+ when Hash
116
+ conditions.each { |condition, value| search.conditions.send("#{condition}=", value) } # explicitly call to enforce blanks
117
+ else
118
+ search.conditions = conditions
119
+ end
120
+ end
121
+ search.options = options
122
+ search.sanitize(searching)
123
+ end
124
+
125
+ def searchlogic_search(options = {})
126
+ scope = {}
127
+ current_scope = scope(:find) && scope(:find).deep_dup
128
+ if current_scope
129
+ [:conditions, :include, :joins].each do |option|
130
+ value = current_scope.delete(option)
131
+ next if value.blank?
132
+ scope[option] = value
133
+ end
134
+
135
+ # Delete nil values in the scope, for some reason habtm relationships like to pass :limit => nil
136
+ new_scope = {}
137
+ current_scope.each { |k, v| new_scope[k] = v unless v.nil? }
138
+ current_scope = new_scope
139
+ end
140
+ search = Searchlogic::Search::Base.create_virtual_class(self).new
141
+ search.scope = scope
142
+ search.options = current_scope
143
+ search.options = options
144
+ search
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ ActiveRecord::Base.send(:extend, Searchlogic::ActiveRecord::Base)
151
+
152
+ module ActiveRecord #:nodoc: all
153
+ class Base
154
+ class << self
155
+ alias_method_chain :calculate, :searchlogic
156
+ alias_method_chain :find, :searchlogic
157
+ alias_method_chain :with_scope, :searchlogic
158
+ alias_method :new_search, :build_search
159
+ alias_method :new_search!, :build_search!
160
+
161
+ def valid_find_options
162
+ VALID_FIND_OPTIONS
163
+ end
164
+
165
+ def valid_calculations_options
166
+ Calculations::CALCULATIONS_OPTIONS
167
+ end
168
+
169
+ private
170
+ # This is copied over from 2 different versions of ActiveRecord. I have to do this in order to preserve the "auto joins"
171
+ # as symbols. Keeping them as symbols allows ActiveRecord to merge them properly. The problem is when they conflict with includes.
172
+ # Includes add joins also, and they add them before joins do. So if they already added them skip them. Now you can do queries like:
173
+ #
174
+ # User.all(:joins => {:orders => :line_items}, :include => :orders)
175
+ #
176
+ # Where as before, the only way to get the above query to work would be to include line_items also, which is not neccessarily what you want.
177
+ def add_joins!(sql, options_or_joins, scope = :auto) # :nodoc:
178
+ code_type = (respond_to?(:array_of_strings?, true) && :array_of_strings) || (respond_to?(:merge_joins, true) && :merge_joins)
179
+
180
+ case code_type
181
+ when :array_of_strings, :merge_joins
182
+ joins = options_or_joins
183
+ scope = scope(:find) if :auto == scope
184
+ merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
185
+ case merged_joins
186
+ when Symbol, Hash, Array
187
+ if code_type == :array_of_strings && array_of_strings?(merged_joins)
188
+ merged_joins.each { |merged_join| sql << " #{merged_join} " unless sql.include?(merged_join) }
189
+ else
190
+ join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
191
+ join_dependency.join_associations.each do |assoc|
192
+ join_sql = assoc.association_join
193
+ sql << " #{join_sql} " unless sql.include?(join_sql)
194
+ end
195
+ end
196
+ when String
197
+ sql << " #{merged_joins} " if merged_joins && !sql.include?(merged_joins)
198
+ end
199
+ else
200
+ options = options_or_joins
201
+ scope = scope(:find) if :auto == scope
202
+ [(scope && scope[:joins]), options[:joins]].each do |join|
203
+ case join
204
+ when Symbol, Hash, Array
205
+ join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, join, nil)
206
+ join_dependency.join_associations.each do |assoc|
207
+ join_sql = assoc.association_join
208
+ sql << " #{join_sql} " unless sql.include?(join_sql)
209
+ end
210
+ else
211
+ sql << " #{join} " if join && !sql.include?(join)
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,172 @@
1
+ module Searchlogic
2
+ module ActiveRecord
3
+ module ConnectionAdapters # :nodoc: all
4
+ module MysqlAdapter
5
+ # Date / time functions
6
+ def microseconds_sql(column_name)
7
+ "MICROSECOND(#{column_name})"
8
+ end
9
+
10
+ def milliseconds_sql(column_name)
11
+ "(MICROSECOND(#{column_name}) / 1000)"
12
+ end
13
+
14
+ def second_sql(column_name)
15
+ "SECOND(#{column_name})"
16
+ end
17
+
18
+ def minute_sql(column_name)
19
+ "MINUTE(#{column_name})"
20
+ end
21
+
22
+ def hour_sql(column_name)
23
+ "HOUR(#{column_name})"
24
+ end
25
+
26
+ def day_of_week_sql(column_name)
27
+ "DAYOFWEEK(#{column_name})"
28
+ end
29
+
30
+ def day_of_month_sql(column_name)
31
+ "DAYOFMONTH(#{column_name})"
32
+ end
33
+
34
+ def day_of_year_sql(column_name)
35
+ "DAYOFYEAR(#{column_name})"
36
+ end
37
+
38
+ def week_sql(column_name)
39
+ "WEEK(#{column_name}, 2)"
40
+ end
41
+
42
+ def month_sql(column_name)
43
+ "MONTH(#{column_name})"
44
+ end
45
+
46
+ def year_sql(column_name)
47
+ "YEAR(#{column_name})"
48
+ end
49
+
50
+ # String functions
51
+ def char_length_sql(column_name)
52
+ "CHAR_LENGTH(#{column_name})"
53
+ end
54
+
55
+ def lower_sql(column_name)
56
+ "LOWER(#{column_name})"
57
+ end
58
+
59
+ def ltrim_sql(column_name)
60
+ "LTRIM(#{column_name})"
61
+ end
62
+
63
+ def md5_sql(column_name)
64
+ "MD5(#{column_name})"
65
+ end
66
+
67
+ def rtrim_sql(column_name)
68
+ "RTRIM(#{column_name})"
69
+ end
70
+
71
+ def trim_sql(column_name)
72
+ "TRIM(#{column_name})"
73
+ end
74
+
75
+ def upper_sql(column_name)
76
+ "UPPER(#{column_name})"
77
+ end
78
+
79
+ # Number functions
80
+ def absolute_sql(column_name)
81
+ "ABS(#{column_name})"
82
+ end
83
+
84
+ def acos_sql(column_name)
85
+ "ACOS(#{column_name})"
86
+ end
87
+
88
+ def asin_sql(column_name)
89
+ "ASIN(#{column_name})"
90
+ end
91
+
92
+ def atan_sql(column_name)
93
+ "ATAN(#{column_name})"
94
+ end
95
+
96
+ def ceil_sql(column_name)
97
+ "CEIL(#{column_name})"
98
+ end
99
+
100
+ def cos_sql(column_name)
101
+ "COS(#{column_name})"
102
+ end
103
+
104
+ def cot_sql(column_name)
105
+ "COT(#{column_name})"
106
+ end
107
+
108
+ def degrees_sql(column_name)
109
+ "DEGREES(#{column_name})"
110
+ end
111
+
112
+ def exp_sql(column_name)
113
+ "EXP(#{column_name})"
114
+ end
115
+
116
+ def floor_sql(column_name)
117
+ "FLOOR(#{column_name})"
118
+ end
119
+
120
+ def hex_sql(column_name)
121
+ "HEX(#{column_name})"
122
+ end
123
+
124
+ def ln_sql(column_name)
125
+ "LN(#{column_name})"
126
+ end
127
+
128
+ def log_sql(column_name)
129
+ "LOG(#{column_name})"
130
+ end
131
+
132
+ def log2_sql(column_name)
133
+ "LOG2(#{column_name})"
134
+ end
135
+
136
+ def log10_sql(column_name)
137
+ "LOG10(#{column_name})"
138
+ end
139
+
140
+ def octal_sql(column_name)
141
+ "OCT(#{column_name})"
142
+ end
143
+
144
+ def radians_sql(column_name)
145
+ "RADIANS(#{column_name})"
146
+ end
147
+
148
+ def round_sql(column_name)
149
+ "ROUND(#{column_name})"
150
+ end
151
+
152
+ def sign_sql(column_name)
153
+ "SIGN(#{column_name})"
154
+ end
155
+
156
+ def sin_sql(column_name)
157
+ "SIN(#{column_name})"
158
+ end
159
+
160
+ def square_root_sql(column_name)
161
+ "SQRT(#{column_name})"
162
+ end
163
+
164
+ def tan_sql(column_name)
165
+ "TAN(#{column_name})"
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ ::ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:include, Searchlogic::ActiveRecord::ConnectionAdapters::MysqlAdapter)