How can you set the order of the items in a nested array for an Active Record object? - ruby-on-rails

Here's the code I have:
def show
#tournament = Tournament.find(params[:id])
#tournament.games = #tournament.games.order(sort_column + ' ' + sort_direction)
puts #tournament.games.inspect
end
I'm trying to sort the games in the tournament. If I do puts #tournament.games.order(sort_column + ' ' + sort_direction) it returns the games in the correct order. But I can't seem to actually change #tournament.games. I've tried the assignment, no assignment, order!, order without the !, and nothing seems to be working.

Related

Quickly create 3 ruby objects

I am trying to create 3 objects in a loop like this...
3.times do |i|
#part + i.to_s = Part.create(part_number: "000#{i + 1}")
end
I get the error
NoMethodError: undefined method to_s=' for 0:Fixnum`
I think its obvious what I am trying to do? I'd like three parts (#part1/#part2/#part3) with part numbers 0001/2/3 after the loop runs.
As Roman already suggested, you should use an array.
However, to answer you question:
3.times do |i|
instance_variable_set("#part#{i + 1}", Part.create(part_number: "000#{i + 1}"))
end
Or even:
(1..3).each do |i|
instance_variable_set("#part#{i}", Part.create(part_number: "000#{i}"))
end

Rails loses change tracking when calling instance method

When changing a model's attributes in the controller's update, I want to generate a history of changes that are about to be made. For that, I've created a method generate_history to access from the instance.
The class:
class Assist < ActiveRecord::Base
belongs_to: status
def generate_history
#Also tried without self
p 'Testing round two'
p 'Status' + self.status.id.to_s
p 'Modified: ' + self.status.id_changed?.to_s
p 'Old value ' + self.status.id_was.to_s
#do something something dark side
end
end
The issue is within that method, the ActiveModel::Dirty isn't aware of the changes that have been made, although the self.status_id's value is the new one.
Meanwhile in the controller:
def update
...
#assist.assign_attributes(assist_params)
p 'Testing round one'
p 'Status' + #assist.status_id.to_s
p 'Modified: ' + #assist.status_id_changed?.to_s
p 'Old value ' + #assist.status_id_was.to_s
p 'Generating history'
#assist.generate_history
p 'Testing round three'
p 'Status' + #assist.status_id.to_s
p 'Modified: ' + #assist.status_id_changed?.to_s
p 'Old value ' + #assist.status_id_was.to_s
end
At first I was suspicious of assign_attributes that somehow interfered with ActiveModel::Dirty but I've realized that ActiveModel::Dirty works in the controller, where the values are being modified, but not when I'm calling generate_history.
Am I doing something wrong from within the instance method or its the way ActiveModel::Dirty works?
Example of output:
Testing round one
Status 1
Modified: true
Old value 2
Generating history
Testing round two
Status 1
Modified: false
Old value 1
Testing round three
Status 1
Modified: true
Old value 2
My guess is that the status_id is not an attribute that is fully tracked by ActiveRecord::Dirty. Instead of looking at status_id, i think you should look at status. Something like:
class Assist < ActiveRecord::Base
belongs_to: status
def generate_history
p 'Testing round two'
p 'Status' + status_id.to_s
p 'Modified: ' + status_id_changed?.to_s
p 'Old value ' + status_id_was.to_s
#do something something dark side
end
end
Then your controller code is right.
And because you're not assigning status but only asking about it, you probably don't need the self.

Search every field or attribute in model Rails

I have a simple model called Company with atributes such as Name, contact email, address, etc. I've created a search form where a user can find the company by searching for any attribute i.e., city, name of business, etc.
I'm new to Rails but I've figured out how to get it working like so in my controller.
if(params[:searchstring].present?)
logger.debug "*** Running search --> "
term = params[:searchstring]
#companies = Company.where('name LIKE ? OR name LIKE ? OR contactemail LIKE ? OR city LIKE ?' , "%#{term}%", "%#{term}%", "%#{term}%","%#{term}%" ).paginate(page: params[:page]).order('id DESC')
else
logger.debug "*** Running search --> get em all "
#companies = Company.all.paginate(page: params[:page], per_page: 5).order('id DESC')
end
end
This works fine but I'm guessing there is a much simplier way. I'd prefer not to modify the query every time I add an attribute. Any ideas on how to improve this?
You could go with something like this (It is just an example, might be a better way, but it is fonctionnal)
# company.rb
def self.search_all_properties term
query = ''
properties = Company.column_names
properties.each_with_index do |prop, index|
if index < properties.count - 1
query = query + prop + ' LIKE :term OR '
else
query = query + prop + ' LIKE :term'
end
end
term = "%" + term + "%"
Company.where(query, :term => term)
end
and use it like this
Company.search_all_properties term

Optional Rails Params in Controller Query

I have a question in regards to Rails params. I currently have only one filter on one of my views that allows users to filter data by a date range. I am adding two more filters to that view so that users can filter by code and country. However, I want those filters to be optional.
My current query looks something like this:
#data = Games
.where("date BETWEEN ? AND ?", *date_range_array)
.includes(:synced_country)
.order(sort_column + " " + sort_direction)
.page(params[:page])
The params for code and country will be something like, params[:code] and params[:country]. I would like to put them in the query like:
#data = Games
.where("date BETWEEN ? AND ?", *date_range_array)
.where("unique_code in ?" params[:code])
.where("country in ?" params[:country])
.includes(:synced_country)
.order(sort_column + " " + sort_direction)
.page(params[:page])
The issue I am having is that if the user does not input anything for params[:code] and params[:country] I get an error because they are nil. Any idea on how to handle this kind of situation?
I would build it incrementally:
#data = Games.where("date BETWEEN ? AND ?", *date_range_array)
#data = #data.where("unique_code in ?", params[:code]) if params[:code]
#data = #data.where("country in ?", params[:country]) if params[:country]
#data = #data.includes(:synced_country)
.order(sort_column + " " + sort_direction)
.page(params[:page])
Note that you are missing two commas right before params[:code] and params[:country] which I've fixed in the above code.
#data = Games.where("date BETWEEN ? AND ?", *date_range_array).scoped
#data = #data.where("unique_code in ?" params[:code]).scoped if params[:code].present?
#data = #data.where("country in ?" params[:country]).scoped if params[:coutry].present?
#data = #data.includes(:synced_country)
.order(sort_column + " " + sort_direction)
.page(params[:page])
Anyway, I would move that complex query to a named scope or something inside the Game Model.
EDIT: added the "scoped" method, i'm not sure if needed, try it without .scoped if you want
I usually use the same method with Agis.
But sometimes I trend to use scope(as arieljuod said):
class Games
scope :in_contry, lambda{|contry| where("contry in ?", contry) if contry.present?}
scope :in_code, lambda{|code| where("unique_code in ?", code) if code.present?}
//other codes
end
//call lambdas as follows:
#data = Games.where("date BETWEEN ? AND ?", *date_range_array)
.in_code(params[:code])
.in_contry(params[:country])
.includes(:synced_country)
.order(sort_column + " " + sort_direction)
.page(params[:page])

I need a search form with a large number of fields. How do I do this cleanly in Ruby on Rails?

I'm building a database application that tracks a lot of data for a Person, such as first_name, last_name, DOB, and 20+ more fields.
Users will need to be able to search for all of these fields. I'm having trouble writing clean code for this. The code below is what I have so far in my people_controller:
The data is submitted from a form_tag
def search
#people = Person.all
general_info_string = String.new
if(params[:first_name] != "") then general_info_string << 'people.first_name = "' + params[:first_name] + '" AND ' end
if(params[:last_name] != "") then general_info_string << "people.last_name = '" + params[:last_name] + "' AND " end
... Lots more of similar clauses
general_info_string = general_info_string[0, general_info_string.length - 5]
# ^This line removes the trailing " AND " from the string
#people = #people.where(general_info_string)
end
general_info_string is so called because there are more "where" clauses(not shown) and separate strings that I build to search for them.
The problem here is that the code looks like a mess and seems like a "hacky" way to do something that should be well supported by Rails. How could I perform this operation in a cleaner way?
That's not just hacky - it leaves you open to a string injection attack.
You need a :conditions using the ? template, like this example:
:conditions => ["key1 = ?", var]
but you need to make the template part into a string that grows once per parameter, and you need to make the var into an array that grows with each parameter's value. That gives you something like this:
template = []
values = []
if params[:first_name].present?
template.push 'people.first_name = ?'
values.push params[:first_name]
end
if params[:last_name].present?
template.push 'people.last_name = ?'
values.push params[:last_name]
end
template = template.join(' AND ')
:conditions => [template, *values]
From there, you should DRY up all those ifs, such as with a table of value keys. Then you'd loop through the table, check the key, and push its results into the arrays:
fields = [:first_name, :last_name, :shoe_size, ...]
fields.each do |field|
if params[field].present?
template.push "people.#{field} = ?"
values.push params[field]
end
end

Resources