Mongo object's id as dirname for file upload in Sinatra/Mongoid - ruby

I'm working on Sinatra site which allows user to upload files. I got this action route:
post '/upload' do
params.delete 'submit'
UsrUpld.new(
:name => params[:name]
:created_at => Time.now
).save
params[:photos].each do |photo|
File.open('public/uploads/' + UsrUpld.id + '/' + photo[:filename], 'w') do |file|
file.write(photo[:tempfile].read)
end
end
redirect '/index'
end
I think this should create document in MongoDB with two fields name and created_at, the take file from POST request and write it in public/uploads/ID/FILE.jpg. But Pow returns me undefined method `id' for UsrUpld:Class. How to ask object's id in route with Mongoid?
Thank you in advance.

To ask an id, object should placed in variable which should contain exactly this object, so code should look like this:
post '/upload' do
params.delete 'submit'
u = UsrUpld.new(
:name => params[:name],
:created_at => Time.now
)
u.save
params[:photos].each do |photo|
unless File.exists?('public/media/' + u.id)
Dir.mkdir('public/media/' + u.id)
end
File.open('public/uploads/' + u.id + '/' + photo[:filename], 'w') do |file|
file.write(photo[:tempfile].read)
end
end
redirect '/index'
end
Also dir should exist before opening the file, thats why Dir.mkdir line added.

Related

Why does Sequel return only the first matching row?

I have an SQLite3 database called sk.db with a table called Sked that displays a schedule of sports matches with a column date. The code below gives me only the first matching row instead of all matching rows, of which there should be many. It doesnt matter what I use inside where(...) it only ever gives me the first matching row. If I use schedule.all it gives me the entire database but only the first matching row if I use where.
Where am I going wrong?
.rb
require 'date'
require 'sequel'
require 'sinatra'
DB = Sequel.connect("sqlite://sk.db")
class Sked < Sequel::Model
end
schedule = DB.from(:sked)
get '/' do
#todaymatches = schedule.where(:date => Date.today)
erb :games
end
.erb
<h1>Games</h1>
<p><%= #todaymatches.inspect %></p>
.where(...) queries don't actually retrieve records, they return datasets that can be used to chain more queries for example:
my_posts = DB[:posts].where(:author => 'david').where(:topic => 'ruby') # no records are retrieved
If you want to actually retrieve the records, put an all at the end:
#todaymatches = schedule.where(:date => Date.today).all # records are retrieved
See: https://sequel.jeremyevans.net/rdoc/classes/Sequel/Dataset.html
This might be clunky, but I think it will do what you want. Give it a whirl.
.rb
.
.
.
get '/' do
#todaymatches = schedule.where(:date => Date.today)
def print_today_matches
#todaymatches.each { |x| puts x.inspect }
end
erb :games
end
.erb
<h1>Games</h1>
<p><%= print_today_matches %></p>
Or, alternatively:
.rb
.
.
.
get '/' do
#todaymatches = schedule.where(:date => Date.today)
erb :games
end
.erb
<h1>Games</h1>
<p><%= #todaymatches.each { |x| p x } %></p>

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

Multiple Route Params in Grape

How do you get multiple route params in Grape to work in grape?
I can make this route work:
.../api/company/:cid
But when I try this:
.../api/company/:cid/members
.../api/company/:cid/members/:mid
I get errors.
Here's the code that works.
resource 'company' do
params do
optional :start_date, type: Date, desc: "Start date of range."
optional :end_date, type: Date, desc: "End date of range."
end
route_param :cid do
get do
{company_id: params[:cid]}
end
end
class API::Company < Grape::API
resource :company do
route_param :cid do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid
# process get
end
resources :members do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid/members/'
# process get
end
route_param :mid do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid/members/:mid'
# process get
end
end
end
end
end
end
You can do that that way. Or you can create two different resources file and mount Members to Company. Like so:
# api/company.rb
class API::Company < Grape::API
resource :company do
route_param :cid do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid
# process get
end
mount API::Members
end
end
end
# api/member.rb
class API::Member < Grape::API
resources :members do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid/members/'
# process get
end
route_param :mid do
desc "GET"
params do
# your params
end
get '/' do # => '.../api/company/:cid/members/:mid'
# process get
end
end
end
Hope that helps
Another way to do that is using a regexp to validate the ids.
resource 'company' :requirements => { :id => /[0-9]*/, :mid => /[0-9]*/ } do
get '/:id' do
# returns company
end
get ":id/members" do
members = Company.find_by_id(params[:id]).members
present members, :with => Members::MemberResponseEntity
end
get ":id/members/:mid" do
member = Member.find_by_id(params[:mid])
present member, :with => Members::MemberResponseEntity
end
end

Ruby JSON issue

I know the title is a bit vague, but I dont know what to put on there.
I'm developing an API with Sinatra for our backend in Ruby. The thing is that I need to be able to pass JSON to the service representing a User. The problem I'm facing is that when I run my tests it does not work, but doing it manually against the service it does work. I'm guessing there is an issue with the JSON format.
I've updated my User model to rely on the helpers from ActiveModel for the JSON serialization. I was running in too much problems with manual conversions. This is what the base User model looks like:
class User
include ActiveModel::Serializers::JSON
attr_accessor :login, :email, :birthday, :created_at, :updated_at, :password_sha, :password_salt
# Creates a new instance of the class using the information stored
# in the hash. If data is missing then nill will be assigned to the
# corresponding property.
def initialize(params = {})
return if params.nil?
self.login = params[:login] if params.key?("login")
self.email = params[:email] if params.key?("email")
self.birthday = Time.parse(params[:birthday]) rescue Time.now
if params.key?("password_salt") && params.key?("password_sha")
self.password_salt = params["password_salt"]
self.password_sha = params["password_sha"]
elsif params.key?("password")
self.set_password(params[:password])
end
self.created_at = Time.now
end
def attributes
{:login => self.login, :email => self.email, :birthday => self.birthday, :created_at => self.created_at, :updated_at => self.updated_at, :password_sha => self.password_sha, :password_salt => self.password_salt}
end
def attributes=(params = {})
self.login = params['login']
self.email = params['email']
self.birthday = params['birthday']
self.created_at = params['created_at']
self.updated_at = params['updated_at']
self.password_sha = params['password_sha']
self.password_salt = params['password_salt']
end
end
I'm using Cucumber, Rack::Test and Capybara to test my API implementation.
The code of the API application looks like this:
# This action will respond to POST request on the /users URI,
# and is responsible for creating a User in the various systems.
post '/users' do
begin
user = User.new.from_json(request.body.read)
201
rescue
400
end
end
In the above piece I expect the json representation in the request body. For some reason the params hash is empty here, don't know why
The test section that makes the actuall post looks like this:
When /^I send a POST request to "([^\"]*)" with the following:$/ do |path, body|
post path, User.new(body.hashes.first).to_json, "CONTENT_TYPE" => "application/json"
end
The example output JSON string generated by the User.rb file looks like this:
"{"user":{"birthday":"1985-02-14T00:00:00+01:00","created_at":"2012-03-23T12:54:11+01:00","email":"arne.de.herdt#gmail.com","login":"airslash","password_salt":"x9fOmBOt","password_sha":"2d3afc55aee8d97cc63b3d4c985040d35147a4a1d312e6450ebee05edcb8e037","updated_at":null}}"
The output is copied from the Rubymine IDE, but when I submit this to the application, I cannot parse it because:
The params hash is empty when using the tests
doing it manually gives me the error about needing at least 2 octets.

Sinatra: DB Authentication with Sessions

I am writing a small sinatra application that I am integrating with Authlogic (following https://github.com/ehsanul/Sinatra-Authlogic-Template)
Everything works except for when I try to login. I get the following error:
NameError at /login
undefined local variable or method `active' for #<User:0x000001040208f0>
I am including the authlogic gem versus including it as a vendor. So my Sinatra app is not exactly the same as the one on Github.
Any and all inquiries will be MUCH appreciated!! Thanks!
Found out my issue.
Here is the model according to the Github page:
class User < ActiveRecord::Base
acts_as_authentic do |c|
# Bcrypt is recommended
#crypto_provider = Authlogic::CryptoProviders::BCrypt
c.perishable_token_valid_for( 24*60*60 )
c.validates_length_of_password_field_options =
{:on => :update, :minimum => 6, :if => :has_no_credentials?}
c.validates_length_of_password_confirmation_field_options =
{:on => :update, :minimum => 6, :if => :has_no_credentials?}
end
def active?
active
end
def has_no_credentials?
crypted_password.blank? #&& self.openid_identifier.blank?
end
def send_activation_email
Pony.mail(
:to => self.email,
:from => "no-reply#domain.tld",
:subject => "Activate your account",
:body => "You can activate your account at this link: " +
"http://domain.tld/activate/#{self.perishable_token}"
)
end
def send_password_reset_email
Pony.mail(
:to => self.email,
:from => "no-reply#domain.tld",
:subject => "Reset your password",
:body => "We have recieved a request to reset your password. " +
"If you did not send this request, then please ignore this email.\n\n" +
"If you did send the request, you may reset your password using the following link: " +
"http://domain.tld/reset-password/#{self.perishable_token}"
)
end
end
I removed all of the mail methods but my script was failing on the active? method because it was looking for an active column in the users table. Since I am unable to append this column to the table (due to data integrity with another system) I simply told my method to return true
My User.rb
class UserSession < Authlogic::Session::Base
end
class User < ActiveRecord::Base
acts_as_authentic do |c|
end
def active?
return true
end
end
Hope this helps someone!

Resources