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/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
|