ransack 1.4.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|