How to apply class method scope without a data table column? - ruby-on-rails

Disclaimer: first question, rails newbie, please be detailed in responses.
I’m trying to create a filter with a class method that is based off a simple equation performed on two columns in my data table. I can’t figure out how to get the query/filter going such that it filters results based on the results of my equation. Here is an abbreviated set up of my table:
t.integer “horizontal_length”
t.integer “thirty_day_flow_rate”
I want a filter based off this equation: ( thirty_day_flow_rate / horizontal_length ), so users could say, "show me all the wells with a 30 day flow rate greater than 'x' barrels per foot of length"
I have created a method in my model to hold the equation and it works fine when I call it on a Well object:
class Well < ActiveRecord::Base
def flow_rate_per_foot
thirty_day_flow_rate / horizontal_length
end
However, when I want to create a filter based on the results of the equation, I am not sure how to proceed. I tried something like this, with the mimimum_flow_rate param passed in from the controller:
class Well < ActiveRecord::Base
def self.flow_rate_filter(minimum_flow_rate)
if mimimum_flow_rate.present?
where(‘flow_rate_per_foot >= ?', minimum_flow_rate)
else
Well.all
end
end
Obviously that does not work, because flow_rate_per_foot is not a column in my data table to be called. How can i work around this? Is there another way to solve this problem? I want to do this type of filtering for a number of different equations and columns, so any solution needs to be flexible. Thanks!
For reference, my controller is shown below and other filters I have set up that run directly from my data table work properly:
def index
#wells = Well.flow_rate_filter(params[:mimimum_flow_rate])
end
Thanks!

you could try using scopes or just query it manually like
#wells = Well.where("(30_day_flow_rate / horizontal_length) >= ?", params[:mimimum_flow_rate])

What I wanted to do initially does not seem possible.
I solved the problem by creating a new field in my table to hold the "flow_rate_per_foot" calculation that I had originally placed in the flow_rate_per_foot method.
Once the result of the calculation had its own field, I could then filter, search and sort based on the results, which was my overall goal.

Add this line in your model
class Well < ActiveRecord::Base
attr_accessor :minimum_flow_rate
...

Related

How to create a class/scope method that orders objects based on values from an instance method?

I’ve been struggling for hours trying to develop a class or scope method, but have a beginner knowledge of SQLite and everything I’ve tried and read has thus far failed.
I’m trying to find a way to order lists by their average rating.
I have a has_many/belongs_to association between List and Rating. Lists have_many Ratings, each List can have_many ratings. I then have an instance method that calculates a list’s average rating:
def average_rating
self.ratings.average(:rating).to_i
end
I’m now trying to find a way to order lists by their average rating, but am not having any success. Following another post, I tried this method:
def self.highest_rating
List.all.sort_by(&:average_rating)
end
But it simply returns the all the lists in no particular order. With this query:
SELECT AVG("ratings"."rating") FROM "ratings" WHERE "ratings"."rated_id" = ?
I've thought of making average_rating an attribute on the List model, but have had difficulty even developing a scope method for that. If you could offer any advice or assistance I would greatly appreciate it!
First of all you can sort your query results by any column not the value. When you trying to sort by average rating as you do it now you like
List.all.sort_by(5) # you sort by result of the method which is simple number
It's not make big sense though.
I believe you need to add average_rating column to your List model. You will be able to calculate average rating for every list and store that value in the column and then sort lists by that column like this
def self.highest_rating
List.all.order(average_rating)
end
To calculate your average rating you may use callback every time you're creating new rating. It may look like:
class Rating < ActiveRecord::Base
after_save :calculate_average
private
def calculate_average
#your code
end
end

Validate uniquness in ActiveRecord on part of a string

Lets say I have registration_id attribute on Dummy model.
Its data type is string.
Its length can be anything from 10-14 characters but I want to put a uniqueness validation on last 10 characters only. (weird but true)
Now how can I achieve this?
What I have thought of:
Create another attribute last_ten_chars_registration_id in Dummy table to hold last 10 characters and put uniqueness on this attribute.
(As Computed attributes apparently don't work for uniqueness validations)
I can create a custom validator and write a query.
I am not sure (may be like query)
Can anyone suggest me any better way to achieve this?
You can use a custom validator like this.
class Dummy < ActiveRecord::Base
validates_with RegistrationValidator, :fields => [:registration_id]
# Whatever else...
end
class RegistrationValidator < ActiveModel::Validator
def validate(record)
reg_id = record.registration_id.last(10).join
if Dummy.where('registration_id LIKE ?',"%#{reg_id}")
record.errors[:registration_id] << "Registration ID taken!"
end
end
end
Same as GoGoCarl, I also think it largely depends on the performance you require. A custom query (not using LIKE but rather the RIGHT(registration_id, 10) function, at least in MySQL) will I think do fine unless the Dummy table is huge or you need the query to be super fast. In that case, I too would do the special column with the last 10 chars, and an accompanying db index.
A pragmatic solution might be to first go the custom query route as it seems simpler to implement to me, and later, if performance starts to suffer, switch to the special column.
You could also do your own benchmarks, e.g. populate a test Dummy table with the number of records you expect and see for yourself if the performance is OK with you or not.
See also this related SO question on string SQL functions performance where the solutions are similar.

Rails/Ruby: Performing calculate on ActiveRecord_AssociationRelation (including custom foreign_key)

I hope I am asking the proper question in the title, as my issue feels like it should be quite trivial yet I'm having terrible luck figuring it out.
I have two basic models with a standard has_many and belongs_to relationship:
class StandingEvent < Event
belongs_to :standing, foreign_key: 'actor_id'
end
class Standing < ActiveRecord::Base
has_many :standing_events
end
My goal is simple: To calculate the SUM of a field in a collection of StandingEvents records acquired through a Standing association. As expected, this collection is of type StandingEvent::ActiveRecord_AssociationRelation.
Ignoring everything else and cutting it down to the barest of bones, I get an error when running the following:
#standing = Standing.find(4)
#standing.standing_events.sum(:change)
The error produced is found below:
Mysql2::Error: Unknown column 'events.standing_id' in 'where clause':
SELECT SUM(`events`.`change`) AS sum_id FROM `events` WHERE `events`.`actor_type` IN ('StandingEvent') AND `events`.`standing_id` = 4
So, as seen from the above error, the exact problem is that the generated SQL query is trying to use standing_id as the column name (presumably because of the associated record) instead of the actual column name specified in the model itself (actor_id).
This issue only comes up when using a calculate method (such as sum), since I'm using both these models and their association very heavily throughout the application without any issue.
The only way "around" this issue that I've found so far seems very poor (and strikes me as unnecessary), which is essentially to chain my where clauses and sum through the base class, rather then using a previously gathered set of associated records:
#standing = Standing.find(4)
StandingEvent.where(standing: #standing).sum(:change)
The above code performs the calculation without issue, but since I'd like to perform multiple calculations upon the same collection within a single request, it seems like a very poor solution to re-query the entire set every time as above (though perhaps I don't understand Rails enough, to be fair).
As mentioned in my question title, I can't help but wonder if this is a bug (for lack of a better term) of some sort related to the use of the foreign_key field I specified for the child association (in this case, the foreign_key for StandingEvent's Standing association is renamed to actor_id).
Any and all insight would be most appreciated!
I think you'll need to specify the foreign_key on both sides of the association
has_many :standing_events, foreign_key: :actor_id

Rails: Track Points On A Weekly Basis

In my current application, I need the ability to track points on a weekly basis so that the point totals for the user reset back to zero each week. I was planning on using the gem merit: https://github.com/tute/merit to track points.
In my users profile I have a field that is storing the points. What I have been unable to locate is how I can have rails on an auto basis for all users clear this field.
I have come across some information Rails reset single column I think this may be the answer in terms of resetting it every Sunday at a set time -- but I am uncertain on this last part and in addition where the code would go (model or controller)
Also, would welcome any suggestions if their is a better method.
You'd be better making a Point model, which belongs_to :user
This will allow you to add any points you want, and can then query the table based on the created_at column to get a .count of the points for the timespan you want
I can give you more info if you think it appropriate
Models
One principle we live by is to extend our models as much as possible
You want each model to hold only its data, thus ensuring more efficient db calls. I'm not super experienced with databases, but it's my opinion that having a lot of smaller models is more efficient than one huge model
So in your question, you wanted to assign some points to a user. The "right" way to do this is to store all the points perpetually; which can only be done with its own model
Points
#app/models/point.rb
Class Point < ActiveRecord::Base
belongs_to :user
end
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :points
end
Points table could look like this:
points
id | user_id | value | created_at | updated_at
Saving
To save the points, you will literally just have to add extra records to the points table. The simplest way to achieve this will be to merge the params, like this:
#app/controllers/points_controller.rb
class PointsController < ApplicationController
def new
#points = Point.new
end
def create
#points = Point.new(points_params)
#points.save
end
private
def points_params
params.require(:points).permit(:value).merge(:user_id => current_user.id)
end
end
You can define the "number" of points by setting in the value column (which I'd set as default 1). This will be how StackOverflow gives different numbers of points; by setting the value column differently ;)
Counting
To get weekly countings, you'll have to create some sort of function which will allow you to split the points by week. Like this:
#app/models/point.rb -> THIS NEEDS MORE WORK
def self.weekly
where(:created_at => Time.now.next_week..Time.now.next_week.end_of_week)
end
That function won't work as it is
I'll sort out the function properly for you if you let me know a little more about how you'd like to record / display the weekly stats. Is it going to be operated via a cron job or something?
Based on your description, you might want to simply track the users points and the time that they got them. Then you can query for any 1 week period (or different periods if you decide you want all-time, annual, etc) and you won't lose historical data.

Managing the default order of a table in Rails 3

I have a model which has over 40,000 entires in it. I want to be able to have this table permanently sorted by one of its attributes. The tricky part of this is that some of the elements have a nil value for the attribute I want to sort by.
Some poking around has led me to default_scope, but it appears this is being deprecated and everyone warns against it. It seems like putting default_scope order('director_id DESC') or something like this would fix things, but this doesn't take into account nil values. What is the better alternative?
Thanks!
EDIT
I'm also using Tire with ElasticSearch for managing searches.
Yes, it's best to be explicit with model scopes. You can just do:
class MyModel < ActiveRecord::Base
def self.default_order
order('director_id DESC NULLS LAST')
end
end
Your database will have a syntax as part of ORDER BY for the placement of NULL values. If you don't want NULL values in the output at all then you can add a where call and the method should be renamed.

Resources