carrierwave be_identical_to helper not working in rspec - ruby

I am having some issues with an rspec test I am trying to run on Carrierwave uploads. Basically, I am trying to test processing to make sure if images are uploaded and processed. I have created a post-processed example file that should be identical to the post-uploaded-and-processed test file. However, I am getting the following warning:
ImageUploader the greyscale version should remove color from the image and make it greyscale
Failure/Error: #uploader.should be_identical_to(#pregreyed_image)
TypeError:
can't convert ImageUploader into String
# ./spec/uploaders/image_uploader_spec.rb:24:in `block (3 levels) in <top (required)>'
Here is my test file:
image_uploader_spec.rb
require File.dirname(__FILE__) + '/../spec_helper'
require 'carrierwave/test/matchers'
describe ImageUploader do
include CarrierWave::Test::Matchers
include ActionDispatch::TestProcess
before do
ImageUploader.enable_processing = true
#uploader_attr = fixture_file_upload('/test_images/testimage.jpg', 'image/jpeg')
#uploader = ImageUploader.new(#uploader_attr)
#uploader.store!
#pregreyed_image = fixture_file_upload('/test_images/testimage_GREY.jpg', 'image/jpeg')
end
after do
#uploader.remove!
ImageUploader.enable_processing = false
end
context 'the greyscale version' do
it "should remove color from the image and make it greyscale" do
#uploader.should be_identical_to(#pregreyed_image)
end
end
end
image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility:
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
# Choose what kind of storage to use for this uploader:
storage :file
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# Process files as they are uploaded:
process :convert_to_grayscale
def convert_to_grayscale
manipulate! do |img|
img.colorspace = Magick::GRAYColorspace
img.quantize(256, Magick::GRAYColorspace)
img = yield(img) if block_given?
img
end
end

Underneath the covers be_identical_to uses FileUtils.identical? on the two arguments. So your expectation:
#uploader.should be_identical_to(#pregreyed_image)
is actually calling:
FileUtils.identcal?(#uploader, #pregreyed_image)
Since in my test environment I'm using a file storage system, I got around this by passing in the #current_path rather than the uploader itself like this:
#uploader.current_path.should be_identical_to(#pregreyed_image)
I actually ended up needing to compare uploaders directly and implemented == on my uploader:
class MyUploader < CarrierWave::Uploader::Base
...
def ==(other)
return true if !present? && !other.present?
FileUtils.identical?(current_path, other.current_path)
end
end

Related

Using an existing image on s3 with Carrierwave?

I'm trying to use existing images on my s3 and relate them to a carrierwave object. These images are stored in a different location from the store_dir of my uploader.
I currently have my migration setup using remote_image_url but it is extremely slow and I don't think re-uploading them is necessary.
So, is it possible to point my uploader to an existing image that uses a folder structure different from my store_dir?
Here's an example of what I have now.
example block from migration:
ActiveRecord::Base.connection.execute(update_images)
Models::Product.where('image IS NOT NULL').select(:id, :image).find_in_batches(batch_size: 500).with_index do |group, batch|
puts "Processing group ##{batch}"
group.each do |prod|
next unless prod[:image].present?
image = Models::ProductImage.new(product_id: prod[:id])
image.remote_image_url = prod[:image]
image.save
end
sleep(7)
end
uploader:
class ImageUploader < CarrierWave::Uploader::Base
storage :fog
def filename
"#{secure_token}.#{file.extension}" if original_filename.present?
end
def store_dir
"#{ENV['RACK_ENV']}/#{sub_folder}"
end
def cache_dir
"#{ENV['RACK_ENV']}/#{sub_folder}"
end
def extension_allowlist
%w(jpg jpeg gif png)
end
def size_range
1.byte..10.megabytes
end
def sub_folder
case model
when Models::UserImage
'user_images'
when Models::ProductImage
'product_images'
else
'misplaced'
end
end
protected
def secure_token
var = :"##{mounted_as}_secure_token"
model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
end
end

How to fix a "value too long for type character varying(255)" error

I'm trying to save a file so that I can upload it to stripe using CarrierWave, but I'm getting the error:
ERROR: value too long for type character varying(255)
and don't understand why as I followed the CarrierWave usage guide on GitHub.
This is my application:
class SplitterStripeServer < Sinatra::Base
CarrierWave.configure do |config|
config.root = File.dirname(__FILE__) + "/public"
end
post "/" do
img = Image.new
img.file = params[:file] #carrierwave will upload the file automatically
img.save!
redirect to("/")
end
get "/upload" do
erb :upload
end
get "/" do
#image = Image.find(1)
erb :index
end
end
This is the uploader:
class ImagesUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
end
This is the model:
class Image
include DataMapper::Resource
property :id, Serial
mount_uploader :file, ImagesUploader
end
I feel like I'm missing something simple.
You need to decrease a length of a file name. Override filename method and cut a file's basename e.g. to 250 characters.
class ImagesUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def filename
"#{file.basename[0..250]}.#{file.extension}" if original_filename.present?
end
end
check if you have additional installed gems
I had was installed gem validates_lengths_from_database and this gem doesn't understand how to work with carrierwave. I am just turn off it in model validates_lengths_from_database except: %i[file]

Duplicating carrierwave image

I have a model called Book which has a cover_image.
mount_uploader :cover_image, BookPixUploader
And the uploader is declared as follows:
class BookPixUploader < CarrierWave::Uploader::Base
# Include RMagick or ImageScience support:
include CarrierWave::RMagick
CarrierWave::Uploader::ExtensionWhitelist
# include CarrierWave::MiniMagick
# include CarrierWave::ImageScience
# Choose what kind of storage to use for this uploader:
#storage :file
storage :fog
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"system/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
....
I am trying to save a new book:
newbook = book.dup
newbook.save
But It's not uploading a new image to amazon S3.
I have tried:
newbook = book.dup
newbook.cover_image = book.cover_image
newbook.save
Any ideas?
newbook = book.dup
newbook.remote_cover_image_url = book.cover_image.url
newbook.save
If this doesn't work, you may want to look into duplicating the attributes another way, since book.dup could do weird things to the uploader.

CarrierWave: Use of file name in store_dir

I'm saving images to S3 using CarrierWave. Before uploading the images are renamed to a random string. Now for faster lookups I wanted to put the files in directories named after the two first letters of the filenames. But doing so, Rails aborts with stack level too deep.
So I assume calling #{model.image[0, 2] leads to an infinite recursion. Is there a way to access just the file name as a string? Or an even simpler solution?
This is the uploader:
class ImageUploader < CarrierWave::Uploader::Base
storage :fog
def store_dir
"images/#{model.image[0, 2]}/"
end
def filename
"#{secure_token}.#{file.extension}" if original_filename.present?
end
protected
def secure_token
var = :"##{mounted_as}_secure_token"
model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.hex(8))
end
end
try the :file_identifier method
class ImageUploader < CarrierWave::Uploader::Base
...
def store_dir
"images/#{model.file_identifier[0, 2]}/#{model.id}"
end
...
end
The easy answer is to add a proxy attribute such as filename in the Image model and access that using read_attribute:
class Image < ApplicationRecord
mount_uploader :image, ImageUploader
def filename
read_attribute(:image)
end
...
end
Then in your ImageUploader#store_dir method make reference to the proxy attribute filename:
def store_dir
"images/#{model.filename}"
end
This worked for me and eliminated the recursive error problem.

Access module Configuration constant from another class

I am trying to understand how the following code is able to do this:
attr_accessor *Configuration::VALID_CONFIG_KEYS
Without requiring the Configuration file. Here is part of the code:
require 'openamplify/analysis/context'
require 'openamplify/connection'
require 'openamplify/request'
module OpenAmplify
# Provides access to the OpenAmplify API http://portaltnx20.openamplify.com/AmplifyWeb_v20/
#
# Basic usage of the library is to call supported methods via the Client class.
#
# text = "After getting the MX1000 laser mouse and the Z-5500 speakers i fell in love with logitech"
# OpenAmplify::Client.new.amplify(text)
class Client
include OpenAmplify::Connection
include OpenAmplify::Request
attr_accessor *Configuration::VALID_CONFIG_KEYS
def initialize(options={})
merged_options = OpenAmplify.options.merge(options)
Configuration::VALID_CONFIG_KEYS.each do |key|
send("#{key}=", merged_options[key])
end
end
....
end
And this is the Configuration module:
require 'openamplify/version'
# TODO: output_format, analysis, scoring can be specied in the client and becomes the default unless overriden
module OpenAmplify
# Defines constants and methods for configuring a client
module Configuration
VALID_CONNECTION_KEYS = [:endpoint, :user_agent, :method, :adapter].freeze
VALID_OPTIONS_KEYS = [:api_key, :analysis, :output_format, :scoring].freeze
VALID_CONFIG_KEYS = VALID_CONNECTION_KEYS + VALID_OPTIONS_KEYS
DEFAULT_ENDPOINT = 'http://portaltnx20.openamplify.com/AmplifyWeb_v21/AmplifyThis'
DEFAULT_HTTP_METHOD = :get
DEFAULT_HTTP_ADAPTER = :net_http
DEFAULT_USER_AGENT = "OpenAmplify Ruby Gem #{OpenAmplify::VERSION}".freeze
DEFAULT_API_KEY = nil
DEFAULT_ANALYSIS = :all
DEFAULT_OUTPUT_FORMAT = :xml
DEFAULT_SCORING = :standard
DEFAULT_SOURCE_URL = nil
DEFAULT_INPUT_TEXT = nil
attr_accessor *VALID_CONFIG_KEYS
....
end
This is from this repository: OpenAmplify
First of all, in both configuration.rb and client.rb, they're using the same naming space, which is module OpenAmplify.
Even though configuration.rb is not required in client.rb, the convention of Ruby project usually requires all necessary files in one file (normally the same name as the name space, and placed in {ProjectName}/lib/, in this case the file is openamplify/lib/openamplify.rb).
So if you go to openamplify/lib/openamplify.rb, you'll notice it actually requires all those two files:
require 'openamplify/configuration'
require 'openamplify/client'
And since constants are already defined in configuration.rb:
module OpenAmplify
module Configuration
VALID_CONFIG_KEYS = ...
end
end
Then obviously constant VALID_CONFIG_KEYS is visible in the same module (re-opened by client.rb) by Configuration::VALID_CONFIG_KEYS (and the * in front just means exploding array, because VALID_CONFIG_KEYS is an array of symbols)
module OpenAmplify
class Client
attr_accessor *Configuration::VALID_CONFIG_KEYS
end
end

Resources