I am a rails newbie writing my first App and I am having a hard time figuring out exactly how views work. I am just trying to write a simple navigation menu that dynamically grabs the list of all my "Services" and then lists the sub-catagories "Service Offerings" under those. This navigation needs to show up on every page in my App.
I tried the following just to test if I could pull the data.
#application_helper.rb
def service_list
ServiceOffering.find(1)
end
#application.html.erb
<%= service_list.name %>
<%= yield %>
This works but I read that I should not put that sort of thing in the application_helper. I moved it to the application_controller.rb but it did not work there. I am really confused about this whole thing. Obviously, I have instance variables for #service_offering in my CRUD but they all mean different things depending on the action. I just want to define one that I can access from anywhere. I have watched and read a lot of tutorials in the past few weeks but hardly any of them touch on application wide stuff like this. I would really appreciate an easy to understand explanation of how to handle things of this nature. Thank you!
If you want to set it up in the application controller you have a couple options:
you could set it up with a helper method so
#application_controller.rb
helper_method :service_list
def service_list
ServiceOffering.find(1)
end
or you could set it up with a hook and put it into a special instance variable that will not conflict with the other ones that you have
#application_controller.rb
before_filter :service_list
def service_list
#special_service_offerice = ServiceOffering.find(1)
end
Related
I have a User model which gets accessed through a REST API and ActiveAdmin. Since there is a lot duplicated code (almost all) it might be a good idea to dry things up. There are a lot of topics out there handling this issue but i do not find a proper way. I put some of the ideas i have below
UserController instance in each controller
Again duplicated code gets created and the functions
#AA
controller do
def create
#userController = UserController.new
#userController.create(params)
end
end
#REST API
#Create instance through callback
def create
#userController.create(params)
end
Inheritance
I would like to have one basic CRUD controller lying above my model through inheritance like this
Class BaseUserController < ApplicationController
Class Api::UserController < BaseUserController
but (yet) i dont know how to do this in AA also in mind with the inherited_resource gem in mind
Concerns
Also concerns seem to be a nice way. But i would then create seperate concerns for each action to have one concern to exactly handle one behaviour
aspect and also to make them more reuseable . This then again seems to unnecessarily bloat the app and also concerns are not really made to be used like that. (?)
Modules/Mixins
Since modules cannot be instantiated but implement actions fully, this might just be what i am looking for. So each controller includes the desired actions or extend fro ma model rather than a class then.
The thing is - the more i read the more i get confused. I hope some of you guys might help me bring some light or i might turn to the dark side an use C# and .NEt again :D
Thanks in advance!
Example Code
I took this create method for a SMS resource in both, the REST API and the AA, where i call a couple of Services, but if complexity grows i have to add these lines in two places. How could i avoid that? Cant i have a basic SMSController which is used from the API endpoints as well as the AA interface?
controller do
def create
#customer =Customer.find_by_phone(params[:sms_message][:phone])
SmsService.sendSMS(#customer[:phone],Cryptoalgorithm.sms(#customer[:id], #customer[:recharge_token],params[:sms_message][:text].to_i)
#sms = SmsMessage.create(:customer_id => #customer[:id], :text => params[:sms_message][:text], :phone => params[:sms_message][:phone])
#customer.increment!(:recharge_token)
redirect_to admin_sms_message_path(#sms[:id])
end
end
Iam new in Ruby on Rails. Normally I work with other web languages and now (of course) I try to compare it with other languages using on web.
But sometimes i have some problem to understand the philosophy and the character of Ruby on Rails. Of course i understand the concept of MVC.
But now Iam not absolutely sure:
Is ist OK to create and use a controller without views? In some cases you need a "class" for some usefull functionality used by other controllers they have views. Or is it a better style to use a module?
I try to find it out by reading a lot of articles and examples, but didnt find detailed information about this content.
When developing Ruby On Rails apps, it's recommended to put most of your business logic in the models, so for the controllers the logic need to be minimal to provide info for the views, so if it doesn't work with a view chance that you need a controller are really low.
Is it OK to create and use a controller without views
Yep it's okay.
The key thing to keep in mind is that Ruby/Rails is object orientated.
This means that every single you do with your controllers/models etc should have the "object" you're manipulating at its core.
With this in mind, it means you can have a controller without corresponding views, as sometimes, you just need to manipulate an object and return a simple response (for example with Ajax).
--
We often re-use views for different actions (does that count as not having a view):
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def search
render :index, layout: false
end
end
The notion of skinny controller, fat model is sound in principle, you have to account for the times when you may need small pieces of functionality that can only be handled by a controller:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def update
#user = User.find params[:id]
#user.update
respond_to do |format|
format.js {render nothing: true}
format.html
end
end
end
A very rudimentary example of Rails is a drive-thru:
view = input interface
controller = accepts order & delivers
model = gets order packaged etc in backend
There are times when the controller may not need a view (for example, if you update your order with specific dietry requirements), and thus the notion that every controller action has to have a view is false.
It's really about making your controller versatile enough to manage your objects correctly.
Shared controller methods can be placed in ApplicationController. Also, in Rails 4 there are concerns (app/controllers/concerns/) where you can put the modules with methods that can be used by multiple controllers.
Controller handles request and renders corresponding view template. So controller without view is absolutely nonsense.
Every time request will execute this controller, it will just end with missing template error, so you will need to create view folder and put empty files with action name inside of it, which is obviously stupid.
Im using bootstrap & rails and have a user model and post model..users create posts (collections)..
with bootstrap in the navbar i want the user to be able to click a dropdown which displays the name's of their posts..i did this on one controller with a private method and a before_action but i don't want to do this for all the controllers and it didn't work for the application controller...
is there a better way to do this??
I was doing this
def list
#user = User.find_by_username(params[:id])
#collections = #user.collections
end
and a
before_action :list
at the top of the controller
What's the most semantic way to accomplish this??
If you could move both to your application controller, then it would be available to any controller. More generally, I'm not sure if this is the best approach to solve your problem.
These tips might also be useful.
Are you using devise? Or some other authentication plugin? If so you're likely going to have a current_user helper. This would allow you to simply do #collections = current_user.collections
To the extent possible, I recommend using more descriptive names for your actions and parameters. def fetch_list_collections might be a better name or instead of passing a param named id, perhaps your param should be named username. These naming conventions become extremely important both for others who might look at your code as well as for yourself if you return to it and are trying to remember what you wrote N months ago.
Your list action is generating a N+1 queries. Meaning that you're hitting the database multiple times when you should do so just once. See the rails guide on this. You might also look at ways to avoid this w/ devise. Devise is pretty well documented and I'll bet there is something in the wiki discussing this.
You may want to consider limiting when you call this action - at a minimum - a post request to an update action? What about before they've logged in? current_user might be nil and you'd have an error attempting to call a collections method on nil.
Take your time learning this stuff. You don't have to learn it all at once, but I thought the above might be helpful.
I got it to work with this in the application controller
before_action :list
private
def list
#collections = current_user.collections
end
thanks #arieljuod
I see this all the time in rails code:
before filter :get_post, only: [:edit, :update, :destroy]
def edit
# code .......
end
def update
# code .......
end
def destroy
# code .......
end
private
def get_post
#post = Post.find(params[:id])
end
I understand it keeps from repeating the same line of code three times, but isn't there a much easier to read and better way to accomplish the same thing by just refactoring the code into a private method without hiding the instance variable and the before filter?
private
def get_post(post_id)
Post.find(post_id)
end
Then you can keep the instance variable in the action
def edit
#post = get_post(params[:id])
end
It doesn't make sense conceptually to hide instance variables in private methods. Why is this so prevalent in rails?
You'll probably see a lot of differing opinions on this particular practice. Personally, I agree with you; I feel that the strict adherence to DRY only serves to introduce a bit of confusion by hiding away the instance variable in a method down at the bottom of the file. I'm not even sure a get_post method really buys you very much myself. That said, I tend to prefer explicicity (if that's a word) over terseness in many cases, and for a team that uses this trick consistently in every controller, there may not be as much (if any) confusion.
If the filter encapsulates a little more complexity, it may be worth it. For example, the popular authorization library CanCan by Ryan Bates introduces a controller class macro called load_and_authorize_resource that installes before_filters such that RESTful resources are automatically loaded and authorized for access against the current user. This could potentially be more than a line or two per method, and would not repeated verbatim each time.
There's a popular middle-ground that some Rails devs use, which is to specify the before_filter as a block, so you don't have to repeat yourself, but the instance variable is up at the top of the file:
before_filter(only: [:edit, :update, :destroy]) do
#post = Post.find(params[:id])
end
As mentioned in the comments, there are some gems, like decent_exposure, which help formalize this pattern and make it significantly less "hidden" or confusing (since it's declared explicitly and the API is known).
As other have stated, this is a matter of opinion. I started off using before filters to hide common instance variables between controllers. I still do this on occasion, but I have found it to be a code smell that the controller is bloated. If you have so many instance variables that you need to hide them in filters you are doing too much. My opinion is that the controller action should be very small, and if all the actions have one line in common that handles the instance variable its not an issue.
My remedy for getting my controller actions to a level that made me comfortable with having a common instance variable was to use service object/presenters to consolidate the information that was once in several instance variables into a single instance variable per controller action. I've found this been a huge improvement in the readability and maintainability of my controllers and views.
I think your suggestion is an improvement over the typical Rails convention. I'd take it a step farther, though and drop the instance variable entirely. It's misleading to use an instance variable because it's typically not representing shared state across multiple controller actions, and often not even shared between any methods (except the before filter that assigns it).
The Rails convention of populating an instance variable for use in a view is a bad idea whose use should disappear. In a clean MVC implementation, nothing from the controller would be available in the view. I think this is a much cleaner implementation which properly separates the layers:
def edit
post = get_post(params[:post_id])
render 'edit', locals: { post: post }
end
No leaking state, no reliance on an instance variable in the view, explicit intent, and a view that is more easily reusable and can be rendered from within other views without injecting a hacky inline instance variable assignment.
Rails has a number of conventions that make for quick bootstrapping, but they don't make for good code.
It's a convention over configuration thing.
Neither approach is wrong, but people who like Ruby often prefer minimalist coding patterns, and value using the quickest approach to solve a problem so they can get on to solving more complex and interesting problems
If one has seen this approach used often enough, it becomes second nature to have all of the member functions (obviously not the collection functions) preload the member variable for you before you define your code. It gets to the point where specifying the member variables in the function does not create a significant clarity advantage (if any).
There is also another approach using helper_method. It's looks almost as you want it.
http://rails-bestpractices.com/posts/57-substituting-before_filter-load_object
I've currently got this in my view file:
<%= "<em>(#{package.to_company})</em>" unless package.to_company.blank? %>
Is my understanding correct that I should move that to a helper?
ie.
def package_company(package)
"<em>(#{package.to_company})</em>" unless package.to_company.blank?
end
I ask because I've got a few dozen unless statements in this specific view based on if a user submits specific data or not. Seemed overkill to create a few dozen helper methods for just single unless statements.
Create this helper if you'are going to re-use this exact chunk of code many times (and stay DRY)… if you're going to use it once, you don't need an helper…