Receiving errors when saving Tweets to a database using Sinatra - ruby

I'm using Sinatra, EventMachine, DataMapper, SQLite3 and the Twitter Stream API to capture and save tweets. When I run the application from my command line, it seems to continually fail at tweet 50. If I'm not saving the tweets, it can run seemingly forever.
Below is the app code to capture tweets with 'oscar' in them, which provided a very quick stream. Just enter your twitter username and password and run at the command line.
require 'rubygems'
require 'sinatra'
require 'em-http'
require 'json'
require 'dm-core'
require 'dm-migrations'
USERNAME = '<your twitter username>'
PASSWORD = '<your secret password>'
STREAMING_URL = 'http://stream.twitter.com/1/statuses/filter.json'
DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/db/development.db")
class Tweet
include DataMapper::Resource
property :id, Serial
property :tweet_id, String
property :username, String
property :avatar_url, String
property :text, Text
end
DataMapper.auto_upgrade!
get '/' do
#tweets = Tweet.all
erb :index
end
def rip_tweet(line)
#count += 1
tweet = Tweet.new :tweet_id => line['id'],
:username => line['user']['screen_name'],
:avatar_url => line['user']['profile_image_url'],
:text => line['text']
if tweet.save
puts #count
else
puts "F"
end
end
EM.schedule do
#count = 0
http = EM::HttpRequest.new(STREAMING_URL).get({
:head => {
'Authorization' => [ USERNAME, PASSWORD]
},
:query => {
'track' => 'oscars'
}
})
buffer = ""
http.stream do |chunk|
buffer += chunk
while line = buffer.slice!(/.+\r?\n/)
rip_tweet JSON.parse(line)
end
end
end
helpers do
alias_method :h, :escape_html
end

I'm not sure you can safely mix EM and Sinatra in the same process. You might want to try splitting the Sinatra viewer and the EventMachine downloader into separate programs and processes.

Related

rails + heroku: point root in routes.rb file to app.rb file

I have a file called app.rb that runs a server when I run it on my local machine. I would like to deploy app.rb to Heroku so the server will run on Heroku. I think I need to make root in routes.rb point it to. How do I do this?
this is the config.ru:
require_relative 'config/environment'
require '/.app.rb'
run Rails.application
run Sinatra::Application
This is the web server code in app.rb, from codepath guides (https://guides.codepath.com/android/Google-Cloud-Messaging#step-3-setup-web-server):
require 'sinatra'
require 'rest-client'
require 'sequel'
# Create a SQLite3 database
DB = Sequel.connect('sqlite://gcm-test.db')
# Create a Device table if it doesn't exist
DB.create_table? :Device do
primary_key :reg_id
String :user_id
String :reg_token
String :os, :default => 'android'
end
Device = DB[:Device] # create the dataset
# Registration endpoint mapping reg_token to user_id
# POST /register?reg_token=abc&user_id=123
post '/register' do
if Device.filter(:reg_token => params[:reg_token]).count == 0
device = Device.insert(:reg_token => params[:reg_token], :user_id => params[:user_id], :os => 'android')
end
end
# Ennpoint for sending a message to a user
# POST /send?user_id=123&title=hello&body=message
post '/send' do
# Find devices with the corresponding reg_tokens
reg_tokens = Device.filter(:user_id => params[:user_id]).map(:reg_token).to_a
if reg_tokens.count != 0
send_gcm_message(params[:title], params[:body], reg_tokens)
end
end
# Sending logic
# send_gcm_message(["abc", "cdf"])
def send_gcm_message(title, body, reg_tokens)
# Construct JSON payload
post_args = {
# :to field can also be used if there is only 1 reg token to send
:registration_ids => reg_tokens,
:data => {
:title => title,
:body => body,
:anything => "foobar"
}
}
# Send the request with JSON args and headers
RestClient.post 'https://gcm-http.googleapis.com/gcm/send', post_args.to_json,
:Authorization => 'key=' + AUTHORIZE_KEY, :content_type => :json, :accept => :json
end
This is the procfile:
web: bundle exec puma -C config/puma.rb
when I follow the getting started with Ruby on Heroku example, I can see the example webpage. However, if I edit the config.ru file with 'run Sinatra::Application', and deploy to heroku, it is not able to show the example webpage anymore, and just says "Not Found"
require '/.app.rb'
This should be ./app.rb instead.

Read parameters via POST with Ruby + Sinatra + MongoDB

I'm creating a simple API with Sinatra + Ruby + MongoDB, working via GET not have problems, but via POST yes... I try to receive params but this come in empty, I don't know if I'm doing thing not good. I am not working with view html, just request and response JSON. I use POSTMAN for pass parameters via POST, but nothing.
Code: app.rb
require 'rubygems'
require 'sinatra'
require 'mongo'
require 'json/ext'
require './config/Database'
require_relative 'routes/Estudiantes'
require_relative 'routes/OtherRoute
Code Estudiantes.rb
# Rest Collection Student
collection = settings.mongo_db['estudiantes']
# Finding
get '/estudiantes/?' do
content_type :json
collection.find.to_a.to_json
end
# find a document by its ID
get '/estudiante/:id/?' do
content_type :json
collection.find_one(:_id => params[:id].to_i).to_json
end
# Inserting
post '/new_estudiante/?' do
content_type :json
student = params # HERE
puts 'Parameters: ' + student
new_id = collection.insert student
document_by_id(new_id)
end
# Updating
post '/update_name/:id/?' do
content_type :json
id = BSON::ObjectId.from_string(params[:id].to_s)
puts 'ID: ' + params[:id].to_s
name = params[:name].to_s # HERE
age = params[:age].to_i # HERE
puts 'Name and Age: ' + name + age.to_s
collection.update({:_id => id}, {'$set' => {:name => name, :age => age} })
document_by_id(id)
end
post '/post/?' do
puts params[:name].to_json # HERE
end
Thanks
Solution:
You should apply a JSON.parse and then read parameter
code
post '/post/?' do
params = JSON.parse request.body.read
puts params['name']
end

Issue with Shoes setup

When i am running shoes.rb file, which contains code to install gem, it throws error.
Undefined method setup for Shoes:Class
Code:
Shoes.setup do
gem 'activerecord' # install AR if not found
require 'active_record'
require 'fileutils'
ActiveRecord::Base.establish_connection(
:adapter => 'postgresql',
:dbfile => 'shoes_app'
)
# create the db if not found
unless File.exist?("shoes_app.sqlite3")
ActiveRecord::Schema.define do
create_table :notes do |t|
t.column :message, :string
end
end
end
end
class ShoesApp < Shoes
require 'note'
url '/', :index
def index
para 'Say something...'
flow do
#note = edit_line
button 'OK' do
Note.new(:message => #note.text).save
#note.text = ''
#result.replace get_notes
end
end
#result = para get_notes
end
def get_notes
messages = []
notes = Note.find(:all, :select => 'message')
notes.each do |foo|
messages << foo.message
end
out = messages.join("n")
end
end
Shoes.app :title => 'Notes', :width => 260, :height => 350
The problem was using Shoes4, where the setup method was unimplemented.
Shoes4 now implements Shoes.setup for backwards compatibility reasons but you don't really need it, so it doesn't do anything except for printing a warning that you should rather do gem install gem_name instead of using Shoes.setup.

How do I use omniauth in rspec for sinatra?

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

Sinatra Mongoid String not valid UTF-8

I wrote this little application :
require 'rubygems'
require 'sinatra'
require 'bson'
require 'mongoid'
Mongoid.configure do |config|
name = "articles"
host = "localhost"
config.master = Mongo::Connection.new.db(name)
config.persist_in_safe_mode = false
end
class Article
include Mongoid::Document
field :title
field :content
end
get '/' do
#articles = Article.all
end
get '/show/:id' do
#article = Article.find(params[:id])
end
get '/new' do
haml :new
end
post '/create' do
#article = Article.new(params['article'])
if #article.save
redirect '/'
else
redirect '/new'
end
end
The following error occur when i post an article with a content "Test d'un article en français"
BSON::InvalidStringEncoding at /create String not valid UTF-8
How i can fix this error ?
Thanks
This is a known issue with Ruby 1.9 and Sinatra. Wait for Sinatra 1.1 to be released or use Sinatra edge version from github.

Resources