rails generators dynamic output in the generated config file - ruby-on-rails

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

Related

Rails 3: How to add a method to the Gem model without changing the Gem source

version : Rails 3.2.22.2
I'm using the Impressionist gem to track user activity:
https://github.com/charlotte-ruby/impressionist
This gives me ActiveModel::MassAssignmentSecurity::Error.
I want to override the Impression module from the gem to use attr_accessible.
I tried 2 things:
1: Create an impression.rb file under app/models
class Impression
def hello
"hello world"
end
end
In initializer/impression.rb,
require 'impression'
when I try the following on console:
a = Impression.first
a.hello
I get method not found error.
2: I added the code in the initializezs/impression.rb file:
Impression.class_eval do
def hi
puts 'you got me!'
end
end
and I still got the same error as in point 1.
How can I effectively override the model from the gem?
I did the following modification to make my code work. Under initializers I create a file -> initializer/my_monkey_patch.rb with following code
class Impression < ActiveRecord::Base
attr_accessible :impressionable_type, :impressionable_id, :controller_name, :action_name, :user_id, :request_hash, :session_hash, :ip_address, :referrer
def hello
"hello WORLD!"
end
end
Thank you #Mohammad Shahadat Hossain for all your help
I would prefer to go with 2 method. But you have see if you method already in a class. Then for overriding it you need to use in your model/impression_decorator.rb.
Impression.class_eval do
attr_accessor :something
def hi
puts 'you got me! #{someting}'
end
end

Export XML file using Builder Gem in 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?

Command to create a rake task with some folder

rails g task folder1/namespace1 task1
Above command will create the task1.rake inside lib/tasks/task1.rake
But, I need to keep my task1.rake inside lib/tasks/folder1/task1.rake
The rails g task generator does not work this way.
Here are the code for the TaskGenerator class
module Rails
module Generators
class TaskGenerator < NamedBase # :nodoc:
argument :actions, type: :array, default: [], banner: "action action"
def create_task_files
template 'task.rb', File.join('lib/tasks', "#{file_name}.rake")
end
end
end
end
As you see the lib/tasks path is hardcoded and you can not pass in options to alter the path.
I think this might be a great addition to the TaskGenerator class.
The answer to your question is that you have to make the folders manually.

RAILS OVERRIDING VIEWS renderization

I want to override how rails creates a view *.html.erb
In ActionView package I already Tried to do it. Doing it
class ERB
class Compiler # :nodoc:
..
class Buffer # :nodoc:
def compile(s)
...
#It stores in a buffer each ruby chunk in the views inside of a Buffer.
end
end
end
...
# Here it is where is called compile method.
# The thing is that if my view is made up of several *.html.erb files such as partials this method will be invoked each time.
#INVOKED PER EACH html.erb file
def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout')
puts ">>> initialize"
#safe_level = safe_level
# ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.update(:default => "%d %b %Y")
compiler = ERB::Compiler.new(trim_mode)
# raise "need a block"
# ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.update(:default => nil)
set_eoutvar(compiler, eoutvar)
#src = compiler.compile(str)
#filename = nil
end
end
end
I would like to find out when the process on the bottom starts. I mean which class, file, etc I have to override to see where Rails starts invoking all html.erb for an specific view.
I think that I should be here:
require 'delegate'
require 'optparse'
require 'fileutils'
require 'tempfile'
require 'erb'
module Rails
module Generator
module Commands
class Create
# Generate a file for a Rails application using an ERuby template.
# Looks up and evaluates a template by name and writes the result.
#
# The ERB template uses explicit trim mode to best control the
# proliferation of whitespace in generated code. <%- trims leading
# whitespace; -%> trims trailing whitespace including one newline.
#
# A hash of template options may be passed as the last argument.
# The options accepted by the file are accepted as well as :assigns,
# a hash of variable bindings. Example:
# template 'foo', 'bar', :assigns => { :action => 'view' }
#
# Template is implemented in terms of file. It calls file with a
# block which takes a file handle and returns its rendered contents.
def template(relative_source, relative_destination, template_options = {})
puts "EEEEEEEEEEEEEEEEEEEEEe"
file(relative_source, relative_destination, template_options) do |file|
# Evaluate any assignments in a temporary, throwaway binding.
vars = template_options[:assigns] || {}
b = template_options[:binding] || binding
vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
# Render the source file with the temporary binding.
ERB.new(file.read, nil, '-').result(b)
end
end
end
end
end
end
But I do not find any trace from the puts method.
All renaming are placed in a file called /lib/*_extensions.erb and in /config/initializers/extensions.rb I have the next:
Dir[File.dirname(__FILE__) + "/../../lib/*_extensions.rb"].each do |fn|
require fn
end
I do not want to reveal why I am doing this.
Thanks
I wanted to use a before_render it does not exist at least in Rails 2.3.4.
So if you want to do it, I did as the next:
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details
# Scrub sensitive parameters from your log
# filter_parameter_logging :password
protected
def render(options = nil, extra_options = {}, &block) #:doc:
puts "BEFORE render"
puts "Setting %H:%M:%S"
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:default => "%H:%M:%S")
# call the ActionController::Base render to show the page
super
puts "AFTER render"
puts "Resetting"
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:default => nil)
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