yinum 0.0.1 → 1.0.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.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- yinum (0.0.1)
4
+ yinum (1.0.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
@@ -20,5 +20,5 @@ PLATFORMS
20
20
  ruby
21
21
 
22
22
  DEPENDENCIES
23
- yinum!
24
23
  rspec
24
+ yinum!
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Enum
1
+ # yinum
2
2
 
3
- A straightforward implementation of an enum for Ruby (on Rails).
3
+ A yummy straightforward implementation of an enum for Ruby (on Rails).
4
4
 
5
5
  The straightforward usage looks like this:
6
6
 
@@ -12,11 +12,11 @@ The straightforward usage looks like this:
12
12
 
13
13
  ## Getting started
14
14
 
15
- $ gem install enum
15
+ $ gem install yinum
16
16
 
17
17
  Or in your Gemfile:
18
18
 
19
- gem "enum"
19
+ gem "yinum"
20
20
 
21
21
  ## Usage
22
22
 
@@ -95,7 +95,7 @@ Now the `color` column is always read and written using the enum value feature:
95
95
 
96
96
  This also creates a `validates\_inclusion\_of` with `allow\_nil => true` to prevent having bad values.
97
97
 
98
- Adding another `:scoped => true` option before the enum values allows automatic generation of scopes and question
98
+ Adding a `:scoped => true` option before the enum values allows automatic generation of scopes and question
99
99
  methods on the model as follows:
100
100
 
101
101
  class Car < ActiveRecord::Base
@@ -106,6 +106,16 @@ methods on the model as follows:
106
106
  Car.last.red?
107
107
  => true
108
108
 
109
+ You can also use another class's enum for your class with enum\_column (including generated methods) like so:
110
+
111
+ class Motorcycle < ActiveRecord::Base
112
+ enum_column :color, Car::COLORS, { :scoped => true }
113
+ end
114
+ Motorcycle.black.to_sql
115
+ => "SELECT `motorcycles`.* FROM `motorcycles` WHERE `motorcycles`.`color` = 2"
116
+ Motorcycle.black
117
+ => Car::COLORS.black
118
+
109
119
  Last but not least, automatic translation lookup.
110
120
  Given the following `config/locales/en.yml`:
111
121
 
@@ -137,7 +147,7 @@ If the translation is missing, it fallbacks to the translation without the class
137
147
 
138
148
  All enum names (usually CONSTANT\_LIKE) and parent class names are converted to snakecase.
139
149
 
140
- == Limitations
150
+ ## Limitations
141
151
 
142
152
  Since the `===` operator is called on the when value, this syntax cannot be used:
143
153
 
@@ -1,4 +1,4 @@
1
- class EnumValue < BasicObject
1
+ class Enum::EnumValue < BasicObject
2
2
  attr_reader :enum, :name, :value
3
3
 
4
4
  def initialize(enum, name, value)
@@ -1,14 +1,6 @@
1
- module EnumGenerator
2
- def enum(name, hash)
3
- const_set name, Enum.new(name, self, hash)
4
- end
5
- end
6
-
7
- class Object
8
- include EnumGenerator
9
- end
1
+ require 'enum/helpers/enum_generator'
10
2
 
11
- module EnumColumns
3
+ module Enum::Helpers::EnumColumns
12
4
  # Bind a column to an enum by:
13
5
  # Generating attribute reader and writer to convert to EnumValue.
14
6
  # Creating a validation for the attribute so it must have valid enum values (allowing nil).
@@ -16,18 +8,18 @@ module EnumColumns
16
8
  # If given a enum name (a symbol) and hash, also creates the enum.
17
9
  def enum_column(attr, name_or_enum, options={}, hash=nil)
18
10
  # generating or getting the enum
19
- if name_or_enum.is_a?(Symbol)
11
+ if name_or_enum.is_a?(Enum)
12
+ e = name_or_enum
13
+ else
20
14
  # the first hash is either options or the hash if the options are missing
21
15
  hash, options = options, {} if hash.nil?
22
16
  # generating the enum if the hash is not empty
23
17
  enum name_or_enum, hash if hash.any?
24
18
 
25
19
  e = const_get(name_or_enum)
26
- else
27
- e = name_or_enum
28
20
  end
29
21
  # attribute reader
30
- define_method(attr) { v = super(); (v.nil? or e.values.exclude?(v)) ? v : e[v] }
22
+ define_method(attr) { v = super(); (v.nil? or not e.values.include?(v)) ? v : e[v] }
31
23
  # attribute writer
32
24
  define_method("#{attr}=") { |v| v.nil? ? super(v) : super(e[v]) }
33
25
  # validation
@@ -44,6 +36,6 @@ end
44
36
 
45
37
  if defined?(ActiveRecord)
46
38
  class ActiveRecord::Base
47
- extend EnumColumns
39
+ extend Enum::Helpers::EnumColumns
48
40
  end
49
41
  end
@@ -0,0 +1,11 @@
1
+ require 'enum'
2
+
3
+ module Enum::Helpers::EnumGenerator
4
+ def enum(name, hash)
5
+ const_set name, Enum.new(name, self, hash)
6
+ end
7
+ end
8
+
9
+ class Object
10
+ include Enum::Helpers::EnumGenerator
11
+ end
@@ -0,0 +1,5 @@
1
+ module Enum::Helpers
2
+ end
3
+
4
+ require 'enum/helpers/enum_generator'
5
+ require 'enum/helpers/enum_columns'
data/lib/enum.rb ADDED
@@ -0,0 +1,56 @@
1
+ class Enum
2
+ require 'enum/enum_value'
3
+ include Enumerable
4
+
5
+ attr_reader :klass, :name, :by_name, :by_value
6
+
7
+ def initialize(name, klass=nil, hash={})
8
+ klass, hash = nil, klass if klass.is_a?(Hash)
9
+ @name, @klass = name, klass
10
+ map_hash(hash)
11
+ generate_methods
12
+ end
13
+
14
+ def to_s
15
+ @klass ? "#{@klass.name}::#{@name}" : @name.to_s
16
+ end
17
+ def inspect
18
+ "#{to_s}(#{@by_name.map { |n,ev| "#{n.inspect} => #{ev.value.inspect}"}.join(", ")})"
19
+ end
20
+
21
+ def names
22
+ @by_name.keys
23
+ end
24
+ def values
25
+ @by_value.keys
26
+ end
27
+ def each(&block)
28
+ @by_name.each_value(&block)
29
+ end
30
+
31
+ def [](name_or_value)
32
+ ev = @by_name[name_or_value] || @by_value[name_or_value]
33
+ raise "#{inspect} does not know #{name_or_value.inspect}" if ev.nil?
34
+ ev
35
+ end
36
+
37
+ private
38
+ def map_hash(hash)
39
+ @by_name = {}
40
+ @by_value = {}
41
+ hash.each do |n, v|
42
+ n, v = v, n if v.is_a?(Symbol) or n.is_a?(Numeric)
43
+ raise "duplicate enum name #{n} for #{to_s}" if @by_name.has_key?(n)
44
+ raise "duplicate enum value #{v} for #{to_s}.#{n}" if @by_value.has_key?(v)
45
+ raise "value can't be nil for #{to_s}.#{n}" if v.nil?
46
+ @by_name[n] = @by_value[v] = EnumValue.new(self, n, v)
47
+ end
48
+ @by_name.freeze
49
+ @by_value.freeze
50
+ end
51
+ def generate_methods
52
+ names.each do |name|
53
+ define_singleton_method(name) { self[name] }
54
+ end
55
+ end
56
+ end
data/lib/yinum.rb CHANGED
@@ -1,52 +1,2 @@
1
- require 'enum_value'
2
-
3
- class Enum
4
- attr_reader :klass, :name, :by_name, :by_value
5
-
6
- def initialize(name, klass=nil, hash={})
7
- klass, hash = nil, klass if klass.is_a?(Hash)
8
- @name, @klass = name, klass
9
- map_hash(hash)
10
- generate_methods
11
- end
12
-
13
- def to_s
14
- @klass ? "#{@klass.name}::#{@name}" : @name.to_s
15
- end
16
- def inspect
17
- "#{to_s}(#{@by_name.map { |n,ev| "#{n.inspect} => #{ev.value.inspect}"}.join(", ")})"
18
- end
19
-
20
- def names
21
- @by_name.keys
22
- end
23
- def values
24
- @by_value.keys
25
- end
26
-
27
- def [](name_or_value)
28
- ev = @by_name[name_or_value] || @by_value[name_or_value]
29
- raise "#{inspect} does not know #{name_or_value.inspect}" if ev.nil?
30
- ev
31
- end
32
-
33
- private
34
- def map_hash(hash)
35
- @by_name = {}
36
- @by_value = {}
37
- hash.each do |n, v|
38
- n, v = v, n if v.is_a?(Symbol) or n.is_a?(Numeric)
39
- raise "duplicate enum name #{n} for #{to_s}" if @by_name.has_key?(n)
40
- raise "duplicate enum value #{v} for #{to_s}.#{n}" if @by_value.has_key?(v)
41
- raise "value can't be nil for #{to_s}.#{n}" if v.nil?
42
- @by_name[n] = @by_value[v] = EnumValue.new(self, n, v)
43
- end
44
- end
45
- def generate_methods
46
- names.each do |name|
47
- define_singleton_method(name) { self[name] }
48
- end
49
- end
50
- end
51
-
52
- require 'helpers'
1
+ require 'enum'
2
+ require 'enum/helpers'
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Enum::EnumValue do
4
+ subject { @enum }
5
+
6
+ context "no parent" do
7
+ before { @enum = Enum.new(:MY_COLORS, :red => 1, :blue => 2) }
8
+ # can't use subject @enum.red as it turns into a Fixnum
9
+
10
+ specify { @enum.red.inspect.should == "MY_COLORS.red" }
11
+ specify { @enum.red.t.should == "I18n not available: enums.my_colors.red" }
12
+ end # context "no parent"
13
+
14
+ context "with parent" do
15
+ before { @enum = Enum.new(:MY_COLORS, Object, :red => 1, :blue => 2) }
16
+ # can't use subject @enum.red as it turns into a Fixnum
17
+
18
+ specify { @enum.red.inspect.should == "Object::MY_COLORS.red" }
19
+ specify { @enum.red.t.should == "I18n not available: enums.object.my_colors.red" }
20
+ end # context "with parent"
21
+
22
+ context "comparison" do
23
+ before { @enum = Enum.new(:MY_COLORS, :red => 1, :blue => 2) }
24
+
25
+ its(:red) { should == 1 }
26
+ its(:blue) { should_not == 1 }
27
+ its(:red) { should == :red }
28
+ its(:blue) { should_not == :red }
29
+ its(:red) { should === 1 }
30
+ its(:blue) { should_not === 1 }
31
+ its(:red) { should === :red }
32
+ its(:blue) { should_not === :red }
33
+ its(:red) { should be_red }
34
+ its(:blue) { should_not be_red }
35
+ specify { @enum.red.object_id.should == @enum[:red].object_id }
36
+ specify { @enum.red.object_id.should == @enum[1].object_id }
37
+ end # context "comparison"
38
+
39
+ context "invalid" do
40
+ specify { expect { @enum[:green] }.to raise_error(StandardError) }
41
+ specify { expect { @enum[3] }.to raise_error(StandardError) }
42
+ end # context "invalid"
43
+ end # describe EnumValue
@@ -0,0 +1,159 @@
1
+ require 'spec_helper'
2
+
3
+ class UsesEnumColumns < Hash # to allow setting 'attributes'
4
+ extend Enum::Helpers::EnumColumns
5
+
6
+ class << self
7
+ # enum definition
8
+ def define_enum(attr, name_or_enum, options={}, hash=nil)
9
+ @enum_name = name_or_enum.is_a?(Enum) ? name_or_enum.name : name_or_enum
10
+ reset_mocks
11
+ enum_column attr, name_or_enum, options, hash
12
+ end
13
+ def undefine_enum
14
+ remove_const :COLORS
15
+ end
16
+ def reset_mocks
17
+ @inclusion_validations = []
18
+ @scopes = []
19
+ end
20
+
21
+ # mocking validates_inclusion_of
22
+ attr_reader :inclusion_validations
23
+ def validates_inclusion_of(*attributes)
24
+ @inclusion_validations << attributes
25
+ end
26
+ # mocking scopes
27
+ def where(*attributes)
28
+ { :where => attributes }
29
+ end
30
+ attr_reader :scopes
31
+ def scope(*attributes)
32
+ @scopes << attributes
33
+ end
34
+
35
+ end
36
+
37
+ # mocking setters
38
+ def method_missing(method, *args, &block)
39
+ if method.to_s.end_with?('=') and args.length == 1
40
+ self[method.to_s.chop.to_sym] = args.first
41
+ elsif method =~ /^[a-z_]*$/ and args.length == 0
42
+ self[method]
43
+ else
44
+ super
45
+ end
46
+ end
47
+ end
48
+
49
+ class UsesAnoterEnumColumns < UsesEnumColumns
50
+ end
51
+
52
+ def enum_generator_specs
53
+ describe Enum::Helpers::EnumGenerator do
54
+ subject { UsesEnumColumns::COLORS }
55
+
56
+ its(:name) { should == :COLORS }
57
+ its(:klass) { should == UsesEnumColumns }
58
+ its(:by_name) { should == { :red => 1, :blue => 2 } }
59
+ end # describe Enum::Helpers::EnumGenerator
60
+ end
61
+
62
+ def enum_columns_attribute_specs
63
+ context "attributes" do
64
+ before do
65
+ @record = UsesEnumColumns.new
66
+ @record[:color] = :unknown
67
+ end
68
+ subject { @record }
69
+
70
+ context "setter" do
71
+ specify "nil" do
72
+ @record.color = nil
73
+ @record[:color].should be_nil
74
+ end
75
+
76
+ specify "name" do
77
+ @record.color = :red
78
+ @record[:color].should be_enum_value and @record[:color].should be_red
79
+ end
80
+
81
+ specify "value" do
82
+ @record.color = 2
83
+ @record[:color].should be_enum_value and @record[:color].should be_blue
84
+ end
85
+
86
+ specify "invalid" do
87
+ expect { @record.color = 3 }.to raise_error(StandardError)
88
+ end
89
+ end # context "setter"
90
+
91
+ context "getter" do
92
+ specify "nil" do
93
+ @record[:color] = nil
94
+ @record.color.should be_nil
95
+ end
96
+
97
+ specify "value" do
98
+ @record[:color] = 2
99
+ @record.color.should be_enum_value and @record.color.should be_blue
100
+ end
101
+
102
+ specify "invalid" do
103
+ @record[:color] = 3
104
+ @record.color.should_not respond_to(:enum_value?) and @record.color.should == 3
105
+ end
106
+ end # context "getter"
107
+ end # context "attribute"
108
+
109
+ context "validations" do
110
+ its(:inclusion_validations) { should have(1).item }
111
+ its(:inclusion_validations) { should include([:color, :in => [1, 2], :allow_nil => true]) }
112
+ end # context "validations"
113
+ end
114
+
115
+ describe Enum::Helpers::EnumColumns do
116
+ after { UsesEnumColumns.undefine_enum }
117
+ subject { UsesEnumColumns }
118
+
119
+ context "not scoped" do
120
+ before { UsesEnumColumns.define_enum(:color, :COLORS, :red => 1, :blue => 2) }
121
+
122
+ enum_generator_specs
123
+ enum_columns_attribute_specs
124
+ end # context "not scoped"
125
+
126
+ context "scoped" do
127
+ before { UsesEnumColumns.define_enum(:color, :COLORS, { :scoped => true }, :red => 1, :blue => 2) }
128
+
129
+ enum_generator_specs
130
+ enum_columns_attribute_specs
131
+
132
+ context "scopes" do
133
+ its(:scopes) { should have(2).items }
134
+ its(:scopes) { should include([:red, :where => [:color => 1]]) }
135
+ its(:scopes) { should include([:blue, :where => [:color => 2]]) }
136
+ end # context "scopes"
137
+
138
+ context "questions" do
139
+ before do
140
+ @record = UsesEnumColumns.new
141
+ @record.color = :red
142
+ end
143
+ subject { @record }
144
+
145
+ it { should be_red }
146
+ it { should_not be_blue }
147
+ end # context "questions"
148
+ end # context "scoped"
149
+
150
+ context "use another enum" do
151
+ before do
152
+ UsesEnumColumns.define_enum(:color, :COLORS, { :scoped => true }, :red => 1, :blue => 2)
153
+ UsesAnoterEnumColumns.define_enum(:color, UsesEnumColumns::COLORS)
154
+ end
155
+
156
+ enum_generator_specs
157
+ enum_columns_attribute_specs
158
+ end
159
+ end # describe Enum::Helpers::EnumColumns
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ class UsesEnumGenerator
4
+ enum :COLORS, :red => 1, :blue => 2
5
+ end
6
+
7
+ describe Enum::Helpers::EnumGenerator do
8
+ subject { UsesEnumGenerator::COLORS }
9
+
10
+ its(:name) { should == :COLORS }
11
+ its(:klass) { should == UsesEnumGenerator }
12
+ its(:by_name) { should == { :red => 1, :blue => 2 } }
13
+ end # describe Enum::Helpers::EnumGenerator
data/spec/enum_spec.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Enum do
4
+ context "keys on left and right" do
5
+ before { @enum = Enum.new(:MY_COLORS, :red => 1, 2 => :blue) }
6
+ subject { @enum }
7
+
8
+ its(:names) { should have(2).items }
9
+ its(:names) { should include(:red, :blue) }
10
+ its(:values) { should have(2).items }
11
+ its(:values) { should include(1, 2) }
12
+ its(:to_a) { should have(2).items }
13
+ end # context "keys on left and right"
14
+
15
+ context "no parent" do
16
+ before { @enum = Enum.new(:MY_COLORS, :red => 1, :blue => 2) }
17
+ subject { @enum }
18
+
19
+ its(:to_s) { should == "MY_COLORS" }
20
+ its(:inspect) { should == "MY_COLORS(:red => 1, :blue => 2)" }
21
+ end # context "no parent"
22
+
23
+ context "with parent" do
24
+ before { @enum = Enum.new(:MY_COLORS, Object, :red => 1, :blue => 2) }
25
+ subject { @enum }
26
+
27
+ its(:to_s) { should == "Object::MY_COLORS" }
28
+ its(:inspect) { should == "Object::MY_COLORS(:red => 1, :blue => 2)" }
29
+ end # context "with parent"
30
+ end # describe Enum
data/yinum.gemspec CHANGED
@@ -3,12 +3,12 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "yinum"
6
- s.version = "0.0.1"
6
+ s.version = "1.0.0"
7
7
  s.authors = ["Oded Niv"]
8
8
  s.email = ["oded.niv@gmail.com"]
9
9
  s.homepage = "https://github.com/toplex/enum"
10
10
  s.summary = %q{Enum implementation}
11
- s.description = %q{Awesome enum implementation that gives integer values with a special wrapping.}
11
+ s.description = %q{Yummy implementation of enum that gives integer values with a special wrapping.}
12
12
 
13
13
  s.rubyforge_project = "yinum"
14
14
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yinum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ bindir: bin
11
11
  cert_chain: []
12
12
  date: 2012-12-06 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: Awesome enum implementation that gives integer values with a special
14
+ description: Yummy implementation of enum that gives integer values with a special
15
15
  wrapping.
16
16
  email:
17
17
  - oded.niv@gmail.com
@@ -24,11 +24,17 @@ files:
24
24
  - Gemfile.lock
25
25
  - README.md
26
26
  - Rakefile
27
- - lib/enum_value.rb
28
- - lib/helpers.rb
27
+ - lib/enum.rb
28
+ - lib/enum/enum_value.rb
29
+ - lib/enum/helpers.rb
30
+ - lib/enum/helpers/enum_columns.rb
31
+ - lib/enum/helpers/enum_generator.rb
29
32
  - lib/yinum.rb
33
+ - spec/enum/enum_value_spec.rb
34
+ - spec/enum/helpers/enum_columns_spec.rb
35
+ - spec/enum/helpers/enum_generator_spec.rb
36
+ - spec/enum_spec.rb
30
37
  - spec/spec_helper.rb
31
- - spec/yinum_spec.rb
32
38
  - yinum.gemspec
33
39
  homepage: https://github.com/toplex/enum
34
40
  licenses: []
@@ -55,5 +61,8 @@ signing_key:
55
61
  specification_version: 3
56
62
  summary: Enum implementation
57
63
  test_files:
64
+ - spec/enum/enum_value_spec.rb
65
+ - spec/enum/helpers/enum_columns_spec.rb
66
+ - spec/enum/helpers/enum_generator_spec.rb
67
+ - spec/enum_spec.rb
58
68
  - spec/spec_helper.rb
59
- - spec/yinum_spec.rb
data/spec/yinum_spec.rb DELETED
@@ -1,57 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Enum do
4
- context "keys on left and right" do
5
- before { @enum = Enum.new(:MY_COLORS, :red => 1, 2 => :blue) }
6
- subject { @enum }
7
-
8
- its(:names) { should have(2).items }
9
- its(:names) { should include(:red, :blue) }
10
- its(:values) { should have(2).items }
11
- its(:values) { should include(1, 2) }
12
- end # context "keys on left and right"
13
-
14
- context "no parent" do
15
- before { @enum = Enum.new(:MY_COLORS, :red => 1, :blue => 2) }
16
- subject { @enum }
17
-
18
- its(:to_s) { should == "MY_COLORS" }
19
- its(:inspect) { should == "MY_COLORS(:red => 1, :blue => 2)" }
20
-
21
- context "enum value" do
22
- specify { @enum.red.inspect.should == "MY_COLORS.red" }
23
- specify { @enum.red.t.should == "I18n not available: enums.my_colors.red" }
24
- end # context "enum value"
25
- end # context "no parent"
26
-
27
- context "with parent" do
28
- before { @enum = Enum.new(:MY_COLORS, Object, :red => 1, :blue => 2) }
29
- subject { @enum }
30
-
31
- its(:to_s) { should == "Object::MY_COLORS" }
32
- its(:inspect) { should == "Object::MY_COLORS(:red => 1, :blue => 2)" }
33
-
34
- context "enum value" do
35
- specify { @enum.red.inspect.should == "Object::MY_COLORS.red" }
36
- specify { @enum.red.t.should == "I18n not available: enums.object.my_colors.red" }
37
- end # context "enum value"
38
- end # context "with parent"
39
-
40
- context "enum value" do
41
- before { @enum = Enum.new(:MY_COLORS, :red => 1, :blue => 2) }
42
- subject { @enum }
43
-
44
- its(:red) { should == 1 }
45
- its(:blue) { should_not == 1 }
46
- its(:red) { should == :red }
47
- its(:blue) { should_not == :red }
48
- its(:red) { should === 1 }
49
- its(:blue) { should_not === 1 }
50
- its(:red) { should === :red }
51
- its(:blue) { should_not === :red }
52
- its(:red) { should be_red }
53
- its(:blue) { should_not be_red }
54
- specify { @enum.red.object_id.should == @enum[:red].object_id }
55
- specify { @enum.red.object_id.should == @enum[1].object_id }
56
- end # context "enum value"
57
- end # describe Enum