galetahub-enum_field 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://semaphoreci.com/api/v1/igor-galeta/enum_field/branches/master/shields_badge.svg)](https://semaphoreci.com/igor-galeta/enum_field)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/galetahub/enum_field/badges/gpa.svg)](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
|