searchgasm 0.9.6 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. data/CHANGELOG +2 -0
  2. data/Manifest +34 -21
  3. data/{README.mdown → README.rdoc} +96 -63
  4. data/Rakefile +1 -1
  5. data/examples/README.rdoc +4 -0
  6. data/lib/searchgasm/active_record/associations.rb +40 -42
  7. data/lib/searchgasm/active_record/base.rb +75 -61
  8. data/lib/searchgasm/condition/base.rb +127 -0
  9. data/lib/searchgasm/condition/begins_with.rb +20 -0
  10. data/lib/searchgasm/condition/child_of.rb +11 -0
  11. data/lib/searchgasm/condition/contains.rb +20 -0
  12. data/lib/searchgasm/condition/descendant_of.rb +24 -0
  13. data/lib/searchgasm/condition/does_not_equal.rb +28 -0
  14. data/lib/searchgasm/condition/ends_with.rb +20 -0
  15. data/lib/searchgasm/condition/equals.rb +20 -0
  16. data/lib/searchgasm/condition/greater_than.rb +25 -0
  17. data/lib/searchgasm/condition/greater_than_or_equal_to.rb +20 -0
  18. data/lib/searchgasm/condition/inclusive_descendant_of.rb +13 -0
  19. data/lib/searchgasm/condition/keywords.rb +33 -0
  20. data/lib/searchgasm/condition/less_than.rb +25 -0
  21. data/lib/searchgasm/condition/less_than_or_equal_to.rb +20 -0
  22. data/lib/searchgasm/condition/sibling_of.rb +16 -0
  23. data/lib/searchgasm/condition/tree.rb +16 -0
  24. data/lib/searchgasm/conditions/base.rb +221 -0
  25. data/lib/searchgasm/conditions/protection.rb +30 -0
  26. data/lib/searchgasm/config.rb +137 -0
  27. data/lib/searchgasm/helpers/form_helper.rb +159 -0
  28. data/lib/searchgasm/helpers/search_helper.rb +178 -0
  29. data/lib/searchgasm/helpers/utilities_helper.rb +125 -0
  30. data/lib/searchgasm/search/base.rb +73 -179
  31. data/lib/searchgasm/search/conditions.rb +42 -166
  32. data/lib/searchgasm/search/ordering.rb +149 -0
  33. data/lib/searchgasm/search/pagination.rb +69 -0
  34. data/lib/searchgasm/search/protection.rb +61 -0
  35. data/lib/searchgasm/utilities.rb +30 -0
  36. data/lib/searchgasm/version.rb +44 -47
  37. data/lib/searchgasm.rb +57 -21
  38. data/searchgasm.gemspec +71 -46
  39. data/test/test_active_record_associations.rb +1 -1
  40. data/test/test_active_record_base.rb +4 -4
  41. data/test/test_condition.rb +143 -0
  42. data/test/{test_searchgasm_conditions.rb → test_conditions_base.rb} +43 -33
  43. data/test/test_search_base.rb +189 -0
  44. data/test/test_search_ordering.rb +91 -0
  45. data/test/test_search_pagination.rb +56 -0
  46. data/test/test_search_protection.rb +35 -0
  47. metadata +70 -45
  48. data/lib/searchgasm/search/condition.rb +0 -105
  49. data/lib/searchgasm/search/condition_types/begins_with_condition.rb +0 -26
  50. data/lib/searchgasm/search/condition_types/child_of_condition.rb +0 -17
  51. data/lib/searchgasm/search/condition_types/contains_condition.rb +0 -26
  52. data/lib/searchgasm/search/condition_types/descendant_of_condition.rb +0 -30
  53. data/lib/searchgasm/search/condition_types/does_not_equal_condition.rb +0 -34
  54. data/lib/searchgasm/search/condition_types/ends_with_condition.rb +0 -26
  55. data/lib/searchgasm/search/condition_types/equals_condition.rb +0 -26
  56. data/lib/searchgasm/search/condition_types/greater_than_condition.rb +0 -31
  57. data/lib/searchgasm/search/condition_types/greater_than_or_equal_to_condition.rb +0 -26
  58. data/lib/searchgasm/search/condition_types/inclusive_descendant_of_condition.rb +0 -19
  59. data/lib/searchgasm/search/condition_types/keywords_condition.rb +0 -39
  60. data/lib/searchgasm/search/condition_types/less_than_condition.rb +0 -31
  61. data/lib/searchgasm/search/condition_types/less_than_or_equal_to_condition.rb +0 -26
  62. data/lib/searchgasm/search/condition_types/sibling_of_condition.rb +0 -22
  63. data/lib/searchgasm/search/condition_types/tree_condition.rb +0 -20
  64. data/lib/searchgasm/search/utilities.rb +0 -34
  65. data/test/test_searchgasm_base.rb +0 -185
  66. data/test/test_searchgasm_condition_types.rb +0 -143
@@ -1,76 +1,90 @@
1
- module BinaryLogic
2
- module Searchgasm
3
- module ActiveRecord
4
- module Base
5
- def calculate_with_searchgasm(*args)
6
- options = args.extract_options!
7
- options = sanitize_options_with_searchgasm(options)
8
- args << options
9
- calculate_without_searchgasm(*args)
10
- end
11
-
12
- def find_with_searchgasm(*args)
13
- options = args.extract_options!
14
- options = sanitize_options_with_searchgasm(options)
15
- args << options
16
- find_without_searchgasm(*args)
17
- end
18
-
19
- def scope_with_searchgasm(method, key = nil)
20
- scope = scope_without_searchgasm(method, key)
21
- return sanitize_options_with_searchgasm(scope) if key.nil? && method == :find && !scope.blank?
22
- scope
23
- end
1
+ module Searchgasm
2
+ module ActiveRecord #:nodoc: all
3
+ module Base
4
+ def calculate_with_searchgasm(*args)
5
+ options = args.extract_options!
6
+ options = sanitize_options_with_searchgasm(options)
7
+ args << options
8
+ calculate_without_searchgasm(*args)
9
+ end
10
+
11
+ def find_with_searchgasm(*args)
12
+ options = args.extract_options!
13
+ options = sanitize_options_with_searchgasm(options)
14
+ args << options
15
+ find_without_searchgasm(*args)
16
+ end
17
+
18
+ def scope_with_searchgasm(method, key = nil)
19
+ scope = scope_without_searchgasm(method, key)
20
+ return sanitize_options_with_searchgasm(scope) if key.nil? && method == :find && !scope.blank?
21
+ scope
22
+ end
23
+
24
+ def build_conditions(values = {}, &block)
25
+ conditions = searchgasm_conditions
26
+ conditions.protect = true
27
+ conditions.conditions = values
28
+ yield conditions if block_given?
29
+ conditions
30
+ end
31
+
32
+ def build_conditions!(values = {}, &block)
33
+ conditions = searchgasm_conditions(values)
34
+ yield conditions if block_given?
35
+ conditions
36
+ end
37
+
38
+ def build_search(options = {}, &block)
39
+ search = searchgasm_searcher
40
+ search.protect = true
41
+ search.options = options
42
+ yield search if block_given?
43
+ search
44
+ end
45
+
46
+ def build_search!(options = {}, &block)
47
+ search = searchgasm_searcher(options)
48
+ yield search if block_given?
49
+ search
50
+ end
24
51
 
25
- def build_conditions(values = {}, &block)
26
- conditions = searchgasm_conditions
27
- conditions.protect = true
28
- conditions.value = values
29
- yield conditions if block_given?
30
- conditions
31
- end
52
+ def conditions_protected(*conditions)
53
+ write_inheritable_attribute(:conditions_protected, Set.new(conditions.map(&:to_s)) + (protected_conditions || []))
54
+ end
55
+
56
+ def protected_conditions
57
+ read_inheritable_attribute(:conditions_protected)
58
+ end
32
59
 
33
- def build_conditions!(values = {}, &block)
34
- conditions = searchgasm_conditions(values)
35
- yield conditions if block_given?
36
- conditions
60
+ def conditions_accessible(*conditions)
61
+ write_inheritable_attribute(:conditions_accessible, Set.new(conditions.map(&:to_s)) + (protected_conditions || []))
62
+ end
63
+
64
+ def accessible_conditions
65
+ read_inheritable_attribute(:conditions_accessible)
66
+ end
67
+
68
+ private
69
+ def sanitize_options_with_searchgasm(options = {})
70
+ return options unless Searchgasm::Search::Base.needed?(self, options)
71
+ searchgasm_searcher(options).sanitize
37
72
  end
38
73
 
39
- def build_search(options = {}, &block)
40
- search = searchgasm_searcher
41
- search.protect = true
42
- search.options = options
43
- yield search if block_given?
44
- search
74
+ def searchgasm_conditions(options = {})
75
+ Searchgasm::Conditions::Base.new(self, options)
45
76
  end
46
77
 
47
- def build_search!(options = {}, &block)
48
- search = searchgasm_searcher(options)
49
- yield search if block_given?
50
- search
78
+ def searchgasm_searcher(options = {})
79
+ Searchgasm::Search::Base.new(self, options)
51
80
  end
52
-
53
- private
54
- def sanitize_options_with_searchgasm(options = {})
55
- return options unless BinaryLogic::Searchgasm::Search::Base.needed?(self, options)
56
- searchgasm_searcher(options).sanitize
57
- end
58
-
59
- def searchgasm_conditions(options = {})
60
- BinaryLogic::Searchgasm::Search::Conditions.new(self, options)
61
- end
62
-
63
- def searchgasm_searcher(options = {})
64
- BinaryLogic::Searchgasm::Search::Base.new(self, options)
65
- end
66
- end
67
81
  end
68
82
  end
69
83
  end
70
84
 
71
- ActiveRecord::Base.send(:extend, BinaryLogic::Searchgasm::ActiveRecord::Base)
85
+ ActiveRecord::Base.send(:extend, Searchgasm::ActiveRecord::Base)
72
86
 
73
- module ::ActiveRecord
87
+ module ActiveRecord #:nodoc: all
74
88
  class Base
75
89
  class << self
76
90
  alias_method_chain :calculate, :searchgasm
@@ -0,0 +1,127 @@
1
+ module Searchgasm
2
+ module Condition # :nodoc:
3
+ # = Conditions condition
4
+ #
5
+ # The base class for creating a condition. Your custom conditions should extend this class.
6
+ # See Searchgasm::Conditions::Base.register_condition on how to write your own condition.
7
+ class Base
8
+ include Utilities
9
+
10
+ attr_accessor :column, :klass
11
+ attr_reader :value
12
+
13
+ class << self
14
+ # Name of the condition inferred from the class name
15
+ def condition_name
16
+ name.split("::").last.gsub(/Condition$/, "").underscore
17
+ end
18
+
19
+ # I pass you a column you tell me what to call the condition. If you don't want to use this condition for the column
20
+ # just return nil
21
+ def name_for_column(column)
22
+ "#{column.name}_#{condition_name}"
23
+ end
24
+
25
+ # Alias methods for the column condition.
26
+ def aliases_for_column(column)
27
+ []
28
+ end
29
+
30
+ # Sane as name_for_column but for the class as a whole. For example the tree methods apply to the class as a whole and not
31
+ # specific columns. Any condition that applies to columns should probably return nil here.
32
+ def name_for_klass(klass)
33
+ nil
34
+ end
35
+
36
+ # Alias methods for the klass condition
37
+ def aliases_for_klass(klass)
38
+ []
39
+ end
40
+
41
+ # A utility method for using in name_for_column. For example the keywords condition only applied to string columns, the great than condition doesnt.
42
+ def string_column?(column)
43
+ [:string, :text].include?(column.type)
44
+ end
45
+
46
+ # A utility method for using in name_for_column. For example you wouldn't want a string column to use the greater thann condition, but you would for an integer column.
47
+ def comparable_column?(column)
48
+ [:integer, :float, :decimal, :datetime, :timestamp, :time, :date].include?(column.type)
49
+ end
50
+ end
51
+
52
+ def initialize(klass, column = nil)
53
+ self.klass = klass
54
+ self.column = column.is_a?(String) ? klass.columns_hash[column] : column
55
+ end
56
+
57
+ # Allows nils to be meaninful values
58
+ def explicitly_set_value=(value)
59
+ @explicitly_set_value = value
60
+ end
61
+
62
+ # Need this if someone wants to actually use nil in a meaningful way
63
+ def explicitly_set_value?
64
+ @explicitly_set_value == true
65
+ end
66
+
67
+ # In most cases a blank value should be ignored, except for conditions like equals. A blank value is meaningful there, but a blank value for they keyswords condition is not.
68
+ def ignore_blanks?
69
+ true
70
+ end
71
+
72
+ # A convenience method for the name of the method for that specific column or klass
73
+ def name
74
+ column ? self.class.name_for_column(column) : self.class.name_for_klass(klass)
75
+ end
76
+
77
+ # A convenience method for the name of this condition
78
+ def condition_name
79
+ self.class.condition_name
80
+ end
81
+
82
+ # Quotes a column name properly for sql.
83
+ def quote_column_name(column_name)
84
+ klass.connection.quote_column_name(column_name)
85
+ end
86
+
87
+ # A convenience method for using when writing your sql in to_conditions. This is the proper way to use a column name in a query for most databases
88
+ def quoted_column_name
89
+ quote_column_name(column.name)
90
+ end
91
+
92
+ # Quotes a table name properly for sql
93
+ def quote_table_name(table_name)
94
+ klass.connection.quote_table_name(table_name)
95
+ end
96
+
97
+ # A convenience method for using when writing your sql in to_conditions. This is the proper way to use a table name in a query for most databases
98
+ def quoted_table_name
99
+ quote_table_name(klass.table_name)
100
+ end
101
+
102
+ # You should refrain from overwriting this method, it performs various tasks before callign your to_conditions method, allowing you to keep to_conditions simple.
103
+ def sanitize(alt_value = nil) # :nodoc:
104
+ return unless explicitly_set_value?
105
+ v = alt_value || value
106
+ if v.is_a?(Array) && !["equals", "does_not_equal"].include?(condition_name)
107
+ merge_conditions(*v.collect { |i| sanitize(i) })
108
+ else
109
+ v = v.utc if column && [:time, :timestamp, :datetime].include?(column.type) && klass.time_zone_aware_attributes && !klass.skip_time_zone_conversion_for_attributes.include?(column.name.to_sym)
110
+ to_conditions(v)
111
+ end
112
+ end
113
+
114
+ # The value for the condition
115
+ def value
116
+ @value.is_a?(String) ? column.type_cast(@value) : @value
117
+ end
118
+
119
+ # Sets the value for the condition, will ignore place if ignore_blanks?
120
+ def value=(v)
121
+ return if ignore_blanks? && v.blank?
122
+ self.explicitly_set_value = true
123
+ @value = v
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,20 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class BeginsWith < Base
4
+ class << self
5
+ def name_for_column(column)
6
+ return unless string_column?(column)
7
+ super
8
+ end
9
+
10
+ def aliases_for_column(column)
11
+ ["#{column.name}_bw", "#{column.name}_starts_with", "#{column.name}_start"]
12
+ end
13
+ end
14
+
15
+ def to_conditions(value)
16
+ ["#{quoted_table_name}.#{quoted_column_name} LIKE ?", "#{value}%"]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class ChildOf < Tree
4
+ def to_conditions(value)
5
+ parent_association = klass.reflect_on_association(:parent)
6
+ foreign_key_name = (parent_association && parent_association.options[:foreign_key]) || "parent_id"
7
+ ["#{quoted_table_name}.#{quote_column_name(foreign_key_name)} = ?", (value.is_a?(klass) ? value.send(klass.primary_key) : value)]
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class Contains < Base
4
+ class << self
5
+ def name_for_column(column)
6
+ return unless string_column?(column)
7
+ super
8
+ end
9
+
10
+ def aliases_for_column(column)
11
+ ["#{column.name}_like", "#{column.name}_has"]
12
+ end
13
+ end
14
+
15
+ def to_conditions(value)
16
+ ["#{quoted_table_name}.#{quoted_column_name} LIKE ?", "%#{value}%"]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class DescendantOf < Tree
4
+ def to_conditions(value)
5
+ # Wish I knew how to do this in SQL
6
+ root = (value.is_a?(klass) ? value : klass.find(value)) rescue return
7
+ strs = []
8
+ subs = []
9
+ all_children_ids(root).each do |child_id|
10
+ strs << "#{quoted_table_name}.#{quote_column_name(klass.primary_key)} = ?"
11
+ subs << child_id
12
+ end
13
+ [strs.join(" OR "), *subs]
14
+ end
15
+
16
+ private
17
+ def all_children_ids(record)
18
+ ids = record.children.collect { |child| child.send(klass.primary_key) }
19
+ record.children.each { |child| ids += all_children_ids(child) }
20
+ ids
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class DoesNotEqual < Base
4
+ class << self
5
+ def aliases_for_column(column)
6
+ ["#{column.name}_is_not", "#{column.name}_not"]
7
+ end
8
+ end
9
+
10
+ def ignore_blanks?
11
+ false
12
+ end
13
+
14
+ def to_conditions(value)
15
+ # Delegate to equals and then change
16
+ condition = Equals.new(klass, column)
17
+ condition.value = value
18
+
19
+ sql = condition.sanitize
20
+ sql.gsub!(/ IS /, " IS NOT ")
21
+ sql.gsub!(/ BETWEEN /, " NOT BETWEEN ")
22
+ sql.gsub!(/ IN /, " NOT IN ")
23
+ sql.gsub!(/=/, "!=")
24
+ sql
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class EndsWith < Base
4
+ class << self
5
+ def name_for_column(column)
6
+ return unless string_column?(column)
7
+ super
8
+ end
9
+
10
+ def aliases_for_column(column)
11
+ ["#{column.name}_ew", "#{column.name}_ends", "#{column.name}_end"]
12
+ end
13
+ end
14
+
15
+ def to_conditions(value)
16
+ ["#{quoted_table_name}.#{quoted_column_name} LIKE ?", "%#{value}"]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class Equals < Base
4
+ class << self
5
+ def aliases_for_column(column)
6
+ ["#{column.name}", "#{column.name}_is"]
7
+ end
8
+ end
9
+
10
+ def ignore_blanks?
11
+ false
12
+ end
13
+
14
+ def to_conditions(value)
15
+ # Let ActiveRecord handle this
16
+ klass.send(:sanitize_sql_hash_for_conditions, {column.name => value})
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class GreaterThan < Base
4
+ class << self
5
+ def name_for_column(column)
6
+ return unless comparable_column?(column)
7
+ super
8
+ end
9
+
10
+ def aliases_for_column(column)
11
+ column_names = [column.name]
12
+ column_names << column.name.gsub(/_at$/, "") if [:datetime, :timestamp, :time, :date].include?(column.type) && column.name =~ /_at$/
13
+
14
+ aliases = []
15
+ column_names.each { |column_name| aliases += ["#{column_name}_gt", "#{column_name}_after"] }
16
+ aliases
17
+ end
18
+ end
19
+
20
+ def to_conditions(value)
21
+ ["#{quoted_table_name}.#{quoted_column_name} > ?", value]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class GreaterThanOrEqualTo < Base
4
+ class << self
5
+ def name_for_column(column)
6
+ return unless comparable_column?(column)
7
+ super
8
+ end
9
+
10
+ def aliases_for_column(column)
11
+ ["#{column.name}_gte", "#{column.name}_at_least"]
12
+ end
13
+ end
14
+
15
+ def to_conditions(value)
16
+ ["#{quoted_table_name}.#{quoted_column_name} >= ?", value]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class InclusiveDescendantOf < Tree
4
+ include Searchgasm::Utilities
5
+
6
+ def to_conditions(value)
7
+ condition = DescendantOf.new(klass, column)
8
+ condition.value = value
9
+ merge_conditions(["#{quoted_table_name}.#{quote_column_name(klass.primary_key)} = ?", (value.is_a?(klass) ? value.send(klass.primary_key) : value)], condition.sanitize, :any => true)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class Keywords < Base
4
+ BLACKLISTED_WORDS = ('a'..'z').to_a + ["about", "an", "are", "as", "at", "be", "by", "com", "de", "en", "for", "from", "how", "in", "is", "it", "la", "of", "on", "or", "that", "the", "the", "this", "to", "und", "was", "what", "when", "where", "who", "will", "with", "www"] # from ranks.nl
5
+
6
+ class << self
7
+ def name_for_column(column)
8
+ return unless string_column?(column)
9
+ super
10
+ end
11
+
12
+ def aliases_for_column(column)
13
+ ["#{column.name}_kwords", "#{column.name}_kw"]
14
+ end
15
+ end
16
+
17
+ def to_conditions(value)
18
+ strs = []
19
+ subs = []
20
+
21
+ search_parts = value.gsub(/,/, " ").split(/ /).collect { |word| word.downcase.gsub(/[^[:alnum:]]/, ''); }.uniq.select { |word| !BLACKLISTED_WORDS.include?(word.downcase) && !word.blank? }
22
+ return if search_parts.blank?
23
+
24
+ search_parts.each do |search_part|
25
+ strs << "#{quoted_table_name}.#{quoted_column_name} LIKE ?"
26
+ subs << "%#{search_part}%"
27
+ end
28
+
29
+ [strs.join(" AND "), *subs]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class LessThan < Base
4
+ class << self
5
+ def name_for_column(column)
6
+ return unless comparable_column?(column)
7
+ super
8
+ end
9
+
10
+ def aliases_for_column(column)
11
+ column_names = [column.name]
12
+ column_names << column.name.gsub(/_at$/, "") if [:datetime, :timestamp, :time, :date].include?(column.type) && column.name =~ /_at$/
13
+
14
+ aliases = []
15
+ column_names.each { |column_name| aliases += ["#{column_name}_lt", "#{column_name}_before"] }
16
+ aliases
17
+ end
18
+ end
19
+
20
+ def to_conditions(value)
21
+ ["#{quoted_table_name}.#{quoted_column_name} < ?", value]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class LessThanOrEqualTo < Base
4
+ class << self
5
+ def name_for_column(column)
6
+ return unless comparable_column?(column)
7
+ super
8
+ end
9
+
10
+ def aliases_for_column(column)
11
+ ["#{column.name}_lte", "#{column.name}_at_most"]
12
+ end
13
+ end
14
+
15
+ def to_conditions(value)
16
+ ["#{quoted_table_name}.#{quoted_column_name} <= ?", value]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class SiblingOf < Tree
4
+ include Searchgasm::Utilities
5
+
6
+ def to_conditions(value)
7
+ parent_association = klass.reflect_on_association(:parent)
8
+ foreign_key_name = (parent_association && parent_association.options[:foreign_key]) || "parent_id"
9
+ parent_id = (value.is_a?(klass) ? value : klass.find(value)).send(foreign_key_name)
10
+ condition = ChildOf.new(klass, column)
11
+ condition.value = parent_id
12
+ merge_conditions(["#{quoted_table_name}.#{quote_column_name(klass.primary_key)} != ?", (value.is_a?(klass) ? value.send(klass.primary_key) : value)], condition.sanitize)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class Tree < Base # :nodoc:
4
+ class << self
5
+ def name_for_column(column)
6
+ nil
7
+ end
8
+
9
+ def name_for_klass(klass)
10
+ return unless klass.reflect_on_association(:parent) && klass.reflect_on_association(:children)
11
+ condition_name
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end