I've created a custom method in my model, which finds a record by name:
def find_city
Place.find_by_name(city_name)
end
I can call this method in my view with place_path(#place.find_city), which works great when a place with the appropriate name exists. What I would like to be able to do is write in a redirect for when the place doesn't exist, and I'm unsure about where the logic should go. Basically, I would like to write something like:
respond_to do |format|
if #place.find_city.blank?
format.html { redirect_to :action => "new" }
else
format.html { render :action => "show" }
end
end
...but I would still like the controller to respond to place_path(#place) in the usual manner as well. Any help would be much appreciated!
EDIT: sorry for the confusion, should have explained my example further. I have a Place model that has both 'city_name' and 'name' as attributes. The find_city custom method that I detailed above finds the place whose name matches the city_name for another place eg.
Place.name = "foo"
Place.city_name = "baz"
So therefore Place.find_city gives the record where Place.name = "baz". Cheers!
Do something like this
keep your model as
class City < ActiveRecord::Base
named_scope :by_name, lambda {|city|{:conditions => "name=#{city}"}}
end
in your controller
class CitiesController < ApplicationController
def index
#city = City.by_name(<city name>)
if #city.nil?
<redirect to one place>
else
<redirect to another place>
end
end
end
and in your view access the #city parameter.
** Normally we shouldnt access Models directly from views. Either we should do it through controllers or helpers
Hope this helps
cheers
sameera
I've decided to create a helper method for this problem - what I've described above might seem a bit of an unorthodox approach, but I only need the find_city method to create a links bar for each place, so I don't think I really need to create a separate city model, or define a formal self-referential relationship. Just in case anyone might find this useful, I've created a links_bar helper:
module PlacesHelper
def links_bar(place)
if place.city_name.blank?
render "no_city_links"
elsif place.find_city.nil?
render "nil_find_city_links"
else
render "complete_links"
end
end
end
Each partial then has the required behaviour depending upon whether or not the find_city method returns a record or not.
Related
I make an search on an model:
def self.search(search)
if search
where('Bezeichnung LIKE?', "%#{search}%")
else
all
end
end
What i would like to change, is that when the search has no hits it fires an javascript file in icd/index.js.erb
Index.js.erb:
$('#chapter_list').html("<%= escape_javascript(render(:partial => 'icd1')) %>");
How can i do this?
Presumably this class method (self.search) is on a model. Since your controllers are where you return responses from, you'd need to have your controller respond with javascript code.
Something like:
def index
results = Model.search(params[:search])
respond_to |format| do
if results
format.html { render 'index.html.erb' }
else
format.js { render 'index.js.erb' }
end
end
end
i would do something like this, i would create another model method, like this:
Model:
def checkSearch(search)
if search
where('Bezeichnung LIKE?', "%#{search}%")
end
end
controller
#checker= Model.checkSearch(search)
in the view you can check if #checker is different from null and redner the partial
I know this aproach will query the database twice, but is a start.
more thought on it you can return an array in you first model method with a bollean in the first position and the active record object in the second one.
hope i gave you some direction!
In my Rails app I have an invoices_controller.rb with these actions:
def new
#invoice = current_user.invoices.build(:project_id => params[:project_id])
#invoice.build_item(current_user)
#invoice.set_number(current_user)
end
def create
#invoice = current_user.invoices.build(params[:invoice])
if #invoice.save
flash[:success] = "Invoice created."
redirect_to edit_invoice_path(#invoice)
else
render :new
end
end
Essentially, the new method instantiates a new invoice record plus one associated item record.
Now, what sort of method do I need if I want to duplicate an existing invoice?
I am a big fan of Rails's RESTful approach, so I wonder if I should add a new method like
def duplicate
end
or if I can use the existing new method and pass in the values of the invoice to be duplicated there?
What is the best approach and what might that method look like?
Naturally, you can extend RESTful routes and controllers.
To be rally RESTful, it is important to look exactly, what you want.
i.e. if you want a new invoice and use an existing one as a kind of template, then it is comparable to a new action, and the verb should be GET (get the input form). As is it based on an existing invoice, it should reference that object. After that you would create the new invoice in the usual way.
So in you routes:
resources :invoices do
member do
get 'duplicate'
end
end
giving you a route duplicate_invoice GET /invoices/:id/duplicate(.format) invoices#duplicate
So in your view you can say
<%= link_to 'duplicate this', duplicate_invoice_path(#invoice) %>
and in your controller
def duplicate
template = Invoice.find(params[:id])
#invoice= template.duplicate # define in Invoice.duplicate how to create a dup
render action: 'new'
end
If I understand correctly your question you can:
resources :invoices do
collection do
get 'duplicate'
end
end
and with this you can do:
def duplicate
# #invoice = [get the invoice]
#invoice.clone_invoice
render 'edit' # or 'new', depends on your needs
end
clone_invoice could be a custom method which should have a invoice.clone call in your custom method.
If you question if you can use additional methods except REST, you absolutely can. Google, for example, encourage developers to use something, what they call "extended RESTful" on GoogleIO, http://www.youtube.com/watch?v=nyu5ZxGUfgs
So use additional method duplicate, but don't forget about "Thin controllers, fat models" approach to incapsulate your duplicating logic inside model.
In my Rails app, when creating a business I have a form that has the following field:
<%= check_box_tag(:default_company) %>
<%= label_tag(:default_company, "Set Company as Default") %>
Essentially when I create a business, if they check this box, I need it to run something like the following code:
def set_default_company(company, user)
exists = DefaultCompany.find(user.id)
if exists
exists.update_attributes(company: company)
else
DefaultCompany.create(company: company, user: user)
end
end
While learning, I would usually do that stuff in my controller but i'm trying to follow best practices and use a fat model, skinny controller, so I'm wanting to use logic like this:
def create
#company = Company.new(params[:company])
if #company.save
if params[:default_company]
Company.set_default_company(#company.id, current_user.id,)
end
flash[:notice] = "Company was successfully created."
redirect_to #company
else
redirect_to new_company_path
end
end
Here is where I am getting confused on whether to use a class method or an instance method, to call set_default_company. They both seem like they would work and I can't see a benefit to one or the other.
In addition to giving me any information as to which method to use, if someone could show me a brief implementation of writing that as a class method vs. instance method it may give me a better understanding as to why.
Here is how I would write them:
def self.set_default_company(company, user)
# Logic here
end
def set_default_company(company, user)
# Logic here
end
Writing them that way I don't see a benefit to either.
As their name suggests, instance methods on a model should be used for logic/operations that relate to a specific instance of a user (the one on which the method is called.) So you could think of setting the default company for a user as an instance method on User. Class methods are for things which don't operate on an individual instance of a model or for cases where you don't have the instance available to you. e.g. you might have a class method to tidy up your database such as User.purge_expired_users which would not apply to an individual user object.
e.g.
class User
def set_default_company(company)
exists = DefaultCompany.find(self.id)
if exists
exists.update_attributes(company: company)
else
DefaultCompany.create(company: company, user: self)
end
end
end
then your controller method would look like:
def create
#company = Company.new(params[:company])
if #company.save
if params[:default_company]
current_user.set_default_company #company
end
flash[:notice] = "Company was successfully created."
redirect_to #company
else
redirect_to new_company_path
end
end
Alternatively, you could think of the relationship from the other perspective and put an instance method on Company e.g. company.set_as_default_for(user).
I would actually make set_default_company an instance method on User. A User has a default Company; why should a Company need to what users it is default for?
class User
def set_default_company(company)
exists = DefaultCompany.find(id)
if exists
exists.update_attributes(company: company)
else
DefaultCompany.create(company: company, user: self)
end
end
end
In my opinion, I always create a class method if the method in question represents information/behavior that is quite generic among all the objects instantiated, different from the instance methods, that I use when I believe it's more like a specific action of the instantiated object in question.
But that is my point-of-view.
A few things: do you have a separate table for DefaultCompany? This seems like it should be a boolean flag on the company table.
Next, is there an association between companies and users? If so, it seems the best way to do it would be
In the user model
def set_default_company(company)
self.companies.each do |c|
c.update_attributes(:default => false)
end
company.update_attributes(:default => true)
end
Or in the Company model
def set_as_default
update_attributes(:default_company => true)
end
in my application, I have a "User" model. Each user can have multiple (email) addresses which are defined in the model "Address":
Class User < ActiveRecord::Base
has_many :addresses
def is_authorized(op)
# returns true or false
end
def is_owned_by(user)
# returns true or false
end
end
Class Address < ActiveRecord::Base
belongs_to :user
end
Inside the AddressController class, the currently logged in user is available in the "#user" instance variable. The controller prevents ordinary users from editing, deleting, viewing etc. addresses which don't belong to them - but he does allow an administrative user to edit those. The AddressController class can ask the AddressModel if the user currently logged in is performing normal or superuser operations.
This all works nicely and database updates are made as expected, however, I'd really like to have different HTML views depending on the mode of operation. I can only think of two ways to achieve that:
Make the mode of operation (normal/privileged) known in the AddressController class (using an instance variable, e.g. #privileged) and use an "if" statement in the view.
Use something like an "after_filter" in the address controller to render a different layout.
If it is possible to display the results of executing a single controller in two completely different layouts, depending on it's mode of operation, what is a good way to achieve that?
Thanks in advance
Stefan
You can specify which view to use to display the result of an action in the action itself. You can also specify which layout to use too. So, for example:
def my_action
if #user.is_authorised(...)
render :action => 'admin_action', :layout => 'admin'
else
render :action => 'non_admin_action', :layout => 'non_admin'
end
end
This will render either admin_action.html.erb or non_admin_action.html.erb depending on the returned value from is_authorised. The :layout option is, er, optional and refers a layout in views/layouts. There are various other options the render call which you can find in the documentation for render.
You can specify the layout of the view for that particular controller, or the whole application in the application controller by:
class SomeController < ApplicationController
layout :set_layout
def set_layout
#user.is_authorized(...) ? "privileged_layout" : "normal_layout"
end
...
end
You can try to figure it out here: http://guides.rubyonrails.org/layouts_and_rendering.html#using-render, under 2.2.12 Finding Layouts
Hope this helps =)
You can simply call the render method manually at the end of your controller action:
if #privileged
render :action => 'show_privileged'
else
render :action => 'show'
end
This will render app/views/myview/show_privileged.html.erb or app/views/myview/show.html.erb. Alternatively, you can use the :template option to give an explicit template file to the render method.
If this is the only controller in your app where you're if/else'ing all over the place that's probably fine. If you start doing this type of logic everywhere that should tell you that you're doing too much at once.
The answer you accepted (which is fine and works!) has a different layout and a different view, to me that says the controller is doing too much - I'd split this out into an admin controller.
You should put administrative actions in an an administrative namespace and restrict it there. Create a directory called admin in your controllers directory and add an _application_controller.rb_ in there:
class Admin::ApplicationController < ApplicationController
before_filter :check_authorized
private
def check_authorized?
if !logged_in? || !current_user.admin?
flash[:notice] = "You've been very bad. Go away.
redirect_to root_path
end
end
end
Now you can put controllers into this namespace and make them inherit from Admin::ApplicationController too.
I'd like to create a before_filter method in my application controller like this...
def check_role(role_name)
unless logged_in_user.has_role? role_name
flash[:notice] = 'Access to that area requires additional privileges.'
redirect_to :back
end
end
However, it doesn't look as though before filters can take parameters.
Is there a way to parameterize this call, or am I trying to drive a screw with a hammer?
You should be able to do this with a block:
before_filter {|controller| controller.check_role('admin') }
You can use a bit of meta-programming. Something like this (completely untested, just something to give you an idea of how it might go):
Module RoleWithIt
Role.all.each do |role|
define_method("check_#{role.name}_role".to_sym) do
check_role(role.name)
end
end
def check_role(role_name)
return if logged_in_user.has_role?(role_name)
flash[:notice] = 'Access to that area requires additional privileges.'
redirect_to :back
end
end
ApplicationController.send :include, RoleWithIt
To have it load when your app initialises, just put it in a file called role_with_it.rb and put it in your lib directory.
am I trying to drive a screw with a
hammer?
Er, possibly ;-)
If I'm reading this correctly, you have a situation where actions within a controller have different access levels, so you want to remove the duplication by creating a single check function?
So you're looking to do something like this?
before_filter :check_role('admin'), :only => [:admin, :debug]
before_filter :check_role('power'), :only => [:edit, :delete]
But the parameter in parens thing is not legal. And anyway, I still see a fair bit of duplication here!
In general, with an area of functionality as well-visited as controller filters, if you can't do something, it's probably because you're looking at something the wrong way. (Remember that Rails is proud to describe itself as "opinionated software"!)
How would it be if you were able to know the action name in your filter method?
Then we'd have
before_filter :check_role
Which is pretty DRY.
We could define permissions in a Hash, perhaps:
Perms = { :admin => ['admin'], :edit => ['admin', 'power'], etc
... which seem to encapsulate the distinct elements of the duplication. If it got complex then the whole thing could move off into a table, although then you're probably duplicating functionality already available in a plugin.
And we'd have
protected
def check_role
for required_role in Perms[params[:action]]
return if logged_in_user.has_role? required_role
end
flash[:notice] = 'Access to that area requires additional privileges.'
redirect_to :back
end
Or something similar. params[:action] works on my current Rails version (2.1.2), although the Rails book (v2) mentions an action_name method that seems to return blank for me.
I don't believe you can pass parameters to filters. So what I have do in the past is made static methods that pass the parameter to the method that needs the params.
So I would have something like this:
def check_role(role_name)
unless logged_in_user.has_role? role_name
flash[:notice] = 'Access to that area requires additional privileges.'
redirect_to :back
end
end
def check_admin_role
check_role('admin')
end
def check_blah_role
check_role('blah')
end
Then in your controller you'd just call
before_filter :check_admin_role
There is probably some way to implement this with meta-programming but I am still quite a n00b and haven't figured that part out yet ;)
it's an old question, but if somebody still wonders, a good asnwer for rails 4 can be found here