Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add #controller_name method to Phoenix.Controller #1016

Closed
wants to merge 3 commits into from

Conversation

acconrad
Copy link

Added a #controller_name function into Phoenix.Controller.

Input: Plug.Conn.t connection

Output: lowercase name of the controller.

Example:

$ Phoenix.Controller.controller_name @conn
=> "page"

where @conn was generated by the PageController.

Why?

When dealing with CSS styling, it's useful to be able to separate out styles by page. The best way to get a page is the combination of it's controller and action. So for example, I can specifically style the homepage if I have .page.index as the classes to the <body> tag of a page. It also helps for modular loading of JS as well as CSS styles. Coming from Rails, it was great to have access to controller_name and action_name, and I found it strange that Phoenix only had action_name but not controller_name, and I felt like this would be a good addition to the core functionality of Phoenix.Controller.

@josevalim
Copy link
Member

I think we have a function in Phoenix.Naming that does exactly what you want:

Phoenix.Naming.resource_name(controller_module(conn), "Controller")

@@ -110,6 +110,19 @@ defmodule Phoenix.Controller do
def action_name(conn), do: conn.private.phoenix_action

@doc """
Returns the controller name as a string, raises if unavailable.
"""
@spec controller_name(Plug.Conn.t) :: atom
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec says it returns an atom but it actually returns a string.

@josevalim
Copy link
Member

Thanks, I have added some comments. It feels like this would be a good addition but I will leave it up for @chrismccord to decide.

@acconrad
Copy link
Author

Thank you @josevalim ! I updated the code based on your comments to include the correct datatype returned.

@chrismccord
Copy link
Member

Thanks @acconrad ! I think this is a welcome addition, but we need to extend it a bit first. For example, it fails for multi-world controllers, i.e.:

iex(2)> Phoenix.Controller.controller_name(%{private: %{
  phoenix_controller: Foo.AdminBarController
}})
"adminbar"

We would need to dasherize or similar, but I don't want the code to get too complex. We also need to discuss if it should include all "namespaces" back to the root namespace, so MyApp.Admin.BarController would return "admin-bar" instead of just "bar". Thoughts? // @josevalim

Returns the controller name as a string, raises if unavailable.
"""
@spec controller_name(Plug.Conn.t) :: String.t
def controller_name(conn) do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation of this function should be just:

Phoenix.Naming.resource_name(conn.private.private_controller, "Controller")

@josevalim
Copy link
Member

@chrismccord the issues you have mentioned are solved by using Phoenix.Naming.resource_name/2, as I have originally mentioned.

About namespacing, that is a bit trickier because it would require storing the namespace in the connection too.

@acconrad
Copy link
Author

@josevalim @chrismccord I've taken your suggestions and updated the code accordingly - this function now supports multi-word controllers. For the sake of CSS conventions, I did a simple String.replace to "dasherize" the output, as it originally snake_cases multi-word controller names

@josevalim
Copy link
Member

@chrismccord I don't think we should dasherize. If we want to dasherize, we should move this to Phoenix.HTML as controller_dom_id or something of sorts.

@chrismccord
Copy link
Member

Now that I think about it, dasherize is only for a css specific use-case, and not something we should leak to this function

@chrismccord
Copy link
Member

@josevalim beats me at the internet again, by 3 seconds, 👍

@acconrad
Copy link
Author

good points re: dasherize. perhaps as a compromise, have the controller_name function within Phoenix.Controller and then Phoenix.HTML.controller_name can simply call Phoenix.Controller.controller_name |> String.replace("_", "-")? I'd keep it as #controller_name and not controller_dom_id because in the general HTML use case, we don't want to explicitly consider it an ID, as it could be used for a class. I don't think using the same function name is necessarily an issue.

@acconrad
Copy link
Author

@josevalim @chrismccord I've reverted the dasherization pipeline and just left it as is. I still think this function would be useful on the backend, but I think it would be worth extending on Phoenix.HTML as well, though I have to ask this somewhat naive question:

I just poured through the phoenix_html project codebase and I couldn't find any Phoenix module references outside of Phoenix.HTML, so is it even possible for me to call Phoenix.Naming inside of that project, and how would I reference a Plug.Conn.t? I noticed in Phoenix.HTML.Form you can pass connection data, but it's manipulated via the path, so you don't actually have to handle the connection type directly. Therefore, I'm not exactly sure how this method would translate onto that project.

@josevalim
Copy link
Member

@acconrad that's a very good point. At best, we could keep controller_name here and Phoenix.HTML would only dasherize it.

@acconrad
Copy link
Author

@josevalim relating to my previous comment, how exactly could Phoenix.HTML dasherize that function - I'm not seeing anywhere in the code where that package references anything outside of Phoenix.HTML, so I'm not sure how I could call Phoenix.Controller in there.

@acconrad
Copy link
Author

@chrismccord just wanted to check back in here - been a few weeks and wanted to see what else I needed to do before we can merge this in

@chrismccord
Copy link
Member

just wanted to check back in here - been a few weeks and wanted to see what else I needed to do before we can merge this in

This is still on my list to think about and review, so hang tight!

@chrismccord
Copy link
Member

@acconrad Thanks very much for this contribution, but I'm going to wait on this until after 1.0. For now, users can provide their own helper easily by using the controller_module(conn) and providing whatever idiom to class names that they follow. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants