scaffolding_extensions 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/README +144 -0
- data/contrib/scaffold_associations_tree/README +9 -0
- data/contrib/scaffold_associations_tree/bullet.gif +0 -0
- data/contrib/scaffold_associations_tree/minus.gif +0 -0
- data/contrib/scaffold_associations_tree/plus.gif +0 -0
- data/contrib/scaffold_associations_tree/scaffold_associations_tree.css +20 -0
- data/contrib/scaffold_associations_tree/scaffold_associations_tree.js +57 -0
- data/contrib/scaffold_auto_complete_style/README +8 -0
- data/contrib/scaffold_auto_complete_style/auto_complete.css +23 -0
- data/contrib/scaffold_form_focus/README +12 -0
- data/contrib/scaffold_form_focus/scaffold_form_focus.js +21 -0
- data/contrib/scaffold_jquery_autocomplete/README +8 -0
- data/contrib/scaffold_jquery_autocomplete/jquery.ui.se_autocomplete.css +22 -0
- data/contrib/scaffold_jquery_autocomplete/jquery.ui.se_autocomplete.js +121 -0
- data/doc/advanced.txt +154 -0
- data/doc/camping.txt +25 -0
- data/doc/controller_spec.txt +20 -0
- data/doc/conversion.txt +102 -0
- data/doc/model_spec.txt +54 -0
- data/doc/ramaze.txt +20 -0
- data/doc/sinatra.txt +20 -0
- data/doc/testing.txt +12 -0
- data/lib/scaffolding_extensions.rb +89 -0
- data/lib/scaffolding_extensions/controller.rb +79 -0
- data/lib/scaffolding_extensions/controller/action_controller.rb +116 -0
- data/lib/scaffolding_extensions/controller/camping.rb +150 -0
- data/lib/scaffolding_extensions/controller/ramaze.rb +116 -0
- data/lib/scaffolding_extensions/controller/sinatra.rb +183 -0
- data/lib/scaffolding_extensions/helper.rb +304 -0
- data/lib/scaffolding_extensions/jquery_helper.rb +58 -0
- data/lib/scaffolding_extensions/meta_controller.rb +337 -0
- data/lib/scaffolding_extensions/meta_model.rb +571 -0
- data/lib/scaffolding_extensions/model.rb +30 -0
- data/lib/scaffolding_extensions/model/active_record.rb +184 -0
- data/lib/scaffolding_extensions/model/ardm.rb +47 -0
- data/lib/scaffolding_extensions/model/data_mapper.rb +190 -0
- data/lib/scaffolding_extensions/overridable.rb +67 -0
- data/lib/scaffolding_extensions/prototype_helper.rb +59 -0
- data/scaffolds/edit.rhtml +12 -0
- data/scaffolds/habtm.rhtml +24 -0
- data/scaffolds/index.rhtml +6 -0
- data/scaffolds/layout.rhtml +82 -0
- data/scaffolds/list.rhtml +13 -0
- data/scaffolds/listtable.rhtml +46 -0
- data/scaffolds/manage.rhtml +15 -0
- data/scaffolds/merge.rhtml +23 -0
- data/scaffolds/new.rhtml +5 -0
- data/scaffolds/search.rhtml +11 -0
- data/scaffolds/show.rhtml +19 -0
- data/test/scaffolding_extensions_test.rb +44 -0
- metadata +106 -0
@@ -0,0 +1,183 @@
|
|
1
|
+
begin
|
2
|
+
require 'erubis'
|
3
|
+
ERB = Erubis::Eruby
|
4
|
+
rescue
|
5
|
+
require 'erb'
|
6
|
+
end
|
7
|
+
require 'cgi'
|
8
|
+
|
9
|
+
module ScaffoldingExtensions
|
10
|
+
SCAFFOLD_ROOTS={}
|
11
|
+
class << self
|
12
|
+
private
|
13
|
+
# Sinatra doesn't have a default location for models, so assume none
|
14
|
+
def model_files
|
15
|
+
@model_files ||= []
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module SinatraHelper
|
20
|
+
private
|
21
|
+
def u(s)
|
22
|
+
CGI.escape(s.to_s)
|
23
|
+
end
|
24
|
+
|
25
|
+
def h(s)
|
26
|
+
CGI.escapeHTML(s.to_s)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Instance methods for the anonymous class that acts as a Controller for Sinatra
|
31
|
+
module SinatraController
|
32
|
+
private
|
33
|
+
# Sinatra doesn't provide a suitable flash. You can hack one together using
|
34
|
+
# session if you really need it.
|
35
|
+
def scaffold_flash
|
36
|
+
{}
|
37
|
+
end
|
38
|
+
|
39
|
+
# Proc that redirects to given url
|
40
|
+
def scaffold_redirect_to(url)
|
41
|
+
env = @sinatra_event.request.env
|
42
|
+
host = env['HTTP_HOST'] || "#{env['SERVER_NAME']}#{":#{env['SERVER_PORT']}" if env['SERVER_PORT'] && env['SERVER_PORT'].to_i != 80}"
|
43
|
+
Proc.new{redirect("//#{host}#{url}")}
|
44
|
+
end
|
45
|
+
|
46
|
+
# In order to override the default templates, you need to set
|
47
|
+
# @scaffold_template_dir and then create a template file inside that
|
48
|
+
# to override the template (make sure the default templates are also
|
49
|
+
# in this folder). It doesn't support user modifiable layouts,
|
50
|
+
# so you'll have to modify the layout.rhtml file in @scaffold_template_dir.
|
51
|
+
#
|
52
|
+
# This returns a proc that renders the necessary template using a plain
|
53
|
+
# text renderer.
|
54
|
+
def scaffold_render_template(action, options = {}, render_options = {})
|
55
|
+
suffix = options[:suffix]
|
56
|
+
suffix_action = "#{action}#{suffix}"
|
57
|
+
@scaffold_options ||= options
|
58
|
+
@scaffold_suffix ||= suffix
|
59
|
+
@scaffold_class ||= @scaffold_options[:class]
|
60
|
+
if render_options.include?(:inline)
|
61
|
+
use_js = @scaffold_javascript
|
62
|
+
text = ERB.new(render_options[:inline]).result(binding)
|
63
|
+
Proc.new do
|
64
|
+
headers('Content-Type'=>'text/javascript') if use_js
|
65
|
+
render(text, :text, :layout=>false)
|
66
|
+
end
|
67
|
+
else
|
68
|
+
@content = ERB.new(File.read(scaffold_path(File.exists?(scaffold_path(suffix_action)) ? suffix_action : action))).result(binding)
|
69
|
+
text = ERB.new(File.read(scaffold_path('layout'))).result(binding)
|
70
|
+
Proc.new{render(text, :text, :layout=>false)}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def scaffold_request_action
|
75
|
+
@scaffold_method
|
76
|
+
end
|
77
|
+
|
78
|
+
def scaffold_request_env
|
79
|
+
@sinatra_event.request.env
|
80
|
+
end
|
81
|
+
|
82
|
+
def scaffold_request_id
|
83
|
+
@sinatra_event.params[:id]
|
84
|
+
end
|
85
|
+
|
86
|
+
def scaffold_request_method
|
87
|
+
@scaffold_request_method
|
88
|
+
end
|
89
|
+
|
90
|
+
def scaffold_request_param(v)
|
91
|
+
sparams = @sinatra_event.params
|
92
|
+
unless param = sparams[v.to_sym]
|
93
|
+
param = {}
|
94
|
+
sparams.each do |k,value|
|
95
|
+
if match = /#{v}\[([^\]]+)\]/.match(k.to_s)
|
96
|
+
param[match[1]] = value
|
97
|
+
end
|
98
|
+
end
|
99
|
+
param = nil if param.empty?
|
100
|
+
end
|
101
|
+
param
|
102
|
+
end
|
103
|
+
|
104
|
+
# You need to enable Camping's session support for this to work,
|
105
|
+
# otherwise, this will always be the empty hash. The session data
|
106
|
+
# is only used for access control, so if you aren't using
|
107
|
+
# scaffold_session_value, it shouldn't matter.
|
108
|
+
def scaffold_session
|
109
|
+
@sinatra_event.session
|
110
|
+
end
|
111
|
+
|
112
|
+
def scaffold_set_vars(meth, event)
|
113
|
+
@scaffold_path = self.class.scaffold_root
|
114
|
+
@scaffold_method = meth
|
115
|
+
@sinatra_event = event
|
116
|
+
end
|
117
|
+
|
118
|
+
# Treats the id option as special, appending it to the path.
|
119
|
+
# Uses the rest of the options as query string parameters.
|
120
|
+
def scaffold_url(action, options = {})
|
121
|
+
escaped_options = {}
|
122
|
+
options.each{|k,v| escaped_options[u(k.to_s)] = u(v.to_s)}
|
123
|
+
id = escaped_options.delete('id')
|
124
|
+
id = id ? "/#{id}" : ''
|
125
|
+
id << "?#{escaped_options.to_a.collect{|k,v| "#{k}=#{v}"}.join('&')}" unless escaped_options.empty?
|
126
|
+
"#{@scaffold_path}/#{action}#{id}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Class methods for Sinatra necessary for Scaffolding Extensions
|
131
|
+
module MetaSinatraController
|
132
|
+
include ScaffoldingExtensions::MetaController
|
133
|
+
attr_accessor :scaffold_root
|
134
|
+
|
135
|
+
private
|
136
|
+
def scaffold_setup_helper
|
137
|
+
include ScaffoldingExtensions::Controller
|
138
|
+
include ScaffoldingExtensions::SinatraController
|
139
|
+
include ScaffoldingExtensions::Helper
|
140
|
+
include ScaffoldingExtensions::PrototypeHelper
|
141
|
+
include ScaffoldingExtensions::SinatraHelper
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
module TextRenderer
|
146
|
+
def render_text(template)
|
147
|
+
template
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
include ScaffoldingExtensions::TextRenderer
|
153
|
+
|
154
|
+
def scaffold(root, model, options = {})
|
155
|
+
scaffold_setup(root).send(:scaffold, model, options)
|
156
|
+
end
|
157
|
+
|
158
|
+
def scaffold_all_models(root, options = {})
|
159
|
+
scaffold_setup(root).send(:scaffold_all_models, options)
|
160
|
+
end
|
161
|
+
|
162
|
+
def scaffold_habtm(root, model, association)
|
163
|
+
scaffold_setup(root).send(:scaffold_habtm, model, association)
|
164
|
+
end
|
165
|
+
|
166
|
+
def scaffold_setup(root)
|
167
|
+
unless klass = ScaffoldingExtensions::SCAFFOLD_ROOTS[root]
|
168
|
+
klass = ScaffoldingExtensions::SCAFFOLD_ROOTS[root] = Class.new
|
169
|
+
klass.send(:extend, ScaffoldingExtensions::MetaSinatraController)
|
170
|
+
klass.scaffold_root = root
|
171
|
+
[:get, :post].each do |req_meth|
|
172
|
+
send(req_meth, "#{klass.scaffold_root}/?:meth?/?:request_id?") do
|
173
|
+
meth = params[:meth] ||= 'index'
|
174
|
+
params[:id] ||= params[:request_id]
|
175
|
+
@controller = klass.new
|
176
|
+
raise(ArgumentError, 'Method Not Allowed') if req_meth == :get && @controller.send(:scaffolded_nonidempotent_method?, meth)
|
177
|
+
@controller.send(:scaffold_set_vars, meth, self)
|
178
|
+
instance_eval(&@controller.send(meth))
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
klass
|
183
|
+
end
|
@@ -0,0 +1,304 @@
|
|
1
|
+
module ScaffoldingExtensions
|
2
|
+
# Helper methods used by the scaffold templates
|
3
|
+
module Helper
|
4
|
+
private
|
5
|
+
# Return a string containing associated objects and links (if they would work) to pages
|
6
|
+
# to manage those objects.
|
7
|
+
def scaffold_association_links
|
8
|
+
klass = @scaffold_class
|
9
|
+
return '' if @scaffold_class.scaffold_associations.empty?
|
10
|
+
read_only = @scaffold_associations_readonly
|
11
|
+
show_edit = read_only ? :show : :edit
|
12
|
+
so = @scaffold_object
|
13
|
+
soid = so.scaffold_id
|
14
|
+
singular_name = @scaffold_options[:singular_name]
|
15
|
+
content = '<h3 class="scaffold_associated_records_header">Associated Records</h3>'
|
16
|
+
content << "<ul id='scaffolded_associations_#{singular_name}_#{soid}' class='#{klass.scaffold_association_list_class}'>\n"
|
17
|
+
klass.scaffold_associations.each do |association|
|
18
|
+
next unless klass.scaffold_show_association_links?(association)
|
19
|
+
class_name = klass.scaffold_associated_name(association)
|
20
|
+
human_name = klass.scaffold_associated_human_name(association)
|
21
|
+
content << "<li>"
|
22
|
+
content << scaffold_check_link(human_name, read_only, "manage_#{class_name}")
|
23
|
+
content << "\n "
|
24
|
+
case klass.scaffold_association_type(association)
|
25
|
+
when :one
|
26
|
+
associated_record = klass.scaffold_associated_objects(association, so, :session=>scaffold_session)
|
27
|
+
content << " - #{scaffold_check_link(associated_record.scaffold_name, false, "#{show_edit}_#{class_name}", :id=>associated_record.scaffold_id) if associated_record}</li>\n"
|
28
|
+
next
|
29
|
+
when :edit
|
30
|
+
content << scaffold_check_link('(associate)', true, "edit_#{singular_name}_#{association}", :id=>soid) unless read_only
|
31
|
+
when :new
|
32
|
+
associated_params = {}
|
33
|
+
klass.scaffold_new_associated_object_values(association, so).each{|key, value| associated_params["#{class_name}[#{key}]"] = value}
|
34
|
+
content << scaffold_check_link('(create)', true, "new_#{class_name}", associated_params) unless read_only
|
35
|
+
end
|
36
|
+
if (records = klass.scaffold_associated_objects(association, so, :session=>scaffold_session)).length > 0
|
37
|
+
content << "<ul>\n"
|
38
|
+
records.each do |associated|
|
39
|
+
content << "<li>#{scaffold_check_link(associated.scaffold_name, false, "#{show_edit}_#{class_name}", :id=>associated.scaffold_id)}</li>\n"
|
40
|
+
end
|
41
|
+
content << "</ul>\n"
|
42
|
+
end
|
43
|
+
content << "</li>\n"
|
44
|
+
end
|
45
|
+
content << "</ul>\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Formats the records returned by scaffold autocompleting to be displayed,
|
49
|
+
# should be an unordered list. By default uses the scaffold_name and id of
|
50
|
+
# the entries as the value.
|
51
|
+
def scaffold_auto_complete_result(entries)
|
52
|
+
return unless entries
|
53
|
+
content = '<ul>'
|
54
|
+
entries.collect{|entry| content << "<li>#{h(entry.scaffold_name_with_id)}</li>"}
|
55
|
+
content << '</ul>'
|
56
|
+
content
|
57
|
+
end
|
58
|
+
|
59
|
+
# Simple button with label text that submits a form to the given url, options are
|
60
|
+
# passed to scaffold_form.
|
61
|
+
def scaffold_button_to(text, url, options={})
|
62
|
+
"#{scaffold_form(url, options)}\n<input type='submit' value='#{text}' />\n</form>"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Simple button with label text that submits a form via Ajax to the given action,
|
66
|
+
# options are passed to scaffold_form_remote_tag.
|
67
|
+
def scaffold_button_to_remote(text, action, options)
|
68
|
+
"#{scaffold_form_remote_tag(action, options)}\n<input type='submit' value=#{text} />\n</form>"
|
69
|
+
end
|
70
|
+
|
71
|
+
# If scaffolding didn't create the action, return the empty string if blank is true
|
72
|
+
# and the text itself if it is not. Otherwise, returns a link to the action, options
|
73
|
+
# are passed to scaffold_link.
|
74
|
+
def scaffold_check_link(text, blank, action, options={})
|
75
|
+
scaffolded_method?(action) ? scaffold_link(text, action, options) : (blank ? '' : h(text))
|
76
|
+
end
|
77
|
+
|
78
|
+
# Proc that formats the label and tag in a table row
|
79
|
+
def scaffold_default_field_wrapper
|
80
|
+
Proc.new{|label, tag| "<tr><td>#{label}</td><td>#{tag}</td></tr>\n"}
|
81
|
+
end
|
82
|
+
|
83
|
+
# Proc that formats each field row inside a table
|
84
|
+
def scaffold_default_form_wrapper
|
85
|
+
Proc.new{|rows|"<table class='#{@scaffold_class.scaffold_table_class(:form)}'><tbody>\n#{rows.join}</tbody></table>\n"}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Forms an input field for the given field_type.
|
89
|
+
#
|
90
|
+
# The following field types are recognized:
|
91
|
+
# * :text => textarea
|
92
|
+
# * :boolean => select box with blank (NULL), True, and False
|
93
|
+
# * :association => select box or autocompleteing text box for the association
|
94
|
+
# * :submit, :password, :hidden, :file => input tag with matching type
|
95
|
+
# * everything else => input tag with type text
|
96
|
+
#
|
97
|
+
# Options are converted to html attributes, with the following special options:
|
98
|
+
# * :value => the value of the tag, which usually will be just an html attribute,
|
99
|
+
# but can be the html inside the textarea, or the choice of selection
|
100
|
+
# for one of the selection options
|
101
|
+
# * :id => if :name is blank, it is also used for :name
|
102
|
+
def scaffold_field_tag(field_type, options, object=nil, field=nil, record_name=nil, field_id=nil)
|
103
|
+
options[:name] ||= options[:id] if options[:id]
|
104
|
+
value = options[:value] || object.scaffold_value(field)
|
105
|
+
case field_type
|
106
|
+
when :text
|
107
|
+
"<textarea #{scaffold_options_to_html(options)}>#{h value.to_s}</textarea>"
|
108
|
+
when :boolean
|
109
|
+
s = {value=>"selected='selected'"}
|
110
|
+
"<select #{scaffold_options_to_html(options)}><option></option><option value='f' #{s[false]}>False</option><option value='t' #{s[true]}>True</option></select>"
|
111
|
+
when :association
|
112
|
+
klass = object.class
|
113
|
+
if klass.scaffold_association_use_auto_complete(field)
|
114
|
+
assocated_object = klass.scaffold_associated_objects(field, object, :session=>scaffold_session)
|
115
|
+
options[:value] = assocated_object ? assocated_object.scaffold_name_with_id : ''
|
116
|
+
scaffold_text_field_tag_with_auto_complete(options[:id], record_name, field, options)
|
117
|
+
else
|
118
|
+
s = {object.scaffold_value(field_id).to_i=>"selected='selected'"}
|
119
|
+
associated_objects = klass.scaffold_association_find_objects(field, :session=>scaffold_session, :object=>object)
|
120
|
+
"<select #{scaffold_options_to_html(options)}><option></option>#{associated_objects.collect{|ao| "<option value='#{i = ao.scaffold_id}' #{s[i]}>#{h ao.scaffold_name}</option>"}.join}</select>"
|
121
|
+
end
|
122
|
+
else
|
123
|
+
options[:type] = :text
|
124
|
+
case field_type
|
125
|
+
when :submit, :password, :hidden, :file
|
126
|
+
options[:size] ||= 30 if field_type == :password
|
127
|
+
options[:type] = field_type
|
128
|
+
when :date, :integer, :float
|
129
|
+
options[:size] ||= 10
|
130
|
+
else
|
131
|
+
options[:size] ||= 30
|
132
|
+
end
|
133
|
+
options[:value] ||= value
|
134
|
+
"<input #{scaffold_options_to_html(options)} />"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Returns an opening form tag for the given url. The following options are
|
139
|
+
# used:
|
140
|
+
# * :method => the method (:get or :post) to be used (default is :post)
|
141
|
+
# * :attributes => extra html attributes for the form tag, as a string
|
142
|
+
def scaffold_form(url, options={})
|
143
|
+
meth = options.delete(:method) || :post
|
144
|
+
"<form action='#{url}' method='#{meth}' #{options[:attributes]}>#{scaffold_token_tag if meth.to_s == 'post'}"
|
145
|
+
end
|
146
|
+
|
147
|
+
# "enctype='multipart/form-data'" if there is a file field in the form, otherwise
|
148
|
+
# the empty string.
|
149
|
+
def scaffold_form_enctype(column_names)
|
150
|
+
klass = @scaffold_class
|
151
|
+
column_names.each{|column_name| return "enctype='multipart/form-data'" if klass.scaffold_column_type(column_name) == :file }
|
152
|
+
''
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns html fragment containing autocompleting text or select boxes to add associated records
|
156
|
+
# to the current record, and line items with buttons to remove associated records
|
157
|
+
# from the current record.
|
158
|
+
def scaffold_habtm_ajax_associations
|
159
|
+
klass = @scaffold_class
|
160
|
+
return '' unless klass.scaffold_habtm_with_ajax
|
161
|
+
sn = @scaffold_options[:singular_name]
|
162
|
+
so = @scaffold_object
|
163
|
+
soid = so.scaffold_id
|
164
|
+
content = "<div class='habtm_ajax_add_associations' id='#{sn}_habtm_ajax_add_associations'>"
|
165
|
+
klass.scaffold_habtm_associations.reject{|association| !scaffolded_method?("add_#{association}_to_#{sn}")}.each do |association|
|
166
|
+
content << "#{scaffold_form_remote_tag("add_#{association}_to_#{sn}", :id=>soid)}\n#{scaffold_habtm_ajax_tag("#{sn}_#{association}_id", so, sn, association)}\n<input name='commit' type='submit' value='Add #{klass.scaffold_associated_human_name(association).singularize}' /></form>\n"
|
167
|
+
end
|
168
|
+
content << "</div><div class='habtm_ajax_remove_associations' id='#{sn}_habtm_ajax_remove_associations'><ul id='#{sn}_associated_records_list'>"
|
169
|
+
klass.scaffold_habtm_associations.reject{|association| !scaffolded_method?("remove_#{association}_from_#{sn}")}.each do |association|
|
170
|
+
klass.scaffold_associated_objects(association, so, :session=>scaffold_session).each do |associated_record|
|
171
|
+
content << scaffold_habtm_association_line_item(klass, association, @scaffold_object, associated_record)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
content << '</ul></div>'
|
175
|
+
content
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns an autocompleting text box, or a select box displaying the records for the associated model that
|
179
|
+
# are not already associated with this record.
|
180
|
+
def scaffold_habtm_ajax_tag(id, record, model_name, association)
|
181
|
+
klass = record.class
|
182
|
+
if klass.scaffold_association_use_auto_complete(association)
|
183
|
+
scaffold_text_field_tag_with_auto_complete(id, model_name, association)
|
184
|
+
else
|
185
|
+
scaffold_select_tag(id, klass.scaffold_unassociated_objects(association, record, :session=>scaffold_session))
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Line item with button for removing the associated record from the current record
|
190
|
+
def scaffold_habtm_association_line_item(klass, association, record, associated_record)
|
191
|
+
name = klass.scaffold_name
|
192
|
+
associated_suffix = klass.scaffold_associated_name(association)
|
193
|
+
arid = associated_record.scaffold_id
|
194
|
+
rid = record.scaffold_id
|
195
|
+
content = "<li id='#{name}_#{rid}_#{association}_#{arid}'>\n"
|
196
|
+
content << scaffold_check_link(klass.scaffold_associated_human_name(association), false, "manage_#{associated_suffix}")
|
197
|
+
content << " - \n"
|
198
|
+
content << scaffold_check_link(associated_record.scaffold_name, false, "edit_#{associated_suffix}", :id=>arid)
|
199
|
+
content << "\n"
|
200
|
+
content << scaffold_button_to_remote('Remove', "remove_#{association}_from_#{name}", :id=>rid, "#{name}_#{association}_id"=>arid)
|
201
|
+
content << "\n</li>\n"
|
202
|
+
content
|
203
|
+
end
|
204
|
+
|
205
|
+
# Script tag with javascript included inside a CDATA section
|
206
|
+
def scaffold_javascript_tag(javascript)
|
207
|
+
"<script type='text/javascript'>\n//<![CDATA[\n#{javascript}\n//]]>\n</script>"
|
208
|
+
end
|
209
|
+
|
210
|
+
# Label for the given html id with the content text
|
211
|
+
def scaffold_label(id, text)
|
212
|
+
"<label for='#{id}'>#{h text}</label>"
|
213
|
+
end
|
214
|
+
|
215
|
+
# 'a' tag with the content text. action and options are passed to
|
216
|
+
# scaffold_url to get the href.
|
217
|
+
def scaffold_link(text, action, options={})
|
218
|
+
"<a href='#{scaffold_url(action, options)}'>#{h text}</a>"
|
219
|
+
end
|
220
|
+
|
221
|
+
# Returns link to the scaffolded management page for the model if it was created by the scaffolding.
|
222
|
+
def scaffold_manage_link
|
223
|
+
manage = "manage#{@scaffold_suffix}"
|
224
|
+
"<br />#{scaffold_link("Manage #{@scaffold_options[:plural_lc_human_name]}", manage)}" if scaffolded_method?(manage)
|
225
|
+
end
|
226
|
+
|
227
|
+
# A html fragment containing a paragraph stating there were errors for the @scaffold_object
|
228
|
+
# and an unordered list with error messages for that object. If there are no errors,
|
229
|
+
# returns an empty string.
|
230
|
+
def scaffold_model_error_messages
|
231
|
+
return '' unless (errors = @scaffold_object.scaffold_error_messages).length > 0
|
232
|
+
content = '<p>There were problems with the following fields:</p><ul>'
|
233
|
+
errors.each{|msg| content << "<li>msg</li>"}
|
234
|
+
content << '</ul>'
|
235
|
+
content
|
236
|
+
end
|
237
|
+
|
238
|
+
# An html fragment with all of the given fields for @scaffold_object, suitable for
|
239
|
+
# inclusion in a form tag
|
240
|
+
def scaffold_model_field_tags(fields)
|
241
|
+
klass = @scaffold_class
|
242
|
+
object = @scaffold_object
|
243
|
+
record_name = @scaffold_options[:singular_name]
|
244
|
+
field_wrapper = klass.scaffold_field_wrapper || scaffold_default_field_wrapper
|
245
|
+
rows = fields.collect do |field|
|
246
|
+
field_id = klass.scaffold_field_id(field)
|
247
|
+
label = scaffold_label("#{record_name}_#{field_id}", klass.scaffold_column_name(field))
|
248
|
+
options = klass.scaffold_column_options(field).merge(:name=>"#{record_name}[#{field_id}]", :id=>"#{record_name}_#{field_id}")
|
249
|
+
field_tag = scaffold_field_tag(klass.scaffold_column_type(field), options, object, field, record_name, field_id)
|
250
|
+
field_wrapper.call(label, field_tag)
|
251
|
+
end
|
252
|
+
(klass.scaffold_form_wrapper || scaffold_default_form_wrapper).call(rows)
|
253
|
+
end
|
254
|
+
|
255
|
+
# Returns an appropriate scaffolded data entry form for the model, with any related error messages.
|
256
|
+
# If a block is given, it yields an empty string which should be modified with html to be added
|
257
|
+
# inside the form before the submit button.
|
258
|
+
def scaffold_model_form(action, fields, &block)
|
259
|
+
content = ''
|
260
|
+
options = {}
|
261
|
+
options[:id] = @scaffold_object.scaffold_id if action=='update'
|
262
|
+
<<-END
|
263
|
+
#{scaffold_model_error_messages}
|
264
|
+
#{scaffold_form(scaffold_url("#{action}#{@scaffold_suffix}", options), :attributes=>scaffold_form_enctype(fields))}
|
265
|
+
#{scaffold_model_field_tags(fields)}
|
266
|
+
#{(yield content; content) if block_given?}
|
267
|
+
<input type='submit' value="#{@scaffold_submit_value || "#{action.capitalize} #{@scaffold_options[:singular_lc_human_name]}"}" />
|
268
|
+
</form>
|
269
|
+
END
|
270
|
+
end
|
271
|
+
|
272
|
+
# Turns a hash of options into a string of html attributes, html escaping the values
|
273
|
+
def scaffold_options_to_html(options)
|
274
|
+
options.collect{|k,v| "#{k}=\"#{h v.to_s}\""}.join(' ')
|
275
|
+
end
|
276
|
+
|
277
|
+
# The suffix needed to params that should be lists. The empty string by default.
|
278
|
+
def scaffold_param_list_suffix
|
279
|
+
''
|
280
|
+
end
|
281
|
+
|
282
|
+
# A select tag with the provided name for the given collection of items. The options
|
283
|
+
# will have the value of scaffold_id and the content of scaffold_name. If multiple is
|
284
|
+
# true, creates a multi-select box.
|
285
|
+
def scaffold_select_tag(name, collection, multiple = false)
|
286
|
+
"<select name='#{name}#{scaffold_param_list_suffix if multiple}' id='#{name}' #{"multiple='multiple'" if multiple}>#{'<option></option>' unless multiple}#{collection.collect{|obj| "<option value='#{i = obj.scaffold_id}' id='#{name}_#{i}'>#{h obj.scaffold_name}</option>"}.join("\n")}</select>"
|
287
|
+
end
|
288
|
+
|
289
|
+
# Text field with scaffold autocompleting. The id is the html id, and the model name and association
|
290
|
+
# are passed to scaffold_javascript_autocompleter. The options are passed to scaffold_field_tag.
|
291
|
+
def scaffold_text_field_tag_with_auto_complete(id, model_name, association = nil, options = {})
|
292
|
+
content = ScaffoldingExtensions.auto_complete_css.dup
|
293
|
+
content << scaffold_field_tag(:string, {:value=>'', :id=>id, :class=>'autocomplete'}.merge(options))
|
294
|
+
content << scaffold_javascript_autocompleter(id, model_name, association)
|
295
|
+
content
|
296
|
+
end
|
297
|
+
|
298
|
+
# A tag for a CSRF protection token. The empty string by default as it
|
299
|
+
# is framework dependent.
|
300
|
+
def scaffold_token_tag
|
301
|
+
''
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|