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.
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