Caching DB Result As Global Var - ruby-on-rails

Hi I currently have a helper method that gets Klass.all.map{|m| m.name}. Now I use ids to get the name from the array and if I add more it'll automatically update.
When I use this helper method in a loop in the view, I think it will make multiple queries to get the Klass each time which means a lot of extra work.
I was wondering how I can "cache" this array or if I should be doing this a better way.
Thanks!

SQL caching is done automatically if you're within the same action. You can see here for a more detailed explanation. Just by the way, it would probably be more efficient to use pluck, as in Klass.pluck(:name). This would optimize your SQL query.

Your helper method should look similar to this
def klass_names
#klass_names ||= Klass.all.map{|m| m.name}
end

Related

Need to store query result instead of storing query in ActiveRecord

I am having following function to find some of the selected records from public pages.
def find_public_page(title)
#footer_public_pages ||= PublicPage.where(title: %w(welcome_to_toylist terms_of_services buying_a_toy selling_a_toy requesting_a_toy ad_guildelines))
#footer_public_pages.find_by(title: title)
end
what I need is that #footer_public_pages should store the result set in first time and then on next time it will directly hit the find_by query instead of firing two queries.
Any help would be appreciated.
You can try this. I hope this will help You.
def find_public_page(title)
#footer_public_pages ||= PublicPage.where(title: %w(welcome_to_toylist terms_of_services buying_a_toy selling_a_toy requesting_a_toy ad_guildelines)).group_by(&:title)
#footer_public_pages[title].first unless #footer_public_pages[title].blank?
end
The instance variable #footer_public_pages belongs to a class, in this case probably the controller. Because of the way the controller works, every action (i.e. redirect) is going to be a new instance, and will need to set that value again on calling find_public_page. The way it's currently coded would help you if you were calling the method multiple times in the same action (and/or its views), but I doubt that is the case. Instead, if you're trying to keep this variable across multiple pages, you'll want to use a session variable. So you could say:
def find_public_page(title)
session[:footer_public_pages] ||= PublicPage.where(title: %w(welcome_to_toylist terms_of_services buying_a_toy selling_a_toy requesting_a_toy ad_guildelines))
session[:footer_public_pages].find_by(title: title)
end
If you follow this approach, just be warned that these session variables will only be dumped when the user closes the browser (NOT when they navigate away from your site), so you have to be careful in managing them.

Remove element from ActiveRecord_Relation in rails

How can i remove the last element from an ActiveRecord_Relation in rails?
e.g. if I set:
#drivers = Driver.all
I can add a another Driver object called #new_driver to #drivers by doing:
#drivers << #new_driver
But how can I remove an object from #drivers?
The delete method doesn't seem to work, i.e.
#drivers.delete(0)
You can use the reject! method, this will remove the object from the collection without affecting the db
for example:
driver_to_delete = #driver.first # you need the object that you want removed
#drivers.reject!{|driver| driver == driver_to_delete}
Very late too, but I arrived here looking for a fast answer and finished by thinking by myself ;)
Just to clarify about the different answers and the Rails 6.1 comment on accepted answer:
The OP wanted to remove one entry from a query, but NOT remove it from database, so any answer with delete or destroy is just wrong (this WILL delete data from your database !!).
In Ruby (and therefore Rails) convention, shebang methods (ending with !) tend to alter the given parameter. So reject! would imply modifying the source list ... but an ActiveRecord_Relation is basically just a query, NOT an array of entries !
So you'd have 2 options:
Write your query differently to specifically say you don't want some id:
#drivers.where.not(id: #driver_to_remove) # This still is an ActiveRecord_Relation
Use reject (NO shebang) on your query to transform it into an Array and "manually" remove the entry you don't want:
#drivers.reject{ |driver| driver == #driver_to_remove}
# The `reject` forces the execution of the query in DB and returns an Array)
On a performance point of view, I would personally recommend the first solution as it would be just a little more complex against the DB where the latter implies looping on the whole (eventually large) array.
Late to the question, but just had the same issue and hope this helps someone else.
reject!did not work for ActiveRecord_Relation in Rails 4.2
drop(1) was the solution
In this case #drivers.drop(0) would work to drop the first element of the relation
Since its an array of objects, have you tried to write something like #drivers.delete(#new_driver) or #drivers.delete(id: #new_driver.id) ?
This is the documentation you need:
#group.avatars << Avatar.new
#group.avatars.delete(#group.avatars.last)
--
.destroy
The problem you've got is you're trying to use collection methods on a non-collection object. You'll need to use the .destroy ActiveRecord method to get rid of the record from the database (and consequently the collection):
#drivers = Driver.all
#drivers.last.destroy
--
Scope
.delete will remove the record from the DB
If you want to pull specific elements from the db to populate the #drivers object, you'll need to use a scope:
#app/models/driver.rb
Class Driver < ActiveRecord::Base
scope :your_scope, -> { where column: "value" }
end
This will allow you to call:
#app/controllers/drivers_controller.rb
def index
#drivers = Driver.your_scope
end
I think you're getting the MVC programming pattern confused - data manipulation is meant to happen in the model, not the controller
As stated above, reject! doesn't work in Rails 4.2, but delete does, so #drivers.delete(#new_driver) works, and more generally:
#drivers.delete(Driver.where(your condition))

Is there any way to define a model's attribute as always html_safe?

I have a model called Feature with a variable called body_string, which contains HTML markup I'd like to render, rather than escape.
Every time I reference body_string in my views, I need to use <%=raw or .html_safe. This seems redundant and not-so-DRY.
Is there any way that I can establish once-and-for-all the body_string variable as html_safe?
I'm assuming this would happen in the app/models/feature.rb file, but I can't figure out what the right syntax would be, exactly. I've thought of this:
def body_string
return self.body_string.html_safe
end
But Rails doesn't like it; it raises a stack level too deep exception.
Naturally I could define a variable/method with a different name:
def safe_body_string
return self.body_string.html_safe
end
And then just change all references in the views from body_string to safe_body_string. But somehow this seems almost as un-DRY as simply using raw or .html_safe in the first place.
Any insights to how best to handle this? I feel like there must be something really elegant that I'm just not seeing.
Just use read_attribute to avoid the recursive call to body_string:
def body_string
read_attribute(:body_string).html_safe
end
read_attribute is complemented by write_attribute for setting attributes from within your model.
A note on style: Don't use explicit returns unless you actually need them. The result of the last statement in a method is implicitly the value returned from the method.
While #meager's answer will definitely work, I don't think this logic belongs in a model. Simply because it adds view-level concerns (HTML safeness) to the model layer, which should just include business logic. Instead, I would recommend using a Presenter for this (see http://nithinbekal.com/posts/rails-presenters/ or find a gem for this -- I personally love Display Case). Your presenter can easily override the body_string method and provide the .html_safe designation when displaying in the view. This way you separate your concerns and can continue to get body_string from other models without mixing in the view concern.
Maybe this gem is useful for you. I also wanted to stop repeating html_safe all the time when the content is completely trustable.
http://rubygems.org/gems/html_safe_attribute
Or you can also use this approach,
def body_string
super && super.html_safe
end

Can I access the collection an instance method was called on in ruby on rails

I'm working on implementing a search form in a ruby on rails application. The general idea is to use form_tag to submit the search fields (via params) to a search function in the model of the class I'm trying to search. The search function will then iterate through each of the params and execute a scoping function if the name of the function appears in params.
The issue is that when I call the search on a collection like so:
#calendar.reservations.search({:search_email => "test"})
I don't know how to refer to the collection of #calendar.reservations from within the search function.
Additionally I'm confused as to why #calendar.reservations.search(...) works, but Reservations.all.search gives me an error saying you can't call an instance method on an array.
I've got the details of the search method over here: https://gist.github.com/783964
Any help would be greatly appreciated!
I don't know how to refer to the
collection of #calendar.reservations
from within the search function.
If you use self (or Reservation, it's the same object) inside the classmethod, you will access the records with the current scope, so in your case you will see only the reservations of a particular calendar.
[edit] I looked at you search function, and I think what you want is:
def self.search(search_fields)
search_fields.inject(self) do |scope, (key, value)|
scope.send(key, value)
end
end
Additionally I'm confused as to why
#calendar.reservations.search(...)
works, but Reservations.all.search
gives me an error saying you can't
call an instance method on an array.
#calendar.reservations does not return a standard array but a (lazy) AssociationCollection, where you can still apply scopes (and classmethods as your filter). On the other hand Reservation.all returns a plain array, so you cannot execute search there (or any scope, for that matter).
You don't really need a search method at all, as far as I can tell.
Simply use where:
#calendar.reservations.where(:search_email => 'test')
I would strongly encourage you to look at the MetaSearch GEM by Ernie Miller. It handles the kind of thing you're working on very elegantly and is quite easy to implement. I suspect that your view code would almost accomplish what the GEM needs already, and this would take care of all your model searching needs very nicely.
Take a look and see if it will solve your problem. Good luck!
Reservation.all.search doesn't work because it returns all the results as an array, while Reservation.where(..) returns an ActiveRecord object (AREL). Reservation.all actually fetches the results instead of just building the query further, which methods like where, limit etc do.

Rails optmization (with activerecord and view helpers)

Is there a way to do this in Rails:
I have an activerecord query
#posts = Post.find_by_id(10)
Anytime the query is called, SQL is generated and executed at the DB that looks like this
SELECT * FROM 'posts' WHERE id = 10
This happens every time the AR query is executed. Similarly with a helper method like this
<%= f.textarea :name => 'foo' %>
#=> <input type='textarea' name='foo' />
I write some Railsy code that generates some text that is used by some other system (database, web browser). I'm wondering if there's a way to write an AR query or a helper method call that generates the text in the file. This way the text rendering is only done once (each time the code changes) instead of each time the method is called?
Look at the line, it may be going to the database for the first one but ones after it could be saying CACHE at the start of the line meaning it's going to ActiveRecord's query cache.
It also sounds to me like you want to cache the page, not the query. And even if it were the query, I don't think it's as simple as find_by_id(10) :)
Like Radar suggested you should probably look into Rails caching. You can start with something simple like the memory store or file cache and then move to something better like memcached if necessary. You can throw in some caching into the helper method which will cache the result after it is queried once. So for example you can do:
id = 10 # id is probably coming in as a param/argument
cache_key = "Post/#{id}"
#post = Rails.cache.read(cache_key)
if #post.nil?
#post = Post.find_by_id(id)
# Write back to the cache for the next time
Rails.cache.write(cache_key,#post)
end
The only other thing left to do is put in some code to expire the cache entry if the post changes. For that take a look at using "Sweepers" in Rails. Alternatively you can look at some of the caching gems like Cache-fu and Cached-model.
I'm not sure I understand your question fully.
If you're asking about the generated query, you can just do find_by_sql and write your own SQL if you don't want to use the active record dynamic methods.
If you're asking about caching the resulset to a file, it's already in the database, I don't know that if it was in a file it would be much more efficient.

Resources