Accessing a column with spaces in the column name from a view - ruby-on-rails

Ruby 2.1.5
Rails 4.1
I've inherited some code with where some of the columns have spaces in them. How do I access these columns from a view. Example:
Table: expenses
Columns: Jan 2015
Feb 2015
In expenses_controller.rb, I have
#epxenses = Expense
In views/expenses/index.html.erb, the following would not work:
#expenses.each do |e|
e.jan 2010
Any ideas?

You can access it using [] or attributes[]:
expense = Expense.last
expense['Jan 2015']
expense.attributes['Jan 2015']

Rails/Active record should not generate migrations with spaces in the field names, or if you specifically tell it to I would be curious as to why because it will likely be inconvenient going forward. If you are hooking up to a database that was created outside of AR, maybe you would benefit from aliasing the field names in your data models.
alias_attribute :new_column_name, :column_name_in_db

Related

Rails - How to use custom attribute in where?

I have Order model in which I have datetime column start and int columns arriving_dur, drop_off_dur, etc.. which are durations in seconds from start
Then in my model I have
class Order < ApplicationRecord
def finish_time
self.start + self.arriving_duration + self.drop_off_duration
end
# other def something_time ... end
end
I want to be able to do this:
Order.where(finish_time: Time.now..(Time.now+2.hours) )
But of course I can't, because there's no such column finish_time. How can I achieve such result?
I've read 4 possible solutions on SA:
eager load all orders and select it with filter - that would not work well if there were more orders
have parametrized scope for each time I need but that means soo much code duplication
have sql function for each time and bind it to model with select() - it's just pain
somehow use http://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute ? But I have no idea how to use it for my case or whether it even solves the problem I have.
Do you have any idea or some 'best practice' how to solve this?
Thanks!
You have different options to implement this behaviour.
Add an additional finish_time column and update it whenever you update/create your time values. This could be done in rails (with either before_validation or after_save callbacks) or as psql triggers.
class Order < ApplicationRecord
before_validation :update_finish_time
private
def update_finish_time
self.finish_time = start_time + arriving_duration.seconds + drop_off_duration.seconds
end
end
This is especially useful when you need finish_time in many places throughout your app. It has the downside that you need to manage that column with extra code and it stores data you actually already have. The upside is that you can easily create an index on that column should you ever have many orders and need to search on it.
An option could be to implement the finish-time update as a postgresql trigger instead of in rails. This has the benefit of being independent from your rails application (e.g. when other sources/scripts access your db too) but has the downside of splitting your business logic into many places (ruby code, postgres code).
Your second option is adding a virtual column just for your query.
def orders_within_the_next_2_hours
finishing_orders = Order.select("*, (start_time + (arriving_duration + drop_off_duration) * interval '1 second') AS finish_time")
Order.from("(#{finishing_orders.to_sql}) AS orders").where(finish_time: Time.now..(Time.now+2.hours) )
end
The code above creates the SQL query for finishing_order which is the order table with the additional finish_time column. In the second line we use that finishing_orders SQL as the FROM clause ("cleverly" aliased to orders so rails is happy). This way we can query finish_time as if it was a normal column.
The SQL is written for relatively old postgresql versions (I guess it works for 9.3+). If you use make_interval instead of multiplying with interval '1 second' the SQL might be a little more readable (but needs newer postgresql version, 9.4+ I think).

Combining distinct with another condition

I'm migrating a Rails 3.2 app to Rails 5.1 (not before time) and I've hit a problem with a where query.
The code that works on Rails 3.2 looks like this,
sales = SalesActivity.select('DISTINCT batch_id').where('salesperson_id = ?', sales_id)
sales.find_each(batch_size: 2000) do |batchToProcess|
.....
When I run this code under Rails 5.1, it appears to cause the following error when it attempts the for_each,
ArgumentError (Primary key not included in the custom select clause):
I want to end up with an array(?) of unique batch_ids for the given salesperson_id that I can then traverse, as was working with Rails 3.2.
For reasons I don't understand, it looks like I might need to include the whole record to traverse through (my thinking being that I need to include the Primary key)?
I'm trying to rephrase the 'where', and have tried the following,
sales = SalesActivity.where(salesperson_id: sales_id).select(:batch_id).distinct
However, the combined ActiveRecordQuery applies the DISTINCT to both the salesperson_id AND the batch_id - that's #FAIL1
Also, because I'm still using a select (to let distinct know which column I want to be 'distinct') it also still only selects the batch_id column of course, which I am trying to avoid - that's #FAIL2
How can I efficiently pull all unique batch_id records for a given salesperson_id, so I can then for_each them?
Thanks!
How about:
SalesActivity.where(salesperson_id: sales_id).pluck('DISTINCT batch_id')
May need to change up the ordering of where and pluck, but pluck should return an array of the batch_ids

How to access ActiveRecord hash column

How can I access an ActiveRecord column that is a hash through the rails console? I am trying to get the properties column.
Example:
=> #<Ahoy::Event:0x007ffde80e3088
id: "abc123def465ghi798j9",
visit_id: "098dca809oiu567hjg678jh",
user_id: 26,
name: "click",
properties:
{"host"=>"localhost/sponsors",
"name"=>"click",
"type"=>"post-to-host",
"value"=>0,
"target"=>"whereintheworldiscarmonsandiego.com/rd/r.php",
"partner"=>"Uber",
"trigger"=>"click",
"utm_source"=>1234567890,
"utm_campaign"=>"organic"},
time: Fri, 06 May 2016 21:42:23 UTC +00:00,
user_type: "Applicant">
I am attempting:
semi/pseudo code
Ahoy::Event.where(properties["type"]: "post-to-host")
if this is a sequel style database.. you can't. the issue with serializing a column (using a hash column) is that youre bypassing the intrinsic sequel abstractions. which means you can't search by that column (effectively).
You can either break out properties to a relationship or do some really hacky stuff like
Ahoy::Event.where('properties LIKE ?', {type: 'post-to-host'})
essentially, whats happening is that the column is actually stored as a yaml formatted string, and when you access it in ruby, it just serializes the string from yaml, so in order to query on it, you'd have to query it like a string which will be pretty ineffective and generally error prone.
tl;dr dont use hash columns (serialize) for data you intend to query on
In Ahoy, we have built method to access json properties. You cn try following syntax:
1. Ahoy::Event.where_properties(type: "post-to-host")
2. Ahoy::Event.where_props(type: "post-to-host")

Dealing with column conversions in Ruby ActiveRecord

Dealing with a legacy database, I've come across a column in a SQL Server database where the date is stored as a decimal. E.g. 2011-04-23 is stored as 20110423.0.
Is there a general ActiveRecord mechanism for dealing with "weird" column storage conventions? Enum-like columns where they're actually stored as integers is another case that might also make use of the same mechanism, but I can't quite find what I'm looking for.
It seems like serialization gets me partly there:
class Thing < ActiveRecord::Base
class DecimalDate
def load(date)
if date.is_a? Numeric
y,m,d = /^(\d\d\d\d)(\d\d)(\d\d)/.match(date.to_s)[1..3].map(&:to_i)
Date.civil(y,m,d)
end
end
def dump(date)
date ? date.strftime('%Y%m%d').to_i : 0
end
end
serialize :weird_date, DecimalDate
end
rails c
> Thing.first.weird_date
=> Sun, 02 Jan 2011
But, the illusion is thin. The column doesn't "know" that it's a date stored as a decimal. E.g. comparisons fail:
rails c
> Thing.where('weird_date > ?', 1.week.ago)
...
ActiveRecord::StatementInvalid: ... Error converting data type varchar to numeric.:
If your are forced to deal with legacy data I see two possibilities to manage it.
1 From your database
You can "convert" your data by making a view of your table which convert your (date) fields on the fly. Then you make a trigger (before insert/update) on this view which convert your data back to your old format. Finally you tell ActiveRecord to use your view instead of your table.
2 From your application (Rails)
Find a way to tell ActiveRecord to do the same job. Have you already tried to manage it with AR callbacks with after_initialize and before_save? More informations here

Ruby On Rails Querying From a model but returning another tables fields

active_courses_past_week = CourseEnrollment.select("courses.*").
joins(:course).
where("date(course_enrollments.created_at) BETWEEN ? and ?", Date.parse(start_date.strftime('%Y-%m-%d')), Date.parse(end_date.strftime('%Y-%m-%d'))).
group("courses.id")
The above query seems odd because I am querying from course enrollments, but only care about the course data where they are enrolled between two dates. It just seems weird because I am not using any of the fields in the CourseErnollment model. Any suggestions?
This is how I would write it using a range instead of "raw" SQL.
active_courses_past_week = CourseEnrollment.where(:created_at => start_date..end_date))
.joins(:course)
.group(courses.id)

Resources