From e0513e33c4da60255e7c1aa71babcc9414f26858 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 28 Jul 2008 13:38:20 -0500 Subject: Routing whitespace cleanup --- .../assertions/routing_assertions.rb | 44 ++++++++-------- actionpack/lib/action_controller/routing.rb | 58 +++++++++++----------- .../lib/action_controller/routing/optimisations.rb | 22 ++++---- .../routing/recognition_optimisation.rb | 2 - actionpack/lib/action_controller/routing/route.rb | 15 +++--- .../lib/action_controller/routing/route_set.rb | 4 +- .../lib/action_controller/routing/routing_ext.rb | 1 - .../lib/action_controller/routing/segments.rb | 5 +- actionpack/test/controller/routing_test.rb | 1 + actionpack/test/controller/test_test.rb | 21 ++++---- 10 files changed, 84 insertions(+), 89 deletions(-) diff --git a/actionpack/lib/action_controller/assertions/routing_assertions.rb b/actionpack/lib/action_controller/assertions/routing_assertions.rb index 491b72d586..312b4e228b 100644 --- a/actionpack/lib/action_controller/assertions/routing_assertions.rb +++ b/actionpack/lib/action_controller/assertions/routing_assertions.rb @@ -2,7 +2,7 @@ module ActionController module Assertions # Suite of assertions to test routes generated by Rails and the handling of requests made to them. module RoutingAssertions - # Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash) + # Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash) # match +path+. Basically, it asserts that Rails recognizes the route given by +expected_options+. # # Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes @@ -14,16 +14,16 @@ module ActionController # # You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used # to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the - # extras argument, appending the query string on the path directly will not work. For example: + # extras argument, appending the query string on the path directly will not work. For example: # # # assert that a path of '/items/list/1?view=print' returns the correct options - # assert_recognizes({:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" }) + # assert_recognizes({:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" }) # - # The +message+ parameter allows you to pass in an error message that is displayed upon failure. + # The +message+ parameter allows you to pass in an error message that is displayed upon failure. # # ==== Examples # # Check the default route (i.e., the index action) - # assert_recognizes({:controller => 'items', :action => 'index'}, 'items') + # assert_recognizes({:controller => 'items', :action => 'index'}, 'items') # # # Test a specific action # assert_recognizes({:controller => 'items', :action => 'list'}, 'items/list') @@ -44,16 +44,16 @@ module ActionController request_method = nil end - clean_backtrace do - ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? + clean_backtrace do + ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? request = recognized_request_for(path, request_method) - + expected_options = expected_options.clone extras.each_key { |key| expected_options.delete key } unless extras.nil? - + expected_options.stringify_keys! routing_diff = expected_options.diff(request.path_parameters) - msg = build_message(message, "The recognized options did not match , difference: ", + msg = build_message(message, "The recognized options did not match , difference: ", request.path_parameters, expected_options, expected_options.diff(request.path_parameters)) assert_block(msg) { request.path_parameters == expected_options } end @@ -64,7 +64,7 @@ module ActionController # a query string. The +message+ parameter allows you to specify a custom error message for assertion failures. # # The +defaults+ parameter is unused. - # + # # ==== Examples # # Asserts that the default action is generated for a route with no action # assert_generates("/items", :controller => "items", :action => "index") @@ -73,34 +73,34 @@ module ActionController # assert_generates("/items/list", :controller => "items", :action => "list") # # # Tests the generation of a route with a parameter - # assert_generates("/items/list/1", { :controller => "items", :action => "list", :id => "1" }) + # assert_generates("/items/list/1", { :controller => "items", :action => "list", :id => "1" }) # # # Asserts that the generated route gives us our custom route # assert_generates "changesets/12", { :controller => 'scm', :action => 'show_diff', :revision => "12" } def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil) - clean_backtrace do + clean_backtrace do expected_path = "/#{expected_path}" unless expected_path[0] == ?/ # Load routes.rb if it hasn't been loaded. - ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? - + ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? + generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults) found_extras = options.reject {|k, v| ! extra_keys.include? k} msg = build_message(message, "found extras , not ", found_extras, extras) assert_block(msg) { found_extras == extras } - - msg = build_message(message, "The generated path did not match ", generated_path, + + msg = build_message(message, "The generated path did not match ", generated_path, expected_path) assert_block(msg) { expected_path == generated_path } end end - # Asserts that path and options match both ways; in other words, it verifies that path generates + # Asserts that path and options match both ways; in other words, it verifies that path generates # options and then that options generates path. This essentially combines +assert_recognizes+ # and +assert_generates+ into one step. # # The +extras+ hash allows you to specify options that would normally be provided as a query string to the action. The - # +message+ parameter allows you to specify a custom error message to display upon failure. + # +message+ parameter allows you to specify a custom error message to display upon failure. # # ==== Examples # # Assert a basic route: a controller with the default action (index) @@ -119,12 +119,12 @@ module ActionController # assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" }) def assert_routing(path, options, defaults={}, extras={}, message=nil) assert_recognizes(options, path, extras, message) - - controller, default_controller = options[:controller], defaults[:controller] + + controller, default_controller = options[:controller], defaults[:controller] if controller && controller.include?(?/) && default_controller && default_controller.include?(?/) options[:controller] = "/#{controller}" end - + assert_generates(path.is_a?(Hash) ? path[:path] : path, options, defaults, extras, message) end diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index dfbaa53b7c..8d51e823a6 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -201,7 +201,7 @@ module ActionController # With conditions you can define restrictions on routes. Currently the only valid condition is :method. # # * :method - Allows you to specify which method can access the route. Possible values are :post, - # :get, :put, :delete and :any. The default value is :any, + # :get, :put, :delete and :any. The default value is :any, # :any means that any method can access the route. # # Example: @@ -213,7 +213,7 @@ module ActionController # # Now, if you POST to /posts/:id, it will route to the create_comment action. A GET on the same # URL will route to the show action. - # + # # == Reloading routes # # You can reload routes if you feel you must: @@ -281,9 +281,9 @@ module ActionController end class << self - # Expects an array of controller names as the first argument. - # Executes the passed block with only the named controllers named available. - # This method is used in internal Rails testing. + # Expects an array of controller names as the first argument. + # Executes the passed block with only the named controllers named available. + # This method is used in internal Rails testing. def with_controllers(names) prior_controllers = @possible_controllers use_controllers! names @@ -292,10 +292,10 @@ module ActionController use_controllers! prior_controllers end - # Returns an array of paths, cleaned of double-slashes and relative path references. - # * "\\\" and "//" become "\\" or "/". - # * "/foo/bar/../config" becomes "/foo/config". - # The returned array is sorted by length, descending. + # Returns an array of paths, cleaned of double-slashes and relative path references. + # * "\\\" and "//" become "\\" or "/". + # * "/foo/bar/../config" becomes "/foo/config". + # The returned array is sorted by length, descending. def normalize_paths(paths) # do the hokey-pokey of path normalization... paths = paths.collect do |path| @@ -314,7 +314,7 @@ module ActionController paths = paths.uniq.sort_by { |path| - path.length } end - # Returns the array of controller names currently available to ActionController::Routing. + # Returns the array of controller names currently available to ActionController::Routing. def possible_controllers unless @possible_controllers @possible_controllers = [] @@ -339,28 +339,27 @@ module ActionController @possible_controllers end - # Replaces the internal list of controllers available to ActionController::Routing with the passed argument. - # ActionController::Routing.use_controllers!([ "posts", "comments", "admin/comments" ]) + # Replaces the internal list of controllers available to ActionController::Routing with the passed argument. + # ActionController::Routing.use_controllers!([ "posts", "comments", "admin/comments" ]) def use_controllers!(controller_names) @possible_controllers = controller_names end - # Returns a controller path for a new +controller+ based on a +previous+ controller path. - # Handles 4 scenarios: - # - # * stay in the previous controller: - # controller_relative_to( nil, "groups/discussion" ) # => "groups/discussion" - # - # * stay in the previous namespace: - # controller_relative_to( "posts", "groups/discussion" ) # => "groups/posts" - # - # * forced move to the root namespace: - # controller_relative_to( "/posts", "groups/discussion" ) # => "posts" - # - # * previous namespace is root: - # controller_relative_to( "posts", "anything_with_no_slashes" ) # =>"posts" - # - + # Returns a controller path for a new +controller+ based on a +previous+ controller path. + # Handles 4 scenarios: + # + # * stay in the previous controller: + # controller_relative_to( nil, "groups/discussion" ) # => "groups/discussion" + # + # * stay in the previous namespace: + # controller_relative_to( "posts", "groups/discussion" ) # => "groups/posts" + # + # * forced move to the root namespace: + # controller_relative_to( "/posts", "groups/discussion" ) # => "posts" + # + # * previous namespace is root: + # controller_relative_to( "posts", "anything_with_no_slashes" ) # =>"posts" + # def controller_relative_to(controller, previous) if controller.nil? then previous elsif controller[0] == ?/ then controller[1..-1] @@ -369,12 +368,11 @@ module ActionController end end end - Routes = RouteSet.new ActiveSupport::Inflector.module_eval do - # Ensures that routes are reloaded when Rails inflections are updated. + # Ensures that routes are reloaded when Rails inflections are updated. def inflections_with_route_reloading(&block) returning(inflections_without_route_reloading(&block)) { ActionController::Routing::Routes.reload! if block_given? diff --git a/actionpack/lib/action_controller/routing/optimisations.rb b/actionpack/lib/action_controller/routing/optimisations.rb index 4b70ea13f2..b44ebd5ca2 100644 --- a/actionpack/lib/action_controller/routing/optimisations.rb +++ b/actionpack/lib/action_controller/routing/optimisations.rb @@ -1,14 +1,14 @@ module ActionController module Routing - # Much of the slow performance from routes comes from the + # Much of the slow performance from routes comes from the # complexity of expiry, :requirements matching, defaults providing - # and figuring out which url pattern to use. With named routes - # we can avoid the expense of finding the right route. So if + # and figuring out which url pattern to use. With named routes + # we can avoid the expense of finding the right route. So if # they've provided the right number of arguments, and have no # :requirements, we can just build up a string and return it. - # - # To support building optimisations for other common cases, the - # generation code is separated into several classes + # + # To support building optimisations for other common cases, the + # generation code is separated into several classes module Optimisation def generate_optimisation_block(route, kind) return "" unless route.optimise? @@ -53,12 +53,12 @@ module ActionController # map.person '/people/:id' # # If the user calls person_url(@person), we can simply - # return a string like "/people/#{@person.to_param}" + # return a string like "/people/#{@person.to_param}" # rather than triggering the expensive logic in +url_for+. class PositionalArguments < Optimiser def guard_condition number_of_arguments = route.segment_keys.size - # if they're using foo_url(:id=>2) it's one + # if they're using foo_url(:id=>2) it's one # argument, but we don't want to generate /foos/id2 if number_of_arguments == 1 "(!defined?(default_url_options) || default_url_options.blank?) && defined?(request) && request && args.size == 1 && !args.first.is_a?(Hash)" @@ -94,14 +94,14 @@ module ActionController end # This case is mostly the same as the positional arguments case - # above, but it supports additional query parameters as the last + # above, but it supports additional query parameters as the last # argument class PositionalArgumentsWithAdditionalParams < PositionalArguments def guard_condition "(!defined?(default_url_options) || default_url_options.blank?) && defined?(request) && request && args.size == #{route.segment_keys.size + 1} && !args.last.has_key?(:anchor) && !args.last.has_key?(:port) && !args.last.has_key?(:host)" end - # This case uses almost the same code as positional arguments, + # This case uses almost the same code as positional arguments, # but add an args.last.to_query on the end def generation_code super.insert(-2, '?#{args.last.to_query}') @@ -110,7 +110,7 @@ module ActionController # To avoid generating "http://localhost/?host=foo.example.com" we # can't use this optimisation on routes without any segments def applicable? - super && route.segment_keys.size > 0 + super && route.segment_keys.size > 0 end end diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb index cf8f5232c1..67d354a943 100644 --- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb +++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb @@ -51,7 +51,6 @@ module ActionController # 3) segm test for /users/:id # (jump to list index = 5) # 4) full test for /users/:id => here we are! - class RouteSet def recognize_path(path, environment={}) result = recognize_optimized(path, environment) and return result @@ -152,7 +151,6 @@ module ActionController segments << nil segments end - end end end diff --git a/actionpack/lib/action_controller/routing/route.rb b/actionpack/lib/action_controller/routing/route.rb index a0d108ba03..a05c8c10ac 100644 --- a/actionpack/lib/action_controller/routing/route.rb +++ b/actionpack/lib/action_controller/routing/route.rb @@ -226,15 +226,14 @@ module ActionController end end - protected - def requirement_for(key) - return requirements[key] if requirements.key? key - segments.each do |segment| - return segment.regexp if segment.respond_to?(:key) && segment.key == key + protected + def requirement_for(key) + return requirements[key] if requirements.key? key + segments.each do |segment| + return segment.regexp if segment.respond_to?(:key) && segment.key == key + end + nil end - nil - end - end end end diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 5bc13cf268..4a9bc259f3 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -1,6 +1,6 @@ module ActionController module Routing - class RouteSet #:nodoc: + class RouteSet #:nodoc: # Mapper instances are used to build routes. The object passed to the draw # block in config/routes.rb is a Mapper instance. # @@ -432,4 +432,4 @@ module ActionController end end end -end \ No newline at end of file +end diff --git a/actionpack/lib/action_controller/routing/routing_ext.rb b/actionpack/lib/action_controller/routing/routing_ext.rb index 2ad20ee699..5f4ba90d0c 100644 --- a/actionpack/lib/action_controller/routing/routing_ext.rb +++ b/actionpack/lib/action_controller/routing/routing_ext.rb @@ -1,4 +1,3 @@ - class Object def to_param to_s diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb index f0ad066bad..18e76b6b82 100644 --- a/actionpack/lib/action_controller/routing/segments.rb +++ b/actionpack/lib/action_controller/routing/segments.rb @@ -130,6 +130,7 @@ module ActionController def extract_value "#{local_name} = hash[:#{key}] && hash[:#{key}].to_param #{"|| #{default.inspect}" if default}" end + def value_check if default # Then we know it won't be nil "#{value_regexp.inspect} =~ #{local_name}" if regexp @@ -141,6 +142,7 @@ module ActionController "#{local_name} #{"&& #{value_regexp.inspect} =~ #{local_name}" if regexp}" end end + def expiry_statement "expired, hash = true, options if !expired && expire_on[:#{key}]" end @@ -175,7 +177,7 @@ module ActionController end def regexp_chunk - if regexp + if regexp if regexp_has_modifiers? "(#{regexp.to_s})" else @@ -214,7 +216,6 @@ module ActionController def regexp_has_modifiers? regexp.options & (Regexp::IGNORECASE | Regexp::EXTENDED) != 0 end - end class ControllerSegment < DynamicSegment #:nodoc: diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 84996fd6b1..22e394dc95 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -834,6 +834,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do puts "#{1 / per_url} url/s\n\n" end end + def test_time_generation n = 5000 if RunTimeTests diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index b624005a57..61b8c83ee0 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -64,7 +64,7 @@ class TestTest < Test::Unit::TestCase HTML end - + def test_xml_output response.content_type = "application/xml" render :text => < { :count => 1, :only => { :tag => "img" } } } } end - + def test_should_not_impose_childless_html_tags_in_xml process :test_xml_output @@ -486,7 +486,7 @@ XML assert_nil @request.env['HTTP_X_REQUESTED_WITH'] end - def test_header_properly_reset_after_get_request + def test_header_properly_reset_after_get_request get :test_params @request.recycle! assert_nil @request.instance_variable_get("@request_method") @@ -532,15 +532,15 @@ XML assert_equal file.path, file.local_path assert_equal expected, file.read end - + def test_test_uploaded_file_with_binary filename = 'mona_lisa.jpg' path = "#{FILES_DIR}/#{filename}" content_type = 'image/png' - + binary_uploaded_file = ActionController::TestUploadedFile.new(path, content_type, :binary) assert_equal File.open(path, READ_BINARY).read, binary_uploaded_file.read - + plain_uploaded_file = ActionController::TestUploadedFile.new(path, content_type) assert_equal File.open(path, READ_PLAIN).read, plain_uploaded_file.read end @@ -549,10 +549,10 @@ XML filename = 'mona_lisa.jpg' path = "#{FILES_DIR}/#{filename}" content_type = 'image/jpg' - + binary_file_upload = fixture_file_upload(path, content_type, :binary) assert_equal File.open(path, READ_BINARY).read, binary_file_upload.read - + plain_file_upload = fixture_file_upload(path, content_type) assert_equal File.open(path, READ_PLAIN).read, plain_file_upload.read end @@ -584,7 +584,7 @@ XML get :test_send_file assert_nothing_raised(NoMethodError) { @response.binary_content } end - + protected def with_foo_routing with_routing do |set| @@ -597,7 +597,6 @@ XML end end - class CleanBacktraceTest < Test::Unit::TestCase def test_should_reraise_the_same_object exception = Test::Unit::AssertionFailedError.new('message') @@ -658,7 +657,7 @@ end class NamedRoutesControllerTest < ActionController::TestCase tests ContentController - + def test_should_be_able_to_use_named_routes_before_a_request_is_done with_routing do |set| set.draw { |map| map.resources :contents } -- cgit v1.2.3 From a5db1488251304ec93256654859b430148f0c506 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 28 Jul 2008 13:41:42 -0500 Subject: Prepare Route#generate and Route#recognize early. Also refactor segments a bit to try to make immutable. --- .../lib/action_controller/routing/builder.rb | 29 +- .../lib/action_controller/routing/optimisations.rb | 1 + .../routing/recognition_optimisation.rb | 40 ++- actionpack/lib/action_controller/routing/route.rb | 300 +++++++++++---------- .../lib/action_controller/routing/route_set.rb | 3 +- .../lib/action_controller/routing/segments.rb | 29 +- actionpack/test/controller/routing_test.rb | 151 ++++------- actionpack/test/controller/test_test.rb | 4 +- 8 files changed, 259 insertions(+), 298 deletions(-) diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb index 912999d845..03427e41de 100644 --- a/actionpack/lib/action_controller/routing/builder.rb +++ b/actionpack/lib/action_controller/routing/builder.rb @@ -48,14 +48,10 @@ module ActionController end when /\A\*(\w+)/ then PathSegment.new($1.to_sym, :optional => true) when /\A\?(.*?)\?/ - returning segment = StaticSegment.new($1) do - segment.is_optional = true - end + StaticSegment.new($1, :optional => true) when /\A(#{separator_pattern(:inverted)}+)/ then StaticSegment.new($1) when Regexp.new(separator_pattern) then - returning segment = DividerSegment.new($&) do - segment.is_optional = (optional_separators.include? $&) - end + DividerSegment.new($&, :optional => (optional_separators.include? $&)) end [segment, $~.post_match] end @@ -176,29 +172,16 @@ module ActionController defaults, requirements, conditions = divide_route_options(segments, options) requirements = assign_route_options(segments, defaults, requirements) - route = Route.new - - route.segments = segments - route.requirements = requirements - route.conditions = conditions + # TODO: Segments should be frozen on initialize + segments.each { |segment| segment.freeze } - if !route.significant_keys.include?(:action) && !route.requirements[:action] - route.requirements[:action] = "index" - route.significant_keys << :action - end - - # Routes cannot use the current string interpolation method - # if there are user-supplied :requirements as the interpolation - # code won't raise RoutingErrors when generating - if options.key?(:requirements) || route.requirements.keys.to_set != Routing::ALLOWED_REQUIREMENTS_FOR_OPTIMISATION - route.optimise = false - end + route = Route.new(segments, requirements, conditions) if !route.significant_keys.include?(:controller) raise ArgumentError, "Illegal route: the :controller must be specified!" end - route + route.freeze end private diff --git a/actionpack/lib/action_controller/routing/optimisations.rb b/actionpack/lib/action_controller/routing/optimisations.rb index b44ebd5ca2..0fe836606c 100644 --- a/actionpack/lib/action_controller/routing/optimisations.rb +++ b/actionpack/lib/action_controller/routing/optimisations.rb @@ -20,6 +20,7 @@ module ActionController class Optimiser attr_reader :route, :kind + def initialize(route, kind) @route = route @kind = kind diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb index 67d354a943..6d54d0334c 100644 --- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb +++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb @@ -67,28 +67,6 @@ module ActionController end end - def recognize_optimized(path, env) - write_recognize_optimized - recognize_optimized(path, env) - end - - def write_recognize_optimized - tree = segment_tree(routes) - body = generate_code(tree) - instance_eval %{ - def recognize_optimized(path, env) - segments = to_plain_segments(path) - index = #{body} - return nil unless index - while index < routes.size - result = routes[index].recognize(path, env) and return result - index += 1 - end - nil - end - }, __FILE__, __LINE__ - end - def segment_tree(routes) tree = [0] @@ -151,6 +129,24 @@ module ActionController segments << nil segments end + + private + def write_recognize_optimized! + tree = segment_tree(routes) + body = generate_code(tree) + instance_eval %{ + def recognize_optimized(path, env) + segments = to_plain_segments(path) + index = #{body} + return nil unless index + while index < routes.size + result = routes[index].recognize(path, env) and return result + index += 1 + end + nil + end + }, __FILE__, __LINE__ + end end end end diff --git a/actionpack/lib/action_controller/routing/route.rb b/actionpack/lib/action_controller/routing/route.rb index a05c8c10ac..2106ac09e0 100644 --- a/actionpack/lib/action_controller/routing/route.rb +++ b/actionpack/lib/action_controller/routing/route.rb @@ -3,11 +3,25 @@ module ActionController class Route #:nodoc: attr_accessor :segments, :requirements, :conditions, :optimise - def initialize - @segments = [] - @requirements = {} - @conditions = {} - @optimise = true + def initialize(segments = [], requirements = {}, conditions = {}) + @segments = segments + @requirements = requirements + @conditions = conditions + + if !significant_keys.include?(:action) && !requirements[:action] + @requirements[:action] = "index" + @significant_keys << :action + end + + # Routes cannot use the current string interpolation method + # if there are user-supplied :requirements as the interpolation + # code won't raise RoutingErrors when generating + has_requirements = @segments.detect { |segment| segment.respond_to?(:regexp) && segment.regexp } + if has_requirements || @requirements.keys.to_set != Routing::ALLOWED_REQUIREMENTS_FOR_OPTIMISATION + @optimise = false + else + @optimise = true + end end # Indicates whether the routes should be optimised with the string interpolation @@ -22,129 +36,6 @@ module ActionController end.compact end - # Write and compile a +generate+ method for this Route. - def write_generation - # Build the main body of the generation - body = "expired = false\n#{generation_extraction}\n#{generation_structure}" - - # If we have conditions that must be tested first, nest the body inside an if - body = "if #{generation_requirements}\n#{body}\nend" if generation_requirements - args = "options, hash, expire_on = {}" - - # Nest the body inside of a def block, and then compile it. - raw_method = method_decl = "def generate_raw(#{args})\npath = begin\n#{body}\nend\n[path, hash]\nend" - instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" - - # expire_on.keys == recall.keys; in other words, the keys in the expire_on hash - # are the same as the keys that were recalled from the previous request. Thus, - # we can use the expire_on.keys to determine which keys ought to be used to build - # the query string. (Never use keys from the recalled request when building the - # query string.) - - method_decl = "def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(options))\nend" - instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" - - method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend" - instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" - raw_method - end - - # Build several lines of code that extract values from the options hash. If any - # of the values are missing or rejected then a return will be executed. - def generation_extraction - segments.collect do |segment| - segment.extraction_code - end.compact * "\n" - end - - # Produce a condition expression that will check the requirements of this route - # upon generation. - def generation_requirements - requirement_conditions = requirements.collect do |key, req| - if req.is_a? Regexp - value_regexp = Regexp.new "\\A#{req.to_s}\\Z" - "hash[:#{key}] && #{value_regexp.inspect} =~ options[:#{key}]" - else - "hash[:#{key}] == #{req.inspect}" - end - end - requirement_conditions * ' && ' unless requirement_conditions.empty? - end - - def generation_structure - segments.last.string_structure segments[0..-2] - end - - # Write and compile a +recognize+ method for this Route. - def write_recognition - # Create an if structure to extract the params from a match if it occurs. - body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams" - body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend" - - # Build the method declaration and compile it - method_decl = "def recognize(path, env={})\n#{body}\nend" - instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" - method_decl - end - - # Plugins may override this method to add other conditions, like checks on - # host, subdomain, and so forth. Note that changes here only affect route - # recognition, not generation. - def recognition_conditions - result = ["(match = #{Regexp.new(recognition_pattern).inspect}.match(path))"] - result << "conditions[:method] === env[:method]" if conditions[:method] - result - end - - # Build the regular expression pattern that will match this route. - def recognition_pattern(wrap = true) - pattern = '' - segments.reverse_each do |segment| - pattern = segment.build_pattern pattern - end - wrap ? ("\\A" + pattern + "\\Z") : pattern - end - - # Write the code to extract the parameters from a matched route. - def recognition_extraction - next_capture = 1 - extraction = segments.collect do |segment| - x = segment.match_extraction(next_capture) - next_capture += Regexp.new(segment.regexp_chunk).number_of_captures - x - end - extraction.compact - end - - # Write the real generation implementation and then resend the message. - def generate(options, hash, expire_on = {}) - write_generation - generate options, hash, expire_on - end - - def generate_extras(options, hash, expire_on = {}) - write_generation - generate_extras options, hash, expire_on - end - - # Generate the query string with any extra keys in the hash and append - # it to the given path, returning the new path. - def append_query_string(path, hash, query_keys=nil) - return nil unless path - query_keys ||= extra_keys(hash) - "#{path}#{build_query_string(hash, query_keys)}" - end - - # Determine which keys in the given hash are "extra". Extra keys are - # those that were not used to generate a particular route. The extra - # keys also do not include those recalled from the prior request, nor - # do they include any keys that were implied in the route (like a - # :controller that is required, but not explicitly used in the - # text of the route.) - def extra_keys(hash, recall={}) - (hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys - end - # Build a query string from the keys of the given hash. If +only_keys+ # is given (as an array), only the keys indicated will be used to build # the query string. The query string will correctly build array parameter @@ -161,12 +52,6 @@ module ActionController elements.empty? ? '' : "?#{elements.sort * '&'}" end - # Write the real recognition implementation and then resend the message. - def recognize(path, environment={}) - write_recognition - recognize path, environment - end - # A route's parameter shell contains parameter values that are not in the # route's path, but should be placed in the recognized hash. # @@ -186,7 +71,7 @@ module ActionController # includes keys that appear inside the path, and keys that have requirements # placed upon them. def significant_keys - @significant_keys ||= returning [] do |sk| + @significant_keys ||= returning([]) do |sk| segments.each { |segment| sk << segment.key if segment.respond_to? :key } sk.concat requirements.keys sk.uniq! @@ -209,12 +94,7 @@ module ActionController end def matches_controller_and_action?(controller, action) - unless defined? @matching_prepared - @controller_requirement = requirement_for(:controller) - @action_requirement = requirement_for(:action) - @matching_prepared = true - end - + prepare_matching! (@controller_requirement.nil? || @controller_requirement === controller) && (@action_requirement.nil? || @action_requirement === action) end @@ -226,7 +106,23 @@ module ActionController end end - protected + # TODO: Route should be prepared and frozen on initialize + def freeze + unless frozen? + write_generation! + write_recognition! + prepare_matching! + + parameter_shell + significant_keys + defaults + to_s + end + + super + end + + private def requirement_for(key) return requirements[key] if requirements.key? key segments.each do |segment| @@ -234,6 +130,126 @@ module ActionController end nil end + + # Write and compile a +generate+ method for this Route. + def write_generation! + # Build the main body of the generation + body = "expired = false\n#{generation_extraction}\n#{generation_structure}" + + # If we have conditions that must be tested first, nest the body inside an if + body = "if #{generation_requirements}\n#{body}\nend" if generation_requirements + args = "options, hash, expire_on = {}" + + # Nest the body inside of a def block, and then compile it. + raw_method = method_decl = "def generate_raw(#{args})\npath = begin\n#{body}\nend\n[path, hash]\nend" + instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" + + # expire_on.keys == recall.keys; in other words, the keys in the expire_on hash + # are the same as the keys that were recalled from the previous request. Thus, + # we can use the expire_on.keys to determine which keys ought to be used to build + # the query string. (Never use keys from the recalled request when building the + # query string.) + + method_decl = "def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(options))\nend" + instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" + + method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend" + instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" + raw_method + end + + # Build several lines of code that extract values from the options hash. If any + # of the values are missing or rejected then a return will be executed. + def generation_extraction + segments.collect do |segment| + segment.extraction_code + end.compact * "\n" + end + + # Produce a condition expression that will check the requirements of this route + # upon generation. + def generation_requirements + requirement_conditions = requirements.collect do |key, req| + if req.is_a? Regexp + value_regexp = Regexp.new "\\A#{req.to_s}\\Z" + "hash[:#{key}] && #{value_regexp.inspect} =~ options[:#{key}]" + else + "hash[:#{key}] == #{req.inspect}" + end + end + requirement_conditions * ' && ' unless requirement_conditions.empty? + end + + def generation_structure + segments.last.string_structure segments[0..-2] + end + + # Write and compile a +recognize+ method for this Route. + def write_recognition! + # Create an if structure to extract the params from a match if it occurs. + body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams" + body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend" + + # Build the method declaration and compile it + method_decl = "def recognize(path, env = {})\n#{body}\nend" + instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" + method_decl + end + + # Plugins may override this method to add other conditions, like checks on + # host, subdomain, and so forth. Note that changes here only affect route + # recognition, not generation. + def recognition_conditions + result = ["(match = #{Regexp.new(recognition_pattern).inspect}.match(path))"] + result << "conditions[:method] === env[:method]" if conditions[:method] + result + end + + # Build the regular expression pattern that will match this route. + def recognition_pattern(wrap = true) + pattern = '' + segments.reverse_each do |segment| + pattern = segment.build_pattern pattern + end + wrap ? ("\\A" + pattern + "\\Z") : pattern + end + + # Write the code to extract the parameters from a matched route. + def recognition_extraction + next_capture = 1 + extraction = segments.collect do |segment| + x = segment.match_extraction(next_capture) + next_capture += Regexp.new(segment.regexp_chunk).number_of_captures + x + end + extraction.compact + end + + # Generate the query string with any extra keys in the hash and append + # it to the given path, returning the new path. + def append_query_string(path, hash, query_keys = nil) + return nil unless path + query_keys ||= extra_keys(hash) + "#{path}#{build_query_string(hash, query_keys)}" + end + + # Determine which keys in the given hash are "extra". Extra keys are + # those that were not used to generate a particular route. The extra + # keys also do not include those recalled from the prior request, nor + # do they include any keys that were implied in the route (like a + # :controller that is required, but not explicitly used in the + # text of the route.) + def extra_keys(hash, recall = {}) + (hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys + end + + def prepare_matching! + unless defined? @matching_prepared + @controller_requirement = requirement_for(:controller) + @action_requirement = requirement_for(:action) + @matching_prepared = true + end + end end end end diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 4a9bc259f3..8dfc22f94f 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -194,6 +194,8 @@ module ActionController def initialize self.routes = [] self.named_routes = NamedRouteCollection.new + + write_recognize_optimized! end # Subclasses and plugins may override this method to specify a different @@ -231,7 +233,6 @@ module ActionController Routing.use_controllers! nil # Clear the controller cache so we may discover new ones clear! load_routes! - install_helpers end # reload! will always force a reload whereas load checks the timestamp first diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb index 18e76b6b82..75784c3b78 100644 --- a/actionpack/lib/action_controller/routing/segments.rb +++ b/actionpack/lib/action_controller/routing/segments.rb @@ -4,11 +4,12 @@ module ActionController RESERVED_PCHAR = ':@&=+$,;' UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze + # TODO: Convert :is_optional accessor to read only attr_accessor :is_optional alias_method :optional?, :is_optional def initialize - self.is_optional = false + @is_optional = false end def extraction_code @@ -63,12 +64,14 @@ module ActionController end class StaticSegment < Segment #:nodoc: - attr_accessor :value, :raw + attr_reader :value, :raw alias_method :raw?, :raw - def initialize(value = nil) + def initialize(value = nil, options = {}) super() - self.value = value + @value = value + @raw = options[:raw] if options.key?(:raw) + @is_optional = options[:optional] if options.key?(:optional) end def interpolation_chunk @@ -97,10 +100,8 @@ module ActionController end class DividerSegment < StaticSegment #:nodoc: - def initialize(value = nil) - super(value) - self.raw = true - self.is_optional = true + def initialize(value = nil, options = {}) + super(value, {:raw => true, :optional => true}.merge(options)) end def optionality_implied? @@ -109,13 +110,17 @@ module ActionController end class DynamicSegment < Segment #:nodoc: - attr_accessor :key, :default, :regexp + attr_reader :key + + # TODO: Convert these accessors to read only + attr_accessor :default, :regexp def initialize(key = nil, options = {}) super() - self.key = key - self.default = options[:default] if options.key? :default - self.is_optional = true if options[:optional] || options.key?(:default) + @key = key + @default = options[:default] if options.key?(:default) + @regexp = options[:regexp] if options.key?(:regexp) + @is_optional = true if options[:optional] || options.key?(:default) end def to_s diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 22e394dc95..6cf134c26f 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -67,66 +67,56 @@ class SegmentTest < Test::Unit::TestCase end def test_interpolation_statement - s = ROUTING::StaticSegment.new - s.value = "Hello" + s = ROUTING::StaticSegment.new("Hello") assert_equal "Hello", eval(s.interpolation_statement([])) assert_equal "HelloHello", eval(s.interpolation_statement([s])) - s2 = ROUTING::StaticSegment.new - s2.value = "-" + s2 = ROUTING::StaticSegment.new("-") assert_equal "Hello-Hello", eval(s.interpolation_statement([s, s2])) - s3 = ROUTING::StaticSegment.new - s3.value = "World" + s3 = ROUTING::StaticSegment.new("World") assert_equal "Hello-World", eval(s3.interpolation_statement([s, s2])) end end class StaticSegmentTest < Test::Unit::TestCase def test_interpolation_chunk_should_respect_raw - s = ROUTING::StaticSegment.new - s.value = 'Hello World' - assert ! s.raw? + s = ROUTING::StaticSegment.new('Hello World') + assert !s.raw? assert_equal 'Hello%20World', s.interpolation_chunk - s.raw = true + s = ROUTING::StaticSegment.new('Hello World', :raw => true) assert s.raw? assert_equal 'Hello World', s.interpolation_chunk end def test_regexp_chunk_should_escape_specials - s = ROUTING::StaticSegment.new - - s.value = 'Hello*World' + s = ROUTING::StaticSegment.new('Hello*World') assert_equal 'Hello\*World', s.regexp_chunk - s.value = 'HelloWorld' + s = ROUTING::StaticSegment.new('HelloWorld') assert_equal 'HelloWorld', s.regexp_chunk end def test_regexp_chunk_should_add_question_mark_for_optionals - s = ROUTING::StaticSegment.new - s.value = "/" - s.is_optional = true + s = ROUTING::StaticSegment.new("/", :optional => true) assert_equal "/?", s.regexp_chunk - s.value = "hello" + s = ROUTING::StaticSegment.new("hello", :optional => true) assert_equal "(?:hello)?", s.regexp_chunk end end class DynamicSegmentTest < Test::Unit::TestCase - def segment + def segment(options = {}) unless @segment - @segment = ROUTING::DynamicSegment.new - @segment.key = :a + @segment = ROUTING::DynamicSegment.new(:a, options) end @segment end def test_extract_value - s = ROUTING::DynamicSegment.new - s.key = :a + s = ROUTING::DynamicSegment.new(:a) hash = {:a => '10', :b => '20'} assert_equal '10', eval(s.extract_value) @@ -149,31 +139,31 @@ class DynamicSegmentTest < Test::Unit::TestCase end def test_regexp_value_check_rejects_nil - segment.regexp = /\d+/ + segment = segment(:regexp => /\d+/) + a_value = nil - assert ! eval(segment.value_check) + assert !eval(segment.value_check) end def test_optional_regexp_value_check_should_accept_nil - segment.regexp = /\d+/ - segment.is_optional = true + segment = segment(:regexp => /\d+/, :optional => true) + a_value = nil assert eval(segment.value_check) end def test_regexp_value_check_rejects_no_match - segment.regexp = /\d+/ + segment = segment(:regexp => /\d+/) a_value = "Hello20World" - assert ! eval(segment.value_check) + assert !eval(segment.value_check) a_value = "20Hi" - assert ! eval(segment.value_check) + assert !eval(segment.value_check) end def test_regexp_value_check_accepts_match - segment.regexp = /\d+/ - + segment = segment(:regexp => /\d+/) a_value = "30" assert eval(segment.value_check) end @@ -184,14 +174,14 @@ class DynamicSegmentTest < Test::Unit::TestCase end def test_optional_value_needs_no_check - segment.is_optional = true + segment = segment(:optional => true) + a_value = nil assert_equal nil, segment.value_check end def test_regexp_value_check_should_accept_match_with_default - segment.regexp = /\d+/ - segment.default = '200' + segment = segment(:regexp => /\d+/, :default => '200') a_value = '100' assert eval(segment.value_check) @@ -234,7 +224,7 @@ class DynamicSegmentTest < Test::Unit::TestCase end def test_extraction_code_should_return_on_mismatch - segment.regexp = /\d+/ + segment = segment(:regexp => /\d+/) hash = merged = {:a => 'Hi', :b => '3'} options = {:b => '3'} a_value = nil @@ -292,7 +282,7 @@ class DynamicSegmentTest < Test::Unit::TestCase end def test_value_regexp_should_match_exacly - segment.regexp = /\d+/ + segment = segment(:regexp => /\d+/) assert_no_match segment.value_regexp, "Hello 10 World" assert_no_match segment.value_regexp, "Hello 10" assert_no_match segment.value_regexp, "10 World" @@ -300,40 +290,36 @@ class DynamicSegmentTest < Test::Unit::TestCase end def test_regexp_chunk_should_return_string - segment.regexp = /\d+/ + segment = segment(:regexp => /\d+/) assert_kind_of String, segment.regexp_chunk end def test_build_pattern_non_optional_with_no_captures # Non optional - a_segment = ROUTING::DynamicSegment.new - a_segment.regexp = /\d+/ #number_of_captures is 0 + a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /\d+/) assert_equal "(\\d+)stuff", a_segment.build_pattern('stuff') end def test_build_pattern_non_optional_with_captures # Non optional - a_segment = ROUTING::DynamicSegment.new - a_segment.regexp = /(\d+)(.*?)/ #number_of_captures is 2 + a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /(\d+)(.*?)/) assert_equal "((\\d+)(.*?))stuff", a_segment.build_pattern('stuff') end def test_optionality_implied - a_segment = ROUTING::DynamicSegment.new - a_segment.key = :id + a_segment = ROUTING::DynamicSegment.new(:id) assert a_segment.optionality_implied? - a_segment.key = :action + a_segment = ROUTING::DynamicSegment.new(:action) assert a_segment.optionality_implied? end def test_modifiers_must_be_handled_sensibly - a_segment = ROUTING::DynamicSegment.new - a_segment.regexp = /david|jamis/i + a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/i) assert_equal "((?i-mx:david|jamis))stuff", a_segment.build_pattern('stuff') - a_segment.regexp = /david|jamis/x + a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/x) assert_equal "((?x-mi:david|jamis))stuff", a_segment.build_pattern('stuff') - a_segment.regexp = /david|jamis/ + a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/) assert_equal "(david|jamis)stuff", a_segment.build_pattern('stuff') end end @@ -560,7 +546,7 @@ class RouteBuilderTest < Test::Unit::TestCase action = segments[-4] assert_equal :action, action.key - action.regexp = /show|in/ # Use 'in' to check partial matches + segments[-4] = ROUTING::DynamicSegment.new(:action, :regexp => /show|in/) builder.assign_default_route_options(segments) @@ -661,10 +647,10 @@ class RoutingTest < Test::Unit::TestCase ActionController::Routing.controller_paths = [] assert_equal [], ActionController::Routing.possible_controllers - ActionController::Routing::Routes.load! ActionController::Routing.controller_paths = [ root, root + '/app/controllers', root + '/vendor/plugins/bad_plugin/lib' ] + ActionController::Routing::Routes.load! assert_equal ["admin/user", "plugin", "user"], ActionController::Routing.possible_controllers.sort ensure @@ -1374,34 +1360,20 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do end def slash_segment(is_optional = false) - returning ROUTING::DividerSegment.new('/') do |s| - s.is_optional = is_optional - end + ROUTING::DividerSegment.new('/', :optional => is_optional) end def default_route unless defined?(@default_route) - @default_route = ROUTING::Route.new - - @default_route.segments << (s = ROUTING::StaticSegment.new) - s.value = '/' - s.raw = true - - @default_route.segments << (s = ROUTING::DynamicSegment.new) - s.key = :controller - - @default_route.segments << slash_segment(:optional) - @default_route.segments << (s = ROUTING::DynamicSegment.new) - s.key = :action - s.default = 'index' - s.is_optional = true - - @default_route.segments << slash_segment(:optional) - @default_route.segments << (s = ROUTING::DynamicSegment.new) - s.key = :id - s.is_optional = true - - @default_route.segments << slash_segment(:optional) + segments = [] + segments << ROUTING::StaticSegment.new('/', :raw => true) + segments << ROUTING::DynamicSegment.new(:controller) + segments << slash_segment(:optional) + segments << ROUTING::DynamicSegment.new(:action, :default => 'index', :optional => true) + segments << slash_segment(:optional) + segments << ROUTING::DynamicSegment.new(:id, :optional => true) + segments << slash_segment(:optional) + @default_route = ROUTING::Route.new(segments).freeze end @default_route end @@ -1489,29 +1461,16 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do end def test_significant_keys - user_url = ROUTING::Route.new - user_url.segments << (s = ROUTING::StaticSegment.new) - s.value = '/' - s.raw = true - - user_url.segments << (s = ROUTING::StaticSegment.new) - s.value = 'user' - - user_url.segments << (s = ROUTING::StaticSegment.new) - s.value = '/' - s.raw = true - s.is_optional = true - - user_url.segments << (s = ROUTING::DynamicSegment.new) - s.key = :user - - user_url.segments << (s = ROUTING::StaticSegment.new) - s.value = '/' - s.raw = true - s.is_optional = true + segments = [] + segments << ROUTING::StaticSegment.new('/', :raw => true) + segments << ROUTING::StaticSegment.new('user') + segments << ROUTING::StaticSegment.new('/', :raw => true, :optional => true) + segments << ROUTING::DynamicSegment.new(:user) + segments << ROUTING::StaticSegment.new('/', :raw => true, :optional => true) - user_url.requirements = {:controller => 'users', :action => 'show'} + requirements = {:controller => 'users', :action => 'show'} + user_url = ROUTING::Route.new(segments, requirements) keys = user_url.significant_keys.sort_by { |k| k.to_s } assert_equal [:action, :controller, :user], keys end diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index 61b8c83ee0..58d9ca537f 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -117,8 +117,8 @@ XML @controller = TestController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new - ActionController::Routing::Routes.reload ActionController::Routing.use_controllers! %w(content admin/user test_test/test) + ActionController::Routing::Routes.load_routes! end def teardown @@ -412,7 +412,7 @@ XML def test_assert_routing_with_method with_routing do |set| - set.draw { |map| map.resources(:content) } + set.draw { |map| map.resources(:content) } assert_routing({ :method => 'post', :path => 'content' }, { :controller => 'content', :action => 'create' }) end end -- cgit v1.2.3 From 19db0b732458347b5237ac90865d62b3fd2157f1 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 28 Jul 2008 14:31:40 -0500 Subject: Added back ActionController::Base.allow_concurrency flag and moved lock down to controller processing. --- actionpack/CHANGELOG | 2 ++ actionpack/lib/action_controller/base.rb | 15 ++++++++++++++- actionpack/lib/action_controller/dispatcher.rb | 18 +++++++----------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 03e011c75c..177c6a354e 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Added back ActionController::Base.allow_concurrency flag [Josh Peek] + * AbstractRequest.relative_url_root is no longer automatically configured by a HTTP header. It can now be set in your configuration environment with config.action_controller.relative_url_root [Josh Peek] * Update Prototype to 1.6.0.2 #599 [Patrick Joyce] diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 5f4a38dac0..ac10a956f3 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -283,6 +283,14 @@ module ActionController #:nodoc: @@debug_routes = true cattr_accessor :debug_routes + # Indicates whether to allow concurrent action processing. Your + # controller actions and any other code they call must also behave well + # when called from concurrent threads. Turned off by default. + @@allow_concurrency = false + cattr_accessor :allow_concurrency + + @@guard = Monitor.new + # Modern REST web services often need to submit complex data to the web application. # The @@param_parsers hash lets you register handlers which will process the HTTP body and add parameters to the # params hash. These handlers are invoked for POST and PUT requests. @@ -537,7 +545,12 @@ module ActionController #:nodoc: forget_variables_added_to_assigns log_processing - send(method, *arguments) + + if @@allow_concurrency + send(method, *arguments) + else + @@guard.synchronize { send(method, *arguments) } + end assign_default_content_type_and_charset response.prepare! unless component_request? diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index 7df987d525..835d8e834e 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -2,8 +2,6 @@ module ActionController # Dispatches requests to the appropriate controller and takes care of # reloading the app after each request when Dependencies.load? is true. class Dispatcher - @@guard = Mutex.new - class << self def define_dispatcher_callbacks(cache_classes) unless cache_classes @@ -101,15 +99,13 @@ module ActionController end def dispatch - @@guard.synchronize do - begin - run_callbacks :before_dispatch - handle_request - rescue Exception => exception - failsafe_rescue exception - ensure - run_callbacks :after_dispatch, :enumerator => :reverse_each - end + begin + run_callbacks :before_dispatch + handle_request + rescue Exception => exception + failsafe_rescue exception + ensure + run_callbacks :after_dispatch, :enumerator => :reverse_each end end -- cgit v1.2.3 From 50bbc87f85e817a2926c56ccd81d3dc498a05e21 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 27 Jul 2008 21:44:53 -0700 Subject: MacRuby: BasicObject unavailable --- activesupport/lib/active_support/basic_object.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/basic_object.rb b/activesupport/lib/active_support/basic_object.rb index e06da79d26..1f77209e7f 100644 --- a/activesupport/lib/active_support/basic_object.rb +++ b/activesupport/lib/active_support/basic_object.rb @@ -7,7 +7,7 @@ # barebones base class that emulates Builder::BlankSlate while still relying on # Ruby 1.9's BasicObject in Ruby 1.9. module ActiveSupport - if RUBY_VERSION >= '1.9' + if defined? ::BasicObject class BasicObject < ::BasicObject undef_method :== undef_method :equal? -- cgit v1.2.3 From eb256718c3c1a0640f69861587239f1e6cde2820 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 27 Jul 2008 21:45:33 -0700 Subject: Remove send! usage, relic of reverted 1.9 behavior --- actionpack/lib/action_controller/integration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 43158ea412..1d2b81355c 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -507,7 +507,7 @@ EOF # Delegate unhandled messages to the current session instance. def method_missing(sym, *args, &block) reset! unless @integration_session - returning @integration_session.send!(sym, *args, &block) do + returning @integration_session.__send__(sym, *args, &block) do copy_session_variables! end end -- cgit v1.2.3 From ae6105ef01b2a767afa2bf5b64c90d288c752995 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 27 Jul 2008 21:45:55 -0700 Subject: Don't rememoize if already frozen --- activesupport/lib/active_support/memoizable.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 21636b8af4..23dd96e4df 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -11,10 +11,9 @@ module ActiveSupport def freeze_with_memoizable methods.each do |method| - if m = method.to_s.match(/^_unmemoized_(.*)/) - send(m[1]) - end - end + __send__($1) if method.to_s =~ /^_unmemoized_(.*)/ + end unless frozen? + freeze_without_memoizable end end -- cgit v1.2.3 From 2cf161a384cc361d856aa76639bcb30570d67286 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 27 Jul 2008 21:46:12 -0700 Subject: Once is enough, mmk --- activesupport/lib/active_support/testing/performance.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 71d6f4d9c6..70a7f84023 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -72,13 +72,13 @@ module ActiveSupport protected def run_warmup - 5.times { GC.start } + GC.start time = Metrics::Time.new run_test(time, :benchmark) puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] - 5.times { GC.start } + GC.start end def run_profile(metric) -- cgit v1.2.3 From 7aaf1689dda863b72623f0e52ad87e2b739cb22b Mon Sep 17 00:00:00 2001 From: Jan De Poorter Date: Tue, 29 Jul 2008 13:11:38 +0200 Subject: Fix that label_tag doesn't take a symbol for a name. [#719 state:resolved] Signed-off-by: Pratik Naik --- actionpack/lib/action_view/helpers/form_tag_helper.rb | 2 +- actionpack/test/template/form_tag_helper_test.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index bdfb2eebd7..e8ca02d760 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -129,7 +129,7 @@ module ActionView # label_tag 'name', nil, :class => 'small_label' # # => def label_tag(name, text = nil, options = {}) - content_tag :label, text || name.humanize, { "for" => name }.update(options.stringify_keys) + content_tag :label, text || name.to_s.humanize, { "for" => name }.update(options.stringify_keys) end # Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 4e4102aec7..9b41ff8179 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -190,6 +190,12 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal expected, actual end + def test_label_tag_with_symbol + actual = label_tag :title + expected = %() + assert_dom_equal expected, actual + end + def test_label_tag_with_text actual = label_tag "title", "My Title" expected = %() -- cgit v1.2.3 From a24398b64757df8c5939b07238c740bddfdab03e Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Tue, 29 Jul 2008 19:49:38 +0200 Subject: Guard the logger's internal buffer to prevent major breakage on genuinely threaded environments --- activesupport/lib/active_support/buffered_logger.rb | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index 67b0a580ea..aec416a6d6 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -40,6 +40,7 @@ module ActiveSupport @buffer = [] @auto_flushing = 1 @no_block = false + @guard = Mutex.new if log.respond_to?(:write) @log = log @@ -66,7 +67,9 @@ module ActiveSupport # If a newline is necessary then create a new message ending with a newline. # Ensures that the original message is not mutated. message = "#{message}\n" unless message[-1] == ?\n - buffer << message + @guard.synchronize do + buffer << message + end auto_flush message end @@ -98,11 +101,16 @@ module ActiveSupport end def flush - unless buffer.empty? - if @no_block - @log.write_nonblock(buffer.slice!(0..-1).join) - else - @log.write(buffer.slice!(0..-1).join) + @guard.synchronize do + unless buffer.empty? + old_buffer = @buffer + @buffer = [] + text_to_write = old_buffer.join + if @no_block + @log.write_nonblock(text_to_write) + else + @log.write(text_to_write) + end end end end -- cgit v1.2.3 From d9452d3ab3063c5e96dfd80cf6056c49192081b3 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Tue, 29 Jul 2008 20:01:25 +0200 Subject: Remove incomplete non-blocking logger functionality --- activesupport/lib/active_support/buffered_logger.rb | 14 +------------- railties/lib/initializer.rb | 1 - 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index aec416a6d6..cedc1afe7f 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -39,7 +39,6 @@ module ActiveSupport @level = level @buffer = [] @auto_flushing = 1 - @no_block = false @guard = Mutex.new if log.respond_to?(:write) @@ -55,12 +54,6 @@ module ActiveSupport end end - def set_non_blocking_io - if !RUBY_PLATFORM.match(/java|mswin/) && !(@log == STDOUT) && @log.respond_to?(:write_nonblock) - @no_block = true - end - end - def add(severity, message = nil, progname = nil, &block) return if @level > severity message = (message || (block && block.call) || progname).to_s @@ -105,12 +98,7 @@ module ActiveSupport unless buffer.empty? old_buffer = @buffer @buffer = [] - text_to_write = old_buffer.join - if @no_block - @log.write_nonblock(text_to_write) - else - @log.write(text_to_write) - end + @log.write(old_buffer.join) end end end diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 32411e8928..782fbebec2 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -408,7 +408,6 @@ Run `rake gems:install` to install the missing gems. logger.level = ActiveSupport::BufferedLogger.const_get(configuration.log_level.to_s.upcase) if configuration.environment == "production" logger.auto_flushing = false - logger.set_non_blocking_io end rescue StandardError => e logger = ActiveSupport::BufferedLogger.new(STDERR) -- cgit v1.2.3 From fea7771d2294bd810d04c03c125ceac5d6bb9806 Mon Sep 17 00:00:00 2001 From: Clemens Kofler Date: Tue, 29 Jul 2008 21:47:02 -0500 Subject: Updated NumberHelper: Full i18n support (except number_to_phone), consolidated API (almost all methods now support :precision, :delimiter and :separator). Added deprecation notices for old API. Added tests for new options [#716 state:resolved] Signed-off-by: Joshua Peek --- .../lib/action_view/helpers/number_helper.rb | 186 ++++++++++++++------- actionpack/lib/action_view/locale/en-US.rb | 35 +++- .../test/template/number_helper_i18n_test.rb | 46 ++++- actionpack/test/template/number_helper_test.rb | 48 +++--- 4 files changed, 219 insertions(+), 96 deletions(-) diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index c4ba7ccc8e..8c1dea2186 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -25,11 +25,11 @@ module ActionView # => +1.123.555.1234 x 1343 def number_to_phone(number, options = {}) number = number.to_s.strip unless number.nil? - options = options.stringify_keys - area_code = options["area_code"] || nil - delimiter = options["delimiter"] || "-" - extension = options["extension"].to_s.strip || nil - country_code = options["country_code"] || nil + options = options.symbolize_keys + area_code = options[:area_code] || nil + delimiter = options[:delimiter] || "-" + extension = options[:extension].to_s.strip || nil + country_code = options[:country_code] || nil begin str = "" @@ -51,10 +51,10 @@ module ActionView # # ==== Options # * :precision - Sets the level of precision (defaults to 2). - # * :unit - Sets the denomination of the currency (defaults to "$"). + # * :unit - Sets the denomination of the currency (defaults to "$"). # * :separator - Sets the separator between the units (defaults to "."). # * :delimiter - Sets the thousands delimiter (defaults to ","). - # * :format - Sets the format of the output string (defaults to "%u%n"). The field types are: + # * :format - Sets the format of the output string (defaults to "%u%n"). The field types are: # # %u The currency unit # %n The number @@ -69,8 +69,11 @@ module ActionView # number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "", :format => "%n %u") # # => 1234567890,50 £ def number_to_currency(number, options = {}) - options = options.symbolize_keys - defaults = I18n.translate(:'currency.format', :locale => options[:locale]) || {} + options.symbolize_keys! + + defaults, currency = I18n.translate([:'number.format', :'number.currency.format'], + :locale => options[:locale]) || [{},{}] + defaults = defaults.merge(currency) precision = options[:precision] || defaults[:precision] unit = options[:unit] || defaults[:unit] @@ -80,8 +83,11 @@ module ActionView separator = '' if precision == 0 begin - parts = number_with_precision(number, precision).split('.') - format.gsub(/%n/, number_with_delimiter(parts[0], delimiter) + separator + parts[1].to_s).gsub(/%u/, unit) + format.gsub(/%n/, number_with_precision(number, + :precision => precision, + :delimiter => delimiter, + :separator => separator) + ).gsub(/%u/, unit) rescue number end @@ -93,26 +99,29 @@ module ActionView # ==== Options # * :precision - Sets the level of precision (defaults to 3). # * :separator - Sets the separator between the units (defaults to "."). + # * :delimiter - Sets the thousands delimiter (defaults to ""). # # ==== Examples - # number_to_percentage(100) # => 100.000% - # number_to_percentage(100, :precision => 0) # => 100% - # - # number_to_percentage(302.24398923423, :precision => 5) - # # => 302.24399% + # number_to_percentage(100) # => 100.000% + # number_to_percentage(100, :precision => 0) # => 100% + # number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000% + # number_to_percentage(302.24398923423, :precision => 5) # => 302.24399% def number_to_percentage(number, options = {}) - options = options.stringify_keys - precision = options["precision"] || 3 - separator = options["separator"] || "." + options.symbolize_keys! + + defaults, percentage = I18n.translate([:'number.format', :'number.percentage.format'], + :locale => options[:locale]) || [{},{}] + defaults = defaults.merge(percentage) + + precision = options[:precision] || defaults[:precision] + separator = options[:separator] || defaults[:separator] + delimiter = options[:delimiter] || defaults[:delimiter] begin - number = number_with_precision(number, precision) - parts = number.split('.') - if parts.at(1).nil? - parts[0] + "%" - else - parts[0] + separator + parts[1].to_s + "%" - end + number_with_precision(number, + :precision => precision, + :separator => separator, + :delimiter => delimiter) + "%" rescue number end @@ -136,87 +145,140 @@ module ActionView # You can still use number_with_delimiter with the old API that accepts the # +delimiter+ as its optional second and the +separator+ as its # optional third parameter: - # number_with_delimiter(12345678, " ") # => 12 345.678 - # number_with_delimiter(12345678.05, ".", ",") # => 12.345.678,05 + # number_with_delimiter(12345678, " ") # => 12 345.678 + # number_with_delimiter(12345678.05, ".", ",") # => 12.345.678,05 def number_with_delimiter(number, *args) options = args.extract_options! + options.symbolize_keys! + + defaults = I18n.translate(:'number.format', :locale => options[:locale]) || {} + unless args.empty? - options[:delimiter] = args[0] || "," - options[:separator] = args[1] || "." + ActiveSupport::Deprecation.warn('number_with_delimiter takes an option hash ' + + 'instead of separate delimiter and precision arguments.', caller) + delimiter = args[0] || defaults[:delimiter] + separator = args[1] || defaults[:separator] end - options.reverse_merge!(:delimiter => ",", :separator => ".") + + delimiter ||= (options[:delimiter] || defaults[:delimiter]) + separator ||= (options[:separator] || defaults[:separator]) begin parts = number.to_s.split('.') - parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}") - parts.join options[:separator] + parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}") + parts.join(separator) rescue number end end # Formats a +number+ with the specified level of :precision (e.g., 112.32 has a precision of 2). - # The default level of precision is 3. + # You can customize the format in the +options+ hash. + # + # ==== Options + # * :precision - Sets the level of precision (defaults to 3). + # * :separator - Sets the separator between the units (defaults to "."). + # * :delimiter - Sets the thousands delimiter (defaults to ""). # # ==== Examples # number_with_precision(111.2345) # => 111.235 # number_with_precision(111.2345, :precision => 2) # => 111.23 # number_with_precision(13, :precision => 5) # => 13.00000 # number_with_precision(389.32314, :precision => 0) # => 389 + # number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.') + # # => 1.111,23 # # You can still use number_with_precision with the old API that accepts the # +precision+ as its optional second parameter: # number_with_precision(number_with_precision(111.2345, 2) # => 111.23 def number_with_precision(number, *args) options = args.extract_options! + options.symbolize_keys! + + defaults, precision_defaults = I18n.translate([:'number.format', :'number.precision.format'], + :locale => options[:locale]) || [{},{}] + defaults = defaults.merge(precision_defaults) + unless args.empty? - options[:precision] = args[0] || 3 + ActiveSupport::Deprecation.warn('number_with_precision takes an option hash ' + + 'instead of a separate precision argument.', caller) + precision = args[0] || defaults[:precision] end - options.reverse_merge!(:precision => 3) - "%01.#{options[:precision]}f" % - ((Float(number) * (10 ** options[:precision])).round.to_f / 10 ** options[:precision]) + + precision ||= (options[:precision] || defaults[:precision]) + separator ||= (options[:separator] || defaults[:separator]) + delimiter ||= (options[:delimiter] || defaults[:delimiter]) + + rounded_number = (Float(number) * (10 ** precision)).round.to_f / 10 ** precision + number_with_delimiter("%01.#{precision}f" % rounded_number, + :separator => separator, + :delimiter => delimiter) rescue number end + STORAGE_UNITS = %w( Bytes KB MB GB TB ).freeze + # Formats the bytes in +size+ into a more understandable representation # (e.g., giving it 1500 yields 1.5 KB). This method is useful for # reporting file sizes to users. This method returns nil if - # +size+ cannot be converted into a number. You can change the default - # precision of 1 using the precision parameter :precision. + # +size+ cannot be converted into a number. You can customize the + # format in the +options+ hash. + # + # ==== Options + # * :precision - Sets the level of precision (defaults to 1). + # * :separator - Sets the separator between the units (defaults to "."). + # * :delimiter - Sets the thousands delimiter (defaults to ""). # # ==== Examples - # number_to_human_size(123) # => 123 Bytes - # number_to_human_size(1234) # => 1.2 KB - # number_to_human_size(12345) # => 12.1 KB - # number_to_human_size(1234567) # => 1.2 MB - # number_to_human_size(1234567890) # => 1.1 GB - # number_to_human_size(1234567890123) # => 1.1 TB - # number_to_human_size(1234567, :precision => 2) # => 1.18 MB - # number_to_human_size(483989, :precision => 0) # => 473 KB + # number_to_human_size(123) # => 123 Bytes + # number_to_human_size(1234) # => 1.2 KB + # number_to_human_size(12345) # => 12.1 KB + # number_to_human_size(1234567) # => 1.2 MB + # number_to_human_size(1234567890) # => 1.1 GB + # number_to_human_size(1234567890123) # => 1.1 TB + # number_to_human_size(1234567, :precision => 2) # => 1.18 MB + # number_to_human_size(483989, :precision => 0) # => 473 KB + # number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,18 MB # # You can still use number_to_human_size with the old API that accepts the # +precision+ as its optional second parameter: # number_to_human_size(1234567, 2) # => 1.18 MB # number_to_human_size(483989, 0) # => 473 KB - def number_to_human_size(size, *args) + def number_to_human_size(number, *args) + return number.nil? ? nil : pluralize(number.to_i, "Byte") if number.to_i < 1024 + options = args.extract_options! + options.symbolize_keys! + + defaults, human = I18n.translate([:'number.format', :'number.human.format'], + :locale => options[:locale]) || [{},{}] + defaults = defaults.merge(human) + unless args.empty? - options[:precision] = args[0] || 1 + ActiveSupport::Deprecation.warn('number_to_human_size takes an option hash ' + + 'instead of a separate precision argument.', caller) + precision = args[0] || defaults[:precision] end - options.reverse_merge!(:precision => 1) - - size = Float(size) - case - when size.to_i == 1; "1 Byte" - when size < 1.kilobyte; "%d Bytes" % size - when size < 1.megabyte; "%.#{options[:precision]}f KB" % (size / 1.0.kilobyte) - when size < 1.gigabyte; "%.#{options[:precision]}f MB" % (size / 1.0.megabyte) - when size < 1.terabyte; "%.#{options[:precision]}f GB" % (size / 1.0.gigabyte) - else "%.#{options[:precision]}f TB" % (size / 1.0.terabyte) - end.sub(/([0-9]\.\d*?)0+ /, '\1 ' ).sub(/\. /,' ') + + precision ||= (options[:precision] || defaults[:precision]) + separator ||= (options[:separator] || defaults[:separator]) + delimiter ||= (options[:delimiter] || defaults[:delimiter]) + + max_exp = STORAGE_UNITS.size - 1 + number = Float(number) + exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024 + exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit + number /= 1024 ** exponent + unit = STORAGE_UNITS[exponent] + + number_with_precision(number, + :precision => precision, + :separator => separator, + :delimiter => delimiter + ).sub(/(\d)(#{Regexp.escape(separator)}[1-9]*)?0+\z/, '\1') + " #{unit}" rescue - nil + number end end end diff --git a/actionpack/lib/action_view/locale/en-US.rb b/actionpack/lib/action_view/locale/en-US.rb index 3adb199681..2c3676dca8 100644 --- a/actionpack/lib/action_view/locale/en-US.rb +++ b/actionpack/lib/action_view/locale/en-US.rb @@ -14,19 +14,40 @@ I18n.backend.store_translations :'en-US', { :over_x_years => ['over 1 year', 'over {{count}} years'] } }, - :currency => { + :number => { :format => { - :unit => '$', - :precision => 2, + :precision => 3, :separator => '.', - :delimiter => ',', - :format => '%u%n', + :delimiter => ',' + }, + :currency => { + :format => { + :unit => '$', + :precision => 2, + :format => '%u%n' + } + }, + :human => { + :format => { + :precision => 1, + :delimiter => '' + } + }, + :percentage => { + :format => { + :delimiter => '' + } + }, + :precision => { + :format => { + :delimiter => '' + } } }, :active_record => { :error => { :header_message => ["1 error prohibited this {{object_name}} from being saved", "{{count}} errors prohibited this {{object_name}} from being saved"], :message => "There were problems with the following fields:" - } - } + } + } } diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb index 50c20c3627..ce0da398cc 100644 --- a/actionpack/test/template/number_helper_i18n_test.rb +++ b/actionpack/test/template/number_helper_i18n_test.rb @@ -2,17 +2,53 @@ require 'abstract_unit' class NumberHelperI18nTests < Test::Unit::TestCase include ActionView::Helpers::NumberHelper - + attr_reader :request + uses_mocha 'number_helper_i18n_tests' do def setup - @defaults = {:separator => ".", :unit => "$", :format => "%u%n", :delimiter => ",", :precision => 2} - I18n.backend.store_translations 'en-US', :currency => {:format => @defaults} + @number_defaults = { :precision => 3, :delimiter => ',', :separator => '.' } + @currency_defaults = { :unit => '$', :format => '%u%n', :precision => 2 } + @human_defaults = { :precision => 1 } + @percentage_defaults = { :delimiter => '' } + @precision_defaults = { :delimiter => '' } + + I18n.backend.store_translations 'en-US', :number => { :format => @number_defaults, + :currency => { :format => @currency_defaults }, :human => @human_defaults } end def test_number_to_currency_translates_currency_formats - I18n.expects(:translate).with(:'currency.format', :locale => 'en-US').returns @defaults + I18n.expects(:translate).with( + [:'number.format', :'number.currency.format'], :locale => 'en-US' + ).returns([@number_defaults, @currency_defaults]) number_to_currency(1, :locale => 'en-US') end + + def test_number_with_precision_translates_number_formats + I18n.expects(:translate).with( + [:'number.format', :'number.precision.format'], :locale => 'en-US' + ).returns([@number_defaults, @precision_defaults]) + number_with_precision(1, :locale => 'en-US') + end + + def test_number_with_delimiter_translates_number_formats + I18n.expects(:translate).with(:'number.format', :locale => 'en-US').returns(@number_defaults) + number_with_delimiter(1, :locale => 'en-US') + end + + def test_number_to_percentage_translates_number_formats + I18n.expects(:translate).with( + [:'number.format', :'number.percentage.format'], :locale => 'en-US' + ).returns([@number_defaults, @percentage_defaults]) + number_to_percentage(1, :locale => 'en-US') + end + + def test_number_to_human_size_translates_human_formats + I18n.expects(:translate).with( + [:'number.format', :'number.human.format'], :locale => 'en-US' + ).returns([@number_defaults, @human_defaults]) + # can't be called with 1 because this directly returns without calling I18n.translate + number_to_human_size(1025, :locale => 'en-US') + end end -end \ No newline at end of file +end diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb index bff349a754..9c9f54936c 100644 --- a/actionpack/test/template/number_helper_test.rb +++ b/actionpack/test/template/number_helper_test.rb @@ -26,7 +26,8 @@ class NumberHelperTest < ActionView::TestCase assert_equal("£1234567890,50", number_to_currency(1234567890.50, {:unit => "£", :separator => ",", :delimiter => ""})) assert_equal("$1,234,567,890.50", number_to_currency("1234567890.50")) assert_equal("1,234,567,890.50 Kč", number_to_currency("1234567890.50", {:unit => "Kč", :format => "%n %u"})) - assert_equal("$x.", number_to_currency("x")) + #assert_equal("$x.", number_to_currency("x")) # fails due to API consolidation + assert_equal("$x", number_to_currency("x")) assert_nil number_to_currency(nil) end @@ -35,7 +36,9 @@ class NumberHelperTest < ActionView::TestCase assert_equal("100%", number_to_percentage(100, {:precision => 0})) assert_equal("302.06%", number_to_percentage(302.0574, {:precision => 2})) assert_equal("100.000%", number_to_percentage("100")) + assert_equal("1000.000%", number_to_percentage("1000")) assert_equal("x%", number_to_percentage("x")) + assert_equal("1.000,000%", number_to_percentage(1000, :delimiter => '.', :separator => ',')) assert_nil number_to_percentage(nil) end @@ -63,28 +66,22 @@ class NumberHelperTest < ActionView::TestCase def test_number_with_precision assert_equal("111.235", number_with_precision(111.2346)) - assert_equal("31.83", number_with_precision(31.825, 2)) - assert_equal("111.23", number_with_precision(111.2346, 2)) - assert_equal("111.00", number_with_precision(111, 2)) + assert_equal("31.83", number_with_precision(31.825, :precision => 2)) + assert_equal("111.23", number_with_precision(111.2346, :precision => 2)) + assert_equal("111.00", number_with_precision(111, :precision => 2)) assert_equal("111.235", number_with_precision("111.2346")) - assert_equal("31.83", number_with_precision("31.825", 2)) - assert_equal("112", number_with_precision(111.50, 0)) - assert_equal("1234567892", number_with_precision(1234567891.50, 0)) + assert_equal("31.83", number_with_precision("31.825", :precision => 2)) + assert_equal("112", number_with_precision(111.50, :precision => 0)) + assert_equal("1234567892", number_with_precision(1234567891.50, :precision => 0)) # Return non-numeric params unchanged. assert_equal("x", number_with_precision("x")) assert_nil number_with_precision(nil) end - def test_number_with_precision_with_options_hash - assert_equal '111.235', number_with_precision(111.2346) - assert_equal '31.83', number_with_precision(31.825, :precision => 2) - assert_equal '111.23', number_with_precision(111.2346, :precision => 2) - assert_equal '111.00', number_with_precision(111, :precision => 2) - assert_equal '111.235', number_with_precision("111.2346") - assert_equal '31.83', number_with_precision("31.825", :precision => 2) - assert_equal '112', number_with_precision(111.50, :precision => 0) - assert_equal '1234567892', number_with_precision(1234567891.50, :precision => 0) + def test_number_with_precision_with_custom_delimiter_and_separator + assert_equal '31,83', number_with_precision(31.825, :precision => 2, :separator => ',') + assert_equal '1.231,83', number_with_precision(1231.825, :precision => 2, :separator => ',', :delimiter => '.') end def test_number_to_human_size @@ -98,18 +95,19 @@ class NumberHelperTest < ActionView::TestCase assert_equal '1.2 MB', number_to_human_size(1234567) assert_equal '1.1 GB', number_to_human_size(1234567890) assert_equal '1.1 TB', number_to_human_size(1234567890123) + assert_equal '1025 TB', number_to_human_size(1025.terabytes) assert_equal '444 KB', number_to_human_size(444.kilobytes) assert_equal '1023 MB', number_to_human_size(1023.megabytes) assert_equal '3 TB', number_to_human_size(3.terabytes) - assert_equal '1.18 MB', number_to_human_size(1234567, 2) - assert_equal '3 Bytes', number_to_human_size(3.14159265, 4) + assert_equal '1.18 MB', number_to_human_size(1234567, :precision => 2) + assert_equal '3 Bytes', number_to_human_size(3.14159265, :precision => 4) assert_equal("123 Bytes", number_to_human_size("123")) - assert_equal '1.01 KB', number_to_human_size(1.0123.kilobytes, 2) - assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, 4) - assert_equal '10 KB', number_to_human_size(10.000.kilobytes, 4) + assert_equal '1.01 KB', number_to_human_size(1.0123.kilobytes, :precision => 2) + assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4) + assert_equal '10 KB', number_to_human_size(10.000.kilobytes, :precision => 4) assert_equal '1 Byte', number_to_human_size(1.1) assert_equal '10 Bytes', number_to_human_size(10) - assert_nil number_to_human_size('x') + #assert_nil number_to_human_size('x') # fails due to API consolidation assert_nil number_to_human_size(nil) end @@ -120,4 +118,10 @@ class NumberHelperTest < ActionView::TestCase assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4) assert_equal '10 KB', number_to_human_size(10.000.kilobytes, :precision => 4) end + + def test_number_to_human_size_with_custom_delimiter_and_separator + assert_equal '1,01 KB', number_to_human_size(1.0123.kilobytes, :precision => 2, :separator => ',') + assert_equal '1,01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4, :separator => ',') + assert_equal '1.000,1 TB', number_to_human_size(1000.1.terabytes, :delimiter => '.', :separator => ',') + end end -- cgit v1.2.3 From a065b764e5beace51c2fca305d1ed5b8aa46b762 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 29 Jul 2008 22:04:07 -0500 Subject: Reapply 'cab168ac' because it was accidentally patched over in '10d9fe4b' --- actionpack/lib/action_view/helpers/text_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 3c9f7230c3..022edf23c8 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -558,7 +558,7 @@ module ActionView [-\w]+ # subdomain or domain (?:\.[-\w]+)* # remaining subdomains or domain (?::\d+)? # port - (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))+)?)* # path + (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$])))*)* # path (?:\?[\w\+@%&=.;-]+)? # query string (?:\#[\w\-]*)? # trailing anchor ) -- cgit v1.2.3 From c8e80f6389b45134c0514dde6736488cf5507765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 30 Jul 2008 01:41:51 -0700 Subject: Initializer skips prepare_dispatcher if Action Controller isn't in use. [#721 state:resolved] --- railties/lib/initializer.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 782fbebec2..8a7461abf5 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -523,6 +523,7 @@ Run `rake gems:install` to install the missing gems. end def prepare_dispatcher + return unless configuration.frameworks.include?(:action_controller) require 'dispatcher' unless defined?(::Dispatcher) Dispatcher.define_dispatcher_callbacks(configuration.cache_classes) Dispatcher.new(RAILS_DEFAULT_LOGGER).send :run_callbacks, :prepare_dispatch -- cgit v1.2.3 From eaab895f83276674891227c656df9b4cebc50200 Mon Sep 17 00:00:00 2001 From: miloops Date: Tue, 22 Jul 2008 11:13:38 -0300 Subject: Prototype helpers should generate Element.insert instead of Insertion.new, which has been deprecated in Prototype 1.6. --- .../assertions/selector_assertions.rb | 26 +++++++++++++--------- .../lib/action_view/helpers/prototype_helper.rb | 12 +++++----- actionpack/test/template/prototype_helper_test.rb | 12 +++++----- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb index 70b0ed53e7..9114894b1d 100644 --- a/actionpack/lib/action_controller/assertions/selector_assertions.rb +++ b/actionpack/lib/action_controller/assertions/selector_assertions.rb @@ -407,6 +407,7 @@ module ActionController if rjs_type == :insert arg = args.shift + position = arg insertion = "insert_#{arg}".to_sym raise ArgumentError, "Unknown RJS insertion type #{arg}" unless RJS_STATEMENTS[insertion] statement = "(#{RJS_STATEMENTS[insertion]})" @@ -418,6 +419,7 @@ module ActionController else statement = "#{RJS_STATEMENTS[:any]}" end + position ||= Regexp.new(RJS_INSERTIONS.join('|')) # Next argument we're looking for is the element identifier. If missing, we pick # any element. @@ -434,9 +436,14 @@ module ActionController Regexp.new("\\$\\(\"#{id}\"\\)#{statement}\\(#{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE) when :remove, :show, :hide, :toggle Regexp.new("#{statement}\\(\"#{id}\"\\)") - else - Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE) - end + when :replace, :replace_html + Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)") + when :insert, :insert_html + Regexp.new("Element.insert\\(\\\"#{id}\\\", \\{ #{position}: #{RJS_PATTERN_HTML} \\}\\);") + else + Regexp.union(Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)"), + Regexp.new("Element.insert\\(\\\"#{id}\\\", \\{ #{position}: #{RJS_PATTERN_HTML} \\}\\);")) + end # Duplicate the body since the next step involves destroying it. matches = nil @@ -445,7 +452,7 @@ module ActionController matches = @response.body.match(pattern) else @response.body.gsub(pattern) do |match| - html = unescape_rjs($2) + html = unescape_rjs(match) matches ||= [] matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? } "" @@ -585,17 +592,16 @@ module ActionController :hide => /Element\.hide/, :toggle => /Element\.toggle/ } + RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})") + RJS_PATTERN_HTML = /"((\\"|[^"])*)"/ RJS_INSERTIONS = [:top, :bottom, :before, :after] RJS_INSERTIONS.each do |insertion| - RJS_STATEMENTS["insert_#{insertion}".to_sym] = Regexp.new(Regexp.quote("new Insertion.#{insertion.to_s.camelize}")) + RJS_STATEMENTS["insert_#{insertion}".to_sym] = /Element.insert\(\"([^\"]*)\", \{ #{insertion.to_s.downcase}: #{RJS_PATTERN_HTML} \}\);/ end - RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})") RJS_STATEMENTS[:insert_html] = Regexp.new(RJS_INSERTIONS.collect do |insertion| - Regexp.quote("new Insertion.#{insertion.to_s.camelize}") + /Element.insert\(\"([^\"]*)\", \{ #{insertion.to_s.downcase}: #{RJS_PATTERN_HTML} \}\);/ end.join('|')) - RJS_PATTERN_HTML = /"((\\"|[^"])*)"/ - RJS_PATTERN_EVERYTHING = Regexp.new("#{RJS_STATEMENTS[:any]}\\(\"([^\"]*)\", #{RJS_PATTERN_HTML}\\)", - Regexp::MULTILINE) + RJS_PATTERN_EVERYTHING = Regexp.new("#{RJS_STATEMENTS[:any]}\\(\"([^\"]*)\", #{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE) RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/ end diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index cb4b53a9f7..5194f00382 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -608,7 +608,7 @@ module ActionView # Example: # # # Generates: - # # new Insertion.Bottom("list", "
  • Some item
  • "); + # # new Element.insert("list", { bottom:
  • Some item
  • " }); # # new Effect.Highlight("list"); # # ["status-indicator", "cancel-link"].each(Element.hide); # update_page do |page| @@ -741,16 +741,16 @@ module ActionView # # # Insert the rendered 'navigation' partial just before the DOM # # element with ID 'content'. - # # Generates: new Insertion.Before("content", "-- Contents of 'navigation' partial --"); + # # Generates: Element.insert("content", { before: "-- Contents of 'navigation' partial --" }); # page.insert_html :before, 'content', :partial => 'navigation' # # # Add a list item to the bottom of the
      with ID 'list'. - # # Generates: new Insertion.Bottom("list", "
    • Last item
    • "); + # # Generates: Element.insert("list", { bottom: "
    • Last item
    • " }); # page.insert_html :bottom, 'list', '
    • Last item
    • ' # def insert_html(position, id, *options_for_render) - insertion = position.to_s.camelize - call "new Insertion.#{insertion}", id, render(*options_for_render) + content = javascript_object_for(render(*options_for_render)) + record "Element.insert(\"#{id}\", { #{position.to_s.downcase}: #{content} });" end # Replaces the inner HTML of the DOM element with the given +id+. @@ -1054,7 +1054,7 @@ module ActionView js_options['asynchronous'] = options[:type] != :synchronous js_options['method'] = method_option_to_s(options[:method]) if options[:method] - js_options['insertion'] = "Insertion.#{options[:position].to_s.camelize}" if options[:position] + js_options['insertion'] = options[:position].to_s.downcase if options[:position] js_options['evalScripts'] = options[:script].nil? || options[:script] if options[:form] diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 92cc85703b..eb3517ef91 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -287,13 +287,13 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest end def test_insert_html_with_string - assert_equal 'new Insertion.Top("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");', + assert_equal 'Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });', @generator.insert_html(:top, 'element', '

      This is a test

      ') - assert_equal 'new Insertion.Bottom("element", "\\u003Cp\u003EThis is a test\\u003C/p\u003E");', + assert_equal 'Element.insert("element", { bottom: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });', @generator.insert_html(:bottom, 'element', '

      This is a test

      ') - assert_equal 'new Insertion.Before("element", "\\u003Cp\u003EThis is a test\\u003C/p\u003E");', + assert_equal 'Element.insert("element", { before: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });', @generator.insert_html(:before, 'element', '

      This is a test

      ') - assert_equal 'new Insertion.After("element", "\\u003Cp\u003EThis is a test\\u003C/p\u003E");', + assert_equal 'Element.insert("element", { after: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });', @generator.insert_html(:after, 'element', '

      This is a test

      ') end @@ -366,8 +366,8 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest @generator.replace_html('baz', '

      This is a test

      ') assert_equal <<-EOS.chomp, @generator.to_s -new Insertion.Top("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E"); -new Insertion.Bottom("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E"); +Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" }); +Element.insert("element", { bottom: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" }); ["foo", "bar"].each(Element.remove); Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E"); EOS -- cgit v1.2.3 From c4038764d2b4c05178cceb22066e0ece59fe49d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 30 Jul 2008 01:49:49 -0700 Subject: Initializer requires ERB explicitly instead of assuming Action Pack loaded it. [#722 state:resolved] --- railties/lib/initializer.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 8a7461abf5..44c24e85f9 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -767,6 +767,7 @@ Run `rake gems:install` to install the missing gems. # contents of the file are processed via ERB before being sent through # YAML::load. def database_configuration + require 'erb' YAML::load(ERB.new(IO.read(database_configuration_file)).result) end -- cgit v1.2.3 From 2617d0dc5ced4b354bff9633bddafdf80ad5a711 Mon Sep 17 00:00:00 2001 From: miloops Date: Tue, 29 Jul 2008 23:14:56 -0300 Subject: Performance: grouping helpers should use yield instead of block as argument. [#723 state:resolved] --- activesupport/lib/active_support/core_ext/array/grouping.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/array/grouping.rb b/activesupport/lib/active_support/core_ext/array/grouping.rb index df37afb053..dd1484f8fa 100644 --- a/activesupport/lib/active_support/core_ext/array/grouping.rb +++ b/activesupport/lib/active_support/core_ext/array/grouping.rb @@ -19,7 +19,7 @@ module ActiveSupport #:nodoc: # %w(1 2 3).in_groups_of(2, false) {|g| p g} # ["1", "2"] # ["3"] - def in_groups_of(number, fill_with = nil, &block) + def in_groups_of(number, fill_with = nil) if fill_with == false collection = self else @@ -31,7 +31,7 @@ module ActiveSupport #:nodoc: end if block_given? - collection.each_slice(number, &block) + collection.each_slice(number) { |slice| yield(slice) } else returning [] do |groups| collection.each_slice(number) { |group| groups << group } @@ -87,11 +87,11 @@ module ActiveSupport #:nodoc: # # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]] # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]] - def split(value = nil, &block) - block ||= Proc.new { |e| e == value } + def split(value = nil) + using_block = block_given? inject([[]]) do |results, element| - if block.call(element) + if (using_block && yield(element)) || (value == element) results << [] else results.last << element -- cgit v1.2.3 From f64bd2ca85595f94cbbe809f51a52cdb9b68af19 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Thu, 31 Jul 2008 09:45:17 +0200 Subject: Ensure dbconsole includes the -p parameter to mysql as intended --- railties/lib/commands/dbconsole.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/lib/commands/dbconsole.rb b/railties/lib/commands/dbconsole.rb index 17acb7b68f..442526ae32 100644 --- a/railties/lib/commands/dbconsole.rb +++ b/railties/lib/commands/dbconsole.rb @@ -41,6 +41,8 @@ when "mysql" if config['password'] && include_password args << "--password=#{config['password']}" + elsif config['password'] && !config['password'].empty? + args << "-p" end args << config['database'] -- cgit v1.2.3 From 108db00aa90fe266564483ab301cf0669cae600f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 31 Jul 2008 15:56:46 +0100 Subject: Raise UnknownAttributeError when unknown attributes are supplied via mass assignment --- activerecord/lib/active_record/base.rb | 10 +++++++++- activerecord/test/cases/base_test.rb | 8 ++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 9cb64223e2..29c2995334 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -122,6 +122,10 @@ module ActiveRecord #:nodoc: class MissingAttributeError < NoMethodError end + # Raised when unknown attributes are supplied via mass assignment. + class UnknownAttributeError < NoMethodError + end + # Raised when an error occurred while doing a mass assignment to an attribute through the # attributes= method. The exception has an +attribute+ property that is the name of the # offending attribute. @@ -2400,7 +2404,11 @@ module ActiveRecord #:nodoc: attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes attributes.each do |k, v| - k.include?("(") ? multi_parameter_attributes << [ k, v ] : send(k + "=", v) + if k.include?("(") + multi_parameter_attributes << [ k, v ] + else + respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}") + end end assign_multiparameter_attributes(multi_parameter_attributes) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 9e4f268db7..e6d1b5ddfd 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -904,6 +904,14 @@ class BasicsTest < ActiveRecord::TestCase assert_nil keyboard.id end + def test_mass_assigning_invalid_attribute + firm = Firm.new + + assert_raises(ActiveRecord::UnknownAttributeError) do + firm.attributes = { "id" => 5, "type" => "Client", "i_dont_even_exist" => 20 } + end + end + def test_mass_assignment_protection_on_defaults firm = Firm.new firm.attributes = { "id" => 5, "type" => "Client" } -- cgit v1.2.3 From fb5cc19707582fa61ca3e426697cc2b00e9e5ffa Mon Sep 17 00:00:00 2001 From: miloops Date: Thu, 31 Jul 2008 10:57:50 -0300 Subject: Fix HasManyThroughAssociationsTest tests. [#733 state:resolved] Signed-off-by: Pratik Naik --- .../cases/associations/has_many_through_associations_test.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index be5170f830..d51a3c7e1c 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -2,15 +2,18 @@ require "cases/helper" require 'models/post' require 'models/person' require 'models/reader' +require 'models/comment' class HasManyThroughAssociationsTest < ActiveRecord::TestCase - fixtures :posts, :readers, :people + fixtures :posts, :readers, :people, :comments def test_associate_existing assert_queries(2) { posts(:thinking);people(:david) } - + + posts(:thinking).people + assert_queries(1) do - posts(:thinking).people << people(:david) + posts(:thinking).people << people(:david) end assert_queries(1) do -- cgit v1.2.3 From 896a3b9ab8dc02639ffa0b1dbf85011e1f3eda9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Thu, 31 Jul 2008 19:52:35 +0300 Subject: Fixed test_joins_with_namespaced_model_should_use_correct_type for postgresql Signed-off-by: Michael Koziarski --- activerecord/test/cases/associations/has_many_associations_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index f8b8b1f96d..47e4b3527d 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1007,7 +1007,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase firm.clients.create({ :name => 'Some Client' }) stats = Namespaced::Firm.find(firm.id, { - :select => "#{Namespaced::Firm.table_name}.*, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients", + :select => "#{Namespaced::Firm.table_name}.id, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients", :joins => :clients, :group => "#{Namespaced::Firm.table_name}.id" }) -- cgit v1.2.3 From 68b207b087588fc9a2cc8edb842408e3be5ba9ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Thu, 31 Jul 2008 19:26:18 +0300 Subject: Cast value to string in validates_uniqueness_of if the column is of text type This fixes an error for postgresql where "text_column = 100" fails in version 8.3 Signed-off-by: Michael Koziarski --- activerecord/lib/active_record/validations.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index b957ee3b9e..52f674847e 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -625,7 +625,13 @@ module ActiveRecord # class (which has a database table to query from). finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? } - if value.nil? || (configuration[:case_sensitive] || !finder_class.columns_hash[attr_name.to_s].text?) + is_text_column = finder_class.columns_hash[attr_name.to_s].text? + + if !value.nil? && is_text_column + value = value.to_s + end + + if value.nil? || (configuration[:case_sensitive] || !is_text_column) condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}" condition_params = [value] else -- cgit v1.2.3 From 030d5854adcf35e6620d667a93a922f5d91725d8 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 31 Jul 2008 13:42:28 -0500 Subject: Turn cache_classes on by default [#645 state:resolved] --- railties/CHANGELOG | 2 ++ railties/lib/initializer.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 5ff1867568..6df7c568dc 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Turn cache_classes on by default [Josh Peek] + * Added configurable eager load paths. Defaults to app/models, app/controllers, and app/helpers [Josh Peek] * Introduce simple internationalization support. [Ruby i18n team] diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 44c24e85f9..88341b9d73 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -903,7 +903,7 @@ Run `rake gems:install` to install the missing gems. end def default_cache_classes - false + true end def default_whiny_nils -- cgit v1.2.3 From cb68b21a52a12b0773f0b4dac1c9c673c93ba355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Thu, 31 Jul 2008 21:51:50 +0300 Subject: Fixed negative default integer parsing for Postgresql 8.3.3 Signed-off-by: Michael Koziarski --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 6a20f41a4b..856435517a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -182,8 +182,8 @@ module ActiveRecord def self.extract_value_from_default(default) case default # Numeric types - when /\A-?\d+(\.\d*)?\z/ - default + when /\A\(?(-?\d+(\.\d*)?\)?)\z/ + $1 # Character types when /\A'(.*)'::(?:character varying|bpchar|text)\z/m $1 -- cgit v1.2.3 From f3da46effae9f23f6ea473f7296e9c0c9bb49fce Mon Sep 17 00:00:00 2001 From: miloops Date: Thu, 31 Jul 2008 16:09:18 -0300 Subject: In javascript helpers option[:type] = :synchronous should work as described in docs. Signed-off-by: Michael Koziarski --- actionpack/lib/action_view/helpers/prototype_helper.rb | 2 +- actionpack/test/template/prototype_helper_test.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 5194f00382..4c3a8311a5 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -111,7 +111,7 @@ module ActionView (100..599).to_a) AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url, :asynchronous, :method, :insertion, :position, - :form, :with, :update, :script ]).merge(CALLBACKS) + :form, :with, :update, :script, :type ]).merge(CALLBACKS) end # Returns a link to a remote action defined by options[:url] diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index eb3517ef91..abc9f930dd 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -77,6 +77,8 @@ class PrototypeHelperTest < PrototypeHelperBaseTest link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot" }) assert_dom_equal %(Remote outauthor), link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' }) + assert_dom_equal %(Remote outauthor), + link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :type => :synchronous) end def test_link_to_remote_html_options @@ -429,6 +431,8 @@ Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E"); def test_sortable assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("blah")})}});), @generator.sortable('blah', :url => { :action => "order" }) + assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:false, evalScripts:true, parameters:Sortable.serialize("blah")})}});), + @generator.sortable('blah', :url => { :action => "order" }, :type => :synchronous) end def test_draggable @@ -439,6 +443,8 @@ Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E"); def test_drop_receiving assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});), @generator.drop_receiving('blah', :url => { :action => "order" }) + assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:false, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});), + @generator.drop_receiving('blah', :url => { :action => "order" }, :type => :synchronous) end def test_collection_first_and_last -- cgit v1.2.3 From f7eaab96d332528253d14581f747dd4b1b378a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Thu, 31 Jul 2008 22:18:14 +0300 Subject: validates_uniqueness_of uses database case sensitivity support instead of using ruby Signed-off-by: Michael Koziarski --- .../abstract/database_statements.rb | 4 +++ .../connection_adapters/mysql_adapter.rb | 4 +++ activerecord/lib/active_record/validations.rb | 42 ++++++++-------------- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 5358491cde..aaf9e2e73f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -149,6 +149,10 @@ module ActiveRecord "INSERT INTO #{quote_table_name(table_name)} VALUES(DEFAULT)" end + def case_sensitive_equality_operator + "=" + end + protected # Returns an array of record hashes with the column names as keys and # column values as values. diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 35b9ed4746..204ebaa2e2 100755 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -511,6 +511,10 @@ module ActiveRecord keys.length == 1 ? [keys.first, nil] : nil end + def case_sensitive_equality_operator + "= BINARY" + end + private def connect @connection.reconnect = true if @connection.respond_to?(:reconnect=) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 52f674847e..e7a9676394 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -627,17 +627,23 @@ module ActiveRecord is_text_column = finder_class.columns_hash[attr_name.to_s].text? - if !value.nil? && is_text_column - value = value.to_s + if value.nil? + comparison_operator = "IS ?" + else + comparison_operator = "#{connection.case_sensitive_equality_operator} ?" + + if is_text_column + value = value.to_s + end end + sql_attribute = "#{record.class.quoted_table_name}.#{connection.quote_column_name(attr_name)}" + if value.nil? || (configuration[:case_sensitive] || !is_text_column) - condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}" + condition_sql = "#{sql_attribute} #{comparison_operator}" condition_params = [value] else - # sqlite has case sensitive SELECT query, while MySQL/Postgresql don't. - # Hence, this is needed only for sqlite. - condition_sql = "LOWER(#{record.class.quoted_table_name}.#{attr_name}) #{attribute_condition(value)}" + condition_sql = "LOWER(#{sql_attribute}) #{comparison_operator}" condition_params = [value.downcase] end @@ -654,28 +660,10 @@ module ActiveRecord condition_params << record.send(:id) end - results = finder_class.with_exclusive_scope do - connection.select_all( - construct_finder_sql( - :select => "#{connection.quote_column_name(attr_name)}", - :from => "#{finder_class.quoted_table_name}", - :conditions => [condition_sql, *condition_params] - ) - ) - end - - unless results.length.zero? - found = true - - # As MySQL/Postgres don't have case sensitive SELECT queries, we try to find duplicate - # column in ruby when case sensitive option - if configuration[:case_sensitive] && finder_class.columns_hash[attr_name.to_s].text? - found = results.any? { |a| a[attr_name.to_s] == value.to_s } - end - - if found + finder_class.with_exclusive_scope do + if finder_class.exists?([condition_sql, *condition_params]) message = record.errors.generate_message(attr_name, :taken, :default => configuration[:message]) - record.errors.add(attr_name, message) + record.errors.add(attr_name, message) end end end -- cgit v1.2.3 From 656f0e7c6c9a305abaf9f9b7fb80479b6f94efce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Thu, 31 Jul 2008 16:36:23 -0500 Subject: Fix file permissions Signed-off-by: Joshua Peek --- actionmailer/README | 0 actionmailer/Rakefile | 0 actionmailer/lib/action_mailer.rb | 0 actionmailer/test/mail_service_test.rb | 0 actionpack/lib/action_controller.rb | 0 actionpack/lib/action_controller/base.rb | 0 actionpack/lib/action_controller/request.rb | 0 actionpack/lib/action_controller/request_profiler.rb | 0 actionpack/lib/action_controller/response.rb | 0 actionpack/lib/action_controller/session/drb_server.rb | 0 actionpack/lib/action_view/helpers/date_helper.rb | 0 actionpack/test/controller/cgi_test.rb | 0 actionpack/test/controller/redirect_test.rb | 0 actionpack/test/controller/session/cookie_store_test.rb | 0 actionpack/test/template/date_helper_test.rb | 0 activemodel/Rakefile | 0 activerecord/README | 0 activerecord/Rakefile | 0 activerecord/lib/active_record.rb | 0 activerecord/lib/active_record/associations.rb | 0 activerecord/lib/active_record/associations/belongs_to_association.rb | 0 .../lib/active_record/associations/belongs_to_polymorphic_association.rb | 0 activerecord/lib/active_record/associations/has_one_association.rb | 0 activerecord/lib/active_record/base.rb | 0 activerecord/lib/active_record/callbacks.rb | 0 activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 0 activerecord/lib/active_record/connection_adapters/mysql_adapter.rb | 0 activerecord/lib/active_record/fixtures.rb | 0 activerecord/lib/active_record/validations.rb | 0 activerecord/test/cases/associations/belongs_to_associations_test.rb | 0 activerecord/test/cases/associations/has_one_associations_test.rb | 0 activerecord/test/cases/associations_test.rb | 0 activerecord/test/cases/attribute_methods_test.rb | 0 activerecord/test/cases/base_test.rb | 0 activerecord/test/cases/deprecated_finder_test.rb | 0 activerecord/test/cases/fixtures_test.rb | 0 activerecord/test/cases/inheritance_test.rb | 0 activerecord/test/cases/lifecycle_test.rb | 0 activerecord/test/cases/readonly_test.rb | 0 activerecord/test/cases/unconnected_test.rb | 0 activerecord/test/cases/validations_test.rb | 0 activerecord/test/models/company.rb | 0 activerecord/test/models/reply.rb | 0 activerecord/test/models/topic.rb | 0 activesupport/lib/active_support/multibyte/generators/generate_tables.rb | 0 railties/bin/about | 0 railties/bin/console | 0 railties/bin/destroy | 0 railties/bin/generate | 0 railties/bin/performance/benchmarker | 0 railties/bin/performance/profiler | 0 railties/bin/performance/request | 0 railties/bin/plugin | 0 railties/bin/process/inspector | 0 railties/bin/process/reaper | 0 railties/bin/process/spawner | 0 railties/bin/runner | 0 railties/bin/server | 0 railties/configs/apache.conf | 0 railties/dispatches/gateway.cgi | 0 railties/lib/commands/ncgi/listener | 0 railties/lib/commands/ncgi/tracker | 0 .../lib/rails_generator/generators/components/plugin/templates/Rakefile | 0 63 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 actionmailer/README mode change 100755 => 100644 actionmailer/Rakefile mode change 100755 => 100644 actionmailer/lib/action_mailer.rb mode change 100755 => 100644 actionmailer/test/mail_service_test.rb mode change 100755 => 100644 actionpack/lib/action_controller.rb mode change 100755 => 100644 actionpack/lib/action_controller/base.rb mode change 100755 => 100644 actionpack/lib/action_controller/request.rb mode change 100755 => 100644 actionpack/lib/action_controller/request_profiler.rb mode change 100755 => 100644 actionpack/lib/action_controller/response.rb mode change 100644 => 100755 actionpack/lib/action_controller/session/drb_server.rb mode change 100755 => 100644 actionpack/lib/action_view/helpers/date_helper.rb mode change 100755 => 100644 actionpack/test/controller/cgi_test.rb mode change 100755 => 100644 actionpack/test/controller/redirect_test.rb mode change 100755 => 100644 actionpack/test/controller/session/cookie_store_test.rb mode change 100755 => 100644 actionpack/test/template/date_helper_test.rb mode change 100644 => 100755 activemodel/Rakefile mode change 100755 => 100644 activerecord/README mode change 100755 => 100644 activerecord/Rakefile mode change 100755 => 100644 activerecord/lib/active_record.rb mode change 100755 => 100644 activerecord/lib/active_record/associations.rb mode change 100755 => 100644 activerecord/lib/active_record/associations/belongs_to_association.rb mode change 100755 => 100644 activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb mode change 100755 => 100644 activerecord/lib/active_record/associations/has_one_association.rb mode change 100755 => 100644 activerecord/lib/active_record/base.rb mode change 100755 => 100644 activerecord/lib/active_record/callbacks.rb mode change 100755 => 100644 activerecord/lib/active_record/connection_adapters/abstract_adapter.rb mode change 100755 => 100644 activerecord/lib/active_record/connection_adapters/mysql_adapter.rb mode change 100755 => 100644 activerecord/lib/active_record/fixtures.rb mode change 100755 => 100644 activerecord/lib/active_record/validations.rb mode change 100755 => 100644 activerecord/test/cases/associations/belongs_to_associations_test.rb mode change 100755 => 100644 activerecord/test/cases/associations/has_one_associations_test.rb mode change 100755 => 100644 activerecord/test/cases/associations_test.rb mode change 100755 => 100644 activerecord/test/cases/attribute_methods_test.rb mode change 100755 => 100644 activerecord/test/cases/base_test.rb mode change 100755 => 100644 activerecord/test/cases/deprecated_finder_test.rb mode change 100755 => 100644 activerecord/test/cases/fixtures_test.rb mode change 100755 => 100644 activerecord/test/cases/inheritance_test.rb mode change 100755 => 100644 activerecord/test/cases/lifecycle_test.rb mode change 100755 => 100644 activerecord/test/cases/readonly_test.rb mode change 100755 => 100644 activerecord/test/cases/unconnected_test.rb mode change 100755 => 100644 activerecord/test/cases/validations_test.rb mode change 100755 => 100644 activerecord/test/models/company.rb mode change 100755 => 100644 activerecord/test/models/reply.rb mode change 100755 => 100644 activerecord/test/models/topic.rb mode change 100644 => 100755 activesupport/lib/active_support/multibyte/generators/generate_tables.rb mode change 100644 => 100755 railties/bin/about mode change 100644 => 100755 railties/bin/console mode change 100644 => 100755 railties/bin/destroy mode change 100644 => 100755 railties/bin/generate mode change 100644 => 100755 railties/bin/performance/benchmarker mode change 100644 => 100755 railties/bin/performance/profiler mode change 100644 => 100755 railties/bin/performance/request mode change 100644 => 100755 railties/bin/plugin mode change 100644 => 100755 railties/bin/process/inspector mode change 100644 => 100755 railties/bin/process/reaper mode change 100644 => 100755 railties/bin/process/spawner mode change 100644 => 100755 railties/bin/runner mode change 100644 => 100755 railties/bin/server mode change 100755 => 100644 railties/configs/apache.conf mode change 100644 => 100755 railties/dispatches/gateway.cgi mode change 100644 => 100755 railties/lib/commands/ncgi/listener mode change 100644 => 100755 railties/lib/commands/ncgi/tracker mode change 100755 => 100644 railties/lib/rails_generator/generators/components/plugin/templates/Rakefile diff --git a/actionmailer/README b/actionmailer/README old mode 100755 new mode 100644 diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile old mode 100755 new mode 100644 diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb old mode 100755 new mode 100644 diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb old mode 100755 new mode 100644 diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb old mode 100755 new mode 100644 diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb old mode 100755 new mode 100644 diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb old mode 100755 new mode 100644 diff --git a/actionpack/lib/action_controller/request_profiler.rb b/actionpack/lib/action_controller/request_profiler.rb old mode 100755 new mode 100644 diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb old mode 100755 new mode 100644 diff --git a/actionpack/lib/action_controller/session/drb_server.rb b/actionpack/lib/action_controller/session/drb_server.rb old mode 100644 new mode 100755 diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb old mode 100755 new mode 100644 diff --git a/actionpack/test/controller/cgi_test.rb b/actionpack/test/controller/cgi_test.rb old mode 100755 new mode 100644 diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb old mode 100755 new mode 100644 diff --git a/actionpack/test/controller/session/cookie_store_test.rb b/actionpack/test/controller/session/cookie_store_test.rb old mode 100755 new mode 100644 diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb old mode 100755 new mode 100644 diff --git a/activemodel/Rakefile b/activemodel/Rakefile old mode 100644 new mode 100755 diff --git a/activerecord/README b/activerecord/README old mode 100755 new mode 100644 diff --git a/activerecord/Rakefile b/activerecord/Rakefile old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb old mode 100755 new mode 100644 diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/deprecated_finder_test.rb b/activerecord/test/cases/deprecated_finder_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/lifecycle_test.rb b/activerecord/test/cases/lifecycle_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/unconnected_test.rb b/activerecord/test/cases/unconnected_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb old mode 100755 new mode 100644 diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb old mode 100755 new mode 100644 diff --git a/activesupport/lib/active_support/multibyte/generators/generate_tables.rb b/activesupport/lib/active_support/multibyte/generators/generate_tables.rb old mode 100644 new mode 100755 diff --git a/railties/bin/about b/railties/bin/about old mode 100644 new mode 100755 diff --git a/railties/bin/console b/railties/bin/console old mode 100644 new mode 100755 diff --git a/railties/bin/destroy b/railties/bin/destroy old mode 100644 new mode 100755 diff --git a/railties/bin/generate b/railties/bin/generate old mode 100644 new mode 100755 diff --git a/railties/bin/performance/benchmarker b/railties/bin/performance/benchmarker old mode 100644 new mode 100755 diff --git a/railties/bin/performance/profiler b/railties/bin/performance/profiler old mode 100644 new mode 100755 diff --git a/railties/bin/performance/request b/railties/bin/performance/request old mode 100644 new mode 100755 diff --git a/railties/bin/plugin b/railties/bin/plugin old mode 100644 new mode 100755 diff --git a/railties/bin/process/inspector b/railties/bin/process/inspector old mode 100644 new mode 100755 diff --git a/railties/bin/process/reaper b/railties/bin/process/reaper old mode 100644 new mode 100755 diff --git a/railties/bin/process/spawner b/railties/bin/process/spawner old mode 100644 new mode 100755 diff --git a/railties/bin/runner b/railties/bin/runner old mode 100644 new mode 100755 diff --git a/railties/bin/server b/railties/bin/server old mode 100644 new mode 100755 diff --git a/railties/configs/apache.conf b/railties/configs/apache.conf old mode 100755 new mode 100644 diff --git a/railties/dispatches/gateway.cgi b/railties/dispatches/gateway.cgi old mode 100644 new mode 100755 diff --git a/railties/lib/commands/ncgi/listener b/railties/lib/commands/ncgi/listener old mode 100644 new mode 100755 diff --git a/railties/lib/commands/ncgi/tracker b/railties/lib/commands/ncgi/tracker old mode 100644 new mode 100755 diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile b/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile old mode 100755 new mode 100644 -- cgit v1.2.3 From 0b9bfbdebf402f4a149359a069dbeb05ea989b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Thu, 31 Jul 2008 16:39:48 -0500 Subject: Use "/usr/bin/env ruby" instead of "/usr/local/bin/ruby" Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/session/drb_server.rb | 14 +++++++------- railties/dispatches/dispatch.fcgi | 2 +- railties/dispatches/dispatch.rb | 4 ++-- railties/dispatches/gateway.cgi | 4 ++-- railties/lib/commands/ncgi/listener | 4 ++-- railties/lib/commands/ncgi/tracker | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/actionpack/lib/action_controller/session/drb_server.rb b/actionpack/lib/action_controller/session/drb_server.rb index 6f90db6747..2caa27f62a 100755 --- a/actionpack/lib/action_controller/session/drb_server.rb +++ b/actionpack/lib/action_controller/session/drb_server.rb @@ -1,8 +1,8 @@ -#!/usr/local/bin/ruby -w - -# This is a really simple session storage daemon, basically just a hash, +#!/usr/bin/env ruby + +# This is a really simple session storage daemon, basically just a hash, # which is enabled for DRb access. - + require 'drb' session_hash = Hash.new @@ -14,13 +14,13 @@ class < Date: Thu, 31 Jul 2008 16:35:17 -0700 Subject: load_application_classes requires files relative to the load path and without .rb extension, including .rb files in subdirectories --- railties/lib/initializer.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 44c24e85f9..611e348acb 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -349,8 +349,9 @@ Run `rake gems:install` to install the missing gems. def load_application_classes if configuration.cache_classes configuration.eager_load_paths.each do |load_path| - Dir.glob("#{load_path}/*.rb").each do |file| - require_dependency file + matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/ + Dir.glob("#{load_path}/**/*.rb").each do |file| + require_dependency file.sub(matcher, '\1') end end end -- cgit v1.2.3 From ad4553587efb8e758349b3f163e365781e4c6ad1 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 31 Jul 2008 17:50:53 -0700 Subject: List available actions in UnknownAction exception message --- actionpack/lib/action_controller/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index ac10a956f3..5689a9825e 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1208,7 +1208,7 @@ module ActionController #:nodoc: elsif template_exists? && template_public? default_render else - raise UnknownAction, "No action responded to #{action_name}", caller + raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.to_a.sort.to_sentence}", caller end end -- cgit v1.2.3 From 909a7f430ba37cd7a54ae11a7af529bd949fa31c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 31 Jul 2008 17:51:43 -0700 Subject: Ensure mailer view path is loaded when it's assigned. Path#[] raises if it isn't loaded. --- actionmailer/lib/action_mailer/base.rb | 2 +- actionpack/lib/action_view/paths.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index a43296461b..88d34a58d5 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -427,7 +427,7 @@ module ActionMailer #:nodoc: def template_root=(root) root = ActionView::PathSet::Path.new(root) if root.is_a?(String) - write_inheritable_attribute(:template_root, root) + write_inheritable_attribute(:template_root, root.load) end end diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index a37706faee..d97f963540 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -42,6 +42,7 @@ module ActionView #:nodoc: end def [](path) + raise "Unloaded view path! #{@path}" unless @loaded @paths[path] end @@ -51,6 +52,7 @@ module ActionView #:nodoc: def load reload! unless loaded? + self end # Rebuild load path directory cache -- cgit v1.2.3 From cb21db1a334e6ca2695d4e7183b1bdce204b9eb3 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 31 Jul 2008 20:09:10 -0500 Subject: Treat ActionMailer template_root as a view path set internally to avoid inheritance and dupping issues --- actionmailer/lib/action_mailer/base.rb | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 88d34a58d5..fa29ae2446 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -250,7 +250,7 @@ module ActionMailer #:nodoc: private_class_method :new #:nodoc: - class_inheritable_accessor :template_root + class_inheritable_accessor :view_paths cattr_accessor :logger cattr_accessor :template_extensions @@ -425,9 +425,12 @@ module ActionMailer #:nodoc: template_extensions << extension end + def template_root + self.view_paths && self.view_paths.first + end + def template_root=(root) - root = ActionView::PathSet::Path.new(root) if root.is_a?(String) - write_inheritable_attribute(:template_root, root.load) + self.view_paths = ActionView::Base.process_view_paths(root) end end @@ -541,12 +544,20 @@ module ActionMailer #:nodoc: initialize_template_class(body).render(opts) end + def template_root + self.class.template_root + end + + def template_root=(root) + self.class.template_root = root + end + def template_path "#{template_root}/#{mailer_name}" end def initialize_template_class(assigns) - ActionView::Base.new(template_root, assigns, self) + ActionView::Base.new(view_paths, assigns, self) end def sort_parts(parts, order = []) -- cgit v1.2.3