renum 1.3.1 → 1.4.0

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.
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - jruby-18mode
6
+ - jruby-19mode
7
+ - rbx-18mode
8
+ - rbx-19mode
9
+ - ruby-head
10
+ - jruby-head
11
+ - 1.8.7
12
+ - ree
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake', :group => [:development, :test]
4
+ gem 'rspec', :group => [:development, :test]
5
+ gem 'jeweler', :group => [:development, :test]
@@ -0,0 +1,30 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ git (1.2.5)
6
+ jeweler (1.8.4)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rdoc
11
+ json (1.7.5)
12
+ rake (0.9.2.2)
13
+ rdoc (3.12)
14
+ json (~> 1.4)
15
+ rspec (2.11.0)
16
+ rspec-core (~> 2.11.0)
17
+ rspec-expectations (~> 2.11.0)
18
+ rspec-mocks (~> 2.11.0)
19
+ rspec-core (2.11.1)
20
+ rspec-expectations (2.11.3)
21
+ diff-lcs (~> 1.1.3)
22
+ rspec-mocks (2.11.3)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ jeweler
29
+ rake
30
+ rspec
@@ -1,12 +1,10 @@
1
1
  h1. renum
2
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
3
+ !https://secure.travis-ci.org/duelinmarkers/renum.png(Build Status)!:http://travis-ci.org/duelinmarkers/renum
6
4
 
7
- <pre syntax="ruby">sudo gem install renum</pre>
5
+ 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.
8
6
 
9
- h2. Demonstration of usage
7
+ h2. Usage
10
8
 
11
9
  Renum allows you to do things like this:
12
10
 
@@ -31,15 +29,15 @@ end</pre>
31
29
  Giving you something that satisfies this spec, plus a bit more:
32
30
 
33
31
  <pre syntax="ruby">describe "enum" do
34
-
32
+
35
33
  it "creates a class for the value type" do
36
34
  Status.class.should == Class
37
35
  end
38
-
36
+
39
37
  it "makes each value an instance of the value type" do
40
38
  Status::NOT_STARTED.class.should == Status
41
39
  end
42
-
40
+
43
41
  it "exposes array of values" do
44
42
  Status.values.should == [Status::NOT_STARTED, Status::IN_PROGRESS, Status::COMPLETE]
45
43
  end
@@ -47,54 +45,90 @@ Giving you something that satisfies this spec, plus a bit more:
47
45
  it "provides an alternative means of declaring values where extra information can be provided for initialization" do
48
46
  Size::Small.description.should == "Really really tiny"
49
47
  end
50
-
48
+
51
49
  it "enumerates over values" do
52
50
  Status.map {|s| s.name}.should == %w[NOT_STARTED IN_PROGRESS COMPLETE]
53
51
  end
54
-
52
+
55
53
  it "indexes values" do
56
54
  Status[2].should == Status::COMPLETE
57
55
  end
58
-
56
+
59
57
  it "provides index lookup on values" do
60
58
  Status::IN_PROGRESS.index.should == 1
61
59
  end
62
-
60
+
63
61
  it "provides a reasonable to_s for values" do
64
62
  Status::NOT_STARTED.to_s.should == "Status::NOT_STARTED"
65
63
  end
66
-
64
+
67
65
  it "makes values comparable" do
68
66
  Status::NOT_STARTED.should < Status::COMPLETE
69
67
  end
70
-
68
+
71
69
  it "allows enums to be nested in other modules or classes" do
72
70
  MyNamespace::FooValue::Bar.class.should == MyNamespace::FooValue
73
71
  end
74
-
72
+
75
73
  end</pre>
76
74
 
77
75
  h2. "Rails":http://www.rubyonrails.com/ Integration
78
76
 
79
- To use enumerated values as ActiveRecord attribute values, "use the constantize_attribute plugin":https://github.com/duelinmarkers/constantize_attribute/tree (also by me).
77
+ [This feature is brand new and should be considered beta-quality.]
78
+
79
+ Use Renum::NameSerializer with ActiveRecord's "`serialize`":http://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html#method-i-serialize method to store enum values as strings.
80
80
 
81
81
  <pre syntax="ruby">class Vehicle < ActiveRecord::Base
82
82
  enum :Status do
83
83
  New()
84
84
  Used()
85
85
  Salvage(true)
86
-
86
+
87
87
  def init(warn = false)
88
88
  @warn = warn
89
89
  end
90
-
90
+
91
91
  def requires_warning_buyer?
92
92
  @warn
93
93
  end
94
94
  end
95
-
95
+
96
+ serialize :status, Renum::NameSerializer.new(Status)
97
+
98
+ end
99
+
100
+ v = Vehicle.create! :status => Vehicle::Status::New
101
+ # Now the database has the string "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 "Salvage".
107
+ v.status.requires_warning_buyer? # => true
108
+
109
+ # Note that if you assign a String, the value will remain a String until you save.
110
+ v.status = "Used"
111
+ v.status.requires_warning_buyer? # => raises NoMethodError</pre>
112
+
113
+ Before ActiveRecord had such flexible serialization support, I wrote a Rails plugin called "constantize_attribute":https://github.com/duelinmarkers/constantize_attribute/tree that would store the fully qualified name of your value in a string column. It hasn't been tested against a recent version of Rails, but it should still work. It's not packaged as a gem, so if you want to use it, I'd recommend you just grab the code. (It's quite tiny.)
114
+
115
+ <pre syntax="ruby">class Vehicle < ActiveRecord::Base
116
+ enum :Status do
117
+ New()
118
+ Used()
119
+ Salvage(true)
120
+
121
+ def init(warn = false)
122
+ @warn = warn
123
+ end
124
+
125
+ def requires_warning_buyer?
126
+ @warn
127
+ end
128
+ end
129
+
96
130
  constantize_attribute :status
97
-
131
+
98
132
  end
99
133
 
100
134
  v = Vehicle.create! :status => Vehicle::Status::New
@@ -113,7 +147,7 @@ v.status.requires_warning_buyer? # => false</pre>
113
147
 
114
148
  h2. License
115
149
 
116
- This code is free to use under the terms of the MIT license.
150
+ This code is free to use under the terms of the MIT license.
117
151
 
118
152
  Permission is hereby granted, free of charge, to any person obtaining
119
153
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -1,22 +1,17 @@
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
- Jeweler::GemcutterTasks.new
20
- rescue LoadError
21
- puts "Jeweler or a dependency not available. To install: sudo gem install jeweler"
22
- end
1
+ require 'rubygems'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |s|
10
+ s.name = "renum"
11
+ s.summary = "provides a readable but terse enum facility for Ruby"
12
+ s.email = "duelin.markers@gmail.com"
13
+ s.homepage = "http://github.com/duelinmarkers/renum"
14
+ s.description = "provides a readable but terse enum facility for Ruby"
15
+ s.authors = ["John Hume"]
16
+ end
17
+ Jeweler::RubygemsDotOrgTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.1
1
+ 1.4.0
@@ -1,13 +1,19 @@
1
1
  require 'renum/enumerated_value_type_factory'
2
+ require 'renum/name_serializer'
3
+ require 'renum/index_serializer'
2
4
 
3
- # Requiring 'renum' mixes the Renum module into both the main Object and
4
- # Module, so it can be called from anywhere that you might reasonably
5
+ # Requiring 'renum' mixes the Renum module into both the main Object and
6
+ # Module, so it can be called from anywhere that you might reasonably
5
7
  # define an enumeration with an implicit receiver.
6
8
  module Renum
7
9
 
8
- # Figures out whether the new enumeration will live in Object or the
9
- # receiving Module, then delegates to EnumeratedValueTypeFactory#create for
10
- # all the real work.
10
+ # Declares an enumerated type. If called inside a module, the enumerated type class will
11
+ # be nested inside that module.
12
+ #
13
+ # @param [Symbol] type_name The name of the EnumeratedType class to create
14
+ # @param [optional, Array<Symbol, String>] values
15
+ # the names of the values in order, can be omitted if you'll specify them via method calls in a block
16
+ # @param block can be used to specify values with method calls, instance methods
11
17
  def enum type_name, values = :defined_in_block, &block
12
18
  nest = self.is_a?(Module) ? self : Object
13
19
  EnumeratedValueTypeFactory.create(nest, type_name, values, &block)
@@ -2,7 +2,8 @@ require 'forwardable'
2
2
 
3
3
  module Renum
4
4
 
5
- # This is the superclass of all enumeration classes.
5
+ # This is the superclass of your enumeration classes.
6
+ # The class methods defined here are intended to be called on your enumerated value classes.
6
7
  # An enumeration class is Enumerable over its values and exposes them by numeric index via [].
7
8
  # Values are also comparable, sorting into the order in which they're declared.
8
9
  class EnumeratedValue
@@ -13,15 +14,19 @@ module Renum
13
14
 
14
15
  def_delegators :values, :each, :[]
15
16
 
16
- # Returns an array of values in the order they're declared.
17
+ # @return [Array] values of this type in the order they're declared.
17
18
  def values
18
19
  @values ||= []
19
20
  end
20
21
 
22
+ # Lookup by name.
23
+ # @param [String] name of the value you want
24
+ # @return [EnumeratedValue, nil] the value with `name` or nil if there is no value by that name
21
25
  def with_name name
22
26
  values_by_name[name]
23
27
  end
24
28
 
29
+ # @return [{String => EnumeratedType}] values keyed by name.
25
30
  def values_by_name
26
31
  @values_by_name ||= values.inject({}) do |memo, value|
27
32
  memo[value.name] = value
@@ -35,6 +40,9 @@ module Renum
35
40
 
36
41
  attr_reader :name, :index
37
42
 
43
+ # You should never directly new-up an EnumeratedValue, so this is basically internal.
44
+ # It sets up the value with its class, so if you override, be sure to call super!
45
+ # Better yet define init as shown in the README.
38
46
  def initialize name
39
47
  @name = name.to_s.freeze
40
48
  @index = self.class.values.size
@@ -42,7 +50,7 @@ module Renum
42
50
  end
43
51
 
44
52
  # Returns the fully qualified name of the constant referring to this value.
45
- # Don't override this if you're using Renum with the constantize_attribute
53
+ # Don't override this if you're using Renum with the constantize_attribute
46
54
  # plugin, which relies on this behavior.
47
55
  def to_s
48
56
  "#{self.class}::#{name}"
@@ -53,5 +61,8 @@ module Renum
53
61
  index <=> other.index
54
62
  end
55
63
 
64
+ def == other
65
+ equal? other
66
+ end
56
67
  end
57
68
  end
@@ -0,0 +1,15 @@
1
+ module Renum
2
+ class IndexSerializer
3
+ def initialize enum_class
4
+ @enum_class = enum_class
5
+ end
6
+
7
+ def dump v
8
+ v && v.index
9
+ end
10
+
11
+ def load i
12
+ i && @enum_class[i]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ module Renum
2
+ class NameSerializer
3
+ def initialize enum_class
4
+ @enum_class = enum_class
5
+ end
6
+
7
+ def dump v
8
+ v && if v.is_a?(String)
9
+ v
10
+ else
11
+ v.name
12
+ end
13
+ end
14
+
15
+ def load s
16
+ s && @enum_class.with_name(s)
17
+ end
18
+ end
19
+ end
@@ -1,49 +1,57 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{renum}
8
- s.version = "1.3.1"
7
+ s.name = "renum"
8
+ s.version = "1.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["John Hume"]
12
- s.date = %q{2010-08-17}
13
- s.description = %q{provides a readable but terse enum facility for Ruby}
14
- s.email = %q{duelin.markers@gmail.com}
12
+ s.date = "2013-03-16"
13
+ s.description = "provides a readable but terse enum facility for Ruby"
14
+ s.email = "duelin.markers@gmail.com"
15
15
  s.extra_rdoc_files = [
16
16
  "README.textile"
17
17
  ]
18
18
  s.files = [
19
+ ".travis.yml",
20
+ "Gemfile",
21
+ "Gemfile.lock",
19
22
  "README.textile",
20
- "Rakefile",
21
- "VERSION",
22
- "lib/renum.rb",
23
- "lib/renum/enumerated_value.rb",
24
- "lib/renum/enumerated_value_type_factory.rb",
25
- "renum.gemspec",
26
- "spec/renum_spec.rb",
27
- "spec/spec_helper.rb"
28
- ]
29
- s.homepage = %q{http://github.com/duelinmarkers/renum}
30
- s.rdoc_options = ["--charset=UTF-8"]
31
- s.require_paths = ["lib"]
32
- s.rubygems_version = %q{1.3.7}
33
- s.summary = %q{provides a readable but terse enum facility for Ruby}
34
- s.test_files = [
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/renum.rb",
26
+ "lib/renum/enumerated_value.rb",
27
+ "lib/renum/enumerated_value_type_factory.rb",
28
+ "lib/renum/index_serializer.rb",
29
+ "lib/renum/name_serializer.rb",
30
+ "renum.gemspec",
35
31
  "spec/renum_spec.rb",
36
- "spec/spec_helper.rb"
32
+ "spec/spec_helper.rb"
37
33
  ]
34
+ s.homepage = "http://github.com/duelinmarkers/renum"
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = "1.8.15"
37
+ s.summary = "provides a readable but terse enum facility for Ruby"
38
38
 
39
39
  if s.respond_to? :specification_version then
40
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
41
40
  s.specification_version = 3
42
41
 
43
42
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
43
+ s.add_development_dependency(%q<rake>, [">= 0"])
44
+ s.add_development_dependency(%q<rspec>, [">= 0"])
45
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
44
46
  else
47
+ s.add_dependency(%q<rake>, [">= 0"])
48
+ s.add_dependency(%q<rspec>, [">= 0"])
49
+ s.add_dependency(%q<jeweler>, [">= 0"])
45
50
  end
46
51
  else
52
+ s.add_dependency(%q<rake>, [">= 0"])
53
+ s.add_dependency(%q<rspec>, [">= 0"])
54
+ s.add_dependency(%q<jeweler>, [">= 0"])
47
55
  end
48
56
  end
49
57
 
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  enum :Status, [ :NOT_STARTED, :IN_PROGRESS, :COMPLETE ]
4
4
 
@@ -43,6 +43,10 @@ describe "basic enum" do
43
43
  it "makes values comparable" do
44
44
  Color::RED.should < Color::GREEN
45
45
  end
46
+
47
+ it "doesn't let Comparable's == confuse things" do
48
+ Color[0].should_not == Status[0]
49
+ end
46
50
  end
47
51
 
48
52
  module MyNamespace
@@ -158,14 +162,35 @@ end
158
162
 
159
163
  describe "prevention of subtle and annoying bugs" do
160
164
  it "prevents you modifying the values array" do
161
- lambda { Color.values << 'some crazy value' }.should raise_error(TypeError, /can't modify frozen/)
165
+ expect { Color.values << 'some crazy value' }.to raise_error(/modify frozen/)
162
166
  end
163
167
 
164
168
  it "prevents you modifying the name hash" do
165
- lambda { Color.values_by_name['MAGENTA'] = 'some crazy value' }.should raise_error(TypeError, /can't modify frozen/)
169
+ expect { Color.values_by_name['MAGENTA'] = 'some crazy value' }.to raise_error(/modify frozen/)
166
170
  end
167
171
 
168
172
  it "prevents you modifying the name of a value" do
169
- lambda { Color::RED.name << 'dish-Brown' }.should raise_error(TypeError, /can't modify frozen/)
173
+ expect { Color::RED.name << 'dish-Brown' }.to raise_error(/modify frozen/)
174
+ end
175
+ end
176
+
177
+ describe "serialization (for ActiveRecord or what-have-you)" do
178
+ it "can serialize to and deserialize from its name string" do
179
+ serializer = Renum::NameSerializer.new Color
180
+ serializer.dump(Color::RED).should == "RED"
181
+ serializer.dump("RED").should == "RED" # for convenient param assignment
182
+ serializer.load("RED").should == Color::RED
183
+
184
+ serializer.dump(nil).should == nil
185
+ serializer.load(nil).should == nil
186
+ end
187
+
188
+ it "can serialize to and deserialize from its positional index" do
189
+ serializer = Renum::IndexSerializer.new Color
190
+ serializer.dump(Color::RED).should == 0
191
+ serializer.load(0).should == Color::RED
192
+
193
+ serializer.dump(nil).should == nil
194
+ serializer.load(nil).should == nil
170
195
  end
171
196
  end
@@ -1,12 +1,2 @@
1
- begin
2
- require 'spec'
3
- rescue LoadError
4
- require 'rubygems'
5
- gem 'rspec'
6
- require 'spec'
7
- end
8
-
9
- if ENV['USE_GEM']
10
- require 'rubygems'
11
- end
1
+ require 'rspec'
12
2
  require 'renum'
metadata CHANGED
@@ -1,76 +1,92 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: renum
3
- version: !ruby/object:Gem::Version
4
- hash: 25
5
- prerelease: false
6
- segments:
7
- - 1
8
- - 3
9
- - 1
10
- version: 1.3.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.4.0
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - John Hume
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2010-08-17 00:00:00 -04:00
19
- default_executable:
20
- dependencies: []
21
-
12
+ date: 2013-03-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &19725600 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *19725600
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &19725120 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *19725120
36
+ - !ruby/object:Gem::Dependency
37
+ name: jeweler
38
+ requirement: &19724600 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *19724600
22
47
  description: provides a readable but terse enum facility for Ruby
23
48
  email: duelin.markers@gmail.com
24
49
  executables: []
25
-
26
50
  extensions: []
27
-
28
- extra_rdoc_files:
51
+ extra_rdoc_files:
29
52
  - README.textile
30
- files:
53
+ files:
54
+ - .travis.yml
55
+ - Gemfile
56
+ - Gemfile.lock
31
57
  - README.textile
32
58
  - Rakefile
33
59
  - VERSION
34
60
  - lib/renum.rb
35
61
  - lib/renum/enumerated_value.rb
36
62
  - lib/renum/enumerated_value_type_factory.rb
63
+ - lib/renum/index_serializer.rb
64
+ - lib/renum/name_serializer.rb
37
65
  - renum.gemspec
38
66
  - spec/renum_spec.rb
39
67
  - spec/spec_helper.rb
40
- has_rdoc: true
41
68
  homepage: http://github.com/duelinmarkers/renum
42
69
  licenses: []
43
-
44
70
  post_install_message:
45
- rdoc_options:
46
- - --charset=UTF-8
47
- require_paths:
71
+ rdoc_options: []
72
+ require_paths:
48
73
  - lib
49
- required_ruby_version: !ruby/object:Gem::Requirement
74
+ required_ruby_version: !ruby/object:Gem::Requirement
50
75
  none: false
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- hash: 3
55
- segments:
56
- - 0
57
- version: "0"
58
- required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
81
  none: false
60
- requirements:
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- hash: 3
64
- segments:
65
- - 0
66
- version: "0"
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
67
86
  requirements: []
68
-
69
87
  rubyforge_project:
70
- rubygems_version: 1.3.7
88
+ rubygems_version: 1.8.15
71
89
  signing_key:
72
90
  specification_version: 3
73
91
  summary: provides a readable but terse enum facility for Ruby
74
- test_files:
75
- - spec/renum_spec.rb
76
- - spec/spec_helper.rb
92
+ test_files: []