How to add property to ruby variable? - ruby-on-rails

I want to create variable item
And write something like this:
item.property1 = "whatever"
item.poperty2 = "whatever"
How do I do this?
Right now I am doing it like this:
item = {}
item [:property1] = "whatever"
Any other options?

Here are the simplest solutions I know of:
If you know the attributes in advance, then use a Struct:
Item = Struct.new(:property1, :property2)
item = Item.new('blue', 'medium') # or:
item = Item.new
item.property1 = 'blue'
item.property2 = 'medium'
puts item.property1
puts item.property2
Otherwise, you can use an OpenStruct:
require 'ostruct'
item = OpenStruct.new
item.property1 = 'blue'
item.property2 = 'medium'
puts item.property1
puts item.property2

You need to create an object to access variables like that.
class Item
attr_accessor :property1, :property2
end
Then you could do what you wrote:
item = Item.new
item.property1 = "whatever"
item.poperty2 = "whatever"
If you are talking about a Rails database object there is more to it though (like writing a database migration first).

Related

How do I test with Rspec an initialize method that invokes find_or_initialize_by?

How do I write a test to check for the find_or_initialize_by block in this initialize method?
def initialize(document, user)
#document = document
#api = #document.api_version
#template = #document.template
#user = user
#brand = #user.brand
#vars = master_var_hash.extend Hashie::Extensions::DeepFind
template_variables.each do |t|
#document.template_variables.find_or_initialize_by(name: t.name) do |d|
d.name = t.name
d.tag = t.tag
d.box_name = t.box_name
d.designator = t.designator
d.order_index = t.order_index
d.master_id = t.id
d.editable = t.editable
d.editable_title = t.editable_title
d.html_box = t.box.stack.html_box if #api == :v3
d.text = t.name == 'title' ? default_title : user_value(t)
end
end
end
I want to be able to test that the right values have been assigned to the #document's TemplateVariables from the class' TemplateVariables. In my coverage report I can't even hit inside the find_or_initialize_by block.
My test for size doesn't really check what I want to test here:
describe 'template_variables' do
it 'initializes all the new vars per document' do
expect(document.template_variables.size).to eq subject.master_var_hash.size
end
end
How can I write a test to check all those values and cover those lines?
You could use
expect(document).to receive_message_chain(:template_variables, :find_or_initialize_by).exactly(8).times
but it's a mess and you would have to also check if each call got proper parameters.
I would suggest extracting this to a method:
Document#initialize_variables(template_variables)
then you could test it as simply as
expect(document).to receive(:initialize_variables).with(expected_hash)
and then you can cover Document#initialize_variables with specs and test it's behavior in depth.

Array of hashes is overriding data directly to array

I want to make an array of hashes. But the problem is after first iteration when code goes to next line then it directly replaces the content of array.
#item_name =[]
item = {}
#invoiceinfo.each do |invoice|
item[:name] = Invoiceinfo.find(#invoiceinfo.id).item.name
item[:desc] = Invoiceinfo.find(#invoiceinfo.id).desc
item[:unit_price] = Invoiceinfo.find(#invoiceinfo.id).unit_price
byebug
#item_name.push (item)
end
This is what i am getting
after first iteration suppose i have this data
#item_name = [{:name=>"usman", :desc=>"sample ", :unit_price=>100}]
As soon as next line is executed it directly changes #item_name(name variable)
After executing item[:name] = Invoiceinfo.find(#invoiceinfo.id).item.name
the content of the #item_name is changed
#item_name = [{:name=>"next_name", :desc=>"sample ", :unit_price=>100}]
Any help would be appreciated.
Thannks
Try something like this
#item_name = []
#invoiceinfo.each do |invoice|
invoice_info = Invoiceinfo.find(#invoiceinfo.id)
item = {}
item[:name] = invoice_info.item.name
item[:desc] = invoice_info.desc
item[:unit_price] = invoice_info.unit_price
#item_name.push(item)
end
If you consider using ruby paradigms and best practices in ruby code, this mistake won’t happen in the future.
#item_name = #invoiceinfo.each_with_object([]) do |invoice, acc|
invoice_info = Invoiceinfo.find(#invoiceinfo.id)
acc.push(
name: invoice_info.item.name,
desc: invoice_info.desc
unit_price: invoice_info.unit_price
)
end

Display Json in view

Im having problems trying to display in my view information comming from a json file. I have already parse it.
Here is my error:
When assigning attributes, you must pass a hash as an argument.
Extracted source (around line #23):
21 # #new_member.constructors = [driver['Constructors'][0]['name']]
22 # #new_member.points = [driver['points']]
23 #new_member.from_json(json)
#members << #new_member
end
# #new_member.constructors = [driver['Constructors'][0]['name']]
# #new_member.points = [driver['points']]
#new_member.from_json(json)
#members << #new_member
end
This is my controller:
require 'open-uri'
require 'json'
url = "http://ergast.com/api/f1/2014/driverStandings.json"
data = JSON.parse(open(url).read)
standings = data['MRData']['StandingsTable']['StandingsLists'][0]['DriverStandings']
#members = Array.new
standings.each do |driver|
json = standings.to_json
#new_member = Member.new
# #new_member.position = [driver['position']]
# #new_member.givenName = [driver['Driver']['givenName']]
# #new_member.familyName = [driver['Driver']['familyName']]
# #new_member.constructors = [driver['Constructors'][0]['name']]
# #new_member.points = [driver['points']]
#new_member.from_json(json)
#members << #new_member
If I uncommented the lines in the controller and delete these lines
#new_member.from_json(json)
json = standings.to_json
I get the following in the view
Name: ["Lewis"]
Name: ["Nico"]
That view is really close but is not what i need, I need the data without [" "],
so the view that I need is something like:
1 Lewis Hamilton Mercedes 384
Thanks in advance.
Alternative 1
Change the line:
#new_member.givenName = [driver['Driver']['givenName']]
into:
#new_member.givenName = driver['Driver']['givenName']
removing the external [ and ], so that the field #new_member.givenName that you're populating becomes a string rather than an array containing one string.
Alternative 2
Is it possible that, when looping through the standings hash, the driver temp variable is a Hash?
In this case it may be sufficient to do:
#members = Array.new
standings.each do |driver|
#members << Member.new(driver)
end
Bonus: you probably don't need to assign an instance variable #new_member, that gets overwritten at each iteration of the loop

How to test a specific line in a rails model using rspec

I have a model with an initializer in it, which basically creates a user from a user hash.
After it gets the user information, it checks whether the "privileges" key in the hash is an array. If it's not, it turns it into an array.
Now the obvious way of doing this would be crafting an entire user_hash so that it would skip those "create user" lines and then check if it turns the input into an array if necessary. However, I was wondering if there is a more DRY way of doing this?
Here is the user model I'm talking about:
def initialize(opts={})
#first_name = opts[:user_hash][:first]
#last_name = opts[:user_hash][:last]
#user_name = opts[:user_hash][:user_name]
#email = opts[:user_hash][:email]
#user_id = opts[:user_hash][:id]
#privileges = {}
if opts[:privs].present?
if !opts[:privs].kind_of?(Array)
opts[:privs] = [opts[:privs]]
end
end
end
You can pass a double which returns the needed value when the proper key is requested, and itself (or something else) otherwise:
it 'turns privs into an array' do
opts = double(:opts)
allow(opts)to receive(:[]).and_return(opts)
allow(opts)to receive(:[]).with(:privs).and_return('not array')
expect(MyClass.new(opts).privileges).to eq(['not array'])
end
Btw, your code could be simplified using the splat operator:
privs = [*opts[:privs]]
sample behavior:
privs = nil
[*privs]
# => []
privs = ['my', 'array']
[*privs]
# => ["my", "array"]
privs = 'my array'
[*privs]
# => ["my array"]
You can even use the idempotent Kernel#Array
def initialize(opts = {})
#first_name = opts[:user_hash][:first]
#last_name = opts[:user_hash][:last]
#user_name = opts[:user_hash][:user_name]
#email = opts[:user_hash][:email]
#user_id = opts[:user_hash][:id]
#privileges = {}
Array(opts[:privs])
end
I hope that helps
Rather than testing the implementation (value is turned into an array), I would test the desired behavior (takes single privilege or multiple privileges):
describe User do
describe '#initialize' do
it "takes single privilege" do
user = User.new(user_hash: {}, privs: 'foo')
expect(user.privileges).to eq(['foo'])
end
it "takes multiple privileges" do
user = User.new(user_hash: {}, privs: ['foo', 'bar'])
expect(user.privileges).to eq(['foo', 'bar'])
end
end
end

Ruby loop problems

I am working on a script that is supposed to be writing a list of items to a hash, but for some reason its only placing the last item in the loop in the hash... I have been working on this script all day, so I am pretty sure its something I am just missing.
Here is the script
#mr = MediaRating.where("user_id = ?", session['user_credentials_id'])
#mr.each do |rating|
#m = Media.where("id = ?", rating.media_id)
#m.each do |m|
s = Profile.find_by_subscriber_id(m.subscriber_id)
#h_lang = Language.find_by_code(s.language)
#history = {m.title => #h_lang.english}
end
end
There are multiple records in the MediaRating table so I know it has to do something with how my loop is. Thanks in advance for the help!
Working code:
#mr = MediaRating.where("user_id = ?", session['user_credentials_id'])
#mr.each do |rating|
#m = Media.find(rating.media_id)
s = Profile.find_by_subscriber_id(#m.subscriber_id)
#h_lang = Language.find_by_code(s.language)
#history[#m.title] = #h_lang.english
end
In the last line, you are over-writing the entire #history hash instead of adding a new key/value pair to it. I'm guessing that isn't what you intended. Change this line:
#history = {m.title => #h_lang.english}
to this:
#history[m.title] = #h_lang.english

Resources