ransack 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,60 @@
1
+ Ransack is an open source project and we encourage contributions.
2
+
3
+ ## Filing an issue
4
+
5
+ When filing an issue on the Ransack project, please provide these details:
6
+
7
+ * A comprehensive list of steps to reproduce the issue.
8
+ * The version of Ransack *and* the version of Rails.
9
+ * Any relevant stack traces ("Full trace" preferred)
10
+
11
+ In 99% of cases, this information is enough to determine the cause and solution to the problem that is being described.
12
+
13
+ Any issue that is open for 14 days without actionable information or activity will be marked as "stalled" and then closed. Stalled issues can be re-opened if the information requested is provided.
14
+
15
+ ## Pull requests
16
+
17
+ We gladly accept pull requests to fix bugs and, in some circumstances, add new features to Ransack.
18
+
19
+ Here's a quick guide:
20
+
21
+ 1. Fork the repo.
22
+
23
+ 2. Run the tests. We only take pull requests with passing tests, and it's great
24
+ to know that you have a clean slate:
25
+
26
+ $ bundle install
27
+ $ bundle exec rake test_app
28
+ $ bundle exec rake
29
+
30
+ 3. Add a test for your change. Only refactoring and documentation changes
31
+ require no new tests. If you are adding functionality or fixing a bug, we need
32
+ a test!
33
+
34
+ 4. Make the test pass.
35
+
36
+ 5. Push to your fork and submit a pull request. If the changes will apply cleanly to the latest stable branches and master branch, you will only need to submit one pull request.
37
+
38
+ At this point you're waiting on us. We like to at least comment on, if not
39
+ accept, pull requests within three business days (and, typically, one business
40
+ day). We may suggest some changes or improvements or alternatives.
41
+
42
+ Some things that will increase the chance that your pull request is accepted,
43
+ taken straight from the Ruby on Rails guide:
44
+
45
+ * Use Rails idioms and helpers
46
+ * Include tests that fail without your code, and pass with it
47
+ * Update the documentation, the surrounding one, examples elsewhere, guides,
48
+ whatever is affected by your contribution
49
+
50
+ Syntax:
51
+
52
+ * Two spaces, no tabs.
53
+ * No trailing whitespace. Blank lines should not have any space.
54
+ * Prefer &&/|| over and/or.
55
+ * `MyClass.my_method(my_arg)` not `my_method( my_arg )` or my_method my_arg.
56
+ * `a = b` and not `a=b`.
57
+ * `a_method { |block| ... }` and not `a_method { | block | ... }`
58
+ * Follow the conventions you see used in the source already.
59
+
60
+ And in case we didn't emphasize it enough: we love tests!
data/README.md CHANGED
@@ -34,7 +34,7 @@ If you're coming from MetaSearch, things to note:
34
34
 
35
35
  1. The default param key for search params is now `:q`, instead of `:search`. This is
36
36
  primarily to shorten query strings, though advanced queries (below) will still
37
- run afoul of URL length limits in most browsers and require a switch to HTTP
37
+ run afoul of URL length limits in most browsers and require a switch to HTTP
38
38
  POST requests. This key is
39
39
  [configurable](https://github.com/ernie/ransack/wiki/Configuration)
40
40
  2. `form_for` is now `search_form_for`, and validates that a Ransack::Search object
@@ -69,7 +69,7 @@ In your view:
69
69
  <% end %>
70
70
 
71
71
  `cont` (contains) and `start` (starts with) are just two of the available search predicates.
72
- See Constants for a full list.
72
+ See [Constants](https://github.com/ernie/ransack/blob/master/lib/ransack/constants.rb) for a full list and the [wiki](https://github.com/ernie/ransack/wiki/Basic-Searching) for more description.
73
73
 
74
74
  ### Advanced Mode
75
75
 
@@ -103,6 +103,56 @@ Once you've done so, you can make use of the helpers in Ransack::Helpers::FormBu
103
103
  construct much more complex search forms, such as the one on the
104
104
  [demo page](http://ransack-demo.heroku.com).
105
105
 
106
+ ### has_many and belongs_to associations
107
+
108
+ You can easily use Ransack to search in associated objects.
109
+
110
+ Given you have these associations ...
111
+
112
+ class Employee < ActiveRecord::Base
113
+ belongs_to :supervisor
114
+
115
+ # has attribute last_name:string
116
+ end
117
+
118
+ class Department < ActiveRecord::Base
119
+ has_many :supervisors
120
+
121
+ # has attribute title:string
122
+ end
123
+
124
+ class Supervisor < ActiveRecord::Base
125
+ belongs_to :department
126
+ has_many :employees
127
+
128
+ # has attribute last_name:string
129
+ end
130
+
131
+ ... and a controller ...
132
+
133
+ class SupervisorsController < ApplicationController
134
+ def index
135
+ @search = Supervisor.search(params[:q])
136
+ @supervisors = @search.result(:distinct => true)
137
+ end
138
+ end
139
+
140
+ ... you might set up your form like this ...
141
+
142
+ <%= search_form_for @search do |f| %>
143
+ <%= f.label :last_name_cont %>
144
+ <%= f.text_field :last_name_cont %>
145
+
146
+ <%= f.label :department_title_cont %>
147
+ <%= f.text_field :department_title_cont %>
148
+
149
+ <%= f.label :employees_last_name_cont %>
150
+ <%= f.text_field :employees_last_name_cont %>
151
+
152
+ <%= f.submit "search" %>
153
+ <% end %>
154
+
155
+
106
156
  ## Contributions
107
157
 
108
158
  To support the project:
@@ -0,0 +1,14 @@
1
+ module Arel
2
+
3
+ module Visitors
4
+
5
+ class DepthFirst < Visitor
6
+
7
+ unless method_defined?(:visit_Arel_Nodes_InfixOperation)
8
+ alias :visit_Arel_Nodes_InfixOperation :binary
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+ end
@@ -1,31 +1,30 @@
1
1
  require 'ransack/context'
2
2
  require 'ransack/adapters/active_record/3.1/context'
3
+ require 'ransack/adapters/active_record/compat'
3
4
  require 'polyamorous'
4
5
 
5
6
  module Ransack
6
7
  module Adapters
7
8
  module ActiveRecord
8
9
  class Context < ::Ransack::Context
9
-
10
+
10
11
  # Redefine a few things that have changed with 3.2.
11
-
12
+
12
13
  def initialize(object, options = {})
13
14
  super
14
15
  @arel_visitor = @engine.connection.visitor
15
16
  end
16
-
17
+
17
18
  def type_for(attr)
18
19
  return nil unless attr && attr.valid?
19
20
  name = attr.arel_attribute.name.to_s
20
21
  table = attr.arel_attribute.relation.table_name
21
22
 
22
- unless @engine.connection.table_exists?(table)
23
- raise "No table named #{table} exists"
24
- end
25
-
26
- @engine.connection.schema_cache.columns_hash[table][name].type
23
+ schema_cache = @engine.connection.schema_cache
24
+ raise "No table named #{table} exists" unless schema_cache.table_exists?(table)
25
+ schema_cache.columns_hash[table][name].type
27
26
  end
28
-
27
+
29
28
  def evaluate(search, opts = {})
30
29
  viz = Visitor.new
31
30
  relation = @object.where(viz.accept(search.base))
@@ -34,7 +33,7 @@ module Ransack
34
33
  end
35
34
  opts[:distinct] ? relation.uniq : relation
36
35
  end
37
-
36
+
38
37
  end
39
38
  end
40
39
  end
@@ -6,12 +6,12 @@ module Ransack
6
6
  AREL_PREDICATES = %w(eq not_eq matches does_not_match lt lteq gt gteq in not_in)
7
7
 
8
8
  DERIVED_PREDICATES = [
9
- ['cont', {:arel_predicate => 'matches', :formatter => proc {|v| "%#{v}%"}}],
10
- ['not_cont', {:arel_predicate => 'does_not_match', :formatter => proc {|v| "%#{v}%"}}],
11
- ['start', {:arel_predicate => 'matches', :formatter => proc {|v| "#{v}%"}}],
12
- ['not_start', {:arel_predicate => 'does_not_match', :formatter => proc {|v| "#{v}%"}}],
13
- ['end', {:arel_predicate => 'matches', :formatter => proc {|v| "%#{v}"}}],
14
- ['not_end', {:arel_predicate => 'does_not_match', :formatter => proc {|v| "%#{v}"}}],
9
+ ['cont', {:arel_predicate => 'matches', :formatter => proc {|v| "%#{escape_wildcards(v)}%"}}],
10
+ ['not_cont', {:arel_predicate => 'does_not_match', :formatter => proc {|v| "%#{escape_wildcards(v)}%"}}],
11
+ ['start', {:arel_predicate => 'matches', :formatter => proc {|v| "#{escape_wildcards(v)}%"}}],
12
+ ['not_start', {:arel_predicate => 'does_not_match', :formatter => proc {|v| "#{escape_wildcards(v)}%"}}],
13
+ ['end', {:arel_predicate => 'matches', :formatter => proc {|v| "%#{escape_wildcards(v)}"}}],
14
+ ['not_end', {:arel_predicate => 'does_not_match', :formatter => proc {|v| "%#{escape_wildcards(v)}"}}],
15
15
  ['true', {:arel_predicate => 'eq', :compounds => false, :type => :boolean, :validator => proc {|v| TRUE_VALUES.include?(v)}}],
16
16
  ['false', {:arel_predicate => 'eq', :compounds => false, :type => :boolean, :validator => proc {|v| TRUE_VALUES.include?(v)}, :formatter => proc {|v| !v}}],
17
17
  ['present', {:arel_predicate => 'not_eq_all', :compounds => false, :type => :boolean, :validator => proc {|v| TRUE_VALUES.include?(v)}, :formatter => proc {|v| [nil, '']}}],
@@ -19,5 +19,11 @@ module Ransack
19
19
  ['null', {:arel_predicate => 'eq', :compounds => false, :type => :boolean, :validator => proc {|v| TRUE_VALUES.include?(v)}, :formatter => proc {|v| nil}}],
20
20
  ['not_null', {:arel_predicate => 'not_eq', :compounds => false, :type => :boolean, :validator => proc {|v| TRUE_VALUES.include?(v)}, :formatter => proc {|v| nil}}]
21
21
  ]
22
+
23
+ module_function
24
+ # replace % _ \ to \% \_ \\
25
+ def escape_wildcards(unescaped)
26
+ unescaped.gsub(/\\/){ "\\\\" }.gsub(/%/, "\\%").gsub(/_/, "\\_")
27
+ end
22
28
  end
23
29
  end
@@ -13,8 +13,8 @@ module Ransack
13
13
  end
14
14
  options[:html] ||= {}
15
15
  html_options = {
16
- :class => options[:as] ? "#{options[:as]}_search" : "#{search.klass.to_s.underscore}_search",
17
- :id => options[:as] ? "#{options[:as]}_search" : "#{search.klass.to_s.underscore}_search",
16
+ :class => options[:class].present? ? "#{options[:class]}" : "#{search.klass.to_s.underscore}_search",
17
+ :id => options[:id].present? ? "#{options[:id]}" : "#{search.klass.to_s.underscore}_search",
18
18
  :method => :get
19
19
  }
20
20
  options[:as] ||= 'q'
@@ -0,0 +1,70 @@
1
+ cs:
2
+ ransack:
3
+ search: "vyhledávání"
4
+ predicate: "predikát"
5
+ and: "a"
6
+ or: "nebo"
7
+ any: "kteroukoliv"
8
+ all: "každou"
9
+ combinator: "kombinátor"
10
+ attribute: "atribut"
11
+ value: "hodnota"
12
+ condition: "podmínka"
13
+ sort: "řazení"
14
+ asc: "vzestupné"
15
+ desc: "sestupné"
16
+ predicates:
17
+ eq: "rovno"
18
+ eq_any: "rovno kterékoliv"
19
+ eq_all: "rovno všem"
20
+ not_eq: "nerovno"
21
+ not_eq_any: "nerovno kterékoliv"
22
+ not_eq_all: "nerovno všem"
23
+ matches: "odpovídá"
24
+ matches_any: "odpovídá kterékoliv"
25
+ matches_all: "odpovídá všem"
26
+ does_not_match: "neodpovídá"
27
+ does_not_match_any: "neodpovídá kterékoliv"
28
+ does_not_match_all: "neodpovídá všem"
29
+ lt: "menší než"
30
+ lt_any: "menší než kterákoliv"
31
+ lt_all: "menší než všechny"
32
+ lteq: "menší nebo rovno než"
33
+ lteq_any: "menší nebo rovno než kterákoliv"
34
+ lteq_all: "menší nebo rovno než všechny"
35
+ gt: "větší než"
36
+ gt_any: "větší než kterákoliv"
37
+ gt_all: "větší než všechny"
38
+ gteq: "větší nebo rovno než"
39
+ gteq_any: "větší nebo rovno než kterákoliv"
40
+ gteq_all: "větší nebo rovno než všechny"
41
+ in: "v"
42
+ in_any: "v kterékoliv"
43
+ in_all: "ve všech"
44
+ not_in: "není v"
45
+ not_in_any: "není v kterékoliv"
46
+ not_in_all: "není ve všech"
47
+ cont: "obsahuje"
48
+ cont_any: "obsahuje kterékoliv"
49
+ cont_all: "obsahuje všechny"
50
+ not_cont: "neobsahuje"
51
+ not_cont_any: "neobsahuje kteroukoliv"
52
+ not_cont_all: "neobsahuje všechny"
53
+ start: "začíná s"
54
+ start_any: "začíná s kteroukoliv"
55
+ start_all: "začíná se všemi"
56
+ not_start: "nezačíná s"
57
+ not_start_any: "nezačíná s kteroukoliv"
58
+ not_start_all: "nezačíná se všemi"
59
+ end: "končí s"
60
+ end_any: "končí s kteroukoliv"
61
+ end_all: "končí se všemi"
62
+ not_end: "nekončí s"
63
+ not_end_any: "nekončí s kteroukoliv"
64
+ not_end_all: "nekončí se všemi"
65
+ 'true': "je pravdivé"
66
+ 'false': "není pravdivé"
67
+ present: "je vyplněné"
68
+ blank: "je prázdné"
69
+ 'null': "je null"
70
+ not_null: "není null"
@@ -21,7 +21,7 @@ en:
21
21
  not_eq_any: "not equal to any"
22
22
  not_eq_all: "not equal to all"
23
23
  matches: "matches"
24
- matches_any: "matches_any"
24
+ matches_any: "matches any"
25
25
  matches_all: "matches all"
26
26
  does_not_match: "doesn't match"
27
27
  does_not_match_any: "doesn't match any"
@@ -0,0 +1,70 @@
1
+ es:
2
+ ransack:
3
+ search: "buscar"
4
+ predicate: "predicado"
5
+ and: "y"
6
+ or: "o"
7
+ any: "cualquier"
8
+ all: "todos"
9
+ combinator: "combinado"
10
+ attribute: "atributo"
11
+ value: "valor"
12
+ condition: "condición"
13
+ sort: "ordernar"
14
+ asc: "ascendente"
15
+ desc: "descendente"
16
+ predicates:
17
+ eq: "es igual a"
18
+ eq_any: "es igual a cualquier"
19
+ eq_all: "es igual a todos"
20
+ not_eq: "no es igual a"
21
+ not_eq_any: "no es igual a cualquier"
22
+ not_eq_all: "no es iguala todos"
23
+ matches: "coincidir"
24
+ matches_any: "coincidir a cualquier"
25
+ matches_all: "coincidir a todos"
26
+ does_not_match: "no coincide"
27
+ does_not_match_any: "no coincide con ninguna"
28
+ does_not_match_all: "no coincide con todos"
29
+ lt: "menor que"
30
+ lt_any: "menor que cualquier"
31
+ lt_all: "menor o igual a"
32
+ lteq: "less than or equal to"
33
+ lteq_any: "menor o igual a cualquier"
34
+ lteq_all: "menor o igual a todos"
35
+ gt: "mayor que"
36
+ gt_any: "mayor que cualquier"
37
+ gt_all: "mayor que todos"
38
+ gteq: "mayor que o igual a"
39
+ gteq_any: "mayor que o igual a cualquier"
40
+ gteq_all: "mayor que o igual a todos"
41
+ in: "en"
42
+ in_any: "en cualquier"
43
+ in_all: "en todos"
44
+ not_in: "no en"
45
+ not_in_any: "no en cualquier"
46
+ not_in_all: "no en todos"
47
+ cont: "contiene"
48
+ cont_any: "contiene cualquier"
49
+ cont_all: "contiene todos"
50
+ not_cont: "no contiene"
51
+ not_cont_any: "no contiene ninguna"
52
+ not_cont_all: "no contiene toda"
53
+ start: "comienza con"
54
+ start_any: "comienza con cualquier"
55
+ start_all: "comienza con toda"
56
+ not_start: "no inicia con"
57
+ not_start_any: "no comienza con cualquier"
58
+ not_start_all: "no inicia con toda"
59
+ end: "termina con"
60
+ end_any: "termina con cualquier"
61
+ end_all: "termina con todo"
62
+ not_end: "no termina con"
63
+ not_end_any: "no termina con cualquier"
64
+ not_end_all: "no termina con todo"
65
+ 'true': "es verdadero"
66
+ 'false': "es falso"
67
+ present: "es presente"
68
+ blank: "está en blanco"
69
+ 'null': "es nula"
70
+ not_null: "no es nula"
@@ -44,6 +44,10 @@ module Ransack
44
44
  false
45
45
  end
46
46
 
47
+ def inspect
48
+ "Attribute <#{name}>"
49
+ end
50
+
47
51
  end
48
52
  end
49
53
  end
@@ -200,6 +200,13 @@ module Ransack
200
200
  predicate.type || (attributes.first && attributes.first.type)
201
201
  end
202
202
 
203
+ def inspect
204
+ data =[['attributes', a.try(:map, &:name)], ['predicate', p], ['combinator', m], ['values', v.try(:map, &:value)]].reject { |e|
205
+ e[1].blank?
206
+ }.map { |v| "#{v[0]}: #{v[1]}" }.join(', ')
207
+ "Condition <#{data}>"
208
+ end
209
+
203
210
  private
204
211
 
205
212
  def valid_combinator?
@@ -154,6 +154,13 @@ module Ransack
154
154
  self
155
155
  end
156
156
 
157
+ def inspect
158
+ data =[['conditions', conditions], ['combinator', combinator]].reject { |e|
159
+ e[1].blank?
160
+ }.map { |v| "#{v[0]}: #{v[1]}" }.join(', ')
161
+ "Grouping <#{data}>"
162
+ end
163
+
157
164
  private
158
165
 
159
166
  def write_attribute(name, val)
@@ -98,6 +98,10 @@ module Ransack
98
98
  end
99
99
  end
100
100
 
101
+ def inspect
102
+ "Value <#{value}>"
103
+ end
104
+
101
105
  def array_of_arrays?(val)
102
106
  Array === val && Array === val.first
103
107
  end
@@ -72,7 +72,7 @@ module Ransack
72
72
  Nodes::Sort.new(@context).build(opts)
73
73
  end
74
74
 
75
- def respond_to?(method_id)
75
+ def respond_to?(method_id, include_private = false)
76
76
  super or begin
77
77
  method_name = method_id.to_s
78
78
  writer = method_name.sub!(/\=$/, '')
@@ -90,6 +90,10 @@ module Ransack
90
90
  end
91
91
  end
92
92
 
93
+ def inspect
94
+ "Ransack::Search<class: #{klass.name}, base: #{base.inspect}>"
95
+ end
96
+
93
97
  private
94
98
 
95
99
  def collapse_multiparameter_attributes!(attrs)
@@ -114,4 +118,4 @@ module Ransack
114
118
  end
115
119
 
116
120
  end
117
- end
121
+ end
@@ -1,3 +1,3 @@
1
1
  module Ransack
2
- VERSION = "0.7.0"
2
+ VERSION = "0.7.1"
3
3
  end
@@ -43,6 +43,11 @@ module Ransack
43
43
  s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith')
44
44
  s.result.first.should eq Person.find_by_name('Aric Smith')
45
45
  end if defined?(Arel::Nodes::InfixOperation)
46
+
47
+ it "doesn't break #count if using InfixOperations" do
48
+ s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith')
49
+ s.result.count.should eq 1
50
+ end if defined?(Arel::Nodes::InfixOperation)
46
51
  end
47
52
 
48
53
  describe '#ransackable_attributes' do
@@ -29,6 +29,10 @@ module Ransack
29
29
  @s.name_cont = 'ric'
30
30
  @s.result.to_sql.should match /"people"."name" LIKE '%ric%'/
31
31
  end
32
+ it 'escapes %, _ and \\ in value' do
33
+ @s.name_cont = '%_\\'
34
+ @s.result.to_sql.should match /"people"."name" LIKE '%\\%\\_\\\\%'/
35
+ end
32
36
  end
33
37
 
34
38
  describe 'not_cont' do
@@ -104,6 +104,11 @@ module Ransack
104
104
  condition.attributes.first.name.should eq 'name'
105
105
  condition.value.should eq ['Ernie', 'Bert']
106
106
  end
107
+
108
+ it 'does not evaluate the query on #inspect' do
109
+ search = Search.new(Person, :children_id_in => [1, 2, 3])
110
+ search.inspect.should_not match /ActiveRecord/
111
+ end
107
112
  end
108
113
 
109
114
  describe '#result' do
@@ -235,5 +240,11 @@ module Ransack
235
240
  end
236
241
  end
237
242
 
243
+ describe '#respond_to' do
244
+ it 'is aware of second argument' do
245
+ Search.new(Person).respond_to?(:name_eq, true).should be_true
246
+ end
247
+ end
248
+
238
249
  end
239
250
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ransack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-07-29 00:00:00.000000000 Z
13
+ date: 2012-12-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -135,6 +135,7 @@ extra_rdoc_files: []
135
135
  files:
136
136
  - .gitignore
137
137
  - .travis.yml
138
+ - CONTRIBUTING.md
138
139
  - Gemfile
139
140
  - LICENSE
140
141
  - README.md
@@ -145,6 +146,7 @@ files:
145
146
  - lib/ransack/adapters/active_record/3.0/context.rb
146
147
  - lib/ransack/adapters/active_record/3.1/context.rb
147
148
  - lib/ransack/adapters/active_record/base.rb
149
+ - lib/ransack/adapters/active_record/compat.rb
148
150
  - lib/ransack/adapters/active_record/context.rb
149
151
  - lib/ransack/configuration.rb
150
152
  - lib/ransack/constants.rb
@@ -152,7 +154,9 @@ files:
152
154
  - lib/ransack/helpers.rb
153
155
  - lib/ransack/helpers/form_builder.rb
154
156
  - lib/ransack/helpers/form_helper.rb
157
+ - lib/ransack/locale/cs.yml
155
158
  - lib/ransack/locale/en.yml
159
+ - lib/ransack/locale/es.yml
156
160
  - lib/ransack/naming.rb
157
161
  - lib/ransack/nodes.rb
158
162
  - lib/ransack/nodes/attribute.rb
@@ -200,21 +204,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
200
204
  - - ! '>='
201
205
  - !ruby/object:Gem::Version
202
206
  version: '0'
203
- segments:
204
- - 0
205
- hash: 4077471372512744125
206
207
  required_rubygems_version: !ruby/object:Gem::Requirement
207
208
  none: false
208
209
  requirements:
209
210
  - - ! '>='
210
211
  - !ruby/object:Gem::Version
211
212
  version: '0'
212
- segments:
213
- - 0
214
- hash: 4077471372512744125
215
213
  requirements: []
216
214
  rubyforge_project: ransack
217
- rubygems_version: 1.8.24
215
+ rubygems_version: 1.8.23
218
216
  signing_key:
219
217
  specification_version: 3
220
218
  summary: Object-based searching for ActiveRecord (currently).