Skip to content

Commit

Permalink
Changed ClassyEnum to use inheritance and a behave like inherited obj…
Browse files Browse the repository at this point in the history
…ects
  • Loading branch information
beerlington committed Nov 14, 2010
1 parent 9359175 commit 1c94235
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 68 deletions.
8 changes: 2 additions & 6 deletions README.textile
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ rails g classy_enum AlarmPriority low medium high
A new file will be created at app/enums/alarm_priority.rb that will look like:

<pre>
class AlarmPriority
extend ClassyEnum

class AlarmPriority < ClassyEnum::Base
enum_classes :low, :medium, :high
end

Expand All @@ -54,9 +52,7 @@ That is the default setup, but can be changed to fit your needs, like so...
Using the @enum_classes@ method, I have defined three priority levels: low, medium, and high. Each priority level can have different properties and methods associated with it. In my example, each enum class has a method called @email?@. By default this method returns false, but is overridden for high priority alarms and returns true.

<pre>
class AlarmPriority
extend ClassyEnum

class AlarmPriority < ClassyEnum::Base
enum_classes :low, :medium, :high

def email?
Expand Down
4 changes: 1 addition & 3 deletions generators/templates/enum.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
class <%= class_name %>
extend ClassyEnum

class <%= class_name %> < ClassyEnum::Base
enum_classes <%= args.map {|a| ":#{a}"}.join(", ") %>
end
<% args.each do |arg| %>
Expand Down
63 changes: 29 additions & 34 deletions lib/classy_enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,65 +7,60 @@

module ClassyEnum

def enum_classes(*options)
self.send(:attr_reader, :enum_classes)
class Base
def self.enum_classes(*options)
self.send(:attr_reader, :enum_classes)

self.const_set("OPTIONS", options) unless self.const_defined? "OPTIONS"
self.const_set("OPTION_HASH", Hash.new) unless self.const_defined? "OPTION_HASH"
self.const_set("OPTIONS", options) unless self.const_defined? "OPTIONS"

self.extend ClassMethods
self.extend ClassMethods

options.each do |option|
options.each_with_index do |option, index|

klass = Class.new(self) do
attr_reader :to_s, :to_sym, :index

klass = Class.new(self) do
self.send(:attr_reader, :to_s, :to_sym, :index)

def initialize(option, index)
@to_s = option.to_s.downcase
@to_sym = @to_s.to_sym
@index = index + 1
end
@option = option

def name
to_s.titleize
end
def initialize
@to_s = self.class.instance_variable_get('@option').to_s
@to_sym = @to_s.to_sym
@index = self.class.instance_variable_get('@index')
end

def <=> other
@index <=> other.index
def name
@to_s.titleize
end

def <=> other
@index <=> other.index
end
end

klass_name = "#{self}#{option.to_s.camelize}"
Object.const_set(klass_name, klass) unless Object.const_defined? klass_name
end

Object.const_set("#{self}#{option.to_s.camelize}", klass)

instance = klass.new(option, options.index(option))

self::OPTION_HASH[option] = instance
end
end

# Added to give someone the option to extend or include with same functionality
def self.included(klass)
klass.send(:extend, ClassyEnum)
end

module ClassMethods

def build(option)
return nil if option.nil?
self::OPTION_HASH[option.to_sym] || TypeError.new("Valid #{self} options are #{self.valid_options}")
return TypeError.new("Valid #{self} options are #{self.valid_options}") unless self::OPTIONS.include? option.to_sym
Object.const_get("#{self}#{option.to_s.camelize}").new
end

# Alias of new
def find(option)
build(option)
end
def find(option); build(option); end;

def all
self::OPTIONS.map {|e| build(e) }
end

# Uses the name field for select options
def all_with_name
def select_options
all.map {|e| [e.name, e.to_s] }
end

Expand Down
4 changes: 2 additions & 2 deletions lib/classy_enum/semantic_form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ def enum_select_input(method, options)

if enum_class.nil?
enum_class = method.to_s.capitalize.constantize rescue Error.invalid_classy_enum_object(method)
options[:collection] = enum_class.all_with_name
options[:collection] = enum_class.select_options
else
Error.invalid_classy_enum_object unless enum_class.respond_to? :enum_classes
options[:collection] = enum_class.class.superclass.all_with_name
options[:collection] = enum_class.class.superclass.select_options
options[:selected] = enum_class.to_s
end

Expand Down
4 changes: 1 addition & 3 deletions lib/generators/classy_enum/templates/enum.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
class <%= class_name %>
extend ClassyEnum

class <%= class_name %> < ClassyEnum::Base
enum_classes <%= values.map {|a| ":#{a}"}.join(", ") %>
end
<% values.each do |arg| %>
Expand Down
29 changes: 12 additions & 17 deletions spec/classy_enum_spec.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')

class TestEnum
extend ClassyEnum

class TestEnum < ClassyEnum::Base
enum_classes :one, :two, :three

def self.test_class_method?
Expand All @@ -24,13 +22,6 @@ def test_instance_method?
end
end

# Used to assert include and extend behave identically
class IncludedEnum
include ClassyEnum

enum_classes :one, :two, :three
end

describe TestEnum do

TestEnum::OPTIONS.each do |option|
Expand All @@ -40,11 +31,11 @@ class IncludedEnum
end

it "should return an array of enums" do
TestEnum.all.should == TestEnum::OPTIONS.map {|o| TestEnum.build(o) }
TestEnum.all.map(&:class).should == [TestEnumOne, TestEnumTwo, TestEnumThree]
end

it "should return an array of enums for a select tag" do
TestEnum.all_with_name.should == TestEnum::OPTIONS.map {|o| [TestEnum.build(o).name, TestEnum.build(o).to_s] }
TestEnum.select_options.should == TestEnum::OPTIONS.map {|o| [TestEnum.build(o).name, TestEnum.build(o).to_s] }
end

it "should return a type error when adding an invalid option" do
Expand Down Expand Up @@ -77,15 +68,19 @@ class IncludedEnum
TestEnum.find("one").class.should == TestEnumOne
end

it "should work with include ClassyEnum" do
IncludedEnum.build(:one).to_s.should == TestEnum.build(:one).to_s
end

end

describe "A ClassyEnum instance" do
before { @enum = TestEnum.build(:one) }

it "should build a TestEnum class" do
@enum.class.should == TestEnumOne
end

it "should instantiate a member" do
TestEnumOne.new.should be_a(TestEnumOne)
end

it "should be a TestEnum" do
@enum.should be_a(TestEnum)
end
Expand Down Expand Up @@ -113,7 +108,7 @@ class IncludedEnum
it "should create the same instance with a string or symbol" do
@enum_string = TestEnum.build("one")

@enum.should == @enum_string
@enum.class.should == @enum_string.class
end
end

Expand Down
4 changes: 1 addition & 3 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@
end
end

class Breed
extend ClassyEnum

class Breed < ClassyEnum::Base
enum_classes :golden_retriever, :snoop
end

Expand Down

0 comments on commit 1c94235

Please sign in to comment.