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.
Files changed (85) hide show
  1. data/CHANGELOG +49 -3
  2. data/README +4 -4
  3. data/bin/mack +1 -2
  4. data/lib/mack/controller/controller.rb +1 -1
  5. data/lib/mack/controller/cookie_jar.rb +4 -0
  6. data/lib/mack/controller/request.rb +55 -3
  7. data/lib/mack/controller/session.rb +9 -0
  8. data/lib/mack/controller/tell.rb +13 -0
  9. data/lib/mack/core_extensions/kernel.rb +39 -11
  10. data/lib/mack/core_extensions/symbol.rb +107 -0
  11. data/lib/mack/generators/controller_generator/controller_generator.rb +64 -0
  12. data/lib/mack/generators/controller_generator/manifest.yml +18 -0
  13. data/lib/mack/generators/controller_generator/templates/app/controllers/controller.rb.template +7 -0
  14. data/lib/mack/generators/controller_generator/templates/test/controllers/rspec.rb.template +15 -0
  15. data/lib/mack/generators/controller_generator/templates/test/controllers/test_case.rb.template +15 -0
  16. data/lib/mack/generators/controller_helper_generator/controller_helper_generator.rb +16 -0
  17. data/lib/mack/generators/controller_helper_generator/manifest.yml +15 -0
  18. data/lib/mack/generators/controller_helper_generator/templates/app/helpers/controllers/helper.rb.template +7 -0
  19. data/lib/mack/generators/controller_helper_generator/templates/test/helpers/controllers/rspec.rb.template +6 -0
  20. data/lib/mack/generators/controller_helper_generator/templates/test/helpers/controllers/test_case.rb.template +10 -0
  21. data/lib/mack/generators/mack_application_generator/mack_application_generator.rb +1 -0
  22. data/lib/mack/generators/mack_application_generator/manifest.yml +26 -10
  23. data/lib/mack/generators/mack_application_generator/templates/Rakefile.template +1 -1
  24. data/lib/mack/generators/mack_application_generator/templates/app/controllers/default_controller.rb.template +7 -1
  25. data/lib/mack/generators/mack_application_generator/templates/app/helpers/controllers/default_controller_helper.rb.template +1 -1
  26. data/lib/mack/generators/mack_application_generator/templates/app/helpers/views/application_helper.rb.template +3 -0
  27. data/lib/mack/generators/mack_application_generator/templates/app/views/default/index.html.erb.template +60 -2
  28. data/lib/mack/generators/mack_application_generator/templates/app/views/layouts/application.html.erb.template +10 -2
  29. data/lib/mack/generators/mack_application_generator/templates/config/app_config/default.yml.template +21 -0
  30. data/lib/mack/generators/mack_application_generator/templates/config/app_config/development.yml.template +1 -0
  31. data/lib/mack/generators/mack_application_generator/templates/config/app_config/production.yml.template +1 -0
  32. data/lib/mack/generators/mack_application_generator/templates/config/app_config/test.yml.template +1 -0
  33. data/lib/mack/generators/mack_application_generator/templates/config/database.yml.template +24 -0
  34. data/lib/mack/generators/mack_application_generator/templates/config/initializers/inflections.rb.template +13 -0
  35. data/lib/mack/generators/mack_application_generator/templates/config/initializers/mime_types.rb.template +1 -0
  36. data/lib/mack/generators/mack_application_generator/templates/public/stylesheets/scaffold.css.template +125 -29
  37. data/lib/mack/generators/mack_application_generator/templates/test/{functional → controllers}/default_controller_spec.rb.template +2 -0
  38. data/lib/mack/generators/mack_application_generator/templates/test/{functional → controllers}/default_controller_test.rb.template +2 -0
  39. data/lib/mack/generators/mack_application_generator/templates/test/helpers/controllers/default_controller_helper_spec.rb.template +6 -0
  40. data/lib/mack/generators/mack_application_generator/templates/test/helpers/controllers/default_controller_helper_test.rb.template +10 -0
  41. data/lib/mack/generators/mack_application_generator/templates/test/helpers/views/application_helper_spec.rb.template +6 -0
  42. data/lib/mack/generators/mack_application_generator/templates/test/helpers/views/application_helper_test.rb.template +10 -0
  43. data/lib/mack/generators/view_helper_generator/manifest.yml +15 -0
  44. data/lib/mack/generators/view_helper_generator/templates/app/helpers/views/helper.rb.template +7 -0
  45. data/lib/mack/generators/view_helper_generator/templates/test/helpers/views/rspec.rb.template +6 -0
  46. data/lib/mack/generators/view_helper_generator/templates/test/helpers/views/test_case.rb.template +10 -0
  47. data/lib/mack/generators/view_helper_generator/view_helper_generator.rb +16 -0
  48. data/lib/mack/initialization/application.rb +6 -6
  49. data/lib/mack/initialization/configuration.rb +17 -9
  50. data/lib/mack/initialization/helpers.rb +3 -3
  51. data/lib/mack/initialization/logging.rb +30 -20
  52. data/lib/mack/rendering/view_template.rb +1 -0
  53. data/lib/mack/routing/route_map.rb +10 -12
  54. data/lib/mack/routing/urls.rb +0 -40
  55. data/lib/mack/runner.rb +7 -2
  56. data/lib/mack/runner_helpers/request_logger.rb +1 -1
  57. data/lib/mack/runner_helpers/session.rb +13 -6
  58. data/lib/mack/tasks/mack_dump_tasks.rake +1 -8
  59. data/lib/mack/tasks/mack_tasks.rake +1 -1
  60. data/lib/mack/tasks/test_tasks.rake +2 -2
  61. data/lib/mack/testing/file.rb +28 -0
  62. data/lib/mack/testing/helpers.rb +59 -4
  63. data/lib/mack/testing/rspec.rb +19 -7
  64. data/lib/mack/utils/mime_types.yml +1 -0
  65. data/lib/mack/utils/server.rb +1 -1
  66. data/lib/mack/version.rb +3 -0
  67. data/lib/mack/view_helpers/date_time_helpers.rb +106 -0
  68. data/lib/mack/view_helpers/form_helpers.rb +282 -0
  69. data/lib/mack/view_helpers/html_helpers.rb +4 -88
  70. data/lib/mack/view_helpers/link_helpers.rb +171 -0
  71. data/lib/mack/view_helpers/object_helpers.rb +14 -0
  72. data/lib/mack/view_helpers/string_helpers.rb +41 -0
  73. data/lib/mack_app.rb +1 -0
  74. data/lib/mack_core.rb +3 -7
  75. data/lib/mack_tasks.rb +8 -16
  76. metadata +36 -27
  77. data/bin/mack_ring_server +0 -33
  78. data/lib/mack/core_extensions/string.rb +0 -21
  79. data/lib/mack/distributed/errors/errors.rb +0 -27
  80. data/lib/mack/distributed/routing/urls.rb +0 -44
  81. data/lib/mack/distributed/utils/rinda.rb +0 -45
  82. data/lib/mack/initialization/orm_support.rb +0 -20
  83. data/lib/mack/tasks/mack_ring_server_tasks.rake +0 -33
  84. data/lib/mack/utils/crypt/default_worker.rb +0 -28
  85. 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.request_method.upcase}] '#{request.path_info}'\n"
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[app_config.mack.session_id]
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
- Cachetastic::Caches::MackSessionCache.set(sess_id, request.session) if app_config.mack.use_sessions
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
- fcs = app_config.instance.instance_variable_get("@final_configuration_settings")
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
- exec "irb #{libs.join(" ")} --simple-prompt"
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
@@ -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
- build_response(request.put(uri, build_request_options({:input => options.to_params})))
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
- build_response(request.post(uri, build_request_options({:input => options.to_params})))
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(cookies[app_config.mack.session_id]) do
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
@@ -7,15 +7,27 @@ module Spec # :nodoc:
7
7
  include Mack::Routes::Urls
8
8
  include Mack::Testing::Helpers
9
9
 
10
- alias_method :mack_spec_execute, :execute
10
+ alias_method :mack_run_with_description_capturing, :run_with_description_capturing
11
11
 
12
- def execute(options, instance_variables)
13
- in_session do
14
- @__res = mack_spec_execute(options, instance_variables)
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
- end
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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Mack
2
+ VERSION = '0.7.0' unless const_defined?("VERSION")
3
+ end
@@ -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