renum 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []