presenting 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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/presentation/form.rb
CHANGED
@@ -1,149 +1,149 @@
|
|
1
|
-
module Presentation
|
2
|
-
class Form < Base
|
3
|
-
# TODO
|
4
|
-
# field type extra details?
|
5
|
-
# * text
|
6
|
-
# * text_area
|
7
|
-
# * password
|
8
|
-
# * check_box checked/unchecked values
|
9
|
-
# * radio (= dropdown) options
|
10
|
-
# * dropdown (= radio) options
|
11
|
-
# * multi-select options
|
12
|
-
# * recordselect url?
|
13
|
-
# * calendar constraints
|
14
|
-
# * time constraints
|
15
|
-
# * date
|
16
|
-
# * datetime
|
17
|
-
#
|
18
|
-
# other
|
19
|
-
# - example / description / help text
|
20
|
-
# - nested fields
|
21
|
-
|
22
|
-
# Fields may be grouped. Groups may or may not have names. Here's how:
|
23
|
-
#
|
24
|
-
# Presentation::Form.new(:groups => [
|
25
|
-
# [:a, :b], # creates a nameless group with fields :a and :b
|
26
|
-
# {"foo" => [:c, :d]} # creates a group named "foo" with fields :c and :d
|
27
|
-
# ])
|
28
|
-
#
|
29
|
-
# Note that if you don't need groups it'll be simpler to just use fields= instead.
|
30
|
-
def groups
|
31
|
-
@groups ||= GroupSet.new
|
32
|
-
end
|
33
|
-
def groups=(args)
|
34
|
-
args.each do |group|
|
35
|
-
groups << group
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class GroupSet < Array
|
40
|
-
def <<(val)
|
41
|
-
if val.is_a? Hash
|
42
|
-
opts = {:name => val.keys.first, :fields => val.values.first}
|
43
|
-
else
|
44
|
-
opts = {:fields => val}
|
45
|
-
end
|
46
|
-
super Group.new(opts)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
class Group
|
51
|
-
include Presenting::Configurable
|
52
|
-
|
53
|
-
# a completely optional group name
|
54
|
-
attr_accessor :name
|
55
|
-
|
56
|
-
# the fields in the group
|
57
|
-
def fields
|
58
|
-
@fields ||= Presenting::FieldSet.new(Field, :name, :type)
|
59
|
-
end
|
60
|
-
def fields=(args)
|
61
|
-
args.each do |field|
|
62
|
-
fields << field
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
# Used to define fields in a group-less form.
|
68
|
-
def fields
|
69
|
-
if groups.empty?
|
70
|
-
groups << []
|
71
|
-
end
|
72
|
-
groups.first.fields
|
73
|
-
end
|
74
|
-
def fields=(args)
|
75
|
-
args.each do |field|
|
76
|
-
fields << field
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# The url where the form posts. May be anything that url_for accepts, including
|
81
|
-
# a set of records.
|
82
|
-
def url
|
83
|
-
@url ||= presentable
|
84
|
-
end
|
85
|
-
attr_writer :url
|
86
|
-
|
87
|
-
# What method the form should use to post. Should default intelligently enough from
|
88
|
-
# the presentable. Not sure what use case would require it being set manually.
|
89
|
-
def method
|
90
|
-
@method ||= presentable.new_record? ? :post : :put
|
91
|
-
end
|
92
|
-
attr_writer :method
|
93
|
-
|
94
|
-
# the text on the submit button
|
95
|
-
def button
|
96
|
-
@button ||= presentable.new_record? ? 'Create' : 'Update'
|
97
|
-
end
|
98
|
-
attr_writer :button
|
99
|
-
|
100
|
-
# a passthrough for form_for's html. useful for classifying a form for ajax behavior (e.g. :html => {:class => 'ajax'})
|
101
|
-
attr_accessor :html
|
102
|
-
|
103
|
-
class Field
|
104
|
-
include Presenting::Configurable
|
105
|
-
|
106
|
-
# the display label of the field
|
107
|
-
def label
|
108
|
-
@label ||= name.to_s.titleize
|
109
|
-
end
|
110
|
-
attr_writer :label
|
111
|
-
|
112
|
-
# the parameter name of the field
|
113
|
-
attr_accessor :name
|
114
|
-
|
115
|
-
# where the value for this field comes from.
|
116
|
-
# - String: a fixed value
|
117
|
-
# - Symbol: a method on the record (no arguments)
|
118
|
-
# - Proc: a custom block that accepts the record as an argument
|
119
|
-
def value
|
120
|
-
@value ||= name.to_sym
|
121
|
-
end
|
122
|
-
attr_writer :value
|
123
|
-
|
124
|
-
def value_from(obj) #:nodoc:
|
125
|
-
v = case value
|
126
|
-
when Symbol: obj.send(value)
|
127
|
-
when String: value
|
128
|
-
when Proc: value.call(obj)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
# the widget type for the field. use type_options to pass arguments to the widget.
|
133
|
-
def type
|
134
|
-
@type ||= :string
|
135
|
-
end
|
136
|
-
attr_writer :type
|
137
|
-
|
138
|
-
# unrestricted options storage for the widget type. this could be a list of options for a select, or extra configuration for a calendar widget.
|
139
|
-
attr_accessor :type_options
|
140
|
-
end
|
141
|
-
|
142
|
-
def iname; :form end
|
143
|
-
|
144
|
-
delegate :request_forgery_protection_token, :allow_forgery_protection, :to => :controller
|
145
|
-
def protect_against_forgery? #:nodoc:
|
146
|
-
allow_forgery_protection && request_forgery_protection_token
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
1
|
+
module Presentation
|
2
|
+
class Form < Base
|
3
|
+
# TODO
|
4
|
+
# field type extra details?
|
5
|
+
# * text
|
6
|
+
# * text_area
|
7
|
+
# * password
|
8
|
+
# * check_box checked/unchecked values
|
9
|
+
# * radio (= dropdown) options
|
10
|
+
# * dropdown (= radio) options
|
11
|
+
# * multi-select options
|
12
|
+
# * recordselect url?
|
13
|
+
# * calendar constraints
|
14
|
+
# * time constraints
|
15
|
+
# * date
|
16
|
+
# * datetime
|
17
|
+
#
|
18
|
+
# other
|
19
|
+
# - example / description / help text
|
20
|
+
# - nested fields
|
21
|
+
|
22
|
+
# Fields may be grouped. Groups may or may not have names. Here's how:
|
23
|
+
#
|
24
|
+
# Presentation::Form.new(:groups => [
|
25
|
+
# [:a, :b], # creates a nameless group with fields :a and :b
|
26
|
+
# {"foo" => [:c, :d]} # creates a group named "foo" with fields :c and :d
|
27
|
+
# ])
|
28
|
+
#
|
29
|
+
# Note that if you don't need groups it'll be simpler to just use fields= instead.
|
30
|
+
def groups
|
31
|
+
@groups ||= GroupSet.new
|
32
|
+
end
|
33
|
+
def groups=(args)
|
34
|
+
args.each do |group|
|
35
|
+
groups << group
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class GroupSet < Array
|
40
|
+
def <<(val)
|
41
|
+
if val.is_a? Hash
|
42
|
+
opts = {:name => val.keys.first, :fields => val.values.first}
|
43
|
+
else
|
44
|
+
opts = {:fields => val}
|
45
|
+
end
|
46
|
+
super Group.new(opts)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Group
|
51
|
+
include Presenting::Configurable
|
52
|
+
|
53
|
+
# a completely optional group name
|
54
|
+
attr_accessor :name
|
55
|
+
|
56
|
+
# the fields in the group
|
57
|
+
def fields
|
58
|
+
@fields ||= Presenting::FieldSet.new(Field, :name, :type)
|
59
|
+
end
|
60
|
+
def fields=(args)
|
61
|
+
args.each do |field|
|
62
|
+
fields << field
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Used to define fields in a group-less form.
|
68
|
+
def fields
|
69
|
+
if groups.empty?
|
70
|
+
groups << []
|
71
|
+
end
|
72
|
+
groups.first.fields
|
73
|
+
end
|
74
|
+
def fields=(args)
|
75
|
+
args.each do |field|
|
76
|
+
fields << field
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# The url where the form posts. May be anything that url_for accepts, including
|
81
|
+
# a set of records.
|
82
|
+
def url
|
83
|
+
@url ||= presentable
|
84
|
+
end
|
85
|
+
attr_writer :url
|
86
|
+
|
87
|
+
# What method the form should use to post. Should default intelligently enough from
|
88
|
+
# the presentable. Not sure what use case would require it being set manually.
|
89
|
+
def method
|
90
|
+
@method ||= presentable.new_record? ? :post : :put
|
91
|
+
end
|
92
|
+
attr_writer :method
|
93
|
+
|
94
|
+
# the text on the submit button
|
95
|
+
def button
|
96
|
+
@button ||= presentable.new_record? ? 'Create' : 'Update'
|
97
|
+
end
|
98
|
+
attr_writer :button
|
99
|
+
|
100
|
+
# a passthrough for form_for's html. useful for classifying a form for ajax behavior (e.g. :html => {:class => 'ajax'})
|
101
|
+
attr_accessor :html
|
102
|
+
|
103
|
+
class Field
|
104
|
+
include Presenting::Configurable
|
105
|
+
|
106
|
+
# the display label of the field
|
107
|
+
def label
|
108
|
+
@label ||= name.to_s.titleize
|
109
|
+
end
|
110
|
+
attr_writer :label
|
111
|
+
|
112
|
+
# the parameter name of the field
|
113
|
+
attr_accessor :name
|
114
|
+
|
115
|
+
# where the value for this field comes from.
|
116
|
+
# - String: a fixed value
|
117
|
+
# - Symbol: a method on the record (no arguments)
|
118
|
+
# - Proc: a custom block that accepts the record as an argument
|
119
|
+
def value
|
120
|
+
@value ||= name.to_sym
|
121
|
+
end
|
122
|
+
attr_writer :value
|
123
|
+
|
124
|
+
def value_from(obj) #:nodoc:
|
125
|
+
v = case value
|
126
|
+
when Symbol: obj.send(value)
|
127
|
+
when String: value
|
128
|
+
when Proc: value.call(obj)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# the widget type for the field. use type_options to pass arguments to the widget.
|
133
|
+
def type
|
134
|
+
@type ||= :string
|
135
|
+
end
|
136
|
+
attr_writer :type
|
137
|
+
|
138
|
+
# unrestricted options storage for the widget type. this could be a list of options for a select, or extra configuration for a calendar widget.
|
139
|
+
attr_accessor :type_options
|
140
|
+
end
|
141
|
+
|
142
|
+
def iname; :form end
|
143
|
+
|
144
|
+
delegate :request_forgery_protection_token, :allow_forgery_protection, :to => :controller
|
145
|
+
def protect_against_forgery? #:nodoc:
|
146
|
+
allow_forgery_protection && request_forgery_protection_token
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/presentation/grid.rb
CHANGED
@@ -1,160 +1,162 @@
|
|
1
|
-
module Presentation
|
2
|
-
# TODO: ability to render a hash
|
3
|
-
# TODO: custom css classes for rows and/or cells
|
4
|
-
# TODO: document or complain for required options -- id and fields
|
5
|
-
# TODO: make fields= accept an ActiveRecord::Base.columns array for a default field set
|
6
|
-
class Grid < Base
|
7
|
-
# The id for this presentation. Required.
|
8
|
-
attr_accessor :id
|
9
|
-
|
10
|
-
# The display title for this presentation. Will default based on the id.
|
11
|
-
attr_writer :title
|
12
|
-
def title
|
13
|
-
@title ||= self.id.titleize
|
14
|
-
end
|
15
|
-
|
16
|
-
# Paradigm Example:
|
17
|
-
# Grid.new(:fields => [
|
18
|
-
# :email,
|
19
|
-
# {"Full Name" => proc{|r| [r.first_name, r.last_name].join(' ')}},
|
20
|
-
# {"Roles" => {:value => :roles, :type => :collection}}
|
21
|
-
# ])
|
22
|
-
#
|
23
|
-
# Is equivalent to:
|
24
|
-
# g = Grid.new
|
25
|
-
# g.fields << :email
|
26
|
-
# g.fields << {"Full Name" => proc{|r| [r.first_name, r.last_name].join(' ')},
|
27
|
-
# g.fields << {"Roles" => {:value => :roles, :type => :collection}}
|
28
|
-
def fields=(args)
|
29
|
-
args.each do |field|
|
30
|
-
self.fields << field
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def fields
|
35
|
-
@fields ||= Presenting::FieldSet.new(Field, :name, :value)
|
36
|
-
end
|
37
|
-
|
38
|
-
def colspan
|
39
|
-
@colspan ||= fields.size + (record_links.empty? ? 0 : 1)
|
40
|
-
end
|
41
|
-
|
42
|
-
def iname; :grid end
|
43
|
-
|
44
|
-
class Field < Presenting::Attribute
|
45
|
-
# Defines how this field sorts. This means two things:
|
46
|
-
# 1. whether it sorts
|
47
|
-
# 2. what name it uses to sort
|
48
|
-
#
|
49
|
-
# Examples:
|
50
|
-
#
|
51
|
-
# # The field is sortable and assumes the sort_name of "first_name".
|
52
|
-
# # This is the default.
|
53
|
-
# Field.new(:sortable => true, :name => "First Name")
|
54
|
-
#
|
55
|
-
# # The field is sortable and assumes the sort_name of "first".
|
56
|
-
# Field.new(:sortable => 'first', :name => 'First Name')
|
57
|
-
#
|
58
|
-
# # The field is unsortable.
|
59
|
-
# Field.new(:sortable => false)
|
60
|
-
def sortable=(val)
|
61
|
-
@sort_name = case val
|
62
|
-
when TrueClass
|
63
|
-
|
64
|
-
else
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
next_direction = 'desc'
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
##
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
#
|
102
|
-
#
|
103
|
-
|
104
|
-
#
|
105
|
-
|
106
|
-
|
107
|
-
#
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
123
|
-
#
|
124
|
-
#
|
125
|
-
#
|
126
|
-
#
|
127
|
-
#
|
128
|
-
#
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
1
|
+
module Presentation
|
2
|
+
# TODO: ability to render a hash
|
3
|
+
# TODO: custom css classes for rows and/or cells
|
4
|
+
# TODO: document or complain for required options -- id and fields
|
5
|
+
# TODO: make fields= accept an ActiveRecord::Base.columns array for a default field set
|
6
|
+
class Grid < Base
|
7
|
+
# The id for this presentation. Required.
|
8
|
+
attr_accessor :id
|
9
|
+
|
10
|
+
# The display title for this presentation. Will default based on the id.
|
11
|
+
attr_writer :title
|
12
|
+
def title
|
13
|
+
@title ||= self.id.titleize
|
14
|
+
end
|
15
|
+
|
16
|
+
# Paradigm Example:
|
17
|
+
# Grid.new(:fields => [
|
18
|
+
# :email,
|
19
|
+
# {"Full Name" => proc{|r| [r.first_name, r.last_name].join(' ')}},
|
20
|
+
# {"Roles" => {:value => :roles, :type => :collection}}
|
21
|
+
# ])
|
22
|
+
#
|
23
|
+
# Is equivalent to:
|
24
|
+
# g = Grid.new
|
25
|
+
# g.fields << :email
|
26
|
+
# g.fields << {"Full Name" => proc{|r| [r.first_name, r.last_name].join(' ')},
|
27
|
+
# g.fields << {"Roles" => {:value => :roles, :type => :collection}}
|
28
|
+
def fields=(args)
|
29
|
+
args.each do |field|
|
30
|
+
self.fields << field
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def fields
|
35
|
+
@fields ||= Presenting::FieldSet.new(Field, :name, :value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def colspan
|
39
|
+
@colspan ||= fields.size + (record_links.empty? ? 0 : 1)
|
40
|
+
end
|
41
|
+
|
42
|
+
def iname; :grid end
|
43
|
+
|
44
|
+
class Field < Presenting::Attribute
|
45
|
+
# Defines how this field sorts. This means two things:
|
46
|
+
# 1. whether it sorts
|
47
|
+
# 2. what name it uses to sort
|
48
|
+
#
|
49
|
+
# Examples:
|
50
|
+
#
|
51
|
+
# # The field is sortable and assumes the sort_name of "first_name".
|
52
|
+
# # This is the default.
|
53
|
+
# Field.new(:sortable => true, :name => "First Name")
|
54
|
+
#
|
55
|
+
# # The field is sortable and assumes the sort_name of "first".
|
56
|
+
# Field.new(:sortable => 'first', :name => 'First Name')
|
57
|
+
#
|
58
|
+
# # The field is unsortable.
|
59
|
+
# Field.new(:sortable => false)
|
60
|
+
def sortable=(val)
|
61
|
+
@sort_name = case val
|
62
|
+
when TrueClass, FalseClass, NilClass
|
63
|
+
val
|
64
|
+
else
|
65
|
+
val.to_s
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# if the field is sortable at all
|
70
|
+
def sortable?
|
71
|
+
self.sortable = Presenting::Defaults.grid_is_sortable unless defined? @sort_name
|
72
|
+
self.sortable = self.id if @sort_name == true
|
73
|
+
!@sort_name.blank?
|
74
|
+
end
|
75
|
+
|
76
|
+
attr_reader :sort_name
|
77
|
+
|
78
|
+
# is this field sorted in the given request?
|
79
|
+
def is_sorted?(request)
|
80
|
+
@is_sorted ||= if sortable? and sorting = request.query_parameters["sort"] and sorting[sort_name]
|
81
|
+
sorting[sort_name].to_s.match(/desc/i) ? 'desc' : 'asc'
|
82
|
+
else
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# for the view -- modifies the current request such that it would sort this field.
|
88
|
+
def sorted_url(request)
|
89
|
+
if current_direction = is_sorted?(request)
|
90
|
+
next_direction = current_direction == 'desc' ? 'asc' : 'desc'
|
91
|
+
else
|
92
|
+
next_direction = 'desc'
|
93
|
+
end
|
94
|
+
request.path + '?' + request.query_parameters.merge("sort" => {sort_name => next_direction}).to_param
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
## Planned
|
99
|
+
##
|
100
|
+
|
101
|
+
# TODO: discover "type" from data class (ActiveRecord) if available
|
102
|
+
# TODO: decorate a Hash object so type is specifiable there as well
|
103
|
+
# PLAN: type should determine how a field renders. custom types for custom renders. this should be the second option to present().
|
104
|
+
# attr_accessor :type
|
105
|
+
|
106
|
+
# PLAN: a field's description would appear in the header column, perhaps only visibly in a tooltip
|
107
|
+
# attr_accessor :description
|
108
|
+
|
109
|
+
# PLAN: any field may be linked. this would happen after :value and :type.
|
110
|
+
# attr_accessor :link
|
111
|
+
end
|
112
|
+
|
113
|
+
# Links are an area where I almost made the mistake of too much configuration. Presentations are configured in the view,
|
114
|
+
# and all of the view helpers are available. When I looked at the (simple) configuration I was building and realized that
|
115
|
+
# I could just as easily take the result of link_to, well, I felt a little silly.
|
116
|
+
#
|
117
|
+
# Compare:
|
118
|
+
#
|
119
|
+
# @grid.links = [
|
120
|
+
# {:name => 'Foo', :url => foo_path, :class => 'foo'}
|
121
|
+
# ]
|
122
|
+
#
|
123
|
+
# vs:
|
124
|
+
#
|
125
|
+
# @grid.links = [
|
126
|
+
# link_to('Foo', foo_path, :class => 'foo')
|
127
|
+
# ]
|
128
|
+
#
|
129
|
+
# Not only is the second example (the supported example, by the way) shorter and cleaner, it encourages the developer
|
130
|
+
# to stay in touch with the Rails internals and therefore discourages a configuration-heavy mindset.
|
131
|
+
def links=(set)
|
132
|
+
set.compact.each do |link|
|
133
|
+
raise ArgumentError, "Links must be strings, such as the output of link_to()." unless link.is_a?(String)
|
134
|
+
links << link
|
135
|
+
end
|
136
|
+
end
|
137
|
+
def links
|
138
|
+
@links ||= []
|
139
|
+
end
|
140
|
+
|
141
|
+
# Like links, except the link will appear for each record. This means that the link must be a block that accepts the
|
142
|
+
# record as its argument. For example:
|
143
|
+
#
|
144
|
+
# @grid.record_links = [
|
145
|
+
# proc{|record| link_to("Foo", foo_path(record), :class => 'foo') }
|
146
|
+
# ]
|
147
|
+
#
|
148
|
+
def record_links=(set)
|
149
|
+
set.compact.each do |link|
|
150
|
+
raise ArgumentError, "Record links must be blocks that accept the record as an argument." unless link.respond_to?(:call) and link.arity == 1
|
151
|
+
record_links << link
|
152
|
+
end
|
153
|
+
end
|
154
|
+
def record_links
|
155
|
+
@record_links ||= []
|
156
|
+
end
|
157
|
+
|
158
|
+
def paginate?
|
159
|
+
defined? WillPaginate and (presentable.is_a? WillPaginate::Collection or presentable.respond_to?(:total_entries))
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
data/lib/presentation/search.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
module Presentation
|
2
|
-
class Search < Base
|
3
|
-
def iname; :search end
|
4
|
-
|
5
|
-
def url
|
6
|
-
request.path + '?' + request.query_parameters.except("search").to_param
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
1
|
+
module Presentation
|
2
|
+
class Search < Base
|
3
|
+
def iname; :search end
|
4
|
+
|
5
|
+
def url
|
6
|
+
request.path + '?' + request.query_parameters.except("search").to_param
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|