Rails 3 - Different Class Behavior in rails console vs rails server - ruby

I'm referencing a custom class from within a model named Plac
Model is defined in models/plac.rb like this:
class Model < ActiveRecord::Base
def notify_owner
notifier = BatchNotify.getInstance
end
end
BatchNotify is defined in lib/modules/batch_notify.rb like so:
class BatchNotify
def self.getInstance
env = Rails.env
if(env == "test")
return TestBatchNotify.new
else
BatchNotify.new
end
end
end
I have also added the modules directory to autoload_path:
config.autoload_paths += %W(#{config.root}/lib/modules)
The weird thing is that when notify_owner() works great from the rails console.
However, when I start the web server with rails server and try to trigger notify_owner by using the app in the browser, I get the following error:
uninitialized constant Plac::BatchNotify
First, why is the behavior different in console vs web server?
Second, why does it still not recognize the Batch notify constant?
By the way, I've also tried defining BatchNotify within a module and referencing it as Module::BatchNotify with no luck...

There are a couple of ways to get the BatchNotify class loaded properly.
Add a config/initializers/00_requires.rb file with the following code:
require "#{Rails.root}/lib/modules/batch_notify.rb"
Or, require models/placebo.rb in the Model class:
require "#{Rails.root}/lib/modules/batch_notify.rb"
A couple of other comments on this code:
Rails already uses the term models, so Model is not a good class name in Rails.
The file naming convention is that the file name should correspond with the model name. So, the models/placebo.rb should be renamed to models/model.rb to follow convention.
BatchNotify is a class so I don't think you should put it a directory name modules.

Related

Ruby: add a method to a module from a separate file and autoload it

I'm using the Sorcery library in a rails app. One of its modules is for external authentication, and I need to add a method to that module.
The existing code is here, I want to add the add_provider_to_user method from this patch.
So, I added a file to my lib/modules directory, which I've told rails to autoload. The file is called sorcery_extension.rb and it looks like this:
module Sorcery
module Controller
module Submodules
module External
module InstanceMethods
protected
# If user is logged, he can add all available providers into his account
def add_provider_to_user(provider)
provider_name = provider.to_sym
provider = Config.send(provider_name)
user_hash = provider.get_user_hash
config = user_class.sorcery_config
user = current_user.send(config.authentications_class.to_s.downcase.pluralize).build(config.provider_uid_attribute_name => user_hash[:uid], config.provider_attribute_name => provider)
user.save(:validate => false)
return user
end
end
end
end
end
end
This didn't work. I get undefined method error in my controller (where calling the other sorcery methods works fine).
So, my basic understanding of ruby is you can add methods to an object or module at any time... I think I've copied the nesting of the modules correctly in the file. Do I need to name the module file something different? I'm not really sure how to do this kind of thing, so any help is much appreciated. Thanks!
Your file is never required. You can double check this by typing in the console:
Sorcery::Controller::Submodules::External::InstanceMethods.method_defined?(:add_provider_to_user)
# => will return false, you want true
The reason is that auloading only happens when a constant is unknown in which case Rails will try to autoload it from the different autoloaded paths.
You have to require your file explicitly (e.g. from a file in initializer) and things will work as expected.
Rails convention on requires is that for every module it looks in a directory of the same name.
For your example
module Sorcery
module Controller
module Submodules
module External
module InstanceMethods
If you want to put the module in the lib directory. When it goes to "autorequire" Rails would be expecting it in this path
lib/sorcery/controller/submodules/external/instance_methods.rb
This is why its generally good convention to keep your module nesting shallow. ~ 2 levels deep.

Where and how should I add this new 'non-rails-way' method in my Rails application

I've written a small method to query and retrieve from an MS SQL 2008 server and I am not sure where to put the code in my rails app.
The scenario:
I am writing a Ruby and Rails app with a connection to a legacy MS SQL 2008 server DB.
A lot is working as expected, which is nice.
For now I work off a copy of the legacy DB and I treat it as readonly. It's big (7000+ tables some of which have over 40 million records). I am using it 'as-is' and don't want to change any of the underlying schema.
I do want to extend some very server-specific functionality. For instance, I make use of:
thing = ActiveRecord::Base.connection.exec_query(my_query_string_here)
... and it works. The result is an array that contains a hash and I can get to the relevant hash value by using:
thing[0][""]
... which works.
So, I thought I should write a method to make this easier and I wrote:
Class Tool < ActiveRecord::Base
def self.queryRDW(x)
res=ActiveRecord::Base.connection.exec_query(x)
ret=res.to_hash
return ret[0][""]
end
end
and put it in config/initializers/tool.rb Unfortunately, webrick complains about the file during boot with the following cryptic error:
.../config/initializers/tool.rb:7: syntax error, unexpected keyword_end, expecting $end (SyntaxError)
I recognize that this is not an out-of-the-box rails-way of doing things, so please don't remind me. (My struggles remind me often enough)
My question:
Where should I put this code so that I can invoke it from within a controller or a view in my rails app? Does this need to be a new Class method or something else?
Many thanks!
Addendum:
I changed Class to class (doh!)
I moved tool.rb into lib/
I changed tool.rb to now be:
module Tool
def self.queryRDW(x)
res = ActiveRecord::Base.connection.exec_query(x)
res.to_hash[0][""]
end
end
but doing this in app/views/stats.html.erb
thing=queryRDW("mysql string")
gets me an 'undefined method error'
Addendum 2
I made the directory app/concerns and put tool.rb there.
When I use:
<%=queryRDW("myStringHere")%>
in:
app/views/stats.html.erb
I get:
undefined method `queryRDW' for #<#<Class:0x0000000378ccf8>:0x00000003c1ce58>
You need to lowercase the keyword class in line 1.
I'd also say that this class doesn't need to inherit from ActiveRecord::Base — and doesn't even really need to be a class — if it's simply a wrapper around exec_query. There's nothing "wrong" with this, but if you never intend to instantiate an object of this class, you could just create a simple utility module:
module Tool
def self.queryRDW(x)
res = ActiveRecord::Base.connection.exec_query(x)
res.to_hash[0][""]
end
end
You can save this file in a couple of places:
lib/tool.rb. If you're using Rails 3, you'll need to add (or uncomment) this line in config/application.rb:
# config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
app/concerns/tool.rb. This will automatically be detected by Rails 3.
I generally use app/concerns for tools that are entirely application-specific and lib for utilities that I might reuse among several different applications.
I wouldn't put this in config/initializers. This seems like code you'd put in app/models.
The error you're getting is syntax related, so double check the syntax.
To answer your question more directly, though, it's acceptable to put this stuff in your model if it's model related (in other words, part of your business domain). If it is something extraneous or orthogonal to your domain, I'd put it in lib.
Hope this helps.

How to dynamically load class using namespaces/subdirectory in Ruby/Rails?

In my Rails 3.1 app (with Ruby 1.9), I have a Deployer1 class that is in a worker subdirectory below the model directory
I am trying to load/instantiate this class dynamically with this code:
clazz = item.deployer_class # deployer_class is the class name in a string
deployer_class = Object.const_get clazz
deployer = deployer_class.new
If I dont use namespaces, eg something global like this:
class Deployer1
end
Then it works fine (deployer_class="Deployer1") - it can load the class and create the object.
If I try and put it into a module to namespace it a bit, like this:
module Worker
class Deployer1
end
end
It doesnt work (deployer_class="Worker::Deployer1") - gives an error about missing constant, which I believe means it cannot find the class.
I can access the class generally in my Rails code in a static way (Worker::Deployer1.new) - so Rails is configured correctly to load this, perhaps I am loading it the wrong way...
EDIT:
So, as per Vlad's answer, the solution I went for is:
deployer_class.constantize.new
Thanks
Chris
try using constantize instead:
module Wtf
class Damm
end
end
#=> nil
'Wtf::Damm'.constantize
#=> Wtf::Damm
Object.const_get 'Wtf::Damm'
#=> Wtf::Damm
Object does not know a constant named Worker::Deployer1, which is why Object.const_get 'Worker::Deployer1' doesn't work. Object only knows a constant Worker. What does work is Worker.const_get 'Deployer1'.
Vlad Khomisch's answer works, because if you look at the implementation of constantize, this is exactly what it does: it splits the string on '::' and recursively const_get's.

In Rails, how to add a new method to String class?

I want to build an index for different objects in my Rails project and would like to add a 'count_occurences' method that I can call on String objects.
I saw I could do something like
class String
def self.count_occurences
do_something_here
end
end
What's the exact way to define this method, and where to put the code in my Rails project?
Thanks
You can define a new class in your application at lib/ext/string.rb and put this content in it:
class String
def to_magic
"magic"
end
end
To load this class, you will need to require it in your config/application.rb file or in an initializer. If you had many of these extensions, an initializer is better! The way to load it is simple:
require 'ext/string'
The to_magic method will then be available on instances of the String class inside your application / console, i.e.:
>> "not magic".to_magic
=> "magic"
No plugins necessary.
I know this is an old thread, but it doesn't seem as if the accepted solution works in Rails 4+ (at least not for me). Putting the extension rb file in to config/initializers worked.
Alternatively, you can add /lib to the Rails autoloader (in config/application.rb, in the Application class:
config.autoload_paths += %W(#{config.root}/lib)
require 'ext/string'
See this:
http://brettu.com/rails-ruby-tips-203-load-lib-files-in-rails-4/
When you want to extend some core class then you usually want to create a plugin (it is handy when need this code in another application). Here you can find a guide how to create a plugin http://guides.rubyonrails.org/plugins.html and point #3 show you how to extend String class: http://guides.rubyonrails.org/plugins.html#extending-core-classes

Accessing a Rails model name from within a lib file of the same method

This may seem like a silly question but I have a model named Ad and I have a library name auto which contains a class Ad(lib/auto.rb).
#auto.rb lib file
module auto
class Ad
def initialize
...
end
def convert
# here I would like to access my model class Ad
# ::Ad does not work. Requiring it does not work either.
end
end
end
Does Rails 3 store models under some global namespace?
Am I missing something or you are defining auto::Ad?
If so, then ::Ad will never work. use auto::Ad or Ad (from within the auto module).
If you really don't want the auto namespace . Remvoe the module auto part in your code.
I think that you can't get it straight from your external class. But you can pass it from your exact model.
I think this can be useful
http://guides.rubyonrails.org/plugins.html#add-an-acts_as-method-to-active-record

Resources