Permalink
Browse files

added macros with spec, README update and a macro enum template for g…

…enerator
  • Loading branch information...
1 parent f928d39 commit aad80299a3e2ce2f70248e4d7739a7b9b2416020 @kristianmandrup committed Aug 4, 2012
View
@@ -53,6 +53,26 @@ end
The class order will define the enum member order as well as additional ClassyEnum behavior, which is described further down in this document.
+#### Macros
+
+The `#enum` and `#enums` macro methods can be made globally available by: `require classy_enum/macros'`
+
+If you want to generate macro enums, specify the option `--macro`.
+
+```
+rails g classy_enum Priority low medium high --macro
+```
+
+The macros generates the same code (class structure) as shown above.
+
+```ruby
+enum :priority
+ enum :low
+ enum :medium
+ enum :high
+end
+```
+
### 2. Customize the Enum
The generator creates a default setup, but each enum member can be changed to fit your needs.
@@ -81,6 +101,18 @@ class Priority::High < Priority
end
```
+Similarly using macros:
+
+```ruby
+enum :priority
+ enum :High do
+ def send_email?
+ true
+ end
+ end
+end
+```
+
### 3. Setup the ActiveRecord model
My ActiveRecord Alarm model needs a text field that will store a string representing the enum member. An example model schema might look something like:
View
@@ -20,4 +20,5 @@ Gem::Specification.new do |gem|
gem.add_development_dependency('sqlite3', '~> 1.3.6')
gem.add_development_dependency('json', '~> 1.6.5')
+ gem.add_development_dependency("RubyInline", "~> 3.11.3")
end
View
@@ -15,6 +15,7 @@ def inherited(klass)
if self == ClassyEnum::Base
klass.base_class = klass
else
+ return unless klass.name
# Ensure subclasses follow expected naming conventions
unless klass.name.start_with? "#{base_class.name}::"
@@ -1,5 +1,7 @@
module ClassyEnum
module Collection
+ extend ActiveSupport::Concern
+
# Sort an array of elements based on the order they are defined
#
# ==== Example
@@ -22,10 +24,6 @@ def <=> other
index <=> other.index
end
- def self.included(klass)
- klass.extend ClassMethods
- end
-
module ClassMethods
def inherited(klass)
if self == ClassyEnum::Base
View
@@ -0,0 +1,49 @@
+def enum name, parent_class = nil, &block
+ clazz_name = name.to_s.camelize
+ context = self.inspect == 'main' ? Object : self
+ parent_class ||= context unless context == Object
+ parent_class ||= ::ClassyEnum::Base
+
+ klass = Class.new(parent_class)
+
+ context.const_set clazz_name, klass
+
+ klass = context.const_get(clazz_name)
+ unless context == Object
+ klass.class_eval do
+ if parent_class == ClassyEnum::Base
+ klass.class_attribute :enum_options
+ klass.enum_options = []
+ else
+ enum_options << klass
+ klass.instance_variable_set('@index', enum_options.size)
+ end
+
+
+ # Add visit_EnumMember methods to support validates_uniqueness_of with enum field
+ # This is due to a bug in Rails where it uses the method result as opposed to the
+ # database value for validation scopes. A fix will be released in Rails 4, but
+ # this will remain until Rails 3.x is no longer prevalent.
+ if defined?(Arel::Visitors::ToSql)
+ Arel::Visitors::ToSql.class_eval do
+ define_method "visit_#{klass.name.split('::').join('_')}", lambda {|value| quote(value.to_s) }
+ end
+ end
+
+ # Convert from MyEnumClass::NumberTwo to :number_two
+ enum = name
+
+ ClassyEnum::Predicate.define_predicate_method(klass, enum)
+
+ klass.instance_variable_set('@option', enum)
+ end
+ end
+
+ if block_given?
+ klass.class_eval &block
+ end
+end
+
+def enums *names
+ names.each {|name| enum name }
+end
@@ -3,12 +3,22 @@ class ClassyEnumGenerator < Rails::Generators::NamedBase
argument :name, :type => :string, :required => true, :banner => 'EnumName'
argument :values, :type => :array, :default => [], :banner => 'value1 value2 value3 etc...'
+ class_option :macro, :type => :boolean, :default => false, :banner => 'Generate enums as macro calls'
source_root File.expand_path("../templates", __FILE__)
def copy_files # :nodoc:
empty_directory 'app/enums'
- template "enum.rb", "app/enums/#{file_name}.rb"
+ template "#{template_name}.rb", "app/enums/#{file_name}.rb"
end
+ protected
+
+ def template_name
+ macro? 'macro_enum' : 'enum'
+ end
+
+ def macro?
+ options[:macro]
+ end
end
@@ -0,0 +1,7 @@
+require 'classy_enum/macros'
+
+enum :<%= class_name.underscore %>
+<% values.each do |arg| %>
+ enum :<%= arg.underscore %>
+<%- end -%>
+end
@@ -0,0 +1,40 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+require 'classy_enum/macros'
+
+enum :priority do
+ enum :one do
+ def send_email?
+ true
+ end
+ end
+
+ enums :two, :three
+end
+
+describe ClassyEnum::Base do
+ context '.build' do
+ context 'invalid option' do
+ it 'should return the option' do
+ Priority.build(:invalid_option).should == :invalid_option
+ end
+ end
+
+ context 'string option' do
+ subject { Priority.build("one") }
+ it { should be_a(::Priority::One) }
+ end
+
+ context 'symbol option' do
+ describe 'two' do
+ subject { Priority.build(:two) }
+ it { should be_a(::Priority::Two) }
+ end
+
+ describe 'three' do
+ subject { Priority.build(:three) }
+ it { should be_a(::Priority::Three) }
+ end
+ end
+ end
+end

0 comments on commit aad8029

Please sign in to comment.