Ruby: "undefined method `[]' for nil:NilClass" in a for loop - ruby-on-rails

I am trying to populate my database using a for loop and JSON parsing. When I do not attempt a for loop and just do it for a single object, it works. Here is my code:
for i in 0..searchArray.size-1 #0..iTunesParsed['results'].size-1 // it is likely that searching for more than one item may not be benificial to us
# NOTE: Need to make sure to only search for artists, filter only songs
if !(ary.include? searchArray[i])
ary.push(searchArray[i])
# grabs artist name if not in database and adds it
# NOTE: this is occuring after the check for if elements are in the
# database, change it so this occurs first
if !(Artist.exists?(artist_name: ary[i]))
# replace all spaces with '+' so it may be passed to spotify
searchResult = searchArray[i]
if searchResult.match(/\s/)
searchResult.gsub!(/\s/,'+')
end
# grabbing the artist art from the Spotify api
# NOTE: have to fix 'special characters'
spotifyUrl = "https://api.spotify.com/v1/search?q=" + searchResult + "&type=artist"
spotifyUri = URI(spotifyUrl)
spotifyResponse = Net::HTTP.get(spotifyUri)
spotifyParsed = JSON.parse(spotifyResponse)
# When putting item into the database, replace '+' with a space
searchResult.gsub!('+',' ')
# create database entry
artist = Artist.create(:artist_name =>
searchResult, :artist_info=> nil,
:biography => nil, :recent_albums => nil, :rating => nil,
:related_artists => nil, :artist_art => spotifyParsed['artists']['items'][0]['images'][0]['url'])
end
end
The issue is mainly here:
artist_art => spotifyParsed['artists']['items'][0]['images'][0]['url'])
I have no idea what is happening. If anyone could guide me in the right direction, that would help a lot.

I believe there could be artists having no images. Use Rails’ try to handle these cases. Instead of:
:artist_art => spotifyParsed['artists']['items'][0]['images'][0]['url']
do:
:artist_art => spotifyParsed.try(:[],'artists')
.try(:[], 'items')
.try(:first)
.try(:[], 'images')
.try(:first)
.try(:[], 'url')
or use inline rescuer:
:artist_art => (spotifyParsed['artists']['items'][0]['images'][0]['url'] rescue nil)

Related

How to access to an object data in rails

I have an object and want to access to some data but I have an error.
This is the object:
list = [{"id"=>0,"title"=>"Purple Rain"}, {"id"=>1,"title"=>"Life is a flower"},]
With binding.pry, i tried to access to the title of the first object by:
list.first.title
Can you tell me why it doesn't work? If i do "list.first", it will show the first object without any problem but when i want to access to only one data, i got this error:
NoMethodError: undefined method `title' for #<Hash:0x...
Thanks for your help
Hash doesn't have dot syntax. OpenStructs do. If you want to use dot syntax, then you can convert to openstruct if you want. But using what Sebastian suggested is fine.
list
# => [{"id"=>0, "title"=>"Purple Rain"}, {"id"=>1, "title"=>"Life is a flower"}]
list.first["title"]
# => "Purple Rain"
require 'ostruct'
# => true
obj = OpenStruct.new(list.first)
# => #<OpenStruct id=0, title="Purple Rain">
obj.title
# => "Purple Rain"

How to remove/replace Smart Collection rules from Shopify with Ruby

I have created a script to add conditional rules to a Shopify Smart Collection but before it does that I need it to remove all the current conditions so that the new condition is the only one there. I am running into an issue that kicks back an undefined error when trying to remove them.
Script:
#update_collection = ShopifyAPI::SmartCollection.find(411011140)
#a = #update_collection.rules[0].attributes
#a.delete_all
#update_collection.rules << ShopifyAPI::Rule.new(:column => 'tag', :relation => 'equals', :condition => 'test12')
#update_collection.save
puts "#{update_collection.title} Updated"
Error Output:
NoMethodError: undefined method `delete_all' for #<ActiveSupport::HashWithIndifferentAccess:0x007fc2cbafd008>
I've tried to remove each attribute separately which is not the correct way I am sure and it removes the attribute but not the entire rule and results in an error upon saving.
Script:
#update_collection = ShopifyAPI::SmartCollection.find(411011140)
#a = #update_collection.rules[0].attributes
#a.delete("column")
#a.delete("relation")
#a.delete("condition")
#update_collection.rules << ShopifyAPI::Rule.new(:column => 'tag', :relation => 'equals', :condition => 'test12')
#update_collection.save
puts "#{update_collection.title} Updated"
Error Output:
irb(main):1806:0> #update_collection.save
=> false
Error lookup:
irb(main):1818:0> #update_collection.errors
"rules"=>[#, #"tag", "relation"=>"equals", "condition"=>"test12"}, #prefix_options={}, #persisted=false>]}, #prefix_options={}, #persisted=true, #remote_errors=#, #validation_context=nil, #errors=#>, #messages={:conditions=>["are not valid"]}, #details={:conditions=>[{:error=>"are not valid"}]}>
I tried .destroy and got the following error:
Script:
#update_collection = ShopifyAPI::SmartCollection.find(411011140)
#a = #update_collection.rules[0].attributes
#a.destroy
#update_collection.rules << ShopifyAPI::Rule.new(:column => 'tag', :relation => 'equals', :condition => 'test12')
#update_collection.save
puts "#{update_collection.title} Updated"
NameError: undefined local variable or method `params' for #<ActiveSupport::HashWithIndifferentAccess:0x007fc2cc168280>
I am not sure what I am missing or doing wrong. Any point in the right direction would be greatly appreciated!
Shopify API Documentation: https://help.shopify.com/api/reference/smartcollection
I just wanted to note that I've created a bandaid for our issue, although for multiple collections it may not be the correct way to write a script and loop through the problematic collections. For future individuals who are trying the same thing I've included the script that seems to work.
#a = ShopifyAPI::SmartCollection.find(COLLECTIONID)
# The line above finds the collection via ID
#a.rules = nil
# The line above changes all of the rules to a "null" value
#a.save
# After changing the condition rules to null we save
#b = ShopifyAPI::SmartCollection.find(COLLECTIONID)
# We go back into the collection and add a new rule (this will be the only existing rule we have now)
#b.rules << ShopifyAPI::Rule.new(:column => 'tag', :relation => 'equals', :condition => 'YOURTAG')
# The above line will add a new rule
#b.save
# We save it again to add the new rule
# If you'd like to test it you can add "#b.reload" to reload the collection information that currently exists and you should see your older rules removed and the new one in place

How to access first item in hash value array

I have a hash whose value is an array of song lyrics (line1, line2, etc..)
Code:
class Song
def initialize(lyrics)
#lyrics = lyrics
end
def get_song_name()
puts #lyrics.keys
end
def get_first_line()
puts #lyrics.values[0]
end
end
wasted = Song.new({"Wasted" => ["I like us better when we're wasted",
"It makes it easier to see"]})
real_world = Song.new("Real World" => ["Straight up what do you want to learn about here", "if i was someone else would this all fall apart"])
wasted.get_song_name()
wasted.get_first_line()
#=>I like us better when we're wasted
#=>It makes it easuer to see
So when I called wasted.get_first_line, I want it to get the first item in the array of the value. I tried doing #lyrics.values[0], but it returns both lines of the song instead of the first one.
How do I accomplish this?
You need to understand that in the above code #lyrics is a Hash. Here is what you are doing and what it translates to:
#lyrics
# => {"Wasted"=>["I like us better when we're wasted", "It makes it easier to see"]}
#lyrics.values
# => [["I like us better when we're wasted", "It makes it easier to see"]]
#lyrics.values[0]
# => ["I like us better when we're wasted", "It makes it easier to see"]
#lyrics.values[0][0]
# => "I like us better when we're wasted"
Therefore to access the first line, you need to get the first element of the values array. i.e.
#lyrics.values[0][0]
or
#lyrics.values.first.first
Lets use this hash for example:
x = {foo: [:bar, :baz]}
x.values # => [[:bar, :baz]]
x.values.first # => [:bar, :baz]
x.values.first.first # => :bar
In other words, #lyrics.values[0] will return the first value in the #lyrics hash, which is the array of two songs. You still have to get the first song out of that array.
This is not the answer to original question, but if I were you, I would modify the class like below. It will be more apt to store song name and lines of lyrics as individual attributes, instead of merging them as a hash - which kind of defies the whole purpose of having Song class.
class Song
attr_accessor :song_name, :lyrics
def initialize(song_name, lyrics)
#song_name = song_name
#lyrics = lyrics
end
end
Please note that you may not need get_first_line method. You could always use Array#first to have same effect:
real_world = Song.new("Real World", ["Line 1", "Line 2"])
puts real_world.lyrics.first # Prints "Line 1"
You can also access lyrics lines using array index
puts real_world.lyrics[1] # Prints "Line 2"

Checking for nil result set before viewing

I've been reading Checking for nil in view in Ruby on Rails but I'm struggling to implement the marked solution.
I want to only load a graph in my View if a result set is not nil.
Controller:
#statistics = # ...my ActiveRecord query...
Helper:
def show_stats(statistics)
if statistics.pluck(:count)
image_tag(Gchart.line :size => '640x260',
:stacked => false,
:title => '',
:data => [statistics.pluck(:count)],
:legend => ['Stats'],
:bar_colors => ['3300CC', '3399FF'],
:axis_with_labels => ['y'])
end
end
View (HAML):
= show_stats(#statistics)
Currently when there are no statistics, I get an error. I want the View to not render the graph if there are no statistics. What am I doing wrong in the helper?
Error:
undefined method `-' for nil:NilClass
on the line where I call the helper.
if i understand correctly statistics.pluck(:count) will always return an array consisting of values of count attribute for each record found.
in ruby empty array evaluates to true, you might try to rewrite that if line like this:
if statistics.pluck(:count).any?
in fact it's good idea to cache that value and not fetch it from db again few lines below:
if (counts = statistics.pluck(:count)).any?
...
:data => [counts]
...
end
also i assume :data option wants array of values and not array of array of values so the final version would be:
if (counts = statistics.pluck(:count)).any?
...
:data => counts
...
end
P.S. if you still have an error - please share a full backtrace with us, knowing only "undefined method" doesn't tell much
Why not check for #statistics in your view like follows:
= show_stats(#statistics) if #statistics
Did you try this?
= show_stats(#statistics) unless #statistics.nil?

Using mongomapper and rails my record refuses to update

My database will not update my active_quests. All I am trying to do is replace one array of hashes with another, updated array of hashes. I assumed would be the simplest way of handling this. Here's the code:
# construct the query
query = Player.where( :_id => player_id).fields( :xp, :lvl_prgrssns, :active_quests, :completed_quests )
# get the player
player = query.first
if !player.nil?
return_val = player.set( :active_quests => [{"quest_id" => "123"}, {"quest_id" => "456"}])
logger.debug "return_val = "+return_val.to_s # comes out as 180
end
My understanding is that, if the return from a set is positive, that means that the set was successful. It returns as 180 in this simplified case but the active_quests never get updated on the player. I can go into the mongo console and execute this:
db.players.update({_id:ObjectId("50756b1896f4f5121a00000a")}, {$set:{active_quests:[{"quest_id":"1"}, {"quest_id":"2"}] }});
and active_quests will update as expected but no matter what I try in rails the update appears to go through but nothing updates.
Here are some of the many alternatives I have tried (all have been tried with and without .to_mongo and with and without player.save after them):
Player.where( :_id => params[:player_id] ).update(:active_quests => active_quests_list.to_mongo)
player.update_attributes(:active_quests => active_quests_list.to_mongo)
player_update = player.as_json
player_update["active_quests"] = active_quests_list
player.update_attributes(player_update)
return_val = query.update( "$set" => {:active_quests => player.active_quests.to_mongo} )
return_val = query.update( {:_id => params[:player_id]}, {"$set" => {:active_quests => active_quests_list.to_mongo}})
I'm hoping someone here might know what I am doing wrong.
After further investigation, it turns out that this was a problem relating to how the player variable was being updated outside of the function.
The following lines will update the record in this case (both in-memory and in the database)
player[:active_quests] << #active_quests_list
player.push_all(:active_quests => player.active_quests)
However, the player variable was local to this function in this case, and was being updated again after the function returned.
This was only discovered after careful examination of the output of "mongod -vvvvv".

Resources