ransack 1.4.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -69,7 +69,7 @@ module Ransack
69
69
  def respond_to?(method_id)
70
70
  super or begin
71
71
  method_name = method_id.to_s
72
- writer = method_name.sub!(/\=$/, '')
72
+ writer = method_name.sub!(/\=$/, Ransack::Constants::EMPTY)
73
73
  attribute_method?(method_name) ? true : false
74
74
  end
75
75
  end
@@ -115,7 +115,7 @@ module Ransack
115
115
 
116
116
  def method_missing(method_id, *args)
117
117
  method_name = method_id.to_s
118
- writer = method_name.sub!(/\=$/, '')
118
+ writer = method_name.sub!(/\=$/, Ransack::Constants::EMPTY)
119
119
  if attribute_method?(method_name)
120
120
  writer ?
121
121
  write_attribute(method_name, *args) :
@@ -126,12 +126,15 @@ module Ransack
126
126
  end
127
127
 
128
128
  def attribute_method?(name)
129
- name = strip_predicate_and_index(name)
130
- case name
129
+ stripped_name = strip_predicate_and_index(name)
130
+ return true if @context.attribute_method?(stripped_name) ||
131
+ @context.attribute_method?(name)
132
+ case stripped_name
131
133
  when /^(g|c|m|groupings|conditions|combinator)=?$/
132
134
  true
133
135
  else
134
- name.split(/_and_|_or_/)
136
+ stripped_name
137
+ .split(/_and_|_or_/)
135
138
  .select { |n| !@context.attribute_method?(n) }
136
139
  .empty?
137
140
  end
@@ -161,10 +164,13 @@ module Ransack
161
164
  end
162
165
 
163
166
  def inspect
164
- data = [['conditions', conditions], ['combinator', combinator]]
165
- .reject { |e| e[1].blank? }
166
- .map { |v| "#{v[0]}: #{v[1]}" }
167
- .join(', ')
167
+ data = [
168
+ ['conditions'.freeze, conditions],
169
+ [Ransack::Constants::COMBINATOR, combinator]
170
+ ]
171
+ .reject { |e| e[1].blank? }
172
+ .map { |v| "#{v[0]}: #{v[1]}" }
173
+ .join(Ransack::Constants::COMMA_SPACE)
168
174
  "Grouping <#{data}>"
169
175
  end
170
176
 
@@ -25,8 +25,8 @@ module Ransack
25
25
 
26
26
  def valid?
27
27
  bound? && attr &&
28
- context.klassify(parent).ransortable_attributes(context.auth_object)
29
- .include?(attr_name)
28
+ context.klassify(parent).ransortable_attributes(context.auth_object)
29
+ .include?(attr_name)
30
30
  end
31
31
 
32
32
  def name=(name)
@@ -35,8 +35,13 @@ module Ransack
35
35
  end
36
36
 
37
37
  def dir=(dir)
38
- dir = dir.try(:downcase)
39
- @dir = %w(asc desc).include?(dir) ? dir : 'asc'
38
+ dir = dir.downcase if dir
39
+ @dir =
40
+ if Ransack::Constants::ASC_DESC.include?(dir)
41
+ dir
42
+ else
43
+ Ransack::Constants::ASC
44
+ end
40
45
  end
41
46
 
42
47
  end
@@ -19,7 +19,7 @@ module Ransack
19
19
 
20
20
  def detect_and_strip_from_string!(str)
21
21
  if p = detect_from_string(str)
22
- str.sub! /_#{p}$/, ''
22
+ str.sub! /_#{p}$/, Ransack::Constants::EMPTY
23
23
  p
24
24
  end
25
25
  end
@@ -28,15 +28,15 @@ module Ransack
28
28
  names_by_decreasing_length.detect { |p| str.end_with?("_#{p}") }
29
29
  end
30
30
 
31
- def name_from_attribute_name(attribute_name)
32
- names_by_decreasing_length.detect {
33
- |p| attribute_name.to_s.match(/_#{p}$/)
34
- }
35
- end
31
+ # def name_from_attribute_name(attribute_name)
32
+ # names_by_decreasing_length.detect {
33
+ # |p| attribute_name.to_s.match(/_#{p}$/)
34
+ # }
35
+ # end
36
36
 
37
- def for_attribute_name(attribute_name)
38
- self.named(detect_from_string(attribute_name.to_s))
39
- end
37
+ # def for_attribute_name(attribute_name)
38
+ # self.named(detect_from_string(attribute_name.to_s))
39
+ # end
40
40
 
41
41
  end
42
42
 
@@ -49,7 +49,7 @@ module Ransack
49
49
  lambda { |v| v.respond_to?(:empty?) ? !v.empty? : !v.nil? }
50
50
  @compound = opts[:compound]
51
51
  @wants_array = opts[:wants_array] == true || @compound ||
52
- ['in', 'not_in'].include?(@arel_predicate)
52
+ Ransack::Constants::IN_NOT_IN.include?(@arel_predicate)
53
53
  end
54
54
 
55
55
  def eql?(other)
@@ -22,7 +22,10 @@ module Ransack
22
22
  end
23
23
  @context = options[:context] || Context.for(object, options)
24
24
  @context.auth_object = options[:auth_object]
25
- @base = Nodes::Grouping.new(@context, options[:grouping] || 'and')
25
+ @base = Nodes::Grouping.new(
26
+ @context,
27
+ options[:grouping] || Ransack::Constants::AND
28
+ )
26
29
  @scope_args = {}
27
30
  build(params.with_indifferent_access)
28
31
  end
@@ -33,7 +36,7 @@ module Ransack
33
36
 
34
37
  def build(params)
35
38
  collapse_multiparameter_attributes!(params).each do |key, value|
36
- if ['s', 'sorts'].include?(key)
39
+ if Ransack::Constants::S_SORTS.include?(key)
37
40
  send("#{key}=", value)
38
41
  elsif base.attribute_method?(key)
39
42
  base.send("#{key}=", value)
@@ -88,7 +91,7 @@ module Ransack
88
91
 
89
92
  def method_missing(method_id, *args)
90
93
  method_name = method_id.to_s
91
- getter_name = method_name.sub(/=$/, '')
94
+ getter_name = method_name.sub(/=$/, Ransack::Constants::EMPTY)
92
95
  if base.attribute_method?(getter_name)
93
96
  base.send(method_id, *args)
94
97
  elsif @context.ransackable_scope?(getter_name, @context.object)
@@ -107,7 +110,10 @@ module Ransack
107
110
  [:class, klass.name],
108
111
  ([:scope, @scope_args] if @scope_args.present?),
109
112
  [:base, base.inspect]
110
- ].compact.map { |d| d.join(': ') }.join(', ')
113
+ ]
114
+ .compact.map { |d| d.join(': '.freeze) }
115
+ .join(Ransack::Constants::COMMA_SPACE)
116
+
111
117
  "Ransack::Search<#{details}>"
112
118
  end
113
119
 
@@ -124,14 +130,15 @@ module Ransack
124
130
 
125
131
  def collapse_multiparameter_attributes!(attrs)
126
132
  attrs.keys.each do |k|
127
- if k.include?("(")
133
+ if k.include?('('.freeze)
128
134
  real_attribute, position = k.split(/\(|\)/)
129
- cast = %w(a s i).include?(position.last) ? position.last : nil
135
+ cast = %w(a s i).freeze.include?(position.last) ? position.last : nil
130
136
  position = position.to_i - 1
131
137
  value = attrs.delete(k)
132
138
  attrs[real_attribute] ||= []
133
- attrs[real_attribute][position] = if cast
134
- (value.blank? && cast == 'i') ? nil : value.send("to_#{cast}")
139
+ attrs[real_attribute][position] =
140
+ if cast
141
+ value.blank? && cast == 'i'.freeze ? nil : value.send("to_#{cast}")
135
142
  else
136
143
  value
137
144
  end
@@ -1,6 +1,8 @@
1
1
  require 'i18n'
2
2
 
3
- I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'locale', '*.yml')]
3
+ I18n.load_path += Dir[
4
+ File.join(File.dirname(__FILE__), 'locale'.freeze, '*.yml'.freeze)
5
+ ]
4
6
 
5
7
  module Ransack
6
8
  module Translate
@@ -23,7 +25,8 @@ module Ransack
23
25
  |x| x.respond_to?(:model_name)
24
26
  }
25
27
  predicate = Predicate.detect_from_string(original_name)
26
- attributes_str = original_name.sub(/_#{predicate}$/, '')
28
+ attributes_str = original_name
29
+ .sub(/_#{predicate}$/, Ransack::Constants::EMPTY)
27
30
  attribute_names = attributes_str.split(/_and_|_or_/)
28
31
  combinator = attributes_str.match(/_and_/) ? :and : :or
29
32
  defaults = base_ancestors.map do |klass|
@@ -71,7 +74,7 @@ module Ransack
71
74
  def self.attribute_name(context, name, include_associations = nil)
72
75
  @context, @name = context, name
73
76
  @assoc_path = context.association_path(name)
74
- @attr_name = @name.sub(/^#{@assoc_path}_/, '')
77
+ @attr_name = @name.sub(/^#{@assoc_path}_/, Ransack::Constants::EMPTY)
75
78
  associated_class = @context.traverse(@assoc_path) if @assoc_path.present?
76
79
  @include_associated = include_associations && associated_class
77
80
 
@@ -97,9 +100,9 @@ module Ransack
97
100
  def self.build_interpolations(associated_class)
98
101
  {
99
102
  :attr_fallback_name => attr_fallback_name(associated_class),
100
- :association_name => association_name
103
+ :association_name => association_name
101
104
  }
102
- .reject! { |_, value| value.nil? }
105
+ .reject { |_, value| value.nil? }
103
106
  end
104
107
 
105
108
  def self.attr_fallback_name(associated_class)
@@ -148,7 +151,7 @@ module Ransack
148
151
 
149
152
  def self.i18n_key(klass)
150
153
  if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
151
- klass.model_name.i18n_key.to_s.tr('.', '/')
154
+ klass.model_name.i18n_key.to_s.tr('.'.freeze, '/'.freeze)
152
155
  else
153
156
  klass.model_name.i18n_key.to_s
154
157
  end
@@ -1,3 +1,3 @@
1
1
  module Ransack
2
- VERSION = "1.4.1"
2
+ VERSION = "1.5.0"
3
3
  end
@@ -18,7 +18,11 @@ module Ransack
18
18
  end
19
19
 
20
20
  def visit_Ransack_Nodes_Grouping(object)
21
- object.combinator == 'or' ? visit_or(object) : visit_and(object)
21
+ if object.combinator == Ransack::Constants::OR
22
+ visit_or(object)
23
+ else
24
+ visit_and(object)
25
+ end
22
26
  end
23
27
 
24
28
  def visit_and(object)
@@ -61,7 +65,9 @@ module Ransack
61
65
  end
62
66
 
63
67
  DISPATCH = Hash.new do |hash, klass|
64
- hash[klass] = "visit_#{klass.name.gsub('::', '_')}"
68
+ hash[klass] = "visit_#{
69
+ klass.name.gsub('::'.freeze, Ransack::Constants::UNDERSCORE)
70
+ }"
65
71
  end
66
72
 
67
73
  end
@@ -24,12 +24,12 @@ module Ransack
24
24
  end
25
25
 
26
26
  it "applies true scopes" do
27
- search = Person.search('active' => true)
27
+ search = Person.search('active' => true)
28
28
  search.result.to_sql.should include "active = 1"
29
29
  end
30
30
 
31
31
  it "ignores unlisted scopes" do
32
- search = Person.search('restricted' => true)
32
+ search = Person.search('restricted' => true)
33
33
  search.result.to_sql.should_not include "restricted"
34
34
  end
35
35
 
@@ -6,8 +6,7 @@ module Ransack
6
6
 
7
7
  router = ActionDispatch::Routing::RouteSet.new
8
8
  router.draw do
9
- resources :people
10
- resources :notes
9
+ resources :people, :comments, :notes
11
10
  get ':controller(/:action(/:id(.:format)))'
12
11
  end
13
12
 
@@ -17,39 +16,57 @@ module Ransack
17
16
  before do
18
17
  @controller = ActionView::TestCase::TestController.new
19
18
  @controller.instance_variable_set(:@_routes, router)
20
- @controller.class_eval do
21
- include router.url_helpers
22
- end
23
-
24
- @controller.view_context_class.class_eval do
25
- include router.url_helpers
26
- end
27
-
19
+ @controller.class_eval { include router.url_helpers }
20
+ @controller.view_context_class.class_eval { include router.url_helpers }
28
21
  @s = Person.search
29
- @controller.view_context.search_form_for @s do |f|
30
- @f = f
31
- end
22
+ @controller.view_context.search_form_for(@s) { |f| @f = f }
32
23
  end
33
24
 
34
25
  it 'selects previously-entered time values with datetime_select' do
35
- @s.created_at_eq = [2011, 1, 2, 3, 4, 5]
26
+ date_values = %w(2011 1 2 03 04 05).freeze
27
+ # @s.created_at_eq = date_values # This works in Rails 4.x but not 3.x
28
+ @s.created_at_eq = [2011, 1, 2, 3, 4, 5] # so we have to do this
36
29
  html = @f.datetime_select(
37
- :created_at_eq,
38
- :use_month_numbers => true,
39
- :include_seconds => true
30
+ :created_at_eq, :use_month_numbers => true, :include_seconds => true
40
31
  )
41
- %w(2011 1 2 03 04 05).each do |val|
42
- expect(html).to match /<option selected="selected" value="#{val}">#{val}<\/option>/
43
- end
32
+ date_values.each { |val| expect(html).to include date_select_html(val) }
44
33
  end
45
34
 
46
35
  describe '#label' do
47
-
48
- it 'localizes attribute names' do
49
- html = @f.label :name_cont
50
- expect(html).to match /Full Name contains/
36
+ context 'with direct model attributes' do
37
+ it 'localizes attribute names' do
38
+ test_label(@f, :name_cont, /Full Name contains/)
39
+ test_label(@f, :only_admin_start, /admin uSer Only starts with/)
40
+ test_label(@f, :salary_gt, /wages greater than/)
41
+ test_label(@f, :awesome_true, /ransack is really awesome is true/)
42
+ end
43
+ it 'falls back to `attribute_name.capitalize` when no translation' do
44
+ test_label(@f, :email_cont, /Email contains/)
45
+ test_label(@f, :only_sort_start, /Only sort starts with/)
46
+ test_label(@f, :only_search_eq, /Only search equals/)
47
+ end
48
+ end
49
+ context 'with `has_many` association attributes' do
50
+ it 'localizes :"#{pluralized model}_#{attribute name}_#{predicate}"' do
51
+ test_label(@f, :articles_body_start, /Article maiN BoDy starts with/)
52
+ end
53
+ it 'falls back to `model_name.capitalize + attribute_name.capitalize` when no translation' do
54
+ test_label(@f, :articles_title_cont, /Article Title contains/)
55
+ test_label(@f, :articles_subject_header_start, /Article Subject header starts with/)
56
+ end
57
+ end
58
+ context 'with `belongs_to` association attributes' do
59
+ before do
60
+ @controller.view_context.search_form_for(Comment.search) { |f| @f = f }
61
+ end
62
+ it 'localizes :"#{singularized model}_#{attribute name}_#{predicate}"' do
63
+ test_label(@f, :article_body_start, /Article maiN BoDy starts with/)
64
+ end
65
+ it 'falls back to `model_name.capitalize + attribute_name.capitalize` when no translation' do
66
+ test_label(@f, :article_title_eq, /Article Title equals/)
67
+ test_label(@f, :article_subject_header_end, /Article Subject header ends with/)
68
+ end
51
69
  end
52
-
53
70
  end
54
71
 
55
72
  describe '#sort_link' do
@@ -63,7 +80,6 @@ module Ransack
63
80
  expect(sort_link).to match /sort_link/
64
81
  expect(sort_link).to match /Full Name<\/a>/
65
82
  end
66
-
67
83
  it 'sort_link for common attribute' do
68
84
  sort_link = @f.sort_link :id, :controller => 'people'
69
85
  expect(sort_link).to match /id<\/a>/
@@ -71,67 +87,56 @@ module Ransack
71
87
  end
72
88
 
73
89
  describe '#submit' do
74
-
75
90
  it 'localizes :search when no default value given' do
76
91
  html = @f.submit
77
92
  expect(html).to match /"Search"/
78
93
  end
79
-
80
94
  end
81
95
 
82
96
  describe '#attribute_select' do
83
-
84
97
  it 'returns ransackable attributes' do
85
98
  html = @f.attribute_select
86
- expect(html.split(/\n/).size).
87
- to eq(Person.ransackable_attributes.size + 1)
99
+ expect(html.split(/\n/).size).to eq(Person.ransackable_attributes.size + 1)
88
100
  Person.ransackable_attributes.each do |attribute|
89
101
  expect(html).to match /<option value="#{attribute}">/
90
102
  end
91
103
  end
92
-
93
104
  it 'returns ransackable attributes for associations with :associations' do
94
- attributes = Person.ransackable_attributes + Article.
95
- ransackable_attributes.map { |a| "articles_#{a}" }
105
+ attributes = Person.ransackable_attributes +
106
+ Article.ransackable_attributes.map { |a| "articles_#{a}" }
96
107
  html = @f.attribute_select(:associations => ['articles'])
97
108
  expect(html.split(/\n/).size).to eq(attributes.size)
98
109
  attributes.each do |attribute|
99
110
  expect(html).to match /<option value="#{attribute}">/
100
111
  end
101
112
  end
102
-
103
113
  it 'returns option groups for base and associations with :associations' do
104
114
  html = @f.attribute_select(:associations => ['articles'])
105
115
  [Person, Article].each do |model|
106
116
  expect(html).to match /<optgroup label="#{model}">/
107
117
  end
108
118
  end
109
-
110
119
  end
111
120
 
112
121
  describe '#predicate_select' do
113
-
114
122
  it 'returns predicates with predicate_select' do
115
123
  html = @f.predicate_select
116
124
  Predicate.names.each do |key|
117
125
  expect(html).to match /<option value="#{key}">/
118
126
  end
119
127
  end
120
-
121
128
  it 'filters predicates with single-value :only' do
122
129
  html = @f.predicate_select :only => 'eq'
123
130
  Predicate.names.reject { |k| k =~ /^eq/ }.each do |key|
124
131
  expect(html).not_to match /<option value="#{key}">/
125
132
  end
126
133
  end
127
-
128
134
  it 'filters predicates with multi-value :only' do
129
135
  html = @f.predicate_select only: [:eq, :lt]
130
136
  Predicate.names.reject { |k| k =~ /^(eq|lt)/ }.each do |key|
131
137
  expect(html).not_to match /<option value="#{key}">/
132
138
  end
133
139
  end
134
-
135
140
  it 'excludes compounds when compounds: false' do
136
141
  html = @f.predicate_select :compounds => false
137
142
  Predicate.names.select { |k| k =~ /_(any|all)$/ }.each do |key|
@@ -142,9 +147,7 @@ module Ransack
142
147
 
143
148
  context 'fields used in polymorphic relations as search attributes in form' do
144
149
  before do
145
- @controller.view_context.search_form_for Note.search do |f|
146
- @f = f
147
- end
150
+ @controller.view_context.search_form_for(Note.search) { |f| @f = f }
148
151
  end
149
152
  it 'accepts poly_id field' do
150
153
  html = @f.text_field(:notable_id_eq)
@@ -155,6 +158,23 @@ module Ransack
155
158
  expect(html).to match /id=\"q_notable_type_eq\"/
156
159
  end
157
160
  end
161
+
162
+ private
163
+
164
+ def test_label(f, query, expected)
165
+ expect(f.label query).to match expected
166
+ end
167
+
168
+ # Starting from Rails 4.2, the date_select html attributes are no longer
169
+ # `sort`ed (for a speed gain), so the tests have to be different:
170
+ def date_select_html(val)
171
+ if ::ActiveRecord::VERSION::STRING >= '4.2'.freeze
172
+ %(<option value="#{val}" selected="selected">#{val}</option>)
173
+ else
174
+ %(<option selected="selected" value="#{val}">#{val}</option>)
175
+ end
176
+ end
177
+
158
178
  end
159
179
  end
160
180
  end