ransack 1.4.1 → 1.5.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.
@@ -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