duelinmarkers-renum 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/License.txt +20 -0
- data/README.textile +120 -0
- data/Rakefile +21 -0
- data/VERSION +1 -0
- data/lib/renum/enumerated_value.rb +37 -0
- data/lib/renum/enumerated_value_type_factory.rb +54 -0
- data/lib/renum/version.rb +9 -0
- data/lib/renum.rb +15 -0
- data/spec/renum_spec.rb +108 -0
- data/spec/spec_helper.rb +14 -0
- metadata +63 -0
data/License.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 John D. Hume
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
h1. renum
|
2
|
+
|
3
|
+
Renum provides a readable but terse enum facility for Ruby. Enums are sometimes called object constants and are analogous to the type-safe enum pattern in Java, though obviously Ruby's flexibility means there's no such thing as type-safety.
|
4
|
+
|
5
|
+
h2. Installing
|
6
|
+
|
7
|
+
<pre syntax="ruby">sudo gem install renum</pre>
|
8
|
+
|
9
|
+
h2. Demonstration of usage
|
10
|
+
|
11
|
+
Renum allows you to do things like this:
|
12
|
+
|
13
|
+
<pre syntax="ruby">enum :Status, %w( NOT_STARTED IN_PROGRESS COMPLETE )
|
14
|
+
|
15
|
+
enum :Size do
|
16
|
+
Small("Really really tiny")
|
17
|
+
Medium("Sort of in the middle")
|
18
|
+
Large("Quite big")
|
19
|
+
|
20
|
+
attr_reader :description
|
21
|
+
|
22
|
+
def init description
|
23
|
+
@description = description
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module MyNamespace
|
28
|
+
enum :FooValue, [ :Bar, :Baz, :Bat ]
|
29
|
+
end</pre>
|
30
|
+
|
31
|
+
Giving you something that satisfies this spec, plus a bit more:
|
32
|
+
|
33
|
+
<pre syntax="ruby">describe "enum" do
|
34
|
+
|
35
|
+
it "creates a class for the value type" do
|
36
|
+
Status.class.should == Class
|
37
|
+
end
|
38
|
+
|
39
|
+
it "makes each value an instance of the value type" do
|
40
|
+
Status::NOT_STARTED.class.should == Status
|
41
|
+
end
|
42
|
+
|
43
|
+
it "exposes array of values" do
|
44
|
+
Status.values.should == [Status::NOT_STARTED, Status::IN_PROGRESS, Status::COMPLETE]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "provides an alternative means of declaring values where extra information can be provided for initialization" do
|
48
|
+
Size::Small.description.should == "Really really tiny"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "enumerates over values" do
|
52
|
+
Status.map {|s| s.name}.should == %w[NOT_STARTED IN_PROGRESS COMPLETE]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "indexes values" do
|
56
|
+
Status[2].should == Status::COMPLETE
|
57
|
+
end
|
58
|
+
|
59
|
+
it "provides index lookup on values" do
|
60
|
+
Status::IN_PROGRESS.index.should == 1
|
61
|
+
end
|
62
|
+
|
63
|
+
it "provides a reasonable to_s for values" do
|
64
|
+
Status::NOT_STARTED.to_s.should == "Status::NOT_STARTED"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "makes values comparable" do
|
68
|
+
Status::NOT_STARTED.should < Status::COMPLETE
|
69
|
+
end
|
70
|
+
|
71
|
+
it "allows enums to be nested in other modules or classes" do
|
72
|
+
MyNamespace::FooValue::Bar.class.should == MyNamespace::FooValue
|
73
|
+
end
|
74
|
+
|
75
|
+
end</pre>
|
76
|
+
|
77
|
+
h2. "Rails":http://www.rubyonrails.com/ Integration
|
78
|
+
|
79
|
+
To use enumerated values as ActiveRecord attribute values, "use the constantize_attribute plugin":https://github.com/duelinmarkers/constantize_attribute/tree (also by me).
|
80
|
+
|
81
|
+
<pre syntax="ruby">class Vehicle < ActiveRecord::Base
|
82
|
+
enum :Status do
|
83
|
+
New()
|
84
|
+
Used()
|
85
|
+
Salvage(true)
|
86
|
+
|
87
|
+
def init(warn = false)
|
88
|
+
@warn = warn
|
89
|
+
end
|
90
|
+
|
91
|
+
def requires_warning_buyer?
|
92
|
+
@warn
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
constantize_attribute :status
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
v = Vehicle.create! :status => Vehicle::Status::New
|
101
|
+
# Now the database has the string "Vehicle::Status::New",
|
102
|
+
# but your record object exposes the Status object:
|
103
|
+
v.status.requires_warning_buyer? # => false
|
104
|
+
|
105
|
+
v.update_attribute :status, Vehicle::Status::Salvage
|
106
|
+
# Now the database has the string "Vehicle::Status::Salvage".
|
107
|
+
v.status.requires_warning_buyer? # => true
|
108
|
+
|
109
|
+
# Since constantize_attribute also accepts strings, it's easy
|
110
|
+
# to use enumerated values with forms.
|
111
|
+
v.status = "Vehicle::Status::Used"
|
112
|
+
v.status.requires_warning_buyer? # => false</pre>
|
113
|
+
|
114
|
+
h2. License
|
115
|
+
|
116
|
+
This code is free to use under the terms of the MIT license.
|
117
|
+
|
118
|
+
h2. Contact
|
119
|
+
|
120
|
+
Renum was created by John D. Hume. Comments are welcome. Send an email to duelin dot markers at gmail or "contact me via my blog":http://elhumidor.blogspot.com/.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
Spec::Rake::SpecTask.new do |t|
|
5
|
+
end
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'jeweler'
|
11
|
+
Jeweler::Tasks.new do |s|
|
12
|
+
s.name = "renum"
|
13
|
+
s.summary = "provides a readable but terse enum facility for Ruby"
|
14
|
+
s.email = "duelin.markers@gmail.com"
|
15
|
+
s.homepage = "http://github.com/duelinmarkers/renum"
|
16
|
+
s.description = "provides a readable but terse enum facility for Ruby"
|
17
|
+
s.authors = ["John Hume"]
|
18
|
+
end
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler or a dependency not available. To install: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
21
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.2
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Renum
|
4
|
+
class EnumeratedValue
|
5
|
+
|
6
|
+
class << self
|
7
|
+
include Enumerable
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :values, :each, :[]
|
11
|
+
|
12
|
+
def values
|
13
|
+
@values ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
include Comparable
|
19
|
+
attr_reader :name
|
20
|
+
attr_reader :index
|
21
|
+
|
22
|
+
def initialize name
|
23
|
+
@name = name.to_s
|
24
|
+
@index = self.class.values.size
|
25
|
+
self.class.values << self
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
"#{self.class}::#{name}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def <=> other
|
33
|
+
index <=> other.index
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'renum/enumerated_value'
|
2
|
+
|
3
|
+
module Renum
|
4
|
+
module EnumeratedValueTypeFactory
|
5
|
+
class << self
|
6
|
+
def create nest, type_name, values, &block
|
7
|
+
klass = create_class nest, type_name
|
8
|
+
create_values klass, values, &block
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_class nest, type_name
|
12
|
+
klass = Class.new EnumeratedValue
|
13
|
+
nest.const_set(type_name, klass)
|
14
|
+
klass
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_values klass, values, &block
|
18
|
+
setup_for_definition_in_block(klass) if values == :defined_in_block
|
19
|
+
klass.class_eval &block if block_given?
|
20
|
+
if values == :defined_in_block
|
21
|
+
klass.block_defined_values.each do |value_name, init_args|
|
22
|
+
value = klass.new(value_name)
|
23
|
+
klass.const_set(value_name, value)
|
24
|
+
value.init *init_args if init_args.any?
|
25
|
+
end
|
26
|
+
teardown_from_definition_in_block(klass)
|
27
|
+
else
|
28
|
+
values.each do |name|
|
29
|
+
klass.const_set(name, klass.new(name))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup_for_definition_in_block klass
|
35
|
+
klass.class_eval do
|
36
|
+
def self.block_defined_values
|
37
|
+
@block_defined_values ||= []
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.method_missing value_name, *init_args
|
41
|
+
block_defined_values << [value_name, init_args]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def teardown_from_definition_in_block klass
|
47
|
+
class << klass
|
48
|
+
remove_method :block_defined_values
|
49
|
+
remove_method :method_missing
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/renum.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
require 'renum/enumerated_value_type_factory'
|
3
|
+
|
4
|
+
module Renum
|
5
|
+
def enum type_name, values = :defined_in_block, &block
|
6
|
+
nest = self.is_a?(Module) ? self : Object
|
7
|
+
EnumeratedValueTypeFactory.create(nest, type_name, values, &block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
extend Renum
|
12
|
+
|
13
|
+
Module.module_eval do
|
14
|
+
include Renum
|
15
|
+
end
|
data/spec/renum_spec.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
enum :Status, [ :NOT_STARTED, :IN_PROGRESS, :COMPLETE ]
|
4
|
+
|
5
|
+
module MyNamespace
|
6
|
+
enum :FooValue, %w( Bar Baz Bat )
|
7
|
+
end
|
8
|
+
|
9
|
+
enum :Color, [ :RED, :GREEN, :BLUE ] do
|
10
|
+
def abbr
|
11
|
+
name[0..0]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
enum :Size do
|
16
|
+
Small("Really really tiny")
|
17
|
+
Medium("Sort of in the middle")
|
18
|
+
Large("Quite big")
|
19
|
+
|
20
|
+
attr_reader :description
|
21
|
+
|
22
|
+
def init description
|
23
|
+
@description = description
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
enum :HairColor do
|
28
|
+
BLONDE()
|
29
|
+
BRUNETTE()
|
30
|
+
RED()
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "basic enum" do
|
34
|
+
|
35
|
+
it "creates a class for the value type" do
|
36
|
+
Status.should be_an_instance_of(Class)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "makes each value an instance of the value type" do
|
40
|
+
Status::NOT_STARTED.should be_an_instance_of(Status)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "exposes array of values" do
|
44
|
+
Status.values.should == [Status::NOT_STARTED, Status::IN_PROGRESS, Status::COMPLETE]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "enumerates over values" do
|
48
|
+
Status.map {|s| s.name}.should == %w[NOT_STARTED IN_PROGRESS COMPLETE]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "indexes values" do
|
52
|
+
Status[2].should == Status::COMPLETE
|
53
|
+
Color[0].should == Color::RED
|
54
|
+
end
|
55
|
+
|
56
|
+
it "provides index lookup on values" do
|
57
|
+
Status::IN_PROGRESS.index.should == 1
|
58
|
+
Color::GREEN.index.should == 1
|
59
|
+
end
|
60
|
+
|
61
|
+
it "provides a reasonable to_s for values" do
|
62
|
+
Status::NOT_STARTED.to_s.should == "Status::NOT_STARTED"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "makes values comparable" do
|
66
|
+
Color::RED.should < Color::GREEN
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "nested enum" do
|
71
|
+
it "is namespaced in the containing module or class" do
|
72
|
+
MyNamespace::FooValue::Bar.class.should == MyNamespace::FooValue
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "enum with a block" do
|
77
|
+
it "can define additional instance methods" do
|
78
|
+
Color::RED.abbr.should == "R"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "enum with no values array and values declared in the block" do
|
83
|
+
it "provides an alternative means of declaring values where extra information can be provided for initialization" do
|
84
|
+
Size::Small.description.should == "Really really tiny"
|
85
|
+
end
|
86
|
+
|
87
|
+
it "works the same as the basic form with respect to ordering" do
|
88
|
+
Size.values.should == [Size::Small, Size::Medium, Size::Large]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "responds as expected to arbitrary method calls, in spite of using method_missing for value definition" do
|
92
|
+
lambda { Size.ExtraLarge() }.should raise_error(NoMethodError)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "supports there being no extra data and no init() method defined, if you don't need them" do
|
96
|
+
HairColor::BLONDE.name.should == "BLONDE"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# It was reported on my blog that <=> was causing segfaults.
|
101
|
+
# I'd love to figure out why, but first I'd love to fix that.
|
102
|
+
describe "digging into this segfault/illegal instruction issue, renum" do
|
103
|
+
it "doesn't cause the ruby process to bomb!" do
|
104
|
+
Color::RED.should < Color::GREEN
|
105
|
+
Color::RED.should_not > Color::GREEN
|
106
|
+
Color::RED.should < Color::BLUE
|
107
|
+
end
|
108
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: duelinmarkers-renum
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Hume
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-19 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: provides a readable but terse enum facility for Ruby
|
17
|
+
email: duelin.markers@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.textile
|
24
|
+
files:
|
25
|
+
- License.txt
|
26
|
+
- README.textile
|
27
|
+
- Rakefile
|
28
|
+
- VERSION
|
29
|
+
- lib/renum.rb
|
30
|
+
- lib/renum/enumerated_value.rb
|
31
|
+
- lib/renum/enumerated_value_type_factory.rb
|
32
|
+
- lib/renum/version.rb
|
33
|
+
- spec/renum_spec.rb
|
34
|
+
- spec/spec_helper.rb
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: http://github.com/duelinmarkers/renum
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options:
|
39
|
+
- --charset=UTF-8
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
version:
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 1.2.0
|
58
|
+
signing_key:
|
59
|
+
specification_version: 2
|
60
|
+
summary: provides a readable but terse enum facility for Ruby
|
61
|
+
test_files:
|
62
|
+
- spec/renum_spec.rb
|
63
|
+
- spec/spec_helper.rb
|