I have two arrays which contain objects of assets, now I want to subtract to get only objects from the first array which the second array doesn't have. I should use "-" right?
Here is my object
class Asset
attr_accessor :id, :url, :text, :img_url, :type, :created_at
def initialize(url, text, type, img_url, created_at)
#id, #url, #text, #img_url, #type, #created_at = "", url, text, img_url, type, created_at
end
def eql?(another_asset)
self_domain = UrlUtils::get_domain(self.url)
another_asset_domain = UrlUtils::get_domain(another_asset.url)
if self_domain == 'youtube' && another_asset_domain == 'youtube'
self_youtube_id = UrlUtils::get_parameter(self.url, "v")
another_asset_youtube_id = UrlUtils::get_parameter(another_asset.url, "v")
return self_youtube_id.eql?(another_asset_youtube_id)
end
return self.url.eql?(another_asset.url)
end
def hash
#created_at.hash + 32 * #url.hash
end
end
The idea is one asset can contain url from youtube which every url might be different but it's the same video, so I have to compare each url with parameter "v" (youtube_id).
And this is my test which is wrong at the moment, because it doesn't do the subtraction correctly.
it "should substract duplicated youtube from mixed assets" do
mixed_assets = Array.new
all_assets = Array.new
google = Asset.new("http://www.google.com", "", "", "", Time.now)
youtube = Asset.new("http://www.youtube.com?v=1", "", "", "", Time.now)
mixed_assets.push(google)
mixed_assets.push(youtube)
another_youtube = Asset.new("http://www.youtube.com?v=1&a=1", "", "", "", Time.now)
all_assets.push(another_youtube)
mixed_assets = mixed_assets - all_assets
mixed_assets.length.should eql 1
mixed_assets[0].url.should eql "http://www.google.com"
end
I'm very new to ruby and I did some research that I should implement "hash" method as well, but I couldn't find any example how to do that.
Thanks.
Array subtraction works via hashes, so you're correct. I couldn't test since I don't know what UrlUtils is, but something similar to the following is likely what you need added to the Asset class:
def hash
domain = UrlUtils::get_domain(self.url)
v = domain == 'youtube' ? UrlUtils::get_parameter(self.url, "v") : ''
domain.hash ^ v.hash
end
You might also need an eql? method. There's a bunch of additional information in this post that you probably will want to look over; it covers this, as well as a bunch of related topics.
Related
I'm using rails 7 and ruby 3.1.2.
My cookie I've created returns these keys and values:
>> cookies[:utm_params]
=> "{:source=>\"facebook\", :medium=>\"facebook_feed\", :campaign=>\"123123123123\", :max_age=>2592000}"
And I've created these fields on my Subscription model:
# utm_source :string
# utm_medium :string
# utm_campaign :string
My Subscription.create code looks like this atm, but I can't figure out how to save them.
#subscription = Subscription.create!({
utm_source: cookies[:utm_params][:source],
# utm_medium: cookies[:utm_params],
# utm_campaign: cookies[:utm_params],
})
EDIT: My own solution and refactor
application_controller.rb
UTM_COOKIE_NAME = "_ta_utm"
before_action :capture_utm_params
private
def capture_utm_params
if(params.has_key?(:utm_medium))
cookies[UTM_COOKIE_NAME] = {
expires: 30.days.from_now,
value: {
source: params[:utm_source],
medium: params[:utm_medium],
campaign: params[:utm_campaign]
}.to_json
}
end
end
checkout_controller.rb
utm_tags = JSON.parse(cookies[UTM_COOKIE_NAME]) rescue nil
if utm_tags.present?
source = utm_tags["source"]
medium = utm_tags["medium"]
campaign = utm_tags["campaign"]
end
#subscription = Subscription.create!({
utm_source: source,
utm_medium: medium,
utm_campaign: campaign
})
You need to convert that string in to hash. I tried to use JSON.parse(string) on your string but it is not parsable as json. So i found this answer for you to convert that string into hash so that you can save the data you want. Hope it helps.
you can use the code in the answer i linked like this:
utm_hash = JSON.parse(cookies[:utm_params].gsub(/:([a-zA-z]+)/,'"\\1"').gsub('=>', ': ')).symbolize_keys
I have .csv file with rows of which every row represents one call with certain duration, number etc. I need to create array of Call objects - every Call.new expects Hash of parameters, so it's easy - it just takes rows from CSV. But for some reason it doesn't work - when I invoke Call.new(raw_call) it's nil.
It's also impossible for me to see any output - I placed puts in various places in code (inside blocks etc) and it simply doesn't show anything. I obviously have another class - Call, which holds initialize for Call etc.
require 'csv'
class CSVCallParser
attr_accessor :io
def initialize(io)
self.io = io
end
NAMES = {
a: :date,
b: :service,
c: :phone_number,
d: :duration,
e: :unit,
f: :cost
}
def run
parse do |raw_call|
parse_call(raw_call)
end
end
private
def parse_call(raw_call)
NAMES.each_with_object({}) do |name, title, memo|
memo[name] = raw_call[title.to_s]
end
end
def parse(&block)
CSV.parse(io, headers: true, header_converters: :symbol, &block)
end
end
CSVCallParser.new(ARGV[0]).run
Small sample of my .csv file: headers and one row:
"a","b","c","d","e","f"
"01.09.2016 08:49","International","48627843111","0:29","","0,00"
I noticed a few things that isn't going as expected. In the parse_call method,
def parse_call(raw_call)
NAMES.each_with_object({}) do |name, title, memo|
memo[name] = raw_call[title.to_s]
end
end
I tried to print name, title, and memo. I expected to get :a, :date, and {}, but what I actually got was [:a,:date],{}, and nil.
Also, raw_call headers are :a,:b,:c..., not :date, :service..., so you should be using raw_call[name], and converting that to string will not help, since the key is a symbol in the raw_call.
So I modified the function to
def parse_call(raw_call)
NAMES.each_with_object({}) do |name_title, memo|
memo[name_title[1]] = raw_call[name_title[0]]
end
end
name_title[1] returns the title (:date, :service, etc)
name_title[0] returns the name (:a, :b, etc)
Also, in this method
def run
parse do |raw_call|
parse_call(raw_call)
end
end
You are not returning any results you get, so you are getting nil,
So, I changed it to
def run
res = []
parse do |raw_call|
res << parse_call(raw_call)
end
res
end
Now, if I output the line
p CSVCallParser.new(File.read("file1.csv")).run
I get (I added two more lines to the csv sample)
[{:date=>"01.09.2016 08:49", :service=>"International", :phone_number=>"48627843111", :duration=>"0:29", :unit=>"", :cost=>"0,00"},
{:date=>"02.09.2016 08:49", :service=>"International", :phone_number=>"48622454111", :duration=>"1:29", :unit=>"", :cost=>"0,00"},
{:date=>"03.09.2016 08:49", :service=>"Domestic", :phone_number=>"48627843111", :duration=>"0:29", :unit=>"", :cost=>"0,00"}]
If you want to run this program from the terminal like so
ruby csv_call_parser.rb calls.csv
(In this case, calls.csv is passed in as an argument to ARGV)
You can do so by modifying the last line of the ruby file.
p CSVCallParser.new(File.read(ARGV[0])).run
This will also return the array with hashes like before.
csv = CSV.parse(csv_text, :headers => true)
puts csv.map(&:to_h)
outputs:
[{a:1, b:1}, {a:2, b:2}]
I am trying to do a post and run some if statement. What I want to do is:
check all fields are filled
if all fields are filled move on to next step, or else reload page
check if already in data base
add if not already in data base
post "/movies/new" do
title = params[:title]
year = params[:year]
gross = params[:gross]
poster = params[:poster]
trailer = params[:trailer]
if title && year && gross && poster && trailer
movie = Movie.find_by(title: title, year: year, gross: gross)
if movie
redirect "/movies/#{movie.id}"
else
movie = Movie.new(title: title, year: year, gross: gross, poster: poster, trailer: trailer)
if movie.save
redirect "/movies/#{movie.id}"
else
erb :'movies/new'
end
end
else
erb :'movies/new'
end
end
I don't think my if statement is correct. It works even if all my fields are not filled
Your code is doing a lot of work in one single method. I would suggest to restructure it into smaller chunks to make it easier to manage. I mostly code for Rails, so apologies if parts of these do not apply to your framework.
post "/movies/new" do
movie = find_movie || create_movie
if movie
redirect "/movies/#{movie.id}"
else
erb :'movies/new'
end
end
def find_movie
# guard condition to ensure that the required parameters are there
required_params = [:title, :year, :gross]
return nil unless params_present?(required_params)
Movie.find_by(params_from_keys(required_params))
end
def create_movie
required_params = [:title, :year, :gross, :poster, :trailer]
return nil unless params_present?(required_params)
movie = Movie.new(params_from_keys(required_params))
movie.save ? movie : nil # only return the movie if it is successfully saved
end
# utility method to check whether all provided params are present
def params_present?(keys)
keys.each {|key| return false if params[key].blank? }
true
end
# utility method to convert params into the hash format required to create / find a record
def params_from_keys(keys)
paras = {}
keys.each { |key| paras.merge!(key: params[key]) }
paras
end
Even if you type nothing in the HTML fields, they will still be submitted as empty strings.
You can avoid having empty parameters by, for example, filtering them:
post '/movies/new' do
params.reject! { |key, value| value.empty? }
# rest of your code
end
Also I would rather post to /movies rather than to /movies/new, that's more REST-wise.
Try if condition to check fields are blank like below -
unless [title, year, gross, poster, trailer].any?(&:blank?)
This will check any of the field should not be nil or blank("").
Sorry, but I didn't find the documentation enlightening at all. Basically, I am trying to iterate through a where some options are not valid. The ones I want have 'class="active"'. Can I do that with mechanize? Here's what I have so far:
class Scraper
def init
mech = Mechanize.new
page = mech.get('url')
#Now go through the <select> to get product numbers for the different flavors
form = page.form_with(:id => 'twister')
select = form.field_with(:name => 'dropdown_selected_flavor_name')
select.options.each do |o|
if (o.text != "")
value = o
end
productNumber = trim_pn(value.to_s[2..12])
puts productNumber
end
end
#Checks validity of product number and removes excess characters if necessary
def trim_pn(pn)
if (pn[0] == ",")
pn = pn[1..-1]
end
return pn
end
end
p = Scraper.new
p.init
All that does is grabs the product number and removes some extra info that I don't want. I thought replacing the .each do with this:
select.options_with(:class => 'active').each do |o|
if (o.text != "")
value = o
end
end
But that throws "undefined method 'dom_class' for Mechanize:Form:Option blah blah." Is there are different way I should be approaching this?
Here is the big hash that I start with (actually its been refined a step or two but this is what I'm starting with at this point.
angel_hash = {"follower_count"=>1369, "name"=>"AngelList", "markets"=>
[{"display_name"=>"Startups", "name"=>"startups", "id"=>448, "tag_type"=>"MarketTag",
"angellist_url"=>"http://angel.co/startups-1"}, {"display_name"=>"Venture Capital",
"name"=>"venture capital", "id"=>856, "tag_type"=>"MarketTag",
"angellist_url"=>"http://angel.co/venture-capital"}], "video_url"=>"",
"created_at"=>"2011-03-18T00:24:29Z", "updated_at"=>"2012-07-09T14:12:28Z",
"product_desc"=>"AngelList is a platform for startups to meet investors and talent. ",
"blog_url"=>"http://blog.angel.co",
"thumb_url"=>"https://s3.amazonaws.com/photos.angel.co/startups/i/6702-
766d1ce00c99ce9a5cbc19d0c87a436e-thumb_jpg.jpg", "id"=>6702,
"company_url"=>"http://angel.co", "locations"=>[{"display_name"=>"San Francisco",
"name"=>"san francisco", "id"=>1692, "tag_type"=>"LocationTag",
"angellist_url"=>"http://angel.co/san-francisco"}], "community_profile"=>false, "status"=>
{"message"=>"Done Deal: #volunteerspot raises $1.5M
http://techcrunch.com/2012/06/27/targeting-power-moms-volunteerspot-secures-1-5m-in-
series-a-from-ff-venture-capital-and-more/ \316\207 20 intros on AngelList \316\207 Funded
by #ff-venture-capital", "created_at"=>"2012-06-28T20:37:58Z", "id"=>63110},
"twitter_url"=>"http://twitter.com/angellist", "high_concept"=>"A platform for startups",
"logo_url"=>"https://s3.amazonaws.com/photos.angel.co/startups/i/6702
-766d1ce00c99ce9a5cbc19d0c87a436e-medium_jpg.jpg",
"angellist_url"=>"http://angel.co/angellist", "screenshots"=>
[{"thumb"=>"https://s3.amazonaws.com/screenshots.angel.co/98/6702/009cff275fb96709c915c4d4abc9
43d6-thumb_jpg.jpg",
"original"=>"https://s3.amazonaws.com/screenshots.angel.co/98/6702/009cff275fb96709c915c4d4abc
943d6-original.jpg"}], "hidden"=>false}
Out of this hash I parsed out some elements, and am doing just fine until I run into the embedded arrays
module SimpleAngel
class Company
attr_accessor :followers, :company_name, :markets_array, :date_joined, :locations_array
attr_accessor :high_concept, :high_concept_long, :thumbnail_logo, :full_size_logo
attr_accessor :angel_url, :twitter_url, :company_url, :blog_url
def initialize(angel_hash)
#followers = angel_hash['follower_count']
#company_name = angel_hash['name']
#markets_array = angel_hash['markets']
#markets_array.each_with_index do |market, i|
###This is where I'm stuck. I want to pull out individual elements
# from each array AND dynamically assign unique instance variable names for
# each separate market in the markets array. Something like #market1_name,
# #market1_id, etc.
end
#date_joined = angel_hash['created_at']
#locations_array = angel_hash['locations']
#high_concept = angel_hash['high_concept']
#high_concept_long = angel_hash['product_desc']
#thumbnail_logo = angel_hash['thumb_url']
#full_size_logo = angel_hash['logo_url']
#angel_url = angel_hash['angellist_url']
#twitter_url = angel_hash['twitter_url']
#company_url = angel_hash['company_url']
#blog_url = angel_hash['blog_url']
end
end
end
Here's a direct answer to your question: you can define arbitrary instance variable by calling instance_variable_set.
#markets_array.each_with_index do |market, i|
market.each do |k, v|
instance_variable_set "market#{i}_#{k}", v
# this will define #market0_id = 448
end
end