How to monkey patch alias_attributes - ruby-on-rails

I have a database with a field 'update' corresponding to updated_at.
The database is not only used by a rails application and it's not possible to migrate the field currently.
In my model, I want to make an alias on the attribute :
class Model < ActiveRecord::Base
alias_attribute 'updated_at', 'update'
...
end
But this call the update method of the model.
So i found the alias_attribute definition here : github code
I want to monkey patch this for something like :
def self.alias_attribute(new_name, old_name)
module_eval <<-STR, __FILE__, __LINE__ + 1
def #{new_name}; self.attributes[#{old_name}]; end
def #{new_name}?; self.attributes[#{old_name}?; end
def #{new_name}=(v); self.attributes[#{old_name} = v; end
STR
end
How can i make this work?

Related

Is there are way that model methods can work with variable with out them being explicitly passed?

I know this sounds like a ridiculous question but I trying to solve a chalange given by an potential employer. I have a schema and a couple of models with their methods. Almost all the methods have no variables passed in. Meaning none of the methods look like this:
def this_is_my_method(variable)
#stuff
end
or
def this_is_my_method variable
#stuff
end
but there are methods that are clearly working with variables like this:
def build_address
if variable
# do something
end
end
Is there a RoR way that a model method will just know about certain parameters or variables in certain situations?
So if my controller was recieving params that looked like this:
?my_method[begin]=1&my_method[end]=5
would the model method "my_method" know what "begin" and "end" where?
def my_method
if self.begin == self.end
# do something
else
# do something else
end
end
Remember that a model method has access to all the attributes (and other methods) of that model instance.
So (for example) this would be a valid model method.
class User < ActiveRecord::Base
def full_name
[first_name, last_name].join(' ')
end
end
This is taking an attribute user.first_name and an attribute user.last_name and combining them to create a new method user.full_name
EDIT as #Sanket has suggested you can pass values into a model if you make them attribute accessor...
def SomeController < ApplicationController
def some_controller_method
#user = User.find(params[:id])
#user.begin = params[:begin]
#user.end = params[:end]
#user.some_model_method
end
end
def User < ActiveRecord::Base
attr_accessor :begin, :end
def some_model_method
if self.begin == self.end
# do something
else
# do something else
end
end
end
Although to be frank I'd rather just pass the values in as method arguments.

How to put these 2 similar methods into one module in my Rails app?

I have 2 models using 2 similar methods. Both of them called are generate and both are called by before_save. I want to refactor them into one. How can I do that?
#model1's generate
before_save :generate
def generate
self.slug = self.title.gsub(' ', '-').downcase
end
#model2‘s generate
before_save :generate
def generate
self.slug = self.name.gsub(' ', '-').downcase
end
Use ActiveSupport::Concern:
module Slugged
extend ActiveSupport::Concern
included do
before_save :generate
end
def slugged_attr
name
end
def generate
self.slug = self.slugged_attr.gsub(' ', '-').downcase
end
end
And then include it into your models.
include Slugged
Add following method to your first model. It will overwrite slugged_attr in concern:
def slugged_attr
title
end

Mark ActiveRecord attribute as html_safe

We have an ActiveRecord model with an html attribute (say Post#body). Is there a nice way that calling body on a post returns an html_safe? string? E.g.:
class Post < ActiveRecord::Base
# is_html_escaped :body or somesuch magic
end
Post.first.body.html_safe? # => true
The problem otherwise is that we have to call raw everything we show that field.
Here's a way I found:
class Post < ActiveRecord::Base
def message
super.html_safe
end
def message=(new_mess)
new_mess = ERB::Util.html_escape(new_mess.sanitize) unless new_mess.html_safe?
super(new_mess)
end
end
FYI. I made a module for this
module SanitizeOnly
def self.included(mod)
mod.extend(ClassMethods)
end
module ClassMethods
def sanitize_on_input_only(*attribute_names)
attribute_names.map(&:to_s).each do | attribute_name |
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{attribute_name}
super.html_safe
end
def #{attribute_name}=(new_val)
new_val = ERB::Util.html_escape(new_val.sanitize) unless new_val.html_safe?
super(new_val)
end
RUBY
end
end
end
end
to use it just include it in your model and add the attributes you want to avoid using raw for to a sanitize_on_input_only line like the following:
sanitize_on_input_only :message, :another_attribute, ...

How to instantiate a model object with attributes?

I would like to instantiate a model object specifying some attributes. For instance
post = Post.new
should set post.vote_total to 0. I tried to do this in the initialize method but it seems it's not working :
def initialize()
vote_total=0
end
thank you in advance.
Pass an attributes hash to the object, as in:
post = Post.new(:vote_total => 123, :author => "Jason Bourne", ...)
If you're new to Ruby on Rails, you'll probably want to read the Getting Started Guide, which covers this and many more useful Rails idioms in some detail.
I would use callbacks:
Available Callbacks
class Post
before_save :set_defaults
def set_defaults
self.vote_total ||= 0
#do other stuff
end
end
You could allow the database to store default values for you
class AddColumnWithDefaultValue < ActiveRecord::Migration
def change
add_column :posts, :vote_total, :integer, :default => 0
end
end
Or for an existing table:
class ChangeColumnWithDefaultValue < ActiveRecord::Migration
def up
change_column_default(:posts, :vote_total, 0)
end
def down
change_column_default(:posts, :vote_total, nil)
end
end
There's a lot of debate about storing default values in the database though.

Virtual attributes in plugin

I need some help with virtual attributes. This code works fine but how do I use it inside a plugin. The goal is to add this methods to all classes that uses the plugin.
class Article < ActiveRecord::Base
attr_accessor :title, :permalink
def title
if #title
#title
elsif self.page
self.page.title
else
""
end
end
def permalink
if #permalink
#permalink
elsif self.page
self.page.permalink
else
""
end
end
end
Thanks
You can run the plugin generator to get started.
script/generate plugin acts_as_page
You can then add a module which defines acts_as_page and extends it into all models.
# in plugins/acts_as_page/lib/acts_as_page.rb
module ActsAsPage
def acts_as_page
# ...
end
end
# in plugins/acts_as_page/init.rb
class ActiveRecord::Base
extend ActsAsPage
end
This way the acts_as_page method is available as a class method to all models and you can define any behavior into there. You could do something like this...
module ActsAsPage
def acts_as_page
attr_writer :title, :permalink
include Behavior
end
module Behavior
def title
# ...
end
def permalink
# ...
end
end
end
And then when you call acts_as_page in the model...
class Article < ActiveRecord::Base
acts_as_page
end
It will define the attributes and add the methods. If you need things to be a bit more dynamic (such as if you want the acts_as_page method to take arguments which changes the behavior) try out the solution I present in this Railscasts episode.
It appears that you want a Module for this
# my_methods.rb
module MyMethods
def my_method_a
"Hello"
end
end
The you want to include it into the classes you want to use it for.
class MyClass < ActiveRecord::Base
include MyMethods
end
> m = MyClass.new
> m.my_method_a
=> "Hello!"
Take a look here for more information on mixing in modules. You can put the module wherever in a plugin if you like, just ensure its named correctly so Rails can find it.
Create a module structure like YourPlugin::InstanceMethods and include it this module like this:
module YourPlugin
module InstanceMethods
# your methods
end
end
ActiveRecord::Base.__send__(:include, YourPlugin::InstanceMethods)
You have to use __send__ to make your code Ruby 1.9 compatible. The __send__ line is usually placed at the init.rb file on your plugin root directory.

Resources