Thor: Class Variables are not working - ruby-on-rails

I have the following test code.
require "thor"
module Snap
class CLI < Thor
desc 'login', 'Login Credentials'
def login(user,pass)
#username = user
#password = pass
say #password
end
desc 'block', 'Block user'
def block(input)
say #username
end
end
end
If I type Snap login abc xyz in my command line.
I get the the output as xyz.
But when I type Snap block a.
The output i get is just a blank space.
ie: nothing gets stored at username or password.
Why is this and how can I resolve this?

The problem is that the program is terminated in between two invocations of your command. Therefore, all state is lost and consequently the username is lost.
In order to persist variables across multiple invocations of your command, you need to save it to a file. For example, you could save a hidden file in the user's home directory in the yaml format.
CAUTION: be aware that this will store the password in plain text in the config file!
require 'thor'
require 'yaml'
module Snap
class CLI < Thor
def initialize(*args)
super
read_config
end
desc 'login', 'Login Credentials'
def login(user, pass)
#username = user
#password = pass
say #password
write_config
end
desc 'block', 'Block user'
def block(input)
say #username
end
private
CONFIG_FILE = '~/.myprogram.conf'
def write_config
config = {}
config['username'] = #username
config['password'] = #password
File.open(CONFIG_FILE, 'w') do |f|
f.write config.to_yaml
end
end
def read_config
config = YAML.load_file(CONFIG_FILE)
#username = config['username']
#password = config['password']
end
end
end

Related

How to set up a callback in an initializer?

I'm using Koudoku for subscriptions. I want to do different things after receiving a Stripe webhook.
In the docs, it shows you can add a callback like so:
Koudoku.setup do |config|
config.subscriptions_owned_by = :user
config.stripe_publishable_key = ENV['STRIPE_PUBLISHABLE_KEY']
config.stripe_secret_key = ENV['STRIPE_SECRET_KEY']
# add webhooks
config.subscribe 'charge.failed', YourChargeFailed
end
What I can't figure out how to write the YourChargeFailed part. I've tried something like:
config.subscribe 'order.payment_succeeded', ActiveRecord::Subscription.after_successful_payment
but I get undefined method after_successful_payment for #<Class:0x007fb845849b30>
How can I successfully subscribe to Stripe events, capture the return data, and initiate a callback function?
Thanks!
UPDATE
Here is what I've tried, and the corresponding errors I'm receiving:
purchases_helper.rb
module PurchasesHelper
require 'stripe'
def stripe_webhook(event)
puts 'Purchases Helper'
puts 'invoice.payment_succeeded'
#customer = Stripe::Customer.retrieve(event[:data][:object][:customer])
#user = User.find_by(email: #customer[:email])
#badge = Badge.find_by(condition: '2019Purchase')
#badges_user = BadgesUser.find_by(user_id: #user.id, badge_id: #badge.id)
# if #badges_user === nil
# BadgesUser.create(user_id: user.id, badge_id: badge.id)
# end
puts 'badge created'
end
end
initializers/koudoku.rb
Koudoku.setup do |config|
include ::PurchasesHelper
config.subscribe 'charge.succeeded' do |event|
puts 'charge created'
::PurchasesHelper.stripe_webhook(event)
end
end
ERROR:
undefined method `stripe_webhook' for PurchasesHelper:Module excluded from capture: Not configured to send/capture in environment 'development'
NoMethodError (undefined method `stripe_webhook' for PurchasesHelper:Module):
Another attempt:
Koudoku.setup do |config|
config.subscribe 'charge.succeeded' do |event|
puts 'charge created'
PurchasesHelper.stripe_webhook(event)
end
end
ERROR:
undefined method `stripe_webhook' for PurchasesHelper:Module excluded from capture: Not configured to send/capture in environment 'development'
NoMethodError (undefined method `stripe_webhook' for PurchasesHelper:Module):
3rd Attempt:
Koudoku.setup do |config|
include PurchasesHelper
config.subscribe 'charge.succeeded' do |event|
puts 'charge created'
stripe_webhook(event)
end
end
ERROR:
A copy of PurchasesHelper has been removed from the module tree but is still active! excluded from capture: Not configured to send/capture in environment 'development'
ArgumentError (A copy of PurchasesHelper has been removed from the module tree but is still active!):
I see only one problem with your code.
module PurchasesHelper
require 'stripe'
def self.stripe_webhook(event) # NB self.
puts 'Purchases Helper'
puts 'invoice.payment_succeeded'
#customer = Stripe::Customer.retrieve(event[:data][:object][:customer])
#user = User.find_by(email: #customer[:email])
#badge = Badge.find_by(condition: '2019Purchase')
#badges_user = BadgesUser.find_by(user_id: #user.id, badge_id: #badge.id)
# if #badges_user === nil
# BadgesUser.create(user_id: user.id, badge_id: badge.id)
# end
puts 'badge created'
end
end
and then you call it by saying
Koudoku.setup do |config|
config.subscribe 'charge.succeeded' do |event|
puts 'charge created'
PurchasesHelper.stripe_webhook(event)
end
end
This should work
Wait but Why?!
Modules are a way of grouping together methods, classes, and constants. Modules give you two major benefits.
provide a namespace and prevent name clashes
implement the mixin facility (when you include them)
You've defined an instance method on the Module that when included it will appear on every instance of the object.
but you are not doing that in this case. You want to call stripe_webhook on the Module itself.
adding self. stripe_webhook in this case = PurchasesHelper. stripe_webhook which is the way to define a methods on the class/module.
You can even do more freaky stuff like:
class Animal
def self.all
%w[dog cat bird]
end
end
def Animal.include?(a)
self.all.include?(a)
end
Animal.include?('cat') # true
Animal.include?('virus') # false
so you can even define methods on the Animal class outside the scope of the class and it will work.
To sum up:
in this example:
module PurchasesHelper
def self.stripe_webhook(event)
#...
end
end
is equal to
module PurchasesHelper
def PurchasesHelper.stripe_webhook(event)
#...
end
end
which is why just adding self allows you to call PurchasesHelper.stripe_webhook
On Koudoku doc's, it says it actually uses stripe_event to handle that https://github.com/integrallis/stripe_event
So, looking on the strip_event examples, you can pass a block and do whatever you need or pass something that respond to the call method https://github.com/integrallis/stripe_event#usage

Rails invalid byte sequence in UTF-8 when using htmlentities gem

So I have the controller who scrapes the entire html of a page and stores it into mysql database. Before I store the data I want to encode it using the htmlentities gem. My issue is that with some websites it works ok e.g https://www.lookagain.co.uk/ but with others I get invalid byte sequence in UTF-8 such as https://www.google.co.uk/ and I do not know why. At first I though it might be something wrong with the database so I have changed all the fields to LONGTEXT but the problem still persists
Controller:
class PageScraperController < ApplicationController
require 'nokogiri'
require 'open-uri'
require 'diffy'
require 'htmlentities'
def scrape
#url = watched_link_params[:url].to_s
puts "LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOG#{#url}"
#page = Nokogiri::HTML(open(#url))
coder = HTMLEntities.new
#encodedHTML = coder.encode(#page)
create
end
def index
#savedHTML = ScrapedPage.all
end
def show
#savedHTML = ScrapedPage.find(id)
end
def new
#savedHTML = ScrapedPage.new
end
def create
#savedHTML = ScrapedPage.create(domain: #url, html: #encodedHTML, css: '', javascript: '')
if #savedHTML.save
puts "ADDED TO THE DATABASE"
redirect_to(root_path)
else
puts "FAILED TO ADD TO THE DATABASE"
end
end
def edit
end
def upadate
end
def delete
#watched_links = ScrapedPage.find(params[:id])
end
def destroy
#watched_links = ScrapedPage.find(params[:id])
#watched_links.destroy
redirect_to(root_path)
end
def watched_link_params
params.require(:default).permit(:url)
end
end

What has happened to the salt with bcrypt?

This is from the github page:
require 'bcrypt'
class User < ActiveRecord::Base
# users.password_hash in the database is a :string
include BCrypt
def password
#password ||= Password.new(password_hash)
end
def password=(new_password)
#password = Password.create(new_password)
self.password_hash = #password
end
end
It appears that to access the password method, you need to call it as an attribute from a create method:
#user.password = user_params[:password]
#user.save
Okay...fine? But where's the salt now stored? I just don't get it at all, how is this even remotely secure anymore?
To retrieve a hashed password, you need this method:
def password
#password ||= Password.new(password_hash)
end
And call it as an attribute:
if #user.password == params[:password]
give_token
else
false
end
So it appears everything's working without a salt...how does it do this?
This means I only need one column in my database to do with passowords now, right?password or password_hash instead of password_salt | password_hash?
Well then why does the github page say this:
But even this has weaknesses -- attackers can just run lists of
possible passwords through the same algorithm, store the results in a
big database, and then look up the passwords by their hash:
PrecomputedPassword.find_by_hash(<unique gibberish>).password #=> "secret1"
Salts
And then this is what really gets me:
The solution to this is to add a small chunk of random data -- called a salt -- to the password before it's hashed:
Why are they explaining all of this if bcrypt is handling everything automatically?
hash(salt + p) #=> <really unique gibberish>
The salt is then stored along with the hash in the database, and used to check potentially valid passwords:
<really unique gibberish> =? hash(salt + just_entered_password)
bcrypt-ruby automatically handles the storage and generation of these salts for you.
Could someone explain how bcrypt stores and generates these salts? Why does it say it handles it all for me and then goes on to tell me how to generate a salt? Do I need to run something like this in my model: self.password_hash = hash(salt + p)
Argh so confused I used to get salts and hashes utterly and now they've changed it all. Terrible, unclear docs...they appear to show you how to use bcrypt without a salt with loads of examples, and then briefly mention how to do it properly with a salt down at the bottom.
Could someone please give me an example how to use the new version of bcrypt to generate a salt and hash, and how to authenticate?
Okay, the has_secure_password is really cool. You don't need to worry about salts and hashes anymore, the salt and hash are stored as one attribute ( password_digest) in the database.
It's saved in such a way that bcrypt knows which part of the password_digest string is the salt, and what is the hash.
If you're setting up authentication from scratch, you literally need to do the following:
1) Add the bcrypt rails gem:
gem bcrypt-rails
2) Add the has_secure_password method to the model tasked with handling your user records:
class User < ActiveRecord::Base
has_secure_password
end
3) Make sure your users table has a password_digest column:
class CreateUser < ActiveRecord::Migration
create_table :users do |t|
t.username
t.password_digest
end
end
4) Create a new method to create a a new empty user instance for the form to use:
class UsersController < ApplicationController
def new
#user = User.new
end
end
5) In the new view, make a form that creates populates the params hash' :password and :username entries:
<% form_for( #user ) do |f| %>
<%= f.text_field :username %>
<%= f.password_field :password %>
<% end %>
6) Back in our controller, permit the username and the password using strong params. The whole reason behind strong params is to prevent some cheeky chappy from using dev tools to create their own html form field (such as one pertaining to id) and populating the database with malicious data:
class UsersController < ApplicationController
def new
#user = User.new
end
private
def user_params
params.require(:user).permit(:username, :password)
end
end
7) Let's create the create method that will use these permitted entries to create a new user, populated by the form:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
#user.save
redirect_to root_path
end
private
def user_params
params.require(:user).permit(:username, :password)
end
end
Set up your routes as you see fit, and that's it! The user record's password_digest column will be automatically populated with one string comprised of a salt appended with the hash of the password. Very elegant.
All you need to remember: password -> password_digest.
In order to authorise the user and signout the user, create a sessions controller with a create method and a destroy method:
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to admin_root_path, :notice => "Welcome back, #{user.username}"
else
flash.now.alert = "Invalid email or password"
redirect_to root_path
end
end
def destroy
reset_session
flash[:info] = "Signed out successfully!"
redirect_to root_path
end
Hope this helps someone!
bcrypt got everything covered for you. Your password digest consists of a few types of information, bcrypt algorithm type, cost, salt and the checksum.
for example:
my_password = BCrypt::Password.create("my password")
#=> "$2a$10$.kyRS8M3OICtvjBpdDd1seUtlvPKO5CmYz1VM49JL7cJWZDaoYWT."
The first part: $2a$ is the variant of the algorithm see: Where 2x prefix are used in BCrypt?
The second part 10 is the cost parameter, you can increase it to slow down the process (logarithmic value) by providing a hash {cost: 12} as the second argument to create.
Now if you call my_password.salt you get "$2a$10$.kyRS8M3OICtvjBpdDd1se" which identifies the part that is being used as the key to creating your checksum.
And finally, your checksum is "UtlvPKO5CmYz1VM49JL7cJWZDaoYWT.". That's the reason if you call create the second time the string is going to be different as another salt will be used.
But as I mentioned earlier you don't need to do anything extra as all these are being taken care of for you.

Rspec says implemented method is not implemented

Below is an rspec test I'm running to test another class I've made. Unfortunately the method I'm trying to test (delete) does not seem to be working. What's throwing me is that the error message I'm getting from the Termianl is:
/Users/user/Ruby/localWikiClient/localwiki_client/spec/delete_spec:11:in 'block (2 levels) in <top (required)>': undefined method 'delete' for #<Proc:0x007fe4739a5448> (NoMethodError)
However, this method is defined in the class. Below is the code:
require 'faraday'
require 'json/pure'
module Localwiki
##
# A client that wraps the localwiki api for a given server instance
#
class Client
attr_accessor :hostname # hostname of the server we'd like to point at
attr_reader :site_name # site resource - display name of wiki
attr_reader :time_zone # site resource - time zone of server, e.g. 'America/Chicago'
attr_reader :language_code # site resource - language code of the server, e.g. 'en-us'
def initialize hostname, user=nil, apikey=nil
#hostname = hostname
#user = user
#apikey = apikey
create_connection
collect_site_details
end
##
# Get site resource and set instance variables
#
def collect_site_details
site = fetch('site','1')
#site_name = site['name']
#time_zone = site['time_zone']
#language_code = site['language_code']
end
##
# create Faraday::Connection instance and set #site
#
def create_connection
#site = Faraday.new :url => #hostname
end
##
# delete a specific resource
# resources are "site", "page", "user", "file", "map", "tag", "page_tag"
# identifier is id, pagename, slug, etc.
def delete(resource,identifier)
case resource
when resource == "site"
#hostname = identifier
create_connection
when resouce == "user"
#hostname = list(identifier)
end
http_delete()
end
def http_delete()
response = #site.delete
puts response.to_s
end
Here's the rspec test I'm trying to run:
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
require 'localwiki_client'
describe '<siteinfo>.amazonaws.com/bears' do
subject { Localwiki::Client.new '<siteinfo>.compute-1.amazonaws.com/bears', '<username>', '[myApiKey]' }
context '#fetch' do
subject.delete('page', 'bears')
end
end
You can't access the subject like that within a context block. You will need to either put it in a before block or within an actual test block (it/specify):
describe '<siteinfo>.amazonaws.com/bears' do
subject { Localwiki::Client.new '<siteinfo>.compute-1.amazonaws.com/bears', '<username>', '[myApiKey]' }
context '#fetch' do
it "deletes the bears page" do
subject.delete('page', 'bears')
end
end
end

Ruby on Rails 2.3.8: Testing: How do I set up an instance variable to use throughout my tests?

Lets say I have some data that remanis the same throughout all of my tests, for forever and eternity. I create this data in setup. I store the data to #instance_var. But when I call #instance_var.attribute in any test, I get the following error:
RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
I know my instance variable isn't null, cause after it is set, I can do a puts #instance_var.inspect on it...
Any ideas?
EDIT:
setup do
user = Factory(:user)
account = Factory(:account)
set_user(user)
set_account(account)
puts "||||||||||||||||||||||||||||||||||||||||||||||" #this proves that the instance vars are not null
puts "| object_test.rb |"
puts "| #{#user.name} "
puts "| #{#account.name} "
puts "||||||||||||||||||||||||||||||||||||||||||||||"
end
A failing test (with the error above)
test "for detection of first content with multiple contents" do
object = Factory(:object, :user_id => #user.id, :account_id => #account.id)
... #the rest of this test isn't important, as it is the above line, on #user, where the nil.id error occers
in test_helper.rb
def set_user(user)
#user = user
end
def set_account(account)
#account = account
end
I don't really think I need these two methods, as when I define the #instance variable in setup, I get teh same result
in test_helper.rb there are some constants set fore ActiveSupport::TestCase:
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = false
fixtures :all
disabling these did nothing. =(
Have you tried
setup do
#user = Factory(:user)
#account = Factory(:account)
end
Normally, if you set the instance variables in the setup block, they should be available to all your tests. (You might be having an issue with scopes.)
My solution was to make a shared class, shared_test.rb
require 'test_helper'
class SharedTest
def self.initialize_testing_data
self.reset_the_database
self.set_up_user_and_account
# make sure our user and account got created
puts "|||||||||||||||||||||||||||||||||||||||||||||"
puts "| The user and account "
puts "| we'll be testing with:"
puts "| #{#user.name}"
puts "| #{#user.account.name}"
puts "|||||||||||||||||||||||||||||||||||||||||||||"
end
def self.reset_the_database
#clear the database and reset it
call_rake("db:test:prepare")
call_rake("db:bootstrap RAILS_ENV=test")
end
def self.set_up_user_and_account
#set up our user for doing all our tests (this person is very busy)
#user = Factory(:user)
#account = Factory(:account)
#user.account = #account
#user.save
end
end
so then at the top of every test file that needs user and account to stay the same between all the tests, you just do
require 'shared_test.rb'
and methods are called like
SharedTest.initialize_testing_data

Resources