Austin Story

Ruby, Rails and Javascript Blog

Powered by Genesis

Creating private_attr_reader in Ruby

February 16, 2016 By Austin Story Leave a Comment

In my current project we use a whole lot of plain ruby objects. One day I was looking at them and noticed that we follow this pattern for just about everything.
[ruby]
module MyModule
class MyClass
attr_reader :params

def initialize(params:)
@params = params
end
def call
params.do_things
end
end
end
[/ruby]

The params attr_reader is used so that we don’t have to use @params throughout the object. I got used to this format and like that my ide looks better without all the @vars everywhere. But I think that it is a false contract. The public method for our objects is `call` and is all that we ever intend for someone else to use. However, the user of attr_reader says that this is a public method.

To get around this, i decided to add a quick patch so that we could still have the use of the params method locally for instance variable lookup but keep it private so that noone accidentally starts using it only for us to change it in the future.

Here is the patch I added to Object. I didn’t go all the way down to Kernel because all of our objects inherit from Object.

[ruby]
class Object
def self.private_attr_reader(*args)
args.each do |arg|
define_method(arg) { instance_variable_get("@#{arg}") }
private arg
end
end
end
[/ruby]

After adding this patch to Object we can now do this in our object and var1 and var2 would both be have attr_readers that are private to the class.

[ruby]
private_attr_reader :var1, var2
[/ruby]

Filed Under: Ruby, Ruby on Rails Tagged With: Metaprogramming, monkey patching, ruby

Ruby/Rails Metaprogramming – Creating a custom alerting module

April 19, 2015 By Austin Story Leave a Comment

I recently needed to be able to dispatch some very context specific alerts when some of my classes are saved. However, the alert and who gets it are completely different based on the class and the context of quite a bit of business logic. This could be accomplished is several ways, I will explain these and then show how i accomplished it with metaprogramming and modules.

The most straightforward way to have done this would be to just add an alert method in each class and then implement the logic there. There are several reasons to shy away from this including polluting your model, strong coupling and harder testing. However, I shy away from that because it promotes bad coding. Each time you tack on just one more method to a class it makes it that much easier to do it again. I’m not saying never just add a method, but think about whether it could be it’s own thing first.

The next would be to have a giant class that handle the logic. This would be better because all the logic for alerting in the app would be in one spot. However, the logic would have several branches (i.e is this a comment or an image or a new post?).

Ultimately i created an alertable module that I included in my models.

[ruby]module Alertable
extend ActiveSupport::Concern

included do
after_save :alert
end

def alert
"Alerts::#{self.class}".constantize.new(self).call
end

end

class Comment < ActiveRecord::Base
include alertable
end
[/ruby]

From the top, ActiveSupport::Concern gives me both class level methods (after_save) and instance methods (def alert). Pretty neat.

The alert method is a bit of metaprogramming that allows me to put the alert logic in it’s own module namespace (aka directory) like this

models/comments.rb #Normal ActiveRecord Model Location
models/alerts/comments.rb #our new Module

Because alertable is include in Comment, when a comment is saved it now does a callback for the method ‘alert’. That is also included from Alertable so after it sees that there are no alert methods defined in the Comment class it calls “Alerts::Comment”.constantize.new(self).call Which is located in models/alerts/comments.rb

The comments.rb file under models/alerts will look something like this.

[ruby]
module Alerts
class Comment
def initialize(comment)
@comment = comment
end

def call
#Do alerting stuff
end

end
end
[/ruby]

So now any time that i need to do some alerting in my program, i include Alertable and then i can either implement the alert method on the class i’m including it into or (preferably) create a new Alerts::#{class_name} file.

I think this setup is better than the 2 previously mentioned because everything that is related to alerting is not located in a single area of the application. The alerts are their own thing in their own area of the application. The downside is that we have callbacks for the Comment class that are completely encapsulated in a module. So that could cause trouble for newer programmers to the project or if the alert ever returns falsy it would halt the save of our comment and not be immediately apparent as to why it was failing.

Filed Under: Ruby, Ruby on Rails Tagged With: Metaprogramming, Modules, Rails, ruby

Categories

  • AngularJS
  • Books
  • Devise
  • Elasticsearch
  • ES6
  • Information Security
  • Integrations
  • Javascript
  • Linux
  • Minitest
  • PhoneGap
  • Programming
  • React
  • Redux
  • Ruby
  • Ruby on Rails
  • Stripe
  • Testing
  • Theory
  • TypeScript
  • Uncategorized
  • Vue
  • Webpack