searchgasm 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
- v0.9.1 Added aliases for datetime, date, time, and timestamp attrs. You could call created_at_after, mow you can also call created_after.
1
+ v0.9.2. Enhanced protection
2
+
3
+ v0.9.1. Added aliases for datetime, date, time, and timestamp attrs. You could call created_at_after, mow you can also call created_after.
2
4
 
3
5
  v0.9.0. First release
data/Manifest CHANGED
@@ -2,7 +2,6 @@ CHANGELOG
2
2
  init.rb
3
3
  lib/searchgasm/active_record/associations.rb
4
4
  lib/searchgasm/active_record/base.rb
5
- lib/searchgasm/active_record/protection.rb
6
5
  lib/searchgasm/helpers.rb
7
6
  lib/searchgasm/search/base.rb
8
7
  lib/searchgasm/search/condition.rb
data/README.mdown CHANGED
@@ -2,12 +2,34 @@
2
2
 
3
3
  Searchgasm is orgasmic. Maybe not orgasmic, but you will get aroused. So go grab a towel and let's dive in.
4
4
 
5
- Searchgasm originated to satisfy a VERY simple need: so that I could use my form builder when making search forms. Sounds simple right? The goal was to use an object, that represents a search, just like an ActiveRecord object in form\_for and fields\_for.
5
+ Searchgasm was a super secret tool of mine until I decided to share with the world. It has saved me tons of time, allowed me to write less code, made searching painless, and kept my controllers DRY.
6
+
7
+ It originated to satisfy a VERY simple need: so that I could use my form builder when making search forms. Sounds simple right? The goal was to use an object, that represents a search, just like an ActiveRecord object in form\_for and fields\_for.
6
8
 
7
9
  I'm a big fan of understanding what I'm using, so here's a quick explanation: The design behind this plugin is pretty simple. The search object "santiizes" down into the options passed into ActiveRecord::Base.find(). It basically serves as a transparent filter between you and ActiveRecord::Base.find(). This filter provides "enhancements" that get translated into options that ActiveRecord::Base.find() can understand. This doesn't step on the toes or dig into he internals of ActiveRecord. It uses what ActiveRecord provides publicly. Letting ActiveRecord do all of the hard work and keeping this plugin solid and less brittle.
8
10
 
9
11
  Here's where you get aroused...
10
12
 
13
+ ## Why I love Searchgasm!
14
+
15
+ # app/controllers/users_controller.rb
16
+ def index
17
+ @search = User.new_search(:conditions => params[:conditions])
18
+ @users, @users_count = @search.all, @search.count
19
+ end
20
+
21
+ Now your view:
22
+
23
+ # app/views/users/index.html.erb
24
+ <%= form_for :conditions, @search.conditions, :url => users_path do |f| %>
25
+ <%= f.text_field :first_name_contains %>
26
+ <%= f.calendar_date_select :created_after %> # nice rails plugin for replacing date_select
27
+ <% f.fields_for :orders, f.object.orders do |orders_f| %>
28
+ <%= f.select :total_gt, (1..100) %>
29
+ <% end %>
30
+ <%= f.submit %>
31
+ <% end %>
32
+
11
33
  ## Install and use
12
34
 
13
35
  sudo gem install searchgasm
@@ -30,7 +52,7 @@ Now go into your console and try out any of these example with your own models.
30
52
 
31
53
  **For all examples, let's assume the following relationships: User => Orders => Line Items**
32
54
 
33
- ## Super Simple Example
55
+ ## Simple Searching Example
34
56
 
35
57
  User.all(
36
58
  :conditions => {
@@ -44,7 +66,7 @@ Now go into your console and try out any of these example with your own models.
44
66
  ## Detailed Example w/ object based searching
45
67
 
46
68
  # Instantiate
47
- search = User.new_search(
69
+ @search = User.new_search(
48
70
  :conditions => {
49
71
  :first_name_contains => "Ben",
50
72
  :age_gt => 18,
@@ -57,21 +79,23 @@ Now go into your console and try out any of these example with your own models.
57
79
  )
58
80
 
59
81
  # Set conditions on relationships
60
- search.conditions.email_ends_with = "binarylogic.com"
61
- search.conditions.oders.line_items.created_after = Time.now
82
+ @search.conditions.email_ends_with = "binarylogic.com"
83
+ @search.conditions.oders.line_items.created_after = Time.now
62
84
 
63
85
  # Set options
64
- search.per_page = 50 # overrides the 20 set above
86
+ @search.per_page = 50 # overrides the 20 set above
65
87
 
66
88
  # Set ANY of the ActiveRecord options
67
- search.group = "last_name"
68
- search.readonly = true
89
+ @search.group = "last_name"
90
+ @search.readonly = true
69
91
  # ... see ActiveRecord documentation
70
92
 
71
93
  # Return results just like ActiveRecord
72
- search.all
73
- search.search # alias for all
74
- search.first
94
+ @search.all
95
+ @search.search # alias for all
96
+ @search.first
97
+
98
+ Take the @search object and pass it right into form\_for or fields\_for (see above).
75
99
 
76
100
  ## Calculations
77
101
 
@@ -131,6 +155,8 @@ Again, if you want to be hardcore:
131
155
  conditions.first_name_contains = "Ben"
132
156
  conditions.search
133
157
 
158
+ Now pass the conditions object right into form\_for or fields\_for (see above for example).
159
+
134
160
  ## Scoped searching
135
161
 
136
162
  @current_user.orders.find(:all, :conditions => {:total_lte => 500})
@@ -170,27 +196,24 @@ Not only can you use searchgasm when searching, but you can use it when setting
170
196
  named_scope :sexy, :conditions => {:first_name => "Ben", email_ends_with => "binarylogic.com"}, :per_page => 20
171
197
  end
172
198
 
173
- ## Always use protection (searching with params)
174
-
175
- If there is one thing we in sex ed. it's to always use protection. As I mentioned above the purpose of this plugin was to create a search object and use it in form\_for or fields\_for. What about receiving the params in the controller and protecting against SQL injection?
199
+ ## Always use protection...against SQL injections
176
200
 
177
- accounts = Account.find_with_protection(params[:search])
178
- accounts = Account.all_with_protection(params[:search])
179
- account = Account.first_with_protection(params[:search])
201
+ If there is one thing we all know, it's to always use protection. That's why searchgasm protects you by default. The new\_search and new\_conditions methods are protected by default. This means that various checks are done to ensure it is not possible to perform any type of SQL injection. But this also limits how you can search, meaning you can't write raw SQL. If you want to be daring and search without protection prepare to accept the consequences. All that you have to do is add ! to the end of the methods: new\_search! and new\_conditions!. That was an awkward paragraph. Let's move on.
180
202
 
181
- For the lazy programmer:
203
+ ### Protected from SQL injections
182
204
 
183
- accounts = Account.findwp(params[:search])
184
- accounts = Account.allwp(params[:search])
185
- account = Account.firstwp(params[:search])
205
+ search = Account.new_search(params[:search])
206
+ conditions = Account.new_conditions(params[:conditions])
186
207
 
187
- This performs various checks to ensure SQL injection is impossible. I'm sure you know this, but I have to say it: *DO NOT* pass params into the "find", "all", or "first" methods, otherwise you are opening yourself up to SQL injections.
208
+ ### *NOT* protected from SQL injections
188
209
 
189
- # DO NOT DO THIS!
210
+ accounts = Account.find(params[:search])
190
211
  accounts = Account.all(params[:search])
191
-
192
- # OR THIS!
193
- accounts = Account.all(:conditions => params[:conditions])
212
+ account = Account.first(params[:search])
213
+ search = Account.new_search!(params[:search])
214
+ conditions = Account.new_conditions!(params[:conditions])
215
+
216
+ Lesson learned: use new\_search and new\_conditions when passing in params as *ANY* of the options.
194
217
 
195
218
  ## Available Conditions
196
219
 
@@ -223,16 +246,6 @@ Some of these conditions come with aliases, so you have your choice how to call
223
246
 
224
247
  You will notice above there is "contains" and "keywords". The difference is that "keywords" is an enhanced search. It acts like a real keyword search. It finds those keywords, in any order, and blacklists meaningless words such as "and", "the", etc. "contains" finds the EXACT string in the column you are searching, spaces and all.
225
248
 
226
- ## Creating your search form
227
-
228
- After all of that, here's why I love this plugin:
229
-
230
- <% form_for :search, User.new_conditions, :url => users_path do |f| %>
231
- <%= f.text_field :first_name_contains %>
232
- <%= f.calendar_date_select :created_after %>
233
- <%= f.select :age_gt, (1..100) %>
234
- <% end %>
235
-
236
249
  ## Credits
237
250
 
238
251
  Author: [Ben Johnson](http://github.com/binarylogic) of [Binary Logic](http://www.binarylogic.com)
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ Echoe.new 'searchgasm' do |p|
11
11
  p.summary = "Orgasmic ActiveRecord searching"
12
12
  p.description = "Makes ActiveRecord searching easier, robust, and powerful. Automatic conditions, pagination support, object based searching, and more."
13
13
  p.url = "http://github.com/binarylogic/searchgasm"
14
- p.dependencies = %w(activerecord)
14
+ p.dependencies = %w(activerecord activesupport)
15
15
  p.include_rakefile = true
16
16
  end
17
17
 
@@ -3,12 +3,6 @@ module BinaryLogic
3
3
  module ActiveRecord
4
4
  module Associations
5
5
  module AssociationCollection
6
- def self.included(klass)
7
- klass.class_eval do
8
- include Protection
9
- end
10
- end
11
-
12
6
  def find_with_searchgasm(*args)
13
7
  options = args.extract_options!
14
8
  args << sanitize_options_with_searchgasm(options)
@@ -16,11 +10,27 @@ module BinaryLogic
16
10
  end
17
11
 
18
12
  def build_conditions(options = {}, &block)
19
- @reflection.klass.build_conditions(options.merge(:scope => scope(:find)[:conditions]), &block)
13
+ conditions = @reflection.klass.build_conditions(options, &block)
14
+ conditions.scope = scope(:find)[:conditions]
15
+ conditions
16
+ end
17
+
18
+ def build_conditions!(options = {}, &block)
19
+ conditions = @reflection.klass.build_conditions!(options, &block)
20
+ conditions.scope = scope(:find)[:conditions]
21
+ conditions
20
22
  end
21
23
 
22
24
  def build_search(options = {}, &block)
23
- @reflection.klass.build_search(options.merge(:scope => scope(:find)[:conditions]), &block)
25
+ conditions = @reflection.klass.build_search(options, &block)
26
+ conditions.scope = scope(:find)[:conditions]
27
+ conditions
28
+ end
29
+
30
+ def build_search!(options = {}, &block)
31
+ conditions = @reflection.klass.build_search!(options, &block)
32
+ conditions.scope = scope(:find)[:conditions]
33
+ conditions
24
34
  end
25
35
  end
26
36
 
@@ -2,49 +2,62 @@ module BinaryLogic
2
2
  module Searchgasm
3
3
  module ActiveRecord
4
4
  module Base
5
- def self.included(klass)
6
- klass.class_eval do
7
- alias_method :new_conditions, :build_conditions
8
- alias_method :new_search, :build_search
9
- include Protection
10
- end
11
- end
12
-
13
5
  def calculate_with_searchgasm(*args)
14
6
  options = args.extract_options!
15
7
  options = sanitize_options_with_searchgasm(options)
16
8
  args << options
17
9
  calculate_without_searchgasm(*args)
18
10
  end
19
-
11
+
20
12
  def find_with_searchgasm(*args)
21
13
  options = args.extract_options!
22
14
  options = sanitize_options_with_searchgasm(options)
23
15
  args << options
24
16
  find_without_searchgasm(*args)
25
17
  end
26
-
18
+
27
19
  def scope_with_searchgasm(method, key = nil)
28
20
  scope = scope_without_searchgasm(method, key)
29
- return sanitize_options_with_searchgasm(scope) if key.nil? && method == :find
21
+ return sanitize_options_with_searchgasm(scope) if key.nil? && method == :find && !scope.blank?
30
22
  scope
31
23
  end
32
-
33
- def build_conditions(options = {})
34
- BinaryLogic::Searchgasm::Search::Conditions(self, options)
24
+
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
35
31
  end
36
-
37
- def build_search(options = {})
38
- searcher = searchgasm_searcher(options)
39
- yield searcher if block_given?
40
- searcher
32
+
33
+ def build_conditions!(values = {}, &block)
34
+ conditions = searchgasm_conditions(values)
35
+ yield conditions if block_given?
36
+ conditions
41
37
  end
42
-
38
+
39
+ def build_search(options = {}, &block)
40
+ options[:protect] = true
41
+ search = searchgasm_searcher(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
51
+
43
52
  private
44
53
  def sanitize_options_with_searchgasm(options)
45
54
  searchgasm_searcher(options).sanitize
46
55
  end
47
-
56
+
57
+ def searchgasm_conditions(options)
58
+ BinaryLogic::Searchgasm::Search::Conditions.new(self, options)
59
+ end
60
+
48
61
  def searchgasm_searcher(options)
49
62
  BinaryLogic::Searchgasm::Search::Base.new(self, options)
50
63
  end
@@ -61,6 +74,10 @@ module ::ActiveRecord
61
74
  alias_method_chain :calculate, :searchgasm
62
75
  alias_method_chain :find, :searchgasm
63
76
  alias_method_chain :scope, :searchgasm
77
+ alias_method :new_conditions, :build_conditions
78
+ alias_method :new_conditions!, :build_conditions!
79
+ alias_method :new_search, :build_search
80
+ alias_method :new_search!, :build_search!
64
81
 
65
82
  def valid_find_options
66
83
  VALID_FIND_OPTIONS
@@ -1,14 +1,16 @@
1
1
  module BinaryLogic
2
2
  module Searchgasm
3
- module Search
3
+ module Search
4
4
  class Base
5
5
  include Utilities
6
6
 
7
7
  SPECIAL_FIND_OPTIONS = [:order_by, :order_as, :page, :per_page]
8
8
  VALID_FIND_OPTIONS = ::ActiveRecord::Base.valid_find_options + SPECIAL_FIND_OPTIONS
9
+ SAFE_OPTIONS = SPECIAL_FIND_OPTIONS + [:conditions, :limit, :offset]
10
+ VULNERABLE_OPTIONS = VALID_FIND_OPTIONS - SAFE_OPTIONS
9
11
 
10
12
  attr_accessor :klass
11
- attr_reader :conditions
13
+ attr_reader :conditions, :protect
12
14
  attr_writer :options
13
15
 
14
16
  def initialize(klass, options = {})
@@ -18,10 +20,12 @@ module BinaryLogic
18
20
  end
19
21
 
20
22
  (::ActiveRecord::Base.valid_find_options - [:conditions]).each do |option|
21
- class_eval <<-SRC
23
+ src = <<-SRC
22
24
  def #{option}(sanitize = false); options[:#{option}]; end
23
25
  def #{option}=(value); self.options[:#{option}] = value; end
24
26
  SRC
27
+
28
+ class_eval src
25
29
  end
26
30
 
27
31
  alias_method :per_page, :limit
@@ -61,6 +65,11 @@ module BinaryLogic
61
65
  includes.blank? ? nil : (includes.size == 1 ? includes.first : includes)
62
66
  end
63
67
 
68
+ def inspect
69
+ options_as_nice_string = ::ActiveRecord::Base.valid_find_options.collect { |name| "#{name}: #{send(name)}" }.join(", ")
70
+ "#<#{klass} #{options_as_nice_string}>"
71
+ end
72
+
64
73
  def limit=(value)
65
74
  return options[:limit] = nil if value.nil? || value == 0
66
75
 
@@ -75,9 +84,12 @@ module BinaryLogic
75
84
  @options ||= {}
76
85
  end
77
86
 
78
- def options=(options)
79
- return unless options.is_a?(Hash)
80
- options.each { |option, value| send("#{option}=", value) }
87
+ def options=(values)
88
+ return unless values.is_a?(Hash)
89
+ self.protect = values.delete(:protect) if values.has_key?(:protect) # make sure we do this first
90
+ values.symbolize_keys.assert_valid_keys(VALID_FIND_OPTIONS)
91
+ frisk!(values) if protect?
92
+ values.each { |option, value| send("#{option}=", value) }
81
93
  end
82
94
 
83
95
  def order_as
@@ -114,6 +126,15 @@ module BinaryLogic
114
126
  value
115
127
  end
116
128
 
129
+ def protect=(value)
130
+ conditions.protect = value
131
+ @protect = value
132
+ end
133
+
134
+ def protect?
135
+ protect == true
136
+ end
137
+
117
138
  def reset!
118
139
  conditions.reset!
119
140
  self.options = {}
@@ -136,6 +157,31 @@ module BinaryLogic
136
157
  def scope=(value)
137
158
  conditions.scope = value
138
159
  end
160
+
161
+ private
162
+ def order_by_safe?(order_by)
163
+ return true if order_by.blank?
164
+
165
+ column_names = klass.column_names
166
+
167
+ [order_by].flatten.each do |column|
168
+ case column
169
+ when Hash
170
+ return false unless order_by_safe?(column.to_a)
171
+ when Array
172
+ return false unless order_by_safe?(column)
173
+ else
174
+ return false unless column_names.include?(column)
175
+ end
176
+ end
177
+
178
+ true
179
+ end
180
+
181
+ def frisk!(options)
182
+ options.symbolize_keys.assert_valid_keys(SAFE_OPTIONS)
183
+ raise(ArgumentError, ":order_by can only contain colum names in the string, hash, or array format") unless order_by_safe?(options[:order_by])
184
+ end
139
185
  end
140
186
  end
141
187
  end
@@ -4,7 +4,7 @@ module BinaryLogic
4
4
  class Conditions
5
5
  include Utilities
6
6
 
7
- attr_accessor :klass, :relationship_name, :scope
7
+ attr_accessor :klass, :protect, :relationship_name, :scope
8
8
 
9
9
  class << self
10
10
  def condition_types_for_column_type(type)
@@ -55,17 +55,27 @@ module BinaryLogic
55
55
  end
56
56
  end
57
57
 
58
- def initialize(klass, values = {})
58
+ def initialize(klass, init_values = {})
59
59
  self.klass = klass
60
60
  klass.columns.each { |column| add_conditions_for_column!(column) }
61
61
  klass.reflect_on_all_associations.each { |association| add_association!(association) }
62
- self.value = values
62
+ self.value = init_values
63
+ end
64
+
65
+ def assert_valid_values(values)
66
+ keys = condition_names.collect { |condition_name| condition_name.to_sym }
67
+ keys += klass.reflect_on_all_associations.collect { |association| association.name }
68
+ values.symbolize_keys.assert_valid_keys(keys)
63
69
  end
64
70
 
65
71
  def associations
66
72
  objects.select { |object| object.is_a?(self.class) }
67
73
  end
68
74
 
75
+ def condition_names
76
+ @condition_names ||= []
77
+ end
78
+
69
79
  def includes
70
80
  i = []
71
81
  associations.each do |association|
@@ -79,6 +89,10 @@ module BinaryLogic
79
89
  @objects ||= []
80
90
  end
81
91
 
92
+ def protect?
93
+ protect == true
94
+ end
95
+
82
96
  def reset!
83
97
  dupped_objects = objects.dup
84
98
  dupped_objects.each do |object|
@@ -97,15 +111,17 @@ module BinaryLogic
97
111
  merge_conditions(sanitized_objects, scope)
98
112
  end
99
113
 
100
- def value=(conditions)
114
+ def value=(values)
101
115
  reset!
102
116
  self.scope = nil
103
117
 
104
- case conditions
118
+ case values
105
119
  when Hash
106
- conditions.each { |condition, value| send("#{condition}=", value) }
120
+ assert_valid_values(values)
121
+ values.each { |condition, value| send("#{condition}=", value) }
107
122
  else
108
- self.scope = conditions
123
+ raise(ArgumentError, "You can not set a scope or pass SQL while the search is being protected") if protect?
124
+ self.scope = values
109
125
  end
110
126
  end
111
127
 
@@ -143,7 +159,8 @@ module BinaryLogic
143
159
 
144
160
  def add_condition!(condition_type, column)
145
161
  name = Condition.generate_name(column, condition_type)
146
-
162
+ self.condition_names << name
163
+
147
164
  # Define accessor methods
148
165
  self.class.class_eval <<-SRC
149
166
  def #{name}_object
@@ -161,6 +178,7 @@ module BinaryLogic
161
178
 
162
179
  # Define aliases
163
180
  self.class.aliases_for_condition(column, condition_type).each do |alias_name|
181
+ self.condition_names << alias_name
164
182
  self.class.class_eval do
165
183
  alias_method alias_name, name
166
184
  alias_method "#{alias_name}=", "#{name}="
@@ -68,7 +68,7 @@ module BinaryLogic
68
68
 
69
69
  MAJOR = 0
70
70
  MINOR = 9
71
- TINY = 1
71
+ TINY = 2
72
72
 
73
73
  # The current version as a Version instance
74
74
  CURRENT = new(MAJOR, MINOR, TINY)
data/lib/searchgasm.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require "active_record"
2
- require "searchgasm/active_record/protection"
3
2
  require "searchgasm/active_record/base"
4
3
  require "searchgasm/active_record/associations"
5
4
  require "searchgasm/version"
data/searchgasm.gemspec CHANGED
@@ -1,18 +1,18 @@
1
1
 
2
- # Gem::Specification for Searchgasm-0.9.1
2
+ # Gem::Specification for Searchgasm-0.9.2
3
3
  # Originally generated by Echoe
4
4
 
5
5
  --- !ruby/object:Gem::Specification
6
6
  name: searchgasm
7
7
  version: !ruby/object:Gem::Version
8
- version: 0.9.1
8
+ version: 0.9.2
9
9
  platform: ruby
10
10
  authors:
11
11
  - Ben Johnson of Binary Logic
12
12
  autorequire:
13
13
  bindir: bin
14
14
 
15
- date: 2008-09-01 00:00:00 -04:00
15
+ date: 2008-09-02 00:00:00 -04:00
16
16
  default_executable:
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
@@ -25,6 +25,16 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: "0"
27
27
  version:
28
+ - !ruby/object:Gem::Dependency
29
+ name: activesupport
30
+ type: :runtime
31
+ version_requirement:
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: "0"
37
+ version:
28
38
  - !ruby/object:Gem::Dependency
29
39
  name: echoe
30
40
  type: :development
@@ -45,7 +55,6 @@ extra_rdoc_files:
45
55
  - CHANGELOG
46
56
  - lib/searchgasm/active_record/associations.rb
47
57
  - lib/searchgasm/active_record/base.rb
48
- - lib/searchgasm/active_record/protection.rb
49
58
  - lib/searchgasm/helpers.rb
50
59
  - lib/searchgasm/search/base.rb
51
60
  - lib/searchgasm/search/condition.rb
@@ -59,7 +68,6 @@ files:
59
68
  - init.rb
60
69
  - lib/searchgasm/active_record/associations.rb
61
70
  - lib/searchgasm/active_record/base.rb
62
- - lib/searchgasm/active_record/protection.rb
63
71
  - lib/searchgasm/helpers.rb
64
72
  - lib/searchgasm/search/base.rb
65
73
  - lib/searchgasm/search/condition.rb
@@ -12,6 +12,19 @@ class TestActiveRecordBase < Test::Unit::TestCase
12
12
  teardown_db
13
13
  end
14
14
 
15
+ def test_standard_searches
16
+ assert_nothing_raised { Account.all }
17
+ assert_nothing_raised { Account.first }
18
+ assert_nothing_raised { Account.find(:all) }
19
+ assert_nothing_raised { Account.find(:first) }
20
+ assert_nothing_raised { Account.find(:all, nil) }
21
+ assert_nothing_raised { Account.find(:all, {}) }
22
+ assert_nothing_raised { Account.count({}) }
23
+ assert_nothing_raised { Account.count(nil) }
24
+ assert_nothing_raised { Account.sum("id") }
25
+ assert_nothing_raised { Account.sum("id", {}) }
26
+ end
27
+
15
28
  def test_valid_find_options
16
29
  assert_equal [ :conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group, :from, :lock ], ActiveRecord::Base.valid_find_options
17
30
  end
@@ -23,6 +36,9 @@ class TestActiveRecordBase < Test::Unit::TestCase
23
36
  assert_equal "awesome", search.conditions.name_keywords
24
37
  assert_equal 2, search.page
25
38
  assert_equal 15, search.per_page
39
+
40
+ search = Account.new_search(:conditions => {:name_keywords => "awesome"}, :page => 2, :per_page => 15)
41
+ assert_equal Account, search.klass
26
42
  end
27
43
 
28
44
  def test_searching
@@ -43,6 +59,6 @@ class TestActiveRecordBase < Test::Unit::TestCase
43
59
  end
44
60
 
45
61
  def test_scoping
46
- assert_equal({}, Account.send(:scope, :find))
62
+ assert_equal nil, Account.send(:scope, :find)
47
63
  end
48
64
  end
@@ -113,4 +113,20 @@ class TestSearchgasmBase < Test::Unit::TestCase
113
113
  assert_equal search.first, Account.find(1)
114
114
  assert_equal search.find(:first), Account.find(1)
115
115
  end
116
+
117
+ def test_protection
118
+ assert_raise(ArgumentError) { Account.build_search(:conditions => "(DELETE FROM users)", :page => 2, :per_page => 15) }
119
+ BinaryLogic::Searchgasm::Search::Base::VULNERABLE_OPTIONS.each { |option| assert_raise(ArgumentError) { Account.build_search(option => "(DELETE FROM users)") } }
120
+
121
+ assert_nothing_raised { Account.build_search!(:conditions => "(DELETE FROM users)", :page => 2, :per_page => 15) }
122
+ BinaryLogic::Searchgasm::Search::Base::VULNERABLE_OPTIONS.each { |option| assert_nothing_raised { Account.build_search!(option => "(DELETE FROM users)") } }
123
+
124
+ account = Account.first
125
+
126
+ assert_raise(ArgumentError) { account.users.build_search(:conditions => "(DELETE FROM users)", :page => 2, :per_page => 15) }
127
+ BinaryLogic::Searchgasm::Search::Base::VULNERABLE_OPTIONS.each { |option| assert_raise(ArgumentError) { account.users.build_search(option => "(DELETE FROM users)") } }
128
+
129
+ assert_nothing_raised { account.users.build_search!(:conditions => "(DELETE FROM users)", :page => 2, :per_page => 15) }
130
+ BinaryLogic::Searchgasm::Search::Base::VULNERABLE_OPTIONS.each { |option| assert_nothing_raised { account.users.build_search!(option => "(DELETE FROM users)") } }
131
+ end
116
132
  end
@@ -118,6 +118,16 @@ class TestSearchgasmConditions < Test::Unit::TestCase
118
118
  assert_equal conditions.scope, "id in (1,2,3,4)"
119
119
  end
120
120
 
121
+ def test_protection
122
+ assert_raise(ArgumentError) { Account.new_conditions("(DELETE FROM users)") }
123
+ assert_nothing_raised { Account.build_conditions!("(DELETE FROM users)") }
124
+
125
+ account = Account.first
126
+
127
+ assert_raise(ArgumentError) { account.users.build_conditions("(DELETE FROM users)") }
128
+ assert_nothing_raised { account.users.build_conditions!("(DELETE FROM users)") }
129
+ end
130
+
121
131
  private
122
132
  def column_value(column)
123
133
  case column.type
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: searchgasm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Johnson of Binary Logic
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-09-01 00:00:00 -04:00
12
+ date: 2008-09-02 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,6 +22,16 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: "0"
24
24
  version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: activesupport
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
25
35
  - !ruby/object:Gem::Dependency
26
36
  name: echoe
27
37
  type: :development
@@ -42,7 +52,6 @@ extra_rdoc_files:
42
52
  - CHANGELOG
43
53
  - lib/searchgasm/active_record/associations.rb
44
54
  - lib/searchgasm/active_record/base.rb
45
- - lib/searchgasm/active_record/protection.rb
46
55
  - lib/searchgasm/helpers.rb
47
56
  - lib/searchgasm/search/base.rb
48
57
  - lib/searchgasm/search/condition.rb
@@ -56,7 +65,6 @@ files:
56
65
  - init.rb
57
66
  - lib/searchgasm/active_record/associations.rb
58
67
  - lib/searchgasm/active_record/base.rb
59
- - lib/searchgasm/active_record/protection.rb
60
68
  - lib/searchgasm/helpers.rb
61
69
  - lib/searchgasm/search/base.rb
62
70
  - lib/searchgasm/search/condition.rb
@@ -1,37 +0,0 @@
1
- module BinaryLogic
2
- module Searchgasm
3
- module ActiveRecord
4
- module Protection
5
- def self.included(klass)
6
- klass.class_eval do
7
- alias_method :new_search, :build_search
8
- alias_method :findwp, :find_with_protection
9
- alias_method :allwp, :all_with_protection
10
- alias_method :firstwp, :first_with_protection
11
- end
12
- end
13
-
14
- def find_with_protection(*args)
15
- options = args.extract_options!
16
- options.merge!(:protect => true)
17
- args << options
18
- find(*args)
19
- end
20
-
21
- def all_with_protection(*args)
22
- options = args.extract_options!
23
- options.merge!(:protect => true)
24
- args << options
25
- all(*args)
26
- end
27
-
28
- def first_with_protection(*args)
29
- options = args.extract_options!
30
- options.merge!(:protect => true)
31
- args << options
32
- first(*args)
33
- end
34
- end
35
- end
36
- end
37
- end