This is the Sinatra code that I wrote. All gems exist, the ruby files compiles perfectly but when i go to localhost:4567/ the sinatra app doesnt run. It takes me to the 'Sinatra doesnt know this ditty' page. What mistake am i making here? Is it a syntax issue? I've posted the main ruby file's code here others are just haml files thats all.
require 'bundler'
Bundler.setup(:default)
require 'sinatra'
require 'haml'
require 'twitter'
require 'oauth'
class MyTweetWeek < Sinatra::Base
set :haml, :format => :html5, :attr_wrapper => '"'
enable :sessions, :static, :raise_errors
set :public_dir, File.join(File.dirname(__FILE__), 'public')
get '/' do
haml :index
end
get '/login' do
request_token = consumer.get_request_token(:oauth_callback => ENV['OAUTH_CALLBACK'])
session[:request_token] = request_token.token
session[:request_token_secret] = request_token.secret
redirect request_token.authorize_url
end
get '/oauth_callback' do
request_token = OAuth::RequestToken.new(
consumer,
session[:request_token],
session[:request_token_secret]
)
session[:request_token] = session[:request_token_secret] = nil
access_token = request_token.get_access_token(:oauth_verifier => params[:oauth_verifier])
session[:access_token] = access_token.token
session[:access_secret] = access_token.secret
redirect '/resume'
end
get '/resume' do
redirect '/' unless authenticated?
today = Date.today #get today's date
monday = today - today.cwday + 1 #calculate Monday
search = Twitter::Search.new
#screen_name = client.verify_credentials.screen_name
#number_of_tweets = 0
#number_of_mentions = 0
results = search.from(#screen_name)
.since_date(monday)
.no_retweets
.per_page(100)
.fetch
#number_of_tweets += results.size
while search.next_page?
results = search.fetch_next_page
#number_of_tweets += results.size
end
search.clear
results = search.q("##{#screen_name.gsub('#', '')}")
.since_date(monday)
.no_retweets
.per_page(100)
.fetch
#number_of_mentions += results.size
while search.next_page?
results = search.fetch_next_page
#number_of_mentions += results.size
end
haml :resume
end
error Twitter::Error::Unauthorized do
redirect '/'
end
not_found do
haml :not_found
end
private
def consumer
#consumer ||= OAuth::Consumer.new(
ENV['CONSUMER_KEY'],
ENV['CONSUMER_SECRET'],
:site => "https://api.twitter.com"
)
end
def client
Twitter.configure do |config|
config.consumer_key = ENV['CONSUMER_KEY']
config.consumer_secret = ENV['CONSUMER_SECRET']
config.oauth_token = session[:access_token]
config.oauth_token_secret = session[:access_secret]
end
#client ||= Twitter::Client.new
end
def authenticated?
!session[:access_token].nil? && !session[:access_secret].nil?
end
end
As you have a modular app do you need to require "sinatra/base" rather than "sinatra"? See here
See Serving a Modular App and add the line run! if app_file == $0 at the end of the class. Also see DavB's answer.
Related
This question already has answers here:
An irritating Issue about ruby hashes
(3 answers)
Closed 7 years ago.
I am creating a CRUD API using Ruby, Sinatra, and MongoDB. I keep getting the following error message and I can't figure out what I've done wrong:
SyntaxError: /yasi.rb:6: syntax error, unexpected =>, expecting '}' Yasi.connect {:server => "localhost", :db => "yasi"} ^ /yasi.rb:6: syntax error, unexpected ',', expecting '}' Yasi.connect {:server => "localhost", :db => "yasi"} ^
Here is what my code looks like:
require 'rubygems'
require 'sinatra'
require 'lib/yasi'
before do
Yasi.connect {:server => "localhost", :db => "yasi"}
end
get "/" do
#yasis = Yasi.find :all
erb :index
end
get "/new" do
erb :new
end
get "/delete/:id" do
Yasi.delete(params[:id])
redirect "/"
end
post "/" do
params.reject! {|k,v| k == "submit"}
Yasi.save(params)
redirect "/"
end
Here is the lib/yasi file:
require 'rubygems'
require 'mongo'
require 'sinatra'
module Yasi
class << self
def connect(config)
#db = Mongo::Connection.new(config[:server],config[:port] || 27017).db(config[:db])
end
def find(search)
if search == :all
#return all
yasi = #db.collection("yasis").find.to_a
return nil_or_array(yasi)
else
return find_with_criteria(search)
end
end
def save(yasi)
stringify_keys(yasi)
#handle author first
if yasi["author"]
stringify_keys(yasi["author"])
author = #db.collection("authors").find_one(yasi["author"])
unless author
author = #db.collection("authors").save(yasi["author"])
end
yasi["author"] = author
end
#db.collection("yasis").save(yasi)
end
def delete(id)
victim = #db.collection("yasis").find_one(Mongo::ObjectID.from_stringid))
#db.collection("yasis").remove(victim) if victim
end
private
def find_with_criteria(search)
stringify_keys(search)
if search["author"]
author = #db.collection("authors").find_one stringify_keys(search["author"])
if author
search[:author] = author
yasi = #db.collection("yasis").find(search).to_a
return nil_or_array yasi
else
nil
end
else
yasi = #db.collection("yasis").find(search).to_a
return nil_or_array(yasi)
end
end
def stringify_keys(hash)
hash.each_key do |key|
hash[key.to_s] = hash.delete(key)
end
hash
end
def nil_or_array(result)
if result.size == 0
return nil
else
return result
end
end
end
end
Couple things. Make sure you're accessing yasi in the correct path. Also, pass a variable referencing your hash as config to Yasi.connect like below:
require './lib/yasi'
before do
config = { :server => "localhost", :db => "yasi" }
Yasi.connect config
end
That should handle the error you've posted about, but I would also double check your indentation in the yasi file to make it easier to troubleshoot.
I am still quite fresh to Ruby, and especially testing in Ruby. Hopefully the code is not a trainwreck :) I am having issues using any_instance with the Twitter gem, while it works fine on my own classes.
This is (what I believe) the relevant code
require 'twitter'
require 'minitest/unit'
require 'mocha/mini_test'
omitting for brevity....
args = { id: 573536452149182464, id_str: 73536452149182464, text: 'This is an initial tweet from the user'}
initial_tweet = ::Twitter::Tweet.new(args)
::Twitter::REST::Timelines.any_instance.stubs(:user_timeline).returns(initial_tweet)
The code produces the following error:
Minitest::UnexpectedError: NoMethodError: undefined method `any_instance|' for Twitter::REST::Timelines:Module
Are principles to stubbing gems different, am I approaching it wrong?
EDIT: I have added the entire code for the two classes below.
twitter.rb
require 'rubygems'
require 'cinch'
require 'cinch/commands'
require 'twitter'
require 'shorturl'
module Gigabot
module Commands
class Twitter
include Cinch::Plugin
include Cinch::Commands
def initialize(bot)
super(bot)
#client = create_client
#follow = config[:follow]
#channels = bot.config.channels
#latest_tweets = Hash.new
set_initial_tweets
end
timer 60, method: :twitter_update
def twitter_update
#follow.each do |user|
new_tweet = #client.user_timeline(user, options = {exclude_replies: true}).first
if #latest_tweets[user] != new_tweet
short_url = ShortURL.shorten("https://twitter.com/#{user}/status/#{new_tweet.id}")
reply = Format(:bold, "<#{user}> ") + "#{new_tweet.full_text} [#{short_url}]"
reply = reply.gsub(/\n/,' ')
#channels.each {|channel| Channel(channel).send(reply)}
#latest_tweets[user] = new_tweet
end
end
end
private
def create_client
::Twitter::REST::Client.new do |c|
c.consumer_key = config[:consumer_key]
c.consumer_secret = config[:consumer_secret]
c.access_token = config[:access_token]
c.access_token_secret = config[:access_token_secret]
end
end
def set_initial_tweets
#follow.each do |user|
#latest_tweets[user] = #client.user_timeline(user, options = {exclude_replies: true}).first
end
end
end
end
end
twitter_test.rb
require 'twitter'
require 'minitest/unit'
require 'mocha/mini_test'
require File.dirname(__FILE__) + '/../../../helper'
require File.dirname(__FILE__) + '/../../../../lib/gigabot/commands/twitter'
module Gigabot
module Commands
class TwitterTest < TestCase
def setup
bot = Cinch::Bot.new
bot.loggers.level = :fatal
bot.config.plugins.options[Twitter] = {
consumer_key: 'test_key',
consumer_secret: 'test_key_secret',
access_token: 'test_access_token',
access_token_secret: 'test_access_token_secret',
follow: %w(follow1 follow2)
}
args = { id: 573536452149182464, id_str: 73536452149182464, text: 'This is an initial tweet from the user'}
initial_tweet = ::Twitter::Tweet.new(args)
::Twitter::REST::Timelines.any_instance.stubs(:user_timeline).returns(initial_tweet)
#plugin = Twitter.new(bot)
end
def test_create_twitter_client_on_initialize
refute_nil(#plugin.instance_variable_get(:#client))
end
end
end
end
I tried to work sinatra application, but the error occurs which is very mystery.
#encoding: utf-8
require 'sinatra'
require 'rss'
require 'dalli'
require './url'
require './feed'
set :bind, '0.0.0.0'
configure :production do
require 'newrelic_rpm'
end
...
configure :development do
require 'sinatra/reloader'
end
...
get '/new_movie' do
if params['tag2']
#key = 'tag1=' + params['tag1'] + '&tag2=' + params['tag2']
else
#key = 'tag1=' + params['tag1']
end
configure :production do ####### ERROR OCCURS AT HERE! #######
# if cache exists
if output = settings.cache.get(#key)
#isCacheUsed = true
output
end
end
unless #isCacheUsed
# Thread One
t1 = Thread.new(params['tag1']) do |param_tag1|
#feed_nico = feed_nico(param_tag1)
puts 'nico' if DEBUG_APP
end
# Thread Two
if params['tag2']
t2 = Thread.new(params['tag2']) do |param_tag2|
#feed_vimeo = feed_vimeo(param_tag2)
puts 'vimeo' if DEBUG_APP
end
end
# Main Thread
feed_hatena1 = feed_hatena(params['tag1'])
puts 'hatena1' if DEBUG_APP
t1.join
t2.join if params['tag2']
if params['tag2']
feed = feed_hatena1.append(
#feed_nico, #feed_vimeo).
unique
puts 'append + unique' if DEBUG_APP
else
feed = feed_hatena1.append(#feed_nico).unique
end
content_type = 'text/xml; charset=utf-8'
#output = feed.to_s
end
end
...
Thank you for your help.
You can't call "configure" from within your route. Make sure that all your configuration parameters exist outside of your routes
I have written a small Sinatra script to fetch 2 tweets of a user and display 10 retweeters in the descending order of their no. of followers:
Puzzle/puzzle.rb
require 'twitter'
require 'json'
require 'sinatra'
#require 'haml'
client = Twitter::REST::Client.new do |config|
config.consumer_key = ""
config.consumer_secret = ""
config.access_token = ""
config.access_token_secret = ""
end
set :server, 'webrick'
set :haml, :format => :html5
get '/' do
content_type :json
arr = []
retweeters = client.retweeters_of(429627812459593728)
retweeters.each do |retweeter|
ob = {}
ob[:name] = retweeter.name
ob[:followers_count] = retweeter.followers_count
arr.push(ob)
end
# remove the duplicates and sort on the users with the most followers,
sorted_influencers = arr.sort_by { |hsh| hsh[:followers_count] }
sorted_influencers.reverse!
sorted_influencers[0..9].to_s
end
I am trying to handle rate limits.
How to cache the json output to avoid rate limit exceeding?
Assuming you keep your very simple scenario, you could use a small custom class to store the information and provide thread-safe methods (it is not clear from your question where your problem exactly resides, but this one problem will arise anyway):
require 'json'
require 'sinatra'
require 'date'
require 'thread'
require 'twitter'
set :server, 'webrick'
set :haml, :format => :html5
class MyCache
def initialize()
#mutex = Mutex.new
#last_update = DateTime.new # by default, -4732 BC
#client = Twitter::REST::Client.new do |config|
config.consumer_key = ""
config.consumer_secret = ""
config.access_token = ""
config.access_token_secret = ""
end
end
def get_cache
#mutex.synchronize do
if DateTime.now - #last_update > 10.0 / (3600 * 24)
#last_update = DateTime.now
arr = []
retweeters = #client.retweeters_of(429627812459593728)
retweeters.each do |retweeter|
ob = {}
ob[:name] = retweeter.name
ob[:followers_count] = retweeter.followers_count
arr.push(ob)
end
# remove the duplicates and sort on the users with the most followers,
sorted_influencers = arr.sort_by { |hsh| hsh[:followers_count] }
sorted_influencers.reverse!
#cache = sorted_influencers[0..9].to_s
end
#cache
end
end
end
my_cache = MyCache.new
get '/' do
content_type :json
my_cache.get_cache
end
This version now includes everything needed. I use the #client to store the instance of the twitter client (I suppose it's reusable), also note how the whole code is inside the if statement, and at last we update #cache. If you are unfamiliar with Ruby, the value of a block is determined by its last expression, so when I write #cache alone it is as if I had written return #cache.
Shortened version:
Using the omniauth gem for sinatra, I can't get rspec log in to work and keep my session for subsequent requests.
Based on suggestions from http://benprew.posterous.com/testing-sessions-with-sinatra, and turning off sessions, I've isolated the problem to this:
app.send(:set, :sessions, false) # From http://benprew.posterous.com/testing-sessions-with-sinatra
get '/auth/google_oauth2/callback', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2] }
# last_request.session => {"uid"=>"222222222222222222222", :flash=>{:success=>"Welcome"}}
# last_response.body => ""
follow_redirect!
# last_request.session => {:flash=>{}}
# last_response.body => Html for the homepage, which is what I want
How do I get rspec to follow the redirect and retain the session variables? Is this possible in Sinatra?
From http://benprew.posterous.com/testing-sessions-with-sinatra, it seems like I'd have to send the session variables on each get/post request that I require login for, but this wouldn't work in the case of redirects.
The details:
I'm trying to use the omniauth gem in sinatra with the following setup:
spec_helper.rb
ENV['RACK_ENV'] = 'test'
# Include web.rb file
require_relative '../web'
# Include factories.rb file
require_relative '../test/factories.rb'
require 'rspec'
require 'rack/test'
require 'factory_girl'
require 'ruby-debug'
# Include Rack::Test in all rspec tests
RSpec.configure do |conf|
conf.include Rack::Test::Methods
conf.mock_with :rspec
end
web_spec.rb
describe "Authentication:" do
before do
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:google_oauth2, {
:uid => '222222222222222222222',
:info => {
:email => "someone#example.com",
:name => 'Someone'
}
})
end
describe "Logging in as a new user" do
it "should work" do
get '/auth/google_oauth2/'
last_response.body.should include("Welcome")
end
end
end
When trying to authenticate, I get a <h1>Not Found</h1> response. What am I missing?
On the Integration testing page of the omniauth docs, it mentions adding two environment variables:
before do
request.env["devise.mapping"] = Devise.mappings[:user]
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
end
But seems to be for rails only, as I added
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google_oauth2]
to my before block in my spec and I get this error:
Failure/Error: request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google_oauth2]
ArgumentError:
wrong number of arguments (0 for 1)
Edit:
Calling get with
get '/auth/google_oauth2/', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2]}
seems to give me last_request.env["omniauth.auth"] equal to
{"provider"=>"google_oauth2", "uid"=>"222222222222222222222", "info"=>{"email"=>"someone#example.com", "name"=>"Someone"}}
which seems right, but last_response.body still returns
<h1>Not Found</h1>
A partial answer...
The callback url works better, with the added request environment variables:
get '/auth/google_oauth2/callback', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2]}
follow_redirect!
last_response.body.should include("Welcome")
However, this doesn't work with sessions after the redirect, which is required for my app to know someone is logged in. Updated the question to reflect this.
Using this gist (originating from https://stackoverflow.com/a/3892401/111884) to store session data, I got my tests to store the session, allowing me to pass the session to further requests.
There might be an easier way though.
Setup code:
# Omniauth settings
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:google_oauth2, {
:uid => '222222222222222222222',
:info => {
:email => "someone#example.com",
:name => 'Someone'
}
})
# Based on https://gist.github.com/375973 (from https://stackoverflow.com/a/3892401/111884)
class SessionData
def initialize(cookies)
#cookies = cookies
#data = cookies['rack.session']
if #data
#data = #data.unpack("m*").first
#data = Marshal.load(#data)
else
#data = {}
end
end
def [](key)
#data[key]
end
def []=(key, value)
#data[key] = value
session_data = Marshal.dump(#data)
session_data = [session_data].pack("m*")
#cookies.merge("rack.session=#{Rack::Utils.escape(session_data)}", URI.parse("//example.org//"))
raise "session variable not set" unless #cookies['rack.session'] == session_data
end
end
def login!(session)
get '/auth/google_oauth2/callback', nil, { "omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2] }
session['uid'] = last_request.session['uid']
# Logged in user should have the same uid as login credentials
session['uid'].should == OmniAuth.config.mock_auth[:google_oauth2]['uid']
end
# Based on Rack::Test::Session::follow_redirect!
def follow_redirect_with_session_login!(session)
unless last_response.redirect?
raise Error.new("Last response was not a redirect. Cannot follow_redirect!")
end
get(last_response["Location"], {}, { "HTTP_REFERER" => last_request.url, "rack.session" => {"uid" => session['uid']} })
end
def get_with_session_login(path)
get path, nil, {"rack.session" => {"uid" => session['uid']}}
end
Sample rspec code:
describe "Authentication:" do
def session
SessionData.new(rack_test_session.instance_variable_get(:#rack_mock_session).cookie_jar)
end
describe "Logging in as a new user" do
it "should create a new account with the user's name" do
login!(session)
last_request.session[:flash][:success].should include("Welcome")
get_with_session_login "/"
follow_redirect_with_session_login!(session)
last_response.body.should include("Someone")
end
end
end