mack 0.6.1.2 → 0.7.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/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
|