amber icon indicating copy to clipboard operation
amber copied to clipboard

automatically create paths helpers

Open jaysneg opened this issue 7 years ago • 11 comments

Description

Add automatically create paths and URL Helpers like at Rails

Steps to Reproduce

http://guides.rubyonrails.org/routing.html#path-and-url-helpers

Versions

0.7.2

jaysneg avatar May 06 '18 12:05 jaysneg

Hi @jaysneg Thank you for open this issue!

I think amber already support resources. Perhaps we just need to add more capabilities to it,

@amberframework/contributors WDYT?

faustinoaq avatar May 06 '18 13:05 faustinoaq

I agree, this will be a good feature to have. Here is my current workaround. I have a file at src/controllers/application/url_helpers.cr which is included into my application_controller and has code like this:


module UrlHelpers
  def root_path
    if current_user.guest? || ! current_user.activated?
      "/"
    else
      domains_path
    end
  end






  def users_path
    "/users"
  end

  def user_path(user : User)
    "/user/#{user.id}"
  end

  def new_user_path
    "/me/register"
  end

  def invited_user_registration_path(invite : Invite)
    "/me/register?invite=#{invite.code}"
  end

  def user_activation_path
    "/me/activate"
  end

# on and on for 150 lines
end

robacarp avatar May 10 '18 00:05 robacarp

I think this is a great Idea.

Some ideas for this would be to include action type check support so for instance if the action does not exist then it throws an error similar to the way Lucky does it.

This basically can introspect the controller action methods to generate the helpers dynamically. And maybe instead of creating methods for it we can create something more intuitive User::New.with(:id)

For each controller define in routes we can dynamically generate helpers as

# Routes
post "/users/activation/:id#integer", UsersController, :activation

# Generated helper
Users::Activation.path(user_id: interger)

eliasjpr avatar Jun 23 '18 14:06 eliasjpr

@eliasjpr If we generate routes based on controller and action, and not based on the actual path, we will never be able to have a single action be accessible at two different paths.

shobhitic avatar Jun 26 '18 11:06 shobhitic

@eliasjpr @shobhitic generating the named routes based off of controller and action seems like a fine way to provide a default. As long as there as a way to override the default, I'm in favor. Rails does this surprisingly well with some very simple method declarations.

robacarp avatar Jun 27 '18 02:06 robacarp

@robacarp IMO, it's not the best default. Lots of times while refactoring, you tend to move an action from one controller to the other while keeping the path to access it same. Happens all the time while refactoring APIs because you can't change routes there. In situations like these, you'll have to change all the references to path, unlike in Rails where the path method is based on the path being used, and not how it's implemented.

I think we should also create path methods based on paths, and not based on their implementations.

shobhitic avatar Jun 27 '18 06:06 shobhitic

@robacarp I would suggest the above example as a start point, I believe if you want to define a custom path that should be more of a global path helper method.

I am also fine with the way Rails does this, but having some checks at compile time would be very beneficial this would avoid having dead URL paths in a large app.

eliasjpr avatar Jul 14 '18 01:07 eliasjpr

I'm not sure this is something that can be generalized and implemented at the framework level. But the docs could add some implementation suggestions or the generator could create a helper, perhaps.

In the spirit of providing more ideas/working code examples, here's what I do:

  macro resource_paths(klass, plural, to_param_method = :id)
    {% singular = klass.id.underscore %}
    def {{plural.id}}_path
      "/{{plural.id}}"
    end
    def new_{{singular.id}}_path(**params)
      qs = params.map { |k, v| "#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}" }.join("&")
      "/{{plural.id}}/new" + (qs.blank? ? "" : "?#{qs}")
    end
    def {{singular.id}}_path({{singular.id}} : {{klass.id}})
      "/{{plural.id}}/#{{{singular.id}}.{{to_param_method.id}}}"
    end
    def edit_{{singular.id}}_path({{singular.id}} : {{klass.id}})
      "/{{plural.id}}/#{{{singular.id}}.{{to_param_method.id}}}/edit"
    end

    def link_to(body : String, obj : {{klass.id}}, **options)
      link_to(body, {{singular.id}}_path(obj), **options)
    end
    def link_to(body : String, obj : {{klass.id}}, **options, &block)
      link_to(body, {{singular.id}}_path(obj), **options, &block)
    end

    def redirect_to(obj : {{klass.id}}, **args)
      redirect_to({{singular.id}}_path(obj), **args)
    end
  end
  resource_paths User, :users

anamba avatar Nov 22 '18 03:11 anamba

@eliasjpr It gets a 99 on Desktop and 93 on Mobile, that's way better than most websites get. Is this a priority for Amber still?

elaine-jackson avatar May 07 '19 21:05 elaine-jackson

Would love to see the the resources macro generate path and url helpers. Anyone interested in taking this on?

eliasjpr avatar Sep 12 '19 17:09 eliasjpr

I use this code I wrote on my case to generate the routes.

This might not the cleanest way to do it but it works, to use it:

  • copy this generate_routes.cr at the root of your project
  • create a folder ./src/controllers/helpers if it's not already done
  • just do a crystal generate_routes.cr, it will create a view_path.cr helper you can include with all the paths

alex-min avatar Mar 21 '20 03:03 alex-min