How can I use rubyzip to attach files in a zip archive to an ActiveStorage object Rails 5 - ruby

I have a model with one attachment that uses ActiveStorage:
class ProofreadDocument < ApplicationRecord
has_one_attached :file
end
I am working on a rake task to attach files to a proofread_document.
The files are compressed into a zip archive.
I understand that I can attach the files as follows from reading the ActiveStorage docs:
#proofread_document.file.attach(io: File.open('/path/to/file'), filename: 'file.pdf')
I have the following in my rake task:
Zip::File.open(args.path_to_directory) do |zipfile|
zipfile.each do |file|
proofread_document = ProofreadDocument.new()
proofread_document.file.attach(io: file.get_input_stream.read, filename: file.name)
proofread_document.save
end
end
This produces the following error:
NoMethodError: undefined method `read' for #<String:0x007f8d894d95e0>
I need to read the contents of each file one at at time to attach them to the proofread_document instance. How can I do this?

I was able to succeed by wrapping the input stream in a StringIO object as follows:
self.file.attach(io: StringIO.new(zip_entry.get_input_stream.read), filename: zip_entry.name, content_type: MS_WORD_CONTENT_TYPE)

Related

Zero Bytes Sent When Sending Axlsx Gem Generated File From Sinatra

Attempting to prompt a download window and stream an XLSX file using Ruby Sinatra and the AXLSX gem, my excel file serializes successfully to local file, so I know its a valid excel doc, but I need it to transfer content to the end user. There haven't been any docs online with examples of AXLS and Sinatra used together, only rails. Help is appreciated!
class Downloads < Sinatra::Base
get '/downloads/report' do
## ...
Axlsx::Package.new do |p|
p.workbook.add_worksheet(name: 'tab name') do |sheet|
## ...
end
content_type 'application/xlsx'
attachment 'cost-code-dashboard.xlsx'
p.to_stream # unsuccessful
# p.to_stream.read # unsuccessful as well
end
end
end
I have also tried the following snippet unsuccessfully
Axlsx::Package.new do |p|
## ...
send_file p.to_stream.read, type: "application/xlsx", filename: "cost-code-dashboard.xlsx"
end
It appears that the issue had everything to do with how Axlsx::Package.new was called, the helper functions were not available inside Axlsx, the following solution worked - online documentation said that the below content_type was better
get '/downloads' do
content_type :'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
p = Axlsx::Package.new
p.workbook.add_worksheet(name: 'Test') do |sheet|
sheet.add_row ['Hello world']
end
p.to_stream
end

How to automatically call classes in Ruby script from a specific directory

I have a directory /lib where I store *.rb files. Each one of them contains a class with a single class method remove_user().
How can I make the main script automatically go over those files and call the same method on all of them? I want to just drop-in files in that directory in the future without modifying the main script in any way.
I do know how to require all the files from a directory based on "Best way to require all files from a directory in ruby?", but I'm not very sure how to invoke the classes "in a loop" from here.
Update
I've tried a the code suggested in "How do I create automatically a instance of every class in a directory?"
files = Dir.glob("lib/*.rb")
def load_modules(class_files)
puts class_files
before = ObjectSpace.each_object(Class).to_a
class_files.each {|file| require_relative file }
after = ObjectSpace.each_object(Class).to_a
(after - before).each {|klass| klass.new.delete_user('myemail#mail.com', 'Audit', 'Test')}
load_modules(files)
end
It produces an error:
/Users/apinchuk/RubymineProjects/autoaudit/init.rb:16:in `new': can't create instance of singleton class (TypeError)
from /Users/RubymineProjects/autoaudit/init.rb:16:in `block in load_modules'
from /Users/RubymineProjects/autoaudit/init.rb:16:in `each'
from /Users/RubymineProjects/autoaudit/init.rb:16:in `load_modules'
from /Users/RubymineProjects/autoaudit/init.rb:20:in `<top (required)>'
from -e:1:in `load'
from -e:1:in `<main>'
And there is nothing I could find about this error.
The create_uat_robot.rb has a structure like this:
class CreateUatRobot
def self.delete_user(email, first_name, last_name)
...
end
end
The name of the file is create_uat_robot.rb
Trying #moveson suggestion as follows:
files = Dir.glob("lib/*.rb")
files.each {|file| require_relative file }
klasses = Dir["lib/*.rb"].map {|file| File.basename(file, '.rb').camelize.constantize}
klasses.each { |klass| klass.delete_user(arguments) }
worked for me.
First you need to create an array of class names:
>> klasses = Dir["lib/*.rb"].map {|file| File.basename(file, '.rb').camelize.constantize }
Then you can call your method on each of them in turn:
>> klasses.each { |klass| klass.remove_user }
If you are not using Rails, you can require ActiveSupport's String extension methods (require 'active_support/core_ext/string'), which will give you the camelize and constantize methods.

Remote_table in spree 0.70.7 controller: #<TypeError: Zip is not a module>

I'm include gem 'remote_table' in my Gemfile (Rails 3.1.12)
In spree admin i'd create new controller:
class Admin::XlsPriceLoadsController < Admin::BaseController
def upload
source_xls = RemoteTable.new(filename)
source_xls.each do |row|
....
end
end
end
but when this action fired, i'm see the next:
TypeError (Zip is not a module):
app/controllers/admin/xls_price_loads_controller.rb:26:in `upload'
...
when i'm explore the source_xls object, a frozen? property of them is true.
So, can any soul write me, why the parsed object is frozed?
And, if i run this code
source_xls = RemoteTable.new(filename)
source_xls.each do |row|
....
end
from lib/tasks as rake task - all work fine!
Thanks for all advice!
Whereabouts does the filename method/attribute come from? Is that something from spree? What value does it have at the point #upload is called?
Regarding
app/controllers/admin/xls_price_loads_controller.rb:26:inupload'`
What's on line 26?

Cannot load data file in Sinatra

I created the following parser:
require "./artist"
require "./song"
require "./genre"
require "debugger"
class Parser
attr_accessor :artists, :genres, :song
attr_reader :mp3
REGEX = /(?<artist>.*)\s\-\s(?<song>.*)\s\[(?<genre>.*)\]/
def initialize(directory="data")
debugger
#mp3 = Dir.entries(directory).select {|f| !File.directory? f}
debugger
end
def parse
#mp3.map do |file|
match = REGEX.match(file)
artist = Artist.find_by_name(match[:artist]) || Artist.new.tap {|artist| artist.name = match[:artist]}
song = Song.new
song.name = match[:song]
song.genre = Genre.find_by_name(match[:genre]) || Genre.new.tap {|genre| genre.name = match[:genre]}
#debugger
artist.add_song(song)
end
end
end
a = Parser.new.parse
I tried running it by calling parser.rb in the directory, lib, where it is located. I get the following error messages:
Parser.rb:47:in `open': No such file or directory - data (Errno::ENOENT)
from parser.rb:47:in `entries'
from parser.rb:47:in `initialize'
from parser.rb:68:in `new'
from parser.rb:68:in `<main>'
This is my file structure:
Can anyone please tell me why it cannot recognize my data directory? I have been staring for a while now and cannot figure it out. It was working like 10 mins ago and I cannot remember what I change to get it all messed up.
Appreciate your feedback! Thanks
You should be able to run your example like ruby -I/lib lib/parser.rb from the directory above lib. The -I will set the "include path", such that the ruby interpreter will find the other required ruby files like (lib/)song.rb.

Uploading a simple CSV file to a controller

I'm trying to upload a CSV file to my controller and access the data within.
Here's the code for the Controller:
class DatabaseImporterController < ApplicationController
def index
end
def import
# Receive the uploaded CSV file and import to the database.
csv_file = params[:csv_file]
logger.debug { csv_file.inspect }
#CSV.foreach("parse.csv") do |row|
#end
render :template => "database_importer/index"
end
end
And the ouput of logger.debug:
{"utf8"=>"✓",
"authenticity_token"=>"Z4+XlmrcH+8JPL6Yq52ymVOMfiGEI9mN8LuoxoBLp8M=",
"csv_file"=>#<ActionDispatch::Http::UploadedFile:0x007feca81b3fb8 #original_filename="Install-Linux-tar.txt", #content_type="text/plain", #headers="Content-Disposition: form-data; name=\"csv_file\"; filename=\"Install-Linux-tar.txt\"\r\nContent-Type: text/plain\r\n", #tempfile=#<File:/tmp/RackMultipart20121229-10294-1ngf634>>,
"commit"=>"Import CSV Car Database",
"controller"=>"database_importer",
"action"=>"import"}
Accoding to the documentation on the official Ruby on Rails page:
The object in the params hash is an instance of a subclass of IO. Depending on the size of the uploaded file it may in fact be a StringIO or an instance of File backed by a temporary file.
As I understand it, the uploaded file is somewhere in my disc (in my Heroku instance) and I can access it temporarily.
How can I access the file? I've tried the following and I get and error message:
csv_file = params[:csv_file][:tempfile] # This is how I try to get the file page of the temporary file.
undefined method `[]' for #<ActionDispatch::Http::UploadedFile:0x007fecb02103c8>
You want to call tempfile, not [:tempfile]:
params[:csv_file].tempfile
You can see all the methods available in the docs for ActionDispatch::Http::UploadedFile.

Resources