Is there a (simple) way to configure the default generator to also generate a js.erb file for each action in addition to html.erb files?
You can override the scaffold generator lib/rails/generators/erb/scaffold/scaffold_generator.rb file.
Step 1:
Copy latest scaffold_generator.rb file.
mkdir -p lib/rails/generators/erb/scaffold && cp $(bundle show railties)/lib/rails/generators/erb/scaffold/scaffold_generator.rb lib/rails/generators/erb/scaffold/
Step 2:
Add custom code to generate .js.erb files you want.
# frozen_string_literal: true
require "rails/generators/erb"
require "rails/generators/resource_helpers"
module Erb # :nodoc:
module Generators # :nodoc:
class ScaffoldGenerator < Base # :nodoc:
include Rails::Generators::ResourceHelpers
argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_root_folder
empty_directory File.join("app/views", controller_file_path)
end
def copy_view_files
available_views.each do |view|
formats.each do |format|
filename = filename_with_extensions(view, format)
template filename, File.join("app/views", controller_file_path, filename)
end
end
javascript_views.each do |view|
path = File.join('app', 'views', controller_file_path, "#{view}.js.erb")
File.open(path, "w")
end
end
private
def available_views
%w(index edit show new _form)
end
def javascript_views
%w(index show create update)
end
end
end
end
Now when you run your scaffold generator you'll see the new .js.erb files that are created.
The answer from #dale-zak it created an empty js file without the contents from my lib/templates/erb/scaffold/index.js.erb.tt
That code with this loop works for me:
javascript_views.each do |view|
filename = filename_with_extensions(view, :js)
template filename, File.join("app/views", controller_file_path, filename)
end
Related
I'm making an export to csv file functionality in a Ruby on Rails repo and I'm almost done. However, when I press the "Export all" button, I get the undefined method `export' for nil:NilClass error. The log shows that format.csv { send_data #foos.export, filename: "foos-#{Date.today}.csv" } went wrong. What am I missing please?
This is model
class Foo < ApplicationRecord
has_many :bars
def export
[id, name, foos.map(&:name).join(' ')]
end
end
This is part of controller
def index
#foos = Foo.all
end
def export
all = Foo.all
attributes = %w{name}
CSV.generate(headers: true) do |csv|
csv << attributes
all.each do |foo|
csv << attributes.map{ |attr| foo.send(attr) }
end
respond_to do |format|
format.csv { send_data #foos.export, filename: "foos-#{Date.today}.csv" }
end
end
end
def name
"#{foo_id} #{name}"
end
This is View
<button class="btn btn-success">export all</button>
This is Routes
Rails.application.routes.draw do
resources :foos
get :export, controller: :foos
root "foos#index"
end
This is Rake (lib/tasks/export.rb)
namespace :export do
task foo: :environment do
file_name = 'exported_foo.csv'
csv_data = Foo.to_csv
File.write(file_name, csv_data)
end
end
Start by creating a service object that takes a collection of records and returns CSV so that you can test the CSV generation in isolation:
# app/services/foo_export_service.rb
# Just a Plain Old Ruby Object that converts a collection of foos into CSV
class FooExportService
# The initializer gives us a good place to setup our service
# #param [Enumerable] foo - an array or collection of records
def initialize(foos)
#headers = %w{name} # the attributes you want to use
#foos = foos
end
# performs the actual work
# #return [String]
def perform
CSV.generate do |csv|
#foos.each do |foo|
csv << foo.serializable_hash.slice(#headers).values
end
end
end
# A convenient factory method which makes stubbing the
# service easier
# #param [Enumerable] foos - an array or collection of records
# #return [String]
def self.perform(foos)
new(foos).perform
end
end
# example usage
FooExportService.perform(Foo.all)
Not everything in a Rails application needs to be jammed into a model, view or controller. They already have enough responsiblities. This also lets you resuse the code for example in your rake task if you actually need it.
This simply iterates over the collection and uses Rails built in serialization features to turn the model instances into hashes that can be serialized as CSV. It also uses the fact that Hash#slice also reorders the hash keys.
In your controller you then just use the service object:
class FoosController
def export
#foos = Foo.all
respond_to do |format|
format.csv do
send_data FooExportService.perform(#foos),
filename: "foos-#{Date.today}.csv"
end
end
end
end
You don't even really need a separate export action in the first place. Just use MimeResponds to add CSV as an availble response format to the index:
class FoosController
def index
# GET /foos
# GET /foos.csv
#foos = Foo.all
respond_to do |format|
format.html
format.csv do
send_data FooExportService.perform(#foos),
filename: "foos-#{Date.today}.csv"
end
end
end
end
<%= link_to("Export as CSV", foos_path(format: :csv)) %>
I am trying to open a file on rails user model with ruby and call it from user controller but it kept throwing me back wrong number of arguments (given 0, expected 1..3).
This is my file directory 'app' ,'assets', 'files', 'test_list.txt'
'app' ,'controllers', 'users controller'
can you help?thanks
class User < ApplicationRecord
def self.my_method
my_array = []
file = File.join(Rails.root, 'app' 'models','assets', 'files', 'test_list.txt')
File.open.each do |line|
my_array << line.gsub!(/\n?/, "")
end
return my_array.to_s
end
end
class UsersController < ApplicationController
require 'open-uri'
require 'net/http'
def show
# uri = URI('https://gist.githubusercontent.com/Kalagan/3b26be21cbf65b62cf05ab549433314e/raw')
# data = Net::HTTP.get(uri)
# anagrams = data.split(/\n/)
#vari = User.my_method
#query = params[:query]
#results = anagrams.select { |word| #query.split('').sort.join == word.split('').sort.join }
end
end
You're passing nothing to the open method. Pass the filename
Change
File.open
to
File.open(file)
open method needs to know at least the filename it has to open
I think you missed a comma.You can write the below code.
file = File.join(Rails.root, 'app', 'models','assets', 'files', 'test_list.txt')
and for reading the content
File.read(file) do |file|
file.each do |line|
p line
end
end
I used to generate PDFs for my users by using the wicked_pdf gem and writing
something like the example code below:
class ThingsController < ApplicationController
def show
respond_to do |format|
format.html
format.pdf do
render pdf: "file_name" # Excluding ".pdf" extension.
end
end
end
end
I would then have a link on a page like <%= link_to report_pdf_path(#report), "Download PDF", target: "_blank" %> that would cause the PDF to show up in the user's browser as a new tab. This is the preferred behavior I would like, but it halts all requests until the PDF is completed for the user and some of these take quite some time to generate.
So I've since offloaded the generating of the PDF to ActiveJob which works nice, but I can't figure out how to have ActiveJob open the file in a new window yet. Currently, I have it writing to the server and then updating a partial that shows the files the user has requested. Below is an example of it.
class GeneratePdfJob < ApplicationJob
queue_as :default
def perform(*args)
params = args.first
generate_pdf_document(params)
end
def generate_pdf_document(params)
html = ApplicationController.new.render_to_string(
template: 'players/board_labels.pdf.erb',
locals: { player_ids: params[:player_ids] }
)
save_to_pdf(html, params[:pdf_title], params[:user_code])
end
def save_to_pdf(html, pdf_title, user_code)
pdf = WickedPdf.new.pdf_from_string(
html,
pdf: "#{pdf_title}",
layout: 'print',
encoding: 'utf-8'
)
pdf_name = "#{pdf_title}.pdf"
pdf_dir = Rails.root.join('public','uploads','reports',"#{user_code}")
pdf_path = Rails.root.join(pdf_dir,pdf_name)
# create the folder if it doesn't exist
FileUtils.mkdir_p(pdf_dir) unless File.directory?(pdf_dir)
# create a new file
File.open(pdf_path,'wb') do |file|
file.binmode
file << pdf.force_encoding("UTF-8")
end
end
end
So how can I make a method that would replace the save_to_pdf method and instead open the file in a new tab for the user? If this isn't possible could I open a tab when I initiate the ActiveJob that is a placeholder tab and then incorporate ActionCable to display the file some how after it's been generated for the user?
I tried to follow the 'Secure Upload' in carrier wave which is a bit confusing because I have customized the file path and all a bit. When I try to run the app, I get 'Cannot read file' error.
Here's the route :
match "/upload_files/:tenant_id/:model/:mount_as/:id/:basename.:extension" => "documents#download",via: [:get, :post]
class ImageUploader < CarrierWave::Uploader::Base
def store_dir
"upload_files/#{model.tenant_id}/#model.class.to_s.underscore}/#mounted_as}/#{model.id}"
end
end
carrierwave.rb initializer :
CarrierWave.configure do |config|
config.permissions = 0600
config.directory_permissions = 0700
config.root = Rails.root
end
documents controller:`
def download
path = request.fullpath
send_file path
end
got the error
ActionController::MissingFile in DocumentsController#download
Cannot read file /upload_files/1/hoshin_attachment/image/3/support3_HoshinUserStatusReports_08_14_2015.pdf
Please help me to find the solution
CarrierWave seems to compute paths using a root variable and the uploader store_dir.
I wanted to store my files in a private folder under Rails.root.
To setup the root:
# config/initializers/carrierwave.rb
# Uploader settings
CarrierWave.root = Rails.root.to_s
CarrierWave::Uploader::Base.root = Rails.root.to_s
To setup the store_dir:
# In your uploader class definition
def store_dir
"private/your_app/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
Since files are not longer in the public folder, they require actions in controller to show and download. Lets assume the column for the uploaded file is named uploaded_file:
# In some controller
# you have to ensure #document initialize with a before_action filter
def show_file
send_file #document.unsigned_file.path,
filename: #document.uploaded_file_identifier,
disposition: :inline,
type: #document.uploaded_file.content_type
end
def download_file
send_file #document.unsigned_file.path,
filename: #document.uploaded_file_identifier,
disposition: :attachment,
type: #document.uploaded_file.content_type
end
I've read over the documentation on generators, but can't find any information about creating views. Reading through the code in the Rails 3 Generators gem, I found that you can override the default templates by placing new ones in lib/generators/erb/scaffold/templates/. You can also specify which views you want to create in the scaffold_generator.rb file with a snippet like:
def available_views
['index', 'edit', 'show', 'new', '_form']
end
So my question is, what if I wanted to create both an index.html.erb file and an index.js.erb file?
Apparently, the actual creation of the views is done by a function called copy_view_files. You can specify what type of view you want within that function. After doing so, my scaffold_generator.rb looks like this:
require 'rails/generators/erb/scaffold/scaffold_generator'
module Erb
module Generators
class ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
source_root File.expand_path(File.join('..', 'templates'), __FILE__)
def copy_view_files
primary_views.each do |view|
filename = filename_with_extensions view
template "#{view}.html.erb", File.join('app', 'views', controller_file_path, filename)
template "#{view}.js.erb", File.join('app', 'views', controller_file_path, filename)
end
end
hook_for :form_builder, :as => :scaffold
protected
def primary_views
['index', 'edit', 'show', 'new']
end
def handler
:erb
end
end
end
end
Note that within the copy_view_files function, there are two calls to template. The first creates a set of html views, while the second creates the js views I wanted.
Similar answer I posted to the question https://stackoverflow.com/a/62441675/385730.
You can override the scaffold generator lib/rails/generators/erb/scaffold/scaffold_generator.rb file.
Step 1:
Copy latest scaffold_generator.rb file.
mkdir -p lib/rails/generators/erb/scaffold && cp $(bundle show railties)/lib/rails/generators/erb/scaffold/scaffold_generator.rb lib/rails/generators/erb/scaffold/
Step 2:
Add custom code to generate .js.erb files you want.
# frozen_string_literal: true
require "rails/generators/erb"
require "rails/generators/resource_helpers"
module Erb # :nodoc:
module Generators # :nodoc:
class ScaffoldGenerator < Base # :nodoc:
include Rails::Generators::ResourceHelpers
argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_root_folder
empty_directory File.join("app/views", controller_file_path)
end
def copy_view_files
available_views.each do |view|
formats.each do |format|
filename = filename_with_extensions(view, format)
template filename, File.join("app/views", controller_file_path, filename)
end
end
javascript_views.each do |view|
path = File.join('app', 'views', controller_file_path, "#{view}.js.erb")
File.open(path, "w")
end
end
private
def available_views
%w(index edit show new _form)
end
def javascript_views
%w(index show create update)
end
end
end
end
Now when you run your scaffold generator you'll see the new .js.erb files that are created.