access custom helper in model - ruby-on-rails

I've written a little helper method in my ApplicationController like this:
helper_method :dehumanize
def dehumanize (string)
string.parameterize.underscore
end
Now I would like to use it in one of my model files, but it seems not to be available there.
I tried also with:
ApplicationController.dehumanize(title)
in the model but it doesn't work.
any clue on how to make it work there?
thanks,

Models generally can't/don't/shouldn't access methods in controllers (MVC conventions), but the method you've written doesn't necessarily belong in a controller anyway - it would be better as an extension to the string class.
I would suggest you write an initializer to add dehumanize to String:
\config\initializers\string_dehumanize.rb
class String
def dehumanize
self.parameterize.underscore
end
end
You will need to restart your server/console but then you can call .dehumanize on any string:
some model:
def some_method
string1 = 'testing_the_method'
string1.dehumanize
end

Matt's answer is totally right, but to give you some clarification, you want to make sure that you're calling your methods on objects / instances, rather than classes themselves
For example, you mentioned you tried this:
ApplicationController.dehumanize(title)
This will never work because it's calling a method on a class which is not initialized, not to mention the class doesn't have that method. Basically, what will you expect if you called this method?
The way to do it is to use the method Matt recommended, or use a class method on your model itself, which will allow you to call the model's method directly:
#app/models/model.rb
class Model < ActiveRecord::Base
def self.dehumanize string
string.parameterize.underscore
end
end
# -> Model.dehumanize title

Related

rails - instance model include module on variable condition

I need to know if I can include a module to an instantiated model.
What works today :
in the controller
#m = MyModel.create(params)
in the model
class Doc < ActiveRecord::Base
after_save :set_include
def set_include
if bool
self.class.send(:include, Module1)
else
self.class.send(:include, Module2)
end
end
end
and this works, but I'm afraid that self.class actually include the module for the class model an not the instantiated model
In this case, this will work.
The module methods are call after the object is saved.
But in many case, the controller will call some modules methods.
I thought of called the method set_include (up there) in a before_action of the controller.
But I really thinks that is not a good idea...
Any idea how I can really do that with in a good way ?
thanks !
Answer to your direct question is no. Your code only appears to be working and is actually not modifying instance of a class, but the class itself. So all instances of it will be getting this "benefit". Probably not what you wanted. Let me demonstrate with simple ruby example: https://repl.it/BnLO
What you can do instead is use extend with instance like: https://repl.it/BnLO/2
Applied to your code it would be:
class Doc < ActiveRecord::Base
after_save :set_include
def set_include
if bool
extend(Module1)
else
extend(Module2)
end
end
end
Also, self is not necessary. https://repl.it/BnLO/3
You need to use instance class (a.k.a eigenklass):
def set_include
singleton_class.instance_eval do
include bool ? Module1 : Module2
end
end
However the fact that you want to do this is suspicious and might lead to a disaster. So the question is: what are you really trying to achieve here - there surely is the better way of doing so.

A way to call a method in most derived class

How do you call a method in the most derived class of a ruby object?
For example, in Rails, one can have a controller that inherits from ApplicationController, or say, Devise::RegistrationsController. So, say I want the user to override some method in their controller, and call that override from the base class: What would be the best syntax for doing it?
There is nothing you need to do, method lookup always starts at the most-derived class. That is, after all, the whole point of overloading.
class A
def foo
bar
end
def bar
:A
end
end
class B < A
def bar
:B
end
end
B.new.foo
# => :B
Are you trying to use a method from, for example, UserController in lieu of the Devise RegistrationsController? Let me know what you are trying to accomplish specifically using code examples. :)
what I am asking is if there is anything like virtual inheritance on other languages?
No, Ruby does not have this.

Wrapping an object with methods from another class

Let's say I have a model called Article:
class Article < ActiveRecord::Base
end
And then I have a class that is intended to add behavior to an article object (a decorator):
class ArticleDecorator
def format_title
end
end
If I wanted to extend behavior of an article object, I could make ArticleDecorator a module and then call article.extend(ArticleDecorator), but I'd prefer something like this:
article = ArticleDecorator.decorate(Article.top_articles.first) # for single object
or
articles = ArticleDecorator.decorate(Article.all) # for collection of objects
How would I go about implementing this decorate method?
What exactly do you want from decorate method? Should it simply add some new methods to passed objects or it should automatically wrap methods of these objects with corresponding format methods? And why do you want ArticleDecorator to be a class and not just a module?
Updated:
Seems like solution from nathanvda is what you need, but I'd suggest a bit cleaner version:
module ArticleDecorator
def format_title
"#{title} [decorated]"
end
def self.decorate(object_or_objects_to_decorate)
object_or_objects_to_decorate.tap do |objects|
Array(objects).each { |obj| obj.extend ArticleDecorator }
end
end
end
It does the same thing, but:
Avoids checking type of the arguments relying on Kernel#Array method.
Calls Object#extend directly (it's a public method so there's no need in invoking it through send).
Object#extend includes only instance methods so we can put them right in ArticleDecorator without wrapping them with another module.
May I propose a solution which is not using Module mixins and thereby granting you more flexibility. For example, using a solution a bit more like the traditional GoF decorator, you can unwrap your Article (you can't remove a mixin if it is applied once) and it even allows you to exchange the wrapped Article for another one in runtime.
Here is my code:
class ArticleDecorator < BasicObject
def self.[](instance_or_array)
if instance_or_array.respond_to?(:to_a)
instance_or_array.map {|instance| new(instance) }
else
new(instance_or_array)
end
end
attr_accessor :wrapped_article
def initialize(wrapped_article)
#wrapped_article = wrapped_article
end
def format_title
#wrapped_article.title.upcase
end
protected
def method_missing(method, *arguments)
#wrapped_article.method(method).call(*arguments)
end
end
You can now extend a single Article by calling
extended_article = ArticleDecorator[article]
or multiple articles by calling
articles = [article_a, article_b]
extended_articles = ArticleDecorator[articles]
You can regain the original Article by calling
extended_article.wrapped_article
Or you can exchange the wrapped Article inside like this
extended_article = ArticleDecorator[article_a]
extended_article.format_title
# => "FIRST"
extended_article.wrapped_article = article_b
extended_article.format_title
# => "SECOND"
Because the ArticleDecorator extends the BasicObject class, which has almost no methods already defined, even things like #class and #object_id stay the same for the wrapped item:
article.object_id
# => 123
extended_article = ArticleDecorator[article]
extended_article.object_id
# => 123
Notice though that BasicObject exists only in Ruby 1.9 and above.
You'd extend the article class instance, call alias_method, and point it at whatever method you want (although it sounds like a module, not a class, at least right now). The new version gets the return value and processes it like normal.
In your case, sounds like you want to match up things like "format_.*" to their respective property getters.
Which part is tripping you up?
module ArticleDecorator
def format_title
"Title: #{title}"
end
end
article = Article.top_articles.first.extend(ArticleDecorator) # for single object
Should work fine.
articles = Article.all.extend(ArticleDecorator)
May also work depending on ActiveRecord support for extending a set of objects.
You may also consider using ActiveSupport::Concern.

Is there a way to use pluralize() inside a model rather than a view?

It seems pluralize only works within a view -- is there some way that my models can use pluralize too?
Rather than extend things, I just it like this:
ActionController::Base.helpers.pluralize(count, 'mystring')
Hope this helps someone else!
Add this to your model:
include ActionView::Helpers::TextHelper
My favorite way is to create a TextHelper in my app that provides these as class methods for use in my model:
app/helpers/text_helper.rb
module TextHelper
extend ActionView::Helpers::TextHelper
end
app/models/any_model.rb
def validate_something
...
errors.add(:base, "#{TextHelper.pluralize(count, 'things')} are missing")
end
Including ActionView::Helpers::TextHelper in your models works, but you also litter up your model with lots of helper methods that don't need to be there.
It's also not nearly as clear where the pluralize method came from in your model. This method makes it explicit - TextHelper.pluralize.
Finally, you won't have to add an include to every model that wants to pluralize something; you can just call it on TextHelper directly.
YOu can add a method like this in your model
def self.pluralize(word)
ActiveSupport::Inflector.pluralize(word)
end
and call it in this way
City.pluralize("ruby")
=> "rubies"
This worked for me in rails 5.1 (see 2nd method, first method is calling it.)
# gets a count of the users certifications, if they have any.
def certifications_count
#certifications_count = self.certifications.count
unless #certifications_count == 0
return pluralize_it(#certifications_count, "certification")
end
end
# custom helper method to pluralize.
def pluralize_it(count, string)
return ActionController::Base.helpers.pluralize(count, string)
end

Is there a way to make Rails ActiveRecord attributes private?

By default, ActiveRecord takes all fields from the corresponding database table and creates public attributes for all of them.
I think that it's reasonable not to make all attributes in a model public. Even more, exposing attributes that are meant for internal use clutters the model's interface and violates the encapsulation principle.
So, is there a way to make some of the attributes literally private?
Or, maybe I should move on to some other ORM?
Jordini was most of the way there
Most of active_record happens in method_missing. If you define the method up front, it won't hit method_missing for that method, and use yours instead (effectively overwriting, but not really)
class YourModel < ActiveRecord::Base
private
def my_private_attribute
self[:my_private_attribute]
end
def my_private_attribute=(val)
write_attribute :my_private_attribute, val
end
end
Stumbled upon this recently. If you want private writing and reading and public reading something like this
class YourModel < ActiveRecord::Base
attr_reader :attribute
private
attr_accessor :attribute
end
seems to work fine for me. You can fiddle with attr_reader, attr_writer and attr_accessor to decide what should be exposed and what should be private.
well, you could always override the methods...
class YourModel < ActiveRecord::Base
private
def my_private_attribute
self[:my_private_attribute]
end
def my_private_attribute=(val)
self[:my_private_attribute] = val
end
end
For me methods from both Otto and jordinl are working fine and make rspec for object of Class to pass:
object.should_not respond_to :attribute
But when I use jordinl method, I have a deprecation message to not write to database directly, but use attr_writer instead.
UPDATE:
But the truly "right" metod to do it turns out to be simple. Thanks to Mladen Jablanović and Christopher Creutzig from here. To make predefined method private or protected... simply redefine it:
Class Class_name
private :method_name
protected :method_name_1
end
What's important, you needn't rewrite previously defined method's logic.
You can make an existing method private:
YourClass.send(:private, :your_method)
Making the setting private does generate ActiveRecord error.
I put access control code in the overwritten method of the public setter by checking the caller:
def my_private_attribute=(val)
if (caller.first.include?('active_record/base.rb') || caller.first.include?('app/models/myclass.rb'))
self[:my_private_attribute] = val
else
raise Exception.new("Cannot set read-only attribute.")
end
end
I don't think there is 100% reliable way to do this. It's also worth checking the most popular ways to access attributes:
http://www.davidverhasselt.com/set-attributes-in-activerecord/

Resources