How to use a method in a lib? - ruby-on-rails

I'm working to integrate UserVoice Single Single On with my rails app. They provide the following class for ruby:
require 'rubygems'
require 'ezcrypto'
require 'json'
require 'cgi'
require 'base64'
module Uservoice
class Token
attr_accessor :data
USERVOICE_SUBDOMAIN = "FILL IN"
USERVOICE_SSO_KEY = "FILL IN"
def initialize(options = {})
options.merge!({:expires => (Time.zone.now.utc + 5 * 60).to_s})
key = EzCrypto::Key.with_password USERVOICE_SUBDOMAIN, USERVOICE_SSO_KEY
encrypted = key.encrypt(options.to_json)
#data = Base64.encode64(encrypted).gsub(/\n/,'') # Remove line returns where are annoyingly placed every 60 characters
end
def to_s
#data
end
end
end
What I can't figure out is how to use this. I added this file to my lib directory and am using Rails Console to run. I tried:
1.9.3-p125 :013 > Uservoice::Token
=> Uservoice::Token
But can't get it to actually return for the options:
Uservoice::Token.new(:guid => 1, :display_name => "jeff goldmen", :email => "jeff#google.com")
Any ideas how to actually use this? Thanks

Looking at the code, it doesn't appear that the initializer (what gets run when you call new) will take just a hash. The method definition looks like this:
def initialize(key, api_key, data)
And it seems to treat the data variable as a hash. You might just need to add the key and api_key values when you instantiate a Token. So a call would look like this:
Uservoice::Token.new(KEY, API_KEY, {guid:1, display_name:'foo', email:'f#b.com'})

Related

Download a CSV file from FTP with Ruby on Rails and Update Existing Records

I'm trying to download a CSV file from an FTP server and if the record exists I want to update that record and not create a duplicate. To give a little more context - I'm trying to upload a group of orders from an FTP folder into my Rails app. There is a new file every hour - sometimes the orders in a certain drop contain duplicates from the previous drop to prevent one from slipping through the tracks or on occasion the order has been updated by the customer (change in qty, change in address, etc.) w/ the next drop. So my question is if the order is purely a duplicate with no changes how can I skip over those orders and if a record has been changed how can I update that record?
Ruby on Rails 5.1.4 - Ruby 2.4.1
Thank you!
The code below is from my model:
class Geek < ApplicationRecord
require 'csv'
def self.download_walmart_orders(out)
out ||= "#{Rails.root}/test_orders.csv"
CSV.foreach(out, :headers => true,
:converters => :all,
:header_converters => lambda { |h| h.downcase.gsub(' ', '_') }
) do |row|
geek = Geek.where(customer_order_id: row.to_h["customer_order_id"],
customer_name: row.to_h["customer_name"],
item_sku: row.to_h["item_sku"],
quantity_to_ship: row.to_h["quantity_to_ship"],
total_items_price: row.to_h["total_items_price"]).first_or_create
puts geek
end
end
end
I am assuming that customer_order_id is unique.
You could try something like this -
def self.update_or_create(attributes)
assign_or_new(attributes).save
end
Geek.where(customer_order_id: row.to_h["customer_order_id"]).update_or_create.(
customer_name: row.to_h["customer_name"],
item_sku: row.to_h["item_sku"],
quantity_to_ship: row.to_h["quantity_to_ship"],
total_items_price: row.to_h["total_items_price"])
^^^ Thank you, Michael, for the direction above. I ended up using this code and it worked perfectly. (For a slightly different project but exact same use case) my finalized model code is below:
class Wheel < ApplicationRecord
require 'csv'
def self.update_or_create(attributes)
obj = first || new
obj.assign_attributes(attributes)
obj.save!
end
def self.import(out)
out ||= "#{Rails.root}/public/300-RRW Daily Inv Report.csv"
CSV.foreach(out, :headers => true,
:converters => :all,
:header_converters => lambda { |h| h.downcase.gsub(' ', '_') }
) do |row|
Wheel.where(item: row.to_h["item"]).update_or_create(
item_desc: row.to_h["item_desc"],
total_quantity: row.to_h["total_quantity"])
end
end
end

Adding some Regex code to my Ruby webscraper

I'm building a webscraper and using Nokogiri. Here is the code that I currently have:
require 'nokogiri'
require 'open-uri'
require 'pry'
class Scraper
def get_page
doc = Nokogiri::HTML(open("http://www.theskimm.com/recent"))
h = {}
doc.xpath('//a[#href]').each do |link|
h[link.text.strip] = link['href']
end
puts h
end
binding.pry
end
Scraper.new.get_page
This returns me a hash of all URLs on the page (I only pasted the first few lines):
{"Back to Sign Up"=>"/", "SHARE THIS"=>"https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fwww.theskimm.com%2F2015%2F12%2F07%2Fskimm-for-december-8th-2&display=popup", "theSkimm\nSkimm for December 8th"=>"/", "Trump campaign press release"=>"http://skimmth.is/1SKR0bP", "assault weapons ban"=>"http://skimmth.is/1QbnCO8"}
However, I'd like to only grab the URLs that contain "http://skimmth.is/" as part of the value. What code/ Regular Expression would I need to add to my original Scraper class to ONLY selects URLs with that address?
You can use contains() function of xpath.
doc.xpath('//a[contains(#href, "http://skimmth.is/")]').map{|e| e.attr(:href)}
=> ["http://skimmth.is/1SKR0bP",
"http://skimmth.is/1QbnCO8",
"http://skimmth.is/1SHBSff",
"http://skimmth.is/1N8dORo",
"http://skimmth.is/1HRwGoO",
"http://skimmth.is/1HRmEUG",
"http://skimmth.is/1NePsmI",
"http://skimmth.is/1IQoJLn",
"http://skimmth.is/1ToQ6T1",
"http://skimmth.is/1IAZ6mW",
"http://skimmth.is/1N7Foy1",
"http://skimmth.is/1m7B6Op",
"http://skimmth.is/1SKBhJW",
"http://skimmth.is/1ToQ6T1",
"http://skimmth.is/1XfpwkX%20",
"http://skimmth.is/1P9rq20"]
You can use if as a statement modifier to check that the value is appropriate before adding it to the hash. For example, update this line:
h[link.text.strip] = link['href']
to
h[link.text.strip] = link['href'] if link['href'] =~ /http:\/\/skimmth.is\//
FWIW: =~ is the match method for the Regexp class.

Iterating in nokogiri with rails doesn't work

So I wrote some nokogiri code that works in a test .rb file but when I put it inside a rails app model it won't iterate and just returns the first value. Here is the code that iterates correctly:
require "rubygems"
require "open-uri"
require "nokogiri"
url = "http://www.ebay.com/sch/Cars-Trucks-/6001/i.html?_from=R40&_sac=1&_vxp=mtr&_nkw=car+projects&_ipg=200&rt=nc"
data = Nokogiri::HTML(open(url))
data.css(".li").each do |item|
item_link = item.at_css(".vip")[:href]
item_doc = Nokogiri::HTML(open(item_link))
puts item_doc.at_css("#itemTitle").text.sub! 'Details about', ''
end
Here is the same code in a rails app that only returns the first title it finds:
require "rubygems"
require "open-uri"
require "nokogiri"
class EbayScraper
attr_accessor :url, :data
def initialize(url)
#url = url
end
def data
#data ||= Nokogiri::HTML(open(#url))
end
def titles
data.css(".li").each do |item|
item_link = item.at_css(".vip")[:href]
item_data = Nokogiri::HTML(open(item_link))
return item_data.at_css("#itemTitle").text.sub! 'Details about', ''
end
end
ebay = EbayScraper.new("http://www.ebay.com/sch/Cars-Trucks-/6001/i.html?_from=R40&_sac=1&_vxp=mtr&_nkw=car+projects&_ipg=200&rt=nc")
titles = ebay.titles
puts titles
Why does the first code iterate through the whole thing and the second bunch of code just returns the first one?
Thanks for your time in advance!
Because you have a return statement in your loop that exits your titles function.

How to pass Arguments and use those in (resque-status) Resque::JobWithStatus?

my resque worker class is:
require 'resque'
require 'resque/job_with_status'
class PatstatResqueWorker < Resque::JobWithStatus
#queue = :my_worker_q
def self.perform(query, label)
puts "query:"
puts options['query']
puts "label:"
puts options['label']
end
end
and my controller part, where I call this resque is...
class MyController < ApplicationController
def resque
job_id = PatstatResqueWorker.create(:query => #query, :label => "yes")
status = Resque::Plugins::Status::Hash.get(job_id)
end
end
and its not working :(
if i remove the parameter from resque function it says Wrong number of arguments (2 for 0) and if i add the parameter section back it says options not defined :(
Could you help?
The reason you're getting the "options not defined" error is that you haven't defined options in the method that uses it. Your self.perform method expects to receive two distinct arguments, query and label, but the code inside the method expects to have an options hash. You've got to choose one or the other.
Either do this:
def self.perform(query, label)
# use the parameters we've already defined
puts "query:"
puts query
puts "label:"
puts label
end
# call it like this
PatstatResqueWorker.create(#query, "yes")
Or else do this:
# change the method signature to match what you're doing
def self.perform(options)
puts "query:"
puts options['query']
puts "label:"
puts options['label']
end
# call it like this, with string keys
PatstatResqueWorker.create('query' => #query, 'label' => "yes")
Notice that with the hash version, I changed the call to use strings for the hash keys instead of symbols. You can use symbols if you want, but you'd have to change it in the body of the method as well (i.e. options[:query] instead of options['query']). You've just got to be consistent.

SimpleDB to ActiveResource. Rails

Im looking for a way to map an ActiveResource to SimpleDB
I want to avoid plugins/gems as all I have used are outdated/buggy/not mantained
It doesnt seem hard, I wonder if any of you have succesfully implemented a rails app with simpleDB as an Active Resource. How did you do it? Thanks.
I haven't worked with SimpleDB, but I have mapped ActiveResource to Amazon's Flexible Payments Service REST api and just skimming the docs they seem similar so here's basically what I did, maybe you can use this as a starting point.
require 'base64'
require 'openssl'
class AmazonFlexiblePaymentResource < ActiveResource::Base
self.site = AMZ_CONFIG['flexible_api_url']
def self.rest_api(options = {})
params = common_request_params.update(options)
sig = compute_signature(AMZ_CONFIG['secret_access_key'], 'get', site, params)
rest_req = {'Signature' => sig}.update(params)
# make the http get call
connection.get("/#{query_string(rest_req)}", headers)
end
protected
# these are the params are common to all rest api calls
def self.common_request_params
{ 'AWSAccessKeyId' => AMZ_CONFIG['access_key_id'],
'SignatureVersion' => 2,
'SignatureMethod' => 'HmacSHA256',
'Timestamp' => Time.now.utc.iso8601,
'Version' => '2008-09-17'}
end
def self.compute_signature(key, method, end_point_url, params)
query_str = parameters.sort.collect {|k, v| v.to_query(k)}.join '&'
# cannot use plus for space, and tilde needs to be reversed
query_str.gsub!('+', '%20')
query_str.gsub!('%7E', '~')
to_sign = [method.upcase, end_point_uri.host.downcase,
end_point_uri.request_uri, query_str].join "\n"
digest = OpenSSL::Digest::Digest.new('sha256')
hmac = OpenSSL::HMAC.digest(digest, key, to_sign)
Base64.encode64(hmac).chomp
end
end
Then I just make calls like this
res = AmazonFlexiblePaymentResource.rest_api({ 'Action' => 'GetTransactionStatus', 'TransactionId' => '1234567890ABCDEFGHIJ' })
And the response is a hash of the parsed xml. Again this works for Amazon Flexible Payments Service, so you may need to make adjustments to match the SimpleDB REST API.
Does it need to be ActiveResource? If you want it like ActiveRecord, check out SimpleRecord.
http://github.com/appoxy/simple_record
It's very actively maintained.

Resources