Export XML file using Builder Gem in Rails - ruby-on-rails

I have this class. I am using builder gem here.
class ClientsExportXML
def initialize(current_user)
#file = File.new("#{Rails.root}/public/data.xml", 'w')
#clients = Repository::Clients.new(current_user).all
#builder = Builder::XmlMarkup.new(:target=> #file, :indent=> 2)
end
def build
#builder.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8", :company => "Tougg"
#builder.clients do
#clients.each do |c|
#builder.client do
#builder.name(c.name)
#builder.razao_social(c.corporate_name)
#builder.rg(c.rg)
#builder.cpf(c.cpf)
#builder.inscricao_estadual(c.state_registration)
#builder.cnpj(c.cnpj)
#builder.email(c.email)
if c.address
#builder.numero_rua(c.address.street_name)
#builder.bairro(c.address.district)
#builder.complement(c.address.complement)
#builder.cep(c.address.zipcode)
#builder.cidade_estado("#{City.find(c.address.city_id).name}/#{City.find(c.address.city_id).state.symbol}")
end
#builder.telefones do
c.phones.each do |f|
#builder.numero(f.number)
end
end
#builder.notas do
c.notes.each do |n|
#builder.nota do
#builder.nota(n.note)
#builder.adicionada_em(n.created_at.strftime("%d/%m/%y"))
end
end
end
end
end
end
#file.close
end
end
Everything works fine when I execute this in rails console, for example. But, I dont know why, when I try add this in some controller:
def export
....
ClientsExportXML.new(current_user).build
...
end
The file is created at public/data.xml but with no content (empty file).
I am a little confusing about what can may be happening here.
Thanks in advance
Édipo
solved
I change file to an instance variable and close the file at end of method build. I updated the code to reflect this.

Try to add #builder.target! on the last line of the build method. target! method generates XML from the the builder.
Edit:
def export
..
File.open(file_path, "w") do |f|
f.write(ClientsExportXML.new(current_user).build)
end
..
end
Do your export method looks something like this?

Related

ActiveStorage download File / IO from attachment object on S3 service?

After the file is uploaded, I want to analyze and immediately process.
I'm currently attaching then processing each:
current_account.archives.attach(archive_params)
current_account.archives.each do |archive|
Job.enqueue(AccountArchiveImportJob.new(current_account.id, archive.id))
end
In the job i'm opening the CSV and parsing junk
attachment = Account.find(account_id).archives.where(id: archive_id).first
CSV.parse(attachment.download) do |row|
do_stuff_with_the_row(row)
end
I would like to do something like:
CSV.foreach(attachment.open) do |row|
do_stuff_with_the_row(row)
end
I cannot find documentation that allows converting the attachment back into a FILE
At least from Rails 6.0 rc1:
model.attachment_changes['attachment_name'].attachable
will give you IO of the original TmpFile BEFORE it is uploaded.
Rails-6 we will get a download method that will yield a file but you can get this very easily!
Add this downloader.rb file as an initializer
Then given this model
class Business < ApplicationRecord
has_one_attached :csvfile
end
you can do
ActiveStorage::Downloader.new(csvfile).download_blob_to_tempfile do |file|
CSV.foreach(file.path, {headers: true}) do |row|
do_something_with_each_row(row.to_h)
end
end
EDIT: not sure why this took me to so long to find service_url. Way more simple, but has been noted that service_url should not be shown to users
open(csvfile.service_url)
From Rails 5.2 official guide
class VirusScanner
include ActiveStorage::Downloading
attr_reader :blob
def initialize(blob)
#blob = blob
end
def scan
download_blob_to_tempfile do |file|
system 'scan_virus', file.path
end
end
end
So you can do
include ActiveStorage::Downloading
attr_reader :blob
def initialize(blob)
#blob = blob
end
def perform
download_blob_to_tempfile do |file|
CSV.foreach(file.path, {headers: true}) do |row|
do_something_with_each_row(row.to_h)
end
end
end
You can get the file path from the attachment, and then open the file.
path = ActiveStorage::Blob.service.send(:path_for, attachment.key)
File.open(path) do |file|
#...
end

rails generators dynamic output in the generated config file

I am building a gem and I would like to make a generator for it, I have a code that looks like that :
module MagicId
module Generators
class ConfigGenerator < Rails::Generators::Base
source_root(File.expand_path(File.dirname(__FILE__)))
def copy_initializer
copy_file 'config.rb', 'config/initializers/magic_id.rb'
end
end
end
end
What it does is copying a config.rb file to the config/initializers of rails app, is there a way to make the code of config.rb generated dynamically when I run the generator ?
On the guide for generators, there are 5 methods listed that may be of interest to you.
create_file
class InitializerGenerator < Rails::Generators::Base
def create_initializer_file
create_file "config/initializers/initializer.rb", "# Add initialization content here"
end
end
This one is actually from Thor, the second parameter is the contents of the file, or you can give it a block with the return value being used as the contents
create_file "lib/fun_party.rb" do
hostname = ask("What is the virtual hostname I should use?")
"vhost.name = #{hostname}"
end
create_file "config/apache.conf", "your apache config"
inject_into_file
Puts code at a pre-determined position in your file. (guide)
inject_into_file 'name_of_file.rb', after: "#The code goes below this line. Don't forget the Line break at the end\n" do <<-'RUBY'
puts "Hello World"
RUBY
end
gsub_file
Essentially the same as above, except gsubs the position instead of adding a line after it. (guide)
gsub_file 'name_of_file.rb', 'method.to_be_replaced', 'method.the_replacing_code'
append_file/prepend_file
Add to the beginning and add of the file, these both come from the Thor documentation
append_to_file 'config/environments/test.rb', 'config.gem "rspec"'
prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"'
I think you're looking for this
in gem code lib/generators/magic_id_generator.rb
module MagicId
module Generators
class ConfigGenerator < Rails::Generators::Base
source_root(File.expand_path(File.dirname(__FILE__)))
argument :name,
type: :string,
required: true,
banner: 'Argument name'
def copy_config_file
template 'magic_id.rb.erb', 'config/initializers/magic_id.rb'
end
end
end
end
in gem code lib/generators/templates/magid_id.rb.erb
<%= "MagicId.config do |stuff|" %>
<%= " stuff.name = :#{name}" %>
<%= "end" %>
then $ rails g magic_id:config is_magic should produce:
in Rails app code config/initializers/magic_id.rb
MagicId.config do |stuff|
stuff.name = :is_magic
end

Rails no return value from model

I'm trying to read files from in a Model and send the result to a controller. But when I try to display the return value, the only thing I get is the following:
#<Modules:0x007fd559e6cf40>
In my code, I'm just calling Modules.new() and saving the result in a variable:
class WelcomeController < ApplicationController
def index
text = Modules.new "../modules"
puts text
end
end
And my Modules-Model reads a directory, looks for a specific file in each subdirectories and returns the content of that file:
class Modules
include Mongoid::Document
def initialize directory = "./modules"
modules
end
def modules directory = "./modules"
unless Dir[directory].empty?
Dir.glob(directory).each do |folder|
unless Dir["#{directory}/#{folder}"].empty?
Dir.glob(folder).each do |folder|
if File.exist? "module.json"
return open("module.json", "json") do |io| io.read end
end
end
end
end
else
return "Directory empty"
end
end
end
Can someone help me?
Are you trying to look for a module.json file in each of these subdirectories?
Because you will need to pass the full path with the directory. Like:
return File.read("#{folder}/module.json") if File.exist? "#{folder}/module.json"

Iterating through the Rails lib folder to find children

The following code is meant to determine which subclass of itself to execute. It cycles through everything in the ObjectSpace to find subclasses and then execute the correct one from there. In rails, this does not work, because classes in the library folder are not in the ObjectSpace. What is a way to search through a specific folder for subclasses?
def execute
ObjectSpace.each_object(Class).select do |klass|
if (klass < self.class)
klass.designations.each do |designation|
if (designation.downcase.capitalize == #action.downcase.capitalize)
command = klass.new(#sumo_params)
command.execute
end
end
end
end
end
OR -- Is there a superior solution to this problem that you would recommend?
I'd say use a module for this. So, this would make you code something like the following:
lib/base_methods.rb
module BaseMethods
def self.included(base)
##classes_including ||= []
##classes_including << base
end
def do_whatever
##classes_including.each do |class|
#....
end
end
end
lib/class_1.rb
class Class1
include BaseMethods
end
I took the easy way out for now (iterating through an array of string values):
def execute
#descendants.each do |descendant|
klass = descendant.downcase.capitalize.constantize
if (klass < self.class)
klass.designations.each do |designation|
if (designation.downcase.capitalize == #action.downcase.capitalize)
command = klass.new(#sumo_params)
command.execute
end
end
end
end
end

Ruby on Rails: extending ActiveRecord::Errors

In Ruby on Rails, where does one put the code from this snippet in http://gist.github.com/376389? I want to extend ActiveRecord::Errors with the code that's available there so I can merge error messages.
Is this something for ApplicationController? or for lib?
Paste from github.com
# monkey patch gleaned from http://dev.rubyonrails.org/attachment/ticket/11394/merge_bang_errors.patch
module ActiveRecord
class Errors
def merge!(errors, options={})
fields_to_merge = if only=options[:only]
only
elsif except=options[:except]
except = [except] unless except.is_a?(Array)
except.map!(&:to_sym)
errors.entries.map(&:first).select do |field|
!except.include?(field.to_sym)
end
else
errors.entries.map(&:first)
end
fields_to_merge = [fields_to_merge] unless fields_to_merge.is_a?(Array)
fields_to_merge.map!(&:to_sym)
errors.entries.each do |field, msg|
add field, msg if fields_to_merge.include?(field.to_sym)
end
end
end
end
You can just drop that into any ruby file in your initializers directory (create a new one, name it whatever you want). Rails runs all of the files in there every time it is booted, and will extend Errors at that time.
This question is stale but if you want to do something similar in rails 3.x you can drop this in a file in your config/initializers/ directory:
# monkey patch gleaned from http://dev.rubyonrails.org/attachment/ticket/11394/merge_bang_errors.patch
module ActiveModel
class Errors
def merge!(errors, options={})
return if errors.blank? || errors.first.size < 2
fields_to_merge = if only=options[:only]
only
elsif except=options[:except]
except = [except] unless except.is_a?(Array)
except.map!(&:to_sym)
errors.entries.map(&:first).select do |field|
!except.include?(field.to_sym)
end
else
errors.entries.map(&:first)
end
fields_to_merge = [fields_to_merge] unless fields_to_merge.is_a?(Array)
fields_to_merge.map!(&:to_sym)
errors.entries.each do |field, msg|
add field, msg if fields_to_merge.include?(field.to_sym)
end
end
end
end

Resources