yinum 0.0.1 → 1.0.0

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