Paperclip Unpermitted parameter: image - ruby-on-rails

I am trying to get Paperclip to upload an image to s3 from my festival model on form submit but am receiving the Unpermitted parameter: image. error
I have checked the strong params, the model content validation and read through the paperclip documents with no avail.
I think I have narrowed the problem down to my post request to the DB cannot handle the File object that gets assigned to festival.image, but can't figure out how I would represent this in the post request.
I am capturing the data in rails using react on rails on the front end with Rails as the backend. I was following along with this sample code https://github.com/carlbaron/react-file-upload-demo
I also use React-dropzone to capture the uploaded file and it adds the preview attribute for the image preview.
Been stuck on this for some time now, any help greatly appreciated!
Beginning of the post request printed to console
Processing by FestivalsController#create as JSON
Parameters: {"festival"=>{"fest_name"=>"Test Festival", "image"=>{"preview"=>"blob:http://localhost:5000/76b95cb5-45bf-46a9-ba7b-f5b9ad127521"}}}
| Unpermitted parameter: image
Festival object printed to the console
Post Request to the DB via axios
postFestival(festival) {
let config = {
responseType: 'json',
processData: false,
contentType: false,
headers: ReactOnRails.authenticityHeaders(),
};
let str = JSON.stringify(festival);
console.log("ENTITY IS " + str);
//returns
//ENTITY IS {"fest_name":"Test Festival","image":{"preview":"blob:http://localhost:5000/76b95cb5-45bf-46a9-ba7b-f5b9ad127521"}}
return(
request.post('/festivals/create', {festival}, config)
);
},
Festival.rb
class Festival < ApplicationRecord
has_attached_file :image, default_url: "/assets/ASOT-COVER.png"
validates_attachment :image,
content_type: { content_type: ["image/jpeg", "image/gif", "image/png"] }
end
Festivals Controller
def create
#festival = Festival.create(festival_params)
puts "festival.image =" + #festival.image.inspect
#returns = festival.image =#<Paperclip::Attachment:0x007fc288868bf0 #name=:image, #name_string="image", #instance=#
if #festival.save
puts "Festival SAved = + " + #festival.inspect
#returns the festival object saved to the DB minus the image param
else
respond_to do |format|
format.json { render json: #festival.errors, status: :unprocessable_entity}
puts "ERROR = " + #festival.errors.inspect
end
end
private
def festival_params
params.require(:festival).permit(:fest_name, :fest_organizer, :fest_location,
:fest_date, :fest_url, :fest_venue, :fest_description,
:image)
end
end

As the image parameter in your request is a hash "image"=>{"preview"=>"blob:http://localhost:5000/76b95cb5-45bf-46a9-ba7b-f5b9ad127521"}, you will need to modify your festival_params method like this:
def festival_params
params.require(:festival).permit(:fest_name, :fest_organizer, :fest_location,
:fest_date, :fest_url, :fest_venue, :fest_description,
{ image: :preview })
end
Let me know if it solves the error.

Related

save json block of code into nested model ruby rails

I get a hash of results from get_connection with koala gem which looks something like this
=> [{"album"=>{"created_time"=>"2011-05-07T23:06:33+0000", "name"=>"Timeline Photos",
"id"=>"10150170707957371"}, "images"=>[{"height"=>1150, "source"=>"https://scontent.xx.fbcdn.net/v/t31.0-8/24173690_10155086191327371_1463889654041392146_o.jpg?oh=8adec503c6066dc20d1be5d71262a03e&oe=5AFEED4A",
"width"=>2048}, {"album"=>{"created_time"=>"2011-05-07T23:06:33+0000", "name"=>"Timeline Photos",
"id"=>"10150170707957371"}, "images"=>[{"height"=>1188, "source"=>"https://scontent.xx.fbcdn.net/v/t31.0-8/24302317_10155086179077371_4000719398736973936_o.jpg?oh=6eba399a4067b847cb38ef245e687321&oe=5AFC195A",
"width"=>2639}]}]
I was looking to save photos like rails does with the params hash but realized it only creates a gallery for each photo with a do block .
def photos(user)
photos = user.facebook.get_connection("me", "photos?fields=album,images,event,height,width,link,place&type=uploaded")
photos.each do |photo|
params = { gallery: {
name: 'Facebook Pictures', pictures_attributes: [
{ fb_album_created_time: photo['album']['created_time'],
fb_album_name: photo['album']['name'],
fb_album_id: photo['album']['id'],
fb_source: photo['images'][0]['source'],
fb_height: photo['images'][0]['height'],
fb_width: photo['images'][0]['width'],
fb_link: photo['link'],
fb_pic_id: photo['id'] }
]
}
}
gallery = user.galleries.new(params[:gallery])
gallery.save!
end
end
How can I execute my nested picture attributes so they save to one gallery. Even a gem which could help me. I was looking at the rails/jbuider gem but not sure if its easier without a gem?
Fixed this with Jbuilder.
def to_jbuilder
Jbuilder.new
end
def photos(u)
json = to_jbuilder
first_page_photos = u.facebook.get_connection("me", "photos?fields=album,images,event,height,width,link,place&type=uploaded")
photos = json.photo first_page_photos do |photo|
json.fb_album_created_time photo['album']['created_time']
json.fb_album_name photo['album']['name']
json.fb_album_id photo['album']['id']
json.fb_source photo['images'][0]['source']
json.fb_height photo['images'][0]['height']
json.fb_width photo['images'][0]['width']
json.fb_link photo['link']
json.fb_pic_id photo['id']
end
params = { gallery: { name: 'Facebook Pictures', pictures_attributes: photos }}
gallery = u.galleries.new(params[:gallery])
gallery.save!
end

get Jcrop coordinates to convert_options in Paperclip and Rails 4

I'm trying to take user hidden field parameters from Jcrop and crop an image before putting it on the database. I've found a few tutorials and questions but none address my exact problem.
This comes the closest. It looks like the poster was trying to use a single parameter from a selection. I have 4 integers. I didn't think that would matter so I assume it's the 'has_attached_file' part:
Supplying a variable string to model for Paperclip Imagemagik resizing
That combined with this:
https://github.com/thoughtbot/paperclip#dynamic-configuration
Here is what I have,
MODEL:
class Muse < ActiveRecord::Base
attr_accessor :w, :h, :x, :y
def get_coor
#turn parameters into class variables
#w = self.w
#h = self.h
#x = self.x
#y = self.y
#build string for options
cropper = "-crop #{#w.to_i}x#{#h.to_i}+#{#x.to_i}+#{#y.to_i} -resize 200x200"
#return string of the convert options I want
#get_coor = cropper
end
belongs_to :user
default_scope -> { order(created_at: :desc) }
has_attached_file :muse_img, styles: lambda { |attachment| { original: (attachment.instance.get_coor) } }
validates_attachment :muse_img, presence: true,
content_type: { content_type: ["image/jpeg"] },
size: { in: 0..500.kilobytes }
end
I don't get errors. But it's acting like the get_coor method is calling nothing.
Before this I tried string interpolations directly on the convert_options, but it kept coming out as nil for all the coordinates. I'm new to Rails and Ruby so I'm still a little hazy on lambdas. Hopefully it's just a syntax error. Thanks for any help!
The solution seemed to be the order I put the params in params method. I had to put the coordinates first and then the image second. I found it here:
https://github.com/thoughtbot/paperclip/issues/1533
I also changed the has_attached_file section but I don't know if that had anything to do with the problem. But here it is anyway.
has_attached_file :muse_img,
:styles => lambda { |attachment|
crop_convert_options = attachment.instance.get_coor
{
original: {
convert_options: crop_convert_options
}
}
}

Rails API carrierwave cannot upload file send from API client

Im using unirest to create a client API in Node JS that will send a data to rails API. I have to change the file into base64 encoded string like this:
unirest.post('http://localhost:3000/api/v1/image_uploaders')
.headers({'Content-Type': 'multipart/form-data'})
.field({
"product_id": 12,
"variant_id": 1,
"image": fs.readFileSync(path).toString('base64')
}) // Form field
.end(function (response) {
console.log(response.body);
});
in Rails side this is what handle the request:
def create
variant = Variant.where(id: params[:variant_id]).first
if variant
product_image = ProductImage.new
product_image.image = StringIO.new(Base64.decode64(params[:image]))
product_image.product_id = params[:product_id]
product_image.variant_id = params[:variant_id]
if product_image.save
render json: true, status: :ok
else
render json: false, status: :bad_request
end
else
render json: false, status: :bad_request
end
end
I didn't get the file uploaded. Any idea? thanks.
UPDATE
I got this error message:
undefined method `unpack'
for #<ActionDispatch::Http::UploadedFile:0x007fe47ac26f80>
which point to this line:
product_image.image = StringIO.new(Base64.decode64(params[:image]))
You can use the following gem to use base64 encoded string with carrierwave:
https://github.com/lebedev-yury/carrierwave-base64
You would just need to change the mount_uploader to mount_base64_uploader in your ProductImage class, and then you can just assign the base64 encoded string to your image field:
product_image = ProductImage.new(params)
Solved it.
I change the:
"image": fs.readFileSync(path).toString('base64')
to:
"image": fs.createReadStream(path)
with that there is no need to decode the file. Thanks everyone

How to retrieve reference to AWS::S3::MultipartUpload with ruby

I have a rails app and in a controller action I am able to create a multipart upload like so:
def create
s3 = AWS::S3.new
bucket = s3.buckets["my_bucket"]
key = "some_new_file_name.ext"
obj = bucket.objects[key]
mpu = obj.multipart_upload
render json: {
:id => mpu.id
}
end
so now the client has the multipart upload id and she can upload parts to aws with her browser. I wish to create another action which will assemble the parts once they are done uploading. Something like:
def assemble
s3 = AWS::S3.new
bucket = s3.buckets["my_bucket"]
key = params['key']
bucket.objects[key].multipart_upload.complete
render json: { :status => "all good" }
end
This isn't working though. How do I retrieve a reference to the multipartUpload object or create a new one with the key or id so that I can call the "complete" method on it? Any insight is appreciated
I found this method in the documentation for the Client class and got it to work as follows:
client = AWS::S3::Client.new
# reassemble partsList
partsList = []
params[:partsList].each do |key, pair|
partsList.push pair
end
options = {
:bucket_name => 'my_bucket',
:key => params[:key],
:upload_id => params[:upload_id],
:parts => partsList
}
client.complete_multipart_upload(options)

without template, need to render not return json

I'm trying to make code from a Sinatra app work in the Rails context. The Sinatra app uses ajax requests to trigger the Sinatra routes/controller actions. For example, if you trigger the new function on a javascript model
new: function() {
var _this = this;
$.ajax({
url: "/gamestart",
type: "POST",
....
It will trigger the route/controller code in the Sinatra app
post "/new" do
end
When I tried to make this work in Rails, I'm getting a 500 internal server error. In my Rails app, the new_game button triggers an ajax request to a Rails route which triggers a controller action, and that controller action uses the Rails model to get data from the database. For some reason that doesn't seem like the right way to do it in Rails, and I'm wondering if it's the reason I'm getting the server error
GET http://localhost:3000/gamestart 500 (Internal Server Error)
If possible, can you tell me where in the chain of actions outlined below that error is arising and what I might do to fix it.
1 Click on the new game button triggers 'startNewGame' method
'click #new_game': 'startNewGame',
2 The startNewGame method calls method on Game model
startNewGame: function() {
this.model.new();
},
3 The new method in the Game model makes a GET request to the url '/gamestart'. I also tried a post request. I don't know why it would need to be a post request, but neither worked. (In the original Sinatra application, the gamestart url led immediately into the function post '/gamestart' do...)
new: function() {
var _this = this;
$.ajax({
url: "/gamestart",
type: "GET", \\\ also tried POST
success: function(response) {
var json = $.parseJSON(response);
_this.set({lost: false});
_this.set({win: false});
_this.trigger("gameStartedEvent", json);
}
})
},
4 I directed the url to a controller action in Rails router file
match 'gamestart' => 'locations#gamestart', :via => :get
Note, in the original Sinatra application, the route and the controller action were combined
5 The gamestart method of the locations_controller.rb
def gamestart
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
{:word => masquerade_word}.to_json
end
6 The get_random method on the word model Word.rb, which is called from locations controller
def get_random
words = []
locations = Location.all (this pulls up the names of the locations from the db)
locations.each do |e|
words << e.name
end
words.sample
end
ERROR MESSAGE
GET http://localhost:3000/gamestart 500 (Internal Server Error) jquery.js:8215
XHR finished loading: "http://localhost:3000/gamestart". jquery.js:8215
send jquery.js:8215
jQuery.extend.ajax jquery.js:7767
window.Game.Backbone.Model.extend game.js:27
window.OptionsView.Backbone.View.extend.startNewGame optionsView.js:14
jQuery.event.dispatch jquery.js:3062
elemData.handle.eventHandle
Note, in the original Sinatra application, the route and the controller action were combined in the usual Sinatra way
post "/gamestart" do
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
{:word => masquerade_word}.to_json
end
UPDATE
The 500 error seemed to be triggered by a missing template. This method in locations controller wasn't rendering anything. It didn't have a view file. I therefore changed the controller to make it respond_to :json and then use respond_with at the end of the action, but that triggered a 406 error.
def gamestart
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
{:word => masquerade_word}.to_json
end
became now triggers 406 error
respond_to :json
def gamestart
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
plainvariable = {:word => masquerade_word}.to_json ###changed
respond_with plainvariable ###changed
end
You say that your gamestart controller method is causing a server error due to a missing template. If we look at that controller method:
def gamestart
#...
{:word => masquerade_word}.to_json
end
we see that it returns a JSON string but it neglects to render anything. You don't call any rendering or redirection methods so Rails helpfully (ha ha) assumes that you want to render the gamestart view template; but, you have no such thing so you get an error.
You should render your JSON, not return it; something more like this:
def gamestart
#...
render :json => { :word => masquerade_word }
end

Resources