Rails 2 Render ERb template in controller? - ruby-on-rails

This code in the controller
av = ActionView::Base.new(Rails::Configuration.new.view_path)
av.extend ApplicationHelper
content = av.render(:file => "show", :locals => { :user => #user })
and the show.html.erb have the link_to helper , operation code error
undefined method `url_for' for nil:NilClass
I add av.extend ActionController::UrlWriter in the controller , still error
undefined method `default_url_options' for ActionView::Base:Class

Try:
content = render_to_string(:file => "show", :locals => { :user => #user })

Usually, in Rails, when something is very hard, it is because you are not approaching the problem from an ideal angle. I can't really answer this question directly, other than to advise not to do this. Ideally, view logic should be in the view, and not the controller. With a few rare exceptions, like using a link_to helper in a flash message (which can be easily solved), these concerns should be separated. This does not seem like one of those exceptions. Instead, I would recommend one of the following (slightly more Rails-y) techniques:
Option 1:
It looks like you are trying to render the view for the show action. This can easily be accomplished by using render :action => 'show' (docs). This will not run the code for the action, just use that view.
Option 2
In the event that option 1 is not viable in your situation, you may alternatively consider some variation of the following technique. Render the default view, as normal. Move the existing content of the view into a partial, and your new content into a partial of its own. Then in your view, simply toggle the partial to render based off of an appropriate condition - the existence of #user, for this example: render :partial => #user ? 'new_content' : 'existing_content'. Depending on your application structure, it may be that this can be further simplified by just rendering the same partial from your show view and the view for the action referenced in the question.
I think keeping the various elements of an application isolated into their intended concerns not only makes this easier to develop and maintain by following the principle of least astonishment, but also usually makes the application much easier to test, as well. Sorry for not answering your question - hope this helps, anyway.

I suppose it was called outside controller so I do it this way in Rails 3:
av = ActionView::Base.new(Rails.configuration.paths["app/views"])
av.class_eval do
include ApplicationHelper
include Rails.application.routes.url_helpers
default_url_options[:host] = 'yoursite.com'
def protect_against_forgery?
false
end
end
#result = av.render(:file => "show", :locals => { :user => #user })

Related

Manage Single Rails form to other controller with create and update action

i'm new of rails. I use rails 3.0.5 .
I have an EMPLOYEE resource, but I would like to manage it with another extern controller (emp_profile_controller).
This extern controller (emp_profile_controller) manages some actions (index, new_employee, create_employee, edit_employee, update_employee ecc.. ) .
My routes for this controller are :
controller :emp_profile do
get 'emp_profile' => :index
get 'emp_edit_profile' => :edit_employee
put 'emp_edit_profile' => :update_employee
get 'new_employee' => :new_employee
post 'new_employee' => :create_employee
get 'emp_list' => :emp_list
end
How can i use one form to handle both Create and Update actions in this controller ?
I tried with :
form_for(#employee, :url => { :controller => "emp_profile"}) do |f|
but it doesn't work.
If i manage only one action at time (create OR update), url_for works, for example :
form_for(#employee, :url => { :controller => "emp_profile", :action => "update_employee" }
but how can i handle both actions with one form ?
Thanks for your availability and I apologize if I asked a stupid question.
EDIT
For now, i solved checking if object exist in the form file, if exist i set a variable with the UPDATE action path, else, i set a variable with the CREATE action path. So in the form_for statement i use url_for with the above variable.
<% if #employee.new_record?
action = "create_employee"
method = "post"
else
action = "update_employee"
method = "put"
end
%>
form_for(#employee, :url => { :controller => "emp_profile", :action => action }, :method => method
I don't think it is the best way but it works and i can use only one form file.
As your model name and controller name are different, you can add this line to your routes
resources :employees,:controller=>"emp_profile",:path=>"emp_profile"
Change the method names of create_employee,update_employee to create and update respectively.
And change your form_for as given below
<%= form_for #employee do |f| %>
....
<% end %>
First of all, if you want to update something, this object should exist.
How do plan to find it out, I don't know (cause there different ways, depends on background).
There are 2 ways of solving this issue.
You can just check if object exist in view file, and if exists, renfer form for update, else for create.
Other way is to do it in controller.
For example:
def create
#employee=Employee.find_by_name('Jack Black') #for example
if #employee!=nil
render :action=> 'update'
else
#employee=Employee.new(:employee)
#employee.save
end
as i understand you want to execute two different actions on the same controller using a form submitting, this is not possible, you can only execute one action using a form submitting,
because the form is reaching to an action controller that action is suppose to render some view at the end of it's execution code, if it was possible to use to actions on form submitting how rails will know which view to render??? (that's why it's not possible).
if you want to do some more code execution at the controller, the right way to it is to call a method with some code in it that you want to execute, that method should be in the model,
because it is a good practice to write all massive chunks of code in the model and leave the controller as light from code as possible :-)
hope this helps.

How to make a model aware of its controller in Rails?

I am making a Rails application, and i would like to be able use a model object passed to a view to get the URL of some action on this object, like this, for example:
link_to object.public_send(attribute),
{ :controller => object.controller_path,
:action => :show,
:id => object.id }
What would be a good way to do this? Can it be done with a decorator like Draper? Are there some examples online?
Update. I have thought about this and decided that a decorator is not a good place to keep controller information. It is not decorator's responsibility. A decorator should only know to render formatted data with markup. For now i have created a module called Accessor where i try to mix models with controller and routing awareness. I still wonder if there is a better way to do.
If you don't mind having another instance variable on your view, you can implement this using a very simple class (no need for decorators).
class MyRouter
def initialize(controller, object)
#controller = controller
#object = object
end
def url_for(action_name)
controller.url_for(object, :action => action_name)
end
end
On your controllers:
class AController
def edit
#router = MyRouter.new(self, object)
render 'shared_view'
end
end
class BController
def edit
#router = MyRouter.new(self, object)
render 'shared_view'
end
end
And on your shared view:
<%= #router.url_for(:show) # Varies with the controller that rendered the view %>
Of course, this assumes that the controller you want as target is the same controller that renders the view, which might not be true. Still, using this pattern you can accommodate a more complex logic that suits your needs (having multiple Router classes, for instance), without having to change the view.
I've found a very interesting solution in Objects on Rails by Avdi Grimm: Exhibits for REST. In short, his idea is to apply multiple Ruby's SimpleDelegators as decorators with various functions.

Bypassing Controller in Rails

Im using Rails 2.3.5 and I have a model, let's call it Post. I used named scopes to apply different kinds of sorts on Post. For example, in the Post model I have possibility to rank posts by its scores:
named_scope :order_by_acception_rate_desc,
Proc.new { |limit| { :limit => limit, :order => "acception_rate desc" } }
In the Post Controller I have:
def best
#best_posts = Post.order_by_acception_rate_desc(10)
end
In the view I just render this collection #best_posts:
<%= render :partial => "post", :collection => #best_posts
Currently my application is working like that, but actually I do not need to have the method "best" in the Controller, and I could move it to the Model Post doing like:
def self.best
self.order_by_acception_rate_desc(10)
end
and then in the view I would render the collection like:
<%= render :partial => "post", :collection => Post.best
I do not know which approach is better, but using the ranking methods in the Model, I could avoid to create routes for each one of ranking methods. What approach is better, is there any better approach than these?
with according to Rails conventions the logic should be separated,
controllers handle permissions, auth/authorization, assign instance/class variables
helpers handle html logic what to show/hide to user
views should not provide any logic, permissions check. think about it from designer's point of view
models handle data collection/manipulation over ORM
I'd like to ask you to try:
#helper
def self.best(limit)
all(:limit => limit, :order => "acception_rate desc")
end
#controller
#best_posts = Post.best
#view
<%= render :partial => "post", :collection => #best_posts %>
You should not bypass the controller and include much logic in your view.
You could keep a single route and filter the Post model depending on one of the params.
You don't tell enough here to answer more clearly but you have the big picture.
You can leave just the view file and it should work.

How to use partial in views with different alias MIME?

Im using 2 different sets of views for 2 different user's roles.
Im using register_alias :
Mime::Type.register_alias "text/html", :basic
in the controller:
class SomeController < ApplicationController
def index
# …
respond_to do |format|
format.html # index.html.erb (advance)
format.basic # index.basic.erb
end
end
end
In some case I have to use the same code in both views, then I would use a Partial, but because of the MIME alias, I have to use 2 identical partials:
my_partial.html.erb and my_partial.basic.erb
I think there is a solution to DRY the code and use only a partial.
Do you have some solutions ?
thank you,
Alessandro
Old Answer:
I probably tried 50 different things until I figured out the right way of writing the partial once, but it was worth it because it's super simple:
Inside your index view, you normally do:
<%= render "my_partial" %>
This implicitly gets mapped to the partial corresponding to the Mime you requested, so it implies having two partial implementations. If you want a DRY partial, simply explicitly specify the format:
<%= render "my_partial.html" %>
As an added bonus of this observation, if your responds_to block of code is really just to switch based on the format and has no logic inside it, you can entirely remove that block of code and things still work implicitly.
Rails 3.2 update:
Rails has deprecated support for the above and support has been completely removed in the latest version of Rails. The following is the correct way as of Rails 3.2:
<%= render :partial => "my_partial", :formats => [:html] %>

One controller rendering using another controller's views

I have QuestionController
I now have AnotherQuestionController with actions which should render using templates and partials in app/views/question/
Is this possible? Seems like it should be.
I've tried
render :template => "question/answer"
but answer.html.erb includes partials and I get errors like
"Missing template another_question/_my_partial.erb in view path"
So is there a way to tell Rails "treat AnotherQuestionController as if its QuestionController and look for views and partials in app/views/question"?
Or will I have to create app/views/another_question - which will cause duplication (this can't be the Rails way).
Thanks
Template rendering should actually work
render :template => "question/answer"
The problem you were having is from the partials looking in the wrong place. The fix is simple, just make your partials absolute in any shared templates. For example, question/answer.html.erb should have
<%= render :partial => 'question/some_partial' %>
rather than the usual
<%= render :partial => 'some_partial' %>
You can achieve it with:
render 'question/answer'
Rails uses a list of prefixes to resolve templates and partials. While you can explicitly specify a prefix ("question/answer"), as suggested in another answer, this approach will fail if the template itself includes unqualified references to other partials.
Assuming that you have an ApplicationController superclass, and QuestionController inherits from it, then the places Rails would look for templates are, in order, "app/views/question/" and "app/views/application/". (Actually it will also look in a series of view paths, too, but I'm glossing over that for simplicity's sake.)
Given the following:
class QuestionController < ApplicationController
end
class AnotherQuestionController < ApplicationController
end
QuestionController._prefixes
# => ["question", "application"]
AnotherQuestionController._prefixes
# => ["another_question", "application"]
Solution #1. Place the partial under "app/views/application/" instead of "app/views/question/", where it will be available to both controllers.
Solution #2. Inherit from QuestionController, if appropriate.
class AnotherQuestionController < QuestionController
end
=> nil
AnotherQuestionController._prefixes
# => ["another_question", "question", "application"]
Solution #3. Define the class method AnotherQuestionController::local_prefixes
This was added in Rails 4.2.
class AnotherQuestionController < ApplicationController
def self.local_prefixes
super + ['question']
end
end
AnotherQuestionController._prefixes
# => ["another_question", "question", "application"]
You could try the inherit_views plugin (http://github.com/ianwhite/inherit_views/tree/master) I mentioned here in the answer to this question.

Resources