How to solve ActiveStorage::FileNotFoundError: when running tests with rspec? - ruby-on-rails

I'm getting a file not found error using ActiveStorage when running rspec and I don't know what's wrong.
This is how it's setup.
storage.yml
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
config/environments/test.rb
config.active_storage.service = :test
Factory
factory :user do
membership_approval { Rack::Test::UploadedFile.new("#{Register::Engine.root}/spec/fixtures/membership.pdf", 'application/pdf') }
end
Mail example
def mail_example(user:)
filename = user.membership_approval.filename.to_s
attachments[filename] = user.membership_approval.download
mail(
...
)
end
When I run rspec I get the following error message when it tries to download the file.
Error message
ActiveStorage::FileNotFoundError:
# ------------------
# --- Caused by: ---
# Errno::ENOENT:
# No such file or directory # rb_sysopen - /Users/myuser/Projects/my_project/tmp/storage/nd/5h/nd5hlet2n2xa673f8v11ljbnru72
It works fine otherwise but I just can't seem to get it to work with rspec. When I'm debugging in rspec it looks like the file is attached to the object but not stored physically anywhere. Any ideas?
Best regards.
EDIT
I have also tried this approach in the factory but no luck, I still get the same error.
after(:build) do |user|
user.membership_approval.attach(
io: File.open("#{Register::Engine.root}/spec/fixtures/membership.pdf"),
filename: 'membership.pdf',
content_type: 'application/pdf'
)
end

Related

How do I open a CSV file that I uploaded with Carrierwave and Fog to Amazon S3?

I have a model called client_billing_file where I use Carrierwave to upload a CSV file like this:
mount_uploader :billing_file_name, UsageFileUploader
and I schedule a job to run 5 minutes after commiting the creation of a new record:
after_commit :generate_usage_file, on: :create
def generate_usage_file
Resque.enqueue_in(5.minutes, GenerateUsageFileQueue, id, admin.email)
end
This is my background job:
def self.perform(client_billing_file_id, email)
cbf = ClientBillingFile.find(client_billing_file_id)
filepath = cbf.billing_file_name.current_path
csv_file = CSV.read(filepath, headers: true)
.
.
.
end
This is working in my development and testing environments, but it fails when I try to open the CSV file in the staging environment (where it actually uploads the file to the S3 bucket). I checked the bucket and the file is getting uploaded to the specified directory correctly, but for some reason the job is throwing the following error:
Exception Errno::ENOENT
Error No such file or directory # rb_sysopen - my_path/my_file.csv
Versions:
Ruby 2.6.6
Rails 4.2.11
Carrierwave 0.8.0
Fog 1.38.0
I tried Jared Beck's idea and it's working now, basically I added this condition to my BG job:
if Rails.env.production? || Rails.env.staging?
url = cbf.billing_file_name.url
cbf.billing_file_name.download!(url)
end
So the final code looks like this:
def self.perform(client_billing_file_id, email)
cbf = ClientBillingFile.find(client_billing_file_id)
if Rails.env.production? || Rails.env.staging?
url = cbf.billing_file_name.url
cbf.billing_file_name.download!(url)
end
filepath = cbf.billing_file_name.current_path
csv_file = CSV.read(filepath, headers: true)
.
.
.
end

Missing host to link to error in Rails 5.2.1

I've gone through every stackoverflow question regarding this error:
https://duckduckgo.com/?q=rails+Missing+host+to+link+to
All the posts mention the same solution, which is to add the config in the environment file you're working on. In my case, I added to my development.rb:
config.active_storage.service = :local
config.action_mailer.default_url_options = { host: "localhost", port: "3000" }
MyApp::Application.default_url_options = Robson::Application.config.action_mailer.default_url_options
Rails.application.routes.default_url_options = Robson::Application.config.action_mailer.default_url_options
But I still get the infamous error message:
Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
In the following locations if I try to open a file that I uploaded locally:
open(file.service_url)
or if I try to access the files from ActiveAdmin (I called the model "Attachments" and I'm using ActiveStorage)
column(:file) {|a| link_to a.file.filename, a.file.service_url}
I also tried setting "host" in a dictionary as a parameter in the above "link_to" and "open" functions. I also tried "only_path".
Nothing works.
Any help would be appreciated!
P.S.: my active storage config:
local:
service: Disk
root: <%= Rails.root.join("storage") %>
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
amazon:
service: S3
access_key_id: S3_ACCESS_KEY_ID
secret_access_key: S3_SECRET_ACCESS_KEY
bucket: S3_BUCKET
region: S3_REGION
UPDATE
Trying to use rails_representation_url but getting an error undefined method 'variation' for ActiveStorage::Attached
class Attachment < ApplicationRecord
include Rails.application.routes.url_helpers
has_one_attached :file
....
def with_uploaded_file
tempfile = Tempfile.open([file.filename.to_s, File.extname(file.filename.to_s)]) do |file_temp|
file_temp.binmode unless file.content_type =~ /text/
require 'open-uri'
# file_temp.write(open(file.service_url).read)
file_temp.write(open(rails_representation_url(file, only_path: true)).read)
file_temp
end
begin
yield(tempfile)
ensure
tempfile.unlink
end
I upgraded Rails from 5.1 to 5.2 and had same problem.
Solution: https://github.com/rails/rails/issues/32866
So when you have your ActiveRecord variant instead of
variant.service_url
do
rails_representation_url(variant, only_path: true)

Rspec - How can I stub a constant that is defined in config/environment.rb?

I have defined a constant in config/environment.rb called LOCAL_SETTINGS that I use for configuration and use throughout my app. It's a YAML file stored in config/local_settings.yml and will sometimes contain sensitive data like API keys etc.
I'm currently trying to write a spec for a method that uses LOCAL_SETTINGS["slack"]["slack_token"].
The problem is that the constant is not being stubbed for my expectations. ie expect(subject.path).to eq(help_request) fails because it returns a path including the non-stubbed LOCAL_SETTINGS hash.
However if I put a debugger in beneath stub_const and then type LOCAL_SETTINGS, I can see that stub_const has worked.
My questions are:
Is there something I can do in Rspec to get stubs working for constants defined in config/environment.rb?
Should I simply define this constant elsewhere? If so, where? I will need access to it throughout my application in the app/ lib/ and spec/ folders.
My config/environment.rb file:
# Load the Rails application.
require_relative 'application'
LOCAL_SETTINGS = YAML.load_file("#{Rails.root}/config/local_settings.yml")
# Initialize the Rails application.
Rails.application.initialize!
My spec:
describe RequestBuilder, type: :model do
let(:help_params) { {"user_name"=>"some_user_name", "text"=>"release-bot help"} }
let(:help_builder) { RequestBuilder.new(help_params) }
let(:help_request) { "/api/files.upload?file=lib%2Fresponses%2Fhelp&filetype=ruby&channels=stubbed_channel&token=stubbed_token" }
let(:slack_settings) { {"slack"=>{"slack_token"=>"stubbed_token", "slack_channel"=>"stubbed_channel"}} }
context 'Given an incoming request' do
context 'With a correctly formatted request' do
context 'And the "help" command' do
subject { help_builder.build_request_hash }
it 'builds a request containing the help data' do
stub_const("LOCAL_SETTINGS", slack_settings)
expect(subject).to have_key(:request)
expect(subject[:request].path).to eq(help_request)
end
end
end
end
end
If you're using Rails, and you already have a .yaml file in the config directory, I would suggest looking at Rails custom configuration to load the YAML file. This way you'll be able to isolate any credentials from your test environment without having to stub the LOCAL_SETTINGS const for every test or change any of your classes.
# config/local_settings.yml
development:
slack_token: some_dev_token
slack_channel: some_channel
test:
slack_token: fake_token
slack_channel: fake_channel
production:
slack_token: <%= ENV['SLACK_TOKEN'] %>
slack_channel: <%= ENV['SLACK_CHANNEL'] %>
And then to load this configuration file:
# config/application.rb
module MyApp
class Application < Rails::Application
config.local_settings = config_for(:local_settings)
end
end
Then you can access the values from Rails.configuration.local_settings['slack_token'] rather than from the LOCAL_SETTINGS constant.
Another blog post highlighting the custom configuration.

Cant send a business_card.png to abbyy with ROR

I am just tryout some new technologies and find out about abbyy gem I created a free account on http://ocrsdk.com/plans-and-pricing/
I am following the instruction on the gem
class Client < ActiveRecord::Base
def abbyy
client = Abbyy::Client.new
client.process_business_card self.business_card, exportFormat: 'xml', imageSource: 'photo'
# Errno::ENOENT: No such file or directory - https://appname-dev.s3.amazonaws.com/uploads/client/business_card/1/bizcard.jpg
client.get_task_status
client.get
end
end
but I am getting a this error
Errno::ENOENT: No such file or directory - https://appname-dev.s3.amazonaws.com/uploads/client/business_card/1/bizcard.jpg
I made sure that the directory I am uploading is public
here is a link to a demo app https://github.com/mzaragoza/abbyy
Add require 'open-uri' to the top of your file.
Then download the file, and only then give it to abby:
def abby
require 'tempfile'
card = Tempfile.new('business_card')
card.binmode
stream = open(self.business_card.url)
card.write(stream.read)
stream.close
card.close
client = Abbyy::Client.new
client.process_business_card card.path, exportFormat: 'xml', imageSource: 'photo'
client.get_task_status
client.get
ensure
# ensuring every handle is closed, and ignoring exceptions, which could arise if handles already closed
# or haven't been opened
stream.close rescue nil
card.close rescue nil
card.unlink rescue nil
end

Rails ckeditor dragonfly Erorr

Gem => https://github.com/galetahub/ckeditor
Rails = 4.1.4
I've done
rails generate ckeditor:install --orm=active_record --backend=dragonfly
ckeditor_dragonfly.rb
# Load Dragonfly for Rails if it isn't loaded already.
require "dragonfly/rails/images"
# Use a separate Dragonfly "app" for CKEditor.
app = Dragonfly[:ckeditor]
app.configure_with(:rails)
app.configure_with(:imagemagick)
# Define the ckeditor_file_accessor macro.
app.define_macro(ActiveRecord::Base, :ckeditor_file_accessor) if defined?(ActiveRecord::Base)
app.define_macro_on_include(Mongoid::Document, :ckeditor_file_accessor) if defined?(Mongoid::Document)
app.configure do |c|
# Store files in public/uploads/ckeditor. This is not
# mandatory and the files don't even have to be stored under
# public. If not storing under public then set server_root to nil.
c.datastore.root_path = Rails.root.join("public", "uploads", "ckeditor", Rails.env).to_s
c.datastore.server_root = Rails.root.join("public").to_s
# Accept asset requests on /ckeditor_assets. Again, this is not
# mandatory. Just be sure to include :job somewhere.
c.url_format = "/uploads/ckeditor/:job/:basename.:format"
end
# Insert our Dragonfly "app" into the stack.
Rails.application.middleware.insert_after Rack::Cache, Dragonfly::Middleware, :ckeditor
But when I try to do something, an error:
Dragonfly::App[:ckeditor] is deprecated - use Dragonfly.app (for the default app) or Dragonfly.app(:ckeditor) (for extra named apps) instead. See docs at http://markevans.github.io/dragonfly for details
NoMethodError: undefined method `configure_with' for Dragonfly:Module
What ideas are there to solve the problem?
UPD. If correct these errors, it becomes:
Dragonfly::Configurable::UnregisteredPlugin: plugin :rails is not registered
Remove all ckeditor initializers.
Add new file with following content:
require 'dragonfly'
# Logger
Dragonfly.logger = Rails.logger
# Add model functionality
if defined?(ActiveRecord::Base)
ActiveRecord::Base.extend Dragonfly::Model
ActiveRecord::Base.extend Dragonfly::Model::Validations
end
Dragonfly.app(:ckeditor).configure do
# this generate path like this:
# /uploads/ckeditor/AhbB1sHOgZmIjIyMDEyLzExLzIzLzE3XzIxXzAwXzY0OF9zdXJ2ZWlsbGFuY2VfbmNjbi5wbmdbCDoGcDoKdGh1bWJJI.something.png
url_format '/uploads/ckeditor/:job/:basename.:format'
# some image from previous version can break without this
verify_urls false
plugin :imagemagick
# required if you want use paths from previous version
allow_legacy_urls true
# "secure" if images needs this
secret 'ce649ceaaa953967035b113647ba56db19fd263fc2af77737bae09d452ad769d'
datastore :file,
root_path: Rails.root.join('public', 'system','uploads', 'ckeditor', Rails.env),
server_root: Rails.root.join('public')
end
Rails.application.middleware.use Dragonfly::Middleware, :ckeditor
This will store image in (old style):
{Rails.root}/public/system/uploads/ckeditor/{development,production,etc...}
Eraden, your example works. I've replaced the contents of ckeditor_dragonfly.rb with what you've given and then "rake db:migrate" was finally successfull.
But then when I upload an image I get:
NoMethodError (undefined method ckeditor_file_accessor' for #<Class:0x007fb92c720118>):
app/models/ckeditor/asset.rb:5:in'
app/models/ckeditor/asset.rb:1:in <top (required)>'
app/models/ckeditor/picture.rb:1:in'
It seems, you need to replace "ckeditor_file_accessor :data" with "dragonfly_accessor :data" in /app/model/ckeditor/asset.rb. This solved this particular error for me, though I've got another one instead, but it seems to have nothing to do with the topic discussed.
(just for the case, I will write this problem here:
DRAGONFLY: validation of property format of data failed with error Command failed ('identify' '-ping' '-format' '%m %w %h' '/var/folders/x6/pwnc5kls5d17z4j715t45jkr0000gr/T/RackMultipart20150406-2109-1ho7120') with exit status and stderr dyld: Library not loaded: /usr/local/lib/liblzma.5.dylib
Referenced from: /usr/local/bin/identify
Reason: image not found
)

Resources