Permalink
Browse files

initial gem commit

  • Loading branch information...
0 parents commit 22f37d9a033823b4feff6258397b5c6ed8ef67bb @yonbergman committed Dec 20, 2011
Showing with 257 additions and 0 deletions.
  1. +5 −0 .gitignore
  2. +1 −0 .rspec
  3. +4 −0 Gemfile
  4. +5 −0 Rakefile
  5. +47 −0 Readme.md
  6. +25 −0 enumify.gemspec
  7. +3 −0 lib/enumify.rb
  8. +49 −0 lib/enumify/model.rb
  9. +9 −0 lib/enumify/railtie.rb
  10. +3 −0 lib/enumify/version.rb
  11. +104 −0 spec/enumify/enum_spec.rb
  12. +2 −0 spec/spec_helper.rb
5 .gitignore
@@ -0,0 +1,5 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
+.idea
1 .rspec
@@ -0,0 +1 @@
+--color
4 Gemfile
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in enumify.gemspec
+gemspec
5 Rakefile
@@ -0,0 +1,5 @@
+require "bundler/gem_tasks"
+require 'rspec/core/rake_task'
+
+RSpec::Core::RakeTask.new(:spec)
+task :default => :spec
47 Readme.md
@@ -0,0 +1,47 @@
+# Enumify
+
+This gem adds an enum command to all ActiveRecord models which enables you to work with string attributes as if they were enums
+
+## Installing
+
+Just add the enumify gem to your GemFile
+
+## How to use
+
+On a model that you have Lets say ***Event*** you want to add a status field
+it just accepts a call to the enum function with the field name and available values as symbols in an array
+ class Event < ActiveRecord::Base
+ enum :status, [:available, :canceled, :completed]
+ end
+
+After that you get several autogenerated commands to use with the enum
+
+ # Access through field name
+ event.status # returns the enum's current value as a symbol
+ event.status = :canceled # sets the enum's value to canceled (can also get a string)
+
+ # Shorthand methods, access through the possible values
+ event.available? # returns true if enum's current status is available
+ event.canceled! # changes the enum's value to canceled
+
+ # Get all the possible values
+ Event::STATUSES # returns all available status of the enum
+
+## Callbacks
+Another cool feature of enumify is the option to add a callback function that will be called each time the value of the field changes
+This is cool to do stuff like log stuff or create behaviour on state changes
+
+All you need to do is add a x_changed method in your class and the enumify will call it
+
+ class Event < ActiveRecord::Base
+ enum :status, [:available, :canceled, :completed]
+
+ def status_changed(old, new)
+ puts "status changed from #{old} to #{new}"
+ end
+ end
+
+
+---
+
+Copyright (c) 2011 Yonatan Bergman, released under the MIT license
25 enumify.gemspec
@@ -0,0 +1,25 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "enumify/version"
+
+Gem::Specification.new do |s|
+ s.name = "enumify"
+ s.version = Enumify::VERSION
+ s.authors = ["yon"]
+ s.email = ["yonatanbergman@gmail.com"]
+ s.homepage = "http://github.com/yonbergman/enumify"
+ s.summary = %q{Enumify adds an enum command for ActiveRecord that changes a string column/attribute to an enum}
+ s.description = %q{enumify rocks}
+
+ s.rubyforge_project = "enumify"
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+
+ # specify any dependencies here; for example:
+ s.add_development_dependency "rspec"
+ s.add_runtime_dependency "supermodel"
+ # s.add_runtime_dependency "rest-client"
+end
3 lib/enumify.rb
@@ -0,0 +1,3 @@
+require "enumify/version"
+require "enumify/model"
+require "enumify/railtie" if defined? Rails
49 lib/enumify/model.rb
@@ -0,0 +1,49 @@
+module Enumify
+ module Model
+ def enum(parameter, opts=[])
+
+ validates_inclusion_of parameter, :in => opts
+
+ const_set("#{parameter.to_s.pluralize.upcase}", opts)
+
+ define_method "#{parameter.to_s}" do
+ attr = read_attribute(parameter)
+ (attr.nil? || attr.empty?) ? nil : attr.to_sym
+ end
+
+ define_method "#{parameter.to_s}=" do |value|
+ send("_set_#{parameter.to_s}", value, false)
+ end
+
+ self.class_eval do
+
+ private
+ define_method "_set_#{parameter.to_s}" do |value, should_save|
+
+ value = value.to_sym
+ old = read_attribute(parameter) ? read_attribute(parameter).to_sym : nil
+ write_attribute(parameter, value.to_s)
+ save if should_save
+ send("#{parameter.to_s}_changed", old, value) if respond_to?("#{parameter.to_s}_changed", true) and old != value and !old.nil?
+ return value
+ end
+ end
+
+ opts.each do |opt|
+ raise "Collision in enum values method #{opt}" if respond_to?("#{opt.to_s}?") or respond_to?("#{opt.to_s}!") or respond_to?("#{opt.to_s}")
+
+ define_method "#{opt.to_s}?" do
+ send("#{parameter.to_s}") == opt
+ end
+
+ define_method "#{opt.to_s}!" do
+ send("_set_#{parameter.to_s}", opt, true)
+ end
+
+ scope opt.to_sym, where(parameter.to_sym => opt.to_s)
+ end
+
+ end
+
+ end
+end
9 lib/enumify/railtie.rb
@@ -0,0 +1,9 @@
+module UrlFormatter
+ class Railtie < Rails::Railtie
+ initializer 'enumify.model' do
+ ActiveSupport.on_load :active_record do
+ extend Enumify::Model
+ end
+ end
+ end
+end
3 lib/enumify/version.rb
@@ -0,0 +1,3 @@
+module Enumify
+ VERSION = "0.0.1"
+end
104 spec/enumify/enum_spec.rb
@@ -0,0 +1,104 @@
+require 'spec_helper'
+
+class Model < SuperModel::Base
+ include ActiveModel::Validations
+ extend Enumify::Model
+ def self.scope(name,hash={}) self end
+ def self.where(hash={}) self end
+
+ enum :status, [:available, :canceled, :completed]
+
+end
+
+describe :Enumify do
+
+ before(:each) do
+ @obj = Model.new(:status => :available)
+ end
+
+ describe "short hand methods" do
+ describe "question mark (?)" do
+ it "should return true if value of enum equals a value" do
+ @obj.available?.should be_true
+ end
+
+ it "should return false if value of enum is different " do
+ @obj.canceled?.should be_false
+ end
+
+ end
+
+ describe "exclemation mark (!)" do
+ it "should change the value of the enum to the methods value" do
+ @obj.canceled!
+ @obj.status.should == :canceled
+ end
+ end
+
+ it "should have two shorthand methods for each possible value" do
+ Model::STATUSES.each do |val|
+ @obj.respond_to?("#{val}?").should be_true
+ @obj.respond_to?("#{val}!").should be_true
+ end
+ end
+ end
+
+ describe "getting value" do
+ it "should always return the enums value as a symbol" do
+ @obj.status.should == :available
+ @obj.status = "canceled"
+ @obj.status.should == :canceled
+ end
+
+ end
+
+ describe "setting value" do
+ it "should except values as symbol" do
+ @obj.status = :canceled
+ @obj.canceled?.should be_true
+ end
+
+ it "should except values as string" do
+ @obj.status = "canceled"
+ @obj.canceled?.should be_true
+ end
+ end
+
+ describe "validations" do
+ it "should not except a value outside the given list" do
+ @obj = Model.new(:status => :available)
+ @obj.status = :foobar
+ @obj.should_not be_valid
+ end
+
+ it "should except value in the list" do
+ @obj = Model.new(:status => :available)
+ @obj.status = :canceled
+ @obj.should be_valid
+ end
+ end
+
+ describe "callbacks" do
+ it "should receive a callback on change of value" do
+ @obj.should_receive(:status_changed).with(:available,:canceled)
+ @obj.canceled!
+ end
+
+ it "should not receive a callback on initial value" do
+ @obj = Model.new
+ @obj.should_not_receive(:status_changed).with(nil, :canceled)
+ @obj.canceled!
+ end
+
+ it "should not receive a callback on value change to same" do
+ @obj.should_not_receive(:status_changed).with(:available, :available)
+ @obj.available!
+ end
+
+ end
+
+ it "class should have a CONST that holds all the available options of the enum" do
+ Model::STATUSES.should == [:available, :canceled, :completed]
+ end
+
+end
2 spec/spec_helper.rb
@@ -0,0 +1,2 @@
+require 'enumify'
+require 'supermodel'

0 comments on commit 22f37d9

Please sign in to comment.