# 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. ```ruby gem "inter" ``` Then generate and run the database migrations. ```shell rails generate inter:install rake db:migrate ``` And finally define an ActiveRecord model as an interactable. ```ruby 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. ```ruby # 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. ```ruby # 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. ```ruby # 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 ```ruby 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 ```ruby 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) ``` ### Related lookup methods Finally if 2 related models are both interactable then you can query related models by interaction status. ```ruby class Article < ActiveRecord::Base include Inter::Actable has_many :comments end class Comment < ActiveRecord::Base include Inter::Actable belongs_to :article end ``` ```ruby 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 * [Cristiano Betta](http://github.com/cbetta) - Developer Evangelist - PayPal ## Todos * Add a generator * Add tests * Add option to disable history ## License See [LICENSE](/LICENSE)