nginx upload module - ruby-on-rails-3

I am having some trouble getting the nginx upload module working with my rails application.
my route
match '/images/fast_upload' => 'images#create', :via => :post
image model
attr_accessor :tmp_upload_dir
after_create :clean_tmp_upload_dir
# handle new param
def fast_asset=(file)
if file && file.respond_to?('[]')
self.tmp_upload_dir = "#{file['filepath']}_1"
tmp_file_path = "#{self.tmp_upload_dir}/#{file['original_name']}"
FileUtils.mkdir_p(self.tmp_upload_dir)
FileUtils.mv(file['filepath'], tmp_file_path)
self.asset = File.new(tmp_file_path)
end
end
private
# clean tmp directory used in handling new param
def clean_tmp_upload_dir
FileUtils.rm_r(tmp_upload_dir) if self.tmp_upload_dir && File.directory? (self.tmp_upload_dir)
end
nginx.conf
upload_pass #fast_upload_endpoint;
upload_store /pathto/shared/uploads_tmp 1;
upload_store_access user:rw group:rw all:r;
upload_set_form_field upload[fast_asset][original_name] "$upload_file_name";
upload_set_form_field upload[fast_asset][content_type] "$upload_content_type";
upload_set_form_field upload[fast_asset][filepath] "$upload_tmp_path";
upload_pass_form_field "^image_id$|^authenticity_token$|^format$";
upload_cleanup 400 404 499 500-505;
}
location #fast_upload_endpoint {
passenger_enabled on;
rails_env production;
}
location / {
rails_env production;
passenger_enabled on;
}
In the controller my create method
def create
#image = current_user.images.build(params[:image])
if #image.save
Basically I'm not sure how to get this create method to use nginx to upload. I tried to use #image = #resource.current_user.images.build(params[:image]) but that was giving me an undefined method error.

What you should check is what parameters nginx passes when creating the upload. I have the same logic like you do. The create action i've got is as follows. Bear with me because i'm not able to test the parameters nginx passes now. But I think it's not "image" but "upload"
#photo = #artist.photos.build(params[:upload])
Is my create method.

Related

Sinatra app as rack middleware TimesOut Rails 3

While in the Rails development environment, I am attempting to add a Sinatra app as a middleware. The Sinatra app uses the geoip gem that processes a user's ip address and returns json with their city.
I can view the returned json by going directly to the example url in the browser or using curl in the command line, http://local.fqdn.org/geoip/locate.json?ip=24.18.211.123. However when I attempt to call the url with wget from within a Rails controller, the Rails app stops responding often crashing my browser and my rails server wont exit using the control+C command.
Any clue to what is happening here? Why would going directly to the url in the browser return the proper response but my call in the controller results in a Time Out?
sinatra-geoip.rb
require 'sinatra'
require 'geoip'
require 'json'
# http://localhost/geoip/locate.json?ip=24.18.211.123
#
# {
# latitude: 47.684700012207
# country_name: "United States"
# area_code: 206
# city: "Seattle"
# region: "WA"
# longitude: -122.384803771973
# postal_code: "98117"
# country_code3: "USA"
# country_code: "US"
# dma_code: 819
# }
class GeoIPServer < Sinatra::Base
get '/geoip/locate.json' do
c = GeoIP.new('/var/www/mywebsite.org/current/GeoLiteCity.dat').city(params[:ip])
body c.to_h.to_json
end
end
routes.rb
mount GeoIPServer => "/geoip"
config/environments/development.rb
Website::Application.configure do
require "sinatra-geoip"
config.middleware.use "GeoIPServer"
...
end
controller
raw_geo_ip = Net::HTTP.get(URI.parse("http://#{geoip_server}/geoip/locate.json?ip=#{request.ip}"))
#geo_ip = JSON.parse(raw_geo_ip)
Our solution was difficult to find. We ended up finding a method in the sinatra source code call forward.
new sinatra-geoip.rb
class GeoIPServer < Sinatra::Base
if defined?(::Rails)
get '/properties.json' do
env["geo_ip.lookup"] = geo_ip_lookup(request.ip)
forward
end
end
def geo_ip_lookup(ip = nil)
ip = ip.nil? ? params[:ip] : ip
result = GeoIP.new('/var/www/mywebsite.org/current/GeoLiteCity.dat').city(ip)
result.to_h.to_json
end
end
Essentially, we removed the /geoip/locate.json route from the file and converted it to a simple method. We needed the geoip lookup to occur when the properties.json was being called, so a new param was added with the geoip information. Then we set the new param equal to #geo_ip variable in the controller.
New properties controller
if Rails.env.development? or Rails.env.test?
# Retrieves param set by sinatra-geoip middleware.
#geo_ip = JSON.parse(env["geo_ip.lookup"] || "{}")
else
# Production and staging code
end
Rather obscure problem and solution. Hopefully it will help someone out there. Cheers.

Paperclip and Phusion Passenger NoHandlerError

I followed this guide to get drag and drop file uploads through AJAX: http://dannemanne.com/posts/drag-n-drop_upload_that_works_with_ror_and_paperclip
Everything was working fine on my development environment with WebBrick but if I deploy to PhusionPassenger then I get:
Paperclip::AdapterRegistry::NoHandlerError (No handler found for #<PhusionPassenger::Utils::RewindableInput:0x000000041aef38 #io=#<PhusionPassen...
I'm using this in my controller:
before_filter :parse_raw_upload, :only => :bulk_submissions
def bulk_submissions
...
#submission = Submission.create!(url: "", file: #raw_file, description: "Please edit this description", work_type: "other", date_completed: DateTime.now.to_date)
...
end
private
def parse_raw_upload
if env['HTTP_X_FILE_UPLOAD'] == 'true'
#raw_file = env['rack.input']
#raw_file.class.class_eval { attr_accessor :original_filename, :content_type }
#raw_file.original_filename = env['HTTP_X_FILE_NAME']
#raw_file.content_type = env['HTTP_X_MIME_TYPE']
end
end
Looking at the request itself all the headers are set (X_MIME_TYPE, X_FILE_NAME) etc.
Any ideas?
Thanks in advance!
The example you're cribbing from expects the file stream to be a StringIO object, but Passenger is giving you a PhusionPassenger::Utils::RewindableInput object instead.
Fortunately, a RewindableInput is duckalike to StringIO for this case, so Paperclip's StringioAdapter can be used to wrap your upload stream.
Inside the if block in your parse_raw_upload, at the end, do:
if #raw_file.class.name == 'PhusionPassenger::Utils::RewindableInput'
#raw_file = Paperclip::StringioAdapter.new(#raw_file)
end

encoding error with ajax upload (qqfile) and paperclip

I'm trying to get an ajax upload working with rails 3.1.3 and paperclip.
I found this solution to my problem Rails 3 get raw post data and write it to tmp file, but using this, I get an 'encoding undefined conversion error "\xFF" from ASCII-8BIT to UTF-8.
The error occurs at the line #user.photo = #user.photo = QqFile.parse(params[:qqfile], request)
I have not edited the code supplied in the previous answer, but I'll include it here so you don't have to switch back and forth.
the gem list paperclip, returns 2.5.2, 2.4.5, 2.3.8
my controller
def create
#user = User.new(params[:user])
#user.photo = QqFile.parse(params[:qqfile], request)
if #user.save
return render :json => #user
else
return render :json => #user.errors
end
end
qq_file.rb
# encoding: utf-8
require 'digest/sha1'
require 'mime/types'
# Usage (paperclip example)
# #asset.data = QqFile.new(params[:qqfile], request)
class QqFile < ::Tempfile
def initialize(filename, request, tmpdir = Dir::tmpdir)
#original_filename = filename
#request = request
super Digest::SHA1.hexdigest(filename), tmpdir
fetch
end
def self.parse(*args)
return args.first unless args.first.is_a?(String)
new(*args)
end
def fetch
self.write #request.raw_post
self.rewind
self
end
def original_filename
#original_filename
end
def content_type
types = MIME::Types.type_for(#request.content_type)
types.empty? ? #request.content_type : types.first.to_s
end
end
This was an encoding error related to Ruby 1.9.2 (or I believe Ruby 1.9+).
this github post lead to the answer
https://github.com/lassebunk/webcam_app/issues/1
You must specify raw_post.force_encoding("UTF-8") when reading an upload as far as I could tell (I'm not a great programmer).

Rails overriding default_url_options causes URL's like "localhost:2999:3000/..."

in my setup I've got nginx running on port 3000 acting as a proxy to my rails app running on port 2999. Obviously, I have to have the rails app use 3000 as the port for generated URL's (so that when I use login_url, it will generate an url like http://localhost:3000/login.
I overrid rails' default_url_options like this:
def default_url_options( options = {} )
options.merge( { :only_path => false, :port => 3000 } )
end
However, this causes URL's like http://localhost:2999:3000/login for login_path.
Only thing I found was this ticket describing a related problem: https://rails.lighthouseapp.com/projects/8994/tickets/1106-rewrite_url-adds-port-twice, but there's nothing helpful in there.
Is there any way to make rails get the URL right?
thx in advance
If you know the hostnames for your different environments, you could set them statically, which routes around the problem:
# config/environments/production.rb
DEFAULT_HOST_WITH_PORT = 'http://myrealsite.com:3000'
# config/environments/development.rb
DEFAULT_HOST_WITH_PORT = 'http://localhost:3000'
# config/environments/test.rb
DEFAULT_HOST_WITH_PORT = 'http://localhost:3000'
and then
def default_url_options( options = {} )
options.merge(:only_path => false, :host => DEFAULT_HOST_WITH_PORT)
end
(notice that you're not setting the port separately, you're just counting on it being included in host)

Error when saving a Backbone.js model with Rails

I have Backbone.js collection and model for a project object:
window.Project = Backbone.Model.extend();
window.Projects = Backbone.Collection.extend({
model: Project,
url: '/projects'
});
I have setup a rails controller to respond to the Backbone.js collection:
class ProjectsController < ApplicationController
def index
render :json => Project.all
end
def create
project = Project.create! params
render :json => project
end
end
Index works fine and I get a list of projects in my web app. The problem is if I try and create a model on the Projects collection I get a 500 error from the server.
The error message on the server is as follows:
Started POST "/projects" for 127.0.0.1 at 2011-08-21 08:27:56 +0100
Processing by ProjectsController#create as JSON
Parameters: {"title"=>"another test"}
Completed 500 Internal Server Error in 16ms
ActiveRecord::UnknownAttributeError (unknown attribute: action):
app/controllers/projects_controller.rb:8:in `create'
I am not sure what the unknown attribute: action is referring to.
For info I have set up the projects_controller as resources :projects. I have also set rails to ActiveRecord::Base.include_root_in_json = false.
Yes, Rails always adds the action and controller to params. The parameters come from ActionDispatch::Http::Parameters:
def parameters
#env["action_dispatch.request.parameters"] ||= begin
params = request_parameters.merge(query_parameters)
params.merge!(path_parameters)
encode_params(params).with_indifferent_access
end
end
And path_parameters:
Returns a hash with the parameters used to form the path of the request. Returned hash keys are strings:
{'action' => 'my_action', 'controller' => 'my_controller'}
So you shouldn't be doing project = Project.create! params. You could go the update_attributes route:
project = Project.new
project.update_attributes params[:model_name]
But this assumes that you have what you need in a sub-hash of params and it won't call your validators. Backbone won't namespace your attributes by default but you could override Backbone.sync and do it yourself. Still, you probably want your validations so update_attributes should generally be avoided.
Your best bet is to pull exactly the attributes out of params that you're expecting to be there. This is even the Backbone recommended practise:
*(In real code, never use update_attributes blindly, and always whitelist the attributes you allow to be changed.)*
You can enable parameter wrapping. Add a file in the initializer directory with:
ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json]
end
and, for json request, you post params will now be wrapped with the model name.