module InheritedResources # = URLHelpers # # When you use InheritedResources it creates some UrlHelpers for you. # And they handle everything for you. # # # /posts/1/comments # resource_url # => /posts/1/comments/#{@comment.to_param} # resource_url(comment) # => /posts/1/comments/#{comment.to_param} # new_resource_url # => /posts/1/comments/new # edit_resource_url # => /posts/1/comments/#{@comment.to_param}/edit # collection_url # => /posts/1/comments # parent_url # => /posts/1 # # # /projects/1/tasks # resource_url # => /projects/1/tasks/#{@task.to_param} # resource_url(task) # => /projects/1/tasks/#{task.to_param} # new_resource_url # => /projects/1/tasks/new # edit_resource_url # => /projects/1/tasks/#{@task.to_param}/edit # collection_url # => /projects/1/tasks # parent_url # => /projects/1 # # # /users # resource_url # => /users/#{@user.to_param} # resource_url(user) # => /users/#{user.to_param} # new_resource_url # => /users/new # edit_resource_url # => /users/#{@user.to_param}/edit # collection_url # => /users # parent_url # => / # # The nice thing is that those urls are not guessed during runtime. They are # all created when you inherit. # module UrlHelpers protected # This method hard code url helpers in the class. # # We are doing this because is cheaper than guessing them when our action # is being processed (and even more cheaper when we are using nested # resources). # # When we are using polymorphic associations, those helpers rely on # polymorphic_url Rails helper. # def create_resources_url_helpers! resource_segments, resource_ivars = [], [] resource_config = self.resources_configuration[:self] singleton = resource_config[:singleton] uncountable = !singleton && resource_config[:route_collection_name] == resource_config[:route_instance_name] polymorphic = self.parents_symbols.include?(:polymorphic) # Add route_prefix if any. unless resource_config[:route_prefix].blank? if polymorphic resource_ivars << resource_config[:route_prefix] else resource_segments << resource_config[:route_prefix] end end # Deal with belongs_to associations and polymorphic associations. # Remember that we don't have to build the segments in polymorphic cases, # because the url will be polymorphic_url. # self.parents_symbols.each do |symbol| if symbol == :polymorphic resource_ivars << :parent else config = self.resources_configuration[symbol] if config[:singleton] && polymorphic resource_ivars << config[:instance_name] else resource_segments << config[:route_name] end if !config[:singleton] resource_ivars << :"@#{config[:instance_name]}" end end end collection_ivars = resource_ivars.dup collection_segments = resource_segments.dup # Generate parent url before we add resource instances. unless parents_symbols.empty? generate_url_and_path_helpers nil, :parent, resource_segments, resource_ivars generate_url_and_path_helpers :edit, :parent, resource_segments, resource_ivars end # In singleton cases, we do not send the current element instance variable # because the id is not in the URL. For example, we should call: # # project_manager_url(@project) # # Instead of: # # project_manager_url(@project, @manager) # # Another exception in singleton cases is that collection url does not # exist. In such cases, we create the parent collection url. So in the # manager case above, the collection url will be: # # project_url(@project) # # If the singleton does not have a parent, it will default to root_url. # collection_segments << resource_config[:route_collection_name] unless singleton resource_segments << resource_config[:route_instance_name] resource_ivars << :"@#{resource_config[:instance_name]}" unless singleton # Finally, polymorphic cases we have to give hints to the polymorphic url # builder. This works by attaching new ivars as symbols or records. # if polymorphic && singleton resource_ivars << resource_config[:instance_name] new_ivars = resource_ivars end # If route is uncountable then add "_index" suffix to collection index route name if uncountable collection_segments << :"#{collection_segments.pop}_index" end generate_url_and_path_helpers nil, :collection, collection_segments, collection_ivars generate_url_and_path_helpers :new, :resource, resource_segments, new_ivars || collection_ivars generate_url_and_path_helpers nil, :resource, resource_segments, resource_ivars generate_url_and_path_helpers :edit, :resource, resource_segments, resource_ivars if resource_config[:custom_actions] [*resource_config[:custom_actions][:resource]].each do | method | generate_url_and_path_helpers method, :resource, resource_segments, resource_ivars end [*resource_config[:custom_actions][:collection]].each do | method | generate_url_and_path_helpers method, :resources, collection_segments, collection_ivars end end end def handle_shallow_resource(prefix, name, segments, ivars) #:nodoc: return segments, ivars unless self.resources_configuration[:self][:shallow] case name when :collection, :resources segments = segments[-2..-1] ivars = [ivars.last] when :resource if prefix == :new segments = segments[-2..-1] ivars = [ivars.last] else segments = [segments.last] ivars = [ivars.last] end when :parent segments = [segments.last] ivars = [ivars.last] end segments ||= [] unless self.resources_configuration[:self][:route_prefix].blank? segments.unshift self.resources_configuration[:self][:route_prefix] end return segments, ivars end def generate_url_and_path_helpers(prefix, name, resource_segments, resource_ivars) #:nodoc: resource_segments, resource_ivars = handle_shallow_resource(prefix, name, resource_segments, resource_ivars) ivars = resource_ivars.dup singleton = self.resources_configuration[:self][:singleton] polymorphic = self.parents_symbols.include?(:polymorphic) # In collection in polymorphic cases, allow an argument to be given as a # replacemente for the parent. # parent_index = ivars.index(:parent) if polymorphic segments = if polymorphic :polymorphic elsif resource_segments.empty? 'root' else resource_segments.join('_') end define_params_helper(prefix, name, singleton, polymorphic, parent_index, ivars) define_helper_method(prefix, name, :path, segments) define_helper_method(prefix, name, :url, segments) end def define_params_helper(prefix, name, singleton, polymorphic, parent_index, ivars) params_method_name = ['', prefix, name, :params].compact.join('_') undef_method params_method_name if method_defined? params_method_name define_method params_method_name do |*given_args| given_args = given_args.collect { |arg| arg.respond_to?(:permitted?) ? arg.to_h : arg } given_options = given_args.extract_options! args = ivars.map do |ivar| ivar.is_a?(Symbol) && ivar.to_s.start_with?('@') ? instance_variable_get(ivar) : ivar end args[parent_index] = parent if parent_index if !(singleton && name != :parent) && args.present? && name != :collection && prefix != :new resource = args.pop args.push(given_args.first || resource) end if polymorphic if name == :collection args[parent_index] = given_args.present? ? given_args.first : parent end if (name == :collection || name == :resource && prefix == :new) && !singleton args << (@_resource_class_new ||= resource_class.new) end args.compact! if self.resources_configuration[:polymorphic][:optional] args = [args] end args << given_options end protected params_method_name end def define_helper_method(prefix, name, suffix, segments) method_name = [prefix, name, suffix].compact.join('_') params_method_name = ['', prefix, name, :params].compact.join('_') segments_method = [prefix, segments, suffix].compact.join('_') undef_method method_name if method_defined? method_name define_method method_name do |*given_args| given_args = send params_method_name, *given_args send segments_method, *given_args end protected method_name end end end