ransack 0.5.3 → 0.5.4

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/.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).