Thursday, March 27, 2014

Ruby SuperModule Comes To The Rescue!!

Tired of ActiveSupport::Concern and Ruby's modules not allowing you to mix in class methods easily? Well, worry no more! SuperModule comes to the rescue!



SuperModule allows defining class methods and method invocations the same way a super class does without using def included(base).

This succeeds ActiveSupport::Concern by offering lighter purely idiomatic Ruby syntax and simpler module dependency support.

1. Just include SuperModule at the top of a module definition:
module UserIdentifiable
  include SuperModule

  belongs_to :user
  validates :user_id, presence: true

  def self.most_active_user
    User.find_by_id(select('count(id) as head_count, user_id').group('user_id').order('count(id) desc').first.user_id)
  end

  def slug
    "#{self.class.name}_#{user_id}"
  end
end

2. Mix newly defined module into a class or another super module
class ClubParticipation < ActiveRecord::Base
  include UserIdentifiable
end
class CourseEnrollment < ActiveRecord::Base
  include UserIdentifiable
end
module Accountable
  include SuperModule
  include UserIdentifiable
end
class Activity < ActiveRecord::Base
  include Accountable
end

3. And start using by invoking class methods or instance methods
CourseEnrollment.most_active_user
ClubParticipation.most_active_user
Activity.last.slug
ClubParticipation.create(club_id: club.id, user_id: user.id).slug
CourseEnrollment.new(course_id: course.id).valid?

More details and examples are available over here: https://github.com/AndyObtiva/super_module

And the SuperModule RubyGem lives over here: http://rubygems.org/gems/super_module

Enjoy!!

2 comments:

Henrik N said...

Cool idea and clever implementation.

I wrote something with a similar "just write it as you would inline" API a few years back. Slightly more boilerplate; simpler implementation:

https://github.com/henrik/augmentations

Annas "Andy" Maleh said...

Thanks for sharing. Interesting indeed. Reminds me a bit of JavaScript's Stampit library though applied in Ruby. Thought I'd mention in case you'd like inspiration for newer features.

By the way, I just released an improved SuperModule v1.1.0 yesterday. Check it out: http://andymaleh.blogspot.ca/2015/04/supermodule-v110-is-new-sherif-in-town.html

SuperModule's goal is just focused on making modules simply includable without any boilerplate ritual, a smaller scope than Stampit's grand ideas, but a good step forward nonetheless that is a lot easier to digest by developers.

Banister's include_complete came really close to addressing my concern, but not quite all the way as mentioned in the gotchas I talk about in this Reddit post: http://www.reddit.com/r/ruby/comments/30j66y/step_aside_activesupportconcern_supermodule_is/

In any case, I'd like to hear back if you end up using SuperModule on projects.

Cheers...