PG::SyntaxError at /bookmarks - I'm unable to work out why the SQL query is wrong - postgresql

When running my application using sinatra, I get the error message PG::SyntaxError at /bookmarks
ERROR: syntax error at or near "{" LINE 1: SELECT * FROM users WHERE id = {:id=>"5"} ^
It happens when I click the submit button on /users/new route which should then take me to index route /.
The backtrace provides the following information
/Users/BartJudge/Desktop/Makers_2018/bookmark-manager-2019/lib/database_connection.rb in async_exec
#connection.exec(sql)
/Users/BartJudge/Desktop/Makers_2018/bookmark-manager-2019/lib/database_connection.rb in query
#connection.exec(sql)
/Users/BartJudge/Desktop/Makers_2018/bookmark-manager-2019/lib/user.rb in find
result = DatabaseConnection.query("SELECT * FROM users WHERE id = #{id}")
app.rb in block in <class:BookmarkManager>
#user = User.find(id: session[:user_id])
This is the database_connection file
require 'pg'
class DatabaseConnection
def self.setup(dbname)
#connection = PG.connect(dbname: dbname)
end
def self.connection
#connection
end
def self.query(sql)
#connection.exec(sql)
end
end
This is the user model
require_relative './database_connection'
require 'bcrypt'
class User
def self.create(email:, password:)
encypted_password = BCrypt::Password.create(password
)
result = DatabaseConnection.query("INSERT INTO users (email, password) VALUES('#{email}', '#{encypted_password}') RETURNING id, email;")
User.new(id: result[0]['id'], email: result[0]['email'])
end
attr_reader :id, :email
def initialize(id:, email:)
#id = id
#email = email
end
def self.find(id)
return nil unless id
result = DatabaseConnection.query("SELECT * FROM users WHERE id = #{id}")
User.new(
id: result[0]['id'],
email: result[0]['email'])
end
end
This is the controller
require 'sinatra/base'
require './lib/bookmark'
require './lib/user'
require './database_connection_setup.rb'
require 'uri'
require 'sinatra/flash'
require_relative './lib/tag'
require_relative './lib/bookmark_tag'
class BookmarkManager < Sinatra::Base
enable :sessions, :method_override
register Sinatra::Flash
get '/' do
"Bookmark Manager"
end
get '/bookmarks' do
#user = User.find(id: session[:user_id])
#bookmarks = Bookmark.all
erb :'bookmarks/index'
end
post '/bookmarks' do
flash[:notice] = "You must submit a valid URL" unless Bookmark.create(url: params[:url], title: params[:title])
redirect '/bookmarks'
end
get '/bookmarks/new' do
erb :'bookmarks/new'
end
delete '/bookmarks/:id' do
Bookmark.delete(id: params[:id])
redirect '/bookmarks'
end
patch '/bookmarks/:id' do
Bookmark.update(id: params[:id], title: params[:title], url: params[:url])
redirect('/bookmarks')
end
get '/bookmarks/:id/edit' do
#bookmark = Bookmark.find(id: params[:id])
erb :'bookmarks/edit'
end
get '/bookmarks/:id/comments/new' do
#bookmark_id = params[:id]
erb :'comments/new'
end
post '/bookmarks/:id/comments' do
Comment.create(text: params[:comment], bookmark_id: params[:id])
redirect '/bookmarks'
end
get '/bookmarks/:id/tags/new' do
#bookmark_id = params[:id]
erb :'/tags/new'
end
post '/bookmarks:id/tags' do
tag = Tag.create(content: params[:tag])
BookmarkTag.create(bookmark_id: params[:id], tag_id: tag.id)
redirect '/bookmarks'
end
get '/users/new' do
erb :'users/new'
end
post '/users' do
user = User.create(email: params[:email], password: params[:password])
session[:user_id] = user.id
redirect '/bookmarks'
end
run! if app_file == $0
end
self.find(id), in the user model, is where the potentially offending SQL query resides.
I've tried;
"SELECT * FROM users WHERE id = #{id}"
and "SELECT * FROM users WHERE id = '#{id}'"
Beyond that, I'm stumped. The query looks fine, but sinatra is having none of it.
Hopefully someone can help me resolve this.
Thanks, in advance.

You're call find with a hash argument:
User.find(id: session[:user_id])
but it is expecting just the id:
class User
...
def self.find(id)
...
end
...
end
Then you end up interpolating a hash into your SQL string which results in invalid HTML.
You should be saying:
#user = User.find(session[:user_id])
to pass in just the id that User.find expects.
You're also leaving yourself open to SQL injection issues because you're using unprotected string interpolation for your queries rather than placeholders.
Your query method should use exec_params instead of exec and it should take some extra parameters for the placeholder values:
class DatabaseConnection
def self.query(sql, *values)
#connection.exec_params(sql, values)
end
end
Then things that call query should use placeholders in the SQL and pass the values separately:
result = DatabaseConnection.query(%q(
INSERT INTO users (email, password)
VALUES($1, $2) RETURNING id, email
), email, encypted_password)
result = DatabaseConnection.query('SELECT * FROM users WHERE id = $1', id)
...

Related

Ruby on Rails Tutorial NoMethodError in Users#index

I'm reading Michael Hartl's great "The Ruby on Rails Tutorial". And I faced a problem that wasn't described in the book.
I push a submit button on a Sign up page with blank fields, then, as it expected, the mistakes messages like these appears:
Name can't be blank
Email can't be blank
Email is invalid
Password can't be blank
But the URL changes from http://[::1]:3000/signup to http://[::1]:3000/users
Then I push the Chrome's refresh button and get redirecting to exactly http://[::1]:3000/users URL.
I understand that the pushing the submit button directs to .../users URL. How to change this route to the right one (still http://[::1]:3000/signup)?
Here's my users_controller.rb file
class UsersController < ApplicationController
before_action :logged_in_user, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def index
#users = User.where(activated: true).paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
redirect_to root_url and return unless #user.activated?
#microposts = #user.microposts.paginate(page: params[:page])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'users/new', status: :unprocessable_entity
end
end
def edit
#user = User.find(params[:id])
end
end
Here's my routes.rb file
Rails.application.routes.draw do
get 'password_resets/new'
get 'password_resets/edit'
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
default_url_options :host => "example.com"
resources :users
end
Thanks in advance!

Rails 4, devise, omniauth, facebook: problems when regular sign up involves nested forms

I am trying to set up facebook login integration with omniauth and devise.
Note that I have a MEMBER model which contains the devise info and a USER model that contains the member profile info (to keep them separate)
On the sign up page, the user has a nested form in the member sign up form and receives all the user data at that time. Once the member is saved, the email value for the saved member is also entered into the user table like so
class Member < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable, :omniauthable
after_save :update_user
def update_user
user.email = self.email
user.save
end
I have the standard model method for the facebook data processing.....
def self.process_omniauth(auth)
where(provider: auth.provider, uid: auth.uid, email: auth.info.email).first_or_create do |member|
member.provider = auth.provider
member.uid = auth.uid
member.email = auth.info.email
end
end
But of course, in this case, there is no nested form so the 'user' is not instantiated and it throws an error when the update_user method is called on saving the member.
How can I modify this method above to instantiate a new user, when signing up via the facebook pathway?
EDIT: This works.....
def update_user
if User.find_by_member_id(self.id).present?
user.email = self.email
user.save
else
User.create(:email => self.email, :member_id => self.id)
end
end
BUT THIS RESULTS IN A CONSTRAINT ERROR - on email - it already exists in the database. From the logs, the else statement here appears to be attempting to create the user twice.
def update_user
if User.find_by_member_id(self.id).present?
user.email = self.email
user.save
else
User.create(:email => self.email)
end
end
Can anyone explain this? I am suprised I had to pass the foreign key to the user in the else block.
I'm not sure what fields are being stored in the User Model but what you should be doing here that in case of Facebook Callback you should create the User if the Member don't have an user associated with it. i.e your update_user should be something like this:
def update_user
if user.present?
user.email = self.email
user.save
else
User.create(:email => self.email, :member_id => self.id)
/*note that a lot of other information is returned in
facebook callback like First Name, Last Name, Image and
lot more, which i guess you should also start saving to
User Model */
end
end
**** EDIT ******** . You should also check if there is user with the same email and associate that with the member.
def update_user
if user.present?
user.email = self.email
user.save
else
user = User.find_by_email(self.email)
if user
user.update_attribute(:member_id, self.id)
else
User.create(:email => self.email, :member_id => self.id)
end
end

Calling Document.find with nil is invalid MongoId in Rails 4

There are three models in my app: User, Subject and Note.
I've already embedded Note model into Subject model and it works. Now when I try to embed Subject into User always get Mongoid::Errors::InvalidFind coming from this line of code:
In the method one should define to use in before_action at the beginning of the controller
#user = User.find(params[:user_id])
subjects_controller
class SubjectsController < ApplicationController
#before_filter :authenticate_user!, only: [:index]
before_action :set_subject, only: [:show, :edit, :update, :destroy]
before_action :load_user
def index
#subjects = #user.subjects
end
def new
#subject = #user.subjects.build
end
def show
end
def create
#subject = #user.subjects.create(subject_params)
respond_to do |format|
if #subject.save
format.html {redirect_to subject_path(#subject)}
else
format.html {render 'new'}
end
end
end
def update
if #subject.update(subject_params)
redirect_to #subject
else
render 'edit'
end
end
def delete
end
private
def subject_params
params.require(:subject).permit(:name)
end
def set_subject
#subject = #user.subject.find(params[:id])
end
def load_user
#user = User.find(params[:user_id])
end
end
Routes
resources :users do
resources :subjects do
resources :notes
end
end
Right now I'm pretty stuck here because haven't found a way to work this around, hope someone around can give a hand.
Take into account this RoR best practice "Resources should never be nested more than 1 level deep." http://guides.rubyonrails.org/routing.html#nested-resources
A collection may need to be scoped by its parent, but a specific member can always be accessed directly by an id, and shouldn’t need scoping (unless the id is not unique, for some reason).
http://weblog.jamisbuck.org/2007/2/5/nesting-resources

About interacting and testing with django tastypie

I read many tutorial about django testing but I don't know how can I test for function insde resource , for example this User resource with signin function and obj_create. I really appreciate any helps because I can not figure out how to test them. k thanks.
class UserResource(ModelResource):
school = fields.ToOneField('frittie.app.api.api.LocationResource', 'user')
class Meta:
queryset = UserProfile.objects.all()
resource_name = 'User'
allowed_methods = ['get','post']
serializer = Serializer(formats=['json', 'plist'])
authorization= Authorization()
#models.signals.post_save.connect(create_api_key, sender=User)
#fields = ['username', 'email']
def obj_create(self, bundle, request=None, **kwargs):
if not request.method == "POST":
raise BadRequest('Object not found or not allowed to create a new one.')
username, email, password = bundle.data['username'], bundle.data['password'], bundle.data['password'],
try:
bundle.obj = User.objects.create_user(username, email, password)
except IntegrityError:
raise BadRequest('That username already exists')
return bundle
def signin(self, request, **kwargs):
self.method_check(request, allowed=['post'])
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return self.create_response(request, {'success': True})
else:
# Return a 'disabled account' error message
return self.create_response(request, {'success': False})
else:
# Return an 'invalid login' error message.
return self.create_response(request, {'success': False})
Tastypie has a descent testing documentation - ResourceTestCase API Reference

Redirected to http://localhost:3000/session/new

So I have a rails app 2.x app that works fine via the web, but when trying to perform a POST I keep getting "Redirected to http://localhost:3000/session/new Filter chain halted as [:require_user] rendered_or_redirected.". In my iPhone app, I can create a new session and sign-in via my iPhone app, but cannot POST to say the POSTS_Controller.
I have this in my code
Posts_Controller
before_filter :require_user, :only => [:create, :update, :destroy]
Application_Controller
# Filters added to this controller apply to all controllers in the application.
# Likewise, all the methods added will be available for all controllers.
class ApplicationController < ActionController::Base
include AuthenticatedSystem
include Geokit::Geocoders
helper :all # include all helpers, all the time
#session :session_key => '_cwa_session_id'
#filter_parameter_logging :password
# See ActionController::RequestForgeryProtection for details
# Uncomment the :secret if you're not using the cookie session store
protect_from_forgery # :secret => 'eejj7eded74769099999944a729b4f'
#filter_parameter_logging(:password)
before_filter :login_from_cookie
before_filter :find_user_interests
before_filter :find_user_posts
protected
def find_user_interests
#user_interests = cur_user ? cur_user.interesting_posts : []
logger.debug "User interests hash: #{current_user.inspect}"
end
def find_user_posts
#user_posts = cur_user ? cur_user.posts : []
end
def cur_user
User.find(session[:user_id]) if session[:user_id]
end
def require_user
unless cur_user
flash[:error] = "You must be logged in to do that."
redirect_to '/session/new'
return false
end
end
geocode_ip_address
def geokit
#location = session[:geo_location]
end
end
I have been working on this for 2 months and cannot figure out the issue. In my iPhone app I am using ObjectiveResource. I am sending over json and have "Mime::Type.register_alias "application/json", :json" set up on the rails side.
I am not a rails developer, but the before filter for require_user is unable to pass the cur_user test in that is cannot find :user_id in the session hash. Are you sure that you have a session that persists when using the iPhone? Are you using devise for authentication? Just for kicks, does it work if you manually pass the user_id as params?