I have a question concerning best practices in rails.
In my rails project I have the following code:
class MyController < ApplicationController
def some_method
#product = MyFabricatorClass.new.create_product
end
...
end
MyFabricatorClass is not dependent of some state and its behaviour is constant. I am also doing a lot of C++ stuff and to me it feels kind of unefficient to always instantiate a new MyFabricatorClass object. In a C++ project I would propably use something like:
class MyController < ApplicationController
##my_fabricator = nil
def some_method
##my_fabricator ||= MyFabricatorClass.new
#product = ##my_fabricator.create_product
end
...
end
Is this style also legit in Rails? What would be the typical rails way to do it?
Thanks for any advice...!
It is a better practice to not use class variables (those that start with ##) in ruby; see here why
This might look like a weird code, but this is the more conventional way:
You set a "class" instance variable, instead of setting a "class variable".
class MyController < ApplicationController
#my_fabricator = nil
class << self
def some_method
#my_fabricator ||= MyFabricatorClass.new
#product = #my_fabricator.create_product
end
end
end
About class << self, see here
The above code is just the same as:
class MyController < ApplicationController
#my_fabricator = nil
def self.some_method
#my_fabricator ||= MyFabricatorClass.new
#product = #my_fabricator.create_product
end
end
Now you can just do:
MyController.some_method
Related
I have the following class
class EvaluateService
def initialize
end
def get_url
end
def self.evaluate_service
#instance ||= new
end
end
class CheckController < ApplicationController
def index
get_url = EvaluateService.get_url
end
end
The problem here is that i know that i can do evaluate_service = EvaluateService.new and use the object evaluate_service.get_url and it will work fine but i also know that some frown upon the idea of initializing the service object this way and rather there is a way of initializing it via a call, send method in the service class.
Just wondering how do i do this?
I think what you're looking for is something like:
class Evaluate
def initialize(foo)
#foo = foo
end
def self.call(foo)
new(foo).call
end
def call
url
end
private
def url
# Implement me
end
end
Now you can do this in your controller:
class CheckController < ApplicationController
def index
#url = Evaluate.call(params)
end
end
The reason some prefer #call as the entry point is that it's polymorphic with lambdas. That is, anywhere you could use a lambda, you can substitute it for an instance of Evaluate, and vice versa.
There are various ways to approach this.
If the methods in EvaluateService don't need state, you could just use class methods, e.g.:
class EvaluateService
def self.get_url
# ...
end
end
class CheckController < ApplicationController
def index
#url = EvaluateService.get_url
end
end
In this scenario, EvaluateService should probably be a module.
If you want a single global EvaluateService instance, there's Singleton:
class EvaluateService
include Singleton
def get_url
# ...
end
end
class CheckController < ApplicationController
def index
#url = EvaluateService.instance.get_url
end
end
But global objects can be tricky.
Or you could use a helper method in your controller that creates a service instance (as needed) and memoizes it:
class EvaluateService
def get_url
# ...
end
end
class CheckController < ApplicationController
def index
#url = evaluate_service.get_url
end
private
def evaluate_service
#evaluate_service ||= EvaluateService.new
end
end
Maybe even move it up to your ApplicationController.
Is it okay to call a private method of a parent class's subclass from a module which is included in the parent class especially when it concerns ApplicationController, Controllers and lib modules in Rails?
Consider if required to change the controller name the method name to reflect the model name(to Article) change.
I feel this is really bad coding and wanted to know what community thinks about this
Example from a Rails Application:
/lib/some_module.rb
module SomeModule
include SomeModuleResource
def filtering_method
calling_method
end
def calling_method
fetch_object
end
end
/lib/some_module_resource.rb
module SomeModuleResource
def fetch_object
note
end
end
/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include SomeModule
before_action :filtering_method
end
/app/controllers/notes_controller.rb
class NotesController < ApplicationController
def show
end
private
def note
#note ||= Note.find(param[:id]))
end
end
I'm of the opinion that this is not necessary bad, although when you expect a certain interface (methods, variables, etc.) from the class that includes the module I would add the following:
module SomeModuleResource
def fetch_object
note
end
private
def note
raise NotImplementedError
end
end
This way, when #note is called without implementing it (because you forgot it was needed or whatever) a NotImplementedError is raised.
Another option is to work around it and create a more general solution. For example, if all controllers behave the same way you described above you can do the following:
module SomeModuleResource
def fetch_object
note
end
private
def note
klass = params[:controller].classify.constantize
instance = klass.find(params[:id])
var_name = "##{klass.underscore}"
instance_variable_set(var_name, instance) unless instance_variable_get(var_name)
end
end
You could also create a class helper method like before_action so that you can pass your own implementation.
module SomeModule
include SomeModuleResource
def self.included(base)
base.extend(ClassMethods)
end
def filtering_method
calling_method
end
def calling_method
fetch_object
end
module ClassMethods
def custom_before_action(&block)
define_method(:note, &block)
private :note
before_action :filtering_method
end
end
end
Now you can use custom_before_filter { #note ||= Note.find(params[:id]) } in every controller (after including).
The above is just to present you with ideas. I'm sure you could find better solution to the problem, but this hopefully points you in the right direction.
See: Alternatives to abstract classes in Ruby?. Or search for abstract classes in Ruby and you'll find more on this subject.
I am trying to make my rails code a bit nicer.
I have this:
class MyController < ApplicationController
before_action do
# #variable_defined_else_where is an object w/ accessors
#variable_defined_else_where.some_value = "string"
end
end
I would like to do this some how get to here:
class MyController < ApplicationController
variable_defined_else_where(some_value: "string")
# or
variable_defined_else_where.some_value = "string"
# or
some_method "string"
end
I looked at the rails actionview code, for "layout" which has a similar syntax-ness
class MyController < ApplicationController
layout "string"
end
However, it declares a method in the class, I need to modify a
#variable_defined_else_where
which then controls how several bits of helpers behave
module MyHelper
def do_if_that
if #variable_defined_else_where == "string"
# do so and so
end
end
end
Does anyone have any suggestions on how I can get to syntaxically happy-ness?
Since before_action executes in the instance context, your variable-in-question is an instance variable, which means it's only set on the instance of the controller (i.e., during the request lifecycle). On the other hand, layout is most likely setting a property on the controller class itself.
If your variable could be moved to the class-level without compromising thread-safety, you could make it a class attribute and set it directly like:
class MyController < ApplicationController
##my_variable = 3
def test
##my_variable # returns 3
end
end
But if you don't like how # signs look, maybe that's not better :)
Here's another option, which just wraps your before_action definition inside a class method:
module SetsSomeVariable
include ActiveSupport::Concern
module ClassMethods
def set_variable(value)
self.before_action do
#my_variable = value
end
end
end
end
# ...
class MyController < ApplicationController
include SetsSomeVariable # this could be in ApplicationController
set_variable 'string'
end
I'm new to ruby/rails world, and I'm facing and issue that appears to be something trivial, but it isn't being that easy for me.
What I want to do, is to write a simple helper method, to help keep the controllers clean. This method will receive a symbol/string, and create an instance variable with the camelized received string, which will evaluate to a constant/class.
My attempts resume themselves to this:
class ApplicationController < ActionController::Base
...
protected
def self.service(name)
instance_eval do
instance_variable_set("##{name.to_s.pluralize}", name.to_s.camelize.constantize)
end
end
And, in my controller:
class UserController < ApplicationController
service :user
But when I do this, it create the instance variables in UserController, not UserController.new. So, I know what is wrong, but I couldn't find how can I do this right.
Can anyone help me with that?
The trouble is that this method is in the class scope:
def self.service(name)
instance_eval do
instance_variable_set("##{name.to_s.pluralize}", name.to_s.camelize.constantize)
end
end
This means it'll be setting class level instance variables - or class variables (kinda).
If you want to follow this approach, you'll need to store these all in an array or similar at the class level, which can be read out and turned into instance variables when the class is instantiated.
However, I don't like to mess with the initialize method on controllers. It's not the way to do things, so could change the class behaviour, break things or become fragile when changing versions.
In your ApplicationController, something like:
class << self
def service(name)
#services ||= []
return #services += name.map(&:to_sym) if name.is_a?(Array)
#services << name.to_sym
end
def service_names
#services
end
end
before_action :services
def services
self.class.service_names.each do |name|
instance_eval do
instance_variable_set("##{name.to_s.pluralize}", name.to_s.camelize.constantize)
end
end
end
This is untested.
my ruby (on rails) class looks like:
class Foo
def self.method1
someAction
end
def self.method2
someAction
end
def someAction
//doSmth
end
end
any ideas how to make this work or achieve the same behavior some other way?
thanks!
If some_action is appropriate as a class method, I'd do it like this:
class Foo
def self.method1
some_action
end
def self.some_action
# do stuff
end
def some_action
self.class.some_action
end
end
If method1 is supposed to be a convenience method, then I'd do like Hates_ said to.
class Foo
def self.method1
self.new.some_action
end
def some_action
# do stuff
end
end
The decision for me is usually whether some_action is more of a utility method (like generating a random key, in which case I'd pick the first form), or if it's an entry point to something more complex (like a parser, in which case I'd pick the second form).
You cannot call an instance method from a class method, without an actual instance of the class itself. You can do it as such:
class Foo
def self.method1
myFoo = Foo.new
myFoo.someAction
end
def someAction
//doSmth
end
end