I'd like to make a function current_order_week that would be available globally throughout my app and could be called similarly to something like current_user. I don't want to have to include it in a specific model / controller, I just want it available everywhere.
I've modified my /lib folder to include a lib_extensions.rb file and added to that file:
class Object
def current_order_week
end
end
I've modified my application.rb to include:
config.autoload_paths << Rails.root.join('lib')
config.eager_load_paths << Rails.root.join('lib')
But when I attempt to call current_order_week from the console or from a test, I still see:
NameError: undefined local variable or method 'current_order_week'
for main:Object
What else do I need to do?
You should add this function in the application_helper.rb file. All controllers extend from ApplicationController and ApplicationController includes the ApplicationHelper.
module ApplicationHelper
def current_order_week
end
end
This will be available to use in views and controllers
Monkey-patching core classes like Object is usually not a good idea, this may interfere with some gems etc. and in general can lead to painful debugging in the future.
If you absolutely want to do this - autoloading will not pick up Object from lib, because it is already defined. Create an initializer in config/initializers, then it will be loaded, but it will not reload on code changes.
But better way is including such code in ApplicationHelper, ApplicationRecord and ApplicationController
autoload_paths and eager_load_paths don't include modules, they only require files in which modules are defined. To use current_order_week you need to specify the name of the module:
module Foo
def current_order_week
.
.
.
end
end
Foo.current_order_week()
In order to use current_order_week without prepending the name of the module to it, you need to include Foo inside your controllers and models:
class ApplicationController < ActionController::Base
include Foo
def some_action
current_order_week()
end
end
class ApplicationRecord < ActiveRecord::Base
include Foo
end
Related
I am doing Rails app and i see that i could refactor my code. Just can't find how. I have many models like Company, News, Profile and many more which use Image upload. So in every class i must always copy-paste 30 rows of methods which implement always the same logic - upload_image, get_image_name, delete_image. How is possible to do, that my Model class would automatically have the methods from somewhere else? I would like to just put to the Model - load 'GlobalMethods', or even somehow include the methds in activerecord:base to just have them for every class and i would use whenever i want. And from controller i would just leave as it is. For example - News.upload_image and it would do this same as this logic would be in original model.
Please explain with example, because i have readed more and didn't understand or it even possible.
At this moment i did:
#models/concerns/uploading.rb
module Uploading
extend ActiveSupport::Concern
included do
def do_upload
puts 'aaaaaaaaaaaaaaa'
end
end
end
and model:
class Company < ActiveRecord::Base
include Uploading
end
and i get this error:
uninitialized constant Company::Uploading
My rails version: '4.2.5'
I did server restarts after every try
My application.rb file looks:
require File.expand_path('../boot', __FILE__)
require 'rails/all'
Bundler.require(*Rails.groups)
module Vca
class Application < Rails::Application
config.active_record.raise_in_transactional_callbacks = true
config.autoload_paths += %W(
#{config.root}/app/models/concerns/uploading.rb
)
end
end
Still the same
NameError in CompaniesController#index
uninitialized constant Company::Uploading
class Company < ActiveRecord::Base
include Uploading # <-- error
end
I have gem 'Spring'. I turned off server, in terminal i runned "spring stop" and started server. But it didn't solve this.
Take a look at ActiveSupport::Concern
For example
app/models/concerns/my_concern.rb
module MyConcern
extend ActiveSupport::Concern
included do
# common stuff here
end
class_methods do
# define class methods here
end
end
app/models/my_model.rb
class MyModel < ActiveRecord::Base
include MyConcern
# ...
end
By the way, you can refer to the model class with self in the included block and out of any instance methods, and inside the methods within the class_methods block.
You'll notice that your standard rails project includes a folder called concerns under your models folder.
Create a module in that folder, image_handling.rb
module ImageHandling
extend ActiveSupport::Concern
included do
# ...(include all your common methods here)
end
end
In your Company, News, Profile models call the concern...
class Company < ActiveReecord::Base
include ImageHandling
...
end
In Rails, how do you use a specific method from a module. For eg,
# ./app/controllers/my_controller.rb
class MyController < ApplicationController
include MyModule
def action
MyModule.a_method
end
private
def a_method
...
end
end
# ------------------------------------------------ #
# ./app/helpers/my_module.rb
module MyModule
def a_method
...
end
end
MyController includes MyModule. And in action ,I want to use MyModule.a_method (Please note I also have a private a_method in MyController and I don't want to use this.)
Things I've tried :
1) Defining the method in the module as self.
def self.a_method
end
2) Using the :: notation in controller (MyModule::a_method)
The error that I keep getting is
Undefined method:a_method for MyModule:module
For now, I've resorted to using a different name for the modules method. But I'd like to know how to namespace the function with either the Module:: or Module. notation
[UPDATE - 11/24/2014]
adding file structure in code, since Rails heavily relies on convention.
So I am not really sure what you are trying to accomplish with your module but a quick solution to get it working is below.
Move my_module.rb out of helpers and into lib/my_module.rb. The helpers directory is for methods that you use in your views. The convention is to utilize helpers that are namespaced after their respective controller or the application_helper.rb for global methods for your views. Not sure if that's what you are trying to accomplish with your module but wanted to throw that out there.
Create an initializer (you can all it whatever) in config/initializers/custom_modules.rb and add require 'my_module'
Update the a_method back to be self.a_method
You can now call MyModule.a_method in your app
Don't forget to restart your server for changes to lib/my_module.rb to take effect.
Also, a lot of people reference this post by Yehuda Katz as guidance on where to store code for your app. Thought it might be a helpful reference.
if you include MyModule into MyController, all the "instance methods" of the first will be mixed-in into the 2nd.
So if you only want to call MyModule.a_method, no need to include your module.
Then you'd want to require (or better autoload) your module before using it. To do so place it in controllers/concerns/my_module.rb, rails (4 at least) should autoload it, otherwise require its file in an intializer
# my_module.rb
module MyModule
def self.a_method
...
end
end
should work, but doing
# my_module.rb
module MyModule
extend self
def a_method
...
end
end
is more clean to me. You'd like to have a look to rails active support concern to understand the "rails way" on this topic.
In a file called foo.rb in my /lib/ directory it reads:
module Foo
def some_method
#text_1 = "Hello!"
end
end
How can I get the results of this method to show up in a View?
I've seen that I need to include the following line in the /config/application.rb file:
config.autoload_paths += %W(#{config.root}/lib
However, I do not completely understand how to pass a variable from a module in a file saved in the /lib/ directory - to show up in a View. I appreciate any advice.
In order to get that value to show up in the view, you'll need to understand how modules are used in Ruby. Typically modules are mixed into other classes either by including or extending them. This would then make that method available to another class which could then be referenced in the view. In your case you might want to include it so it becomes available to instances of whatever class you put it in. Say you have an ActiveRecord model called MyClass and you include Foo. You can then call my_method on instances of that model as demonstrated below:
class MyClass < ActiveRecord::Base
include Foo
end
In your controller:
class MyController
def new
#my_class = MyClass.new
end
end
In your view:
#my_class.some_method
Having said all that, it seems like there might be a better way to do whatever it is you're trying to do :)
Yes.I agree with
Beerlington.
You can do it in an other way,
It is not mandatory to add config.autoload_paths += %W(#{config.root}/lib to application file.Because by default the files which are located in /lib directory won't be executed at first when we run an application using rails s.
In order to make those files to be loaded,we need to include that line in application.rb.
Otherwise,we can directly write it as below,
In model,
require 'Filename'
class MyClass < ActiveRecord::Base
include Foo
end
In controller,
require 'foobar'
class BuyerController < ApplicationController
include Foobar
end
In foobar.rb,
module Foobar
def Foobar.foobar
"Hello world!"
end
end
In view,
<%= Foobar.foobar %> (You can directly call the method by using Modulenmae.Methodname)
I have created a simple railtie, adding a bunch of stuff to ActiveRecord:
0 module Searchable
1 class Railtie < Rails::Railtie
2 initializer 'searchable.model_additions' do
3 ActiveSupport.on_load :active_record do
4 extend ModelAdditions
5 end
6 end
7 end
8 end
I require this file (in /lib) by adding the following line to config/environment.rb before the application is called:
require 'searchable'
This works great with my application and there are no major problems.
I have however encountered a problem with rake db:seed.
In my seeds.rb file, I read data in from a csv and populate the database. The problem I am having is that the additions I made to ActiveRecord don't get loaded, and seeds fails with a method_missing error. I am not calling these methods, but I assume that since seeds.rb loads the models, it tries to call some of the methods and that's why it fails.
Can anyone tell me a better place to put the require so that it will be included every time ActiveRecord is loaded (not just when the full application is loaded)? I would prefer to keep the code outside of my models, as it is code shared between most of my models and I want to keep them clean and DRY.
Putting the extend there just adds it to ActiveRecord::Base.
When a model class is referenced, via Rails 3.1 autoloading/constant lookup, it will load the class. At that point, it is pure Ruby (nothing magic) as to what happens, basically. So I think you have at least a few options. The "bad" option that kind of does what you want it to hook into dependency loading. Maybe something like:
module ActiveSupport
module Dependencies
alias_method(:load_missing_constant_renamed_my_app_name_here, :load_missing_constant)
undef_method(:load_missing_constant)
def load_missing_constant(from_mod, const_name)
# your include here if const_name = 'ModelName'
# perhaps you could list the app/models directory, put that in an Array, and do some_array.include?(const_name)
load_missing_constant_renamed_my_app_name_here(from_mod, const_name)
end
end
end
Another way to do it would be to use a Railtie like you were doing and add a class method to ActiveRecord::Base that then includes stuff, like:
module MyModule
class Railtie < Rails::Railtie
initializer "my_name.active_record" do
ActiveSupport.on_load(:active_record) do
# ActiveRecord::Base gets new behavior
include ::MyModule::Something # where you add behavior. consider using an ActiveSupport::Concern
end
end
end
end
If using an ActiveSupport::Concern:
module MyModule
module Something
extend ActiveSupport::Concern
included do
# this area is basically for anything other than class and instance methods
# add class_attribute's, etc.
end
module ClassMethods
# class method definitions go here
def include_some_goodness_in_the_model
# include or extend a module
end
end
# instance method definitions go here
end
end
Then in each model:
class MyModel < ActiveRecord::Base
include_some_goodness_in_the_model
#...
end
However, that isn't much better than just doing an include in each model, which is what I'd recommend.
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.