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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +75 -0
- data/CONTRIBUTING.md +7 -7
- data/Gemfile +7 -0
- data/README.md +139 -34
- data/lib/ransack/adapters/active_record/3.0/compat.rb +5 -5
- data/lib/ransack/adapters/active_record/3.0/context.rb +32 -16
- data/lib/ransack/adapters/active_record/3.1/context.rb +31 -16
- data/lib/ransack/adapters/active_record/context.rb +34 -26
- data/lib/ransack/configuration.rb +1 -1
- data/lib/ransack/constants.rb +72 -37
- data/lib/ransack/context.rb +18 -12
- data/lib/ransack/helpers/form_builder.rb +32 -15
- data/lib/ransack/helpers/form_helper.rb +83 -55
- data/lib/ransack/naming.rb +1 -1
- data/lib/ransack/nodes/condition.rb +9 -9
- data/lib/ransack/nodes/grouping.rb +15 -9
- data/lib/ransack/nodes/sort.rb +9 -4
- data/lib/ransack/predicate.rb +10 -10
- data/lib/ransack/search.rb +15 -8
- data/lib/ransack/translate.rb +9 -6
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +8 -2
- data/spec/ransack/adapters/active_record/base_spec.rb +2 -2
- data/spec/ransack/helpers/form_builder_spec.rb +63 -43
- data/spec/ransack/helpers/form_helper_spec.rb +163 -2
- data/spec/ransack/nodes/grouping_spec.rb +44 -1
- data/spec/ransack/predicate_spec.rb +165 -3
- data/spec/ransack/translate_spec.rb +4 -1
- data/spec/support/en.yml +5 -0
- data/spec/support/schema.rb +6 -0
- metadata +3 -3
@@ -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
|
-
|
130
|
-
|
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
|
-
|
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 = [
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
|
data/lib/ransack/nodes/sort.rb
CHANGED
@@ -25,8 +25,8 @@ module Ransack
|
|
25
25
|
|
26
26
|
def valid?
|
27
27
|
bound? && attr &&
|
28
|
-
|
29
|
-
|
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.
|
39
|
-
@dir =
|
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
|
data/lib/ransack/predicate.rb
CHANGED
@@ -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
|
-
|
52
|
+
Ransack::Constants::IN_NOT_IN.include?(@arel_predicate)
|
53
53
|
end
|
54
54
|
|
55
55
|
def eql?(other)
|
data/lib/ransack/search.rb
CHANGED
@@ -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(
|
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
|
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
|
-
]
|
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] =
|
134
|
-
|
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
|
data/lib/ransack/translate.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'i18n'
|
2
2
|
|
3
|
-
I18n.load_path += Dir[
|
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
|
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
|
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
|
data/lib/ransack/version.rb
CHANGED
data/lib/ransack/visitor.rb
CHANGED
@@ -18,7 +18,11 @@ module Ransack
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def visit_Ransack_Nodes_Grouping(object)
|
21
|
-
object.combinator ==
|
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_#{
|
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 =
|
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 =
|
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
|
21
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
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 +
|
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
|
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
|