What is the best way to use functions of Controller and Conn in Plug Module? - phoenix-framework

I want to write my own plug in my phoenix app. The plug is to check the cookies and render the error page if cookies don't exist. Similar to the 404 error. Based on the logic, those functions below may be called:
put_status: set the status code of response, I set to 422.
put_view: My error page is located in templates/error/422.html.eex, so the ErrorView should be included.
render: Render the html page.
halt: halt the connection.
conn
|> put_status(422)
|> put_view(ErrorView)
|> render("422.html")
|> halt()
put_status and halt are in Plug.Conn. put_view and render are in Phoenix.Controller. I know that I can use the full namespace to call those functions, but it looks redundant. So I am considering import/use/alias functions as well.
For Plug.Conn, the Programming Phoenix 1.4 Book uses import​ Plug.Conn, some official plug uses alias Plug.Conn.
For Phoenix.Controller, I haven't found any example, but import could work. Is it not recommended to use Controller functions in Plug?
I am considering both the code simplify, readability and performance. Does anyone know what's the best practice for this?
Cheers

You can import Phoenix.Controller and Plug.conn in your plug and import that plug into the controller.
defmodule DubberConnectWeb.CheckCookie do
import Plug.Conn
import Phoenix.Controller
def check_cookie(conn, _opts) do
if <check cookie existence condition..> do
conn
|> put_status(422)
|> put_view(DubberConnectWeb.ErrorView)
|> render("422.html")
|> halt()
else
conn
end
end
end
Then in your controller, you will just do
import DubberConnectWeb.CheckCookie
plug(:check_cookie)

Regarding to the alias/import, neither of them causes a performance loss.
In the import doc, it says:
We use import whenever we want to easily access functions or macros from other modules without using the fully-qualified name.
And alias does it too, with the namespace included.
Therefore, both alias Plug.Conn and import Plug.Conn are good to use in the plug module.

Related

Using "helper" method in view

I made a helper in my rails project that makes a request in an external api and get a certain value from it.
def show_CoinPrice coin
begin
coinTicker = JSON.parse(HTTParty.get("https://www.mercadobitcoin.net/api/#{coin}/ticker/").body)
"R$ #{coinTicker["ticker"]["last"].to_number.round}"
rescue
"---"
end
end
However I have doubts if this was a good practice to do (this code caused slowness in my view), there is something I can do better, whether with a controller or something ?!
Thanks.
I think u should request to external API using client-side (JS). Cs fetching API with rails helper it's will run while your server is rendering view.

What is the use of hello_view.ex in Phoenix

I was reading the 'Programming Phoenix' book to create my first MVC simple routing project. In the book, it says to create a 'hello_view.ex', and it's under the view section in MVC. But there is already a view in templates. This feels very confusing.
Also, they told me to put hello_world.html inside 'hello' folder in templates. Can I change this folder name? Why can't I rename it? In 'hello_controller.ex', it says 'hello_world.html', not 'hello/hello_world.html'
Phoenix views aren't HTML templates with embedded programming language like html.erb, but the names are similar to the ones in Django, where view means code for displaying data and template means actual HTML with embedded Elixir code (and they have extension eex).
According to the official guide for Phoenix Framework, short info about views:
They also act as a presentation layer for raw data from the controller, preparing it for use in a template. Functions which perform this transformation should go in a view.
Check this out:
Ruby on Rails flow:
router.rb -> Controller (-> Model) -> view in html.erb
Django flow:
urls.py -> View (act like Controller) -> Template
Phoenix flow:
endpoint (in lib) -> router (in web) -> Controller -> View -> Template
The Phoenix flow seems to be longer, but it's not. The biggest advantage for you:
You have explicit control over the middleware!
Remember that- framework does the magic for you, but explicitly. You exactly see what macros and other stuff are called.
Edit
Check out the stuff that's imported when using use in your code.
In your controller you're using Phoenix.Controller stuff, which provides you render function.
You call render with conn, template and assigns parameters. In deps/phoenix/lib/phoenix/controller.ex you have this render
function stored and in the private function do_render line:
view = Map.get(conn.private, :phoenix_view) ||
raise "a view module was not specified, set one with put_view/2"
Gets your View name from the current connection.
And few lines later uses it:
Phoenix.View.render_to_iodata(view, template, Map.put(conn.assigns, :conn, conn))
This call uses internally render in deps/phoenix/lib/phoenix/view.ex
and then render_within, but the name of the folder is set in __using__
use Phoenix.Template, Phoenix.View.__template_options__(__MODULE__, unquote(opts))
which calls __template__options where following lines are placed:
module
|> Module.split()
|> Enum.take(1)
|> Module.concat()
which does the Hello from Hello.PageView or whatever the name is.
Edit2
About changing default view in controller (info from Phoenix documentation):
By default, Controllers render templates in a view with a similar name
to the controller. For example, MyApp.UserController will render
templates inside the MyApp.UserView. This information can be
changed any time by using render/3, render/4 or the put_view/2
function:
def show(conn, _params) do
render(conn, MyApp.SpecialView, :show, message: "Hello")
end
def show(conn, _params) do
conn
|> put_view(MyApp.SpecialView)
|> render(:show, message: "Hello")
end

Using SimpleTemplate in Bottle

I'm new in Frameworks like Bottle and working through the Documentation/Tutorial.
Now I got a Problem with the Template-Engine:
I have a file called index.tpl in my folder views. (it's plain html)
When I use the following Code, it works to display my html:
from bottle import Bottle, SimpleTemplate, run, template
app = Bottle()
#app.get('/')
def index():
return template('index')
run(app, debug=True)
Now I want to implement this engine in my project and dont want to use template()
I want to use it as it stands in the documentation, like:
tpl = SimpleTemplate('index')
#app.get('/')
def index():
return tpl.render()
But if I do so, the Browser shows me just a white page with the word
index
written, instead of loading the template.
In the documentation, there is no further information on how I use this OO approach.
I just couldn't figure out why this happens and how I have to do it right...
Here's a nice, simple solution in the spirit of your original question:
tpl = SimpleTemplate(name='views/index.tpl') # note the change here
#app.get('/')
def index():
return tpl.render()

How do I load and cache data into a bottle.py app that's accessible in multiple functions without using global variables?

I have a bottle.py app that should load some data, parts of which get served depending on specific routes. (This is similar to memcached in principle, except the data isn't that big and I don't want the extra complexity.) I can load the data into global variables which are accessible from each function I write, but this seems less clean. Is there any way to load some data into a Bottle() instance during initialization?
You can do it by using bottle.default_app
Here's simple example.
main.py (used sample code from http://bottlepy.org/docs/dev/)
import bottle
from bottle import route, run, template
app = bottle.default_app()
app.myvar = "Hello there!" # add new variable to app
#app.route('/hello/<name>')
def index(name='World'):
return template('<b>Hello {{name}}</b>!', name=name)
run(app, host='localhost', port=8080)
some_handler.py
import bottle
def show_var_from_app():
var_from_app = bottle.default_app().myvar
return var_from_app

why decorate Jinja2 instances with #webapp2.cached_property

The webapp2 site (http://webapp-improved.appspot.com/api/webapp2_extras/jinja2.html) has a tutorial on how to use webapp2_extras.jinja2, and the code is below.
My question is: why cache the webapp2_extras.jinja2.Jinja2 instance return by return jinja2.get_jinja2(app=self.app)? I checked the code of #webapp2.cached_property and found that it caches the Jinja2 instance in a instance of BaseHandler, which will be destroyed after the request, so why bother to cache it? Did I miss something here?
import webapp2
from webapp2_extras import jinja2
class BaseHandler(webapp2.RequestHandler):
#webapp2.cached_property
def jinja2(self):
# Returns a Jinja2 renderer cached in the app registry.
return jinja2.get_jinja2(app=self.app)
def render_response(self, _template, **context):
# Renders a template and writes the result to the response.
rv = self.jinja2.render_template(_template, **context)
self.response.write(rv)
Here you can find the documentation about cached_property.
The BaseHandler class will be later on called often. My understanding is that to avoid the overhead of calling jinja2.get_jinja2(app=self.app) each time, such reference is evaluated the first time only, and then returned many times later on, i.e. every time a view is called.
To see this happen in code, see this example, where each view is derived from the same BaseHandler class.

Resources