How to cycle through rails model parameters - ruby-on-rails

I'm trying to set up a method in rails so that if there is a parameter has a nil value, the name of the parameter is returned.
I have a model called Usages(gas,elec,biomass)
I know how to do this in ruby:
u = {elec: 100, gas: 200, biomass: nil}
t = %w(gas elec chp)
t.each do |t|
s=t.to_sym
if u[s] == nil
puts "#{t}"
end
end
, which returns "biomass"
Say I had this in Rails:
u = Usage.create(elec:100, gas:200)
How would I return "biomass"??
Cheers!

There is an attributes hash on ActiveRecord models that is based on the database table linked to the class. Empty fields are mapped with nil values, so you can look them up like this:
u.attributes.select {|_,v| v.nil? }.keys

I think you just want to loop through the active record attributes?
u = Usage.create(elec:100, gas:200)
u.attributes.each do |x|
if u.public_send(x).nil?
puts "#{x} is nil"
end
end

Related

Convert array of JSON to array of Activerecord models?

I have an array of JSON strings. How do I convert them to an array of Activerecord models?
My current code looks like this, and I'd to not iterate it one by one:
jsons = ['{"id": 1, "field1" : "value1"}'] #this is an array of jsons
models = [] #i want an array of models back
jsons.each do |json|
if(json == nil)
next
end
begin
hash = JSON.parse(json)
rescue
next
end
model = className.new
model.attributes = hash
model.id = hash["id"]
models << model
end
You should do:
models = jsons.compact.map { |json| Klass.new(JSON.parse(json)) }
where Klass is the ActiveRecord model class
EDIT
Based on the comments, you don't want to mass assign ID's, it could really get clumsy, it's better to leave rails to generate it for you, non mass assignment is:
models = jsons.compact.map { |json| Klass.new(JSON.parse(json).except(:id, "id")) }
:id, "id" is because I am not sure if the parsed JSON uses symbol or strings as keys

ActiveRecord: How to set the "changed" property of an model?

For every model in ActiveRecord, there seems to be a private property called "changed", which is an array listing all the fields that have changed since you retrieved the record from the database.
Example:
a = Article.find(1)
a.shares = 10
a.url = "TEST"
a.changed ["shares", "url"]
Is there anyway to set this "changed" property yourself? I know it sounds hacky/klunky, but I am doing something rather unordinary, which is using Redis to cache/retrieve objects.
ActiveModel::Dirty#changed returns keys of the #changed_attributes hash, which itself returns attribute names and their original values:
a = Article.find(1)
a.shares = 10
a.url = "TEST"
a.changed #=> ["shares", "url"]
a.changed_attributes #=> {"shares" => 9, "url" => "BEFORE_TEST"}
Since there is no setter method changed_attributes=, you can set the instance variable by force:
a.instance_variable_set(:#changed_attributes, {"foo" => "bar"})
a.changed #=> ["foo"]
See this example from: Dirty Attributes
class Person
include ActiveModel::Dirty
define_attribute_methods :name
def name
#name
end
def name=(val)
name_will_change! unless val == #name
#name = val
end
def save
#previously_changed = changes
#changed_attributes.clear
end
end
So, if you have a attribute foo and want to "change" that just call foo_will_change!.

Storing and retrieving session objects

I'm storing an object in hash which is in session object like this :
hash_key = ImportantData.new
hash_key.test_id = params[:test_id]
hash_key.user_id = params[:user_id]
session[:important_data] ||= {}
session[:important_data][hash_key] = nil
And then I print this map session[:important_data][hash_key] in my other controller and try to check whether the object is in there or not like this :
hash_key = ImportantData.new
hash_key.schedule_id = #test.id
hash_key.user_id = #user.id
in_hash = session[:important_data].has_key?(hash_key) unless session[:important_data].nil?
in_hash is always false to me, what am I doing wrong? and is there a better way to do this?
In Ruby, hash keys work by equality. You can store and retrieve a value in a hash as long as the key you're using is equal to the key that's stored.
For instance:
hsh = { 'hello' => 'world' }
p hsh['hello'] #=> "world"
'hello'.eql? 'hello' #=> true
You can retrieve the value because the same value string is always eql? in Ruby.
This is not the case for most objects:
class Example; end
object1 = Example.new
object2 = Example.new
object1.eql? object2 #=> false
Therefore, the key that you use to store in the hash must be eql? to the one that you're using to retrieve. eql? is equivalent to == and equal?.
You're creating two instances of ImportantData, which will not be equal to each other. It looks like you can accomplish what you want with a single hash key:
hash_value = ImportantData.new
hash_value.test_id = params[:test_id]
hash_value.user_id = params[:user_id]
session[:important_data] ||= hash_value
puts session[:important_data].class.name #=> ImportantData
session[:important_data].test_id #=> puts out value of test_id
I think you should assign it like this
session[:important_data][:hash_key] = hash_key
and access it
session[:important_data][:hash_key]

How to check if any of the Objects have the same data for the same field?

Is there a sweet irb line of code that could check is any of the Object's have the same data in their field?
For example, could I see if any of the Object's have the same email? Alternatively, check and see if any of the objects are not unique?
You could try it this way:
Create a hash with all instance variables as key-value pairs and then compare the hashes:
Assuming you have two objects a and b:
hash_a = a.instance_variables.inject({}){|res,v| res[v] = a.instance_variable_get(v); res }
hash_b = b.instance_variables.inject({}){|res,v| res[v] = b.instance_variable_get(v); res }
if hash_a == hash_b
puts "equal"
else
puts "not equal"
end
Edit:
If you are talking about Rails Models then you need this:
if a.attributes == b.attributes
puts "equal"
end

Using a method while looping through an array in ruby

I am using ruby-aaws to return Amazon Products and I want to enter them into my DB. I have created a model Amazonproduct and I have created a method get_amazon_data to return an array with all the product information. When i define the specific element in the array ( e.g. to_a[0] ) and then use ruby-aaws item_attributes method, it returns the name I am searching for and saves it to my DB. I am trying to iterate through the array and still have the item_attributes method work. When i don't define the element, i get this error: undefined method `item_attributes' for #Array:0x7f012cae2d68
Here is the code in my controller.
def create
#arr = Amazonproduct.get_amazon_data( :r ).to_a
#arr.each { |name|
#amazonproduct = Amazonproduct.new(params[:amazonproducts])
#amazonproduct.name = #arr.item_attributes.title.to_s
}
EDIT: Code in my model to see if that helps:
class Amazonproduct < ActiveRecord::Base
def self.get_amazon_data(r)
resp = Amazon::AWS.item_search('GourmetFood', { 'Keywords' => 'Coffee Maker' })
items = resp.item_search_response.items.item
end
end
Thanks for any help/advice.
I'm not familiar with the Amazon API, but I do observe that #arr is an array. Arrays do not usually have methods like item_attributes, so you probably lost track of which object was which somewhere in the coding process. It happens ;)
Try moving that .item_attributes call onto the object that supports that method. Maybe amazonproduct.get_amazon_data(:r), before its being turned into an array with to_a, has that method?
It's not quite clear to me what your classes are doing but to use #each, you can do something like
hash = {}
[['name', 'Macbook'], ['price', 1000]].each do |sub_array|
hash[sub_array[0]] = sub_array[1]
end
which gives you a hash like
{ 'name' => 'Macbook', 'price' => 1000 }
This hash may be easier to work with
#product = Product.new
#product.name = hash[:name]
....
EDIT
Try
def create
#arr = Amazonproduct.get_amazon_data( :r ).to_a
#arr.each do |aws_object|
#amazonproduct = Amazonproduct.new(params[:amazonproducts])
#amazonproduct.name = aws_object.item_attributes.title.to_s
end
end

Resources