Rails STI, subclasses don't show up unless used? - ruby-on-rails

I am using Rails STI to model a complex domain. In a view, I am listing all subclasses with:
> <% StateDescription.subclasses.each do |state_description| %> <li>
> <%= state_description.to_s %> </li> <% end %>
With the intent to eventually make each label of the subclass a link to the individual "index" page for just instances of that subclass.
This saves me from having to have a list of all possible subclasses myself (and updating it later if I add more).
However, I have noticed that only those subclasses I have actually loaded from the database show up in the list. If I say StateDescription.all, then all existing subclasses show up (but none that have no instances yet). If I instead ask for all of a particular subclass, then only that subclass show up.
I imagine this is part of the "lazy loading" I have heard about. Is it? It SEEMS like the problem is that if I don't grab a particular "type" from the database, even if I have a model for it, it may as well not exist?
Is there a way around this, or am I doomed to have to write out a link for every single subclass I create?
Edit:
On the console, if I type
StateDescription.subclasses.count
I get 0.
If I then do StateDescription.all and THEN StateDescription.subclasses.count, i get 14.

After doing some digging, it seems your hypothesis about subclasses not showing up due to lazy loading appears to be correct. Since you're running your application in development mode, all your classes are not loaded until they are specifically called. In production, you would not have this problem since everything is loaded at once and cached.
One way to get around this problem, according to this post, is to do something like this:
[Subclass1, Subclass2, Subclass3] if Rails.env == 'development'
You could put this at the top of your controller so that it loads the instant the controller classes is loaded, or in a before filter.

Not tested!
StateDescription.select("distinct type").map { |sd| Kernel.const_get(sd.type) }

The easiest way to do this is eager_load! in development.
Something like:
<% Rails.application.eager_load! if Rails.env.development? %>
<% StateDescription.subclasses.each do |state_description| %>
<li><%= state_description.to_s %> </li>
<% end %>

Related

Conditionally Omit/Hide Button in Rails

My first time asking. I'm in an internship so I'm not that proficient in Rails. Currently I'm working in a code that isn't mine, it has no automated tests and no documentation whatsoever, so I spent lots of time trying to understand what the functions does before actually writing some code.
My question:
What I'm trying to accomplish is to hide/omit a button, under a set of conditions.
My research:
Already searched everywhere but with no luck to my specific problem. Tried the solutions here: How to disable all form_for input fields in Ruby on Rails app? and here: In Rails edit action form, hide a form field. Also read rails documentation about form helpers: https://guides.rubyonrails.org/v4.2.10/form_helpers.html
The problem:
There is an object called school that is divided into Schools and SchoolGroup, theoretically schools that have children should be SchoolGroup. I'm trying to omit the Delete button to every SchoolGroup that have Schools associated with it.
If I put like this nothing happens
<% if school.children.length < 1 %>
<%= button_to 'Delete', school, :data => {:confirm => 'Are you sure you want to delete this school?'}, :method => :delete, :class=>"btn btn-sm btn-delete" %>
<% end %>
However if I put like this
<% if school.children.length > 0 %>
every button disappears!
So, I'm inferring that every school probably have children, even though it shouldn't and I need to ask for a different condition. Or should I try a controller-wise solution? If so, what can I do?
I'm using ruby 2.2.10 and rails 4.2.10.
Thank you all for your time and any help :)
The school.children.length returns the size of the collection. So,new child is also counted.
length() Returns the size of the collection calling size on the target. If the collection has been already loaded, length and size are equivalent. If not and you are going to need the records anyway this method will take one less query. Otherwise size is more efficient.
You can use school.children.count to count the records that persisted in the database. This method call will fire a database query.

What is the purpose of nil? and exists? methods in ruby/rails conditions?

I often (very often) see code like this in the conditions:
<% catalog.each do |article| %>
<% if article.image.nil? %>
...
or (e.g. seen there)
<% catalog.each do |article| %>
<% if article.image.exists? %>
...
However we all know that nil interprets like false in the conditions. ActiveRecord query returns nil if nothing found.
Why not simply write:
<% unless article.image %>
(unless there is article do something)
instead of
<% if article.image.nil? %>
(if there is nothing at article.image do something)
and
<% if article.image %> instead of <% if article.image.exists? %>
I usually write the code without nil? and exists?. What am I missing? Is there any pitfalls?
In your example, and in many typical RESTful Rails patterns, you're correct that using the implicit version is identical in behavior.
But there are certainly suations where nil? and exists? are necessary and/or more readable...
For instance, nil? is the only option when you're reading a boolean attribute, and you need different behavior for false vs nil (since both are falsey in Ruby).
Or, assume in your example, that each Article has many images, and you define a method to get an Article's first image according to display order:
def primary_image
images.order(display_order: "ASC").limit(1)
end
In this case, if your Article doesn't have any images, primary_image will return an empty collection, which is truthy. So, in this case, you'd need to use exists? (or present?). Rails provides several convenience methods for checking data records.
(or make sure the primary_image method returns nil or false when the collection is empty)
Personally, I rarely use either one of them. Like you suggested, I mostly just use if or unless depends on the situation without bothering with nil? or exists?
As for the difference between the two:
nil? is a Ruby method to see whether the object is nil or not.
exists? is a Rails method to see whether the record exists in the database
I guess, just use whichever is more efficient. I have been trying to convert my codes to use try to avoid no method error.
Most of the time it's just to be more explicit I suppose.
In your example above it doesn't matter if you check explicitly or implicitly.
But if you want to check explicitly for nil for example, you use the #nil? because sometimes you want to react differently if false is returned.
As an example: false and nil are often not interchangeable for boolean values in a database.
Like #lusketeer already said is nil? a method of the ruby standard library and exists? a method of the Rails framework.
But I think almost always you are good with the implicit way.

Is there a way to do an early exit or "return" from a view in Rails?

Does Rails offer any way to exit early or "return" from a view that is being rendered?
For example, I have a view structured as follows:
<h1>Your records summary</h1>
<% if #current_user.has_records? %>
<p>You don't have any records.</p>
<% else %>
... long block of view emission code here ...
<% end %>
In non-erb code, I'd just return from that if condition, and wouldn't need to put that long block inside an else. It would look like:
<h1>Your records summary</h1>
<% if #current_user.has_records? %>
<p>You don't have any records.</p>
<% return (or something) %>
<% end %>
... long block of view emission code here ...
Is there a way to do something like that in the erb?
EDIT: To be clear, I'm not asking for alternate approaches, such as have been suggested. I'm not a Rails noob. I really just want to know whether Ruby provides a mechanism for what I suggested or not.
This is an old question, but I figured I'd take the time to spare future people the hour or so of their lives trying to figure out if this is doable.
I've discovered that, as far back as Rails 3.2, this is actually possible.
It requires some knowledge of ActionView internals and I do not advocate anyone do this in production code. Further, this particular implementation only works with the built-in ERB engine used by Rails which, itself, uses Erubi.
Alright, now that we're done with the warnings, on to the action!
The surprisingly simple answer is to return #output_buffer.to_s.
As long as you've closed all your HTML tags before returning, it seems to work and everything is correctly rendered.
For the curious, this output buffer is currently configured in the initializer for the Erubi ERB handler. With the rest of the work done by the underlying Erubi::Engine implementation.
I've poked around at something that provides a less brittle way to go about returning early, but I've unsuccessful so far.
In Rails 3.2.2, return inside a template works just fine for me.
<% return unless allowed_tags %>
IIRC there's no built-in mechanism for exiting a template early. This is in line with how (I think) a view layer should behave, although erb isn't quite a template engine.
The old-timey solution was to wrap your template in a catch and throw something from within the template, like a symbol.
You could also embed the eval inside a lambda, allowing return to work.
Still ew, though.
The best way is to handle the conditional in your controller before rendering the template. There you can early return a different template (like the Add Record form) or the results template.
You can also offload that logic to Javascript in the view itself.

Rails 3 and Memcached - Intelligent caching without expiration

I am implementing caching into my Rails project via Memcached and particularly trying to cache side column blocks (most recent photos, blogs, etc), and currently I have them expiring the cache every 15 minutes or so. Which works, but if I can do it more up-to-date like whenever new content is added, updated or whatnot, that would be better.
I was watching the episode of the Scaling Rails screencasts on Memcached http://content.newrelic.com/railslab/videos/08-ScalingRails-Memcached-fixed.mp4, and at 8:27 in the video, Gregg Pollack talks about intelligent caching in Memcached in a way where intelligent keys (in this example, the updated_at timestamp) are used to replace previously cached items without having to expire the cache. So whenever the timestamp is updated, the cache would refresh as it seeks a new timestamp, I would presume.
I am using my "Recent Photos" sideblock for this example, and this is how it's set up...
_side-column.html.erb:
<div id="photos"">
<p class="header">Photos</p>
<%= render :partial => 'shared/photos', :collection => #recent_photos %>
</div>
_photos.html.erb
<% cache(photos) do %>
<div class="row">
<%= image_tag photos.thumbnail.url(:thumb) %>
<h3><%= link_to photos.title, photos %></h3>
<p><%= photos.photos_count %> Photos</p>
</div>
</div>
<% end %>
On the first run, Memcached caches the block as views/photos/1-20110308040600 and will reload that cached fragment when the page is refreshed, so far so good. Then I add an additional photo to that particular row in the backend and reload, but the photo count is not updated. The log shows that it's still loading from views/photos/1-20110308040600 and not grabbing an updated timestamp. Everything I'm doing appears to be the same as what the video is doing, what am I doing wrong above?
In addition, there is a part two to this question. As you see in the partial above, #recent_photos query is called for the collection (out of a module in my lib folder). However, I noticed that even when the block is cached, this SELECT query is still being called. I attempted to wrap the entire partial in a block at first as <% cache(#recent_photos) do %>, but obviously this doesn't work - especially as there is no real timestamp on the whole collection, just it's individual items of course. How can I prevent this query from being made if the results are cached already?
UPDATE
In reference to the second question, I found that unless Rails.cache.exist? may just be my ticket, but what's tricky is the wildcard nature of using the timestamp...
UPDATE 2
Disregard my first question entirely, I figured out exactly why the cache wasn't refreshing. That's because the updated_at field wasn't being updated. Reason for that is that I was adding/deleting an item that is a nested resource in a parent, and I probably need to implement a "touch" on that in order to update the updated_at field in the parent.
But my second question still stands...the main #recent_photos query is still being called even if the fragment is cached...is there a way using cache.exists? to target a cache that is named something like /views/photos/1-2011random ?
One of the major flaws with Rails caching is that you cannot reliably separate the controller and the view for cached components. The only solution I've found is to embed the query in the cached block directly, but preferably through a helper method.
For instance, you probably have something like this:
class PhotosController < ApplicationController
def index
# ...
#recent_photos = Photos.where(...).all
# ...
end
end
The first instinct would be to only run that query if it will be required by the view, such as testing for the presence of the cached content. Unfortunately there is a small chance that the content will expire in the interval between you testing for it being cached and actually rendering the page, something that will lead to a template rendering error when the nil-value #recent_photos is used.
Here's a simpler approach:
<%= render :partial => 'shared/photos', :collection => recent_photos %>
Instead of using an instance variable, use a helper method. Define your helper method as you would've the load inside the controller:
module PhotosHelper
def recent_photos
#recent_photos ||= Photos.where(...).all
end
end
In this case the value is saved so that multiple calls to the same helper method only triggers the query once. This may not be necessary in your application and can be omitted. All the method is obligated to do is return a list of "recent photos", after all.
A lot of this mess could be eliminated if Rails supported sub-controllers with their own associated views, which is a variation on the pattern employed here.
As I've been working further with caching since asking this question, I think I'm starting to understand exactly the value of this kind of caching technique.
For example, I have an article and through a variety of things I need for the page which include querying other tables, maybe I need to do five-seven different queries per article. However, caching the article in this way reduces all those queries to one.
I am assuming that with this technique, there always needs to have at least "one" query, as there needs to be "some" way to tell whether the timestamp has been updated or not.

Avoiding nil in Rails views

I'm sure this has been asked already, but I can't find the answer.
I have a Project model, which has a belongs_to relationship with my Client model. A client has a name, but a project doesn't necessarily have a client.
In my view, I've got code like this:
<%=h project.client && project.client.name %>
because if the project doesn't have a client then trying to access project.client.name causes a NoMethodError (nil doesn't have a method called name).
The question is, is it acceptable to have this kind of nil checking in the view, or should I be looking for another way around it?
Just use
project.client.try(:name)
I think its perfectly acceptable - this is view logic, you are more or less deciding whether or not to show portions of your view, based on whether there is data.
I run into this all the time, and yes it's annoying. Even when there is supposed to never be a nil, dirty data that I inherited sometimes triggers it.
Your solution is one way of handling it. You could also add a method to Project called client_name that displays the client name if it exists, but then you are linking the models together more than some people recommend.
def client_name
client && client.name
end
You could also make a helper method to do it, but you can end up writing a lot of them. :)
As mentioned by Skilldrick below, this is also useful to add a default string:
def client_name
client ? client.name : "no client"
end
You can use delegate in your Project class, so this way you will respect the Law of demeter which says that you should "talk only to your immediate friends".
project.rb
class Project
delegate :name, to: :client, prefix: true, allow_nil: true
end
So this way the project object will know where to ask about the client's name:
#You can now call
project.client_name
See more about delegate in the Rails documentation.
my hacky solution is to yield a block and rescue the error. Many would say using rescue as logic is very bad form. Just don't use this where you would actually need to know when something is nil and shouldn't be.
In application_helper.rb:
def none_on_fail
begin
return yield
rescue
return "(none entered)"
end
end
Then in the view:
<%= none_on_fail { project.client.name } %>
Then methods can be chained as deep as needed and it can be used on any method BUT it will cover up other potential problems with models/relationships/methods if they exist. I would equate it to taking out a splinter with a flamethrower. Very effective with painful consequences if used improperly.
I think these checks can usually be eliminated with a bit of thought. This has the benefit of keeping your view code cleaner, and more importantly, keeping logic out of the view layer, which is a best practice. Some templating engines don't allow any logic in the view.
There are at least a couple of scenarios. Let's say you have a show action that depends on an instance variable. I'd say if the record is not found the controller should not render the html, by redirecting or something else. If you have a loop in the view for an array, use #array.each do |a| end so that it doesn't evaluate if the array is empty. If you truly want an application default in the view, try loading it from a config file, e.g. #page_title || #{#APP_CONFIG['page_title']} (see Railscasts #85). Remember you may want to change these strings later, for example translating the UI.
Those are a couple scenarios where presence checks and usage of try can be avoided. I'd try to avoid them if possible. If you can't avoid them, I'd put the conditional checks in a view helper and add a helper unit test for it to verify (and document) both code paths.

Resources