ransack 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - ree
5
+ - rbx
6
+ - rbx-2.0
7
+ - ruby-head
data/Gemfile CHANGED
@@ -1,14 +1,39 @@
1
1
  source "http://rubygems.org"
2
2
  gemspec
3
3
 
4
- if ENV['RAILS_VERSION'] == 'release'
5
- gem 'activesupport'
6
- gem 'activerecord'
7
- gem 'actionpack'
4
+ gem 'rake'
5
+
6
+ rails = ENV['RAILS'] || 'master'
7
+ arel = ENV['AREL'] || 'master'
8
+
9
+ arel_opts = case arel
10
+ when /\// # A path
11
+ {:path => arel}
12
+ when /^v/ # A tagged version
13
+ {:git => 'git://github.com/rails/arel.git', :tag => arel}
14
+ else
15
+ {:git => 'git://github.com/rails/arel.git', :branch => arel}
16
+ end
17
+
18
+ gem 'arel', arel_opts
19
+
20
+ case rails
21
+ when /\// # A path
22
+ gem 'activesupport', :path => "#{rails}/activesupport"
23
+ gem 'activemodel', :path => "#{rails}/activemodel"
24
+ gem 'activerecord', :path => "#{rails}/activerecord"
25
+ gem 'actionpack', :path => "#{rails}/activerecord"
26
+ when /^v/ # A tagged version
27
+ git 'git://github.com/rails/rails.git', :tag => rails do
28
+ gem 'activesupport'
29
+ gem 'activemodel'
30
+ gem 'activerecord'
31
+ gem 'actionpack'
32
+ end
8
33
  else
9
- gem 'arel', :git => 'git://github.com/rails/arel.git'
10
- git 'git://github.com/rails/rails.git' do
34
+ git 'git://github.com/rails/rails.git', :branch => rails do
11
35
  gem 'activesupport'
36
+ gem 'activemodel'
12
37
  gem 'activerecord'
13
38
  gem 'actionpack'
14
39
  end
@@ -25,10 +25,6 @@ module Ransack
25
25
  end
26
26
  end
27
27
 
28
- def can_accept?(object)
29
- method_defined? DISPATCH[object.class]
30
- end
31
-
32
28
  end
33
29
 
34
30
  def initialize(object)
@@ -99,7 +95,7 @@ module Ransack
99
95
  end
100
96
 
101
97
  def unpolymorphize_association(str)
102
- if (match = str.match(/_of_(.+?)_type$/)) && Kernel.const_defined?(match.captures.first)
98
+ if (match = str.match(/_of_(.+?)_type$/))
103
99
  [match.pre_match, Kernel.const_get(match.captures.first)]
104
100
  else
105
101
  [str, nil]
@@ -16,7 +16,7 @@ module Ransack
16
16
  :a => attributes,
17
17
  :p => predicate.name,
18
18
  :m => combinator,
19
- :v => [values]
19
+ :v => %w(in not_in).include?(predicate.arel_predicate) ? Array(values) : [values]
20
20
  )
21
21
  # TODO: Figure out what to do with multiple types of attributes, if anything.
22
22
  # Tempted to go with "garbage in, garbage out" on this one
@@ -41,7 +41,7 @@ module Ransack
41
41
  end
42
42
 
43
43
  def valid_arity?
44
- values.size <= 1 || predicate.compound || %w(in not_in).include?(predicate.name)
44
+ values.size <= 1 || predicate.wants_array
45
45
  end
46
46
 
47
47
  def attributes
@@ -113,7 +113,7 @@ module Ransack
113
113
  end
114
114
 
115
115
  def value
116
- predicate.compound ? values.map {|v| v.cast(default_type)} : values.first.cast(default_type)
116
+ predicate.wants_array ? values.map {|v| v.cast(default_type)} : values.first.cast(default_type)
117
117
  end
118
118
 
119
119
  def build(params)
@@ -180,7 +180,7 @@ module Ransack
180
180
  end
181
181
 
182
182
  def validated_values
183
- values.select {|v| predicate.validator ? predicate.validator.call(v.value) : v.present?}
183
+ values.select {|v| predicate.validator.call(v.value)}
184
184
  end
185
185
 
186
186
  def casted_values_for_attribute(attr)
@@ -188,11 +188,12 @@ module Ransack
188
188
  end
189
189
 
190
190
  def formatted_values_for_attribute(attr)
191
- casted_values_for_attribute(attr).map do |val|
191
+ formatted = casted_values_for_attribute(attr).map do |val|
192
192
  val = attr.ransacker.formatter.call(val) if attr.ransacker && attr.ransacker.formatter
193
193
  val = predicate.format(val)
194
194
  val
195
195
  end
196
+ predicate.wants_array ? formatted : formatted.first
196
197
  end
197
198
 
198
199
  def default_type
@@ -65,10 +65,12 @@ module Ransack
65
65
  end
66
66
 
67
67
  def cast_to_boolean(val)
68
- if val.is_a?(String) && val.blank?
69
- nil
68
+ if Constants::TRUE_VALUES.include?(val)
69
+ true
70
+ elsif Constants::FALSE_VALUES.include?(val)
71
+ false
70
72
  else
71
- Constants::TRUE_VALUES.include?(val)
73
+ nil
72
74
  end
73
75
  end
74
76
 
@@ -1,6 +1,6 @@
1
1
  module Ransack
2
2
  class Predicate
3
- attr_reader :name, :arel_predicate, :type, :formatter, :validator, :compound
3
+ attr_reader :name, :arel_predicate, :type, :formatter, :validator, :compound, :wants_array
4
4
 
5
5
  class << self
6
6
 
@@ -39,8 +39,9 @@ module Ransack
39
39
  @arel_predicate = opts[:arel_predicate]
40
40
  @type = opts[:type]
41
41
  @formatter = opts[:formatter]
42
- @validator = opts[:validator] || lambda { |v| v.present? }
42
+ @validator = opts[:validator] || lambda { |v| v.respond_to?(:empty?) ? !v.empty? : !v.nil? }
43
43
  @compound = opts[:compound]
44
+ @wants_array = @compound || ['in', 'not_in'].include?(@arel_predicate)
44
45
  end
45
46
 
46
47
  def eql?(other)
@@ -1,3 +1,3 @@
1
1
  module Ransack
2
- VERSION = "0.5.3"
2
+ VERSION = "0.5.4"
3
3
  end
@@ -5,33 +5,36 @@ module Ransack
5
5
  module ActiveRecord
6
6
  describe Base do
7
7
 
8
- it 'adds a ransack method to ActiveRecord::Base' do
9
- ::ActiveRecord::Base.should respond_to :ransack
10
- end
8
+ subject { ::ActiveRecord::Base }
11
9
 
12
- it 'aliases the method to search if available' do
13
- ::ActiveRecord::Base.should respond_to :search
14
- end
10
+ it { should respond_to :ransack }
11
+ it { should respond_to :search }
15
12
 
16
13
  describe '#search' do
17
- before do
18
- @s = Person.search
19
- end
14
+ subject { Person.search }
20
15
 
21
- it 'creates a search with Relation as its object' do
22
- @s.should be_a Search
23
- @s.object.should be_an ::ActiveRecord::Relation
16
+ it { should be_a Search }
17
+ it 'has a Relation as its object' do
18
+ subject.object.should be_an ::ActiveRecord::Relation
24
19
  end
25
20
  end
26
21
 
27
22
  describe '#ransacker' do
23
+ # in schema.rb, class Person:
24
+ # ransacker :reversed_name, :formatter => proc {|v| v.reverse} do |parent|
25
+ # parent.table[:name]
26
+ # end
27
+ #
28
+ # ransacker :doubled_name do |parent|
29
+ # Arel::Nodes::InfixOperation.new('||', parent.table[:name], parent.table[:name])
30
+ # end
28
31
  it 'creates ransack attributes' do
29
32
  s = Person.search(:reversed_name_eq => 'htimS cirA')
30
33
  s.result.should have(1).person
31
34
  s.result.first.should eq Person.find_by_name('Aric Smith')
32
35
  end
33
36
 
34
- it 'allows access of attributes through associations' do
37
+ it 'can be accessed through associations' do
35
38
  s = Person.search(:children_reversed_name_eq => 'htimS cirA')
36
39
  s.result.to_sql.should match /"children_people"."name" = 'Aric Smith'/
37
40
  end
@@ -42,6 +45,22 @@ module Ransack
42
45
  end if defined?(Arel::Nodes::InfixOperation)
43
46
  end
44
47
 
48
+ describe '#ransackable_attributes' do
49
+ subject { Person.ransackable_attributes }
50
+
51
+ it { should include 'name' }
52
+ it { should include 'reversed_name' }
53
+ it { should include 'doubled_name' }
54
+ end
55
+
56
+ describe '#ransackable_associations' do
57
+ subject { Person.ransackable_associations }
58
+
59
+ it { should include 'parent' }
60
+ it { should include 'children' }
61
+ it { should include 'articles' }
62
+ end
63
+
45
64
  end
46
65
  end
47
66
  end
@@ -4,19 +4,35 @@ module Ransack
4
4
  module Adapters
5
5
  module ActiveRecord
6
6
  describe Context do
7
- before do
8
- @c = Context.new(Person)
7
+ subject { Context.new(Person) }
8
+
9
+ describe '#evaluate' do
10
+ it 'evaluates search obects' do
11
+ search = Search.new(Person, :name_eq => 'Joe Blow')
12
+ result = subject.evaluate(search)
13
+
14
+ result.should be_an ::ActiveRecord::Relation
15
+ result.to_sql.should match /"name" = 'Joe Blow'/
16
+ end
17
+
18
+ it 'SELECTs DISTINCT when :distinct => true' do
19
+ search = Search.new(Person, :name_eq => 'Joe Blow')
20
+ result = subject.evaluate(search, :distinct => true)
21
+
22
+ result.should be_an ::ActiveRecord::Relation
23
+ result.to_sql.should match /SELECT DISTINCT/
24
+ end
9
25
  end
10
26
 
11
27
  it 'contextualizes strings to attributes' do
12
- attribute = @c.contextualize 'children_children_parent_name'
28
+ attribute = subject.contextualize 'children_children_parent_name'
13
29
  attribute.should be_a Arel::Attributes::Attribute
14
30
  attribute.name.to_s.should eq 'name'
15
31
  attribute.relation.table_alias.should eq 'parents_people'
16
32
  end
17
33
 
18
34
  it 'builds new associations if not yet built' do
19
- attribute = @c.contextualize 'children_articles_title'
35
+ attribute = subject.contextualize 'children_articles_title'
20
36
  attribute.should be_a Arel::Attributes::Attribute
21
37
  attribute.name.to_s.should eq 'title'
22
38
  attribute.relation.name.should eq 'articles'
@@ -7,5 +7,25 @@ module Ransack
7
7
  config.should eq Ransack
8
8
  end
9
9
  end
10
+
11
+ it 'adds predicates' do
12
+ Ransack.configure do |config|
13
+ config.add_predicate :test_predicate
14
+ end
15
+
16
+ Ransack.predicates.should have_key 'test_predicate'
17
+ Ransack.predicates.should have_key 'test_predicate_any'
18
+ Ransack.predicates.should have_key 'test_predicate_all'
19
+ end
20
+
21
+ it 'avoids creating compound predicates if :compounds => false' do
22
+ Ransack.configure do |config|
23
+ config.add_predicate :test_predicate_without_compound, :compounds => false
24
+ end
25
+
26
+ Ransack.predicates.should have_key 'test_predicate_without_compound'
27
+ Ransack.predicates.should_not have_key 'test_predicate_without_compound_any'
28
+ Ransack.predicates.should_not have_key 'test_predicate_without_compound_all'
29
+ end
10
30
  end
11
31
  end
@@ -7,6 +7,23 @@ module Ransack
7
7
  @s = Search.new(Person)
8
8
  end
9
9
 
10
+ describe 'eq' do
11
+ it 'generates an equality condition for boolean true' do
12
+ @s.awesome_eq = true
13
+ @s.result.to_sql.should match /"people"."awesome" = 't'/
14
+ end
15
+
16
+ it 'generates an equality condition for boolean false' do
17
+ @s.awesome_eq = false
18
+ @s.result.to_sql.should match /"people"."awesome" = 'f'/
19
+ end
20
+
21
+ it 'does not generate a condition for nil' do
22
+ @s.awesome_eq = nil
23
+ @s.result.to_sql.should_not match /WHERE/
24
+ end
25
+ end
26
+
10
27
  describe 'cont' do
11
28
  it 'generates a LIKE query with value surrounded by %' do
12
29
  @s.name_cont = 'ric'
@@ -21,5 +38,18 @@ module Ransack
21
38
  end
22
39
  end
23
40
 
41
+ describe 'null' do
42
+ it 'generates a value IS NULL query' do
43
+ @s.name_null = true
44
+ @s.result.to_sql.should match /"people"."name" IS NULL/
45
+ end
46
+ end
47
+
48
+ describe 'not_null' do
49
+ it 'generates a value IS NOT NULL query' do
50
+ @s.name_not_null = true
51
+ @s.result.to_sql.should match /"people"."name" IS NOT NULL/
52
+ end
53
+ end
24
54
  end
25
- end
55
+ end
@@ -126,7 +126,12 @@ module Ransack
126
126
  )
127
127
  search.result.should be_an ActiveRecord::Relation
128
128
  where = search.result.where_values.first
129
- where.to_sql.should match /\("people"."name" = 'Ernie' OR "children_people"."name" = 'Ernie'\) AND \("people"."name" = 'Bert' OR "children_people"."name" = 'Bert'\)/
129
+ sql = where.to_sql
130
+ first, second = sql.split(/ AND /)
131
+ first.should match /"people"."name" = 'Ernie'/
132
+ first.should match /"children_people"."name" = 'Ernie'/
133
+ second.should match /"people"."name" = 'Bert'/
134
+ second.should match /"children_people"."name" = 'Bert'/
130
135
  end
131
136
 
132
137
  it 'returns distinct records when passed :distinct => true' do
@@ -175,13 +180,11 @@ module Ransack
175
180
  }
176
181
  }
177
182
  @s.sorts.should have(2).items
178
- sort1, sort2 = @s.sorts
179
- sort1.should be_a Nodes::Sort
180
- sort1.name.should eq 'id'
181
- sort1.dir.should eq 'desc'
182
- sort2.should be_a Nodes::Sort
183
- sort2.name.should eq 'name'
184
- sort2.dir.should eq 'asc'
183
+ @s.sorts.should be_all {|s| Nodes::Sort === s}
184
+ id_sort = @s.sorts.detect {|s| s.name == 'id'}
185
+ name_sort = @s.sorts.detect {|s| s.name == 'name'}
186
+ id_sort.dir.should eq 'desc'
187
+ name_sort.dir.should eq 'asc'
185
188
  end
186
189
  end
187
190
 
data/spec/spec_helper.rb CHANGED
@@ -25,4 +25,17 @@ RSpec.configure do |config|
25
25
  config.before(:each) { Sham.reset(:before_each) }
26
26
 
27
27
  config.include RansackHelper
28
+ end
29
+
30
+ RSpec::Matchers.define :be_like do |expected|
31
+ match do |actual|
32
+ actual.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip ==
33
+ expected.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip
34
+ end
35
+ end
36
+
37
+ RSpec::Matchers.define :have_attribute_method do |expected|
38
+ match do |actual|
39
+ actual.attribute_method?(expected)
40
+ end
28
41
  end
@@ -53,6 +53,7 @@ module Schema
53
53
  t.integer :parent_id
54
54
  t.string :name
55
55
  t.integer :salary
56
+ t.boolean :awesome, :default => false
56
57
  t.timestamps
57
58
  end
58
59
 
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.5.3
4
+ version: 0.5.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-06-27 00:00:00.000000000Z
12
+ date: 2011-07-24 00:00:00.000000000 -04:00
13
+ default_executable:
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: activerecord
16
- requirement: &2153182720 !ruby/object:Gem::Requirement
17
+ requirement: &70355886036760 !ruby/object:Gem::Requirement
17
18
  none: false
18
19
  requirements:
19
20
  - - ~>
@@ -21,10 +22,10 @@ dependencies:
21
22
  version: '3.0'
22
23
  type: :runtime
23
24
  prerelease: false
24
- version_requirements: *2153182720
25
+ version_requirements: *70355886036760
25
26
  - !ruby/object:Gem::Dependency
26
27
  name: activesupport
27
- requirement: &2153182180 !ruby/object:Gem::Requirement
28
+ requirement: &70355886036220 !ruby/object:Gem::Requirement
28
29
  none: false
29
30
  requirements:
30
31
  - - ~>
@@ -32,10 +33,10 @@ dependencies:
32
33
  version: '3.0'
33
34
  type: :runtime
34
35
  prerelease: false
35
- version_requirements: *2153182180
36
+ version_requirements: *70355886036220
36
37
  - !ruby/object:Gem::Dependency
37
38
  name: actionpack
38
- requirement: &2153181700 !ruby/object:Gem::Requirement
39
+ requirement: &70355886035700 !ruby/object:Gem::Requirement
39
40
  none: false
40
41
  requirements:
41
42
  - - ~>
@@ -43,10 +44,10 @@ dependencies:
43
44
  version: '3.0'
44
45
  type: :runtime
45
46
  prerelease: false
46
- version_requirements: *2153181700
47
+ version_requirements: *70355886035700
47
48
  - !ruby/object:Gem::Dependency
48
49
  name: rspec
49
- requirement: &2153181180 !ruby/object:Gem::Requirement
50
+ requirement: &70355886035200 !ruby/object:Gem::Requirement
50
51
  none: false
51
52
  requirements:
52
53
  - - ~>
@@ -54,10 +55,10 @@ dependencies:
54
55
  version: 2.6.0
55
56
  type: :development
56
57
  prerelease: false
57
- version_requirements: *2153181180
58
+ version_requirements: *70355886035200
58
59
  - !ruby/object:Gem::Dependency
59
60
  name: machinist
60
- requirement: &2153180680 !ruby/object:Gem::Requirement
61
+ requirement: &70355886034700 !ruby/object:Gem::Requirement
61
62
  none: false
62
63
  requirements:
63
64
  - - ~>
@@ -65,10 +66,10 @@ dependencies:
65
66
  version: 1.0.6
66
67
  type: :development
67
68
  prerelease: false
68
- version_requirements: *2153180680
69
+ version_requirements: *70355886034700
69
70
  - !ruby/object:Gem::Dependency
70
71
  name: faker
71
- requirement: &2153180220 !ruby/object:Gem::Requirement
72
+ requirement: &70355886034140 !ruby/object:Gem::Requirement
72
73
  none: false
73
74
  requirements:
74
75
  - - ~>
@@ -76,10 +77,10 @@ dependencies:
76
77
  version: 0.9.5
77
78
  type: :development
78
79
  prerelease: false
79
- version_requirements: *2153180220
80
+ version_requirements: *70355886034140
80
81
  - !ruby/object:Gem::Dependency
81
82
  name: sqlite3
82
- requirement: &2153179740 !ruby/object:Gem::Requirement
83
+ requirement: &70355886033680 !ruby/object:Gem::Requirement
83
84
  none: false
84
85
  requirements:
85
86
  - - ~>
@@ -87,7 +88,7 @@ dependencies:
87
88
  version: 1.3.3
88
89
  type: :development
89
90
  prerelease: false
90
- version_requirements: *2153179740
91
+ version_requirements: *70355886033680
91
92
  description: Ransack is the successor to the MetaSearch gem. It improves and expands
92
93
  upon MetaSearch's functionality, but does not have a 100%-compatible API.
93
94
  email:
@@ -97,6 +98,7 @@ extensions: []
97
98
  extra_rdoc_files: []
98
99
  files:
99
100
  - .gitignore
101
+ - .travis.yml
100
102
  - Gemfile
101
103
  - LICENSE
102
104
  - README.md
@@ -153,6 +155,7 @@ files:
153
155
  - spec/spec_helper.rb
154
156
  - spec/support/en.yml
155
157
  - spec/support/schema.rb
158
+ has_rdoc: true
156
159
  homepage: http://metautonomo.us/projects/ransack
157
160
  licenses: []
158
161
  post_install_message:
@@ -173,7 +176,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
176
  version: '0'
174
177
  requirements: []
175
178
  rubyforge_project: ransack
176
- rubygems_version: 1.8.5
179
+ rubygems_version: 1.6.2
177
180
  signing_key:
178
181
  specification_version: 3
179
182
  summary: Object-based searching for ActiveRecord (currently).