How Do I Add Methods To ActiveRecord::Base? - ruby-on-rails-3

I'm trying to create a customized ActiveRecord::Base that includes additional metadata about the connection. I see two ways to go about this:
1.) Inherit from ActiveRecord::Base and add methods & fields in this subclass.
2.) Encapsulate an ActiveRecord::Base object inside my own class
1 has all kinds of problems with the inability to override initialize, weird problems where it doesn't seem to have custom methods I've added, etc.
undefined method `set_profile' for #<Class:0xf041f0>
2 I have not been able to figure out, due to problems with using ActiveRecord::Base.new
I am trying to make an all-purpose ActiveRecord class that I can dynamically establish_connection & set_table_name on, (i.e. not have one underlying table that this ActiveRecord::Base represents) but I can't seem to find a way to accomplish it. Any ideas?
This works:
class MyTable < ActiveRecord::Base
establish_connection $config['custom-db-config'];
set_table_name 'MY_TABLE'
end
but I need a class I can call these things on repeatedly.

Not entirely sure why you'll want that, but maybe you can try this?
module ActiveRecord
class Base
def self.your_method
# implementation goes here
end
end
end
You will need to save this file and put it in config/intializers.

You can also extend the ActiveRecord::Base class in order to add the those methods dynamically which are directly callable by the class inheriting the ActiveRecord::Base...Many acts_as plugins are defined and made according to this practice...

Related

get all records via operation and show in view

I have trouble with trailblazer when setting up a simple show all Things view.
operation
class Thing < ApplicationRecord
class ShowAll < Trailblazer::Operation
include Model
model Thing, :all #why :all is not working here?
def process
end
end
end
controller
class PageController < ApplicationController
def index
run Word::ShowAll
end
end
why is :all not working for getting all Things from the db but :find works to get one via its id?
The best place to ask TRB questions is actually on Github channel.
I'm not sure where you found that example, as it is not supposed to work AFAIK, :find is a shortcut I believe, I've never actually used it.
All your logic should be defined inside the process method. http://trailblazer.to/gems/operation/1.1/api.html#process
Having said that, trying to get all records without some kind of pagination is a really bad idea, unless you are 100% sure that your table won't grow beyond several dozen records. Unless you know you don't have a large load. So to define that kind of shortcut is dangerous.
Calling Trailblazer::Model#model as you're doing there is just a shortcut for overriding the TrailBlazer::Operaration#model! method. So what you seem to want to do is:
class Thing < ApplicationRecord
class ShowAll < Trailblazer::Operation
def model!(params)
Thing.all # add any filtering or pagination here
end
end
end
And in your controller call present instead of run so that it sets up the model but doesn't call the process method of the operation.
class PageController < ApplicationController
def index
present Word::ShowAll
end
end

uninitialized constant in rails?

So I've built a new controller = "Categories_controller.rb" and a new Model = "Category.rb" and now I would normally take my Savedfriend.rb model and use it with Category.rb model like so;
<%= category.savedfriends.size %>
However this time around I keep getting;
uninitialized constant Category::Savedfriend
It's driving me crazy. I do have models all set with belongs_to.
By Rails convention, if you haven't specified your class_name on the association, it is going to look for a singularized, camelized version of the association name for the class name. If, for instance, you have a model SavedFriend, your association should be named saved_friends. If it can't find the class for the association, Rails tends to look for a scoped class within the class that's trying to call it. The error is a little obscure, but I've seen it plenty of times when I have a typo in my associations.
# in app/models/saved_friends.rb
class SavedFriend < ActiveRecord::Base
belongs_to :category
end
# in app/models/category.rb
class Category < ActiveRecord::Base
has_many :saved_friends
end
Also, if your naming scheme for files and classes is as sporadic as it is in your question, you're going to have a bad time. File names should be lowercase and underscored, class names should be a camelized version of the file name. i.e. Categories_controller.rb should be categories_controller.rb, and the class should be CategoriesController. Similarly, saved_friend.rb should contain class SavedFriend.

Writing a custom attr_special function in Rails 3

Apologies in advance - I'm very much a learner, and writing projects as a means to learn. In this one, I'm trying to extend ActiveRecord so that I can do the following...
In my model definition, call ...
attr_special :field, :field
Then, elsewhere, be able to access this list through something like
Model.special_attributes
Probably something really obvious. I'm fine extending ActiveRecord, but I'm not even sure what I'm searching for for guidance beyond this (constructor?)...
You can define something like the code below to create custom DSL in your models:
module SpecialAttributes
module ClassMethods
def attr_special(*attrs)
class_attribute :special_attributes
self.special_attributes = attrs
end
def special_attributes
self.special_attributes
end
end
module InstanceMethods
# some code here
end
def self.included(base)
base.extend ClassMethods
base.send :include, InstanceMethods
end
end
class ActiveRecord::Base
include SpecialAttributes
end
I reopen ActiveRecord::Base class instead of use inheritance because it's more common in Ruby that inheritance.
I like to use submodules called ClassMethods and InstanceMethods in my module and use self.included method to add them in the base class. So you can use 'include MyModule' without have to know if you add instance or class methods.
I hope I can help you.

avoiding code duplication in Rails 3 models

I'm working on a Rails 3.1 application where there are a number of different enum-like models that are stored in the database. There is a lot of identical code in these models, as well as in the associated controllers and views. I've solved the code duplication for the controllers and views via a shared parent controller class and the new view/layout inheritance that's part of Rails 3.
Now I'm trying to solve the code duplication in the models, and I'm stuck. An example of one of my enum models is as follows:
class Format < ActiveRecord::Base
has_and_belongs_to_many :videos
attr_accessible :name
validates :name, presence: true, length: { maximum: 20 }
before_destroy :verify_no_linked_videos
def verify_no_linked_videos
unless self.videos.empty?
self.errors[:base] << "Couldn't delete format with associated videos."
raise ActiveRecord::RecordInvalid.new self
end
end
end
I have four or five other classes with nearly identical code (the association declaration being the only difference). I've tried creating a module with the shared code that they all include (which seems like the Ruby Way), but much of the duplicate code relies on ActiveRecord, so the methods I'm trying to use in the module (validate, attr_accessible, etc.) aren't available. I know about ActiveModel, but that doesn't get me all the way there.
I've also tried creating a common, non-persistent parent class that subclasses ActiveRecord::Base, but all of the code I've seen to accomplish this assumes that you won't have subclasses of your non-persistent class that do persist.
Any suggestions for how best to avoid duplicating these identical lines of code across many different enum models?
I found a solution to the code sharing for Rails 3 models, so thought I'd share it with others. It turns out ActiveModel does have everything I need (so far, at least). I created an Enum module using ActiveSupport::Concern, ActiveModel::Validations, and ActiveModel::MassAssignmentSecurity, and I include the module in each of my enum models:
module Enum
extend ActiveSupport::Concern
include ActiveModel::Validations
include ActiveModel::MassAssignmentSecurity
included do
attr_accessible :name
validates :name, presence: true, length: { maximum: 20 }
before_destroy :verify_no_linked_videos
private
def verify_no_linked_videos
unless self.videos.empty?
self.errors[:base] << "Couldn't delete object with associated videos."
raise ActiveRecord::RecordInvalid.new self
end
end
end
end
The way the Rails 3 team pulled out the non-database code from ActiveRecord into ActiveModel really is pretty slick! The following links helped solidify my understanding of how to use this stuff:
http://www.fakingfantastic.com/2010/09/20/concerning-yourself-with-active-support-concern/
http://asciicasts.com/episodes/237-dynamic-attr-accessible

Whenever a User object is created, create UserInfo object too

Whenever a User object is created, I want a UserInfo object to be created too, and linked to it.
Unfortunately this does not create any UserInfo:
class User < ActiveRecord::Base
has_one :user_info
...
def init
self.user_info = user_info
self.save!
end
Why is the init method not called? How to reach my goal?
sombe's technique is right, but his details aren't ideal. In fact, since create_user_info is already a method on User instances, all you want is something like:
class User < ActiveRecord::Base
has_one :user_info
before_create :create_user_info
end
Edit: init doesn't do anything particularly magical under Rails (I... don't think it does under basic Ruby either - are you thinking of initialize? I'll assume you are). initialize is fired off when an instance of the Ruby class is created in memory. That's divorced by quite some margin from an instance of the model being created in the database; a new class instance could be due to you calling build (and not saving yet), or even due to reading an instance out of the database.
If you want to step in on database operations, you need to make use of the ActiveRecord callbacks. You might find my answer to this question useful.
before_save callback triggers on create and update.
I'd suggest to use after_create because before_create can return errors
class User < ActiveRecord::Base
has_one :user_info
...
after_create do
create_user_info
end
In your User model, use a before_save filter instead of init like this:
before_save :create_user_info
...
private
def create_user_info
user_info = UserInfo.new
if user_info.save
self.user_info_id = user_info.id
end
end