mack 0.6.1.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +49 -3
- data/README +4 -4
- data/bin/mack +1 -2
- data/lib/mack/controller/controller.rb +1 -1
- data/lib/mack/controller/cookie_jar.rb +4 -0
- data/lib/mack/controller/request.rb +55 -3
- data/lib/mack/controller/session.rb +9 -0
- data/lib/mack/controller/tell.rb +13 -0
- data/lib/mack/core_extensions/kernel.rb +39 -11
- data/lib/mack/core_extensions/symbol.rb +107 -0
- data/lib/mack/generators/controller_generator/controller_generator.rb +64 -0
- data/lib/mack/generators/controller_generator/manifest.yml +18 -0
- data/lib/mack/generators/controller_generator/templates/app/controllers/controller.rb.template +7 -0
- data/lib/mack/generators/controller_generator/templates/test/controllers/rspec.rb.template +15 -0
- data/lib/mack/generators/controller_generator/templates/test/controllers/test_case.rb.template +15 -0
- data/lib/mack/generators/controller_helper_generator/controller_helper_generator.rb +16 -0
- data/lib/mack/generators/controller_helper_generator/manifest.yml +15 -0
- data/lib/mack/generators/controller_helper_generator/templates/app/helpers/controllers/helper.rb.template +7 -0
- data/lib/mack/generators/controller_helper_generator/templates/test/helpers/controllers/rspec.rb.template +6 -0
- data/lib/mack/generators/controller_helper_generator/templates/test/helpers/controllers/test_case.rb.template +10 -0
- data/lib/mack/generators/mack_application_generator/mack_application_generator.rb +1 -0
- data/lib/mack/generators/mack_application_generator/manifest.yml +26 -10
- data/lib/mack/generators/mack_application_generator/templates/Rakefile.template +1 -1
- data/lib/mack/generators/mack_application_generator/templates/app/controllers/default_controller.rb.template +7 -1
- data/lib/mack/generators/mack_application_generator/templates/app/helpers/controllers/default_controller_helper.rb.template +1 -1
- data/lib/mack/generators/mack_application_generator/templates/app/helpers/views/application_helper.rb.template +3 -0
- data/lib/mack/generators/mack_application_generator/templates/app/views/default/index.html.erb.template +60 -2
- data/lib/mack/generators/mack_application_generator/templates/app/views/layouts/application.html.erb.template +10 -2
- data/lib/mack/generators/mack_application_generator/templates/config/app_config/default.yml.template +21 -0
- data/lib/mack/generators/mack_application_generator/templates/config/app_config/development.yml.template +1 -0
- data/lib/mack/generators/mack_application_generator/templates/config/app_config/production.yml.template +1 -0
- data/lib/mack/generators/mack_application_generator/templates/config/app_config/test.yml.template +1 -0
- data/lib/mack/generators/mack_application_generator/templates/config/database.yml.template +24 -0
- data/lib/mack/generators/mack_application_generator/templates/config/initializers/inflections.rb.template +13 -0
- data/lib/mack/generators/mack_application_generator/templates/config/initializers/mime_types.rb.template +1 -0
- data/lib/mack/generators/mack_application_generator/templates/public/stylesheets/scaffold.css.template +125 -29
- data/lib/mack/generators/mack_application_generator/templates/test/{functional → controllers}/default_controller_spec.rb.template +2 -0
- data/lib/mack/generators/mack_application_generator/templates/test/{functional → controllers}/default_controller_test.rb.template +2 -0
- data/lib/mack/generators/mack_application_generator/templates/test/helpers/controllers/default_controller_helper_spec.rb.template +6 -0
- data/lib/mack/generators/mack_application_generator/templates/test/helpers/controllers/default_controller_helper_test.rb.template +10 -0
- data/lib/mack/generators/mack_application_generator/templates/test/helpers/views/application_helper_spec.rb.template +6 -0
- data/lib/mack/generators/mack_application_generator/templates/test/helpers/views/application_helper_test.rb.template +10 -0
- data/lib/mack/generators/view_helper_generator/manifest.yml +15 -0
- data/lib/mack/generators/view_helper_generator/templates/app/helpers/views/helper.rb.template +7 -0
- data/lib/mack/generators/view_helper_generator/templates/test/helpers/views/rspec.rb.template +6 -0
- data/lib/mack/generators/view_helper_generator/templates/test/helpers/views/test_case.rb.template +10 -0
- data/lib/mack/generators/view_helper_generator/view_helper_generator.rb +16 -0
- data/lib/mack/initialization/application.rb +6 -6
- data/lib/mack/initialization/configuration.rb +17 -9
- data/lib/mack/initialization/helpers.rb +3 -3
- data/lib/mack/initialization/logging.rb +30 -20
- data/lib/mack/rendering/view_template.rb +1 -0
- data/lib/mack/routing/route_map.rb +10 -12
- data/lib/mack/routing/urls.rb +0 -40
- data/lib/mack/runner.rb +7 -2
- data/lib/mack/runner_helpers/request_logger.rb +1 -1
- data/lib/mack/runner_helpers/session.rb +13 -6
- data/lib/mack/tasks/mack_dump_tasks.rake +1 -8
- data/lib/mack/tasks/mack_tasks.rake +1 -1
- data/lib/mack/tasks/test_tasks.rake +2 -2
- data/lib/mack/testing/file.rb +28 -0
- data/lib/mack/testing/helpers.rb +59 -4
- data/lib/mack/testing/rspec.rb +19 -7
- data/lib/mack/utils/mime_types.yml +1 -0
- data/lib/mack/utils/server.rb +1 -1
- data/lib/mack/version.rb +3 -0
- data/lib/mack/view_helpers/date_time_helpers.rb +106 -0
- data/lib/mack/view_helpers/form_helpers.rb +282 -0
- data/lib/mack/view_helpers/html_helpers.rb +4 -88
- data/lib/mack/view_helpers/link_helpers.rb +171 -0
- data/lib/mack/view_helpers/object_helpers.rb +14 -0
- data/lib/mack/view_helpers/string_helpers.rb +41 -0
- data/lib/mack_app.rb +1 -0
- data/lib/mack_core.rb +3 -7
- data/lib/mack_tasks.rb +8 -16
- metadata +36 -27
- data/bin/mack_ring_server +0 -33
- data/lib/mack/core_extensions/string.rb +0 -21
- data/lib/mack/distributed/errors/errors.rb +0 -27
- data/lib/mack/distributed/routing/urls.rb +0 -44
- data/lib/mack/distributed/utils/rinda.rb +0 -45
- data/lib/mack/initialization/orm_support.rb +0 -20
- data/lib/mack/tasks/mack_ring_server_tasks.rake +0 -33
- data/lib/mack/utils/crypt/default_worker.rb +0 -28
- data/lib/mack/utils/crypt/keeper.rb +0 -46
@@ -17,7 +17,7 @@ module Mack
|
|
17
17
|
@total_time = @end_time - @start_time
|
18
18
|
@requests_per_second = (1 / @total_time).round
|
19
19
|
if app_config.log.detailed_requests
|
20
|
-
msg = "\n\t[#{request.
|
20
|
+
msg = "\n\t[#{request.params[:method].to_s.upcase}] '#{request.path_info}'\n"
|
21
21
|
msg << "\tSession ID: #{request.session.id}\n" if app_config.mack.use_sessions
|
22
22
|
msg << "\tParameters: #{request.all_params}\n"
|
23
23
|
msg << Mack::Utils::Ansi::Color.wrap(app_config.log.completed_color, "\tCompleted in #{@total_time} (#{@requests_per_second} reqs/sec) | #{response.status} [#{request.full_host}]")
|
@@ -7,26 +7,33 @@ module Mack
|
|
7
7
|
|
8
8
|
def start(request, response, cookies)
|
9
9
|
if app_config.mack.use_sessions
|
10
|
-
sess_id = cookies
|
11
|
-
unless sess_id
|
12
|
-
sess_id = create_new_session(request, response, cookies)
|
10
|
+
self.sess_id = retrieve_session_id(request, response, cookies)
|
11
|
+
unless self.sess_id
|
12
|
+
self.sess_id = create_new_session(request, response, cookies)
|
13
13
|
else
|
14
|
-
sess = Cachetastic::Caches::MackSessionCache.get(sess_id)
|
14
|
+
sess = Cachetastic::Caches::MackSessionCache.get(self.sess_id)
|
15
15
|
if sess
|
16
16
|
request.session = sess
|
17
17
|
else
|
18
18
|
# we couldn't find it in the store, so we need to create it:
|
19
|
-
sess_id = create_new_session(request, response, cookies)
|
19
|
+
self.sess_id = create_new_session(request, response, cookies)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
def complete(request, response, cookies)
|
26
|
-
|
26
|
+
unless response.redirection?
|
27
|
+
request.session.delete(:tell)
|
28
|
+
end
|
29
|
+
Cachetastic::Caches::MackSessionCache.set(request.session.id, request.session) if app_config.mack.use_sessions
|
27
30
|
end
|
28
31
|
|
29
32
|
private
|
33
|
+
def retrieve_session_id(request, response, cookies)
|
34
|
+
cookies[app_config.mack.session_id]
|
35
|
+
end
|
36
|
+
|
30
37
|
def create_new_session(request, response, cookies)
|
31
38
|
id = String.randomize(40).downcase
|
32
39
|
cookies[app_config.mack.session_id] = {:value => id, :expires => nil}
|
@@ -4,14 +4,7 @@ namespace :mack do
|
|
4
4
|
|
5
5
|
desc "Dumps out the configuration for the specified environment."
|
6
6
|
task :config => :environment do
|
7
|
-
|
8
|
-
conf = {}
|
9
|
-
fcs.each_pair do |k, v|
|
10
|
-
unless v.is_a?(Application::Configuration::Namespace)
|
11
|
-
conf[k.to_s] = v unless k.to_s.match("__")
|
12
|
-
end
|
13
|
-
end
|
14
|
-
pp conf
|
7
|
+
pp Mack::Configuration.dump
|
15
8
|
end # config
|
16
9
|
|
17
10
|
desc "Show all the routes available"
|
@@ -10,7 +10,7 @@ namespace :mack do
|
|
10
10
|
libs = []
|
11
11
|
libs << "-r irb/completion"
|
12
12
|
libs << "-r #{File.join(File.dirname(__FILE__), '..', 'initialization', 'console')}"
|
13
|
-
|
13
|
+
system "irb #{libs.join(" ")} --simple-prompt"
|
14
14
|
end # console
|
15
15
|
|
16
16
|
end # mack
|
@@ -26,7 +26,7 @@ namespace :test do
|
|
26
26
|
ENV["MACK_ENV"] = "test"
|
27
27
|
Rake::Task["mack:environment"].invoke
|
28
28
|
Rake::Task["test:setup"].invoke
|
29
|
-
x = `rcov test/**/*_#{app_config.mack.testing_framework == "rspec" ? "spec" : "test"}.rb -T --no-html -x Rakefile,config\/`
|
29
|
+
x = `rcov test/**/*_#{app_config.mack.testing_framework == "rspec" ? "spec" : "test"}.rb -T --no-html -x Rakefile,config\/,tasks\/`
|
30
30
|
x.each do |line|
|
31
31
|
case line
|
32
32
|
when /^\+[\+\-]*\+$/, /^\|.*\|$/, /\d+\sLines\s+\d+\sLOC/
|
@@ -40,7 +40,7 @@ namespace :test do
|
|
40
40
|
ENV["MACK_ENV"] = "test"
|
41
41
|
Rake::Task["mack:environment"].invoke
|
42
42
|
Rake::Task["test:setup"].invoke
|
43
|
-
`rcov test/**/*_#{app_config.mack.testing_framework == "rspec" ? "spec" : "test"}.rb -x Rakefile,config\/`
|
43
|
+
`rcov test/**/*_#{app_config.mack.testing_framework == "rspec" ? "spec" : "test"}.rb -x Rakefile,config\/,tasks\/`
|
44
44
|
`open coverage/index.html`
|
45
45
|
end
|
46
46
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'base64'
|
2
|
+
module Mack
|
3
|
+
module Testing
|
4
|
+
class FileWrapper # :nodoc:
|
5
|
+
|
6
|
+
attr_reader :path
|
7
|
+
attr_reader :file_name
|
8
|
+
attr_reader :content
|
9
|
+
attr_reader :size
|
10
|
+
attr_reader :mime
|
11
|
+
|
12
|
+
def initialize(path)
|
13
|
+
@path = path
|
14
|
+
@file_name = File.basename(path)
|
15
|
+
extension = File.extname(path)
|
16
|
+
extension = extension.gsub!(".", "")
|
17
|
+
if extension and !extension.empty?
|
18
|
+
@mime = Mack::Utils::MimeTypes.instance.get(extension) if extension
|
19
|
+
else
|
20
|
+
@mime = "application/octet-stream"
|
21
|
+
end
|
22
|
+
raw_content = File.read(path)
|
23
|
+
@content = Base64.encode64(raw_content).strip
|
24
|
+
@size = @content.size
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/mack/testing/helpers.rb
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
require "test/unit"
|
2
2
|
|
3
|
+
#--
|
4
|
+
if Mack.env == "test"
|
5
|
+
module Mack
|
6
|
+
module RunnerHelpers # :nodoc:
|
7
|
+
class Session
|
8
|
+
private
|
9
|
+
def retrieve_session_id(request, response, cookies)
|
10
|
+
$current_session_id
|
11
|
+
end
|
12
|
+
end # Session
|
13
|
+
end # RunnerHelpers
|
14
|
+
end # Mack
|
15
|
+
end
|
16
|
+
#++
|
17
|
+
|
3
18
|
module Mack
|
4
19
|
module Testing # :nodoc:
|
5
20
|
module Helpers
|
@@ -65,12 +80,27 @@ module Mack
|
|
65
80
|
|
66
81
|
# Performs a 'put' request for the specified uri.
|
67
82
|
def put(uri, options = {})
|
68
|
-
|
83
|
+
if options[:multipart]
|
84
|
+
form_input = build_multipart_data(options)
|
85
|
+
build_response(request.put(uri, build_request_options({"CONTENT_TYPE" => "multipart/form-data, boundary=Mack-boundary", "CONTENT_LENGTH" => form_input.size, :input => form_input})))
|
86
|
+
else
|
87
|
+
build_response(request.put(uri, build_request_options({:input => options.to_params})))
|
88
|
+
end
|
69
89
|
end
|
70
|
-
|
90
|
+
|
91
|
+
# create a wrapper object for file upload testing.
|
92
|
+
def file_for_upload(path)
|
93
|
+
return Mack::Testing::FileWrapper.new(path)
|
94
|
+
end
|
95
|
+
|
71
96
|
# Performs a 'post' request for the specified uri.
|
72
97
|
def post(uri, options = {})
|
73
|
-
|
98
|
+
if options[:multipart]
|
99
|
+
form_input = build_multipart_data(options)
|
100
|
+
build_response(request.post(uri, build_request_options({"CONTENT_TYPE" => "multipart/form-data, boundary=Mack-boundary", "CONTENT_LENGTH" => form_input.size, :input => form_input})))
|
101
|
+
else
|
102
|
+
build_response(request.post(uri, build_request_options({:input => options.to_params})))
|
103
|
+
end
|
74
104
|
end
|
75
105
|
|
76
106
|
# Performs a 'delete' request for the specified uri.
|
@@ -95,11 +125,12 @@ module Mack
|
|
95
125
|
|
96
126
|
# Returns a Mack::Session from the request.
|
97
127
|
def session # :nodoc:
|
98
|
-
Cachetastic::Caches::MackSessionCache.get(
|
128
|
+
Cachetastic::Caches::MackSessionCache.get($current_session_id) do
|
99
129
|
id = String.randomize(40).downcase
|
100
130
|
set_cookie(app_config.mack.session_id, id)
|
101
131
|
sess = Mack::Session.new(id)
|
102
132
|
Cachetastic::Caches::MackSessionCache.set(id, sess)
|
133
|
+
$current_session_id = id
|
103
134
|
sess
|
104
135
|
end
|
105
136
|
end
|
@@ -108,7 +139,9 @@ module Mack
|
|
108
139
|
def in_session
|
109
140
|
@_mack_in_session = true
|
110
141
|
clear_session
|
142
|
+
$current_session_id = session.id
|
111
143
|
yield
|
144
|
+
$current_session_id = nil
|
112
145
|
clear_session
|
113
146
|
@_mack_in_session = false
|
114
147
|
end
|
@@ -134,6 +167,28 @@ module Mack
|
|
134
167
|
end
|
135
168
|
|
136
169
|
private
|
170
|
+
|
171
|
+
def build_multipart_data(options)
|
172
|
+
form_input = ""
|
173
|
+
boundary = "--Mack-boundary\r\n"
|
174
|
+
options.each_pair do |k, v|
|
175
|
+
if v.kind_of?(Mack::Testing::FileWrapper)
|
176
|
+
form_input += boundary
|
177
|
+
form_input += "content-disposition: form-data; name=\"#{k}\"; filename=\"#{v.file_name}\"\r\n"
|
178
|
+
form_input += "Content-Type: #{v.mime}\r\n\r\n"
|
179
|
+
form_input += "#{v.content}\r\n"
|
180
|
+
elsif k != :multipart
|
181
|
+
form_input += boundary
|
182
|
+
form_input += "content-disposition: form-data; name=\"#{k}\"\r\n"
|
183
|
+
form_input += "Content-Type: text/plain\r\n\r\n"
|
184
|
+
form_input += "#{v}\r\n"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
form_input += boundary
|
188
|
+
return form_input
|
189
|
+
end
|
190
|
+
|
191
|
+
|
137
192
|
def test_cookies
|
138
193
|
@test_cookies = {} if @test_cookies.nil?
|
139
194
|
@test_cookies
|
data/lib/mack/testing/rspec.rb
CHANGED
@@ -7,15 +7,27 @@ module Spec # :nodoc:
|
|
7
7
|
include Mack::Routes::Urls
|
8
8
|
include Mack::Testing::Helpers
|
9
9
|
|
10
|
-
alias_method :
|
10
|
+
alias_method :mack_run_with_description_capturing, :run_with_description_capturing
|
11
11
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
12
|
+
def run_with_description_capturing
|
13
|
+
begin
|
14
|
+
in_session do
|
15
|
+
instance_eval(&(@_implementation || PENDING_EXAMPLE_BLOCK))
|
16
|
+
end
|
17
|
+
ensure
|
18
|
+
@_matcher_description = Spec::Matchers.generated_description
|
19
|
+
Spec::Matchers.clear_generated_description
|
15
20
|
end
|
16
|
-
@__res
|
17
21
|
end
|
18
22
|
|
23
|
+
end # ExampleMethods
|
24
|
+
end # Example
|
25
|
+
|
26
|
+
module Matchers # :nodoc:
|
27
|
+
def match(regexp)
|
28
|
+
regexp = /#{regexp}/ if regexp.is_a?(String)
|
29
|
+
Matchers::Match.new(regexp)
|
19
30
|
end
|
20
|
-
end
|
21
|
-
|
31
|
+
end # Matchers
|
32
|
+
|
33
|
+
end # Spec
|
@@ -282,6 +282,7 @@
|
|
282
282
|
:xls: application/excel; application/vnd.ms-excel; application/x-excel; application/x-msexcel
|
283
283
|
:js_official: application/x-javascript
|
284
284
|
:js: text/javascript
|
285
|
+
:json: text/javascript
|
285
286
|
:ani: application/x-navi-animation
|
286
287
|
:rmp: audio/x-pn-realaudio; audio/x-pn-realaudio-plugin
|
287
288
|
:pre: application/x-freelance
|
data/lib/mack/utils/server.rb
CHANGED
@@ -18,7 +18,7 @@ module Mack
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# Any urls listed will go straight to the public directly and will not be served up via the app:
|
21
|
-
app = Rack::Static.new(app, :urls => ["/css", "/images", "/files", "/images", "/stylesheets", "/javascripts", "/media"], :root => "public")
|
21
|
+
app = Rack::Static.new(app, :urls => ["/css", "/images", "/files", "/images", "/stylesheets", "/javascripts", "/media", "/favicon.ico"], :root => "public")
|
22
22
|
app = Rack::Lint.new(app) if app_config.mack.use_lint
|
23
23
|
app = Rack::ShowStatus.new(app)
|
24
24
|
app = Rack::ShowExceptions.new(app) if app_config.mack.show_exceptions
|
data/lib/mack/version.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
module Mack
|
2
|
+
module ViewHelpers # :nodoc:
|
3
|
+
module FormHelpers
|
4
|
+
|
5
|
+
MONTHS = [["January", 1], ["February", 2], ["March", 3], ["April", 4], ["May", 5], ["June", 6], ["July", 7], ["August", 8],
|
6
|
+
["September", 9], ["October", 10], ["November", 11], ["December", 12]]
|
7
|
+
DAYS = []
|
8
|
+
1.upto(31) do |m|
|
9
|
+
DAYS << [(m < 10 ? "0#{m}" : m), m]
|
10
|
+
end
|
11
|
+
HOURS = []
|
12
|
+
1.upto(24) do |h|
|
13
|
+
HOURS << [(h < 10 ? "0#{h}" : h), h]
|
14
|
+
end
|
15
|
+
MINUTES = []
|
16
|
+
1.upto(59) do |m|
|
17
|
+
MINUTES << [(m < 10 ? "0#{m}" : m), m]
|
18
|
+
end
|
19
|
+
|
20
|
+
# This will create a series of select boxes that compromise a time object. By default boxes will be
|
21
|
+
# created for day/month/year hour:minute. You can optionally turn on or off any of these boxes, including
|
22
|
+
# seconds by setting them to true/false. For example:
|
23
|
+
# <%= date_time_select :some_time, :days => false %>
|
24
|
+
# will not produce a select box for days, and so on...
|
25
|
+
#
|
26
|
+
# You can pass in an array of arrays to represent the options for any of the boxes like such:
|
27
|
+
# <%= date_time_select :some_time, :day_options => [[1,"one"], [2,"two"]] %>
|
28
|
+
# Will produce a day select box with only two options. Alternatively you can pass in an array of values
|
29
|
+
# and the options will be done for you. Like such:
|
30
|
+
# <%= date_time_select :some_time, :day_values => 1..60 %>
|
31
|
+
# Will produce a day select box with 60 options whose values and keys will be the same.
|
32
|
+
#
|
33
|
+
# The separators for dates and times can be set with the date_separator and time_separator options. By
|
34
|
+
# default they are:
|
35
|
+
# :date_separator => '/'
|
36
|
+
# :time_separator => ':'
|
37
|
+
def date_time_select(name, *args)
|
38
|
+
var = instance_variable_get("@#{name}")
|
39
|
+
fe = FormElement.new(*args)
|
40
|
+
|
41
|
+
time = var if var.is_a?(Time) || var.is_a?(Date)
|
42
|
+
time = var.nil? ? Time.now : (var.send(fe.calling_method) || Time.now) if time.nil?
|
43
|
+
|
44
|
+
years = []
|
45
|
+
(time.year - 5).upto(time.year + 5) { |y| years << [y, y]}
|
46
|
+
|
47
|
+
options = {:years => true, :months => true, :days => true, :hours => true, :minutes => true, :seconds => false, :year_options => years, :month_options => MONTHS, :day_options => DAYS, :hour_options => HOURS, :minute_options => MINUTES, :second_options => MINUTES, :date_separator => '/', :time_separator => ':'}.merge(fe.options)
|
48
|
+
|
49
|
+
[:year, :month, :day, :hour, :minute, :second].each do |v|
|
50
|
+
if options["#{v}_values".to_sym]
|
51
|
+
options["#{v}_options".to_sym] = []
|
52
|
+
options["#{v}_values".to_sym].each {|i| options["#{v}_options".to_sym] << [i, i]}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
fe.options - [:years, :months, :days, :hours, :minutes, :seconds, :year_options, :month_options, :day_options, :hour_options, :minute_options, :second_options, :year_values, :month_values, :day_values, :hour_values, :minute_values, :second_values]
|
57
|
+
|
58
|
+
label = label_parameter_tag(name, (fe.calling_method == :to_s ? name : "#{name}_#{fe.calling_method}"), var, fe)
|
59
|
+
|
60
|
+
|
61
|
+
date_boxes = []
|
62
|
+
date_boxes << dt_select(:month, name, fe, time.month, options[:month_options]) if options[:months]
|
63
|
+
date_boxes << dt_select(:day, name, fe, time.day, options[:day_options]) if options[:days]
|
64
|
+
date_boxes << dt_select(:year, name, fe, time.year, options[:year_options]) if options[:years]
|
65
|
+
|
66
|
+
time_boxes = []
|
67
|
+
time_boxes << dt_select(:hour, name, fe, time.hour, options[:hour_options]) if options[:hours]
|
68
|
+
time_boxes << dt_select(:minute, name, fe, time.min, options[:minute_options]) if options[:minutes]
|
69
|
+
time_boxes << dt_select(:second, name, fe, time.sec, options[:second_options]) if options[:seconds]
|
70
|
+
|
71
|
+
boxes = date_boxes.join(options[:date_separator])
|
72
|
+
|
73
|
+
unless time_boxes.empty?
|
74
|
+
boxes << " " << time_boxes.join(options[:time_separator])
|
75
|
+
end
|
76
|
+
|
77
|
+
return label + boxes
|
78
|
+
end
|
79
|
+
|
80
|
+
# Used just like date_time_select, but it has hours, minutes, and seconds turned off. See date_time_select
|
81
|
+
# for more options.
|
82
|
+
#
|
83
|
+
# @user = User.new
|
84
|
+
# <%= :user.date_select :birth_date %>
|
85
|
+
# @some_time = Time.new
|
86
|
+
# <%= :date_select :some_time, :label => true %>
|
87
|
+
def date_select(name, *args)
|
88
|
+
fe = FormElement.new(*args)
|
89
|
+
date_time_select(name, fe.calling_method, {:hours => false, :minutes => false, :seconds => false}.merge(fe.options))
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def dt_select(col, name, fe, selected, values)
|
94
|
+
options = {:name => name, :id => name}
|
95
|
+
unless fe.calling_method == :to_s
|
96
|
+
options.merge!(:name => "#{name}[#{fe.calling_method}(#{col})]", :id => "#{name}_#{fe.calling_method}_#{col}")
|
97
|
+
else
|
98
|
+
options.merge!(:name => "#{name}(#{col})", :id => "#{name}_#{col}")
|
99
|
+
end
|
100
|
+
options.merge!(:options => values, :selected => selected)
|
101
|
+
select_tag(name, fe.options.merge(options))
|
102
|
+
end
|
103
|
+
|
104
|
+
end # DateTimeHelpers
|
105
|
+
end # ViewHelpers
|
106
|
+
end # Mack
|
@@ -0,0 +1,282 @@
|
|
1
|
+
module Mack
|
2
|
+
module ViewHelpers # :nodoc:
|
3
|
+
# A useful collection of helpers for forms.
|
4
|
+
module FormHelpers
|
5
|
+
|
6
|
+
# Examples:
|
7
|
+
# <% form(users_create_url) do -%>
|
8
|
+
# # form stuff here...
|
9
|
+
# <% end -%>
|
10
|
+
#
|
11
|
+
# <% form(users_update_url, :method => :put) do -%>
|
12
|
+
# # form stuff here...
|
13
|
+
# <% end -%>
|
14
|
+
#
|
15
|
+
# <% form(photos_create_url, :multipart => true) do -%>
|
16
|
+
# # form stuff here...
|
17
|
+
# <% end -%>
|
18
|
+
def form(action, options = {}, &block)
|
19
|
+
options = {:method => :post, :action => action}.merge(options)
|
20
|
+
if options[:id]
|
21
|
+
options = {:class => options[:id]}.merge(options)
|
22
|
+
end
|
23
|
+
if options[:multipart]
|
24
|
+
options = {:enctype => "multipart/form-data"}.merge(options)
|
25
|
+
options.delete(:multipart)
|
26
|
+
end
|
27
|
+
meth = nil
|
28
|
+
unless options[:method] == :get || options[:method] == :post
|
29
|
+
meth = "<input name=\"_method\" type=\"hidden\" value=\"#{options[:method]}\" />\n"
|
30
|
+
options[:method] = :post
|
31
|
+
end
|
32
|
+
concat("<form#{build_options(options)}>\n", block.binding)
|
33
|
+
concat(meth, block.binding) unless meth.blank?
|
34
|
+
yield
|
35
|
+
concat("\n</form>", block.binding)
|
36
|
+
# content_tag(:form, options, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Generates a button with a form around it and will set the request method to delete.
|
40
|
+
def delete_button(url, value = "Delete", form_options = {}, button_options = {})
|
41
|
+
if button_options[:confirm]
|
42
|
+
button_options[:onclick] = "if (confirm('#{button_options[:confirm]}')) {submit();}; return false;"
|
43
|
+
button_options.delete(:confirm)
|
44
|
+
end
|
45
|
+
t = "\n" << hidden_field(:_method, :value => :delete)
|
46
|
+
t << "\n" << submit_button(value, button_options)
|
47
|
+
t << "\n"
|
48
|
+
content_tag(:form, {:action => url, :method => :post}.merge(form_options), t)
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_deprecated_method :submit_tag, :submit_button, '0.7.0'
|
52
|
+
|
53
|
+
# Examples:
|
54
|
+
# <%= submit_button %> # => <input type="submit" value="Submit" />
|
55
|
+
# <%= submit_button "Login" %> # => <input type="submit" value="Login" />
|
56
|
+
def submit_button(value = "Submit", options = {}, *original_args)
|
57
|
+
# non_content_tag(:input, {:type => :submit, :value => value}.merge(options))
|
58
|
+
content_tag(:button, {:type => :submit}.merge(options), value)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Examples:
|
62
|
+
# @user = User.new(:accepted_tos => true)
|
63
|
+
# <%= check_box :user, :accepted_tos %> # => <input checked="checked" id="user_accepted_tos" name="user[accepted_tos]" type="checkbox" />
|
64
|
+
# <%= check_box :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="checkbox" />
|
65
|
+
def check_box(name, *args)
|
66
|
+
build_form_element(name, {:type => :checkbox}, *args) do |var, fe, options|
|
67
|
+
if options[:value]
|
68
|
+
options.merge!(:checked => "checked")
|
69
|
+
end
|
70
|
+
options.delete(:value)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Examples:
|
75
|
+
# @user = User.new(:bio_file => "~/bio.doc")
|
76
|
+
# <%= file_field :user, :bio_file %> # => <input id="user_bio_field" name="user[bio_field]" type="file" value="~/bio.doc" />
|
77
|
+
# <%= file_field :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="file" value="" />
|
78
|
+
def file_field(name, *args)
|
79
|
+
build_form_element(name, {:type => :file}, *args)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Examples:
|
83
|
+
# @user = User.new(:email => "mark@mackframework.com")
|
84
|
+
# <%= hidden_field :user, :email %> # => <input id="user_email" name="user[email]" type="hidden" value="mark@mackframework.com" />
|
85
|
+
# <%= hidden_field :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="hidden" />
|
86
|
+
def hidden_field(name, *args)
|
87
|
+
build_form_element(name, {:type => :hidden}, *args)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Examples:
|
91
|
+
# <%= image_submit "logo.png" %> # => <input src="/images/logo.png" type="image" />
|
92
|
+
def image_submit(src, options = {})
|
93
|
+
non_content_tag(:input, {:type => :image, :src => "/images/#{src}"}.merge(options))
|
94
|
+
end
|
95
|
+
|
96
|
+
# Examples:
|
97
|
+
# @user = User.new(:level => 1)
|
98
|
+
# <%= select_tag :user, :level, :options => [["one", 1], ["two", 2]] %> # => <select id="user_level" name="user[level]"><option value="1" selected>one</option><option value="2" >two</option></select>
|
99
|
+
# <%= select_tag :user :level, :options => {:one => 1, :two => 2} %> # => <select id="user_level" name="user[level]"><option value="1" selected>one</option><option value="2" >two</option></select>
|
100
|
+
# <%= select_tag :i_dont_exist :options => [["one", 1], ["two", 2]], :selected => 1 %> # => <select id="i_dont_exist" name="i_dont_exist"><option value="1" selected>one</option><option value="2" >two</option></select>
|
101
|
+
def select_tag(name, *args)
|
102
|
+
var = instance_variable_get("@#{name}")
|
103
|
+
fe = FormElement.new(*args)
|
104
|
+
options = {:name => name, :id => name}
|
105
|
+
unless fe.calling_method == :to_s
|
106
|
+
options.merge!(:name => "#{name}[#{fe.calling_method}]", :id => "#{name}_#{fe.calling_method}")
|
107
|
+
end
|
108
|
+
|
109
|
+
content = ""
|
110
|
+
|
111
|
+
opts = fe.options[:options]
|
112
|
+
unless opts.nil?
|
113
|
+
sopts = opts
|
114
|
+
if opts.is_a?(Array)
|
115
|
+
elsif opts.is_a?(Hash)
|
116
|
+
sopts = []
|
117
|
+
opts.sort.each do |k,v|
|
118
|
+
sopts << [k, v]
|
119
|
+
end
|
120
|
+
else
|
121
|
+
raise ArgumentError.new(":options must be either an Array of Arrays or a Hash!")
|
122
|
+
end
|
123
|
+
sel_value = var.send(fe.calling_method) if var
|
124
|
+
sel_value = fe.options[:selected] if fe.options[:selected]
|
125
|
+
sopts.each do |kv|
|
126
|
+
selected = kv[1].to_s == sel_value.to_s ? "selected" : ""
|
127
|
+
content << %{\n<option value="#{kv[1]}" #{selected}>#{kv[0]}</option>}
|
128
|
+
end
|
129
|
+
fe.options.delete(:selected)
|
130
|
+
fe.options.delete(:options)
|
131
|
+
end
|
132
|
+
|
133
|
+
return label_parameter_tag(name, options[:id], var, fe) + content_tag(:select, options.merge(fe.options), content)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Examples:
|
137
|
+
# @user = User.new(:bio => "my bio here")
|
138
|
+
# <%= text_area :user, :bio %> # => <textarea id="user_bio" name="user[bio]">my bio here</textarea>
|
139
|
+
# <%= text_area :i_dont_exist %> # => <textarea id="i_dont_exist" name="i_dont_exist"></textarea>
|
140
|
+
# <%= text_area :i_dont_exist :value => "hi there" %> # => <textarea id="i_dont_exist" name="i_dont_exist">hi there</textarea>
|
141
|
+
def text_area(name, *args)
|
142
|
+
var = instance_variable_get("@#{name}")
|
143
|
+
fe = FormElement.new(*args)
|
144
|
+
options = {:name => name, :id => name, :cols => 60, :rows => 20}
|
145
|
+
if var.nil?
|
146
|
+
value = fe.options[:value]
|
147
|
+
fe.options.delete(:value)
|
148
|
+
return label_parameter_tag(name, options[:id], var, fe) + content_tag(:textarea, options.merge(fe.options), value)
|
149
|
+
else
|
150
|
+
unless fe.calling_method == :to_s
|
151
|
+
options.merge!(:name => "#{name}[#{fe.calling_method}]", :id => "#{name}_#{fe.calling_method}")
|
152
|
+
end
|
153
|
+
options[:value] = var.send(fe.calling_method)
|
154
|
+
|
155
|
+
yield var, fe, options if block_given?
|
156
|
+
|
157
|
+
content = options[:value]
|
158
|
+
options.delete(:value)
|
159
|
+
|
160
|
+
return label_parameter_tag(name, options[:id], var, fe) + content_tag(:textarea, options.merge(fe.options), content)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Examples:
|
165
|
+
# @user = User.new(:email => "mark@mackframework.com")
|
166
|
+
# <%= text_field :user, :email %> # => <input id="user_email" name="user[email]" type="text" value="mark@mackframework.com" />
|
167
|
+
# <%= text_field :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="text" />
|
168
|
+
def text_field(name, *args)
|
169
|
+
build_form_element(name, {:type => :text}, *args)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Examples:
|
173
|
+
# @user = User.new(:email => "mark@mackframework.com")
|
174
|
+
# <%= password_field :user, :email %> # => <input id="user_email" name="user[email]" type="password" value="mark@mackframework.com" />
|
175
|
+
# <%= password_field :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="password" />
|
176
|
+
def password_field(name, *args)
|
177
|
+
build_form_element(name, {:type => :password}, *args)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Examples:
|
181
|
+
# @user = User.new(:level => 1)
|
182
|
+
# <%= radio_button :user, :level %> # => <input checked="checked" id="user_level" name="user[level]" type="radio" value="1" />
|
183
|
+
# <%= radio_button :user, :level, :value => 2 %> # => <input id="user_level" name="user[level]" type="radio" value="2" />
|
184
|
+
# <%= radio_button :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="radio" value="" />
|
185
|
+
def radio_button(name, *args)
|
186
|
+
build_form_element(name, {:type => :radio, :value => ""}, *args) do |var, fe, options|
|
187
|
+
if fe.options[:value]
|
188
|
+
if fe.options[:value] == options[:value]
|
189
|
+
options.merge!(:checked => "checked")
|
190
|
+
end
|
191
|
+
elsif options[:value]
|
192
|
+
options.merge!(:checked => "checked")
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Examples:
|
198
|
+
# @user = User.new
|
199
|
+
# <%= label_tag :user, :email %> # => <label for="user_email">Email</label>
|
200
|
+
# <%= label_tag :i_dont_exist %> # => <label for="i_dont_exist">I don't exist</label>
|
201
|
+
# <%= label_tag :i_dont_exist, :value => "Hello" %> # => <label for="i_dont_exist">Hello</label>
|
202
|
+
def label_tag(name, *args)
|
203
|
+
fe = FormElement.new(*args)
|
204
|
+
unless fe.options[:for]
|
205
|
+
fe.options[:for] = (fe.calling_method == :to_s ? name.to_s : "#{name}_#{fe.calling_method}")
|
206
|
+
end
|
207
|
+
unless fe.options[:value]
|
208
|
+
fe.options[:value] = (fe.calling_method == :to_s ? name.to_s.humanize : fe.calling_method.to_s.humanize)
|
209
|
+
end
|
210
|
+
content = fe.options[:value]
|
211
|
+
fe.options.delete(:value)
|
212
|
+
content_tag(:label, fe.options, content)
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
def label_parameter_tag(name, id, var, fe)
|
217
|
+
label = ""
|
218
|
+
if fe.options[:label]
|
219
|
+
if fe.options[:label].is_a?(TrueClass)
|
220
|
+
if var.nil?
|
221
|
+
label = %{<label for="#{id}">#{name.to_s.humanize}</label>}
|
222
|
+
else
|
223
|
+
label = %{<label for="#{id}">#{fe.calling_method.to_s.humanize}</label>}
|
224
|
+
end
|
225
|
+
else
|
226
|
+
label = %{<label for="#{id}">#{fe.options[:label]}</label>}
|
227
|
+
end
|
228
|
+
fe.options.delete(:label)
|
229
|
+
end
|
230
|
+
return label
|
231
|
+
end
|
232
|
+
|
233
|
+
def build_form_element(name, options, *original_args) # :nodoc:
|
234
|
+
var = instance_variable_get("@#{name}")
|
235
|
+
fe = FormElement.new(*original_args)
|
236
|
+
options = {:name => name, :id => name}.merge(options)
|
237
|
+
if var.nil?
|
238
|
+
return label_parameter_tag(name, options[:id], var, fe) + non_content_tag(:input, options.merge(fe.options))
|
239
|
+
else
|
240
|
+
unless fe.calling_method == :to_s
|
241
|
+
options.merge!(:name => "#{name}[#{fe.calling_method}]", :id => "#{name}_#{fe.calling_method}")
|
242
|
+
end
|
243
|
+
options[:value] = var.send(fe.calling_method)
|
244
|
+
|
245
|
+
yield var, fe, options if block_given?
|
246
|
+
|
247
|
+
return label_parameter_tag(name, options[:id], var, fe) + non_content_tag(:input, options.merge(fe.options))
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
class FormElement # :nodoc:
|
252
|
+
|
253
|
+
attr_accessor :calling_method
|
254
|
+
attr_accessor :options
|
255
|
+
|
256
|
+
def initialize(*args)
|
257
|
+
args = args.parse_splat_args
|
258
|
+
self.calling_method = :to_s
|
259
|
+
self.options = {}
|
260
|
+
case args
|
261
|
+
when Symbol, String
|
262
|
+
self.calling_method = args
|
263
|
+
when Hash
|
264
|
+
self.options = args
|
265
|
+
when Array
|
266
|
+
self.calling_method = args[0]
|
267
|
+
self.options = args[1]
|
268
|
+
when nil
|
269
|
+
else
|
270
|
+
raise ArgumentError.new("You must provide either a Symbol, a String, a Hash, or a combination thereof.")
|
271
|
+
end
|
272
|
+
if self.options[:checked]
|
273
|
+
self.options[:checked] = :checked
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
end # FormHelpers
|
281
|
+
end # ViewHelpers
|
282
|
+
end # Mack
|