Inter

Easily add arbitrary attributes to an ActiveRecord model on the fly, without the need of any extra new database columns or migrations.

Why?

Ever added a 5th boolean to a model just so you could keep track if something had happened to this user, article, etc? With Inter you can add any arbitrary attributes to any ActiveRecord model on the fly without the need to add yet another database migration. Instead all interactions are stored in a seperate table and hooked up using polymorphism. Using a simple DSL you can then quickly manipulate and query interactions.

Benefits:

  • Cleaner models
  • Simple and powerful DSL
  • More lightweight than a full state machine
  • Can store any value

Usage

Dependencies

  • >= Ruby 2.0

Installation

Add the gem to your Gemfile.

gem "inter"

Then generate and run the database migrations.

rails generate inter:install
rake db:migrate

And finally define an ActiveRecord model as an interactable.

class Article < ActiveRecord::Base
  include Inter::Actable
end

Basic usage

Instance methods

Setting a value on an Article is now easy. Just pick a key name and set it to a desired value.

# simple boolean setting and getting2
article.is_published! # sets the published interaction to true
article.isnt_published! # sets the published interaction to false
article.is_published? #=> false

# you can also combine keys
article.is_published_and_not_promoted! #=> nil
article.is_published_and_promoted? #=> false
article.is_published_and_not_promoted? #=> true

# more complex values
article.set :tags, [:ruby, :rails, :gem] # or article.set "tags", [:ruby, :rails, :gem]
article.get :tags #=> ["ruby", "rails", "gem"]
article.has? :tags #=> true
article.set :tags, nil
article.has? :tags #=> false

Class methods

You can query interactable objects in a similar fasion.

# similar to instance methods there are simple lookup methods
Article.is_published #=> all articles for which published == true
Article.isnt_published #=> all articles for which published != true

# again you can combine interactions
Article.is_published_and_promoted #=> all promoted and published articles

Interaction history

One of the advantages of Interactions is that they are kept as a permanent log. Thefefore you can easily look up if an interaction has ever happened in the past.

# look up if an article has ever been published
article.was_published? #=> false
article.is_published! #=> nil
article.isnt_published! #=> nil
article.is_published? #=> false
article.was_published? #=> true
article.wasnt_published? #=> false

# again you can combine keys
article.was_published_and_promoted? #=> false

# to fully inspect the log you need to dig a bit deeper
# and access the interactions directly
article.history :published #=> all interactions that match this key
article.history "published"
article.history :published, :promoted
article.history ["published", "promoted"]

Advanced usage

A lot of the magic methods have underlying dumb methods that can be used too to further customize your needs.

Instance methods

article.is "published" #=> nil
article.is :published, :promoted #=> nil
article.is [:published, :not_promoted] #=> nil
article.is? :published, :promoted #=> false
article.is? [:published, :not_promoted] #=> true
article.get :promoted #=> true

Class methods

Article.inter(published: true) # loads all articles for which the current published state us true
Article.inter(published: true).first # lazy loads the latest article
# You can also get access to the raw interactions
Article.interactions(published: true)

Finally if 2 related models are both interactable then you can query related models by interaction status.

class Article < ActiveRecord::Base
  include Inter::Actable
  has_many :comments
end

class Comment < ActiveRecord::Base
  include Inter::Actable
  belongs_to :article
end
article.comments.first.is_spam! #=> nil
article.spam_comments #=> all comments with an interaction of "spam" set to true
article.not_spam_comments #=> all comments with an interaction of "spam" != true
article.spam_comments_ids #=> an array of IDs
article.spam_comments_count #=> the length of all the comments that match the criteria

# all of this maps down to the known pattern
article.comments.inter(spam: true)

Changelog

  • 0.0.2 Copied over implementation
  • 0.0.1 Gem skeleton

Contributors

Todos

  • Add a generator
  • Add tests
  • Add option to disable history

License

See LICENSE