presenting 2.0.0 → 2.0.1
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/LICENSE +20 -20
- data/README +10 -10
- data/Rakefile +22 -22
- data/app/assets/javascripts/search.js +13 -13
- data/app/assets/stylesheets/details-color.css +7 -7
- data/app/assets/stylesheets/details.css +10 -10
- data/app/assets/stylesheets/form.css +1 -1
- data/app/assets/stylesheets/grid-color.css +71 -71
- data/app/assets/stylesheets/grid.css +64 -64
- data/app/assets/stylesheets/search-color.css +16 -16
- data/app/assets/stylesheets/search.css +45 -45
- data/app/controllers/presentation/assets_controller.rb +42 -42
- data/app/views/presentations/_details.erb +11 -11
- data/app/views/presentations/_field_search.erb +14 -14
- data/app/views/presentations/_form.erb +20 -20
- data/app/views/presentations/_grid.erb +70 -70
- data/app/views/presentations/_search.erb +7 -7
- data/lib/presentation/base.rb +31 -31
- data/lib/presentation/details.rb +21 -21
- data/lib/presentation/field_search.rb +67 -67
- data/lib/presentation/form.rb +149 -149
- data/lib/presentation/grid.rb +162 -160
- data/lib/presentation/search.rb +9 -9
- data/lib/presenting/attribute.rb +51 -51
- data/lib/presenting/configurable.rb +10 -10
- data/lib/presenting/defaults.rb +10 -10
- data/lib/presenting/field_set.rb +26 -26
- data/lib/presenting/form_helpers.rb +51 -51
- data/lib/presenting/helpers.rb +114 -114
- data/lib/presenting/sanitize.rb +19 -19
- data/lib/presenting/search.rb +185 -185
- data/lib/presenting/sorting.rb +87 -87
- data/test/attribute_test.rb +61 -61
- data/test/configurable_test.rb +20 -20
- data/test/details_test.rb +68 -68
- data/test/field_search_test.rb +102 -102
- data/test/field_set_test.rb +46 -46
- data/test/grid_test.rb +246 -239
- data/test/helpers_test.rb +72 -72
- data/test/presenting_test.rb +15 -15
- data/test/r3/Gemfile +7 -7
- data/test/r3/Gemfile.lock +86 -82
- data/test/r3/config/application.rb +12 -12
- data/test/r3/config/boot.rb +6 -6
- data/test/r3/config/database.yml +22 -22
- data/test/r3/config/environment.rb +5 -5
- data/test/r3/config/environments/test.rb +35 -35
- data/test/r3/db/test.sqlite3 +0 -0
- data/test/r3/log/test.log +336 -0
- data/test/r3/public/javascripts/presenting/grid.js +0 -0
- data/test/r3/public/javascripts/presenting/search.js +13 -13
- data/test/r3/public/stylesheets/presenting/details-color.css +7 -7
- data/test/r3/public/stylesheets/presenting/details.css +10 -10
- data/test/r3/public/stylesheets/presenting/form.css +1 -1
- data/test/r3/public/stylesheets/presenting/grid-color.css +71 -71
- data/test/r3/public/stylesheets/presenting/grid.css +64 -64
- data/test/r3/public/stylesheets/presenting/search-color.css +16 -16
- data/test/r3/public/stylesheets/presenting/search.css +45 -45
- data/test/sanitize_test.rb +15 -15
- data/test/search_conditions_test.rb +137 -137
- data/test/search_test.rb +30 -30
- data/test/sorting_test.rb +63 -63
- metadata +74 -74
data/lib/presenting/search.rb
CHANGED
@@ -1,185 +1,185 @@
|
|
1
|
-
module Presenting
|
2
|
-
class Search
|
3
|
-
include Presenting::Configurable
|
4
|
-
|
5
|
-
# I want to support three configuration formats:
|
6
|
-
#
|
7
|
-
# Search.new(:fields => [:first_name, :last_name, :email])
|
8
|
-
#
|
9
|
-
# Search.new(:fields => {
|
10
|
-
# 'first_name' => :equals,
|
11
|
-
# 'last_name' => :begins_with,
|
12
|
-
# 'email' => :not_null
|
13
|
-
# })
|
14
|
-
#
|
15
|
-
# Search.new(:fields => {
|
16
|
-
# 'fname' => {:sql => 'first_name', :pattern => :equals},
|
17
|
-
# 'lname' => {:sql => 'last_name', :pattern => :begins_with},
|
18
|
-
# 'email' => {:sql => 'email', :pattern => :not_null}
|
19
|
-
# })
|
20
|
-
def fields=(obj)
|
21
|
-
case obj
|
22
|
-
when Array
|
23
|
-
obj.each do |name| fields << name end
|
24
|
-
|
25
|
-
when Hash
|
26
|
-
obj.each do |k, v|
|
27
|
-
fields << {k => v}
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def fields
|
33
|
-
@fields ||= FieldSet.new
|
34
|
-
end
|
35
|
-
|
36
|
-
def to_sql(params, type = :simple)
|
37
|
-
send("to_#{type}_sql", params) unless params.blank?
|
38
|
-
end
|
39
|
-
|
40
|
-
protected
|
41
|
-
|
42
|
-
# handles a simple search where a given term is matched against a number of fields, and can match any of them.
|
43
|
-
# this is usually presented to the user as a single "smart" search box.
|
44
|
-
def to_simple_sql(term)
|
45
|
-
sql = fields.map(&:fragment).join(' OR ')
|
46
|
-
binds = fields.collect{|f| f.bind(term)}.compact
|
47
|
-
[sql, binds].flatten.compact
|
48
|
-
end
|
49
|
-
|
50
|
-
# handles a search setup where a user may enter a search value for any field, and anything entered must match.
|
51
|
-
# this is usually presented to the user as a set of labeled search boxes.
|
52
|
-
#
|
53
|
-
# example field terms:
|
54
|
-
# field_terms = {
|
55
|
-
# 'first_name' => {:value => 'Bob'}
|
56
|
-
# 'last_name' => {:value => 'Smith'}
|
57
|
-
# }
|
58
|
-
#
|
59
|
-
def to_field_sql(field_terms)
|
60
|
-
searched_fields = fields.select{|f| field_terms[f.name] and not field_terms[f.name][:value].blank?}
|
61
|
-
unless searched_fields.empty?
|
62
|
-
sql = searched_fields.map(&:fragment).join(' AND ')
|
63
|
-
binds = searched_fields.collect{|f| f.bind(field_terms[f.name][:value])}
|
64
|
-
|
65
|
-
[sql, binds].flatten.compact
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
class FieldSet < Array
|
70
|
-
def <<(val)
|
71
|
-
if val.is_a? Hash
|
72
|
-
k, v = *val.to_a.first
|
73
|
-
opts = v.is_a?(Hash) ? v : {:pattern => v}
|
74
|
-
opts[:name] = k
|
75
|
-
else
|
76
|
-
opts = {:name => val}
|
77
|
-
end
|
78
|
-
super Field.new(opts)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# TODO: a field may require extra joins when it is searched on
|
83
|
-
# TODO: support more than just mysql (need access to a Connection for quoting and attribute conditions)
|
84
|
-
class Field
|
85
|
-
include Presenting::Configurable
|
86
|
-
|
87
|
-
# required (this is what appears in the parameter hash)
|
88
|
-
attr_reader :name
|
89
|
-
def name=(val)
|
90
|
-
@name = val.to_s
|
91
|
-
end
|
92
|
-
|
93
|
-
# sql field (default == name)
|
94
|
-
def sql
|
95
|
-
@sql ||= name
|
96
|
-
end
|
97
|
-
attr_writer :sql
|
98
|
-
|
99
|
-
# a shortcut for common operator/bind_pattern combos
|
100
|
-
def pattern=(val)
|
101
|
-
case val
|
102
|
-
when :equals
|
103
|
-
self.operator = '= ?'
|
104
|
-
self.bind_pattern = '?'
|
105
|
-
when :begins_with
|
106
|
-
self.operator = 'LIKE ?'
|
107
|
-
self.bind_pattern = '?%'
|
108
|
-
when :ends_with
|
109
|
-
self.operator = 'LIKE ?'
|
110
|
-
self.bind_pattern = '%?'
|
111
|
-
when :contains
|
112
|
-
self.operator = 'LIKE ?'
|
113
|
-
self.bind_pattern = '%?%'
|
114
|
-
when :null
|
115
|
-
self.operator = 'IS NULL'
|
116
|
-
when :not_null
|
117
|
-
self.operator = 'IS NOT NULL'
|
118
|
-
when :true
|
119
|
-
self.operator = '= ?'
|
120
|
-
self.bind_pattern = true
|
121
|
-
when :false
|
122
|
-
self.operator = '= ?'
|
123
|
-
self.bind_pattern = false
|
124
|
-
when :less_than
|
125
|
-
self.operator = '< ?'
|
126
|
-
when :less_than_or_equal_to, :not_greater_than
|
127
|
-
self.operator = '<= ?'
|
128
|
-
when :greater_than
|
129
|
-
self.operator = '> ?'
|
130
|
-
when :greater_than_or_equal_to, :not_less_than
|
131
|
-
self.operator = '>= ?'
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
# the format for comparison with :sql, with an optional bind for search terms
|
136
|
-
# '= ?', 'LIKE ?', 'IN (?)', etc.
|
137
|
-
def operator
|
138
|
-
@operator ||= '= ?'
|
139
|
-
end
|
140
|
-
attr_writer :operator
|
141
|
-
|
142
|
-
# formats the term BEFORE binding into the sql
|
143
|
-
# e.g. '?', '?%', etc.
|
144
|
-
def bind_pattern
|
145
|
-
@bind_pattern ||= '?'
|
146
|
-
end
|
147
|
-
attr_writer :bind_pattern
|
148
|
-
|
149
|
-
# composes the sql fragment
|
150
|
-
def fragment
|
151
|
-
"#{sql} #{operator}"
|
152
|
-
end
|
153
|
-
|
154
|
-
# prepares the bindable term
|
155
|
-
def bind(term)
|
156
|
-
return nil unless operator.include?('?')
|
157
|
-
return bind_pattern unless bind_pattern.is_a? String
|
158
|
-
bind_pattern == '?' ? typecast(term) : bind_pattern.sub('?', typecast(term).to_s)
|
159
|
-
end
|
160
|
-
|
161
|
-
# you can set a data type for the field, which will be used to convert
|
162
|
-
# parameter values. currently this is mostly useful for :time searches.
|
163
|
-
attr_accessor :type
|
164
|
-
|
165
|
-
protected
|
166
|
-
|
167
|
-
def typecast(val)
|
168
|
-
case type
|
169
|
-
when :date
|
170
|
-
val.is_a?(String) ?
|
171
|
-
(Time.zone ? Time.zone.parse(val) : Time.parse(val)).to_date :
|
172
|
-
val
|
173
|
-
|
174
|
-
when :time, :datetime
|
175
|
-
val.is_a?(String) ?
|
176
|
-
(Time.zone ? Time.zone.parse(val) : Time.parse(val)) :
|
177
|
-
val
|
178
|
-
|
179
|
-
else
|
180
|
-
val.to_s.strip
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
1
|
+
module Presenting
|
2
|
+
class Search
|
3
|
+
include Presenting::Configurable
|
4
|
+
|
5
|
+
# I want to support three configuration formats:
|
6
|
+
#
|
7
|
+
# Search.new(:fields => [:first_name, :last_name, :email])
|
8
|
+
#
|
9
|
+
# Search.new(:fields => {
|
10
|
+
# 'first_name' => :equals,
|
11
|
+
# 'last_name' => :begins_with,
|
12
|
+
# 'email' => :not_null
|
13
|
+
# })
|
14
|
+
#
|
15
|
+
# Search.new(:fields => {
|
16
|
+
# 'fname' => {:sql => 'first_name', :pattern => :equals},
|
17
|
+
# 'lname' => {:sql => 'last_name', :pattern => :begins_with},
|
18
|
+
# 'email' => {:sql => 'email', :pattern => :not_null}
|
19
|
+
# })
|
20
|
+
def fields=(obj)
|
21
|
+
case obj
|
22
|
+
when Array
|
23
|
+
obj.each do |name| fields << name end
|
24
|
+
|
25
|
+
when Hash
|
26
|
+
obj.each do |k, v|
|
27
|
+
fields << {k => v}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def fields
|
33
|
+
@fields ||= FieldSet.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_sql(params, type = :simple)
|
37
|
+
send("to_#{type}_sql", params) unless params.blank?
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
# handles a simple search where a given term is matched against a number of fields, and can match any of them.
|
43
|
+
# this is usually presented to the user as a single "smart" search box.
|
44
|
+
def to_simple_sql(term)
|
45
|
+
sql = fields.map(&:fragment).join(' OR ')
|
46
|
+
binds = fields.collect{|f| f.bind(term)}.compact
|
47
|
+
[sql, binds].flatten.compact
|
48
|
+
end
|
49
|
+
|
50
|
+
# handles a search setup where a user may enter a search value for any field, and anything entered must match.
|
51
|
+
# this is usually presented to the user as a set of labeled search boxes.
|
52
|
+
#
|
53
|
+
# example field terms:
|
54
|
+
# field_terms = {
|
55
|
+
# 'first_name' => {:value => 'Bob'}
|
56
|
+
# 'last_name' => {:value => 'Smith'}
|
57
|
+
# }
|
58
|
+
#
|
59
|
+
def to_field_sql(field_terms)
|
60
|
+
searched_fields = fields.select{|f| field_terms[f.name] and not field_terms[f.name][:value].blank?}
|
61
|
+
unless searched_fields.empty?
|
62
|
+
sql = searched_fields.map(&:fragment).join(' AND ')
|
63
|
+
binds = searched_fields.collect{|f| f.bind(field_terms[f.name][:value])}
|
64
|
+
|
65
|
+
[sql, binds].flatten.compact
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class FieldSet < Array
|
70
|
+
def <<(val)
|
71
|
+
if val.is_a? Hash
|
72
|
+
k, v = *val.to_a.first
|
73
|
+
opts = v.is_a?(Hash) ? v : {:pattern => v}
|
74
|
+
opts[:name] = k
|
75
|
+
else
|
76
|
+
opts = {:name => val}
|
77
|
+
end
|
78
|
+
super Field.new(opts)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# TODO: a field may require extra joins when it is searched on
|
83
|
+
# TODO: support more than just mysql (need access to a Connection for quoting and attribute conditions)
|
84
|
+
class Field
|
85
|
+
include Presenting::Configurable
|
86
|
+
|
87
|
+
# required (this is what appears in the parameter hash)
|
88
|
+
attr_reader :name
|
89
|
+
def name=(val)
|
90
|
+
@name = val.to_s
|
91
|
+
end
|
92
|
+
|
93
|
+
# sql field (default == name)
|
94
|
+
def sql
|
95
|
+
@sql ||= name
|
96
|
+
end
|
97
|
+
attr_writer :sql
|
98
|
+
|
99
|
+
# a shortcut for common operator/bind_pattern combos
|
100
|
+
def pattern=(val)
|
101
|
+
case val
|
102
|
+
when :equals
|
103
|
+
self.operator = '= ?'
|
104
|
+
self.bind_pattern = '?'
|
105
|
+
when :begins_with
|
106
|
+
self.operator = 'LIKE ?'
|
107
|
+
self.bind_pattern = '?%'
|
108
|
+
when :ends_with
|
109
|
+
self.operator = 'LIKE ?'
|
110
|
+
self.bind_pattern = '%?'
|
111
|
+
when :contains
|
112
|
+
self.operator = 'LIKE ?'
|
113
|
+
self.bind_pattern = '%?%'
|
114
|
+
when :null
|
115
|
+
self.operator = 'IS NULL'
|
116
|
+
when :not_null
|
117
|
+
self.operator = 'IS NOT NULL'
|
118
|
+
when :true
|
119
|
+
self.operator = '= ?'
|
120
|
+
self.bind_pattern = true
|
121
|
+
when :false
|
122
|
+
self.operator = '= ?'
|
123
|
+
self.bind_pattern = false
|
124
|
+
when :less_than
|
125
|
+
self.operator = '< ?'
|
126
|
+
when :less_than_or_equal_to, :not_greater_than
|
127
|
+
self.operator = '<= ?'
|
128
|
+
when :greater_than
|
129
|
+
self.operator = '> ?'
|
130
|
+
when :greater_than_or_equal_to, :not_less_than
|
131
|
+
self.operator = '>= ?'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# the format for comparison with :sql, with an optional bind for search terms
|
136
|
+
# '= ?', 'LIKE ?', 'IN (?)', etc.
|
137
|
+
def operator
|
138
|
+
@operator ||= '= ?'
|
139
|
+
end
|
140
|
+
attr_writer :operator
|
141
|
+
|
142
|
+
# formats the term BEFORE binding into the sql
|
143
|
+
# e.g. '?', '?%', etc.
|
144
|
+
def bind_pattern
|
145
|
+
@bind_pattern ||= '?'
|
146
|
+
end
|
147
|
+
attr_writer :bind_pattern
|
148
|
+
|
149
|
+
# composes the sql fragment
|
150
|
+
def fragment
|
151
|
+
"#{sql} #{operator}"
|
152
|
+
end
|
153
|
+
|
154
|
+
# prepares the bindable term
|
155
|
+
def bind(term)
|
156
|
+
return nil unless operator.include?('?')
|
157
|
+
return bind_pattern unless bind_pattern.is_a? String
|
158
|
+
bind_pattern == '?' ? typecast(term) : bind_pattern.sub('?', typecast(term).to_s)
|
159
|
+
end
|
160
|
+
|
161
|
+
# you can set a data type for the field, which will be used to convert
|
162
|
+
# parameter values. currently this is mostly useful for :time searches.
|
163
|
+
attr_accessor :type
|
164
|
+
|
165
|
+
protected
|
166
|
+
|
167
|
+
def typecast(val)
|
168
|
+
case type
|
169
|
+
when :date
|
170
|
+
val.is_a?(String) ?
|
171
|
+
(Time.zone ? Time.zone.parse(val) : Time.parse(val)).to_date :
|
172
|
+
val
|
173
|
+
|
174
|
+
when :time, :datetime
|
175
|
+
val.is_a?(String) ?
|
176
|
+
(Time.zone ? Time.zone.parse(val) : Time.parse(val)) :
|
177
|
+
val
|
178
|
+
|
179
|
+
else
|
180
|
+
val.to_s.strip
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
data/lib/presenting/sorting.rb
CHANGED
@@ -1,87 +1,87 @@
|
|
1
|
-
module Presenting
|
2
|
-
class Sorting
|
3
|
-
include Presenting::Configurable
|
4
|
-
|
5
|
-
# I want to support two configuration formats:
|
6
|
-
#
|
7
|
-
# Sorting.new(:fields => [:first_name, :last_name, :email])
|
8
|
-
#
|
9
|
-
# Sorting.new(:fields => {
|
10
|
-
# 'name' => 'CONCAT(first_name, last_name)',
|
11
|
-
# 'email' => 'email_address'
|
12
|
-
# })
|
13
|
-
def fields=(obj)
|
14
|
-
case obj
|
15
|
-
when Array
|
16
|
-
obj.each do |name| fields << name end
|
17
|
-
|
18
|
-
when Hash
|
19
|
-
obj.each do |k, v|
|
20
|
-
fields << {k => v}
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def fields
|
26
|
-
@fields ||= FieldSet.new
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_sql(param)
|
30
|
-
fields.each do |field|
|
31
|
-
# search for and return the first known field
|
32
|
-
return "#{field.sql} #{desc_or_asc param[field.name]}" if param[field.name]
|
33
|
-
end unless param.blank?
|
34
|
-
# no known fields found
|
35
|
-
default
|
36
|
-
end
|
37
|
-
|
38
|
-
# The default sorting, if no known fields are found in the parameters.
|
39
|
-
# Default sorting is specified by name/direction, using an array.
|
40
|
-
#
|
41
|
-
# Example:
|
42
|
-
#
|
43
|
-
# @sorting.default = [:name, 'asc']
|
44
|
-
#
|
45
|
-
def default
|
46
|
-
@default ||= "#{fields.first.sql} ASC"
|
47
|
-
end
|
48
|
-
def default=(val)
|
49
|
-
@default = "#{fields.find{|f| f.name == val.first.to_s}.sql} #{desc_or_asc val.second}"
|
50
|
-
end
|
51
|
-
|
52
|
-
protected
|
53
|
-
|
54
|
-
def desc_or_asc(val)
|
55
|
-
val.to_s.downcase == 'desc' ? 'DESC' : 'ASC'
|
56
|
-
end
|
57
|
-
|
58
|
-
class FieldSet < Array
|
59
|
-
def <<(val)
|
60
|
-
if val.is_a? Hash
|
61
|
-
k, v = *val.to_a.first
|
62
|
-
opts = v.is_a?(Hash) ? v : {:sql => v}
|
63
|
-
opts[:name] = k
|
64
|
-
else
|
65
|
-
opts = {:name => val}
|
66
|
-
end
|
67
|
-
super Field.new(opts)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
class Field
|
72
|
-
include Presenting::Configurable
|
73
|
-
|
74
|
-
# required (this is what appears in the parameter hash)
|
75
|
-
attr_reader :name
|
76
|
-
def name=(val)
|
77
|
-
@name = val.to_s
|
78
|
-
end
|
79
|
-
|
80
|
-
# sql field (default == name)
|
81
|
-
attr_writer :sql
|
82
|
-
def sql
|
83
|
-
@sql ||= self.name.to_s
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
1
|
+
module Presenting
|
2
|
+
class Sorting
|
3
|
+
include Presenting::Configurable
|
4
|
+
|
5
|
+
# I want to support two configuration formats:
|
6
|
+
#
|
7
|
+
# Sorting.new(:fields => [:first_name, :last_name, :email])
|
8
|
+
#
|
9
|
+
# Sorting.new(:fields => {
|
10
|
+
# 'name' => 'CONCAT(first_name, last_name)',
|
11
|
+
# 'email' => 'email_address'
|
12
|
+
# })
|
13
|
+
def fields=(obj)
|
14
|
+
case obj
|
15
|
+
when Array
|
16
|
+
obj.each do |name| fields << name end
|
17
|
+
|
18
|
+
when Hash
|
19
|
+
obj.each do |k, v|
|
20
|
+
fields << {k => v}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def fields
|
26
|
+
@fields ||= FieldSet.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_sql(param)
|
30
|
+
fields.each do |field|
|
31
|
+
# search for and return the first known field
|
32
|
+
return "#{field.sql} #{desc_or_asc param[field.name]}" if param[field.name]
|
33
|
+
end unless param.blank?
|
34
|
+
# no known fields found
|
35
|
+
default
|
36
|
+
end
|
37
|
+
|
38
|
+
# The default sorting, if no known fields are found in the parameters.
|
39
|
+
# Default sorting is specified by name/direction, using an array.
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
#
|
43
|
+
# @sorting.default = [:name, 'asc']
|
44
|
+
#
|
45
|
+
def default
|
46
|
+
@default ||= "#{fields.first.sql} ASC"
|
47
|
+
end
|
48
|
+
def default=(val)
|
49
|
+
@default = "#{fields.find{|f| f.name == val.first.to_s}.sql} #{desc_or_asc val.second}"
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
def desc_or_asc(val)
|
55
|
+
val.to_s.downcase == 'desc' ? 'DESC' : 'ASC'
|
56
|
+
end
|
57
|
+
|
58
|
+
class FieldSet < Array
|
59
|
+
def <<(val)
|
60
|
+
if val.is_a? Hash
|
61
|
+
k, v = *val.to_a.first
|
62
|
+
opts = v.is_a?(Hash) ? v : {:sql => v}
|
63
|
+
opts[:name] = k
|
64
|
+
else
|
65
|
+
opts = {:name => val}
|
66
|
+
end
|
67
|
+
super Field.new(opts)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Field
|
72
|
+
include Presenting::Configurable
|
73
|
+
|
74
|
+
# required (this is what appears in the parameter hash)
|
75
|
+
attr_reader :name
|
76
|
+
def name=(val)
|
77
|
+
@name = val.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
# sql field (default == name)
|
81
|
+
attr_writer :sql
|
82
|
+
def sql
|
83
|
+
@sql ||= self.name.to_s
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/test/attribute_test.rb
CHANGED
@@ -1,61 +1,61 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/test_helper'
|
2
|
-
|
3
|
-
class AttributeTest < Presenting::Test
|
4
|
-
|
5
|
-
def setup
|
6
|
-
@a = Presenting::Attribute.new
|
7
|
-
end
|
8
|
-
|
9
|
-
def test_assigning_a_symbol_name
|
10
|
-
@a.name = :foo
|
11
|
-
assert_equal "Foo", @a.name, "name is typecast to a string and titleized"
|
12
|
-
assert_equal :foo, @a.value, "value is assumed to be a symbol as well"
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_assigning_a_string_name
|
16
|
-
@a.name = "foo"
|
17
|
-
assert_equal "foo", @a.name, "name remains a string"
|
18
|
-
assert_equal "foo", @a.value, "value is assumed to be a string"
|
19
|
-
end
|
20
|
-
|
21
|
-
def test_symbol_values
|
22
|
-
@a.value = :foo
|
23
|
-
assert_equal "bar", @a.value_from(stub('row', :foo => "bar")), "symbols are methods"
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_string_values
|
27
|
-
@a.value = "foo"
|
28
|
-
assert_equal "foo", @a.value_from(stub('row', :foo => "bar")), "strings are constant"
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_proc_values
|
32
|
-
@a.value = proc{|row| "hello"}
|
33
|
-
assert_equal "hello", @a.value_from(stub('row', :foo => "bar")), "procs are custom"
|
34
|
-
end
|
35
|
-
|
36
|
-
def test_that_value_from_does_not_sanitizes_itself
|
37
|
-
@a.value = '<span>hello</span>'
|
38
|
-
@a.sanitize = true
|
39
|
-
assert_equal '<span>hello</span>', @a.value_from(nil)
|
40
|
-
end
|
41
|
-
|
42
|
-
def test_hash_rows_with_symbol_values
|
43
|
-
@a.value = :foo
|
44
|
-
assert_equal 'bar', @a.value_from({:foo => 'bar'}), "symbols are hash keys"
|
45
|
-
end
|
46
|
-
|
47
|
-
def test_sanitize_is_default_true
|
48
|
-
assert @a.sanitize?
|
49
|
-
end
|
50
|
-
|
51
|
-
def test_assigning_a_symbol_id
|
52
|
-
@a.id = :foo
|
53
|
-
assert_equal 'foo', @a.id
|
54
|
-
end
|
55
|
-
|
56
|
-
def test_default_id_from_complex_name
|
57
|
-
@a.name = 'Hello, World!'
|
58
|
-
assert_equal 'hello_world', @a.id
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class AttributeTest < Presenting::Test
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@a = Presenting::Attribute.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_assigning_a_symbol_name
|
10
|
+
@a.name = :foo
|
11
|
+
assert_equal "Foo", @a.name, "name is typecast to a string and titleized"
|
12
|
+
assert_equal :foo, @a.value, "value is assumed to be a symbol as well"
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_assigning_a_string_name
|
16
|
+
@a.name = "foo"
|
17
|
+
assert_equal "foo", @a.name, "name remains a string"
|
18
|
+
assert_equal "foo", @a.value, "value is assumed to be a string"
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_symbol_values
|
22
|
+
@a.value = :foo
|
23
|
+
assert_equal "bar", @a.value_from(stub('row', :foo => "bar")), "symbols are methods"
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_string_values
|
27
|
+
@a.value = "foo"
|
28
|
+
assert_equal "foo", @a.value_from(stub('row', :foo => "bar")), "strings are constant"
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_proc_values
|
32
|
+
@a.value = proc{|row| "hello"}
|
33
|
+
assert_equal "hello", @a.value_from(stub('row', :foo => "bar")), "procs are custom"
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_that_value_from_does_not_sanitizes_itself
|
37
|
+
@a.value = '<span>hello</span>'
|
38
|
+
@a.sanitize = true
|
39
|
+
assert_equal '<span>hello</span>', @a.value_from(nil)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_hash_rows_with_symbol_values
|
43
|
+
@a.value = :foo
|
44
|
+
assert_equal 'bar', @a.value_from({:foo => 'bar'}), "symbols are hash keys"
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_sanitize_is_default_true
|
48
|
+
assert @a.sanitize?
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_assigning_a_symbol_id
|
52
|
+
@a.id = :foo
|
53
|
+
assert_equal 'foo', @a.id
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_default_id_from_complex_name
|
57
|
+
@a.name = 'Hello, World!'
|
58
|
+
assert_equal 'hello_world', @a.id
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|