Trying to make my own app generator (i.e. rails new appName -m path/to/generator) and use my custom generator alongside the rails one.
All goes well except when I want to add a source_path. There are several posts about how this could work with generators for existing apps - see here and I get the same error.
When I add the following line to the top of my script:
source_root File.expand_path('../dependencies',FILE)
the console returns:
apply': undefined method 'source_root' for # Rails::Generators:: App Generator : 0x007f8b0a2a3798> (NoMethodError)
how should I restructure my generator given I am in a Rails project and not a pure ruby one?
Related
I'm new to Ruby on Rails and am trying to access my site's database. I generated and set up a model and controller called Machine, and noticed that in places like the Machine view I could iterate through all the machines in my database simply using #machines.each. However, this doesn't appear to be universal, as when I created a new Ruby file directly in my project's outermost directory, both #machines.each and the attempted assignment #machines = Machine.all threw errors (a NoMethodError and NameError respectively). Here's an example of code I could try to run:
#machines = Machine.all
#machines.each do |machine|
puts machine.created_at
end
Perhaps I need some kind of import statement?
If you are writing a script in plain Ruby -- then yes, you'll have to import everything manually, establish a connection to the DB, etc.
The code would roughly look like this:
require 'active_support'
require 'active_record'
your_db_config = {
# your DB config goes here
}
ActiveSupport::Dependencies.autoload_paths += File.join(__dir__, "app/models")
ActiveRecord::Base.establish_connection(your_db_config)
machines = Machine.all
Consider creating a task if you want Rails to take care of all that and don't want to be doing all that stuff manually.
When you start a rails server (or a rails console) it preloads your Rails application so that your models, constants, etc. are automatically in scope. If you want to access your application's resources from a separate script you still need to load the app. The simplest way to do that is with the rails runner command, which loads your app and then executes a script. So if your script above is in lib/show_machines you'd run:
$ bin/rails runner lib/show_machines
If you like self-executing scripts you can also use runner as a 'shebang' line:
#!/usr/bin/env <your_project_path>/rails/runner
#machines = Machine.all
#machines.each do |machine|
puts machine.created_at
end
I'm trying to use my Engine in another app to test the install generator and it seems to be failing. I haven't used my install generator for a long time, so I'm not sure when it broke (or if it ever truly smoothly worked). My project is based off radar/forem, so I tried to borrow a lot of their code (including the generator).
Edit: My installer works for the engines test/dummy but not in other apps. Why?
GH issue: https://github.com/NJayDevelopment/mongoid_forums/issues/16
Here is the log:
$ rails g mongoid_forums:install
What is your user class called? [User]
What is the current_user helper called in your app? [current_user]
Defining mongoid_forums_user method inside ApplicationController...
insert app/controllers/application_controller.rb
Adding mongoid_forums initializer (config/initializers/mongoid_forums.rb)...
create config/initializers/mongoid_forums.rb
(erb):5:in `template': undefined method `per_page' for MongoidForums:Module (NoMethodError)
The route is successfully added, however the initializer/mongoid_forums.rb is a blank file. The method is defined exactly how radar/forem does it, what could be the error?
Here is the relevant code:
Per page method definition: https://github.com/NJayDevelopment/mongoid_forums/blob/master/lib/mongoid_forums.rb#L33
Mattr accessor:
https://github.com/NJayDevelopment/mongoid_forums/blob/master/lib/mongoid_forums.rb#L9
Initializer template:
https://github.com/NJayDevelopment/mongoid_forums/blob/master/lib/generators/mongoid_forums/install/templates/initializer.rb
Install generator at error point:
https://github.com/NJayDevelopment/mongoid_forums/blob/master/lib/generators/mongoid_forums/install_generator.rb#L47
Turns out when you try requiring mongoid_forums in pry, you'll see that an error involving decorators occurs. The issue is fixed here in my pull request to decorators: parndt/decorators#13
It's because of the way files are required and how load! is called over there.
Waiting on PR status, that is the same version that radar/forem uses as well.
In our existing Rails 3 applications we use an overridden migration.rb file to customise the table creation behaviour.
This was done in Rails 3 by placing our custom file in lib/templates/active_record/model/migration.rb, however it appears Rails 4 has changed the location used for these templates, and this override isn't picked up anymore (it uses the default ActiveRecord migration when creating tables).
I've had a look through the 4.1 ActiveRecord code but can't get the override to work again.
Does anyone know the correct location to place our custom migration.rb in a Rails 4 codebase?
EDIT FOR CLARIFICATION
When you create a new model in Rails, the migration that is generated for you is based off the template found in (> 4.1.x) activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb in the Rails gem.
This has changed since Rails 3.2. In Rails 3.2 the template that was used was called migration.rb and was in the activerecord/lib/rails/generators/active_record/model/templates directory of the Rails gem.
In order to customise the generated template (add a custom SQL block that would be executed in the change method), we override this file by placing a modified copy of it in in our local code base under the lib/templates/active_record/model directory.
We customise it to add some application specific SQL to the end of the migration. In rails 3 this meant that any time you generated a new model, the resulting migration would auto-magically include our custom SQL at the end of the migration.
In our Rails 4 upgrade this custom migration isn't being used anymore, so we're getting vanilla migration files generated by rails, and are having to manually add the SQL each time.
I have tried following the same convention and placing the file in lib/templates/active_record/migration/migration.rb (and a variety of other locations) but the custom template is not being used by Rails when generating a migration.
In Rails 4.2.6 those paths are like this:
lib/templates/active_record/migration/migration.rb
lib/templates/migration/templates/create_table_migration.rb
And the ultimate answer to this problem is to look into source code of Thor library, because all rails generators are based on it. This is how I found correct paths.
Go to lib/thor/actions.rb file and look for find_in_source_paths method and just add puts statement there. Whenever you run any generator you can see a list of all paths that are searched for templates. There are also other ways, but this should give you an idea.
In Rails 4.1.4 this should go here:
activerecord/lib/active_record/migration.rb
Source:
http://api.rubyonrails.org/files/activerecord/lib/active_record/migration_rb.html
To be clear, here's NOT what I'm trying to:
Have my custom generator call a default Rails generator
Replace a default Rails generator with my own
What I want to do is have my generator be invoked automatically when I call:
rails generate scaffold User name age:integer
I'm not writing a test replacement or anything, it's completely custom. All information I find about generators out there involve one of those first two cases but not what I want to do. As soon as I found hook_for I immediately thought that was exactly what I needed, but it appears to do the opposite -- invokes another Rails generator from inside of my custom one (if I wanted a test file created for my custom generator I'd call hook_for :test_framework and then define a TestUnit::MyCustomGenerator class somewhere).
I suppose I could monkey patch the default scaffold generator to call mine but that feels dirty. I've looked into some gems that do something similar like https://github.com/Skalar/i18n-yaml-generator but trying to convert that to use an initializer and lib/generators isn't working for me. The scaffold_generator runs but mine never gets called.
for me it works from lib/generators/
$ rails g generator scaffold
create lib/generators/scaffold
create lib/generators/scaffold/scaffold_generator.rb
create lib/generators/scaffold/USAGE
create lib/generators/scaffold/templates
$ rails g scaffold
Usage:
rails generate scaffold NAME [options]
....
what/will/it/create
http://guides.rubyonrails.org/generators.html#generators-lookup
another way may be :)
fork railties gem
override Rails::Generators::ScaffoldGenerator class to your liking
install locally or specify source path in Gemfile
also if 'its completely custom', it is just fair to call it a different name, no?
I am experimenting with gem development, right now specifically generators. So far I have successfully created two generators that do their job just perfectly. These two generators are in the same directory.
However, right now I have to call each of them separately.
What I'd like to do is just call one generator and have that generator call all the other ones. Just would type
rails g generator_name
and this would call x other generators.
Does anyone know how would I got about doing this?
Help is much appreciated, thanks!
In your generator, you can just call
generate "some:generator" # can be anything listed by 'rails g'
for example:
module MyGem
class InstallGenerator < Rails::Generators::Base
def run_other_generators
generate "jquery:install" # or whatever you want here
end
end
end
By the way, if you are working on Rails 3 gems, this question can also help out:
Rails 3 generators in gem
Another possibility is to use something like
invoke 'active_record:model', 'foo bar:string baz:float'
which is not as clean as generate, but has one advantage: When your generator gets called via rails destroy, this call -- like may other of Thors actions -- will try to revoke the action of the generator you invoke.
There's a catch however: Probably due to Thors dependency management, this only works once per generator you want to call, meaning that a second invoke of the same generator will do nothing. This can be circumvented by using a statement like
Rails::Generators.invoke 'active_record:model', '...', behavior: behavior
instead. In this case you have to explicitly pass through the behavior of your generator (which is a method returning values like :invoke, :revoke and possibly others, depending on which command -- rails generate, rails destroy, rails update, etc. -- called your generator) to achieve the same result as above. If you don't do this, the generator you call with Rails::Generators.invoke will also be executed when running your generator with rails destroy.
Alternatively you could stick to invoke and try to tamper with Thors invocation system. See also here for example.
Generators are based off of Thor, so you can use the apply method.
This is what the Rails Templater gem does. (Here's a walk through the Rails Templater gem.)
Take a look at the scaffold generator that comes with rails.
/Users/XYZ/sources/rails/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
def manifest
record do |m|
#....rest of the source is removed for brevity....
m.dependency 'model', [name] + #args, :collision => :skip
end
end
Here the scaffold generator is using the model generator. So take a look at the dependency method. You can find the API docs for it over here.