X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;ds=sidebyside;f=vendor%2Frails%2Factionpack%2Flib%2Faction_controller%2Frouting%2Foptimisations.rb;fp=vendor%2Frails%2Factionpack%2Flib%2Faction_controller%2Frouting%2Foptimisations.rb;h=4522ebcb1abf3f574704ef1aea1ac94c5088f913;hb=d115f2e23823271635bad69229a42cd8ac68debe;hp=0000000000000000000000000000000000000000;hpb=37cb670bf3ddde90b214e591f100ed4446469484;p=depot.git diff --git a/vendor/rails/actionpack/lib/action_controller/routing/optimisations.rb b/vendor/rails/actionpack/lib/action_controller/routing/optimisations.rb new file mode 100644 index 0000000..4522ebc --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/routing/optimisations.rb @@ -0,0 +1,130 @@ +module ActionController + module Routing + # 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 + # 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 + module Optimisation + def generate_optimisation_block(route, kind) + return "" unless route.optimise? + OPTIMISERS.inject("") do |memo, klazz| + memo << klazz.new(route, kind).source_code + memo + end + end + + class Optimiser + attr_reader :route, :kind + GLOBAL_GUARD_CONDITIONS = [ + "(!defined?(default_url_options) || default_url_options.blank?)", + "(!defined?(controller.default_url_options) || controller.default_url_options.blank?)", + "defined?(request)", + "request" + ] + + def initialize(route, kind) + @route = route + @kind = kind + end + + def guard_conditions + ["false"] + end + + def generation_code + 'nil' + end + + def source_code + if applicable? + guard_condition = (GLOBAL_GUARD_CONDITIONS + guard_conditions).join(" && ") + "return #{generation_code} if #{guard_condition}\n" + else + "\n" + end + end + + # Temporarily disabled :url optimisation pending proper solution to + # Issues around request.host etc. + def applicable? + true + end + end + + # Given a route + # + # map.person '/people/:id' + # + # If the user calls person_url(@person), we can simply + # return a string like "/people/#{@person.to_param}" + # rather than triggering the expensive logic in +url_for+. + class PositionalArguments < Optimiser + def guard_conditions + number_of_arguments = route.segment_keys.size + # 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 + ["args.size == 1", "!args.first.is_a?(Hash)"] + else + ["args.size == #{number_of_arguments}"] + end + end + + def generation_code + elements = [] + idx = 0 + + if kind == :url + elements << '#{request.protocol}' + elements << '#{request.host_with_port}' + end + + elements << '#{ActionController::Base.relative_url_root if ActionController::Base.relative_url_root}' + + # The last entry in route.segments appears to *always* be a + # 'divider segment' for '/' but we have assertions to ensure that + # we don't include the trailing slashes, so skip them. + (route.segments.size == 1 ? route.segments : route.segments[0..-2]).each do |segment| + if segment.is_a?(DynamicSegment) + elements << segment.interpolation_chunk("args[#{idx}].to_param") + idx += 1 + else + elements << segment.interpolation_chunk + end + end + %("#{elements * ''}") + end + end + + # This case is mostly the same as the positional arguments case + # above, but it supports additional query parameters as the last + # argument + class PositionalArgumentsWithAdditionalParams < PositionalArguments + def guard_conditions + ["args.size == #{route.segment_keys.size + 1}"] + + UrlRewriter::RESERVED_OPTIONS.collect{ |key| "!args.last.has_key?(:#{key})" } + end + + # This case uses almost the same code as positional arguments, + # but add a question mark and args.last.to_query on the end, + # unless the last arg is empty + def generation_code + super.insert(-2, '#{\'?\' + args.last.to_query unless args.last.empty?}') + end + + # 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 + end + end + + OPTIMISERS = [PositionalArguments, PositionalArgumentsWithAdditionalParams] + end + end +end