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
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Lance Ivy
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Presenting
|
2
|
+
==========
|
3
|
+
Bringing together my work on ActiveScaffold and Components into standardized, reusable view macros.
|
4
|
+
|
5
|
+
Performance
|
6
|
+
===========
|
7
|
+
I don't see why it wouldn't be fast. But if anyone "discovers":http://rpm.newrelic.com a "problem":http://ruby-prof.rubyforge.org/, we can go from there. Why worry early?
|
8
|
+
|
9
|
+
|
10
|
+
Copyright (c) 2008 Lance Ivy, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the presenting plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the presenting plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'Presenting'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
$('.presentation-search .compact fieldset label').livequery(function() {
|
2
|
+
var label = $(this);
|
3
|
+
var field = label.siblings('input');
|
4
|
+
if (!field[0] || field.attr('type') == 'checkbox') return;
|
5
|
+
|
6
|
+
label.addClass('overlabel');
|
7
|
+
|
8
|
+
var hide_label = function() { label.css('text-indent', '-1000px') };
|
9
|
+
var show_label = function() { this.value || label.css('text-indent', '0px') };
|
10
|
+
|
11
|
+
$(field).focus(hide_label).blur(show_label).each(hide_label).each(show_label);
|
12
|
+
$(label).click(function() {field.focus()});
|
13
|
+
});
|
@@ -0,0 +1 @@
|
|
1
|
+
.presentation-form {}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
.presentation-grid a {
|
2
|
+
color: #06c;
|
3
|
+
}
|
4
|
+
|
5
|
+
.presentation-grid {
|
6
|
+
font-family: sans-serif;
|
7
|
+
background-color: #fff;
|
8
|
+
}
|
9
|
+
|
10
|
+
.presentation-grid caption {
|
11
|
+
background-color: transparent;
|
12
|
+
}
|
13
|
+
|
14
|
+
.presentation-grid thead {
|
15
|
+
background-color: transparent;
|
16
|
+
}
|
17
|
+
|
18
|
+
.presentation-grid thead th {
|
19
|
+
border-bottom: 2px solid #aaa;
|
20
|
+
font-weight: normal;
|
21
|
+
color: #666;
|
22
|
+
background-color: transparent;
|
23
|
+
}
|
24
|
+
|
25
|
+
.presentation-grid tbody tr.even {
|
26
|
+
background-color: #e6f2ff;
|
27
|
+
border-bottom: 1px solid #c5dbf7;
|
28
|
+
border-top: 1px solid #c5dbf7;
|
29
|
+
}
|
30
|
+
|
31
|
+
.presentation-grid tbody td.sorted {
|
32
|
+
background-color: #f5f5f5;
|
33
|
+
}
|
34
|
+
|
35
|
+
.presentation-grid tbody tr.even td.sorted {
|
36
|
+
background-color: #c5dbf7;
|
37
|
+
}
|
38
|
+
|
39
|
+
.presentation-grid tfoot .pagination {
|
40
|
+
margin-top: 0.5em;
|
41
|
+
}
|
42
|
+
|
43
|
+
.presentation-grid tfoot .pagination a {
|
44
|
+
text-decoration: none;
|
45
|
+
}
|
46
|
+
|
47
|
+
.presentation-grid tfoot .pagination .prev_page,
|
48
|
+
.presentation-grid tfoot .pagination .next_page {
|
49
|
+
padding: 0.1em 0.5em;
|
50
|
+
background-color: #eee;
|
51
|
+
border: 1px solid #e5e5e5;
|
52
|
+
}
|
53
|
+
|
54
|
+
.presentation-grid tfoot .pagination a.prev_page:hover,
|
55
|
+
.presentation-grid tfoot .pagination a.next_page:hover {
|
56
|
+
background-color: #e6f2ff;
|
57
|
+
border-color: #c5dbf7;
|
58
|
+
}
|
59
|
+
|
60
|
+
.presentation-grid tfoot .pagination .prev_page {
|
61
|
+
margin-right: 0.7em;
|
62
|
+
}
|
63
|
+
|
64
|
+
.presentation-grid tfoot .pagination .next_page {
|
65
|
+
margin-left: 0.7em;
|
66
|
+
}
|
67
|
+
|
68
|
+
.presentation-grid tfoot .pagination .current {
|
69
|
+
padding: 0.1em 0.5em;
|
70
|
+
border: 1px solid #e5e5e5;
|
71
|
+
}
|
@@ -0,0 +1,64 @@
|
|
1
|
+
/** cellspacing **/
|
2
|
+
.presentation-grid table,
|
3
|
+
.presentation-grid td,
|
4
|
+
.presentation-grid th {
|
5
|
+
border: 0;
|
6
|
+
border-collapse: collapse;
|
7
|
+
}
|
8
|
+
|
9
|
+
.presentation-grid table {
|
10
|
+
width: 100%;
|
11
|
+
}
|
12
|
+
|
13
|
+
.presentation-grid caption {
|
14
|
+
text-align: left;
|
15
|
+
font-weight: bold;
|
16
|
+
padding-left: 0.2em;
|
17
|
+
}
|
18
|
+
|
19
|
+
.presentation-grid th {
|
20
|
+
padding: 0.2em 0.5em 0em 0.2em;
|
21
|
+
text-align: left;
|
22
|
+
vertical-align: bottom;
|
23
|
+
}
|
24
|
+
|
25
|
+
.presentation-grid th a {
|
26
|
+
text-decoration: none;
|
27
|
+
}
|
28
|
+
|
29
|
+
.presentation-grid td {
|
30
|
+
padding: 0.4em 0.5em 0.4em 0.2em;
|
31
|
+
vertical-align: top;
|
32
|
+
}
|
33
|
+
|
34
|
+
.presentation-grid ul.actions {
|
35
|
+
overflow: auto;
|
36
|
+
zoom: 1;
|
37
|
+
float: right;
|
38
|
+
list-style: none;
|
39
|
+
margin: 0;
|
40
|
+
padding: 0;
|
41
|
+
}
|
42
|
+
|
43
|
+
.presentation-grid ul.actions li {
|
44
|
+
float: left;
|
45
|
+
}
|
46
|
+
|
47
|
+
.presentation-grid ul.actions li a {
|
48
|
+
font-weight: normal;
|
49
|
+
display: block;
|
50
|
+
padding: 0em 0.3em;
|
51
|
+
text-decoration: none;
|
52
|
+
}
|
53
|
+
|
54
|
+
.presentation-grid ul.actions li a:hover {
|
55
|
+
text-decoration: underline;
|
56
|
+
}
|
57
|
+
|
58
|
+
.presentation-grid ul.actions li a {
|
59
|
+
outline: none;
|
60
|
+
}
|
61
|
+
|
62
|
+
.presentation-grid caption ul.actions {
|
63
|
+
margin-right: 0.5em;
|
64
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
.presentation-search label {
|
2
|
+
color: #666;
|
3
|
+
}
|
4
|
+
|
5
|
+
.presentation-search fieldset {
|
6
|
+
border: 1px solid #c5dbf7;
|
7
|
+
background-color: #e6f2ff;
|
8
|
+
padding: 2px;
|
9
|
+
}
|
10
|
+
|
11
|
+
.presentation-search fieldset.submit {
|
12
|
+
border: 0px;
|
13
|
+
padding: 0px;
|
14
|
+
background-color: transparent;
|
15
|
+
}
|
16
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
.presentation-search {
|
2
|
+
overflow: hidden;
|
3
|
+
zoom: 1;
|
4
|
+
}
|
5
|
+
|
6
|
+
.presentation-search fieldset {
|
7
|
+
border: 0;
|
8
|
+
margin: 0 0.5em 1em 0;
|
9
|
+
padding: 0;
|
10
|
+
float: left;
|
11
|
+
width: 145px;
|
12
|
+
}
|
13
|
+
|
14
|
+
.presentation-search fieldset label {
|
15
|
+
font-weight: normal;
|
16
|
+
margin: 0;
|
17
|
+
padding: 0;
|
18
|
+
text-transform: none;
|
19
|
+
}
|
20
|
+
|
21
|
+
.presentation-search fieldset input,
|
22
|
+
.presentation-search fieldset select {
|
23
|
+
padding: 0px;
|
24
|
+
margin: 0px;
|
25
|
+
}
|
26
|
+
|
27
|
+
/* for overlabels (requires javascript) */
|
28
|
+
|
29
|
+
.presentation-search .compact label {
|
30
|
+
display: inline;
|
31
|
+
}
|
32
|
+
|
33
|
+
.presentation-search .compact fieldset {
|
34
|
+
position: relative;
|
35
|
+
width: auto;
|
36
|
+
}
|
37
|
+
|
38
|
+
.presentation-search .compact fieldset label.overlabel {
|
39
|
+
display: block;
|
40
|
+
position: absolute;
|
41
|
+
top: 0.2em;
|
42
|
+
left: 0.5em;
|
43
|
+
z-index: 1;
|
44
|
+
cursor: text;
|
45
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class Presentation::AssetsController < ActionController::Base
|
2
|
+
# TODO: consider packaging a minifier so we get the perfect solution: a cached, minified bundle of assets
|
3
|
+
caches_page :stylesheet, :javascript
|
4
|
+
|
5
|
+
def stylesheet
|
6
|
+
dir = asset_path(:stylesheets)
|
7
|
+
sheet = params[:id].split(',').collect{ |id| File.read("#{dir}/#{id}.css") }.join("\n")
|
8
|
+
|
9
|
+
respond_to do |type|
|
10
|
+
type.css {render :text => sheet}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# TODO: bundle unobtrusive javascripts that can add behavior.
|
15
|
+
# - jquery vs prototype (i'll develop jquery, and wait for prototype contributions)
|
16
|
+
# - ajax links with html response
|
17
|
+
# - - inline
|
18
|
+
# - - modal dialog
|
19
|
+
# - ajax links with js response
|
20
|
+
# - ajax links with no response
|
21
|
+
# - inline editing
|
22
|
+
# - "dirty" form awareness
|
23
|
+
# - tabbed forms
|
24
|
+
# - tooltips for extended information (e.g. column description or truncated field text)
|
25
|
+
# - basic form validation
|
26
|
+
# - - required fields
|
27
|
+
# TODO: tests for ujs
|
28
|
+
def javascript
|
29
|
+
dir = asset_path(:javascript)
|
30
|
+
script = params[:id].split(',').collect{ |id| File.read("#{dir}/#{id}.js") }.join("\n")
|
31
|
+
|
32
|
+
respond_to do |type|
|
33
|
+
type.js {render :text => script}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def asset_path(type)
|
40
|
+
File.join(File.dirname(__FILE__), '..', '..', 'assets', type.to_s)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<div class="presentation-details">
|
2
|
+
<% if @details.title %>
|
3
|
+
<h3><%= @details.title %></h3>
|
4
|
+
<% end %>
|
5
|
+
<dl>
|
6
|
+
<% @details.fields.each do |field| %>
|
7
|
+
<dt><%= field.name %></dt>
|
8
|
+
<dd><%= present(field.value_from(@details.presentable), :h => field.sanitize?) %></dd>
|
9
|
+
<% end %>
|
10
|
+
</dl>
|
11
|
+
</div>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div class="presentation-search">
|
2
|
+
<% form_tag @search.url, :method => :get, :class => ("compact" if @search.compact) do %>
|
3
|
+
<% @search.fields.each do |field| %>
|
4
|
+
<fieldset>
|
5
|
+
<label><%= field.name %></label>
|
6
|
+
<%= present(field, "#{field.type}_search", :name => "search[#{field.param}][value]") %>
|
7
|
+
</fieldset>
|
8
|
+
<% end %>
|
9
|
+
<fieldset class="submit">
|
10
|
+
<input type="submit" value="Search" class="submit" />
|
11
|
+
<%= link_to 'reset', @search.url %>
|
12
|
+
</fieldset>
|
13
|
+
<% end %>
|
14
|
+
</div>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<% form_for @form.presentable, :url => @form.url, :method => @form.method, :html => @form.html do |f| %>
|
2
|
+
<% @form.groups.each do |group| %>
|
3
|
+
<fieldset>
|
4
|
+
<% unless group.name.blank? %>
|
5
|
+
<legend><%= group.name %></legend>
|
6
|
+
<% end %>
|
7
|
+
<% group.fields.each do |field| %>
|
8
|
+
<% if field.type.to_sym == :hidden %>
|
9
|
+
<%= f.present(field) %>
|
10
|
+
<% else %>
|
11
|
+
<div class="field">
|
12
|
+
<%= f.label field.name, field.label %>
|
13
|
+
<%= f.present(field) %>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
16
|
+
<% end %>
|
17
|
+
</fieldset>
|
18
|
+
<% end %>
|
19
|
+
<%= f.submit @form.button %>
|
20
|
+
<% end %>
|
@@ -0,0 +1,70 @@
|
|
1
|
+
<div id="<%= @grid.id %>" class="presentation-grid">
|
2
|
+
<table>
|
3
|
+
<% unless @grid.title.blank? and @grid.links.empty? %>
|
4
|
+
<caption>
|
5
|
+
<% unless @grid.links.empty? %>
|
6
|
+
<ul class="actions">
|
7
|
+
<% @grid.links.each do |link| %>
|
8
|
+
<li><%= link %></li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<%= @grid.title %>
|
14
|
+
</caption>
|
15
|
+
<% end %>
|
16
|
+
<thead>
|
17
|
+
<tr>
|
18
|
+
<% @grid.fields.each do |field| %>
|
19
|
+
<th class="<%= field.id %>">
|
20
|
+
<% if field.sortable? %>
|
21
|
+
<%= link_to field.name, field.sorted_url(request), :class => "sortable" %>
|
22
|
+
<% else %>
|
23
|
+
<%= field.name %>
|
24
|
+
<% end %>
|
25
|
+
</th>
|
26
|
+
<% end %>
|
27
|
+
<% unless @grid.record_links.empty? %>
|
28
|
+
<th></th>
|
29
|
+
<% end %>
|
30
|
+
</tr>
|
31
|
+
</thead>
|
32
|
+
<tbody>
|
33
|
+
<% if @grid.presentable.blank? %>
|
34
|
+
<tr>
|
35
|
+
<td colspan="<%= @grid.colspan %>">
|
36
|
+
No records found.
|
37
|
+
</td>
|
38
|
+
</tr>
|
39
|
+
<% else %>
|
40
|
+
<% @grid.presentable.each do |record| %>
|
41
|
+
<tr class="<%= cycle('odd', 'even') %>">
|
42
|
+
<% @grid.fields.each do |field| %>
|
43
|
+
<td class="<%= [field.id, ('sorted' if field.is_sorted?(request))].compact.join(' ') %>">
|
44
|
+
<%= present(field.value_from(record), :h => field.sanitize?) %>
|
45
|
+
</td>
|
46
|
+
<% end %>
|
47
|
+
<% unless @grid.record_links.empty? %>
|
48
|
+
<td>
|
49
|
+
<ul class="actions">
|
50
|
+
<% @grid.record_links.each do |link| %>
|
51
|
+
<li><%= link.call(record) %></li>
|
52
|
+
<% end %>
|
53
|
+
</ul>
|
54
|
+
</td>
|
55
|
+
<% end %>
|
56
|
+
</tr>
|
57
|
+
<% end %>
|
58
|
+
<% end %>
|
59
|
+
</tbody>
|
60
|
+
<% if @grid.paginate? %>
|
61
|
+
<tfoot>
|
62
|
+
<tr>
|
63
|
+
<td colspan="<%= @grid.colspan %>">
|
64
|
+
<%= will_paginate @grid.presentable %>
|
65
|
+
</td>
|
66
|
+
</tr>
|
67
|
+
</tfoot>
|
68
|
+
<% end %>
|
69
|
+
</table>
|
70
|
+
</div>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Presentation
|
2
|
+
class Base
|
3
|
+
include Presenting::Configurable
|
4
|
+
|
5
|
+
def render
|
6
|
+
view.render :partial => "presentations/#{self.class.to_s.split('::').last.underscore}"
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :presentable
|
10
|
+
|
11
|
+
##
|
12
|
+
## begin ActionView compat
|
13
|
+
##
|
14
|
+
|
15
|
+
attr_accessor :controller # not strictly for compat, but it makes the compat easy
|
16
|
+
|
17
|
+
def request
|
18
|
+
controller.request
|
19
|
+
end
|
20
|
+
|
21
|
+
def url_for(*args)
|
22
|
+
controller.url_for(*args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def params
|
26
|
+
request.parameters
|
27
|
+
end
|
28
|
+
|
29
|
+
## end ActionView compat
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
# what the presentation is called in its templates
|
34
|
+
def iname
|
35
|
+
:presentation
|
36
|
+
end
|
37
|
+
|
38
|
+
# a reference to the view
|
39
|
+
def view #:nodoc:
|
40
|
+
@view ||= Presenting::View.new(ActionController::Base.view_paths, assigns_for_view, self)
|
41
|
+
end
|
42
|
+
|
43
|
+
def assigns_for_view
|
44
|
+
{iname => self, :_request => request}
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Presentation
|
2
|
+
# TODO: abstract what's common between Record and Grid into a shared module or reusable objects or something
|
3
|
+
class Details < Grid
|
4
|
+
def iname; :details end
|
5
|
+
|
6
|
+
# The display title for this presentation. Will default based on the id.
|
7
|
+
attr_accessor :title
|
8
|
+
|
9
|
+
def fields=(args)
|
10
|
+
args.each do |field|
|
11
|
+
self.fields << field
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def fields
|
16
|
+
@fields ||= Presenting::FieldSet.new(Presenting::Attribute, :name, :value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Presentation
|
2
|
+
class FieldSearch < Search
|
3
|
+
# This method supports the configuration-on-initialization paradigm. It makes:
|
4
|
+
# present = Presentation::FieldSearch.new(:fields => [
|
5
|
+
# {:a => {:type => :list, :options => %w(foo bar baz)}},
|
6
|
+
# :b,
|
7
|
+
# {:c => :boolean}
|
8
|
+
# ])
|
9
|
+
#
|
10
|
+
# equivalent to:
|
11
|
+
#
|
12
|
+
# present = Presentation::FieldSearch.new
|
13
|
+
# present.fields << {:a => {:type => :list, :options => %w(foo bar baz)}}
|
14
|
+
# present.fields << :b
|
15
|
+
# present.fields << {:c => :boolean}
|
16
|
+
def fields=(args)
|
17
|
+
args.each do |field|
|
18
|
+
self.fields << field
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def fields
|
23
|
+
@fields ||= Presenting::FieldSet.new(Field, :param, :type)
|
24
|
+
end
|
25
|
+
|
26
|
+
def compact
|
27
|
+
@compact != false
|
28
|
+
end
|
29
|
+
attr_writer :compact
|
30
|
+
|
31
|
+
class Field
|
32
|
+
include Presenting::Configurable
|
33
|
+
|
34
|
+
# the display name of the field.
|
35
|
+
attr_reader :name
|
36
|
+
def name=(val)
|
37
|
+
@name = val.is_a?(Symbol) ? val.to_s.titleize : val
|
38
|
+
end
|
39
|
+
|
40
|
+
# the parameter name of the field. note that this is not necessarily the name of a database column or record attribute.
|
41
|
+
# it simply needs to be recognized as a "field" by the controller logic.
|
42
|
+
attr_reader :param
|
43
|
+
def param=(val)
|
44
|
+
self.name ||= val
|
45
|
+
@param = val.to_s.underscore.downcase.gsub(' ', '_')
|
46
|
+
end
|
47
|
+
|
48
|
+
# the type of search interface for this field. supported options:
|
49
|
+
# * :text (default)
|
50
|
+
# * :checkbox
|
51
|
+
# * :time [planned]
|
52
|
+
# * :list [planned]
|
53
|
+
def type
|
54
|
+
@type ||= :text
|
55
|
+
end
|
56
|
+
attr_writer :type
|
57
|
+
|
58
|
+
# extra options for the field.
|
59
|
+
def options
|
60
|
+
@options ||= {}
|
61
|
+
end
|
62
|
+
attr_writer :options
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|