presenting 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README +10 -0
- data/Rakefile +22 -0
- data/app/assets/javascript/grid.js +0 -0
- data/app/assets/javascript/search.js +13 -0
- data/app/assets/stylesheets/details-color.css +7 -0
- data/app/assets/stylesheets/details.css +10 -0
- data/app/assets/stylesheets/form.css +1 -0
- data/app/assets/stylesheets/grid-color.css +71 -0
- data/app/assets/stylesheets/grid.css +64 -0
- data/app/assets/stylesheets/search-color.css +16 -0
- data/app/assets/stylesheets/search.css +45 -0
- data/app/controllers/presentation/assets_controller.rb +42 -0
- data/app/views/presentations/_details.erb +11 -0
- data/app/views/presentations/_field_search.erb +14 -0
- data/app/views/presentations/_form.erb +20 -0
- data/app/views/presentations/_grid.erb +70 -0
- data/app/views/presentations/_search.erb +7 -0
- data/config/routes.rb +3 -0
- data/lib/presentation/base.rb +48 -0
- data/lib/presentation/details.rb +19 -0
- data/lib/presentation/field_search.rb +65 -0
- data/lib/presentation/form.rb +149 -0
- data/lib/presentation/grid.rb +160 -0
- data/lib/presentation/search.rb +9 -0
- data/lib/presenting/attribute.rb +51 -0
- data/lib/presenting/configurable.rb +10 -0
- data/lib/presenting/defaults.rb +10 -0
- data/lib/presenting/field_set.rb +26 -0
- data/lib/presenting/form_helpers.rb +51 -0
- data/lib/presenting/helpers.rb +110 -0
- data/lib/presenting/sanitize.rb +19 -0
- data/lib/presenting/search.rb +185 -0
- data/lib/presenting/sorting.rb +87 -0
- data/lib/presenting/view.rb +3 -0
- data/lib/presenting.rb +9 -0
- data/rails/init.rb +12 -0
- data/test/assets_test.rb +58 -0
- data/test/attribute_test.rb +61 -0
- data/test/configurable_test.rb +20 -0
- data/test/details_test.rb +68 -0
- data/test/field_search_test.rb +102 -0
- data/test/field_set_test.rb +46 -0
- data/test/form_test.rb +287 -0
- data/test/grid_test.rb +219 -0
- data/test/helpers_test.rb +72 -0
- data/test/presenting_test.rb +15 -0
- data/test/rails/app/controllers/application_controller.rb +15 -0
- data/test/rails/app/controllers/users_controller.rb +36 -0
- data/test/rails/app/helpers/application_helper.rb +3 -0
- data/test/rails/app/helpers/users_helper.rb +2 -0
- data/test/rails/app/models/user.rb +2 -0
- data/test/rails/app/views/layouts/application.html.erb +15 -0
- data/test/rails/app/views/users/index.html.erb +10 -0
- data/test/rails/app/views/users/new.html.erb +2 -0
- data/test/rails/app/views/users/show.html.erb +1 -0
- data/test/rails/config/boot.rb +109 -0
- data/test/rails/config/database.yml +17 -0
- data/test/rails/config/environment.rb +13 -0
- data/test/rails/config/environments/development.rb +17 -0
- data/test/rails/config/environments/production.rb +24 -0
- data/test/rails/config/environments/test.rb +22 -0
- data/test/rails/config/locales/en.yml +5 -0
- data/test/rails/config/routes.rb +5 -0
- data/test/rails/db/development.sqlite3 +0 -0
- data/test/rails/db/migrate/20090213085444_create_users.rb +13 -0
- data/test/rails/db/migrate/20090213085607_populate_users.rb +13 -0
- data/test/rails/db/schema.rb +23 -0
- data/test/rails/db/test.sqlite3 +0 -0
- data/test/rails/log/development.log +858 -0
- data/test/rails/public/404.html +30 -0
- data/test/rails/public/422.html +30 -0
- data/test/rails/public/500.html +33 -0
- data/test/rails/public/javascripts/application.js +2 -0
- data/test/rails/public/javascripts/jquery.livequery.min.js +11 -0
- data/test/rails/public/javascripts/prototype.js +4320 -0
- data/test/rails/script/console +3 -0
- data/test/rails/script/dbconsole +3 -0
- data/test/rails/script/destroy +3 -0
- data/test/rails/script/generate +3 -0
- data/test/rails/script/plugin +3 -0
- data/test/rails/script/runner +3 -0
- data/test/rails/script/server +3 -0
- data/test/sanitize_test.rb +15 -0
- data/test/search_conditions_test.rb +137 -0
- data/test/search_test.rb +30 -0
- data/test/sorting_test.rb +63 -0
- data/test/test_helper.rb +66 -0
- metadata +217 -0
@@ -0,0 +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
|
@@ -0,0 +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
|
data/lib/presenting.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
module Presenting
|
2
|
+
# prior to Rails 2.3, you need to call this manually from your config/routes.rb
|
3
|
+
def self.draw_routes(map)
|
4
|
+
map.namespace(:presentation) do |presentation|
|
5
|
+
presentation.stylesheet 'stylesheets/:id.css', :controller => 'assets', :action => 'stylesheet', :format => 'css'
|
6
|
+
presentation.javascript 'javascript/:id.js', :controller => 'assets', :action => 'javascript', :format => 'js'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
ActionView::Base.class_eval { include Presenting::Helpers }
|
2
|
+
ActionController::Base.const_set(:Search, Presenting::Search)
|
3
|
+
ActionController::Base.const_set(:Sorting, Presenting::Sorting)
|
4
|
+
ActionView::Helpers::FormBuilder.class_eval { include Presenting::FormHelpers }
|
5
|
+
|
6
|
+
if defined? Rails # because tests don't define the Rails module
|
7
|
+
if Rails.version.match(/^2\.2/)
|
8
|
+
ActionController::Base.view_paths << File.join(File.dirname(__FILE__), 'app', 'views')
|
9
|
+
ActiveSupport::Dependencies.load_paths << File.dirname(__FILE__) + '/app/controllers'
|
10
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/app/controllers/'
|
11
|
+
end
|
12
|
+
end
|
data/test/assets_test.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
require 'action_controller/test_case'
|
3
|
+
require 'action_controller/integration'
|
4
|
+
|
5
|
+
class Presentation::AssetsControllerTest < ActionController::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
ActionController::Base.perform_caching = false
|
9
|
+
end
|
10
|
+
|
11
|
+
# stylesheet
|
12
|
+
|
13
|
+
def test_stylesheet_routing_recognition
|
14
|
+
assert_recognizes({:controller => "presentation/assets", :action => "stylesheet", :id => "foo", :format => "css"}, "/presentation/stylesheets/foo.css")
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_stylesheet_routing_generation
|
18
|
+
assert_generates "/presentation/stylesheets/foo.css", {:controller => "presentation/assets", :action => "stylesheet", :id => "foo", :format => "css"}
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_named_stylesheet_routes
|
22
|
+
assert_equal "/presentation/stylesheets/foo.css", presentation_stylesheet_path("foo", :format => 'css')
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_retrieving_a_named_stylesheet
|
26
|
+
get :stylesheet, :id => 'grid', :format => 'css'
|
27
|
+
assert_response :success
|
28
|
+
assert_equal @response.body, File.read(File.join(PLUGIN_ROOT, 'app', 'assets', 'stylesheets', 'grid.css'))
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_retrieving_multiple_named_stylesheets
|
32
|
+
get :stylesheet, :id => 'grid,form', :format => 'css'
|
33
|
+
assert_response :success
|
34
|
+
assert @response.body.include?(File.read(File.join(PLUGIN_ROOT, 'app', 'assets', 'stylesheets', 'grid.css')))
|
35
|
+
assert @response.body.include?(File.read(File.join(PLUGIN_ROOT, 'app', 'assets', 'stylesheets', 'form.css')))
|
36
|
+
end
|
37
|
+
|
38
|
+
# javascript
|
39
|
+
|
40
|
+
def test_javascript_routing_recognition
|
41
|
+
assert_recognizes({:controller => "presentation/assets", :action => "javascript", :id => "foo", :format => "js"}, "/presentation/javascript/foo.js")
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_javascript_routing_generation
|
45
|
+
assert_generates "/presentation/javascript/foo.js", {:controller => "presentation/assets", :action => "javascript", :id => "foo", :format => "js"}
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_named_javascript_routes
|
49
|
+
assert_equal "/presentation/javascript/foo.js", presentation_javascript_path("foo", :format => 'js')
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_retrieving_multiple_named_javascripts
|
53
|
+
get :javascript, :id => 'grid,search', :format => 'js'
|
54
|
+
assert_response :success
|
55
|
+
assert @response.body.include?(File.read(File.join(PLUGIN_ROOT, 'app', 'assets', 'javascript', 'grid.js')))
|
56
|
+
assert @response.body.include?(File.read(File.join(PLUGIN_ROOT, 'app', 'assets', 'javascript', 'search.js')))
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +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
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class ConfigurableTest < Presenting::Test
|
4
|
+
class ConfigurableUser
|
5
|
+
include Presenting::Configurable
|
6
|
+
attr_accessor :first_name
|
7
|
+
attr_reader :name
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_setting_first_name_with_initializer
|
11
|
+
user = ConfigurableUser.new(:first_name => "Bob")
|
12
|
+
assert_equal 'Bob', user.first_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_setting_unsettable_attribute
|
16
|
+
assert_raises NoMethodError do
|
17
|
+
ConfigurableUser.new(:name => 'Bob Jenkins')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class DetailsTest < Presenting::Test
|
4
|
+
def setup
|
5
|
+
@d = Presentation::Details.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_adding_a_field_by_name
|
9
|
+
@d.fields = ["foo"]
|
10
|
+
assert_equal "foo", @d.fields.first.name, "name is stringified"
|
11
|
+
assert_equal "foo", @d.fields.first.value, "value is assumed to be a method"
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_adding_a_field_by_name_and_value
|
15
|
+
@d.fields = [{"foo" => :bar}]
|
16
|
+
assert_equal "foo", @d.fields.first.name, "key is name"
|
17
|
+
assert_equal :bar, @d.fields.first.value, "value is value"
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_adding_a_field_by_name_and_options
|
21
|
+
@d.fields = [{"foo" => {:value => :bar}}]
|
22
|
+
assert_equal "foo", @d.fields.first.name, "key is name"
|
23
|
+
assert_equal :bar, @d.fields.first.value, "value is found and saved"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class DetailsRenderTest < Presentation::RenderTest
|
28
|
+
def setup
|
29
|
+
@presentation = Presentation::Details.new
|
30
|
+
@presentation.presentable = stub('user', :name => 'foo', :email => 'foo@example.com')
|
31
|
+
@presentation.controller = TestController.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_rendering_a_title
|
35
|
+
@presentation.title = 'Hello World'
|
36
|
+
|
37
|
+
assert_select '.presentation-details h3', 'Hello World'
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_rendering_an_attribute
|
41
|
+
@presentation.fields = [:email]
|
42
|
+
|
43
|
+
assert_select '.presentation-details' do
|
44
|
+
assert_select 'dl' do
|
45
|
+
assert_select 'dt', 'Email'
|
46
|
+
assert_select 'dd', 'foo@example.com'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_rendering_sanitized_data
|
52
|
+
@presentation.fields = [
|
53
|
+
{:email => {:sanitize => false}},
|
54
|
+
{:name => {:sanitize => true}}
|
55
|
+
]
|
56
|
+
@presentation.presentable = stub('user', :name => '&', :email => '&')
|
57
|
+
|
58
|
+
assert_select '.presentation-details' do
|
59
|
+
assert_select 'dl' do
|
60
|
+
assert_select 'dt:nth-of-type(1)', 'Email'
|
61
|
+
assert_select 'dd:nth-of-type(1)', '&'
|
62
|
+
|
63
|
+
assert_select 'dt:nth-of-type(2)', 'Name'
|
64
|
+
assert_select 'dd:nth-of-type(2)', '&'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class FieldSearchTest < Presenting::Test
|
4
|
+
def setup
|
5
|
+
@s = Presentation::FieldSearch.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_adding_a_field_by_name
|
9
|
+
@s.fields = [:foo]
|
10
|
+
assert_equal "Foo", @s.fields.first.name
|
11
|
+
assert_equal :text, @s.fields.first.type
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_adding_a_field_by_name_and_type
|
15
|
+
@s.fields = [{"foo" => :bar}]
|
16
|
+
assert_equal "foo", @s.fields.first.name
|
17
|
+
assert_equal :bar, @s.fields.first.type
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_adding_a_field_by_name_and_options
|
21
|
+
@s.fields = [{"foo" => {:type => :bar}}]
|
22
|
+
assert_equal "foo", @s.fields.first.name
|
23
|
+
assert_equal :bar, @s.fields.first.type
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class FieldSearchFieldTest < Presenting::Test
|
28
|
+
def setup
|
29
|
+
@f = Presentation::FieldSearch::Field.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_assigning_a_symbol_name
|
33
|
+
@f.name = :foo
|
34
|
+
assert_equal "Foo", @f.name, "name is typecast to a string and titleized"
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_assigning_a_string_name
|
38
|
+
@f.name = "foo"
|
39
|
+
assert_equal "foo", @f.name, "name remains a string"
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_default_type
|
43
|
+
assert_equal :text, @f.type
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class FieldSearchRenderingTest < Presentation::RenderTest
|
48
|
+
def setup
|
49
|
+
@presentation = Presentation::FieldSearch.new
|
50
|
+
@presentation.controller = TestController.new
|
51
|
+
@presentation.controller.request.path = '/users'
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_rendering_a_form_reuses_current_path
|
55
|
+
assert_select 'form[action=/users?][method=get]'
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_rendering_a_text_field
|
59
|
+
@presentation.fields << {:foo => :text}
|
60
|
+
assert_select "form input[type=text][name='search[foo][value]']"
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_rendering_a_text_field_with_existing_value
|
64
|
+
@presentation.fields << {:foo => :text}
|
65
|
+
@presentation.controller.params[:search] = {'foo' => {:value => 'bar'}}
|
66
|
+
assert_select "form input[type=text][name='search[foo][value]'][value=bar]"
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_rendering_a_checkbox_field
|
70
|
+
@presentation.fields << {:foo => :checkbox}
|
71
|
+
assert_select "form input[type=checkbox][name='search[foo][value]'][value=1]"
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_rendering_a_checkbox_field_with_existing_value
|
75
|
+
@presentation.fields << {:foo => :checkbox}
|
76
|
+
@presentation.controller.params[:search] = {'foo' => {:value => '1'}}
|
77
|
+
|
78
|
+
assert_select "form input[type=checkbox][name='search[foo][value]'][value=1][checked=checked]"
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_rendering_a_dropdown_field_with_existing_value
|
82
|
+
@presentation.fields << {:foo => {:type => :dropdown, :options => [["Dollar", "$"], ["Kroner", "DKK"]]}}
|
83
|
+
@presentation.controller.params[:search] = {'foo' => {:value => 'DKK'}}
|
84
|
+
|
85
|
+
assert_select "form select[name='search[foo][value]']" do
|
86
|
+
assert_select "option[value='$']"
|
87
|
+
assert_select "option[value='$'][selected='selected']", false
|
88
|
+
assert_select "option[value='DKK'][selected='selected']"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_rendering_a_dropdown_field_with_integers_and_existing_value
|
93
|
+
@presentation.fields << {:foo => {:type => :dropdown, :options => [["Dollar", 1], ["Kroner", 2]]}}
|
94
|
+
@presentation.controller.params[:search] = {'foo' => {:value => '2'}}
|
95
|
+
|
96
|
+
assert_select "form select[name='search[foo][value]']" do
|
97
|
+
assert_select "option[value='1']"
|
98
|
+
assert_select "option[value='1'][selected='selected']", false
|
99
|
+
assert_select "option[value='2'][selected='selected']"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class FieldSetTest < Presenting::Test
|
4
|
+
def setup
|
5
|
+
@set = Presenting::FieldSet.new(Presenting::Attribute, :name, :value)
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_reading_a_field_by_name
|
9
|
+
@set << "foo"
|
10
|
+
assert_equal "foo", @set["foo"].name
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_pushing_a_field_by_name
|
14
|
+
@set << "foo"
|
15
|
+
assert_equal "foo", @set.first.name
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_pushing_a_field_by_name_and_value
|
19
|
+
@set << {"foo" => :bar}
|
20
|
+
assert_equal "foo", @set.first.name
|
21
|
+
assert_equal :bar, @set.first.value
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_pushing_a_field_by_name_and_options
|
25
|
+
@set << {"foo" => {:value => :bar}}
|
26
|
+
assert_equal "foo", @set.first.name
|
27
|
+
assert_equal :bar, @set.first.value
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_adding_a_field_by_name
|
31
|
+
@set["foo"] = nil
|
32
|
+
assert_equal "foo", @set.first.name
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_adding_a_field_by_name_and_value
|
36
|
+
@set["foo"] = :bar
|
37
|
+
assert_equal "foo", @set.first.name
|
38
|
+
assert_equal :bar, @set.first.value
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_adding_a_field_by_name_and_options
|
42
|
+
@set["foo"] = {:value => :bar}
|
43
|
+
assert_equal "foo", @set.first.name
|
44
|
+
assert_equal :bar, @set.first.value
|
45
|
+
end
|
46
|
+
end
|