scoped_search 2.0.1 → 2.2.0

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.
data/.gitignore CHANGED
@@ -6,3 +6,4 @@ scoped_search-*.gem
6
6
  /coverage
7
7
  /classes
8
8
  /files
9
+ /spec/database.yml
data/README.rdoc CHANGED
@@ -10,20 +10,37 @@ and build a query based on the search string they enter. If you want to build a
10
10
  more complex search form with multiple fields, searchlogic (see
11
11
  http://github.com/binarylogic/searchlogic) may be a good choice for you.
12
12
 
13
+ === Usage
14
+
15
+ If you include a :profile parameter in scoped_search, the fields specified will only
16
+ be search when you pass :profile into the search_for command:
17
+
18
+ class User < ActiveRecord::Base
19
+ scoped_search :on => :public_information
20
+ scoped_search :on => :private_information, :profile => :members
21
+ end
22
+
23
+ This will only search the :public_information column:
24
+
25
+ User.search_for('blah blah blah')
26
+
27
+ And this will only search the :private_information column:
28
+
29
+ User.search_for('blah blah blah', :profile => :members)
13
30
 
14
31
  == Installing
15
32
 
16
- The recommended method to enable scoped_search in your project is adding the scoped_search gem to your environment. Add the following code to your Rails configuration in <tt>config/environment.rb</tt>:
33
+ The recommended method to enable scoped search in your project is adding the
34
+ scoped_search gem to your environment. Add the following code to your Rails
35
+ configuration in <tt>config/environment.rb</tt>, and run <tt>rake
36
+ gems:install</tt> to install the gem.:
17
37
 
18
38
  Rails::Initializer.run do |config|
19
39
  ...
20
- config.gem 'wvanbergen-scoped_search', :lib => 'scoped_search',
21
- :source => 'http://gems.github.com/'
40
+ config.gem 'scoped_search'
22
41
  end
23
42
 
24
- Run <tt>sudo rake gems:install</tt> to install the gem.
25
-
26
- Alternatively, install scoped_search as a Rails plugin:
43
+ Alternatively, install scoped search as a Rails plugin (*deprecated*):
27
44
 
28
45
  script/plugin install git://github.com/wvanbergen/scoped_search.git
29
46
 
@@ -95,9 +112,9 @@ For more info, see the the project wiki: http://wiki.github.com/wvanbergen/scope
95
112
 
96
113
  == Additional resources
97
114
 
98
- * Source code: http://github.com/wvanbergen/scoped_search/tree/master
115
+ * Source code: http://github.com/wvanbergen/scoped_search/tree
99
116
  * Project wiki: http://wiki.github.com/wvanbergen/scoped_search
100
- * RDoc documentation: http://wvanbergen.github.com/scoped_search
117
+ * RDoc documentation: http://rdoc.info/projects/wvanbergen/scoped_search
101
118
  * wvanbergen's blog posts: http://techblog.floorplanner.com/tag/scoped_search
102
119
 
103
120
  == License
data/lib/scoped_search.rb CHANGED
@@ -12,7 +12,7 @@ module ScopedSearch
12
12
 
13
13
  # The current scoped_search version. Do not change thisvalue by hand,
14
14
  # because it will be updated automatically by the gem release script.
15
- VERSION = "2.0.1"
15
+ VERSION = "2.2.0"
16
16
 
17
17
  # The ClassMethods module will be included into the ActiveRecord::Base class
18
18
  # to add the <tt>ActiveRecord::Base.scoped_search</tt> method and the
@@ -74,6 +74,8 @@ module ScopedSearch
74
74
  # scoped_search call on the ActiveRecord-based model class.
75
75
  def initialize(definition, options = {})
76
76
  @definition = definition
77
+ @definition.profile = options[:profile] if options[:profile]
78
+
77
79
  case options
78
80
  when Symbol, String
79
81
  @field = field.to_sym
@@ -96,18 +98,32 @@ module ScopedSearch
96
98
  end
97
99
  end
98
100
 
99
- attr_reader :klass, :fields, :unique_fields
101
+ attr_reader :klass
100
102
 
101
103
  # Initializes a ScopedSearch definition instance.
102
104
  # This method will also setup a database adapter and create the :search_for
103
105
  # named scope if it does not yet exist.
104
106
  def initialize(klass)
105
- @klass = klass
106
- @fields = {}
107
- @unique_fields = []
107
+ @klass = klass
108
+ @fields = {}
109
+ @unique_fields = []
110
+ @profile_fields = {:default => {}}
111
+ @profile_unique_fields = {:default => []}
108
112
 
109
113
  register_named_scope! unless klass.respond_to?(:search_for)
110
114
  end
115
+
116
+ attr_accessor :profile
117
+
118
+ def fields
119
+ @profile ||= :default
120
+ @profile_fields[@profile] ||= {}
121
+ end
122
+
123
+ def unique_fields
124
+ @profile ||= :default
125
+ @profile_unique_fields[@profile] ||= []
126
+ end
111
127
 
112
128
  NUMERICAL_REGXP = /^\-?\d+(\.\d+)?$/
113
129
 
@@ -139,7 +155,24 @@ module ScopedSearch
139
155
 
140
156
  # Registers the search_for named scope within the class that is used for searching.
141
157
  def register_named_scope! # :nodoc
142
- @klass.named_scope(:search_for, lambda { |*args| ScopedSearch::QueryBuilder.build_query(args[1] || self, args[0]) })
158
+ if @klass.ancestors.include?(ActiveRecord::Base)
159
+ case ActiveRecord::VERSION::MAJOR
160
+ when 2
161
+ @klass.named_scope(:search_for, lambda { |*args| ScopedSearch::QueryBuilder.build_query(self, args[0], args[1]) })
162
+ when 3
163
+ @klass.scope(:search_for, lambda { |*args|
164
+ find_options = ScopedSearch::QueryBuilder.build_query(self, args[0], args[1])
165
+ search_scope = @klass.scoped
166
+ search_scope = search_scope.where(find_options[:conditions]) if find_options[:conditions]
167
+ search_scope = search_scope.includes(find_options[:include]) if find_options[:include]
168
+ search_scope
169
+ })
170
+ else
171
+ raise "This ActiveRecord version is currently not supported!"
172
+ end
173
+ else
174
+ raise "Currently, only ActiveRecord 2.1 or higher is supported!"
175
+ end
143
176
  end
144
177
  end
145
178
  end
@@ -14,12 +14,15 @@ module ScopedSearch
14
14
  # This method will parse the query string and build an SQL query using the search
15
15
  # query. It will return an ampty hash if the search query is empty, in which case
16
16
  # the scope call will simply return all records.
17
- def self.build_query(definition, query)
17
+ def self.build_query(definition, *args)
18
+ query = args[0]
19
+ options = args[1] || {}
20
+
18
21
  query_builder_class = self.class_for(definition)
19
22
  if query.kind_of?(ScopedSearch::QueryLanguage::AST::Node)
20
- return query_builder_class.new(definition, query).build_find_params
23
+ return query_builder_class.new(definition, query, options[:profile]).build_find_params
21
24
  elsif query.kind_of?(String)
22
- return query_builder_class.new(definition, ScopedSearch::QueryLanguage::Compiler.parse(query)).build_find_params
25
+ return query_builder_class.new(definition, ScopedSearch::QueryLanguage::Compiler.parse(query), options[:profile]).build_find_params
23
26
  elsif query.nil?
24
27
  return { }
25
28
  else
@@ -36,8 +39,8 @@ module ScopedSearch
36
39
  end
37
40
 
38
41
  # Initializes the instance by setting the relevant parameters
39
- def initialize(definition, ast)
40
- @definition, @ast = definition, ast
42
+ def initialize(definition, ast, profile)
43
+ @definition, @ast, @definition.profile = definition, ast, profile
41
44
  end
42
45
 
43
46
  # Actually builds the find parameters hash that should be used in the search_for
@@ -63,7 +66,7 @@ module ScopedSearch
63
66
  find_attributes = {}
64
67
  find_attributes[:conditions] = [sql] + parameters unless sql.nil?
65
68
  find_attributes[:include] = includes.uniq unless includes.empty?
66
- find_attributes # Uncomment for debugging
69
+ # p find_attributes # Uncomment for debugging
67
70
  return find_attributes
68
71
  end
69
72
 
@@ -107,7 +110,7 @@ module ScopedSearch
107
110
  # fall inside/outside the range of timestamps of that day.
108
111
  yield(:parameter, timestamp)
109
112
  yield(:parameter, timestamp + 1)
110
- negate = (operator == :ne) ? 'NOT' : ''
113
+ negate = (operator == :ne) ? 'NOT ' : ''
111
114
  field_sql = field.to_sql(operator, &block)
112
115
  return "#{negate}(#{field_sql} >= ? AND #{field_sql} < ?)"
113
116
 
@@ -165,10 +168,10 @@ module ScopedSearch
165
168
  # This function may yield an :include that should be used in the
166
169
  # ActiveRecord::Base#find call, to make sure that the field is avalable
167
170
  # for the SQL query.
168
- def to_sql(builder, operator = nil, &block) # :yields: finder_option_type, value
171
+ def to_sql(operator = nil, &block) # :yields: finder_option_type, value
169
172
  yield(:include, relation) if relation
170
- definition.klass.connection.quote_table_name(klass.table_name) + "." +
171
- definition.klass.connection.quote_column_name(field)
173
+ definition.klass.connection.quote_table_name(klass.table_name.to_s) + "." +
174
+ definition.klass.connection.quote_column_name(field.to_s)
172
175
  end
173
176
  end
174
177
 
@@ -182,16 +185,21 @@ module ScopedSearch
182
185
  fragments = definition.default_fields_for(value).map do |field|
183
186
  builder.sql_test(field, field.default_operator, value, &block)
184
187
  end
185
- "(#{fragments.join(' OR ')})"
188
+
189
+ case fragments.length
190
+ when 0 then nil
191
+ when 1 then fragments.first
192
+ else "#{fragments.join(' OR ')}"
193
+ end
186
194
  end
187
195
  end
188
196
 
189
197
  # Defines the to_sql method for AST operator nodes
190
198
  module OperatorNode
191
199
 
192
- # Returns a NOT(...) SQL fragment that negates the current AST node's children
200
+ # Returns a NOT (...) SQL fragment that negates the current AST node's children
193
201
  def to_not_sql(builder, definition, &block)
194
- "(NOT(#{rhs.to_sql(builder, definition, &block)}) OR #{rhs.to_sql(builder, definition, &block)} IS NULL)"
202
+ "NOT COALESCE(#{rhs.to_sql(builder, definition, &block)}, 0)"
195
203
  end
196
204
 
197
205
  # Returns an IS (NOT) NULL SQL fragment
@@ -213,7 +221,11 @@ module ScopedSearch
213
221
  fragments = definition.default_fields_for(rhs.value, operator).map { |field|
214
222
  builder.sql_test(field, operator, rhs.value, &block) }.compact
215
223
 
216
- fragments.empty? ? nil : "(#{fragments.join(' OR ')})"
224
+ case fragments.length
225
+ when 0 then nil
226
+ when 1 then fragments.first
227
+ else "#{fragments.join(' OR ')}"
228
+ end
217
229
  end
218
230
 
219
231
  # Explicit field name given, run the operator on the specified field only
@@ -246,8 +258,8 @@ module ScopedSearch
246
258
  # Defines the to_sql method for AST AND/OR operators
247
259
  module LogicalOperatorNode
248
260
  def to_sql(builder, definition, &block)
249
- fragments = children.map { |c| c.to_sql(builder, definition, &block) }.compact
250
- fragments.empty? ? nil : "(#{fragments.join(" #{operator.to_s.upcase} ")})"
261
+ fragments = children.map { |c| c.to_sql(builder, definition, &block) }.compact.map { |sql| "(#{sql})" }
262
+ fragments.empty? ? nil : "#{fragments.join(" #{operator.to_s.upcase} ")}"
251
263
  end
252
264
  end
253
265
  end
@@ -284,6 +296,19 @@ module ScopedSearch
284
296
  end
285
297
  end
286
298
  end
299
+
300
+ # The Oracle adapter also requires some tweaks to make the case insensitive LIKE work.
301
+ class OracleEnhancedAdapter < ScopedSearch::QueryBuilder
302
+
303
+ def sql_test(field, operator, value, &block) # :yields: finder_option_type, value
304
+ if field.textual? && [:like, :unlike].include?(operator)
305
+ yield(:parameter, (value !~ /^\%/ && value !~ /\%$/) ? "%#{value}%" : value)
306
+ return "LOWER(#{field.to_sql(operator, &block)}) #{self.sql_operator(operator, field)} LOWER(?)"
307
+ else
308
+ return super(field, operator, value, &block)
309
+ end
310
+ end
311
+ end
287
312
  end
288
313
 
289
314
  # Include the modules into the corresponding classes
@@ -3,20 +3,20 @@ Gem::Specification.new do |s|
3
3
 
4
4
  # Do not change the version and date fields by hand. This will be done
5
5
  # automatically by the gem release script.
6
- s.version = "2.0.1"
7
- s.date = "2009-10-02"
6
+ s.version = "2.2.0"
7
+ s.date = "2010-05-26"
8
8
 
9
- s.summary = "A Rails plugin to search your models with a simple query language, implemented using a named_scope"
10
- s.description = <<EOS
9
+ s.summary = "Easily search you ActiveRecord models with a simple query language using a named scope."
10
+ s.description = <<-EOS
11
11
  Scoped search makes it easy to search your ActiveRecord-based models.
12
12
  It will create a named scope :search_for that can be called with a query string. It will build an SQL query using
13
13
  the provided query string and a definition that specifies on what fields to search. Because the functionality is
14
14
  built on named_scope, the result of the search_for call can be used like any other named_scope, so it can be
15
15
  chained with another scope or combined with will_paginate."
16
- EOS
16
+ EOS
17
17
 
18
18
  s.authors = ['Willem van Bergen', 'Wes Hays']
19
- s.email = ['willem@vanbergen.org', 'weshays@gbdev.com']
19
+ s.email = ['willem@railsdoctors.com', 'weshays@gbdev.com']
20
20
  s.homepage = 'http://wiki.github.com/wvanbergen/scoped_search'
21
21
 
22
22
  s.add_runtime_dependency('activerecord', '>= 2.1.0')
@@ -27,6 +27,6 @@ EOS
27
27
 
28
28
  # Do not change the files and test_files fields by hand. This will be done
29
29
  # automatically by the gem release script.
30
- s.files = %w(spec/spec_helper.rb spec/integration/string_querying_spec.rb spec/integration/relation_querying_spec.rb .gitignore spec/lib/mocks.rb scoped_search.gemspec lib/scoped_search/query_language/parser.rb LICENSE spec/lib/matchers.rb lib/scoped_search/definition.rb init.rb spec/unit/tokenizer_spec.rb spec/unit/parser_spec.rb spec/unit/ast_spec.rb lib/scoped_search/query_language/ast.rb spec/lib/database.rb Rakefile tasks/github-gem.rake spec/unit/query_builder_spec.rb lib/scoped_search/query_language.rb lib/scoped_search/query_builder.rb README.rdoc spec/unit/definition_spec.rb spec/database.yml spec/integration/api_spec.rb spec/integration/ordinal_querying_spec.rb lib/scoped_search/query_language/tokenizer.rb lib/scoped_search.rb)
31
- s.test_files = %w(spec/integration/string_querying_spec.rb spec/integration/relation_querying_spec.rb spec/unit/tokenizer_spec.rb spec/unit/parser_spec.rb spec/unit/ast_spec.rb spec/unit/query_builder_spec.rb spec/unit/definition_spec.rb spec/integration/api_spec.rb spec/integration/ordinal_querying_spec.rb)
30
+ s.files = %w(spec/spec_helper.rb spec/integration/string_querying_spec.rb lib/scoped_search/definition.rb spec/lib/mocks.rb scoped_search.gemspec lib/scoped_search/query_language/parser.rb spec/lib/matchers.rb .gitignore LICENSE spec/unit/ast_spec.rb spec/database.yml init.rb Rakefile spec/unit/tokenizer_spec.rb spec/unit/parser_spec.rb spec/integration/api_spec.rb lib/scoped_search/query_language/ast.rb lib/scoped_search/query_language.rb lib/scoped_search/query_builder.rb README.rdoc spec/unit/definition_spec.rb spec/lib/database.rb spec/integration/profile_querying_spec.rb tasks/github-gem.rake spec/unit/query_builder_spec.rb lib/scoped_search.rb spec/integration/relation_querying_spec.rb spec/integration/ordinal_querying_spec.rb lib/scoped_search/query_language/tokenizer.rb)
31
+ s.test_files = %w(spec/integration/string_querying_spec.rb spec/unit/ast_spec.rb spec/unit/tokenizer_spec.rb spec/unit/parser_spec.rb spec/integration/api_spec.rb spec/unit/definition_spec.rb spec/integration/profile_querying_spec.rb spec/unit/query_builder_spec.rb spec/integration/relation_querying_spec.rb spec/integration/ordinal_querying_spec.rb)
32
32
  end
data/spec/database.yml CHANGED
@@ -13,13 +13,13 @@ sqlite3:
13
13
  mysql:
14
14
  adapter: "mysql"
15
15
  host: "localhost"
16
- user: "root"
16
+ username: "root"
17
17
  password:
18
18
  database: "scoped_search_test"
19
19
 
20
- postgresql:
21
- adapter: "postgresql"
22
- host: "localhost"
23
- username: "scoped_search"
24
- password: "scoped_search"
25
- database: "scoped_search_test"
20
+ # postgresql:
21
+ # adapter: "postgresql"
22
+ # host: "localhost"
23
+ # username: "scoped_search"
24
+ # password: "scoped_search"
25
+ # database: "scoped_search_test"
@@ -44,8 +44,18 @@ describe ScopedSearch, "API" do
44
44
  @class.should respond_to(:search_for)
45
45
  end
46
46
 
47
- it "should return an ActiveRecord::NamedScope::Scope when :search_for is called" do
48
- @class.search_for('query').class.should eql(ActiveRecord::NamedScope::Scope)
47
+ if ActiveRecord::VERSION::MAJOR == 2
48
+
49
+ it "should return a ActiveRecord::NamedScope::Scope instance" do
50
+ @class.search_for('query').class.should eql(ActiveRecord::NamedScope::Scope)
51
+ end
52
+
53
+ elsif ActiveRecord::VERSION::MAJOR == 3
54
+
55
+ it "should return a ActiveRecord::Relation instance" do
56
+ @class.search_for('query').class.should eql(ActiveRecord::Relation)
57
+ end
58
+
49
59
  end
50
60
  end
51
61
 
@@ -0,0 +1,61 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ # These specs will run on all databases that are defined in the spec/database.yml file.
4
+ # Comment out any databases that you do not have available for testing purposes if needed.
5
+ ScopedSearch::Spec::Database.test_databases.each do |db|
6
+
7
+ describe ScopedSearch, "using a #{db} database" do
8
+
9
+ before(:all) do
10
+ ScopedSearch::Spec::Database.establish_named_connection(db)
11
+
12
+ @class = ScopedSearch::Spec::Database.create_model(:public => :string, :private => :string, :useless => :string) do |klass|
13
+ klass.scoped_search :on => :public
14
+ klass.scoped_search :on => :private, :profile => :private_profile
15
+ klass.scoped_search :on => :useless, :profile => :another_profile
16
+ end
17
+
18
+ @item1 = @class.create!(:public => 'foo', :private => 'bar', :useless => 'boo')
19
+ @item2 = @class.create!(:public => 'qwerty', :private => 'foo', :useless => 'cool')
20
+ @item3 = @class.create!(:public => 'asdf', :private => 'blargh', :useless => 'foo')
21
+ end
22
+
23
+ after(:all) do
24
+ ScopedSearch::Spec::Database.drop_model(@class)
25
+ ScopedSearch::Spec::Database.close_connection
26
+ end
27
+
28
+ context "searching without profile specified" do
29
+ before(:each) do
30
+ @results = @class.search_for('foo')
31
+ end
32
+
33
+ it "should find results on column specified" do
34
+ @results.should include(@item1)
35
+ end
36
+
37
+ it "should not find results on columns only specified with a given profile" do
38
+ @results.should_not include(@item2)
39
+ end
40
+ end
41
+
42
+ context "searching with profile specified" do
43
+ before(:each) do
44
+ @results = @class.search_for('foo', :profile => :private_profile)
45
+ end
46
+
47
+ # it "should find results on columns indexed w/o profile" do
48
+ # @results.should include(@item1)
49
+ # end
50
+
51
+ it "should find results on column indexed with specified profile" do
52
+ @results.should include(@item2)
53
+ end
54
+
55
+ it "should not find results on a column indexed with a different profile" do
56
+ @results.should_not include(@item3)
57
+ end
58
+ end
59
+
60
+ end
61
+ end
@@ -17,7 +17,7 @@ ScopedSearch::Spec::Database.test_databases.each do |db|
17
17
 
18
18
  @class.create!(:string => 'foo', :another => 'temp 1', :explicit => 'baz')
19
19
  @class.create!(:string => 'bar', :another => 'temp 2', :explicit => 'baz')
20
- @class.create!(:string => 'baz', :another => 'temp 3', :explicit => nil)
20
+ @class.create!(:string => 'baz', :another => nil, :explicit => nil)
21
21
  end
22
22
 
23
23
  after(:all) do
@@ -30,7 +30,7 @@ ScopedSearch::Spec::Database.test_databases.each do |db|
30
30
  @class.search_for('foo').should have(1).item
31
31
  end
32
32
 
33
- it "should find the opther two records using NOT with an exact string match" do
33
+ it "should find the other two records using NOT with an exact string match" do
34
34
  @class.search_for('-foo').should have(2).item
35
35
  end
36
36
 
@@ -133,11 +133,15 @@ ScopedSearch::Spec::Database.test_databases.each do |db|
133
133
  end
134
134
 
135
135
  it "should find a partial match when the like operator is given" do
136
- @class.search_for('~ temp').should have(3).item
136
+ @class.search_for('~ temp').should have(2).item
137
+ end
138
+
139
+ it "should find a negation of partial match when the like operator is give with an explicit NOT operator" do
140
+ @class.search_for('!(~ temp)').should have(1).item
137
141
  end
138
142
 
139
143
  it "should find a partial match when the like operator and the field name is given" do
140
- @class.search_for('another ~ temp').should have(3).item
144
+ @class.search_for('another ~ temp').should have(2).item
141
145
  end
142
146
  end
143
147
 
data/spec/lib/mocks.rb CHANGED
@@ -7,7 +7,9 @@ module ScopedSearch::Spec::Mocks
7
7
  def mock_activerecord_class
8
8
  ar_mock = mock('ActiveRecord::Base')
9
9
  ar_mock.stub!(:named_scope).with(:search_for, anything)
10
+ ar_mock.stub!(:scope).with(:search_for, anything)
10
11
  ar_mock.stub!(:connection).and_return(mock_database_connection)
12
+ ar_mock.stub!(:ancestors).and_return([ActiveRecord::Base])
11
13
  return ar_mock
12
14
  end
13
15
 
@@ -8,17 +8,35 @@ describe ScopedSearch::Definition do
8
8
  @definition.stub!(:setup_adapter)
9
9
  end
10
10
 
11
+
11
12
  describe '#initialize' do
12
13
 
13
- it "should create the named scope when" do
14
- @klass.should_receive(:named_scope).with(:search_for, instance_of(Proc))
15
- ScopedSearch::Definition.new(@klass)
16
- end
14
+ if ActiveRecord::VERSION::MAJOR == 2
15
+
16
+ it "should create the named scope when" do
17
+ @klass.should_receive(:named_scope).with(:search_for, instance_of(Proc))
18
+ ScopedSearch::Definition.new(@klass)
19
+ end
20
+
21
+ it "should not create the named scope if it already exists" do
22
+ @klass.stub!(:search_for)
23
+ @klass.should_not_receive(:named_scope)
24
+ ScopedSearch::Definition.new(@klass)
25
+ end
26
+
27
+ elsif ActiveRecord::VERSION::MAJOR == 3
28
+
29
+ it "should create the named scope when" do
30
+ @klass.should_receive(:scope).with(:search_for, instance_of(Proc))
31
+ ScopedSearch::Definition.new(@klass)
32
+ end
17
33
 
18
- it "should not create the named scope if it already exists" do
19
- @klass.stub!(:search_for)
20
- @klass.should_not_receive(:named_scope)
21
- ScopedSearch::Definition.new(@klass)
34
+ it "should not create the named scope if it already exists" do
35
+ @klass.stub!(:search_for)
36
+ @klass.should_not_receive(:scope)
37
+ ScopedSearch::Definition.new(@klass)
38
+ end
39
+
22
40
  end
23
41
  end
24
42
  end
@@ -5,6 +5,8 @@ describe ScopedSearch::QueryBuilder do
5
5
  before(:each) do
6
6
  @definition = mock('ScopedSearch::Definition')
7
7
  @definition.stub!(:klass).and_return(Class.new(ActiveRecord::Base))
8
+ @definition.stub!(:profile).and_return(:default)
9
+ @definition.stub!(:profile=).and_return(true)
8
10
  end
9
11
 
10
12
  it "should return empty conditions if the search query is nil" do
@@ -119,23 +119,43 @@ module GithubGem
119
119
  checks = [:check_current_branch, :check_clean_status, :check_not_diverged, :check_version]
120
120
  checks.unshift('spec:basic') if has_specs?
121
121
  checks.unshift('test:basic') if has_tests?
122
- checks.push << [:check_rubyforge] if gemspec.rubyforge_project
122
+ # checks.push << [:check_rubyforge] if gemspec.rubyforge_project
123
123
 
124
124
  desc "Perform all checks that would occur before a release"
125
125
  task(:release_checks => checks)
126
126
 
127
- release_tasks = [:release_checks, :set_version, :build, :github_release]
128
- release_tasks << [:rubyforge_release] if gemspec.rubyforge_project
127
+ release_tasks = [:release_checks, :set_version, :build, :github_release, :gemcutter_release]
128
+ # release_tasks << [:rubyforge_release] if gemspec.rubyforge_project
129
129
 
130
- desc "Release a new verison of the gem"
130
+ desc "Release a new version of the gem using the VERSION environment variable"
131
131
  task(:release => release_tasks) { release_task }
132
+
133
+ namespace(:release) do
134
+ desc "Release the next version of the gem, by incrementing the last version segment by 1"
135
+ task(:next => [:next_version] + release_tasks) { release_task }
132
136
 
133
- task(:check_rubyforge) { check_rubyforge_task }
134
- task(:rubyforge_release) { rubyforge_release_task }
137
+ desc "Release the next version of the gem, using a bump increment (0.0.1)"
138
+ task(:bump => [:next_bump_version] + release_tasks) { release_task }
139
+
140
+ desc "Release the next version of the gem, using a minor increment (0.1.0)"
141
+ task(:minor => [:next_minor_version] + release_tasks) { release_task }
142
+
143
+ desc "Release the next version of the gem, using a major increment (1.0.0)"
144
+ task(:major => [:next_major_version] + release_tasks) { release_task }
145
+ end
146
+
147
+ # task(:check_rubyforge) { check_rubyforge_task }
148
+ # task(:rubyforge_release) { rubyforge_release_task }
149
+ task(:gemcutter_release) { gemcutter_release_task }
135
150
  task(:github_release => [:commit_modified_files, :tag_version]) { github_release_task }
136
151
  task(:tag_version) { tag_version_task }
137
152
  task(:commit_modified_files) { commit_modified_files_task }
138
153
 
154
+ task(:next_version) { next_version_task }
155
+ task(:next_bump_version) { next_version_task(:bump) }
156
+ task(:next_minor_version) { next_version_task(:minor) }
157
+ task(:next_major_version) { next_version_task(:major) }
158
+
139
159
  desc "Updates the gem release tasks with the latest version on Github"
140
160
  task(:update_tasks) { update_tasks_task }
141
161
  end
@@ -159,6 +179,32 @@ module GithubGem
159
179
  sh "mv #{gemspec.name}-#{gemspec.version}.gem pkg/#{gemspec.name}-#{gemspec.version}.gem"
160
180
  end
161
181
 
182
+ def newest_version
183
+ git.tags.map { |tag| tag.name.split('-').last }.compact.map { |v| Gem::Version.new(v) }.max || Gem::Version.new('0.0.0')
184
+ end
185
+
186
+ def next_version(increment = nil)
187
+ next_version = newest_version.segments
188
+ increment_index = case increment
189
+ when :micro then 3
190
+ when :bump then 2
191
+ when :minor then 1
192
+ when :major then 0
193
+ else next_version.length - 1
194
+ end
195
+
196
+ next_version[increment_index] ||= 0
197
+ next_version[increment_index] = next_version[increment_index].succ
198
+ ((increment_index + 1)...next_version.length).each { |i| next_version[i] = 0 }
199
+
200
+ Gem::Version.new(next_version.join('.'))
201
+ end
202
+
203
+ def next_version_task(increment = nil)
204
+ ENV['VERSION'] = next_version(increment).version
205
+ puts "Releasing version #{ENV['VERSION']}..."
206
+ end
207
+
162
208
  # Updates the version number in the gemspec file, the VERSION constant in the main
163
209
  # include file and the contents of the VERSION file.
164
210
  def version_task
@@ -172,9 +218,7 @@ module GithubGem
172
218
  def check_version_task
173
219
  raise "#{ENV['VERSION']} is not a valid version number!" if ENV['VERSION'] && !Gem::Version.correct?(ENV['VERSION'])
174
220
  proposed_version = Gem::Version.new(ENV['VERSION'] || gemspec.version)
175
- # Loads the latest version number using the created tags
176
- newest_version = git.tags.map { |tag| tag.name.split('-').last }.compact.map { |v| Gem::Version.new(v) }.max
177
- raise "This version (#{proposed_version}) is not higher than the highest tagged version (#{newest_version})" if newest_version && newest_version >= proposed_version
221
+ raise "This version (#{proposed_version}) is not higher than the highest tagged version (#{newest_version})" if newest_version >= proposed_version
178
222
  end
179
223
 
180
224
  # Checks whether the current branch is not diverged from the remote branch
@@ -215,18 +259,22 @@ module GithubGem
215
259
  git.push(remote, remote_branch, true)
216
260
  end
217
261
 
218
- # Checks whether Rubyforge is configured properly
219
- def check_rubyforge_task
220
- # Login no longer necessary when using rubyforge 2.0.0 gem
221
- # raise "Could not login on rubyforge!" unless `rubyforge login 2>&1`.strip.empty?
222
- output = `rubyforge names`.split("\n")
223
- raise "Rubyforge group not found!" unless output.any? { |line| %r[^groups\s*\:.*\b#{Regexp.quote(gemspec.rubyforge_project)}\b.*] =~ line }
224
- raise "Rubyforge package not found!" unless output.any? { |line| %r[^packages\s*\:.*\b#{Regexp.quote(gemspec.name)}\b.*] =~ line }
225
- end
226
-
227
- # Task to release the .gem file toRubyforge.
228
- def rubyforge_release_task
229
- sh 'rubyforge', 'add_release', gemspec.rubyforge_project, gemspec.name, gemspec.version.to_s, "pkg/#{gemspec.name}-#{gemspec.version}.gem"
262
+ # # Checks whether Rubyforge is configured properly
263
+ # def check_rubyforge_task
264
+ # # Login no longer necessary when using rubyforge 2.0.0 gem
265
+ # # raise "Could not login on rubyforge!" unless `rubyforge login 2>&1`.strip.empty?
266
+ # output = `rubyforge names`.split("\n")
267
+ # raise "Rubyforge group not found!" unless output.any? { |line| %r[^groups\s*\:.*\b#{Regexp.quote(gemspec.rubyforge_project)}\b.*] =~ line }
268
+ # raise "Rubyforge package not found!" unless output.any? { |line| %r[^packages\s*\:.*\b#{Regexp.quote(gemspec.name)}\b.*] =~ line }
269
+ # end
270
+
271
+ # # Task to release the .gem file toRubyforge.
272
+ # def rubyforge_release_task
273
+ # sh 'rubyforge', 'add_release', gemspec.rubyforge_project, gemspec.name, gemspec.version.to_s, "pkg/#{gemspec.name}-#{gemspec.version}.gem"
274
+ # end
275
+
276
+ def gemcutter_release_task
277
+ sh "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
230
278
  end
231
279
 
232
280
  # Gem release task.
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scoped_search
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ prerelease: false
5
+ segments:
6
+ - 2
7
+ - 2
8
+ - 0
9
+ version: 2.2.0
5
10
  platform: ruby
6
11
  authors:
7
12
  - Willem van Bergen
@@ -10,32 +15,40 @@ autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
17
 
13
- date: 2009-10-02 00:00:00 +02:00
18
+ date: 2010-05-26 00:00:00 +02:00
14
19
  default_executable:
15
20
  dependencies:
16
21
  - !ruby/object:Gem::Dependency
17
22
  name: activerecord
18
- type: :runtime
19
- version_requirement:
20
- version_requirements: !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
21
25
  requirements:
22
26
  - - ">="
23
27
  - !ruby/object:Gem::Version
28
+ segments:
29
+ - 2
30
+ - 1
31
+ - 0
24
32
  version: 2.1.0
25
- version:
33
+ type: :runtime
34
+ version_requirements: *id001
26
35
  - !ruby/object:Gem::Dependency
27
36
  name: rspec
28
- type: :development
29
- version_requirement:
30
- version_requirements: !ruby/object:Gem::Requirement
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
31
39
  requirements:
32
40
  - - ">="
33
41
  - !ruby/object:Gem::Version
42
+ segments:
43
+ - 1
44
+ - 1
45
+ - 4
34
46
  version: 1.1.4
35
- version:
47
+ type: :development
48
+ version_requirements: *id002
36
49
  description: " Scoped search makes it easy to search your ActiveRecord-based models.\n It will create a named scope :search_for that can be called with a query string. It will build an SQL query using\n the provided query string and a definition that specifies on what fields to search. Because the functionality is\n built on named_scope, the result of the search_for call can be used like any other named_scope, so it can be\n chained with another scope or combined with will_paginate.\"\n"
37
50
  email:
38
- - willem@vanbergen.org
51
+ - willem@railsdoctors.com
39
52
  - weshays@gbdev.com
40
53
  executables: []
41
54
 
@@ -46,32 +59,33 @@ extra_rdoc_files:
46
59
  files:
47
60
  - spec/spec_helper.rb
48
61
  - spec/integration/string_querying_spec.rb
49
- - spec/integration/relation_querying_spec.rb
50
- - .gitignore
62
+ - lib/scoped_search/definition.rb
51
63
  - spec/lib/mocks.rb
52
64
  - scoped_search.gemspec
53
65
  - lib/scoped_search/query_language/parser.rb
54
- - LICENSE
55
66
  - spec/lib/matchers.rb
56
- - lib/scoped_search/definition.rb
67
+ - .gitignore
68
+ - LICENSE
69
+ - spec/unit/ast_spec.rb
70
+ - spec/database.yml
57
71
  - init.rb
72
+ - Rakefile
58
73
  - spec/unit/tokenizer_spec.rb
59
74
  - spec/unit/parser_spec.rb
60
- - spec/unit/ast_spec.rb
75
+ - spec/integration/api_spec.rb
61
76
  - lib/scoped_search/query_language/ast.rb
62
- - spec/lib/database.rb
63
- - Rakefile
64
- - tasks/github-gem.rake
65
- - spec/unit/query_builder_spec.rb
66
77
  - lib/scoped_search/query_language.rb
67
78
  - lib/scoped_search/query_builder.rb
68
79
  - README.rdoc
69
80
  - spec/unit/definition_spec.rb
70
- - spec/database.yml
71
- - spec/integration/api_spec.rb
81
+ - spec/lib/database.rb
82
+ - spec/integration/profile_querying_spec.rb
83
+ - tasks/github-gem.rake
84
+ - spec/unit/query_builder_spec.rb
85
+ - lib/scoped_search.rb
86
+ - spec/integration/relation_querying_spec.rb
72
87
  - spec/integration/ordinal_querying_spec.rb
73
88
  - lib/scoped_search/query_language/tokenizer.rb
74
- - lib/scoped_search.rb
75
89
  has_rdoc: true
76
90
  homepage: http://wiki.github.com/wvanbergen/scoped_search
77
91
  licenses: []
@@ -90,28 +104,31 @@ required_ruby_version: !ruby/object:Gem::Requirement
90
104
  requirements:
91
105
  - - ">="
92
106
  - !ruby/object:Gem::Version
107
+ segments:
108
+ - 0
93
109
  version: "0"
94
- version:
95
110
  required_rubygems_version: !ruby/object:Gem::Requirement
96
111
  requirements:
97
112
  - - ">="
98
113
  - !ruby/object:Gem::Version
114
+ segments:
115
+ - 0
99
116
  version: "0"
100
- version:
101
117
  requirements: []
102
118
 
103
119
  rubyforge_project:
104
- rubygems_version: 1.3.5
120
+ rubygems_version: 1.3.6
105
121
  signing_key:
106
122
  specification_version: 3
107
- summary: A Rails plugin to search your models with a simple query language, implemented using a named_scope
123
+ summary: Easily search you ActiveRecord models with a simple query language using a named scope.
108
124
  test_files:
109
125
  - spec/integration/string_querying_spec.rb
110
- - spec/integration/relation_querying_spec.rb
126
+ - spec/unit/ast_spec.rb
111
127
  - spec/unit/tokenizer_spec.rb
112
128
  - spec/unit/parser_spec.rb
113
- - spec/unit/ast_spec.rb
114
- - spec/unit/query_builder_spec.rb
115
- - spec/unit/definition_spec.rb
116
129
  - spec/integration/api_spec.rb
130
+ - spec/unit/definition_spec.rb
131
+ - spec/integration/profile_querying_spec.rb
132
+ - spec/unit/query_builder_spec.rb
133
+ - spec/integration/relation_querying_spec.rb
117
134
  - spec/integration/ordinal_querying_spec.rb