Using Libraries in Rails (4.1.x) Engines? - ruby-on-rails

So the core issue at heart here is the following message:
`<class:ApplicationController>': uninitialized constant Xaaron::Core (NameError)
So I think some of the steps I have done will be redundant, but I am new to trying to add code to my library folder in a rails engine, in rails its rather easy. But here its not so much.
So here is what I have done:
in:
xaaron/
lib/
xaaron/
I have a directory called core/ with a file called loder.rb.
Inside of core is a directory called controllers and in side there is a file called user_controller which looks like:
module Xaaron
module Core
module UserController
def assign_to_member_group(user)
memeber = Xaron::Group.find('member')
user.add_group = memeber.group_name
end
end
end
end
To load this I have a loader file:
module Xaaron
module Core
module Loader
include Xaaron::Core::Controllers::UserController
end
end
end
Which I do not think is needed because in the engine.rb file I do: config.autoload_paths << File.expand_path("../xaaron/core/**", __FILE__) which just goes up one directory to the lib/ directory and loads xaaron/core/ and everything in it (or so I thought).
This loader.rb file is included in the ApplicationController
module Xaaron
class ApplicationController < ActionController::Base
...
include Xaaron::Core::Loader
...
end
end
So:
Whats the proper way to load my "core" library
Why am I getting the error above?

I guess your problem is with config.autoload_paths << File.expand_path("../xaaron/core/**", __FILE__). It expands into smth like Rails.root/lib/engine_name/xaaron/core/** and your lib path should be Rails.root/lib/xaaron/core. So, in your case, lib path should be config.autoload_paths << File.expand_path("../../xaaron/core", __FILE__)
Moreover, beeing within your ApplicationController, it is enough to include include Core::Loader, because you're already within Xaaron namespace.
Before you'll start working with controllers, try just call your module Xaaron::Core::Loader within rails console.

Related

Organizing files in lib directory

I'm trying to extract a portion of my Rails project into my lib directory but I can't work out how to link my files up correctly. My directory structure looks like this:
lib/
eventable/
calendar.rb
helpers.rb
# Rest of rails directories/files
I'm requiring the eventable directory in config/application.rb:
config.autoload_paths += %W(#{config.root}/lib #{config.root}/lib/eventable)
My helpers and calendar rb files:
# helpers.rb
module Eventable
module Helpers
def calendar_for...
Calendar.new...
end
end
end
# calendar.rb
module Eventable
class Calendar
# methods defined here
end
end
I'm then mixing my Eventable::Helpers module in the regular Rails helpers so that I can use calendar_for in my views:
ActionView::Base.send :include, Eventable::Helpers
This last step seems to work fine. However, when I go to a view which is using this helper I get:
uninitialized constant Eventable::Helpers::Calendar
If I change my helper so that it tries to access Eventable::Calendar.new instead then I get:
uninitialized constant Eventable::Calendar
When I had all of these in a single file, it all worked perfectly. So how I can correctly link these files up?
It looks like you need a loader-type file to tell Rails where to find code for the Eventable module.
Try add a lib/eventable.rb with:
module Eventable
autoload :Calendar, 'eventable/calendar'
autoload :Helpers, 'eventable/helpers'
end
You shouldn't need to change your load path if you have the loader file in place.
I had a similar problem. I solved it by changing the way modules are required.
In application.rb
config.autoload_paths += Dir["#{config.root}/lib/"]
Create /lib/eventable.rb with the following code
require "eventable/helpers"
require "eventable/calendar"

How to change source for a custom rails generator? (Thor)

I'm making a custom generator that generates a new rails app, and I do it like this
require 'thor'
require 'rails/generators/rails/app/app_generator'
class AppBuilder < Rails::AppBuilder
include Thor::Actions
include Thor::Shell
...
end
The problem is, how do I add a new source directory (which is then used by Thor::Actions#copy_file, Thor::Actions#template, and the others)? I saw in the Thor's documentation that Thor::Actions#source_paths holds the sources (it's an array of paths), so I tried overriding it inside my class (since I've included Thor::Actions):
def source_paths
[File.join(File.expand_path(File.dirname(__FILE__)), "templates")] + super
end
With this I wanted to add the ./templates directory in the sources, while still keeping the Rails' one (that's why the + super at the end). But it doesn't work, it still lists the Rails' source path as the only one.
I tried browsing through the Rails' source code, but I couldn't find how Rails put his directory in the source paths. And I really want to know that :)
This worked:
require 'thor'
require 'rails/generators/rails/app/app_generator'
module Thor::Actions
def source_paths
[MY_TEMPLATES]
end
end
class AppBuilder < Rails::AppBuilder
...
end
I don't understand why, but I've spent too much time on this already, so I don't care.
Thor will access your source_paths method and add these to the defaults:
# Returns the source paths in the following order:
#
# 1) This class source paths
# 2) Source root
# 3) Parents source paths
#
def source_paths_for_search
paths = []
paths += self.source_paths
paths << self.source_root if self.source_root
paths += from_superclass(:source_paths, [])
paths
end
So all you need to do in your class is:
class NewgemGenerator < Thor::Group
include Thor::Actions
def source_paths
['/whatever', './templates']
end
end
Hope this helps :)
The source_paths method doesn't work when using AppBuilder. (which is another option to using rails templates). I have a files directory next to the app_builder.rb file that this class is in. I have this working, though it seems there should still be a more elegant way.
tree .
|-- app_builder.rb
|-- files
`-- Gemfile
class AppBuilder < Rails::AppBuilder
def initialize generator
super generator
path = File.expand_path( File.join( '..', File.dirname( __FILE__ )) )
source_paths << path
end
def gemfile
copy_file 'files/Gemfile', 'Gemfile'
end
and then on the console:
rails new my_app -b path_to_app_builder.rb
The dots are required since the ruby file 'app_builder.rb' is slurped up and eval'd after the rails new command changes into the new app directory (I think).
It's an old post, however, the problem remains the same, I spent time to figure that out. I let a comment here if it can help.
Rails add by default the path lib/templates so you can custom any templates by copying them in this directory.
Check out the correct directories structures https://github.com/rails/rails/tree/v6.0.1/railties/lib/rails/generators/rails
There is subtle though and is not so obvious to catch.
Take for instance the helper generator, as mentioned in the official Rails documentation, the structure is
https://github.com/rails/rails/tree/v6.0.1/railties/lib/rails/generators/rails/helper
- helper
- templates
- helper.rb.tt
- helper_generator.rb
Here, what Railties expects to find in your project:
- lib
- templates
- helper
- helper.rb
your helper.rb is a copy of helper.rb.tt
the last thing, if you plan to use it in Rails::Engine you have to tell it to load that path
# lib/blorgh/engine.rb
module Blorgh
class Engine < ::Rails::Engine
isolate_namespace Blorgh
config.generators.templates << File.expand_path('../templates', __dir__)
end
end
Really hope that can help and save time for others.
https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/application/configuration.rb#L188
https://guides.rubyonrails.org/generators.html#customizing-your-workflow-by-changing-generators-templates
https://github.com/rails/rails/blob/v6.0.1/railties/CHANGELOG.md
https://guides.rubyonrails.org/engines.html

Where to put custom classes to make them globally available to Rails app?

I have a class that I'm trying to use in my controller in the index action.
To simplify it, it looks like this
class PagesController < ApplicationController
def index
#front_page = FrontPage.new
end
end
FrontPage is a class that I have defined. To include it, I have placed it in the /lib/ folder. I've attempted to require 'FrontPage', require 'FrontPage.rb', require 'front_page', and each of those with the path prepended, eg require_relative '../../lib/FrontPage.rb'
I keep getting one of the following messages: cannot load such file -- /Users/josh/src/ruby/rails/HNReader/lib/front_page or
uninitialized constant PagesController::FrontPage
Where do I put this file/how do I include it into a controller so that I can instantiate an object?
This is Rails 3.1.3, Ruby 1.9.2, OS X Lion
You should be able to use require 'front_page' if you are placing front_page.rb somewhere in your load path. I.e.: this should work:
require 'front_page'
class PagesController < ApplicationController
def index
#front_page = FrontPage.new
end
end
To check your load path, try this:
$ rails console
ree-1.8.7-2011.03 :001 > puts $:
/Users/scottwb/src/my_app/lib
/Users/scottwb/src/my_app/vendor
/Users/scottwb/src/my_app/app/controllers
/Users/scottwb/src/my_app/app/helpers
/Users/scottwb/src/my_app/app/mailers
/Users/scottwb/src/my_app/app/models
/Users/scottwb/src/my_app/app/stylesheets
# ...truncated...
You can see in this example, the first line is the project's lib directory, which is where you said your front_page.rb lives.
Another thing you can do is add this in your config/application.rb:
config.autoload_paths += %W(#{config.root}/lib)
That should make it so you don't even need the require; instead Rails will autoload it then (and everything else in your lib dir, so be careful).
The file was named FrontPage.rb. Changing the name to 'front_page.rb', but leaving the class name as 'FrontPage' resolved the issue.
We just need to load the file,
class PagesController < ApplicationController
require 'front_page.rb'
def index
#front_page = FrontPage.new
end
end
lib/front_page.rb
class FrontPage
end
We can also set the application.rb to autoload these files
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
Second option would be a preferable solution.

How can I extend ActiveRecord from app/modules?

I have several different acts_as_... custom class methods I'd like to use in my app. I would like the code for those methods to be in files in the app/modules directory.
I have been unable to get this working.
For instance, I have a file: app/modules/acts_as_lockable
module ActsAsLockable
def acts_as_lockable
before_create :set_lock
include InstanceMethods
end
module InstanceMethods
protected
def set_lock
now = Time.now.to_s
self.lock = Digest::SHA1.hexdigest(now)
end
end
end
ActiveRecord::Base.extend ActsAsLockable
And in application.rb
config.autoload_paths += %W(#{config.root}/app/modules)
When I try to load up a model that calls acts_as_lockable I get the following error:
NameError: undefined local variable or
method `acts_as_lockable'
My guess is that I shouldn't be autoloading the modules folder because ActiveRecord has already been loaded when I extend it? Is there another way to do this? I would like to be able to alter the file during development without restarting my server but that's more of a want that a need.
I think you're thinking about this in the wrong way.
You are adding this module to the load path,
but it will only load if you either say;
require 'acts_as_lockable'
or
ActsAsLockable
I'd suggest you never really want to say either of these inside your code.
The correct paradigm you're looking for is an "initializer".
I suggest you create a file called "config/initializers/acts_as_lockable.rb"
In this file you can either include the whole code,
or just include a require 'acts_as_lockable'
Normally I keep things like this inside the libs directory
ensure lib is in the load path
** config/application.rb **
config.autoload_paths += %W(#{config.root}/lib)
** lib/acts_as_lockable.rb **
module ActsAsLockable
def acts_as_lockable
before_create :set_lock
include InstanceMethods
end
module InstanceMethods
protected
def set_lock
now = Time.now.to_s
self.lock = Digest::SHA1.hexdigest(now)
end
end
end
then in the initializer
** config/initializers/acts_as_lockable.rb **
require 'acts_as_lockable'
ActiveRecord::Base.extend ActsAsLockable
The problem is that ruby autoload mechanism is a lazy process: When a constant like ActsAsLockable is used within your code, it looks for a file called acts_as_lockable.rb within the autoload_paths. As You never actually use ActsAsLockable, the file never gets loaded. You could do (although not tremendously beautiful):
ActsAsLockable
class MyModel < ActiveRecord::Base
acts_as_lockable
...
end
I think the acts_as_* pattern is ment to be used be plugins and gems to easily integrate functionality into your code. Plugins and gems are supposed to be in a final state when you integrate them into your project so you would not need the reloading functionality for the development mode.
I hope this helps.

Rails /lib modules and

I am writing a custom wrapper for open_flash_chart plugin. It's placed in /lib and load it as a module in ApplicationController.
However, I have either a problem with the Class hierarchy or some other problem.
From any controller I can access open_flash_chart functions as OpenFlashChart, Line etc.
However, in a class in a /lib module, it doesnt work!
Any ideas?
There are two ways that files get loaded in Rails:
It is registered in the autoload process, and you reference a constant that corresponds to the file name. For instance, if you have app/controllers/pages_controller.rb and reference PagesController, app/controllers/pages_controller.rb will automatically be loaded. This happens for a preset list of directories in the load path. This is a feature of Rails, and is not part of the normal Ruby load process.
Files are explicitly required. If a file is required, Ruby looks through the entire list of paths in your load paths, and find the first case where the file you required is in the load path. You can see the entire load path by inspecting $LOAD_PATH (an alias for $:).
Since lib is in your load path, you have two options: either name your files with the same names as the constants, so Rails will automatically pick them up when you reference the constant in question, or explicitly require the module.
I also notice that you might be confused about another thing. ApplicationController is not the root object in the system. Observe:
module MyModule
def im_awesome
puts "#{self} is so awesome"
end
end
class ApplicationController < ActionController::Base
include MyModule
end
class AnotherClass
end
AnotherClass.new.im_awesome
# NoMethodError: undefined method `im_awesome' for #<AnotherClass:0x101208ad0>
You will need to include the module into whatever class you want to use it in.
class AnotherClass
include MyModule
end
AnotherClass.new.im_awesome
# AnotherClass is so awesome
Of course, in order to be able to include the module in the first place, you'll need to have it available (using either of the techniques above).
In Rails 3 /lib modules are not loaded automatically.
This is because the line:
# config.autoload_paths += %W(#{config.root}/extras)
inside config/application.rb is commented.
You can try to uncomment this line or, (it worked even better for me), leave this commented (for future reference) and add this two lines:
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
What worked for me, besides uncommenting config.autoload_paths (I’m on Rails 3.1.3), was to create a initializer like this:
#config/initializers/myapp_init.rb
require 'my_module'
include MyModule
This way I can call mymodule methods from anywhere and as class methods Model.mymodule_method or as instance methods mymodel.mymodule_method
Maybe some expert may explain the implications of this. By now, use it at your own risk.
Edit: Afterwards, I think a better approuch would be:
create a initializer like this:
#config/initializers/myapp_init.rb
require ‘my_module’
Include the module where needed, like this:
1) if you want to use it as "Class Methods" use "extend":
class Myclass < ActiveRecord::Base
extend MyModule
def self.method1
Myclass.my_module_method
end
end
2) if you want to use it as "Instance Methods" include it inside Class definition:
class Myclass < ActiveRecord::Base
include MyModule
def method1
self.my_module_method
end
end
3) remember that include MyModule refers to a file my_module.rb in your load path that must be required first
To use the module lib/my_module.rb in your models and controllers:
In config/application.rb:
config.watchable_dirs['lib'] = [:rb]
In your model (similar idea for your controller):
require_dependency 'my_module'
class MyModel < ActiveRecord::Base
include MyModule
MyModule.some_method
end
This method is described in more detail at http://hakunin.com/rails3-load-paths
It might be the case that you want to explicitly load file(s) under lib directory at time of application initialization. In my config/application.rb, I have an entry as, config.autoload_paths += %W(#{config.root}/lib) Also this might be the case that module name/hierarchy is not same as it is in file or location/name of file is not same as that hierarchy, so auto-load of that file is also not possible. So when I added an entry at bottom of config/application.rb as, require "./lib/file_name_without_extention it worked fine.

Resources