Model methods in views - ruby-on-rails

I have the following piece of code in my view:
<% if #user.get_products.any? %>
<%= f.select('products', #user.get_products.collect { |user| [user.name, user.id]}) %>
Where get_products is making an ActiveRecord query to DB. So I wonder if it actually does two same queries or uses cached result from first query on second call? I tried to check it in server logs - but found nothing. I would also like to know if I can control this behavior somehow, i.e. set/unset cache for this view.
Thanks!
UPDATE:
I think it violates MVC too, but what confused me was IDE warning: "view should not share more than two variables with controller."
However, I am creating somewhat "one page" website, so I need to have #user, #nearest_users, and #user_products in the same view. So I found the following article:
http://matthewpaulmoore.com/post/5190436725/ruby-on-rails-code-quality-checklist#instances
which said
Only one or two instance variables are shared between each controller
and view.
Instance variable hell - when a lot of instance variables are shared
between a controller and a view - is easy to do in Rails. It’s also
potentially dangerous to performance, because you can end up making
duplicate calls on associations unknowingly. Instead, your controller
should only manage one instance variable - and perhaps a second for
the current_user. That way, all calls to associations are loaded “on
demand”, and can be instance-variable-cached in a single place.
This methodology also works out well for fragment caching, because you
can check caches in views before actually loading associations.
For example, instead of having your Blog controller create an instance
variable for both #post and #related_posts, just make a single method,
#post, and give your Post model a related_posts method, so you can
just call #post.related_posts in your views.

To answer your question: Queries in view don't get cached.
What's more, Ruby codes in ERB template are executed using eval, which is very inefficient.
So my advice is: avoid writing logic in view, it's kind of bad practice.

Related

What is automatically "cached" when rendering a view?

Let's say we have these two outputs in a view:
#post.user.first_name
current_user.posts.size
If the outputs above would be called multiple times each in a single view, is Rails "smart enough" to not hit the database every time?
If the answer is yes - are there any "general rule" about this worth knowing?
If the answer is no - would a good practice then be to store the associated object/objects in it's own variable?
ActiveRecord by default caches queries for performance. If you do AC query in console a few times you will see that the second query executes much faster due to AC cache. So I guess this works for queries in the view as well.
You can manually cache objects with Rails Fragment Caching feature.
Fragment Caching allows a fragment of view logic to be wrapped in a
cache block and served out of the cache store when the next request
comes in.
Also there is Cache Stores for you to use.
Rails provides different stores for the cached data (apart from SQL
and page caching).
Queries for a view are done in the controller action that renders the view. You will notice that you define #post in your controller action but you may not see current_user defined. This is typically because you're using the devise gem and the code defining the current_user method is part of the gem.
Everything you need to render a view should be queried in your controller by ActiveRecord and in memory of the app in preparation of the rendering of the view. So multiple calls of #post or current_user shouldn't matter.
Sometimes objects are called via associations for a view eg. #post.user.name Is going to have to query for the user. This will work but it is better Model-View-Controller separation to eager load the users along with the posts in the controller. Following MVC and making sure your queries happen in the controller will also help you avoid N + 1 query performance issues. See Rails Active Record Query Eager Load
An example of querying users with their posts.
#post = Post.find(post_params).includes(:user)

Ruby on Rails: Creating a link based on new database entry

I'm in the process of updating a website I made almost 2 years ago. It was my first real website and I made some mistakes (some more serious that others).
What apparently is one of my biggest is making database calls from the view.
I'm pretty damn sure there is a better way to do this:
Use Case:
Someone fills out a form for a new subject, populating the Subject table, and they have been marked "enrolled", Subject.enrolled = 1
Based on that, I now need to create a record in 5 other tables (such as Baseline)
Downhill from here, here is my method
Determine if the record exist based on subject_id from Subject (sub)
<$ if Baseline.where(subject_id: sub.subject_id).first != nil $>
If it does not exist, create the record, (otherwise display the link)
<%= Baseline.create(subject_id: sub.subject_id) %>
This all happens in the view, and creates a front-end table with links to each record in the process. So I'm creating records based on for-loop logic...
Question:
So I'm looking for direction. I don't want to guess how to do this - I'm pretty sure the model/controller should do this - I want to learn how to do it correctly. How do I create records automatically, based on a value in a table?
Thank you for your time.
Not quite sure how your domain and code looks like, but to answer this question: 'How do I create records automatically, based on a value in a table?', it seems that you could use ActiveRecord callbacks, like this:
class Subject < ActiveRecord::Base
after_commit :create_baseline_if_enrolled, on: [:create, :update]
private
def create_baseline_if_enrolled
return unless enrolled?
# enrolled? == true, you may create these models here
end
end
To answer your question:
It depends :) This is just one possible solution. Another one would be to put such a custom logic in your SubjectsController and call it directly from the #create, #update methods. Both approaches have pros and cons. For example, abusing callbacks (anywhere) makes code less readable and harder to debug. On the other hand, putting such logic in controllers puts a burden on you that you have to remember about calling it if you happen to be editing subjects in other places (but is more explicit). Whichever way you choose, remember not to make your classes too fat, for example try to use service object pattern to separate such custom logic as soon as you feel like it is getting out of hand. :) And don't forget about tests - when things go wrong, tests make refactoring easier.

Where to put mathematical calculations in Rails?

In case you were unaware, this is a beginner's question.
Having learned a bit of Ruby, I have ventured onto Rails, but have run into a brick wall when it comes to organizing methods. I'm building something which is supposed to take the data/parameters you get when someone creates something, but instead of showing it like you would a tweet or blog post, I want to use them as variables in some mathematical calculations, the results of which I then want to show.
Now, in Ruby, I would make a method for each little math operation, to make sure they (the methods) have a single responsibility each. In Rails, though, what am I supposed to do? In case you don't understand my problem, I think it has to do with my lack of understanding of instances in Rails. Instances in Ruby can call upon the methods I make, but where do I call upon my methods (actions?) in Rails?
You should follow the MVC paradigm:
The controller should receive the parameters that the user gave via some html form.
Then that controller should instantiate an object that is doing the math calculation and then the controller should render a view to present the results to the user.
The place where the controller and the views are stored is already decided by the framework:
controllers are in app/controllers and the views in app/views/<controller_name>
Now the question is where you put the class that performs the calculations.
You might think about the app/models folder, but that one is typically for the models that inherit from ActiveRecord::Base, ie, all those that are persisted in the database.
Normally the kind of class that you are implementing lives in the lib folder.
For example, you might have the following structure:
app/controllers/calculations_controller.rb
def perform_calculations
math_calculator = MathCalculator.new params[:operation]
#result = math_calculator.calculate
end
lib/math_calculator.rb
class MathCalculator
def calculate
# whatever you need to do here
end
end
app/views/calculations/perform_calculations.html.erb
<%= #result %>
There's nothing official, I think its helping you
http://www.caliban.org/ruby/rubyguide.shtml

Clean controllers: preparing data for views

Consider I have a controller with an action which renders a view. The view needs data to render. I know the following ways to prepare and send it to the view:
Using instance variables
class CitiesController < ApplicationController
def index
#cities = Cities.order(:name).limit(10)
end
end
This is the default approach which can be found in Rails documentation, but it has some disadvantages:
It makes the action code fat which becomes responsible not only for controller logic, but also for the data preparation.
Views need to access this data through instance variables – those #-variables break the principle of least astonishment.
Using helper methods
class CitiesController < ApplicationController
helper_method :cities
def index
end
def cities
#cities ||= Cities.order(:name).limit(10)
end
end
That's the way I prefer the most. It keeps action methods clean, so I may implement controller logic there not mixing it with data preparations in one method. Also, there's no need to use mysterious instance variables in views, making them isolated. However:
The data preparations are still in the controller. It becomes unreadable when there are a lot of these helper methods, especially when they are relative to different actions/views.
There's a need of having a unique name for each helper method. Say, I can't have a method called products which will return different data for different actions (of course, I can do it in one method, but it would look ugly).
Using the facade pattern
Partially the problem is solved in this article: https://medium.com/p/d65b86cdb5b1
But I didn't like this approach because it introduces a #magic_facade_object in views.
Using inherited resources
It may look beautiful in examples, but in my opinion when it comes to the real code, controller code becomes a spaghetti-monster very fast. The other thing is that a page view usually needs not only the resource but also other data to render (sidebar blocks, etc.) and I still have to use another way to prepare it. Combining different approaches makes the code even more unreadable. Finally, I don't like to use resource variable, because it makes not very clear what is the view about.
So, here is the question. How do you keep your controllers clean?
How do you keep your controllers clean?
By writing DRY code and sprinkling some gem magic around.
Having a look at your bullet points, I think I have a different opinion on most of the stuff.
#cities = Cities.order(:name).limit(10) is exactly what i think belongs into a rails controller and it does not violate the principle of least surprise, it's kind of the opposite. instance variables are the default way of passing around variables from controllers to views, even though that is a pretty ugly thing to do. it's "the rails way" (TM)!
decent_exposure takes away most of these concerns
please stop applying old-school pattern to rails or ruby code. it's really just useful in large applications where you are struggling to keep sane with the amount of code that's within a single controller method. write clean code, test it thoroughly and you will be fine 80% of the time.
don't use "one size fits all" tools. most often, you need to write more configuration than you would need code to make it work. it's also getting a lot more complex through this kind of things.

Rails, mutiple create methods in one controller?

I need to have two (or maybe even more) different create (and update) methods in one controller. I already have views displaying the forms, so I basicaly only need to tell the submit which method to call in the controller. Is that possible? If so, how? Or can I only have one create method and have that method do different things depending on which view called the method?
Thanks
Ignoring the fact that this sounds like a really terrible idea, it's possible. You will need to add some more routes that will match the new actions in your controller. You won't be able to call them 'create' and 'update' because method names must be unique within the same class.
Having said that, I really beg you to rethink your approach. REST, as described in the Rails Getting Started guide, by far the standard for building Rails applications. If you're not familiar with it, I would recommend stopping where you are and reading up on it. Your application will be much easier to build and maintain, and you won't waste time asking structural questions. If you are familiar with it and are choosing to ignore it, then I wish you the best of luck.
you can use this command:
rails g scaffold_controller 'controller_name'
or if spastic method you can use this:
rails generate controller 'controller_name' add new
Let's say that you have an object Book. You can change values of Book in any method inside of your books_controller.rb as long as that method has access to #book.id.
def crazy_create_method
book.create (book_params)
book.save
end
That being said, try to stick to the default new/create methods and if you need to get weird later on it's always easy to call the code belong in whatever method you need. Rails bakes a lot of out of the box functionality into the default REST actions.
book.title = my_title
book.save

Resources