presenting 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/LICENSE +20 -0
  2. data/README +10 -0
  3. data/Rakefile +22 -0
  4. data/app/assets/javascript/grid.js +0 -0
  5. data/app/assets/javascript/search.js +13 -0
  6. data/app/assets/stylesheets/details-color.css +7 -0
  7. data/app/assets/stylesheets/details.css +10 -0
  8. data/app/assets/stylesheets/form.css +1 -0
  9. data/app/assets/stylesheets/grid-color.css +71 -0
  10. data/app/assets/stylesheets/grid.css +64 -0
  11. data/app/assets/stylesheets/search-color.css +16 -0
  12. data/app/assets/stylesheets/search.css +45 -0
  13. data/app/controllers/presentation/assets_controller.rb +42 -0
  14. data/app/views/presentations/_details.erb +11 -0
  15. data/app/views/presentations/_field_search.erb +14 -0
  16. data/app/views/presentations/_form.erb +20 -0
  17. data/app/views/presentations/_grid.erb +70 -0
  18. data/app/views/presentations/_search.erb +7 -0
  19. data/config/routes.rb +3 -0
  20. data/lib/presentation/base.rb +48 -0
  21. data/lib/presentation/details.rb +19 -0
  22. data/lib/presentation/field_search.rb +65 -0
  23. data/lib/presentation/form.rb +149 -0
  24. data/lib/presentation/grid.rb +160 -0
  25. data/lib/presentation/search.rb +9 -0
  26. data/lib/presenting/attribute.rb +51 -0
  27. data/lib/presenting/configurable.rb +10 -0
  28. data/lib/presenting/defaults.rb +10 -0
  29. data/lib/presenting/field_set.rb +26 -0
  30. data/lib/presenting/form_helpers.rb +51 -0
  31. data/lib/presenting/helpers.rb +110 -0
  32. data/lib/presenting/sanitize.rb +19 -0
  33. data/lib/presenting/search.rb +185 -0
  34. data/lib/presenting/sorting.rb +87 -0
  35. data/lib/presenting/view.rb +3 -0
  36. data/lib/presenting.rb +9 -0
  37. data/rails/init.rb +12 -0
  38. data/test/assets_test.rb +58 -0
  39. data/test/attribute_test.rb +61 -0
  40. data/test/configurable_test.rb +20 -0
  41. data/test/details_test.rb +68 -0
  42. data/test/field_search_test.rb +102 -0
  43. data/test/field_set_test.rb +46 -0
  44. data/test/form_test.rb +287 -0
  45. data/test/grid_test.rb +219 -0
  46. data/test/helpers_test.rb +72 -0
  47. data/test/presenting_test.rb +15 -0
  48. data/test/rails/app/controllers/application_controller.rb +15 -0
  49. data/test/rails/app/controllers/users_controller.rb +36 -0
  50. data/test/rails/app/helpers/application_helper.rb +3 -0
  51. data/test/rails/app/helpers/users_helper.rb +2 -0
  52. data/test/rails/app/models/user.rb +2 -0
  53. data/test/rails/app/views/layouts/application.html.erb +15 -0
  54. data/test/rails/app/views/users/index.html.erb +10 -0
  55. data/test/rails/app/views/users/new.html.erb +2 -0
  56. data/test/rails/app/views/users/show.html.erb +1 -0
  57. data/test/rails/config/boot.rb +109 -0
  58. data/test/rails/config/database.yml +17 -0
  59. data/test/rails/config/environment.rb +13 -0
  60. data/test/rails/config/environments/development.rb +17 -0
  61. data/test/rails/config/environments/production.rb +24 -0
  62. data/test/rails/config/environments/test.rb +22 -0
  63. data/test/rails/config/locales/en.yml +5 -0
  64. data/test/rails/config/routes.rb +5 -0
  65. data/test/rails/db/development.sqlite3 +0 -0
  66. data/test/rails/db/migrate/20090213085444_create_users.rb +13 -0
  67. data/test/rails/db/migrate/20090213085607_populate_users.rb +13 -0
  68. data/test/rails/db/schema.rb +23 -0
  69. data/test/rails/db/test.sqlite3 +0 -0
  70. data/test/rails/log/development.log +858 -0
  71. data/test/rails/public/404.html +30 -0
  72. data/test/rails/public/422.html +30 -0
  73. data/test/rails/public/500.html +33 -0
  74. data/test/rails/public/javascripts/application.js +2 -0
  75. data/test/rails/public/javascripts/jquery.livequery.min.js +11 -0
  76. data/test/rails/public/javascripts/prototype.js +4320 -0
  77. data/test/rails/script/console +3 -0
  78. data/test/rails/script/dbconsole +3 -0
  79. data/test/rails/script/destroy +3 -0
  80. data/test/rails/script/generate +3 -0
  81. data/test/rails/script/plugin +3 -0
  82. data/test/rails/script/runner +3 -0
  83. data/test/rails/script/server +3 -0
  84. data/test/sanitize_test.rb +15 -0
  85. data/test/search_conditions_test.rb +137 -0
  86. data/test/search_test.rb +30 -0
  87. data/test/sorting_test.rb +63 -0
  88. data/test/test_helper.rb +66 -0
  89. 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
@@ -0,0 +1,3 @@
1
+ class Presenting::View < ActionView::Base
2
+ delegate :protect_against_forgery?, :form_authenticity_token, :to => :controller
3
+ 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
@@ -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)', '&amp;'
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