actionpack 3.2.0.rc1 → 3.2.0.rc2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

@@ -1,5 +1,11 @@
1
1
  ## Rails 3.2.0 (unreleased) ##
2
2
 
3
+ * Add font_path helper method *Santiago Pastorino*
4
+
5
+ * Depends on rack ~> 1.4.0 *Santiago Pastorino*
6
+
7
+ * Add :gzip option to `caches_page`. The default option can be configured globally using `page_cache_compression` *Andrey Sitnik*
8
+
3
9
  * The ShowExceptions middleware now accepts a exceptions application that is responsible to render an exception when the application fails. The application is invoked with a copy of the exception in `env["action_dispatch.exception"]` and with the PATH_INFO rewritten to the status code. *José Valim*
4
10
 
5
11
  * Add `button_tag` support to ActionView::Helpers::FormBuilder.
@@ -241,6 +241,7 @@ module AbstractController
241
241
  # the template name
242
242
  # false:: There is no layout
243
243
  # true:: raise an ArgumentError
244
+ # nil:: Force default layout behavior with inheritance
244
245
  #
245
246
  # ==== Parameters
246
247
  # * <tt>layout</tt> - The layout to use.
@@ -254,7 +255,7 @@ module AbstractController
254
255
  conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
255
256
  self._layout_conditions = conditions
256
257
 
257
- @_layout = layout || false # Converts nil to false
258
+ @_layout = layout
258
259
  _write_layout_method
259
260
  end
260
261
 
@@ -35,7 +35,7 @@ module AbstractController
35
35
  include AbstractController::ViewPaths
36
36
 
37
37
  included do
38
- config_accessor :protected_instance_variables, :instance_reader => false
38
+ class_attribute :protected_instance_variables
39
39
  self.protected_instance_variables = []
40
40
  end
41
41
 
@@ -59,12 +59,6 @@ module AbstractController
59
59
 
60
60
  attr_internal_writer :view_context_class
61
61
 
62
- # Explicitly define protected_instance_variables so it can be
63
- # inherited and overwritten by other modules if needed.
64
- def protected_instance_variables
65
- config.protected_instance_variables
66
- end
67
-
68
62
  def view_context_class
69
63
  @_view_context_class ||= self.class.view_context_class
70
64
  end
@@ -16,7 +16,7 @@ module ActionController #:nodoc:
16
16
  # caches_page :show, :new
17
17
  # end
18
18
  #
19
- # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, which match the URLs used
19
+ # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, which match the URLs used
20
20
  # that would normally trigger dynamic page generation. Page caching works by configuring a web server to first check for the
21
21
  # existence of files on disk, and to serve them directly when found, without passing the request through to Action Pack.
22
22
  # This is much faster than handling the full dynamic request in the usual way.
@@ -38,23 +38,25 @@ module ActionController #:nodoc:
38
38
  extend ActiveSupport::Concern
39
39
 
40
40
  included do
41
- ##
42
- # :singleton-method:
43
41
  # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
44
42
  # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>Rails.root + "/public"</tt>). Changing
45
43
  # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
46
44
  # web server to look in the new location for cached files.
47
- config_accessor :page_cache_directory
45
+ class_attribute :page_cache_directory
48
46
  self.page_cache_directory ||= ''
49
47
 
50
- ##
51
- # :singleton-method:
52
48
  # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
53
49
  # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
54
50
  # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
55
51
  # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
56
- config_accessor :page_cache_extension
52
+ class_attribute :page_cache_extension
57
53
  self.page_cache_extension ||= '.html'
54
+
55
+ # The compression used for gzip. If false (default), the page is not compressed.
56
+ # If can be a symbol showing the ZLib compression method, for example, :best_compression
57
+ # or :best_speed or an integer configuring the compression level.
58
+ class_attribute :page_cache_compression
59
+ self.page_cache_compression ||= false
58
60
  end
59
61
 
60
62
  module ClassMethods
@@ -66,24 +68,31 @@ module ActionController #:nodoc:
66
68
 
67
69
  instrument_page_cache :expire_page, path do
68
70
  File.delete(path) if File.exist?(path)
71
+ File.delete(path + '.gz') if File.exist?(path + '.gz')
69
72
  end
70
73
  end
71
74
 
72
75
  # Manually cache the +content+ in the key determined by +path+. Example:
73
76
  # cache_page "I'm the cached content", "/lists/show"
74
- def cache_page(content, path, extension = nil)
77
+ def cache_page(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION)
75
78
  return unless perform_caching
76
79
  path = page_cache_path(path, extension)
77
80
 
78
81
  instrument_page_cache :write_page, path do
79
82
  FileUtils.makedirs(File.dirname(path))
80
83
  File.open(path, "wb+") { |f| f.write(content) }
84
+ if gzip
85
+ Zlib::GzipWriter.open(path + '.gz', gzip) { |f| f.write(content) }
86
+ end
81
87
  end
82
88
  end
83
89
 
84
- # Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that
90
+ # Caches the +actions+ using the page-caching approach that'll store
91
+ # the cache in a path within the page_cache_directory that
85
92
  # matches the triggering url.
86
93
  #
94
+ # You can also pass a :gzip option to override the class configuration one.
95
+ #
87
96
  # Usage:
88
97
  #
89
98
  # # cache the index action
@@ -91,10 +100,28 @@ module ActionController #:nodoc:
91
100
  #
92
101
  # # cache the index action except for JSON requests
93
102
  # caches_page :index, :if => Proc.new { |c| !c.request.format.json? }
103
+ #
104
+ # # don't gzip images
105
+ # caches_page :image, :gzip => false
94
106
  def caches_page(*actions)
95
107
  return unless perform_caching
96
108
  options = actions.extract_options!
97
- after_filter({:only => actions}.merge(options)) { |c| c.cache_page }
109
+
110
+ gzip_level = options.fetch(:gzip, page_cache_compression)
111
+ gzip_level = case gzip_level
112
+ when Symbol
113
+ Zlib.const_get(gzip_level.to_s.upcase)
114
+ when Fixnum
115
+ gzip_level
116
+ when false
117
+ nil
118
+ else
119
+ Zlib::BEST_COMPRESSION
120
+ end
121
+
122
+ after_filter({:only => actions}.merge(options)) do |c|
123
+ c.cache_page(nil, nil, gzip_level)
124
+ end
98
125
  end
99
126
 
100
127
  private
@@ -136,7 +163,7 @@ module ActionController #:nodoc:
136
163
  # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used.
137
164
  # If no options are provided, the url of the current request being handled is used. Example:
138
165
  # cache_page "I'm the cached content", :controller => "lists", :action => "show"
139
- def cache_page(content = nil, options = nil)
166
+ def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION)
140
167
  return unless self.class.perform_caching && caching_allowed?
141
168
 
142
169
  path = case options
@@ -152,7 +179,7 @@ module ActionController #:nodoc:
152
179
  extension = ".#{type_symbol}"
153
180
  end
154
181
 
155
- self.class.cache_page(content || response.body, path, extension)
182
+ self.class.cache_page(content || response.body, path, extension, gzip)
156
183
  end
157
184
 
158
185
  end
@@ -56,7 +56,7 @@ module ActionController
56
56
  include AbstractController::Helpers
57
57
 
58
58
  included do
59
- config_accessor :helpers_path, :include_all_helpers
59
+ class_attribute :helpers_path, :include_all_helpers
60
60
  self.helpers_path ||= []
61
61
  self.include_all_helpers = true
62
62
  end
@@ -141,11 +141,11 @@ module ActionController
141
141
  end
142
142
 
143
143
  def decode_credentials(request)
144
- ActiveSupport::Base64.decode64(request.authorization.split(' ', 2).last || '')
144
+ ::Base64.decode64(request.authorization.split(' ', 2).last || '')
145
145
  end
146
146
 
147
147
  def encode_credentials(user_name, password)
148
- "Basic #{ActiveSupport::Base64.encode64s("#{user_name}:#{password}")}"
148
+ "Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
149
149
  end
150
150
 
151
151
  def authentication_request(controller, realm)
@@ -286,7 +286,7 @@ module ActionController
286
286
  t = time.to_i
287
287
  hashed = [t, secret_key]
288
288
  digest = ::Digest::MD5.hexdigest(hashed.join(":"))
289
- ActiveSupport::Base64.encode64("#{t}:#{digest}").gsub("\n", '')
289
+ ::Base64.encode64("#{t}:#{digest}").gsub("\n", '')
290
290
  end
291
291
 
292
292
  # Might want a shorter timeout depending on whether the request
@@ -295,7 +295,7 @@ module ActionController
295
295
  # allow a user to use new nonce without prompting user again for their
296
296
  # username and password.
297
297
  def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
298
- t = ActiveSupport::Base64.decode64(value).split(":").first.to_i
298
+ t = ::Base64.decode64(value).split(":").first.to_i
299
299
  nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
300
300
  end
301
301
 
@@ -16,7 +16,7 @@ module ActionDispatch
16
16
  end
17
17
  end
18
18
 
19
- attr_reader :app
19
+ attr_reader :app, :constraints
20
20
 
21
21
  def initialize(app, constraints, request)
22
22
  @app, @constraints, @request = app, constraints, request
@@ -1053,6 +1053,13 @@ module ActionDispatch
1053
1053
  #
1054
1054
  # The above example will now change /posts/new to /posts/brand_new
1055
1055
  #
1056
+ # [:path]
1057
+ # Allows you to change the path prefix for the resource.
1058
+ #
1059
+ # resources :posts, :path => 'postings'
1060
+ #
1061
+ # The resource and all segments will now route to /postings instead of /posts
1062
+ #
1056
1063
  # [:only]
1057
1064
  # Only generate routes for the given actions.
1058
1065
  #
@@ -356,7 +356,7 @@ module ActionDispatch
356
356
  conditions = build_conditions(conditions, valid_conditions, path.names.map { |x| x.to_sym })
357
357
 
358
358
  route = @set.add_route(app, path, conditions, defaults, name)
359
- named_routes[name] = route if name && !named_routes[name]
359
+ named_routes[name] = route if name
360
360
  route
361
361
  end
362
362
 
@@ -16,6 +16,10 @@ module ActionDispatch
16
16
  end
17
17
  end
18
18
 
19
+ def respond_to?(method, include_private = false)
20
+ super || routes.url_helpers.respond_to?(method)
21
+ end
22
+
19
23
  def method_missing(method, *args)
20
24
  if routes.url_helpers.respond_to?(method)
21
25
  self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -3,7 +3,7 @@ module ActionPack
3
3
  MAJOR = 3
4
4
  MINOR = 2
5
5
  TINY = 0
6
- PRE = "rc1"
6
+ PRE = "rc2"
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
9
  end
@@ -1,4 +1,3 @@
1
- require 'action_view/helpers/form_helper'
2
1
  require 'active_support/core_ext/class/attribute_accessors'
3
2
  require 'active_support/core_ext/enumerable'
4
3
  require 'active_support/core_ext/object/blank'
@@ -47,9 +46,5 @@ module ActionView
47
46
  options['type'] != 'hidden'
48
47
  end
49
48
  end
50
-
51
- class InstanceTag
52
- include ActiveModelInstanceTag
53
- end
54
49
  end
55
50
  end
@@ -306,6 +306,20 @@ module ActionView
306
306
  end
307
307
  alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
308
308
 
309
+ # Computes the path to a font asset in the public fonts directory.
310
+ # Full paths from the document root will be passed through.
311
+ #
312
+ # ==== Examples
313
+ # font_path("font") # => /fonts/font
314
+ # font_path("font.ttf") # => /fonts/font.ttf
315
+ # font_path("dir/font.ttf") # => /fonts/dir/font.ttf
316
+ # font_path("/dir/font.ttf") # => /dir/font.ttf
317
+ # font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
318
+ def font_path(source)
319
+ asset_paths.compute_public_path(source, 'fonts')
320
+ end
321
+ alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
322
+
309
323
  # Returns an html image tag for the +source+. The +source+ can be a full
310
324
  # path or a file that exists in your public images directory.
311
325
  #
@@ -422,7 +422,7 @@ module ActionView
422
422
  end
423
423
 
424
424
  # Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
425
- # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
425
+ # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
426
426
  # Override the field name using the <tt>:field_name</tt> option, 'second' by default.
427
427
  #
428
428
  # ==== Examples
@@ -448,7 +448,7 @@ module ActionView
448
448
 
449
449
  # Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
450
450
  # Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
451
- # selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
451
+ # selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
452
452
  # Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
453
453
  #
454
454
  # ==== Examples
@@ -473,7 +473,7 @@ module ActionView
473
473
  end
474
474
 
475
475
  # Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
476
- # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
476
+ # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
477
477
  # Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
478
478
  #
479
479
  # ==== Examples
@@ -868,7 +868,7 @@ module ActionView
868
868
  tag_options = { :value => value }
869
869
  tag_options[:selected] = "selected" if selected == i
870
870
  text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
871
- text = options[:ampm] ? AMPM_TRANSLATION[i] : text
871
+ text = options[:ampm] ? AMPM_TRANSLATION[i] : text
872
872
  select_options << content_tag(:option, text, tag_options)
873
873
  end
874
874
  (select_options.join("\n") + "\n").html_safe
@@ -974,7 +974,7 @@ module ActionView
974
974
  end
975
975
  end
976
976
 
977
- class InstanceTag #:nodoc:
977
+ module DateHelperInstanceTag
978
978
  def to_date_select_tag(options = {}, html_options = {})
979
979
  datetime_selector(options, html_options).select_date.html_safe
980
980
  end
@@ -1030,6 +1030,10 @@ module ActionView
1030
1030
  end
1031
1031
  end
1032
1032
 
1033
+ class InstanceTag #:nodoc:
1034
+ include DateHelperInstanceTag
1035
+ end
1036
+
1033
1037
  class FormBuilder
1034
1038
  def date_select(method, options = {}, html_options = {})
1035
1039
  @template.date_select(@object_name, method, objectify_options(options), html_options)
@@ -2,6 +2,7 @@ require 'cgi'
2
2
  require 'action_view/helpers/date_helper'
3
3
  require 'action_view/helpers/tag_helper'
4
4
  require 'action_view/helpers/form_tag_helper'
5
+ require 'action_view/helpers/active_model_helper'
5
6
  require 'active_support/core_ext/class/attribute'
6
7
  require 'active_support/core_ext/hash/slice'
7
8
  require 'active_support/core_ext/module/method_names'
@@ -963,7 +964,7 @@ module ActionView
963
964
  end
964
965
 
965
966
  class InstanceTag
966
- include Helpers::TagHelper, Helpers::FormTagHelper
967
+ include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper
967
968
 
968
969
  attr_reader :object, :method_name, :object_name
969
970
 
@@ -584,7 +584,7 @@ module ActionView
584
584
  # [nil, []]
585
585
  # { nil => [] }
586
586
  #
587
- if !choices.empty? && Array === choices.first.last
587
+ if !choices.empty? && choices.first.respond_to?(:last) && Array === choices.first.last
588
588
  option_tags = grouped_options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
589
589
  else
590
590
  option_tags = options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
@@ -47,7 +47,7 @@ module ActionView
47
47
  # <% end -%>
48
48
  # # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
49
49
  #
50
- # <%= form_tag('/posts', :remote => true) %>
50
+ # <%= form_tag('/posts', :remote => true) %>
51
51
  # # => <form action="/posts" method="post" data-remote="true">
52
52
  #
53
53
  # form_tag('http://far.away.com/form', :authenticity_token => false)
@@ -393,21 +393,17 @@ module ActionView
393
393
  # submit_tag "Save edits", :disabled => true
394
394
  # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
395
395
  #
396
- #
397
396
  # submit_tag "Complete sale", :disable_with => "Please wait..."
398
- # # => <input name="commit" data-disable-with="Please wait..."
399
- # # type="submit" value="Complete sale" />
397
+ # # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" />
400
398
  #
401
399
  # submit_tag nil, :class => "form_submit"
402
400
  # # => <input class="form_submit" name="commit" type="submit" />
403
401
  #
404
402
  # submit_tag "Edit", :disable_with => "Editing...", :class => "edit_button"
405
- # # => <input class="edit_button" data-disable_with="Editing..."
406
- # # name="commit" type="submit" value="Edit" />
403
+ # # => <input class="edit_button" data-disable_with="Editing..." name="commit" type="submit" value="Edit" />
407
404
  #
408
405
  # submit_tag "Save", :confirm => "Are you sure?"
409
- # # => <input name='commit' type='submit' value='Save'
410
- # data-confirm="Are you sure?" />
406
+ # # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
411
407
  #
412
408
  def submit_tag(value = "Save changes", options = {})
413
409
  options = options.stringify_keys
@@ -451,12 +447,11 @@ module ActionView
451
447
  # content_tag(:strong, 'Ask me!')
452
448
  # end
453
449
  # # => <button name="button" type="button">
454
- # <strong>Ask me!</strong>
455
- # </button>
450
+ # # <strong>Ask me!</strong>
451
+ # # </button>
456
452
  #
457
453
  # button_tag "Checkout", :disable_with => "Please wait..."
458
- # # => <button data-disable-with="Please wait..." name="button"
459
- # type="submit">Checkout</button>
454
+ # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
460
455
  #
461
456
  def button_tag(content_or_options = nil, options = nil, &block)
462
457
  options = content_or_options if block_given? && content_or_options.is_a?(Hash)
@@ -582,7 +577,7 @@ module ActionView
582
577
  #
583
578
  # ==== Examples
584
579
  # number_field_tag 'quantity', nil, :in => 1...10
585
- # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
580
+ # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
586
581
  def number_field_tag(name, value = nil, options = {})
587
582
  options = options.stringify_keys
588
583
  options["type"] ||= "number"
@@ -33,22 +33,29 @@ module ActionView
33
33
  # in the +options+ hash.
34
34
  #
35
35
  # ==== Options
36
- # * <tt>:area_code</tt> - Adds parentheses around the area code.
37
- # * <tt>:delimiter</tt> - Specifies the delimiter to use (defaults to "-").
38
- # * <tt>:extension</tt> - Specifies an extension to add to the end of the
39
- # generated number.
36
+ #
37
+ # * <tt>:area_code</tt> - Adds parentheses around the area code.
38
+ # * <tt>:delimiter</tt> - Specifies the delimiter to use (defaults to "-").
39
+ # * <tt>:extension</tt> - Specifies an extension to add to the end of the
40
+ # generated number.
40
41
  # * <tt>:country_code</tt> - Sets the country code for the phone number.
42
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
41
43
  #
42
44
  # ==== Examples
45
+ #
43
46
  # number_to_phone(5551234) # => 555-1234
47
+ # number_to_phone("5551234") # => 555-1234
44
48
  # number_to_phone(1235551234) # => 123-555-1234
45
49
  # number_to_phone(1235551234, :area_code => true) # => (123) 555-1234
46
50
  # number_to_phone(1235551234, :delimiter => " ") # => 123 555 1234
47
51
  # number_to_phone(1235551234, :area_code => true, :extension => 555) # => (123) 555-1234 x 555
48
52
  # number_to_phone(1235551234, :country_code => 1) # => +1-123-555-1234
53
+ # number_to_phone("123a456") # => 123a456
54
+ #
55
+ # number_to_phone("1234a567", :raise => true) # => InvalidNumberError
49
56
  #
50
57
  # number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".")
51
- # => +1.123.555.1234 x 1343
58
+ # # => +1.123.555.1234 x 1343
52
59
  def number_to_phone(number, options = {})
53
60
  return unless number
54
61
 
@@ -83,6 +90,7 @@ module ActionView
83
90
  # in the +options+ hash.
84
91
  #
85
92
  # ==== Options
93
+ #
86
94
  # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
87
95
  # * <tt>:precision</tt> - Sets the level of precision (defaults to 2).
88
96
  # * <tt>:unit</tt> - Sets the denomination of the currency (defaults to "$").
@@ -95,12 +103,17 @@ module ActionView
95
103
  # an hyphen to the formatted number given by <tt>:format</tt>).
96
104
  # Accepts the same fields than <tt>:format</tt>, except
97
105
  # <tt>%n</tt> is here the absolute value of the number.
106
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
98
107
  #
99
108
  # ==== Examples
109
+ #
100
110
  # number_to_currency(1234567890.50) # => $1,234,567,890.50
101
111
  # number_to_currency(1234567890.506) # => $1,234,567,890.51
102
112
  # number_to_currency(1234567890.506, :precision => 3) # => $1,234,567,890.506
103
113
  # number_to_currency(1234567890.506, :locale => :fr) # => 1 234 567 890,51 €
114
+ # number_to_currency("123a456") # => $123a456
115
+ #
116
+ # number_to_currency("123a456", :raise => true) # => InvalidNumberError
104
117
  #
105
118
  # number_to_currency(-1234567890.50, :negative_format => "(%u%n)")
106
119
  # # => ($1,234,567,890.50)
@@ -142,23 +155,33 @@ module ActionView
142
155
 
143
156
  end
144
157
 
145
- # Formats a +number+ as a percentage string (e.g., 65%). You can customize the
146
- # format in the +options+ hash.
158
+ # Formats a +number+ as a percentage string (e.g., 65%). You can customize the format in the +options+ hash.
147
159
  #
148
160
  # ==== Options
149
- # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
150
- # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
151
- # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+)
152
- # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
153
- # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
154
- # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +false+)
161
+ #
162
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current
163
+ # locale).
164
+ # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
165
+ # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+,
166
+ # the # of fractional digits (defaults to +false+).
167
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults
168
+ # to ".").
169
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
170
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator
171
+ # (defaults to +false+).
172
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
155
173
  #
156
174
  # ==== Examples
175
+ #
157
176
  # number_to_percentage(100) # => 100.000%
177
+ # number_to_percentage("98") # => 98.000%
158
178
  # number_to_percentage(100, :precision => 0) # => 100%
159
179
  # number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000%
160
180
  # number_to_percentage(302.24398923423, :precision => 5) # => 302.24399%
161
181
  # number_to_percentage(1000, :locale => :fr) # => 1 000,000%
182
+ # number_to_percentage("98a") # => 98a%
183
+ #
184
+ # number_to_percentage("98a", :raise => true) # => InvalidNumberError
162
185
  def number_to_percentage(number, options = {})
163
186
  return unless number
164
187
 
@@ -185,19 +208,26 @@ module ActionView
185
208
  # customize the format in the +options+ hash.
186
209
  #
187
210
  # ==== Options
211
+ #
188
212
  # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
189
213
  # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
190
214
  # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
215
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
191
216
  #
192
217
  # ==== Examples
218
+ #
193
219
  # number_with_delimiter(12345678) # => 12,345,678
220
+ # number_with_delimiter("123456") # => 123,456
194
221
  # number_with_delimiter(12345678.05) # => 12,345,678.05
195
222
  # number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
196
223
  # number_with_delimiter(12345678, :delimiter => ",") # => 12,345,678
197
224
  # number_with_delimiter(12345678.05, :separator => " ") # => 12,345,678 05
198
225
  # number_with_delimiter(12345678.05, :locale => :fr) # => 12 345 678,05
226
+ # number_with_delimiter("112a") # => 112a
199
227
  # number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
200
228
  # # => 98 765 432,98
229
+ #
230
+ # number_with_delimiter("112a", :raise => true) # => raise InvalidNumberError
201
231
  def number_with_delimiter(number, options = {})
202
232
  options.symbolize_keys!
203
233
 
@@ -225,12 +255,15 @@ module ActionView
225
255
  # You can customize the format in the +options+ hash.
226
256
  #
227
257
  # ==== Options
228
- # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
229
- # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
230
- # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+)
231
- # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
232
- # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
233
- # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +false+)
258
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
259
+ # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
260
+ # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+,
261
+ # the # of fractional digits (defaults to +false+).
262
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults
263
+ # to ".").
264
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
265
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator
266
+ # (defaults to +false+).
234
267
  #
235
268
  # ==== Examples
236
269
  # number_with_precision(111.2345) # => 111.235
@@ -241,8 +274,10 @@ module ActionView
241
274
  # number_with_precision(111.2345, :precision => 1, :significant => true) # => 100
242
275
  # number_with_precision(13, :precision => 5, :significant => true) # => 13.000
243
276
  # number_with_precision(111.234, :locale => :fr) # => 111,234
277
+ #
244
278
  # number_with_precision(13, :precision => 5, :significant => true, :strip_insignificant_zeros => true)
245
279
  # # => 13
280
+ #
246
281
  # number_with_precision(389.32314, :precision => 4, :significant => true) # => 389.3
247
282
  # number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
248
283
  # # => 1.111,23
@@ -26,6 +26,8 @@ module ActionView
26
26
  elsif options.key?(:template)
27
27
  options[:template].respond_to?(:render) ?
28
28
  options[:template] : find_template(options[:template], options[:prefixes], false, keys, @details)
29
+ else
30
+ raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file or :text option."
29
31
  end
30
32
  end
31
33
 
@@ -173,6 +173,50 @@ module ActionView
173
173
  @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier
174
174
  end
175
175
 
176
+ # This method is responsible for properly setting the encoding of the
177
+ # source. Until this point, we assume that the source is BINARY data.
178
+ # If no additional information is supplied, we assume the encoding is
179
+ # the same as <tt>Encoding.default_external</tt>.
180
+ #
181
+ # The user can also specify the encoding via a comment on the first
182
+ # line of the template (# encoding: NAME-OF-ENCODING). This will work
183
+ # with any template engine, as we process out the encoding comment
184
+ # before passing the source on to the template engine, leaving a
185
+ # blank line in its stead.
186
+ def encode!
187
+ return unless source.encoding_aware? && source.encoding == Encoding::BINARY
188
+
189
+ # Look for # encoding: *. If we find one, we'll encode the
190
+ # String in that encoding, otherwise, we'll use the
191
+ # default external encoding.
192
+ if source.sub!(/\A#{ENCODING_FLAG}/, '')
193
+ encoding = magic_encoding = $1
194
+ else
195
+ encoding = Encoding.default_external
196
+ end
197
+
198
+ # Tag the source with the default external encoding
199
+ # or the encoding specified in the file
200
+ source.force_encoding(encoding)
201
+
202
+ # If the user didn't specify an encoding, and the handler
203
+ # handles encodings, we simply pass the String as is to
204
+ # the handler (with the default_external tag)
205
+ if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
206
+ source
207
+ # Otherwise, if the String is valid in the encoding,
208
+ # encode immediately to default_internal. This means
209
+ # that if a handler doesn't handle encodings, it will
210
+ # always get Strings in the default_internal
211
+ elsif source.valid_encoding?
212
+ source.encode!
213
+ # Otherwise, since the String is invalid in the encoding
214
+ # specified, raise an exception
215
+ else
216
+ raise WrongEncodingError.new(source, encoding)
217
+ end
218
+ end
219
+
176
220
  protected
177
221
 
178
222
  # Compile a template. This method ensures a template is compiled
@@ -195,15 +239,7 @@ module ActionView
195
239
  end
196
240
 
197
241
  # Among other things, this method is responsible for properly setting
198
- # the encoding of the source. Until this point, we assume that the
199
- # source is BINARY data. If no additional information is supplied,
200
- # we assume the encoding is the same as <tt>Encoding.default_external</tt>.
201
- #
202
- # The user can also specify the encoding via a comment on the first
203
- # line of the template (# encoding: NAME-OF-ENCODING). This will work
204
- # with any template engine, as we process out the encoding comment
205
- # before passing the source on to the template engine, leaving a
206
- # blank line in its stead.
242
+ # the encoding of the compiled template.
207
243
  #
208
244
  # If the template engine handles encodings, we send the encoded
209
245
  # String to the engine without further processing. This allows
@@ -215,40 +251,8 @@ module ActionView
215
251
  # In general, this means that templates will be UTF-8 inside of Rails,
216
252
  # regardless of the original source encoding.
217
253
  def compile(view, mod) #:nodoc:
254
+ encode!
218
255
  method_name = self.method_name
219
-
220
- if source.encoding_aware?
221
- # Look for # encoding: *. If we find one, we'll encode the
222
- # String in that encoding, otherwise, we'll use the
223
- # default external encoding.
224
- if source.sub!(/\A#{ENCODING_FLAG}/, '')
225
- encoding = magic_encoding = $1
226
- else
227
- encoding = Encoding.default_external
228
- end
229
-
230
- # Tag the source with the default external encoding
231
- # or the encoding specified in the file
232
- source.force_encoding(encoding)
233
-
234
- # If the user didn't specify an encoding, and the handler
235
- # handles encodings, we simply pass the String as is to
236
- # the handler (with the default_external tag)
237
- if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
238
- source
239
- # Otherwise, if the String is valid in the encoding,
240
- # encode immediately to default_internal. This means
241
- # that if a handler doesn't handle encodings, it will
242
- # always get Strings in the default_internal
243
- elsif source.valid_encoding?
244
- source.encode!
245
- # Otherwise, since the String is invalid in the encoding
246
- # specified, raise an exception
247
- else
248
- raise WrongEncodingError.new(source, encoding)
249
- end
250
- end
251
-
252
256
  code = @handler.call(self)
253
257
 
254
258
  # Make sure that the resulting String to be evalled is in the
@@ -297,7 +301,11 @@ module ActionView
297
301
  raise e
298
302
  else
299
303
  assigns = view.respond_to?(:assigns) ? view.assigns : {}
300
- template = @virtual_path ? refresh(view) : self
304
+ template = self
305
+ unless template.source
306
+ template = refresh(view)
307
+ template.encode!
308
+ end
301
309
  raise Template::Error.new(template, assigns, e)
302
310
  end
303
311
  end
@@ -89,14 +89,10 @@ module ActionView
89
89
  line_counter = start_on_line
90
90
  return unless source_code = source_code[start_on_line..end_on_line]
91
91
 
92
- extract = source_code.sum do |line|
92
+ source_code.sum do |line|
93
93
  line_counter += 1
94
94
  "#{indent}#{line_counter}: #{line}\n"
95
95
  end
96
-
97
- extract.encode! if extract.respond_to?(:encode!)
98
-
99
- extract
100
96
  end
101
97
 
102
98
  def sub_template_of(template_path)
@@ -63,13 +63,18 @@ module Sprockets
63
63
  end
64
64
  alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
65
65
 
66
- def javascript_path(source)
66
+ def font_path(source)
67
67
  path_to_asset(source)
68
68
  end
69
+ alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
70
+
71
+ def javascript_path(source)
72
+ path_to_asset(source, :ext => 'js')
73
+ end
69
74
  alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with an javascript_path named route
70
75
 
71
76
  def stylesheet_path(source)
72
- path_to_asset(source)
77
+ path_to_asset(source, :ext => 'css')
73
78
  end
74
79
  alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with an stylesheet_path named route
75
80
 
@@ -114,11 +119,6 @@ module Sprockets
114
119
 
115
120
  class AssetNotPrecompiledError < StandardError; end
116
121
 
117
- # Return the filesystem path for the source
118
- def compute_source_path(source, ext)
119
- asset_for(source, ext)
120
- end
121
-
122
122
  def asset_for(source, ext)
123
123
  source = source.to_s
124
124
  return nil if is_uri?(source)
metadata CHANGED
@@ -1,144 +1,195 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: actionpack
3
- version: !ruby/object:Gem::Version
4
- version: 3.2.0.rc1
5
- prerelease: 6
3
+ version: !ruby/object:Gem::Version
4
+ hash: 977940591
5
+ prerelease: true
6
+ segments:
7
+ - 3
8
+ - 2
9
+ - 0
10
+ - rc2
11
+ version: 3.2.0.rc2
6
12
  platform: ruby
7
- authors:
13
+ authors:
8
14
  - David Heinemeier Hansson
9
15
  autorequire:
10
16
  bindir: bin
11
17
  cert_chain: []
12
- date: 2011-12-20 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
18
+
19
+ date: 2012-01-04 00:00:00 -02:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
15
23
  name: activesupport
16
- requirement: &2156023280 !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - =
20
- - !ruby/object:Gem::Version
21
- version: 3.2.0.rc1
22
- type: :runtime
23
24
  prerelease: false
24
- version_requirements: *2156023280
25
- - !ruby/object:Gem::Dependency
26
- name: activemodel
27
- requirement: &2156021980 !ruby/object:Gem::Requirement
25
+ requirement: &id001 !ruby/object:Gem::Requirement
28
26
  none: false
29
- requirements:
30
- - - =
31
- - !ruby/object:Gem::Version
32
- version: 3.2.0.rc1
27
+ requirements:
28
+ - - "="
29
+ - !ruby/object:Gem::Version
30
+ hash: 977940591
31
+ segments:
32
+ - 3
33
+ - 2
34
+ - 0
35
+ - rc2
36
+ version: 3.2.0.rc2
33
37
  type: :runtime
38
+ version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: activemodel
34
41
  prerelease: false
35
- version_requirements: *2156021980
36
- - !ruby/object:Gem::Dependency
37
- name: rack-cache
38
- requirement: &2156037660 !ruby/object:Gem::Requirement
42
+ requirement: &id002 !ruby/object:Gem::Requirement
39
43
  none: false
40
- requirements:
41
- - - ~>
42
- - !ruby/object:Gem::Version
43
- version: '1.1'
44
+ requirements:
45
+ - - "="
46
+ - !ruby/object:Gem::Version
47
+ hash: 977940591
48
+ segments:
49
+ - 3
50
+ - 2
51
+ - 0
52
+ - rc2
53
+ version: 3.2.0.rc2
44
54
  type: :runtime
55
+ version_requirements: *id002
56
+ - !ruby/object:Gem::Dependency
57
+ name: rack-cache
45
58
  prerelease: false
46
- version_requirements: *2156037660
47
- - !ruby/object:Gem::Dependency
48
- name: builder
49
- requirement: &2156037100 !ruby/object:Gem::Requirement
59
+ requirement: &id003 !ruby/object:Gem::Requirement
50
60
  none: false
51
- requirements:
61
+ requirements:
52
62
  - - ~>
53
- - !ruby/object:Gem::Version
54
- version: 3.0.0
63
+ - !ruby/object:Gem::Version
64
+ hash: 13
65
+ segments:
66
+ - 1
67
+ - 1
68
+ version: "1.1"
55
69
  type: :runtime
70
+ version_requirements: *id003
71
+ - !ruby/object:Gem::Dependency
72
+ name: builder
56
73
  prerelease: false
57
- version_requirements: *2156037100
58
- - !ruby/object:Gem::Dependency
59
- name: i18n
60
- requirement: &2156036440 !ruby/object:Gem::Requirement
74
+ requirement: &id004 !ruby/object:Gem::Requirement
61
75
  none: false
62
- requirements:
76
+ requirements:
63
77
  - - ~>
64
- - !ruby/object:Gem::Version
65
- version: '0.6'
78
+ - !ruby/object:Gem::Version
79
+ hash: 7
80
+ segments:
81
+ - 3
82
+ - 0
83
+ - 0
84
+ version: 3.0.0
66
85
  type: :runtime
67
- prerelease: false
68
- version_requirements: *2156036440
69
- - !ruby/object:Gem::Dependency
86
+ version_requirements: *id004
87
+ - !ruby/object:Gem::Dependency
70
88
  name: rack
71
- requirement: &2156035940 !ruby/object:Gem::Requirement
89
+ prerelease: false
90
+ requirement: &id005 !ruby/object:Gem::Requirement
72
91
  none: false
73
- requirements:
92
+ requirements:
74
93
  - - ~>
75
- - !ruby/object:Gem::Version
76
- version: 1.3.5
94
+ - !ruby/object:Gem::Version
95
+ hash: 7
96
+ segments:
97
+ - 1
98
+ - 4
99
+ - 0
100
+ version: 1.4.0
77
101
  type: :runtime
78
- prerelease: false
79
- version_requirements: *2156035940
80
- - !ruby/object:Gem::Dependency
102
+ version_requirements: *id005
103
+ - !ruby/object:Gem::Dependency
81
104
  name: rack-test
82
- requirement: &2156035260 !ruby/object:Gem::Requirement
105
+ prerelease: false
106
+ requirement: &id006 !ruby/object:Gem::Requirement
83
107
  none: false
84
- requirements:
108
+ requirements:
85
109
  - - ~>
86
- - !ruby/object:Gem::Version
110
+ - !ruby/object:Gem::Version
111
+ hash: 5
112
+ segments:
113
+ - 0
114
+ - 6
115
+ - 1
87
116
  version: 0.6.1
88
117
  type: :runtime
89
- prerelease: false
90
- version_requirements: *2156035260
91
- - !ruby/object:Gem::Dependency
118
+ version_requirements: *id006
119
+ - !ruby/object:Gem::Dependency
92
120
  name: journey
93
- requirement: &2156034700 !ruby/object:Gem::Requirement
121
+ prerelease: false
122
+ requirement: &id007 !ruby/object:Gem::Requirement
94
123
  none: false
95
- requirements:
124
+ requirements:
96
125
  - - ~>
97
- - !ruby/object:Gem::Version
126
+ - !ruby/object:Gem::Version
127
+ hash: 977940574
128
+ segments:
129
+ - 1
130
+ - 0
131
+ - 0
132
+ - rc1
98
133
  version: 1.0.0.rc1
99
134
  type: :runtime
100
- prerelease: false
101
- version_requirements: *2156034700
102
- - !ruby/object:Gem::Dependency
135
+ version_requirements: *id007
136
+ - !ruby/object:Gem::Dependency
103
137
  name: sprockets
104
- requirement: &2156034160 !ruby/object:Gem::Requirement
138
+ prerelease: false
139
+ requirement: &id008 !ruby/object:Gem::Requirement
105
140
  none: false
106
- requirements:
141
+ requirements:
107
142
  - - ~>
108
- - !ruby/object:Gem::Version
143
+ - !ruby/object:Gem::Version
144
+ hash: 15
145
+ segments:
146
+ - 2
147
+ - 1
148
+ - 2
109
149
  version: 2.1.2
110
150
  type: :runtime
111
- prerelease: false
112
- version_requirements: *2156034160
113
- - !ruby/object:Gem::Dependency
151
+ version_requirements: *id008
152
+ - !ruby/object:Gem::Dependency
114
153
  name: erubis
115
- requirement: &2156033600 !ruby/object:Gem::Requirement
154
+ prerelease: false
155
+ requirement: &id009 !ruby/object:Gem::Requirement
116
156
  none: false
117
- requirements:
157
+ requirements:
118
158
  - - ~>
119
- - !ruby/object:Gem::Version
159
+ - !ruby/object:Gem::Version
160
+ hash: 19
161
+ segments:
162
+ - 2
163
+ - 7
164
+ - 0
120
165
  version: 2.7.0
121
166
  type: :runtime
122
- prerelease: false
123
- version_requirements: *2156033600
124
- - !ruby/object:Gem::Dependency
167
+ version_requirements: *id009
168
+ - !ruby/object:Gem::Dependency
125
169
  name: tzinfo
126
- requirement: &2156032980 !ruby/object:Gem::Requirement
170
+ prerelease: false
171
+ requirement: &id010 !ruby/object:Gem::Requirement
127
172
  none: false
128
- requirements:
173
+ requirements:
129
174
  - - ~>
130
- - !ruby/object:Gem::Version
175
+ - !ruby/object:Gem::Version
176
+ hash: 41
177
+ segments:
178
+ - 0
179
+ - 3
180
+ - 29
131
181
  version: 0.3.29
132
182
  type: :development
133
- prerelease: false
134
- version_requirements: *2156032980
135
- description: Web apps on Rails. Simple, battle-tested conventions for building and
136
- testing MVC web applications. Works with any Rack-compatible server.
183
+ version_requirements: *id010
184
+ description: Web apps on Rails. Simple, battle-tested conventions for building and testing MVC web applications. Works with any Rack-compatible server.
137
185
  email: david@loudthinking.com
138
186
  executables: []
187
+
139
188
  extensions: []
189
+
140
190
  extra_rdoc_files: []
141
- files:
191
+
192
+ files:
142
193
  - CHANGELOG.md
143
194
  - README.rdoc
144
195
  - MIT-LICENSE
@@ -329,29 +380,43 @@ files:
329
380
  - lib/sprockets/helpers.rb
330
381
  - lib/sprockets/railtie.rb
331
382
  - lib/sprockets/static_compiler.rb
383
+ has_rdoc: true
332
384
  homepage: http://www.rubyonrails.org
333
385
  licenses: []
386
+
334
387
  post_install_message:
335
388
  rdoc_options: []
336
- require_paths:
389
+
390
+ require_paths:
337
391
  - lib
338
- required_ruby_version: !ruby/object:Gem::Requirement
392
+ required_ruby_version: !ruby/object:Gem::Requirement
339
393
  none: false
340
- requirements:
341
- - - ! '>='
342
- - !ruby/object:Gem::Version
394
+ requirements:
395
+ - - ">="
396
+ - !ruby/object:Gem::Version
397
+ hash: 57
398
+ segments:
399
+ - 1
400
+ - 8
401
+ - 7
343
402
  version: 1.8.7
344
- required_rubygems_version: !ruby/object:Gem::Requirement
403
+ required_rubygems_version: !ruby/object:Gem::Requirement
345
404
  none: false
346
- requirements:
347
- - - ! '>'
348
- - !ruby/object:Gem::Version
405
+ requirements:
406
+ - - ">"
407
+ - !ruby/object:Gem::Version
408
+ hash: 25
409
+ segments:
410
+ - 1
411
+ - 3
412
+ - 1
349
413
  version: 1.3.1
350
- requirements:
414
+ requirements:
351
415
  - none
352
416
  rubyforge_project:
353
- rubygems_version: 1.8.7
417
+ rubygems_version: 1.3.7
354
418
  signing_key:
355
419
  specification_version: 3
356
420
  summary: Web-flow and rendering framework putting the VC in MVC (part of Rails).
357
421
  test_files: []
422
+