get Jcrop coordinates to convert_options in Paperclip and Rails 4 - ruby-on-rails

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
}
}
}

Related

Paperclip Unpermitted parameter: image

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.

remove id from select().distinct

I have this is AR on my controller
areas = WaterQuality.select(:area_id, :area).distinct
The problem is, the result always has id together with area_id and area. I have to do this bit to remove id from the result.
areas = WaterQuality.select(:area_id, :area).distinct
new_areas = []
areas.each do |area|
new_area = {
:area_id => area.area_id,
:area => area.area
}
new_areas << new_area
end
respond_with(new_areas)
I set the respond as respond_to :xml, :json
How can I have the result with only 2 attributes that I have defined without any other attributes?
You can try this-
results = WaterQuality.select(:area_id, :area).distinct.as_json
results_without_id = results.each {|h| h.delete("id")}
This would return you an array of hashes with key/value pair.
Hope it helps!
Maybe a bit over the top for your but I've started using ActiveModel::Serializer a while ago and I'm very happy with it.
class WaterQualityDistinctSerializer < ActiveModel::Serializer
attributes :area_id, :area
end
You could try:
WaterQuality.all.map { |record| record.slice(*%w(area_id area)).symbolize_keys }
Or even:
WaterQuality.all.map { |record| record.slice(*%w(area_id area)) }
This will return a slightly different hash in console, but the same json or xml.

How can I validate an attribute to be between various noncontinuous ranges?

I'm wanting to validate that my height attribute is within a bunch of different ranges. So my attempt was something like what I did below... however this is incorrect. How should this be done? Thanks!
validates :height, :numericality => { in: { 5020..5028, 5030..5038, 5040..5048, 5050..5058, 5060..5068, 5070..5078, 5080..5088, 5090..5098, 5100..5108, 5110..5118,
6000..6008, 6010..6018, 6020..6028, 6030..6038, 6040..6048, 6050..6058, 6060..6068, 6070..6078, 6080..6088, 6090..6098, 6100..6108, 6110..6118,
7000..7008, 7010..7018, 7020..7028, 7030..7038, 7040..7048, 7050..7058, 7060..7068, 7070..7078, 7080..7088, 7090..7098, 7100..7108, 7110..7118 } }
You can put that in a custom validate method:
class YourModel < ActiveRecord::Base
VALID_HEIGHT_RANGES = [5020..5028, 5030..5038, 5040..5048, 5050..5058, 5060..5068, 5070..5078, 5080..5088, 5090..5098, 5100..5108, 5110..5118, 6000..6008, 6010..6018, 6020..6028, 6030..6038, 6040..6048, 6050..6058, 6060..6068, 6070..6078, 6080..6088, 6090..6098, 6100..6108, 6110..6118, 7000..7008, 7010..7018, 7020..7028, 7030..7038, 7040..7048, 7050..7058, 7060..7068, 7070..7078, 7080..7088, 7090..7098, 7100..7108, 7110..7118]
validate :height_in_valid_range
private
def height_in_valid_range
VALID_HEIGHT_RANGES.each do |range|
unless range.include? height
errors.add :height, "not in valid range"
break
end
end
end
end

Setting single coordinates for RGeo Point

RGeo provides built in methods for POINT features, for example getter methods lat() and lon() to pull latitude and longitude values from a POINT object. Unfortunately, these don't work as setters. For example:
point = RGeo::Geographic.spherical_factory(:srid => 4326).point(3,5) // => #<RGeo::Geographic::SphericalPointImpl:0x817e521c "POINT (3.0 5.0)">
I can do this:
point.lat // => 5.0
point.lon // => 3.0
But I can't do:
point.lat = 4 // => NoMethodError: undefined method `lat=' for #<RGeo::Geographic::SphericalPointImpl:0x00000104024770>
Any suggestions as to how to implement setter methods? Would you do it in the Model or extend the Feature class?
I'm the author of RGeo, so you can consider this answer authoritative on that basis.
In short, PLEASE AVOID DOING THIS. RGeo objects intentionally have no setter methods because they are meant to be immutable objects. This is so that they can be cached, used as hash keys, used across threads, etc. Some of the RGeo calculations assume that the value of a feature object will never change, so making changes like this could have unexpected and unpredictable consequences.
If you really want a "changed" value, create a new object. For example:
p1 = my_create_a_point()
p2 = p1.factory.point(p1.lon + 20.0, p2.lat)
I have found something that works, although there might be more elegant solutions.
In my Location model I have added theses methods:
after_initialize :init
def init
self.latlon ||= Location.rgeo_factory_for_column(:latlon).point(0, 0)
end
def latitude
self.latlon.lat
end
def latitude=(value)
lon = self.latlon.lon
self.latlon = Location.rgeo_factory_for_column(:latlon).point(lon, value)
end
def longitude
self.latlon.lon
end
def longitude=(value)
lat = self.latlon.lat
self.latlon = Location.rgeo_factory_for_column(:latlon).point(value, lat)
end
I ended up doing something like this in my model:
class MyModel < ActiveRecord::Base
attr_accessor :longitude, :latitude
attr_accessible :longitude, :latitude
validates :longitude, numericality: { greater_than_or_equal_to: -180, less_than_or_equal_to: 180 }, allow_blank: true
validates :latitude, numericality: { greater_than_or_equal_to: -90, less_than_or_equal_to: 90 }, allow_blank: true
before_save :update_gps_location
def update_gps_location
if longitude.present? || latitude.present?
long = longitude || self.gps_location.longitude
lat = latitude || self.gps_location.latitude
self.gps_location = RGeo::Geographic.spherical_factory(srid: 4326).point(long, lat)
end
end
end
Then you can just update the position like so:
my_model.update_attributes(longitude: -122, latitude: 37)
I didn't load up longitude/latitude in an after_initialize block because in my app we never need to read the data, only write it. You could easily add that though.
Credit to this answer for the validations.

Active Record: Get two random objects?

What's the simplest way to get an array with three objects (Card), one of which I already have? The other two should be randomly selected from the database.
My current approach looks like this:
[
#deck.cards[rand(#deck.cards.size)],
#deck.cards[rand(#deck.cards.size)],
#mycard
].sort_by {rand}
The problem I have right now is that sometimes #mycard shows up twice in the array. How can this be avoided?
something like this might work:
class Card < ActiveRecord::Base
belongs_to :deck
named_scope :random, lambda {
{ :offset => Kernel.rand(Card.count) }
}
named_scope :not_in, lambda { |a|
{ :conditions => [ 'id NOT IN (?)', a ] }
}
end
my_cards = []
#mycard = Card.last
my_cards << #mycard
2.times {
my_cards << #deck.cards.not_in(my_cards.collect(&:id)).random
}
Get a card from the deck. Check it's not the samy as #mycard.
Get another card from the deck. Check it's not the same as #mycard or the previous card.
Pretty straightforward, I'd have thought.
You have to remove each card from the deck as you draw from it, unless you are going to reshuffle after each draw, in which case I would just draw again until you get a unique one you haven't already drawn.

Resources