galetahub-enum_field 0.4.0 → 0.5.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.
- checksums.yaml +4 -4
- data/README.md +138 -0
- data/lib/enum_field.rb +13 -9
- data/lib/enum_field/builder.rb +50 -55
- data/lib/enum_field/define_enum.rb +39 -10
- data/lib/enum_field/enumerated_attribute.rb +9 -41
- data/lib/enum_field/version.rb +1 -1
- data/spec/enum_field/define_enum_spec.rb +128 -0
- data/spec/enum_field/enumerated_attribute_spec.rb +42 -0
- data/spec/enum_field_spec.rb +11 -0
- data/spec/spec_helper.rb +2 -0
- metadata +76 -12
- data/README.rdoc +0 -143
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8fe01fd9a421a42a8bc5a49fc1f1f57d78b3ef8
|
4
|
+
data.tar.gz: 3b86f719ab1b2dc329af61fca85ffff9e797bf42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c51540d123b2444fbc79dcde8457590960911e5147d19aac45931eda9ce5aa842efe64bd95e5fa3e258e5699e51d9fe3b47c33348290be6dfd9040d003f634e8
|
7
|
+
data.tar.gz: 744b7af8a17cf2c2ee51f5a1d154a42ad03926a2a5e8f72943d63f5053110f658a7ce2bfe97c50f7ed2ad3040fd9e7cffc6df90b415ebd40f5d01e782beb7446
|
data/README.md
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
# enum_field
|
2
|
+
|
3
|
+
[](https://semaphoreci.com/igor-galeta/enum_field)
|
4
|
+
[](https://codeclimate.com/github/galetahub/enum_field)
|
5
|
+
|
6
|
+
Enables Active Record attributes to point to enum like objects, by saving in your database
|
7
|
+
only an integer ID.
|
8
|
+
|
9
|
+
## INSTALL:
|
10
|
+
|
11
|
+
gem 'galetahub-enum_field', require: 'enum_field'
|
12
|
+
|
13
|
+
## FEATURES:
|
14
|
+
|
15
|
+
* Allows creation of Classes with enum like behaviour.
|
16
|
+
* Allows any number of members and methods in the enum classes.
|
17
|
+
* Allows an integer id to be used in your database columns to link to the enum members (user.role_id)
|
18
|
+
* Enables higher abstraction interaction with +AR+ attributes:
|
19
|
+
* <code>user.role = Role.admin</code>
|
20
|
+
* <code>if user.role.can_edit?</code>
|
21
|
+
* Saves in your +AR+ tables, only an integer id pointing to the enumeration member.
|
22
|
+
|
23
|
+
## SYNOPSIS:
|
24
|
+
|
25
|
+
When in an Active Record class, you have an attribute like role, state or country you have
|
26
|
+
several options.
|
27
|
+
|
28
|
+
* You can create a roles, states or countries table, and dump there all possible values.
|
29
|
+
* You can use a string to identify, for instance, the role.
|
30
|
+
* You can use an id to identify the role.
|
31
|
+
|
32
|
+
If you are not comfortable with any of this options, maybe +enum_field+ is an answer for you.
|
33
|
+
|
34
|
+
## BASIC USAGE:
|
35
|
+
|
36
|
+
Define rules:
|
37
|
+
|
38
|
+
``` ruby
|
39
|
+
class Role
|
40
|
+
include EnumField::DefineEnum
|
41
|
+
|
42
|
+
define_enum do
|
43
|
+
member :admin
|
44
|
+
member :manager
|
45
|
+
member :employee
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class User < ActiveRecord::Base
|
50
|
+
extend EnumField::EnumeratedAttribute
|
51
|
+
|
52
|
+
# in the database table there is a role_id integer column
|
53
|
+
enumerated_attribute :role
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
Usage:
|
58
|
+
|
59
|
+
``` ruby
|
60
|
+
user.role = Role.manager
|
61
|
+
user.role_id == Role.manager.id # will be true
|
62
|
+
|
63
|
+
Role.manager.name # :manager
|
64
|
+
user.role.name # :manager
|
65
|
+
|
66
|
+
User.first.role.id == User.first.role_id # will be true
|
67
|
+
|
68
|
+
Role[:manager] == Role.manager # will be true
|
69
|
+
Role['manager'] == Role.manager # will be true
|
70
|
+
|
71
|
+
instance = Role[:employee]
|
72
|
+
instance.admin? # false
|
73
|
+
instance.employee? # true
|
74
|
+
```
|
75
|
+
|
76
|
+
Your enum classes can have all the methods you need:
|
77
|
+
|
78
|
+
``` ruby
|
79
|
+
class PhoneType
|
80
|
+
include EnumField::DefineEnum
|
81
|
+
|
82
|
+
def initialize(name)
|
83
|
+
@name = name
|
84
|
+
end
|
85
|
+
|
86
|
+
define_enum do
|
87
|
+
member :home, object: new('home')
|
88
|
+
member :commercial, object: new('commercial')
|
89
|
+
member :mobile, object: new('mobile')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
user.phone.type.name
|
94
|
+
```
|
95
|
+
|
96
|
+
You have some +AR+ like methods in enum classes
|
97
|
+
|
98
|
+
``` ruby
|
99
|
+
PhoneType.all == [PhoneType.home, PhoneType.commercial, PhoneType.mobile] # ordered all
|
100
|
+
PhoneType.first == PhoneType.home
|
101
|
+
PhoneType.last == PhoneType.mobile
|
102
|
+
|
103
|
+
PhoneType.find_by_id(PhoneType.home.id) == PhoneType.home
|
104
|
+
PhoneType.find_by_id(123456) == nil
|
105
|
+
PhoneType.find(2) == PhoneType.commercial
|
106
|
+
PhoneType.find(123456) # will raise
|
107
|
+
|
108
|
+
PhoneType.find([1, 2]) == [PhoneType.home, PhoneType.commercial]
|
109
|
+
```
|
110
|
+
|
111
|
+
### Start id from specific number
|
112
|
+
|
113
|
+
``` ruby
|
114
|
+
class CommentType
|
115
|
+
include EnumField::DefineEnum
|
116
|
+
|
117
|
+
define_enum id_start_from: 100 do
|
118
|
+
member :video
|
119
|
+
member :audio
|
120
|
+
member :text
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
CommentType.video.id # 101
|
125
|
+
CommentType.audio.id # 102
|
126
|
+
CommentType.text.id # 103
|
127
|
+
```
|
128
|
+
|
129
|
+
## Tests
|
130
|
+
|
131
|
+
bundle install
|
132
|
+
bundle exec rspec ./spec/
|
133
|
+
|
134
|
+
## LICENSE
|
135
|
+
|
136
|
+
(The MIT License)
|
137
|
+
|
138
|
+
Copyright (c) 2017 Fodojo LLC
|
data/lib/enum_field.rb
CHANGED
@@ -1,20 +1,24 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
|
3
|
+
# Basic usage:
|
4
|
+
#
|
5
|
+
# class Role
|
6
|
+
# include EnumField::DefineEnum
|
7
|
+
#
|
8
|
+
# define_enum do
|
9
|
+
# member :admin
|
10
|
+
# member :manager
|
11
|
+
# member :employee
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
2
15
|
module EnumField
|
3
16
|
autoload :DefineEnum, 'enum_field/define_enum'
|
4
17
|
autoload :Builder, 'enum_field/builder'
|
5
18
|
autoload :EnumeratedAttribute, 'enum_field/enumerated_attribute'
|
6
19
|
autoload :Version, 'enum_field/version'
|
7
|
-
|
8
|
-
class BadId < StandardError
|
9
|
-
attr_reader :repeated_id
|
10
|
-
|
11
|
-
def initialize(repeated_id)
|
12
|
-
@repeated_id = repeated_id
|
13
|
-
end
|
14
|
-
end
|
15
20
|
|
16
21
|
class RepeatedId < StandardError; end
|
17
22
|
class InvalidId < StandardError; end
|
18
|
-
class InvalidOptions < StandardError; end
|
19
23
|
class ObjectNotFound < StandardError; end
|
20
24
|
end
|
data/lib/enum_field/builder.rb
CHANGED
@@ -1,96 +1,91 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
|
2
3
|
module EnumField
|
3
4
|
class Builder
|
5
|
+
METHODS = %w[all names find_by_id find first last ids].freeze
|
4
6
|
|
5
|
-
|
7
|
+
attr_reader :members
|
6
8
|
|
7
|
-
def initialize(target)
|
9
|
+
def initialize(target, options = {})
|
8
10
|
@target = target
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@name2obj = {}
|
12
|
-
@sorted = []
|
11
|
+
@options = options
|
12
|
+
@members = {}
|
13
13
|
end
|
14
14
|
|
15
15
|
def member(name, options = {})
|
16
|
-
|
17
|
-
|
18
|
-
assign_name(obj, name)
|
19
|
-
define_in_meta(name) { obj }
|
20
|
-
save(name, obj)
|
21
|
-
obj.freeze
|
16
|
+
unique_name = normalize_name(name)
|
17
|
+
@members[unique_name] = create_new_object(unique_name, options)
|
22
18
|
end
|
23
19
|
|
24
20
|
def all
|
25
|
-
@
|
21
|
+
@members.values
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](value)
|
25
|
+
unique_name = normalize_name(value)
|
26
|
+
@members[unique_name]
|
26
27
|
end
|
27
28
|
|
28
29
|
def names
|
29
|
-
@
|
30
|
+
@members.keys
|
31
|
+
end
|
32
|
+
|
33
|
+
def first
|
34
|
+
key = names.first
|
35
|
+
@members[key]
|
36
|
+
end
|
37
|
+
|
38
|
+
def last
|
39
|
+
key = names.last
|
40
|
+
@members[key]
|
41
|
+
end
|
42
|
+
|
43
|
+
def ids
|
44
|
+
all.map(&:id)
|
30
45
|
end
|
31
46
|
|
32
47
|
def find(id)
|
33
|
-
find_by_id(id)
|
48
|
+
find_by_id(id) || raise(EnumField::ObjectNotFound)
|
34
49
|
end
|
35
50
|
|
36
51
|
def find_by_id(id)
|
37
52
|
case id
|
38
|
-
when Integer, String, Float, Fixnum then
|
39
|
-
@id2obj[id.to_i]
|
40
53
|
when Array then
|
41
|
-
id.
|
42
|
-
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
items
|
47
|
-
end
|
54
|
+
all.select { |object| id.include?(object.id) }
|
55
|
+
else
|
56
|
+
all.detect { |object| object.id == id }
|
48
57
|
end
|
49
58
|
end
|
50
59
|
|
51
|
-
def first; @sorted.first; end
|
52
|
-
def last; @sorted.last; end
|
53
|
-
|
54
60
|
private
|
55
61
|
|
56
|
-
def
|
57
|
-
|
58
|
-
metaclass.send(:define_method, name, &block)
|
59
|
-
end
|
62
|
+
def create_new_object(name, options)
|
63
|
+
object = (options[:object] || @target.new)
|
60
64
|
|
61
|
-
|
62
|
-
id
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
def assign_name(obj, name)
|
67
|
-
obj.instance_variable_set(:@name, name)
|
68
|
-
end
|
65
|
+
object.instance_variable_set(:@name, name)
|
66
|
+
object.instance_variable_set(:@id, find_next_object_id(options))
|
67
|
+
object.freeze
|
69
68
|
|
70
|
-
|
71
|
-
validate_candidate_id(candidate)
|
72
|
-
candidate || find_next_id
|
69
|
+
object
|
73
70
|
end
|
74
71
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
72
|
+
def find_next_object_id(options)
|
73
|
+
new_id = (options[:id] || generate_next_object_id)
|
74
|
+
validate_candidate_id!(new_id)
|
75
|
+
new_id
|
78
76
|
end
|
79
77
|
|
80
|
-
def
|
81
|
-
@
|
82
|
-
@next_id
|
78
|
+
def generate_next_object_id
|
79
|
+
@options.fetch(:id_start_from, 0) + @members.size + 1
|
83
80
|
end
|
84
81
|
|
85
|
-
def
|
86
|
-
raise EnumField::
|
87
|
-
|
82
|
+
def validate_candidate_id!(id)
|
83
|
+
raise EnumField::InvalidId.new(message: id) if id.nil?
|
84
|
+
raise EnumField::RepeatedId.new(message: id) if ids.include?(id)
|
88
85
|
end
|
89
86
|
|
90
|
-
def
|
91
|
-
|
92
|
-
@sorted << obj
|
93
|
-
@name2obj[name] = obj
|
87
|
+
def normalize_name(value)
|
88
|
+
value.to_s.to_sym
|
94
89
|
end
|
95
90
|
end
|
96
91
|
end
|
@@ -1,26 +1,55 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
|
2
3
|
module EnumField
|
4
|
+
# Add enum methods to class
|
5
|
+
# Usage:
|
6
|
+
# class Role
|
7
|
+
# include EnumField::DefineEnum
|
8
|
+
# ...
|
9
|
+
# end
|
10
|
+
#
|
3
11
|
module DefineEnum
|
4
12
|
def self.included(base)
|
5
13
|
base.send :extend, ClassMethods
|
14
|
+
base.send :include, InstanceMethods
|
6
15
|
end
|
7
16
|
|
8
17
|
module ClassMethods
|
9
|
-
def
|
10
|
-
|
11
|
-
|
18
|
+
def define_enum(options = {}, &block)
|
19
|
+
@enum_builder ||= EnumField::Builder.new(self, options)
|
20
|
+
@enum_builder.instance_exec(&block)
|
21
|
+
|
22
|
+
EnumField::Builder::METHODS.each do |method|
|
23
|
+
define_singleton_method method do |*args, &method_block|
|
24
|
+
@enum_builder.send(method, *args, &method_block)
|
25
|
+
end
|
12
26
|
end
|
13
|
-
end
|
14
27
|
|
15
|
-
|
16
|
-
|
17
|
-
|
28
|
+
@enum_builder.members.keys.each do |method|
|
29
|
+
define_singleton_method method do
|
30
|
+
@enum_builder.members[method]
|
31
|
+
end
|
18
32
|
|
19
|
-
|
20
|
-
|
21
|
-
@enum_builder.send(method, *args, &block)
|
33
|
+
define_method "#{method}?" do
|
34
|
+
@name == method
|
22
35
|
end
|
23
36
|
end
|
37
|
+
|
38
|
+
define_singleton_method '[]' do |value|
|
39
|
+
@enum_builder[value]
|
40
|
+
end
|
41
|
+
|
42
|
+
@enum_builder.members.freeze
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module InstanceMethods
|
47
|
+
def id
|
48
|
+
@id
|
49
|
+
end
|
50
|
+
|
51
|
+
def name
|
52
|
+
@name
|
24
53
|
end
|
25
54
|
end
|
26
55
|
end
|
@@ -1,4 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'active_support/inflector'
|
4
|
+
|
2
5
|
module EnumField
|
3
6
|
# Easies the inclusion of enumerations as ActiveRecord columns.
|
4
7
|
# If you have a User AR, with a role_id column, you could do:
|
@@ -19,7 +22,7 @@ module EnumField
|
|
19
22
|
# class User
|
20
23
|
# extend EnumField::EnumeratedAttribute
|
21
24
|
#
|
22
|
-
#
|
25
|
+
# enumerated_attribute :role
|
23
26
|
# end
|
24
27
|
#
|
25
28
|
# class UserRole < ActiveRecord::Base
|
@@ -41,53 +44,18 @@ module EnumField
|
|
41
44
|
# * +class+: the class that will be instantiated when +name_attribute+ method is called. Defaults to
|
42
45
|
# +name_attribute+ in camelcase form.
|
43
46
|
#
|
44
|
-
def enumerated_attribute(name_attribute, options
|
47
|
+
def enumerated_attribute(name_attribute, options = {})
|
45
48
|
id_attribute = (options[:id_attribute] || "#{name_attribute}_id").to_sym
|
46
49
|
klass = options[:class] || (options[:class_name] || name_attribute).to_s.camelcase.constantize
|
47
50
|
|
48
51
|
define_method(name_attribute) do
|
49
|
-
|
52
|
+
raw = send(id_attribute)
|
53
|
+
klass.find_by_id(raw)
|
50
54
|
end
|
51
55
|
|
52
56
|
define_method("#{name_attribute}=") do |value|
|
53
|
-
|
54
|
-
|
55
|
-
end
|
56
|
-
|
57
|
-
# alias of enumerated_attribute
|
58
|
-
alias belongs_to_enumerated_attribute enumerated_attribute
|
59
|
-
|
60
|
-
# Defines a one-to-many association between an AR class and the enumerated
|
61
|
-
# * +association+: the name of the one-to-many association, for instance +roles+
|
62
|
-
# * +options+: Valid options are:
|
63
|
-
# * +through+ : the name of the AR class needed to persist the one-to-many association.
|
64
|
-
# Defaults to AR class in camelcase form concatenated with the enumerated class in camelcase form.
|
65
|
-
# * +class+: the enumerated class, it will be instantiated +n+ times when +association+ method is called.
|
66
|
-
# Defaults to +association+ in singular camelcase form.
|
67
|
-
def has_many_enumerated_attributes(association, options = {})
|
68
|
-
enum_attr = association.to_s.singularize
|
69
|
-
klass = options[:class] || enum_attr.camelcase.constantize
|
70
|
-
through = options[:through] || (self.name + klass.name)
|
71
|
-
self_attribute = self.name.demodulize.underscore
|
72
|
-
association_ids = association.to_s.singularize + '_ids'
|
73
|
-
has_many_aux = through.demodulize.underscore.pluralize
|
74
|
-
|
75
|
-
has_many has_many_aux, {:class_name => through, :dependent => :destroy}
|
76
|
-
|
77
|
-
define_method(association) do
|
78
|
-
self.send(has_many_aux).map(&enum_attr.to_sym)
|
79
|
-
end
|
80
|
-
|
81
|
-
define_method(association.to_s + '=') do |values|
|
82
|
-
self.send(has_many_aux + '=', values.map{|g| through.constantize.new(self_attribute => self, enum_attr => g)})
|
83
|
-
end
|
84
|
-
|
85
|
-
define_method(association_ids) do
|
86
|
-
self.send(association).map(&:id)
|
87
|
-
end
|
88
|
-
|
89
|
-
define_method(association_ids + '=') do |values|
|
90
|
-
self.send(has_many_aux + '=', values.map{|g| g.to_i unless g.blank?}.compact.map{|g_id| through.constantize.new(self_attribute => self, enum_attr + '_id' => g_id) })
|
57
|
+
raw = value ? value.id : nil
|
58
|
+
send("#{id_attribute}=", raw)
|
91
59
|
end
|
92
60
|
end
|
93
61
|
end
|
data/lib/enum_field/version.rb
CHANGED
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EnumField::DefineEnum do
|
4
|
+
# Sample ruby class
|
5
|
+
class Role
|
6
|
+
include EnumField::DefineEnum
|
7
|
+
|
8
|
+
define_enum do
|
9
|
+
member :admin
|
10
|
+
member :manager
|
11
|
+
member :employee
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'must setup class methods for fast instance get' do
|
16
|
+
expect(Role.admin).not_to eq nil
|
17
|
+
expect(Role.admin.id).to eq 1
|
18
|
+
expect(Role.admin.name).to eq :admin
|
19
|
+
|
20
|
+
expect(Role[:admin]).to eq(Role.admin)
|
21
|
+
expect(Role['admin']).to eq(Role.admin)
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'static' do
|
25
|
+
it 'must get all instances' do
|
26
|
+
expect(Role.all.size).to eq 3
|
27
|
+
expect(Role.all).to eq [Role.admin, Role.manager, Role.employee]
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'must get all names' do
|
31
|
+
expect(Role.names.size).to eq 3
|
32
|
+
expect(Role.names).to eq [:admin, :manager, :employee]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'must find_by_id one instance' do
|
36
|
+
expect(Role.find_by_id(1)).to eq Role.admin
|
37
|
+
expect(Role.find_by_id(2)).to eq Role.manager
|
38
|
+
expect(Role.find_by_id(3)).to eq Role.employee
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'must find_by_id array of instances' do
|
42
|
+
expect(Role.find_by_id([2, 3])).to eq [Role.manager, Role.employee]
|
43
|
+
expect(Role.find_by_id([nil, 1])).to eq [Role.admin]
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'must not find instance via wrong id' do
|
47
|
+
expect(Role.find_by_id(4)).to eq nil
|
48
|
+
expect(Role.find_by_id('1')).to eq nil
|
49
|
+
expect(Role.find_by_id(nil)).to eq nil
|
50
|
+
expect(Role.find_by_id('wrong')).to eq nil
|
51
|
+
expect(Role.find_by_id(0)).to eq nil
|
52
|
+
expect(Role.find_by_id(-1)).to eq nil
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'must find one instance' do
|
56
|
+
expect(Role.find(1)).to eq Role.admin
|
57
|
+
expect(Role.find(2)).to eq Role.manager
|
58
|
+
expect(Role.find(3)).to eq Role[:employee]
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'must not find one instance with invalid id' do
|
62
|
+
expect { Role.find(100) }.to raise_error(EnumField::ObjectNotFound)
|
63
|
+
expect { Role.find(-1) }.to raise_error(EnumField::ObjectNotFound)
|
64
|
+
expect { Role.find('1') }.to raise_error(EnumField::ObjectNotFound)
|
65
|
+
expect { Role.find(nil) }.to raise_error(EnumField::ObjectNotFound)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'must get first instance' do
|
69
|
+
expect(Role.first).to eq(Role.admin)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'must get last instance' do
|
73
|
+
expect(Role.last).to eq(Role.employee)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'must get all ids' do
|
77
|
+
expect(Role.ids).to eq([1, 2, 3])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'wrong id' do
|
82
|
+
it 'must raise error on dublicated id' do
|
83
|
+
expect do
|
84
|
+
# Wrong class
|
85
|
+
class PostType
|
86
|
+
include EnumField::DefineEnum
|
87
|
+
|
88
|
+
define_enum do
|
89
|
+
member :default, id: 1
|
90
|
+
member :video, id: 1
|
91
|
+
member :audio, id: 2
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end.to raise_error(EnumField::RepeatedId)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'check' do
|
99
|
+
let(:instance) { Role[:admin] }
|
100
|
+
|
101
|
+
it 'must check methods' do
|
102
|
+
expect(instance.admin?).to eq true
|
103
|
+
expect(instance.manager?).to eq false
|
104
|
+
expect(instance.employee?).to eq false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'id start number' do
|
109
|
+
let(:start_number) { 100 }
|
110
|
+
let(:comment_type) do
|
111
|
+
Class.new(Object) do
|
112
|
+
include EnumField::DefineEnum
|
113
|
+
|
114
|
+
define_enum id_start_from: 100 do
|
115
|
+
member :video
|
116
|
+
member :audio
|
117
|
+
member :text
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'must set id + start_number' do
|
123
|
+
expect(comment_type.video.id).to eq start_number + 1
|
124
|
+
expect(comment_type.audio.id).to eq start_number + 2
|
125
|
+
expect(comment_type.text.id).to eq start_number + 3
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EnumField::EnumeratedAttribute do
|
4
|
+
# Sample ruby class
|
5
|
+
class RoleType
|
6
|
+
include EnumField::DefineEnum
|
7
|
+
|
8
|
+
define_enum do
|
9
|
+
member :admin
|
10
|
+
member :manager
|
11
|
+
member :employee
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Sample ruby class
|
16
|
+
class User
|
17
|
+
extend EnumField::EnumeratedAttribute
|
18
|
+
|
19
|
+
attr_accessor :role_type_id
|
20
|
+
|
21
|
+
enumerated_attribute :role_type
|
22
|
+
|
23
|
+
def initialize(role_type_id)
|
24
|
+
@role_type_id = role_type_id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:admin_user) { User.new(RoleType.admin.id) }
|
29
|
+
let(:new_role) { RoleType.employee }
|
30
|
+
|
31
|
+
it 'must set role value' do
|
32
|
+
expect(admin_user.role_type).to eq RoleType.admin
|
33
|
+
expect(admin_user.role_type_id).to eq RoleType.admin.id
|
34
|
+
expect(admin_user.role_type.admin?).to eq true
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'must set role_type_id' do
|
38
|
+
expect {
|
39
|
+
admin_user.role_type = new_role
|
40
|
+
}.to change { admin_user.role_type_id }.from(RoleType.admin.id).to(new_role.id)
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: galetahub-enum_field
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Galeta
|
@@ -9,24 +9,84 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
12
|
+
date: 2017-04-28 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: bundler
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '1.12'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '1.12'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '10.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '10.0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rspec
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '3.0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.0'
|
14
70
|
description: Enables Active Record attributes to point to enum like objects, by saving
|
15
71
|
in your database only an integer ID
|
16
72
|
email: galeta.igor@gmail.com
|
17
73
|
executables: []
|
18
74
|
extensions: []
|
19
75
|
extra_rdoc_files:
|
20
|
-
- README.
|
76
|
+
- README.md
|
21
77
|
files:
|
78
|
+
- MIT-LICENSE
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- lib/enum_field.rb
|
22
82
|
- lib/enum_field/builder.rb
|
23
83
|
- lib/enum_field/define_enum.rb
|
24
84
|
- lib/enum_field/enumerated_attribute.rb
|
25
85
|
- lib/enum_field/version.rb
|
26
|
-
-
|
27
|
-
-
|
28
|
-
-
|
29
|
-
-
|
86
|
+
- spec/enum_field/define_enum_spec.rb
|
87
|
+
- spec/enum_field/enumerated_attribute_spec.rb
|
88
|
+
- spec/enum_field_spec.rb
|
89
|
+
- spec/spec_helper.rb
|
30
90
|
homepage: https://github.com/galetahub/enum_field
|
31
91
|
licenses: []
|
32
92
|
metadata: {}
|
@@ -45,9 +105,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
45
105
|
- !ruby/object:Gem::Version
|
46
106
|
version: '0'
|
47
107
|
requirements: []
|
48
|
-
rubyforge_project:
|
49
|
-
rubygems_version: 2.1
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.5.1
|
50
110
|
signing_key:
|
51
111
|
specification_version: 4
|
52
|
-
summary: Enumerated attributes for Active Record
|
53
|
-
test_files:
|
112
|
+
summary: Enumerated attributes for any ruby class aka Active Record
|
113
|
+
test_files:
|
114
|
+
- spec/enum_field/define_enum_spec.rb
|
115
|
+
- spec/enum_field/enumerated_attribute_spec.rb
|
116
|
+
- spec/enum_field_spec.rb
|
117
|
+
- spec/spec_helper.rb
|
data/README.rdoc
DELETED
@@ -1,143 +0,0 @@
|
|
1
|
-
= enum_field
|
2
|
-
|
3
|
-
* http://github.com/paraseba/enum_field
|
4
|
-
|
5
|
-
== DESCRIPTION:
|
6
|
-
|
7
|
-
Enables Active Record attributes to point to enum like objects, by saving in your database
|
8
|
-
only an integer ID.
|
9
|
-
|
10
|
-
== INSTALL:
|
11
|
-
|
12
|
-
gem 'galetahub-enum_field', require: 'enum_field'
|
13
|
-
|
14
|
-
|
15
|
-
== FEATURES:
|
16
|
-
|
17
|
-
* Allows creation of Classes with enum like behaviour.
|
18
|
-
* Allows any number of members and methods in the enum classes.
|
19
|
-
* Allows an integer id to be used in your database columns to link to the enum members (user.role_id)
|
20
|
-
* Enables higher abstraction interaction with +AR+ attributes:
|
21
|
-
* <code>user.role = Role.admin</code>
|
22
|
-
* <code>if user.role.can_edit?</code>
|
23
|
-
* Saves in your +AR+ tables, only an integer id pointing to the enumeration member.
|
24
|
-
|
25
|
-
== SYNOPSIS:
|
26
|
-
|
27
|
-
When in an Active Record class, you have an attribute like role, state or country you have
|
28
|
-
several options.
|
29
|
-
|
30
|
-
* You can create a roles, states or countries table, and dump there all possible values.
|
31
|
-
* You can use a string to identify, for instance, the role.
|
32
|
-
* You can use an id to identify the role.
|
33
|
-
|
34
|
-
If you are not comfortable with any of this options, maybe +enum_field+ is an answer for you.
|
35
|
-
|
36
|
-
== BASIC USAGE:
|
37
|
-
|
38
|
-
class Role
|
39
|
-
include EnumField::DefineEnum
|
40
|
-
|
41
|
-
define_enum do |builder|
|
42
|
-
builder.member :admin
|
43
|
-
builder.member :manager
|
44
|
-
builder.member :employee
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
class User < ActiveRecord::Base
|
49
|
-
extend EnumField::EnumeratedAttribute
|
50
|
-
|
51
|
-
# in the database table there is a role_id integer column
|
52
|
-
enumerated_attribute :role
|
53
|
-
end
|
54
|
-
|
55
|
-
|
56
|
-
link_to_if(current_user.role == Role.admin, edit_foo_path(@foo))
|
57
|
-
|
58
|
-
user.role = Role.manager
|
59
|
-
user.role_id == Role.manager.id #will be true
|
60
|
-
|
61
|
-
Role.manager.name # :manager
|
62
|
-
user.role.name # :manager
|
63
|
-
|
64
|
-
User.first.role.id == User.first.role_id #will be true
|
65
|
-
|
66
|
-
Your enum classes can have all the methods you need:
|
67
|
-
|
68
|
-
class PhoneType
|
69
|
-
include EnumField::DefineEnum
|
70
|
-
|
71
|
-
def initialize(name)
|
72
|
-
@name = name
|
73
|
-
end
|
74
|
-
attr_reader :name
|
75
|
-
|
76
|
-
define_enum do |b|
|
77
|
-
b.member :home, :object => new('home')
|
78
|
-
b.member :commercial, :object => new('commercial')
|
79
|
-
b.member :mobile, :object => new('mobile')
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
user.phone.type.name
|
84
|
-
|
85
|
-
You have some +AR+ like methods in enum classes
|
86
|
-
|
87
|
-
PhoneType.all == [PhoneType.home, PhoneType.commercial, PhoneType.mobile] # ordered all
|
88
|
-
PhoneType.first == PhoneType.home
|
89
|
-
PhoneType.last == PhoneType.mobile
|
90
|
-
|
91
|
-
PhoneType.find_by_id(PhoneType.home.id) == PhoneType.home
|
92
|
-
PhoneType.find_by_id(123456) == nil
|
93
|
-
PhoneType.find(2) == PhoneType.commercial
|
94
|
-
PhoneType.find(123456) # will raise
|
95
|
-
|
96
|
-
PhoneType.find([1, 2]) == [PhoneType.home, PhoneType.commercial]
|
97
|
-
|
98
|
-
The library also mimics has_many :through behavior, for cases such as:
|
99
|
-
|
100
|
-
class Role
|
101
|
-
include EnumField::DefineEnum
|
102
|
-
|
103
|
-
define_enum do |builder|
|
104
|
-
builder.member :admin
|
105
|
-
builder.member :manager
|
106
|
-
builder.member :employee
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
class User
|
111
|
-
extend EnumField::EnumeratedAttribute
|
112
|
-
|
113
|
-
has_many_enumerated_attributes :roles, :through => UserRole
|
114
|
-
end
|
115
|
-
|
116
|
-
class UserRole < ActiveRecord::Base
|
117
|
-
extend EnumField::EnumeratedAttribute
|
118
|
-
|
119
|
-
belongs_to :user
|
120
|
-
enumerated_attribute :role
|
121
|
-
end
|
122
|
-
|
123
|
-
user = User.create
|
124
|
-
user.role = [Role.manager, Role.admin]
|
125
|
-
user.roles.include?(Role.admin) #will be true
|
126
|
-
user.roles.include?(Role.manager) #will be true
|
127
|
-
user.roles.include?(Role.employee) #will be false
|
128
|
-
user.role_ids.include?(Role.manager.id) #will be true
|
129
|
-
user.role_ids = [Role.employee.id]
|
130
|
-
user.roles.include?(Role.employee) #will be true
|
131
|
-
user.roles.include?(Role.admin) #will be false
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
== REQUIREMENTS:
|
136
|
-
|
137
|
-
* activerecord
|
138
|
-
|
139
|
-
== LICENSE:
|
140
|
-
|
141
|
-
(The MIT License)
|
142
|
-
|
143
|
-
Copyright (c) 2009 Sebastián Bernardo Galkin
|