carrierwave not saving image - ruby-on-rails

i'm using Angularjs as front end,
i'm able to upload image along with some parameters .
i'm using this directive angular-file-upload to upload single image with parameters:
uploader = $scope.uploader = new FileUploader(
url: '/recipes',
alias: 'cover',
removeAfterUpload: true,
#transformRequest: angular.identity,
headers: {'X-CSRF-TOKEN': csrf_token,'accept': 'application/json'},
withCredentials: true
)
uploader.onBeforeUploadItem = (item)->
#data = angular.toJSON($scope.recipe)
item.formData.push("recipe": angular.toJson($scope.recipe))
#item.upload()
console.info('uploader', $scope.uploader);
uploader.uploadAll()
this is the create action:
def create
params[:recipe] = JSON.parse params[:recipe]
params[:recipe][:cover] = params[:cover]
#ingredients = Ingredient.where(:id => params[:recipe][:ingredients].map {|ingredient| ingredient[:id]})
#recipe = current_user.recipes.new(params.require(:recipe).permit(:name,:instructions))
#recipe.ingredients << #ingredients
#recipe.user_id = current_user.id
#recipe.save
render 'show', status: 201
end
recipe model :
class Recipe < ActiveRecord::Base
mount_uploader :cover, AvatarUploader
belongs_to :user
has_and_belongs_to_many :ingredients,:join_table => "ingredients_recipes"
accepts_nested_attributes_for :ingredients
attr_accessor :cover
end
this is the request :
-----------------------------100101598926016265538511946
Content-Disposition: form-data; name="recipe"
{"name":"amzpld","instructions":"mpzmdpzmez","ingredients":[{"id":3,"title":"oeuf"}]}
-----------------------------100101598926016265538511946
Content-Disposition: form-data; name="cover"; filename="10.jpg"
Content-Type: image/jpeg
ÿØÿà�JFIF������ÿÛ�� ( %"1"%),...383,7(-.+
0& $,,,2,,,,,,4,4,,,,,,,,,-,,,,,,,,,,,4,,,,,,,,,,,,,,,ÿÀ��¨,"�ÿÄ�������������
�ÿÄ�#�����!1AQa"q2¡±#BRÁáð3bÑr$ñCT¢ÿÄ��������������
ÿÄ�0�������!1A"Qaq¡2±#B3CÑáðÿÚ���?�öá]®Qâ1 Bs(À¡V_D$¯8þ&¡i6¨Û)Ò
´oóçÊ�dÌôªçñEÏêÚÙ]ëãzn£hÓÚêÄ6I½&&·¼ßËðȨ̈)L¤hGQÞ°Pf�MÔúSkÔBønàôÕjal3YðäKÇÖ¾Á¢#..........
the recipe is saved but with cover = nil
plz what i'm missing

You're making :cover part of your params[:recipe] hash:
params[:recipe][:cover] = params[:cover]
But you're not white-listing it, so instead of:
#recipe = current_user.recipes.new(params.require(:recipe).permit(:name,:instructions))
Try this:
#recipe = current_user.recipes.new(params.require(:recipe).permit(:name, :instructions, :cover))
update
You also have another issue in your Recipe model, you're using:
attr_accessor :cover
Remove that line, you don't need it because cover should be a real column in your recipes table, and if that column already exists this will override the method that assigns values to that column, thus causing your column to contain nil.
Now if you don't have that column in your recipes table, just add it using a migration, in your console type:
rails g migration add_cover_to_recipes cover:string
and then migrate:
rake db:migrate
Hope that solves your issue.

Related

activeadmin and dynamic store accessors fails on new resource

I want to generate forms for a resource that has a postgres jsonb column :data, and I want the schema for these forms to be stored in a table in the database. After a lot of research I am 90% there but my method fails in ActiveAdmin forms upon create (not update). Can anyone explain this?
Sorry for the long code snippets. This is a fairly elaborate setup but I think it would be of some interest since if this works one could build arbitrary new schemas dynamically without hard-coding.
I am following along this previous discussion with Rails 6 and ActiveAdmin 2.6.1 and ruby 2.6.5.
I want to store Json Schemas in a table SampleActionSchema that belong_to SampleAction (using the json-schema gem for validation)
class SampleActionSchema < ApplicationRecord
validates :category, uniqueness: { case_sensitive: false }, allow_nil: false, allow_blank: true
validate :schema_is_json_schema
private
def schema_is_json_schema
metaschema = JSON::Validator.validator_for_name("draft4").metaschema
unless JSON::Validator.validate(metaschema, schema)
errors.add :schema, 'not a compliant json schema'
end
end
end
class SampleAction < ActiveRecord::Base
belongs_to :sample
validate :is_sample_action
validates :name, uniqueness: { case_sensitive: false }
after_initialize :add_field_accessors
before_create :add_field_accessors
before_update :add_field_accessors
def add_store_accessor field_name
singleton_class.class_eval {store_accessor :data, field_name.to_sym}
end
def add_field_accessors
num_fields = schema_properties.try(:keys).try(:count) || 0
schema_properties.keys.each {|field_name| add_store_accessor field_name} if num_fields > 0
end
def schema_properties
schema_arr=SampleActionSchema.where(category: category)
if schema_arr.size>0
sc=schema_arr[0]
if !sc.schema.empty?
props=sc.schema["properties"]
else
props=[]
end
else
[]
end
end
private
def is_sample_action
sa=SampleActionSchema.where(category: category)
errors.add :category, 'not a known sample action' unless (sa.size>0)
errors.add :base, 'incorrect json format' unless (sa.size>0) && JSON::Validator.validate(sa[0].schema, data)
end
end
This all works correctly; For example, for a simple schema called category: "cleave", where :data looks like data: {quality: "good"}, I can create a resource as follows in the rails console:
sa=SampleAction.new(sample_id: 6, name: "test0", data: {}, category: "cleave" )
=> #<SampleAction id: nil, name: "test0", category: "cleave", data: {}, created_at: nil, updated_at: nil, sample_id: 6>
sa.quality = "good" => true
sa.save => true
To make this system work in AA forms, I call the normal path (new or edit)_admix_sample_action_form with params: {category: "cleave"} and then I generate permit_params dynamically:
ActiveAdmin.register SampleAction, namespace: :admix do
permit_params do
prms=[:name, :category, :data, :sample_id, :created_at, :updated_at]
#the first case is creating a new record (gets parameter from admix/sample_actions/new?category="xxx"
#the second case is updating an existing record
#falls back to blank (no extra parameters)
categ = #_params[:category] || (#_params[:sample_action][:category] if #_params[:sample_action]) || nil
cat=SampleActionSchema.where(category: categ)
if cat.size>0 && !cat[0].schema.empty?
cat[0].schema["properties"].each do |key, value|
prms+=[key.to_sym]
end
end
prms
end
form do |f|
f.semantic_errors
new=f.object.new_record?
cat=params[:category] || f.object.category
f.object.category=cat if cat && new
f.object.add_field_accessors if new
sas=SampleActionSchema.where(category: cat)
is_schema=(sas.size>0) && !sas[0].schema.empty?
if session[:active_sample]
f.object.sample_id=session[:active_sample]
end
f.inputs "Sample Action" do
f.input :sample_id
f.input :name
f.input :category
if !is_schema
f.input :data, as: :jsonb
else
f.object.schema_properties.each do |key, value|
f.input key.to_sym, as: :string
end
end
end
f.actions
end
Everything works fine if I am editing an existing resource (as created in the console above). The form is displayed and all the dynamic fields are updated upon submit. But when creating a new resource where e.g. :data is of the form data: {quality: "good"} I get
ActiveModel::UnknownAttributeError in Admix::SampleActionsController#create
unknown attribute 'quality' for SampleAction.
I have tried to both add_accessors in the form and to override the new command to add the accessors after initialize (these should not be needed because the ActiveRecord callback appears to do the job at the right time).
def new
build_resource
resource.add_field_accessors
new!
end
Somehow when the resource is created in the AA controller, it seems impossible to get the accessors stored even though it works fine in the console. Does anyone have a strategy to initialize the resource correctly?
SOLUTION:
I traced what AA was doing to figure out the minimum number of commands needed. It was necessary to add code to build_new_resource to ensure that any new resource AA built had the correct :category field, and once doing so, make the call to dynamically add the store_accessor keys to the newly built instance.
Now users can create their own original schemas and records that use them, without any further programming! I hope others find this useful, I certainly will.
There are a couple ugly solutions here, one is that adding the parameters to the active admin new route call is not expected by AA, but it still works. I guess this parameter could be passed in some other way, but quick and dirty does the job. The other is that I had to have the form generate a session variable to store what kind of schema was used, in order for the post-form-submission build to know, since pressing the "Create Move" button clears the params from the url.
The operations are as follows: for a model called Move with field :data that should be dynamically serialized into fields according to the json schema tables, both
admin/moves/new?category="cleave" and admin/moves/#/edit find the "cleave" schema from the schema table, and correctly create and populate a form with the serialized parameters. And, direct writes to the db
m=Move.new(category: "cleave") ==> true
m.update(name: "t2", quality: "fine") ==> true
work as expected. The schema table is defined as:
require "json-schema"
class SampleActionSchema < ApplicationRecord
validates :category, uniqueness: { case_sensitive: false }, allow_nil: false, allow_blank: true
validate :schema_is_json_schema
def self.schema_keys(categ)
sas=SampleActionSchema.find_by(category: categ)
schema_keys= sas.nil? ? [] : sas[:schema]["properties"].keys.map{|k| k.to_sym}
end
private
def schema_is_json_schema
metaschema = JSON::Validator.validator_for_name("draft4").metaschema
unless JSON::Validator.validate(metaschema, schema)
errors.add :schema, 'not a compliant json schema'
end
end
end
The Move table that employs this schema is:
class Move < ApplicationRecord
after_initialize :add_field_accessors
def add_field_accessors
if category!=""
keys=SampleActionSchema.schema_keys(category)
keys.each {|k| singleton_class.class_eval{store_accessor :data, k}}
end
end
end
Finally, the working controller:
ActiveAdmin.register Move do
permit_params do
#choice 1 is for new records, choice 2 is for editing existing
categ = #_params[:category] || (#_params[:move][:category] if #_params[:move]) || ""
keys=SampleActionSchema.schema_keys(categ)
prms = [:name, :data] + keys
end
form do |f|
new=f.object.new_record?
f.object.category=params[:category] if new
if new
session[:current_category]=params[:category]
f.object.add_field_accessors
else
session[:current_category] = ""
end
keys=SampleActionSchema.schema_keys(f.object.category)
f.inputs do
f.input :name
f.input :category
keys.each {|k| f.input k}
end
f.actions
end
controller do
def build_new_resource
r=super
r.assign_attributes(category: session[:current_category])
r.add_field_accessors
r
end
end
end

How to display image returned from Mongoid::GridFs

I'm trying to display my image saved in Mongo in the record row with the rails_admin gem.
I have my model saving, and my image saving, and I'm saving the image ID in the model record.
Here's my model:
require 'mongoid/grid_fs'
class Asset
include Mongoid::Document
field :data_file_name, type: String
field :data_content_type, type: String
field :data_file_size, type: Integer
field :image_id, type: String
end
Here's what I'm trying to do in rails_admin.rb for my Asset model:
list do
field :id
field :data_file_name
field :data_content_type
field :data_file_size
field :image do
formatted_value do
grid_fs = Mongoid::GridFs
bindings[:view].tag(:img, { :src => grid_fs.get(bindings[:object].image_id)})
end
end
end
And here's the action responsible for saving the model and image:
register_instance_option :controller do
proc do
if request.get? # EDIT
respond_to do |format|
format.html { render #action.template_name }
format.js { render #action.template_name, layout: false }
end
elsif request.put? # UPDATE
tempFile = params[:picture][:asset].tempfile
file = File.open(tempFile)
grid_fs = Mongoid::GridFS
grid_file = grid_fs.put(file.path)
Asset.new.tap do |asset|
asset.data_file_name = params[:picture][:asset].original_filename
asset.data_content_type = params[:picture][:asset].content_type
asset.data_file_size = ::ApplicationController.helpers.number_to_human_size(File.size(tempFile))
asset.image_id = grid_file.id
asset.save
binding.pry
end
end
end
end
The model is saving, and I can see the file saving in fs.files and fs.chunks, but at the moment, I'm just getting the following in the record row:
Update:
I've now tried getting the file from mongo (Which seems to work) and then displaying the image by using the file's actual filename.
field :image do
formatted_value do
grid_fs = Mongoid::GridFs
f = grid_fs.get(bindings[:object].image_id)
bindings[:view].tag(:img, { :src => f.filename})
end
end
Unfortunately this hasn't changed anything. Trying to open the image in a new tab takes me to the following link: /admin/asset#<Mongoid::GridFs::Fs::File:0x981vj5ry>
Update 2:
Changed field :image_id, type: String to field :image_id, type: BSON::ObjectId
No change in result.
If you are saving image data in GridFS, you need to have an endpoint in your application to retrieve that image data from GridFS and serve it to the applications. See this answer for how to serve image data: Rails - How to send an image from a controller
Then, link to this endpoint instead of linking to "f.filename" as you have indicated in the last code snippet.
After a lot more research, it looks like you can encode the data returned from grid_fs to base64.
In turn, you can use this to display the image by specifying the source to be data:image/png;base64,+base64Image like so:
field :asset_thumbnail do
formatted_value do
grid_fs = Mongoid::GridFs
f = grid_fs.get(bindings[:object].thumb_image_id)
b64 = Base64.strict_encode64(f.data)
bindings[:view].tag(:img, { :src => "data:image/png;base64,"+b64})
end
end

Cannot upload images to the cloud using Cloudinary(paperclip) gem - Rails

I got problem with cloudinary gem when uploading images, here is my image model:
class Image < ApplicationRecord
default_scope { where.not(photo_file_name: [nil, ""]).where.not(photo_content_type: [nil, ""]) }
belongs_to :article, optional: true
after_save :delete_invalid_image
has_attached_file :photo, :storage => :cloudinary, path: "/uploaded/:class/:attachment/:id/:style_:filename", styles: { thumb: "300x200#", large: "1024x768>"}
validates_attachment_content_type :photo, content_type: /\Aimage\/.*\Z/
#attr_accessor :photo
def original_photo_url
photo.path(:large, timestamp: false)
end
paginates_per 30
private
def delete_invalid_image
if !photo?
self.destroy
end
end
end
here is create method in image controller:
def create
if !params[:hint].nil?
#image = Image.new(photo: params[:file])
if #image.save
render json: {
image: {
url: #image.original_photo_url,
id: #image.id
}
}, content_type: "text/html"
else
render json: {
error: "Something is wrong"
}, content_type: "text/html"
end
else
image = Image.create!(image_params)
if params[:ajax_upload].present?
image = {
id: image.id,
title: image.title,
caption: image.caption,
description: image.description,
width: image.width,
height: image.height,
url: image.photo.path(:thumb)
}
respond_to do |format|
format.json { render json: {image: image}}
end
else
redirect_to admin_images_path
end
end
end
When I trying to create(upload) a new image, the log show:
SQL (7.6ms) INSERT INTO `images` (`caption`, `description`, `title`, `created_at`, `updated_at`) VALUES ('', '', '14696760_1230106580395129_29071409_n', '2017-01-06 00:43:51', '2017-01-06 00:43:51')
SQL (7.2ms) DELETE FROM `images` WHERE `images`.`id` = 22
you can notice the insert and delete commands were happened simultaneously. I guest the problem come from the create method, but I cannot point out exactly where is it. Pls show me where I was wrong.
You seem to be using Paperclip raw with Cloudinary. There is a gem to use Paperclip with Cloudinary. Try using that instead.
https://github.com/GoGoCarl/paperclip-cloudinary
That gem says you can't start :path with a forward slash.
You should specify the Paperclip path pattern that you would like to use to store and access your saved attachments. The value should be URL-friendly, should NOT begin with a forward slash, and, aside from forward slashes, can only contain alphanumeric characters, dashes (-), periods (.) and underscores (_). The path can be specified in your default Paperclip options or via has_attached_file.
Also don't use photo.path(:large, timestamp: false). Use photo.url instead.
https://github.com/thoughtbot/paperclip#view-helpers
Also, you seem to be missing fields. Paperclip will create *_file_name, *_file_size, etc. fields. I think your migrations are wrong.
https://github.com/thoughtbot/paperclip#usage

CarrierWave attributes that are not in the database is always equal to nil

I am use Rails 4.0.0 with CarrierWave gem.
Why are the attributes that are not in the database is always equal to nil?
It is only within the file PostUploader. Ultimately, the data arrives. How to make sure that these attributes are available in the file PostUploader?
class PostUploader < CarrierWave::Uploader::Base
...
version :thumb do
process :crop
end
def crop
model.image_crop # => nil
end
...
end
model:
validates :name, presence: true
mount_uploader :image, PostUploader
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
controller:
def create
#post = Post.new(post_params)
...
end
def post_params
params.require(:post).permit(:name, :image, :crop_x, :crop_y, :crop_w, :crop_h)
end
post_params
{"name"=>"trololo", "image"=>#, #original_filename="large (3).jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"post[image]\"; filename=\"large (3).jpg\"\r\nContent-Type: image/jpeg\r\n">, "crop_x"=>"0", "crop_y"=>"0", "crop_w"=>"100", "crop_h"=>"200"}
model object in PostUploader:
Post id: nil, name: "trololo", image: nil, created_at: nil, updated_at: nil
and
model.crop_x && model.crop_w always => nil
https://github.com/CandyDandy/Realty/tree/development This project.
I think the reason(based upon your code ) for that is the way assignment are working in tandem inside rails and carrierwave to explain in detail please observe the example below
class A
attr_accessor :abuse_word,:word
def initialize(abuse_word,word)
self.word = word
self.abuse_word = abuse_word
end
def word=(word)
puts "Abuse word isnt set yet ...."
puts "This will be nil => #{self.abuse_word}"
#word = word
end
end
O/p for the program is Like this
a1 = A.new("BAD","GOOD")
=> Abuse word isnt set yet ....
=> This will be nil =>
# But when you do this
a1.abuse_word
=> "BAD"
Now you can imagine something like this happening internally here what I meant
Considering this is your hash
{"name"=>"trololo", "image"=>#, #original_filename="large (3).jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"post[image]\"; filename=\"large (3).jpg\"\r\nContent-Type: image/jpeg\r\n">, "crop_x"=>"0", "crop_y"=>"0", "crop_w"=>"100", "crop_h"=>"200"}
No mind you image= (i.e your uploader_column= setter attributes is overridden by Carrierwave which internally call the processor which you have define i.e crop)
Now If you check the rails,rails does an assign_attributes based upon the hash passed to it check over here
Now when rails does this (by parsing your params)
image=[Value obtain from hash] this invoke the method define by carrierave which internally call the
processor crop and hence you get the desired result as crop_x && crop_w always set to nil because the rails hasn't finish with the assignment of image= and is yet to assign crop_x and crop_w because in hash above they are after in the image key
So
public_send("crop_x=",[desired_value])
public_send("crop_w=",[desired_value])
is not been evaluate yet when your code reached to processor to process the image hence
when evaluating in processor crop you get there value as nil
SOLUTION :
I suggest you do something like this in your controller
p1 = Post.new(post_params.except("image"))
p1.image = post_params.delete("image")
p1.save
Or else(I wont suggest this) make sure somehow make sure the crop_x,crop_w are prior to image key in the post_params hash so your hash would look something like this
{"name"=>"trololo","crop_x"=>"0", "crop_y"=>"0", "crop_w"=>"100", "crop_h"=>"200", "image"=>#, #original_filename="large (3).jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"post[image]\"; filename=\"large (3).jpg\"\r\nContent-Type: image/jpeg\r\n">}
You can clearly identify the difference between the two
Hope this help

Rails build assocation before_create doesn't save records

I have a student which has multiple products. Products can be selected to generate an invoice. This is the generate function:
def generate
#student = #driving_school.students.find(params[:student_id])
#products = #student.products.find(params[:product_ids])
#invoice = #driving_school.invoices.build({student_id: #student.id})
#invoice.reference = #student.full_name
#invoice.products = #products
#invoice.payment_is_due_in = 14
if #invoice.save!
redirect_to #invoice
else
redirect_to #invoice
end
end
The invoice contains lines, which are generated when creating the invoice using a before_create filter.
class Invoice < ActiveRecord::Base
...
before_create :copy_products_to_lines
...
private
def copy_products_to_lines
self.products.each do |product|
self.lines.build(
invoice_id: self.id,
description: product.name + ' ' + product.description,
amount: product.amount,
price: product.netto,
vat: true,
vat_rate: product.vat_rate,
undeletable: true
)
end
end
In rails 3.1.3 this has always worked perfectly well. Since updating to 3.1.10 because of the latest vulnerability in Rails this code broke. The invoice is generated successfully, but the lines are not created anymore. Does anyone know how come?
Adding has_many :lines, autosave: true solved it. Thanks charlysisto.

Resources