I have a tweets_controller
#called when user submits twitter form
def message
unless current_user
session[:twitter_message] = params[:twitter_message] #sets the message from the form so it's available for send_tweet in tweet.rb after we pass through omniauth
redirect_to '/auth/twitter' #redirects to authorize via omniauth/twitter and create the user
else
#auth = Authorization.find_by_user_id(current_user)
Tweet.update_status(#auth, params[:twitter_message])
redirect_to edit_user_path(current_user), :notice => "Tweet sent."
end
end
I'm trying to rescue when the status update fails. I want to display a flash message to the user, but -- this is as far as I can seem to get:
def self.update_status(auth, msg)
#token = auth.token
#secret = auth.secret
#message = msg
#t = Twitter::Client.new
Twitter.configure do |config|
config.consumer_key = '[key]'
config.consumer_secret = '[secret]'
config.oauth_token = #token
config.oauth_token_secret = #secret
config.gateway = '[gateway_url]'
end
ret = #t.update(#message)
tweet ||= Tweet.create_from_response(ret, auth.id)
rescue Twitter::Error => e
logger.error "#{e.message}."
end
How do I get the error message so I can display it to my user through the controller?
You can create and throw a custom exception based on the application.
In app/lib/could_not_update_status_error.rb
class CouldNotUpdateStatusError < StandardError
end
Then in your model:
rescue Twitter::Error => e
logger.error "#{e.message}."
raise CouldNotUpdateStatusError.new("Could not update status")
And in your controller
else
begin
#auth = Authorization.find_by_user_id(current_user)
Tweet.update_status(#auth, params[:twitter_message])
redirect_to edit_user_path(current_user), notice: "Tweet sent."
rescue CoundNotUpdateStatusError => e
# Do error stuff
end
Another option would be to do rescue return false in your Twitter::Error clause and wrap the update_status call in an if statement, however Exceptions are a more robust solution.
Related
so im trying to do web scraping with rails and kimurai, the problem i ran in to was that for some reason i get a single big object instead of one for each of the products im scraping, here is my code:
class ProductsSpider < Kimurai::Base
#name = "products_spider"
#engine = :mechanize
def self.process(url)
#start_urls = [url]
self.crawl!
end
def parse(response, url:, data: {})
response.xpath("//div[#class='andes-card andes-card--flat andes-card--default ui-search-result ui-search-result--core andes-card--padding-default andes-card--animated']").each do |product|
item = {}
item[:product_name] = product.xpath("//h2[#class='ui-search-item__title ui-search-item__group__element']")&.text&.squish
item[:price] = product.xpath("//span[#class='price-tag-fraction']")&.text&.squish&.delete('^0-9')to_i
item[:shipping] = product.xpath("//p[#class='ui-search-item__shipping ui-search-item__shipping--free']")&.text&.squish
Product.where(item).first_or_create
end
end
end
and here is the function on the controller:
def scrape
url = "https://computacion.mercadolibre.com.ar/componentes-pc-placas-video/msi/cordoba/placa-de-video_NoIndex_True#applied_filter_id%3Dstate%26applied_filter_name%3DUbicaci%C3%B3n%26applied_filter_order%3D13%26applied_value_id%3DTUxBUENPUmFkZGIw%26applied_value_name%3DC%C3%B3rdoba%26applied_value_order%3D11%26applied_value_results%3D120%26is_custom%3Dfalse%26view_more_flag%3Dtrue"
response = ProductsSpider.process(url)
if response[:status] == :completed && response[:error].nil?
flash.now[:notice] = "Successfully scraped url"
else
flash.now[:alert] = response[:error]
end
rescue StandardError => e
flash.now[:alert] = "Error: #{e}"
end
going through this course to build ROR app and sending a twilio message to verify persons mobile number. I get the error" The requested resource /2010-04-01/Accounts//Messages.json was not found" found a previous question request however they did not have much code to review and he mentioned that he finally has it working mentioned he had a null variable of $sid and token; not sure what to verify. I tried both test and production sid and token #s from my acct. Thanks so much for your attention.
initailizer/twilio.rb
Twilio.configure do |config|
config.account_sid = ENV['TWILIO_ACCOUNT_SID']
config.auth_token = ENV['TWILIO_AUTH_TOKEN']
end
models/user.rb
def generate_pin
self.pin = SecureRandom.hex(2)
self.phone_verified = false
save
end
def send_pin
#client = Twilio::REST::Client.new
#client.messages.create(
from: '+15812345678',
to: self.phone_number,
body: "Your pin is #{self.pin}"
)
end
def verify_pin(entered_pin)
update(phone_verified: true) if self.pin == entered_pin
end
Application.yml
TWILIO_ACCOUNT_SID: '12345678901234567'
TWILIO_AUTH_TOKEN: '123434567890123456'
TWILIO_NUMBER: '+15878551234'
users controller
def update_phone_number
current_user.update_attributes(user_params)
current_user.generate_pin
current_user.send_pin
redirect_to edit_user_registration_path, notice: "Saved!"
rescue Exception => e
redirect_to edit_user_registration_path, alert: "#{e.message}"
end
def verify_phone_number
current_user.verify_pin(params[:user][:pin])
if current_user.phone_verified
flash[:notice] = "FYi Your phone number is verified"
else
flash[:alert] = "Cannot verify your phone number"
end
redirect_to edit_user_registration_path
rescue Exception => e
redirect_to edit_user_registration_path, alert: "#{e.message}"
end
private
def user_params
params.require(:user).permit(:phone_number, :pin)
end
I'm not sure but could you try this
def send_pin
account_sid = 'your-account-sid'
auth_token = 'your-auth-token'
#client = Twilio::REST::Client.new account_sid, auth_token
#client.messages.create({
from: '+15812345678',
to: self.phone_number,
body: "Your pin is #{self.pin}"
})
end
In my application, I have a twitter and facebook login, however I need to prompt a password and email after they first register with twitter or facebook. I am using omniauth gems and my controller/user model looks like this:
//socials_controller.rb
def create
#render text: request.env['omniauth.auth'].to_yaml and return
#user = User.from_omniauth(request.env['omniauth.auth'])
if(#user.email == nil)
redirect_to patient_login_entry_url(#user)
elsif #user.confirmed
log_in #user
redirect_to #user
else
flash[:danger] = "Bir hata olustu."
redirect_to root_url
end
end
def login_entry
#patient = Patient.find(params[:id])
end
def update_social
#patient = Patient.find(params[:id])
if #patient.update_attributes(user_params)
SendVerificationEmailJob.perform_later #patient
flash[:success] = "Aktivasyon için #{#patient.email} adresinizi kontrol ediniz."
redirect_to root_url
else
flash[:danger] = "Bilgilerinizi kontrol edip tekrar deneyiniz."
redirect_to patient_login_entry_url(#patient)
end
end
and my from_omniauth method is:
//user.rb
has_secure_password
class << self
def from_omniauth(auth_hash)
if exists?(uid: auth_hash['uid'])
user = find_by(uid: auth_hash['uid'])
else
user = find_or_create_by(uid: auth_hash['uid'], provider: auth_hash['provider'], type: 'Patient')
user.location = get_social_location_for user.provider, auth_hash['info']['location']
if auth_hash.provider == 'facebook'
user.avatar = User.process_uri(auth_hash['info']['image'])
user.name = auth_hash['extra']['raw_info']['first_name']
user.surname = auth_hash['extra']['raw_info']['last_name']
user.email = auth_hash['extra']['raw_info']['email']
user.gender = auth_hash['extra']['raw_info']['gender']
elsif auth_hash.provider == 'twitter'
user.avatar = auth_hash['info']['image']
user.name = auth_hash['info']['name']
end
user.url = get_social_url_for user.provider, auth_hash['info']['urls']
user.save!
end
user
end
At the login_entry page, I simply prompt the email and password, and POSTing them to the update_social method.
However, as expected, my app throws the error "Password can't be blank", because has_secure_password validates its presence by default. So, I need to persist it between the requests since I can not save it without a password. How can I achieve this?
I tried to store the created object in session by using to_json method, and turning it into a hash between requests, however this time the profile picture I got from twitter/facebook did not persist (I'm using AWS S3 + Paperclip, the URL persists but there is no such image when I check it from the S3 console) so I think that solution was not good.
Sometime one in a long series of events within a controller action fails. For example, a credit card is processed but then an ActiveRecord query times out. Is there any way to make those calls reversible?
E.g. with this controller action:
def process_order
cart = Cart.new(params[:cart])
load_order
response = credit_card.charge
if response
submit_order
order.receipt = Pdf.new(render_to_string(:partial => 'receipt')
order.receipt.pdf.generate
order.receipt.save
render :action => 'finished'
else
order.transaction = response
#message = order.transaction.message
order.transaction.save
render :action => 'charge_failed'
end
end
I would like to be able to put a block around it like so:
def process_order
transaction
cart = Cart.new(params[:cart])
load_order
response = credit_card.charge
if response
submit_order
order.receipt = Pdf.new(render_to_string(:partial => 'receipt')
order.receipt.pdf.generate
order.receipt.save
render :action => 'finished'
else
order.transaction = response
#message = order.transaction.message
order.transaction.save
render :action => 'charge_failed'
end
rollback
credit_card.cancel_charge
...
end
end
This is just a contrived example and I'm not really sure how it would work in practice. What typically happens is we get an exception like ActiveRecord::StatementInvalid: : execution expired for the line with submit_order and then we have to go and manually run the rest of the lines that should have run.
Here's a generic solution.
class Transactable
def initialize(&block)
raise LocalJumpError unless block_given?
#block = block
end
def on_rollback(&block)
raise LocalJumpError unless block_given?
#rollback = block
self
end
def call
#block.call
end
def rollback
#rollback.call if #rollback
end
end
class Transaction
def initialize(tasks)
tasks = Array(tasks)
tasks.each do |t|
Transactable === t or raise TypeError
end
#tasks = tasks
end
def run
finished_tasks = []
begin
#tasks.each do |t|
t.call
finished_tasks << t
end
rescue => err
finished_tasks.each do |t|
t.rollback
end
raise err
end
end
end
if __FILE__ == $0
Transaction.new([
Transactable.new { puts "1: call" }.on_rollback { puts "1: rollback" },
Transactable.new { puts "2: call" }.on_rollback { puts "2: rollback" },
Transactable.new { puts "3: call"; raise "fail!" }.on_rollback { puts "3: rollback" },
]).run
end
Note that it doesn't:
handle errors in the rollback block
call the rollback for the failed task, but that's easy to adjust
Just wrap it in
cart.transaction do
# ...
end
to use transactions. For details see http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
I'm a bit late but I think you should use save! instead of save. save just returns false if something fails within your model but save! raises an exception and your ActiveRecord::Base.transaction do block rolls back your changes correctly...
For example:
def process_order
ActiveRecord::Base.transaction do
begin
cart = Cart.new(params[:cart])
load_order
response = credit_card.charge
if response
submit_order
order.receipt = Pdf.new(render_to_string(:partial => 'receipt')
order.receipt.pdf.generate
order.receipt.save!
render :action => 'finished'
else
order.transaction = response
#message = order.transaction.message
order.transaction.save!
render :action => 'charge_failed'
end
rescue
# Exception raised ... ROLLBACK
raise ActiveRecord::Rollback
end
end
Newly added description: (sorry for not mentioning)
The ApplicationController.current_account is defined as:
class ApplicationController < ActionController::Base
class << self
def current_account
#current_account
end
def current_account=(value)
#current_account = value
end
end
=========
I encountered a strange performance in my current project, which is about session. The strange part is it was normal in Safari but failed in other browsers (includes chrome, firefox and opera).
There is a registration form for input of part of the key information (email, password, etc) and is submitted to an action called "create"
This is the basic code of create action:
#account = Account.new(params[:account])
if #account.save
ApplicationController.current_account = #account
session[:current_account] = ApplicationController.current_account
session[:account] = ApplicationController.current_account.id
email = #account.email
Mailer.deliver_account_confirmation(email)
flash[:type] = "success"
flash[:notice] = "Successfully Created Account"
redirect_to :controller => "accounts", :action => "create_step_2"
else
flash[:type] = "error"
flash[:title] = "Oops, something wasn't right."
flash[:notice] = "Mistakes are marked below in red. Please fix them and resubmit the form. Thanks."
render :action => "new"
end
Also I created a before_filter in the application controller, which has the following code:
ApplicationController.current_account = Account.find_by_id(session[:current_account].id) unless session[:current_account].blank?
For Safari, there is no any problem. But for the other browsers, the session[:current_account] does not exist and so produced the following error message:
RuntimeError in AccountsController#create_step_2
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
Please could anyone help me?
1] don't write
ApplicationController.current_account
Just
current_account
2] in your application_controller
def current_account
session[:current_account]
end
3]
ApplicationController.current_account = #account
session[:current_account] = ApplicationController.current_account
session[:account] = ApplicationController.current_account.id
should be
session[:current_account] = #account
session[:account] = #account.id