scaffolding_extensions 1.0.0
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 +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
|