I just started using paperclip and have a custom processor. I have put the new processor in the correct location RAILS_ROOT/lib/paperclip_processors But, it does not get loaded.
The reason for not loading, is that Rails.root is nil at the time paperclip is loaded. I've tested this by putting explicit code into the paperclip.rb
puts "What is Rails.root? #{Rails.root} #{Rails.root.nil?}"
if defined?(Rails.root) && Rails.root
Dir.glob(File.join(File.expand_path(Rails.root), "lib", "paperclip_processors", "*.rb")).each do |processor|
require processor
end
end
Which will print out What is Rails.root true. And the processors are never load.
Is there a fix or work around for this? The work around right now is to just add a require for our processor. but that doesn't seem right. Here is the work around (our processor does tar'ing), in the model that will be using the processor, just require it at the top:
require "#{Rails.root}/lib/paperclip_processors/tar.rb"
class Ad < ActiveRecord::Base
has_attached_file :adzip,
:styles => { :targzip => {:processors => [:tar], :format => 'tgz'} }
end
I had to include the paperclip_processors directory
module Trunk
class Application < Rails::Application
Paperclip::Railtie.insert
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{Rails.root}/lib)
config.autoload_paths += %W(#{Rails.root}/lib/paperclip_processors)
end
end
But I'm still having a problem where it's not loading the class within cropper.
Expected /Users/jspooner/Dropbox/active/local.active.com/rails3/trunk/lib/paperclip_processors/cropper.rb to define Cropper
cropper.rb
module Paperclip
class Cropper < Thumbnail
def transformation_command
if crop_command
crop_command + super[1..super.length]
else
super
end
end
def crop_command
target = #attachment.instance
if target.cropping?
" -crop '#{target.crop_w.to_i}x#{target.crop_h.to_i}+#{target.crop_x.to_i}+#{target.crop_y.to_i}'"
end
end
end
end
Related
I'd like my gem to add a method to my AR classes that would enable some functionality:
class User < ApplicationRecord
enable_my_uber_features :blah
end
For this, within my_uber_gem/lib/uber_gem.rb, I attempt the following:
# my_uber_gem/lib/uber_gem.rb
require "active_support"
# ...
require "uber/extensions"
# ...
ActiveSupport.on_load(:active_record) do
class ActiveRecord::Base
include Uber::Extensions
end
end
In uber/extensions.rb I have:
# my_uber_gem/lib/uber/extensions.rb
require 'active_support/concern'
module Uber
module Extensions
extend ActiveSupport::Concern
instance_methods do
def foo
p :bar
end
end
class_methods do
def enable_my_uber_features(value)
p value
end
end
end
end
Now I'd expect to see a blah in the console when instantiating an User, however I get an error:
[...] `method_missing': undefined method `enable_my_uber_features' for User:Class (NoMethodError)
I've tried including Uber::Extensions into ApplicationRecord instead of ActiveRecord::Base, no luck.
I've also tried extend Uber::Extensions, no luck.
I've also tried defining a module ClassMethods from within Uber::Extensions the extending/including, still no luck.
I've also followed this guy's guide by the letter: https://waynechu.cc/posts/405-how-to-extend-activerecord, still no luck.
Am I doing something wrong?
The way I've done this is:
# in my_uber_gem/lib/active_record/base/activerecord_base.rb
require 'uber/extensions'
class ActiveRecord::Base
include Uber::Extensions
def baz
p "got baz"
end
end
# and in the my_uber_gem/lib/my_uber_gem.rb
module MyUberGem
require_relative './active_record/base/activerecord_base.rb'
end
# and finally define the extension in my_uber_gem/lib/uber/extensions.rb
require 'active_support/concern'
class Uber
module Extensions
extend ActiveSupport::Concern
def foobar(val)
p val
end
end
end
# now you can do
User.first.foobar("qux") #=> "qux"
User.first.baz #=> "got baz"
Turns out Rails was caching my local gem I was working on, and any changes I made to any gem files were only visible in a new app or after rebuilding the entire machine.
I was able to turn this off so it will reload the gem on every console reload:
# config/environments/development.rb
config.autoload_paths += %W(#{config.root}/../my_uber_gem/lib)
ActiveSupport::Dependencies.explicitly_unloadable_constants << 'MyUberGem'
Having troubles writing RSpec tests for my implementation of "auto_orient" on my CarrierWave uploader. Im also not finding much love on the matter online.
I understand that the manipulate method would be specced by the gem, but Im just wanting to validate that my ImageUploader is implementing it correctly with RSpec.
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
...
version :tile do
process :auto_orient
end
# Fix images being uploaded the wrong orientation
def auto_orient
manipulate! do |img|
img = img.auto_orient
end
end
let(:uploader) { ImageUploader.new(profile, :avatar) }
before do
ImageUploader.enable_processing = true
File.open(File.join(Rails.root, '/spec/support/images/logo.png')) { |f| uploader.store!(f) }
end
after do
ImageUploader.enable_processing = false
uploader.remove!
end
xit 'runs auto_orient on the image' do
# ???
end
Thanks
I'm not 100% sure how to do this in MiniMagick exactly, but here's how I approached the problem using RMagick if that helps. Should be a similar approach I would think.
uploader.cache_stored_file!
# For minimagick, you would probably use MiniMagick::Image::read
mg = ::Magick::Image::read(uploader.file.file).first
# Not sure if minimagick has an "orient" attribute, might try reading the data using something from here instead: https://github.com/probablycorey/mini_magick/blob/master/lib/mini_magick.rb#L204
expect(mg.orientation.to_i).to eq(1)
I have a folder structure that looks like the following:
app/models/
concerns/
quxable.rb
foo/
bar.rb
baz.rb
I'm in Rails 3 so I've autoloaded my concerns with:
config.autoload_paths += Dir[Rails.root.join('app', 'models', "concerns", '**/')]
And the files are as follows:
quxable.rb
module Quxable
extend ActiveSupport::Concern
module ClassMethods
def new_method
end
end
end
bar.rb
class Foo::Bar < ActiveRecord::Base
include Quxable
end
baz.rb
class Foo::Baz < ActiveRecord::Base
include Quxable
end
Now in the console if do this, I get the following outputs:
Foo::Bar.respond_to? :new_method #=> true
Foo::Baz.respond_to? :new_method #=> false
reload!
Foo::Baz.respond_to? :new_method #=> true
Foo::Bar.respond_to? :new_method #=> false
So it would seem to only be properly included on the model that is first accessed. And yet, If I run the following:
ActiveRecord::Base.descendants.select{ |c| c.included_modules.include?(Quxable) }.map(&:name)
I get ["Foo::Bar", "Foo::Baz"].
Any idea what's going on here? I'm guessing something with autoloading/eagerloading, but I'm not sure why both models aren't getting the new class method.
PS - I've tried rewriting the module without ActiveSupport::Concern (just because I'm on an old Rails version and I'm taking shots in the dark) using:
def include(base)
base.send :extend, ClassMethods
end
but I still have the same problem.
EDIT
I initially left this out (just trying to present the simplest problem), so I apologize to those trying to help earlier. But quxable.rb actually looks like this:
module Quxable
extend ActiveSupport::Concern
LOOKUP = {
Foo::Bar => "something",
Foo::Baz => "something else"
}
module ClassMethods
def new_method
end
end
end
So I'm guessing I created some kind of circular dependency defining a constant with the Class objects. Can anyone confirm? Weird that it just fails silently by not defining the class methods on the class that's accessed second though. I don't know why that is?
Based on your edit, this code is problematic:
LOOKUP = {
Foo::Bar => "something",
Foo::Baz => "something else"
}
It will instantiate Foo::Bar before the module is complete. Thus new_method will be omitted. Tradition in this case is to use strings in the lookup and .constantize them to turn them into classes when needed.
LOOKUP = {
"Foo::Bar" => "something",
"Foo::Baz" => "something else"
}
then
LOOKUP.keys.first.constantize.new_method
or
result = LOOKUP[Foo::Bar.name]
to use it.
I think you have a typo, and concerns include some magic that lets you transcend the limitations on mix-ins.
Also, if you're working in a directory under something that's already autoloaded, like 'models', just namespace everything to that directory name.
Try this:
module Concerns
module Quxable
extend ActiveSupport::Concern
included do
def self.new_method
end
end
end
end
module Foo
class Baz < ActiveRecord::Base
include Concerns::Quxable
end
end
As far as I remember you shouldn't need the extra autoload directive, as using the namespace in a directory under models will just work.
Edit after comments:
I've set up a Rails project with the following added files:
app/models/foo/doer.rb
app/models/foo/thinker.rb
app/models/concerns/thingable.rb
thingable.rb is:
module Concerns
module Thingable
extend ActiveSupport::Concern
included do
def self.thing
end
end
end
end
doer.rb is:
module Foo
class Doer < ActiveRecord::Base
include Concerns::Thingable
end
end
thinker.rb is:
module Foo
class Thinker < ActiveRecord::Base
include Concerns::Thingable
end
end
In a console:
Loading development environment (Rails 3.2.22)
2.1.3 :001 > Foo::Doer.respond_to? :thing
=> true
2.1.3 :002 > Foo::Thinker.respond_to? :thing
=> true
2.1.3 :003 > reload!
Reloading...
=> true
2.1.3 :004 > Foo::Doer.respond_to? :thing
=> true
2.1.3 :005 > Foo::Thinker.respond_to? :thing
=> true
2.1.3 :006 >
I did not change the autoloading at all, I relied on Rails to find files based on namespacing. (Use a namespace for directories under known directories like 'models')
I would reset your autoloading to default, then use Rails conventions for file locations and namespacing. If that doesn't work, there may be other things your project is doing that I don't know about.
Let me know if you can provide any more details.
I have a lot of files with has_attached_file method from paperclip but without :default_url; now I want to use the same image for all of them but I don't want to go from file to file and add this :default_url line for each file. There are some solution to set this in one place and will work for all the has_attached_file method calls?
I tried to do it in this way, but seems like it does not have any effect
module Paperclip
module ClassMethods
def has_attached_file(name, options = {})
options[:default_url] => Rails.root + "/missing.png"
HasAttachedFile.define_on(self, name, options)
end
end
end
You are about to make it work – just change options[:default_url] => Rails.root + "/missing.png" to options[:default_url] = Rails.root + "/missing.png" (= instead of =>).
However, I suggest a better solution:
module Paperclip
module ClassMethods
def has_attached_file_with_preconfigured_default_url(name, options = {})
options.reverse_merge! default_url: Rails.root + "/missing.png"
has_attached_file_without_preconfigured_default_url(name, options)
end
alias_method_chain :has_attached_file, :preconfigured_default_url
end
end
# Now you could use both new has_attached_file and old has_attached_file_without_preconfigured_default_url.
And place this code into initializer.
I've followed the instructions on how to define a test-specific store directory for carrierwave uploads, which suggests opening the CarrierWave::Uploader::Base class and redefining store_dir and cache_dir like so:
if defined?(CarrierWave)
CarrierWave::Uploader::Base.descendants.each do |klass|
next if klass.anonymous?
klass.class_eval do
def cache_dir
"#{Rails.root}/spec/support/uploads/tmp"
end
def store_dir
"#{Rails.root}/spec/support/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
end
end
This works like a charm, except when I run rspec with spork, then it fails to modify the class and uploads are stored in the default location. Does anyone have any clue why this might be happening?
Someone else working on our project solved this problem by adding a line with just AvatarUploader ahead of the CarrierWave::Uploader::Base.descendants.each line, like this:
if defined?(CarrierWave)
AvatarUploader # load AvatarUploader class
CarrierWave::Uploader::Base.descendants.each do |klass|
#...
Not entirely sure why this works, but it does.
Little addition to accepted answer, for anyone coming here:
if uploader classes are not loaded before the call to
CarrierWave::Uploader::Base.descendants, it will return empty array, so either specify each uploader like in accepted answer or you could do something like this to require all uploaders from let's say rails uploaders folder
Dir["#{Rails.root}/app/uploaders/*.rb"].each {|file| require file}
CarrierWave::Uploader::Base.descendants.each do |klass|
#...