rails3 monkey-patch generator - ruby-on-rails-3

I want to monkey-patch the rspec-rails generator to generates a _form.html_spec.rb template. I know how do that, but not where I should put this patch!
What I've done:
# rspec_generator_patch.rb
module Rspec
module Generators
class ScaffoldGenerator < Base
def generate_views_specs_with_form *args, &block
generate_views_specs_without_form
copy_view :_form
end
alias_method_chain :generate_views_specs, :form
end
end
end
Where I put this file? Thank you.
EDIT:
Ok, I think problem is almost solved. Instead of monkey-patch, I've inherited the specific generators and edited the method. There's the solution:
# lib/generators/rspec_modded/scaffold/scaffold_generator.rb
require 'generators/rspec/scaffold/scaffold_generator.rb'
module RspecModded
module Generators
class ScaffoldGenerator < Rspec::Generators::ScaffoldGenerator
def generate_view_specs
super
copy_view :_form
end
end
end
end
If I do rails g rspec_modded:scaffold is in list and actually work if called manually (rails g rspec_modded:scaffold test).
# config/application.rb
# ...
config.generators do |g|
g.test_framework :rspec_modded, :fixture => false, fixture_replacement: nil
g.fallbacks[:rspec_modded] = :rspec
end
For what I know, every hook_for :test_framework should call rspec_modded generator and rspec should manage the rest (fallbacks). But it doesn't work: for some reason the unit_test generator kick in! What the matter? I really don't understand...

You can put your monkey patch anywhere, as long as it gets loaded. With rails, you'd usually put it in the /lib folder.

Related

How can I create a concern in a Rails engine?

I'm trying to create a concern inside an engine in order to add/override this functions in the main application which is going to mount this engine. The problem is that I have had problems including the concern in the engine module. It seems that Rails can't find it.
This is my post.rb file in app/models/blorgh/post.rb:
module Blorgh
class Post < ActiveRecord::Base
include Blorgh::Concerns::Models::Post
end
end
And this is my post.rb concern in lib/concerns/models/post.rb:
require 'active_support/concern'
module Concerns::Models::Post
extend ActiveSupport::Concern
# 'included do' causes the included code to be evaluated in the
# conext where it is included (post.rb), rather than be
# executed in the module's context (blorgh/concerns/models/post).
included do
attr_accessible :author_name, :title, :text
attr_accessor :author_name
belongs_to :author, class_name: Blorgh.user_class
has_many :comments
before_save :set_author
private
def set_author
self.author = User.find_or_create_by_name(author_name)
end
end
def summary
"#{title}"
end
module ClassMethods
def some_class_method
'some class method string'
end
end
end
When I run the test/dummy, I got the this error: uninitialized constant Blorgh::Concerns
This is my blorgh.gemspec:
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "blorgh/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "blorgh"
s.version = Blorgh::VERSION
s.authors = ["***"]
s.email = ["***"]
s.homepage = "***"
s.summary = "Engine test."
s.description = "Description of Blorgh."
s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.rdoc"]
s.test_files = Dir["test/**/*"]
s.add_dependency "rails", "~> 3.2.8"
s.add_dependency "jquery-rails"
s.add_development_dependency "sqlite3"
end
Can someone help me with this?
When using engines, you need to keep track of the load order, especially when changing behavior of the main app. Suppose your engine is called "engine_name", you should have this file: engine_name/lib/engine_name/engine.rb. This is one place to include your Concerns.
Bundler.require
module EngineName
class Engine < ::Rails::Engine
require 'engine_name/path/to/concerns/models/post'
initializer 'engine_name.include_concerns' do
ActionDispatch::Reloader.to_prepare do
Blorgh::Post.send(:include, Concerns::Models::Post)
end
end
# Autoload from lib directory
config.autoload_paths << File.expand_path('../../', __FILE__)
isolate_namespace EngineName
end
end
This way you make sure everything is loaded alright, but be very careful using Concerns and maybe reconsider using dependency injection by refactoring Blorgh::Post to deal with different configurations.
This is happening because in Rails 3 the lib directory is not being automatically looked at to find classes. Your can either update config.autoload_paths to add the lib directory to it in the engine or you can move concerns/models/post.rb out of the lib directory and into app/models where it will be automatically found.

Rails - include module into controller, to be used in the view

I'm really new to Rails and I try to setup a module file to be used in the view. So I believe the correct behavior is to define the module as a helper within a controller and voila, it should be working. However, that's not the case for me. Here is the structure.
lib
functions
-- form_manager.rb
form_manager.rb:
Module Functions
Module FormManager
def error_message() ...
end
end
end
users_controller.rb
class UsersController < ApplicationController
helper FormManager
def new ...
Well, the structure is like the above and when I call the error_message from new.html.erb it gives me the error: uninitialized constant UsersController::FormManager.
So, first of all, I know that in rails 3 lib is not automatically loaded. Assuming that it is not mandatory to autoload the lib folder, how can I make this work and what am I missing?
BTW, please don't say that this question is duplicate. I'm telling you I've been searching for this crap for almost 2 days.
Your module is not autoloaded (at least not in 3.2.6). You have to load it explicitly. You can achieve this with the following line of code
# in application.rb
config.autoload_paths += %W(#{config.root}/lib)
You can check your autoload paths with Rails.application.config.autoload_paths. Maybe it's indeed defined for you?
Now you're sure your module gets loaded, you can check it in rails console by calling
> Functions::FormHelper
Now you can't use that module as a view helper by default. Use #included to define the helper when your module gets included. You achieve "lazy evaluation" this way. I think the problem with your code is that the helper method gets called before the module gets included. (somebody should correct me if I'm wrong)
Here's the code:
Module Functions
Module FormManager
def error_message() ...
end
def self.included m
return unless m < ActionController::Base
m.helper_method :error_message
end
end
end
You should also remove the helper line from your controller.
EDIT:
You can achieve this without autoloading. Just use require "functions/form_manager". You define a helper_method for every method. If you wish use all the module methods as helpers use
def self.included m
return unless m < ActionController::Base
m.helper_method self.instance_methods
end
EDIT2:
It appears that you don't need to use self.included. This achieves the same functionality:
class ApplicationController < ActionController::Base
include Functions::FormManager
helper_method Functions::FormManager.instance_methods
end
It appears you are namespacing FormManager inside of Functions which means you would call it by helper Functions::FormManager
Try that

How do I stub ::Rails.root in rspec?

I'm writing a gem that can be used both with and without rails. In a few places I use code like
path = Rails.root if defined?(::Rails)
and I want to test this logic with rspec. I have tried stubbing it like
stub(:"::Rails").should_receive(:root).and_return("/rails")
but this does not make defined?(::Rails) evaluate to true.
Even if defined?(::Rails) is evaluated to true, you still need a Rails object to inject the method stub. There might be several ways to do this, following is a example of my preferred approach:
before(:each) do
unless defined?(::Rails)
#mocked_rails_class = true
class ::Rails
end
end
end
it do
::Rails.should_receive(:root).and_return('/rails')
your_method.should == '/rails'
end
after(:each) do
# Clean up the Rails class if it's generated by the test case.
Object.send(:remove_const, :Rails) if #mocked_rails_class
end
I'm not sure if it works on all ruby version, but at least it can work on Ruby 1.9.x.

How to print the filename passed to `require`?

When starting the Rails 3.x server (rails s), is it possible to print the path and name of each file as it is being loaded?
Something like below, but for every file that is required by Rails (think loading of each railtie, for example):
Loading boot.rb...
Loading application.rb...
Loading environment.rb...
Loading development.rb...
Loading routes.rb...
Note that I am able to do the above simply by using puts calls in the files above, but that is not what I am aiming for. Instead I would like to print the file name being required by any code, not just in my application but whenever require is called. For example, printing file names for calls to require being made inside the Rails code.
UPDATE
Thanks #ScottJShea. I used the following code:
In application.rb:
require File.expand_path('../boot', __FILE__)
module Kernel
def require_and_print(string)
puts string
require_original(string)
end
alias_method :require_original, :require
alias_method :require, :require_and_print
end
....
Looks like you would need to edit kernel.rb. Here is an SO Post about it where the responder suggests this (although you may ant to limit it to the require method):
module Kernel
def load_and_print(string)
$:.each do |p|
if File.exists? File.join(p, string)
puts File.join(p, string)
break
end
end
load_original(string)
end
alias_method :load_original, :load
alias_method :load, :load_and_print
end

Adding to Rails autoload_path from Gem

I want to write a gem that adds a app/services directory to a Rails application.
Since I want to add it from within the Gem i came up with this solution:
class Railtie < ::Rails::Railtie
config.after_initialize do |app|
::Rails.logger.info "adding #{ActiveService::Configuration.path} to autoload_path"
app.config.autoload_paths = [ActiveService::Configuration.path] + app.config.autoload_paths
end
end
The problem is that config.autoload_path is a frozen array, so that modifing it seems not to be a good idea.
Any suggestions of how this could be achieved in a better way?
config.autoload_paths is frozen inside the :set_autload_paths initializer. The Array is passed on to ActiveSupport::Dependencies.autoload_paths, so modifying the original Array would not make much sense. Therefore it's frozen.
You should be able to hook into :before => :set_autoload_paths and extend config.autoload_paths before it's passed and frozen:
class Railtie < ::Rails::Railtie
initializer 'activeservice.autoload', :before => :set_autoload_paths do |app|
app.config.autoload_paths << ActiveService::Configuration.path
end
end
Documentation about initializer hooks can be found at guides.rubyonrails.org/initialization.html
First, all directories under app/* are already in the load path since Rails 3.0. In any case, if you want to do it, you should use the paths api instead. Example from Rails source code:
https://github.com/rails/rails/blob/master/railties/lib/rails/engine/configuration.rb#L42