rails_string_enum 0.1.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 +7 -0
- data/.gitignore +10 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +133 -0
- data/Rakefile +1 -0
- data/lib/rails_string_enum/patch_enum_null.rb +17 -0
- data/lib/rails_string_enum/pg_enum_migration.rb +143 -0
- data/lib/rails_string_enum/simple_form.rb +69 -0
- data/lib/rails_string_enum/string_enum.rb +131 -0
- data/lib/rails_string_enum/version.rb +3 -0
- data/lib/rails_string_enum.rb +20 -0
- data/rails_string_enum.gemspec +26 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3b9e6d5bf513ef92f4a9811d1b7156db72a41a92
|
4
|
+
data.tar.gz: 5b72388e7ee40dc6bae33b4932c3fd43d5a3c203
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b08ec344d78a24b071d4b597313e0f683f8d416e031cb9f8dee6ee8d3f3be87ee17c7043b1ccd3c7152d02675c98b22bb1733f3538afbb55c70b5d4134b34d8b
|
7
|
+
data.tar.gz: 1ffb81727cdf171425a883e2f123e3c95d7622d58ae7519dcf856f2616e38e2db38f8b1e91f9409fd62b886e673759a05ee2da7f9865748e701c51de1f421513
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Ermolaev Andrey
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
## rails_string_enum
|
2
|
+
support in rails db enums or string (using as flexible enum)
|
3
|
+
|
4
|
+
This gem inspired rails native enum and https://github.com/zmbacker/enum_help
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rails_string_enum'
|
10
|
+
|
11
|
+
|
12
|
+
Tested on Rails 4.2, ruby 2.2
|
13
|
+
|
14
|
+
#### Native postgresql enum (migrations)
|
15
|
+
```ruby
|
16
|
+
|
17
|
+
create_enum :color, %w(red green blue) # default schema is 'public'
|
18
|
+
create_enum :color, %w(red green blue), schema: 'cmyk'
|
19
|
+
|
20
|
+
add_enum_value :color, 'black'
|
21
|
+
add_enum_value :color, 'black', after: 'red'
|
22
|
+
add_enum_value :color, 'black', before: 'blue'
|
23
|
+
add_enum_value :color, 'black', schema: 'public'
|
24
|
+
|
25
|
+
drop_enum :color
|
26
|
+
drop_enum :color, schema: 'cmyk'
|
27
|
+
|
28
|
+
rename_enum_value :color, 'gray', 'black'
|
29
|
+
reorder_enum_values :order_state_enum, %w(black white) # other color will be latest
|
30
|
+
|
31
|
+
delete_enum_value :color, 'black'
|
32
|
+
# you should delete record with deleting value
|
33
|
+
# if exists index with condition method raise exeption
|
34
|
+
# "ERROR: operator does not exist: order_type_enum <> order_type_enum_new"
|
35
|
+
# you should first remove and then create an index
|
36
|
+
Product.where(color: 'black').delete_all # or Product.where(color: 'black').update_all(state: nil)
|
37
|
+
remove_index :product, :color, where: "color NOT IN ('pink')"
|
38
|
+
delete_enum_value :color, 'black'
|
39
|
+
add_index :product, :color, where: "color NOT IN ('pink')"
|
40
|
+
```
|
41
|
+
|
42
|
+
###### Convert exist columns (int, string) to postgresql enum
|
43
|
+
```ruby
|
44
|
+
string_to_enums :product, :color, enum_name: 'product_color_enum', definitions: %w(red green blue)
|
45
|
+
string_to_enums :product, :color, enum_name: 'product_color_enum', definitions: Product.uniq.pluck(:color)
|
46
|
+
|
47
|
+
int_to_enums :product, :color, enum_name: 'product_color_enum', definitions: { red: 0, green: 1, blue: 2 }
|
48
|
+
```
|
49
|
+
|
50
|
+
|
51
|
+
#### Usage
|
52
|
+
```ruby
|
53
|
+
class CreateTables < ActiveRecord::Migration
|
54
|
+
def change
|
55
|
+
create_table :products do |t|
|
56
|
+
t.string :color
|
57
|
+
end
|
58
|
+
|
59
|
+
create_enum :enum_color, %w(red green blue)
|
60
|
+
|
61
|
+
create_table :pages do |t|
|
62
|
+
t.column :color, :enum_color, default: 'red'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Product < ActiveRecord::Base
|
68
|
+
string_enum :color, %w(red green yellow black white), scope: true # default false
|
69
|
+
|
70
|
+
def self.colored
|
71
|
+
where.not(color: [COLOR::BLACK, COLOR::WHITE])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Page < ActiveRecord::Base
|
76
|
+
string_enum :background, %w(red green), i18n_scope: 'product.color'
|
77
|
+
end
|
78
|
+
|
79
|
+
Product::COLORS # => ["red", "green", "yellow"]
|
80
|
+
Product::COLOR::RED # => "red"
|
81
|
+
Product::COLOR::GREEN # => "green"
|
82
|
+
|
83
|
+
@product = Product.new
|
84
|
+
@product.color_i18n # => nil
|
85
|
+
@product.red!
|
86
|
+
@product.color_i18n # => 'Красный'
|
87
|
+
|
88
|
+
Product.color_i18n_for('red') # => 'Красный'
|
89
|
+
Product.colors_i18n # => {green: 'Зеленый', red: 'Красный', yellow: 'Желтый'}
|
90
|
+
Product.red # if scope: true
|
91
|
+
```
|
92
|
+
|
93
|
+
|
94
|
+
I18n local file example (compatible with https://github.com/zmbacker/enum_help):
|
95
|
+
|
96
|
+
```yaml
|
97
|
+
# config/locals/ru.yml
|
98
|
+
ru:
|
99
|
+
enums:
|
100
|
+
product:
|
101
|
+
color:
|
102
|
+
red: Красный
|
103
|
+
green: Зеленый
|
104
|
+
yellow: Желтый
|
105
|
+
```
|
106
|
+
|
107
|
+
Support `simple_form`:
|
108
|
+
```erb
|
109
|
+
<%= f.input :color %>
|
110
|
+
```
|
111
|
+
|
112
|
+
#### Benchmark i18n methods (rails_string_enum faster 2x then enum_help)
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
Benchmark.ips do |x|
|
116
|
+
x.report('rails_string_enum') { u.social_app_i18n }
|
117
|
+
x.report('enum_help') { u.social_app_i18n_eh }
|
118
|
+
x.report('rails_string_enum_c') { Order.states_i18n }
|
119
|
+
x.report('enum_help_c') { Order.states_i18n_eh }
|
120
|
+
end
|
121
|
+
|
122
|
+
Calculating -------------------------------------
|
123
|
+
rails_string_enum 3.149k i/100ms
|
124
|
+
enum_help 1.489k i/100ms
|
125
|
+
rails_string_enum_c 3.002k i/100ms
|
126
|
+
enum_help_c 1.384k i/100ms
|
127
|
+
-------------------------------------------------
|
128
|
+
rails_string_enum 42.727k (± 4.4%) i/s - 214.132k
|
129
|
+
enum_help 16.686k (±11.9%) i/s - 81.895k
|
130
|
+
rails_string_enum_c 38.159k (± 5.3%) i/s - 192.128k
|
131
|
+
enum_help_c 15.939k (± 4.5%) i/s - 80.272k
|
132
|
+
```
|
133
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'active_record/connection_adapters/postgresql/oid/enum'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module PostgreSQL
|
6
|
+
module OID # :nodoc:
|
7
|
+
class Enum < Type::Value # :nodoc:
|
8
|
+
def type_cast(value)
|
9
|
+
unless value.nil?
|
10
|
+
value.to_s
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module PgEnumMigrations
|
2
|
+
|
3
|
+
# create_enum :color, %w(red green blue) # default schema is 'public'
|
4
|
+
# create_enum :color, %w(red green blue), schema: 'cmyk'
|
5
|
+
def create_enum(enum_name, values, schema: nil)
|
6
|
+
execute "CREATE TYPE #{enum_name(enum_name, schema)} AS ENUM (#{escape_enum_values(values)})"
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
# add_enum_value :color, 'black'
|
11
|
+
# add_enum_value :color, 'purple', after: 'red'
|
12
|
+
# add_enum_value :color, 'pink', before: 'purple'
|
13
|
+
# add_enum_value :color, 'white', schema: 'public'
|
14
|
+
def add_enum_value(enum_name, value, before: nil, after: nil, schema: nil)
|
15
|
+
opts = if before then "BEFORE #{escape_enum_value(before)}"
|
16
|
+
elsif after then "AFTER #{escape_enum_value(after)}"
|
17
|
+
else ''
|
18
|
+
end
|
19
|
+
execute "ALTER TYPE #{enum_name(enum_name, schema)} ADD VALUE #{escape_enum_value(value)} #{opts}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# drop_enum :color
|
23
|
+
# drop_enum :color, schema: 'cmyk'
|
24
|
+
def drop_enum(enum_name, schema: nil)
|
25
|
+
execute "DROP TYPE #{enum_name(enum_name, schema)}"
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# you should delete record with deleting value
|
30
|
+
# Order.back_picking.delete_all or Order.back_picking.update_all(state: nil)
|
31
|
+
#
|
32
|
+
# if exists index with condition - add_index :orders, :state, where: "state NOT IN ('closed', 'canceled', 'returned')"
|
33
|
+
# this method show exeption ERROR: operator does not exist: order_type_enum <> order_type_enum_new
|
34
|
+
# you must first remove and then create an index
|
35
|
+
#
|
36
|
+
# delete_enum_value :order_state_enum, 'back_picking'
|
37
|
+
def delete_enum_value(enum_name, value_name, scheme: nil)
|
38
|
+
old_values = select_values("SELECT enumlabel FROM pg_catalog.pg_enum WHERE enumtypid = #{enum_name_as_srt(enum_name, scheme)}::regtype::oid")
|
39
|
+
new_values = old_values - Array(value_name)
|
40
|
+
|
41
|
+
execute <<-SQL
|
42
|
+
ALTER TYPE #{enum_name} rename to #{enum_name}_old;
|
43
|
+
CREATE TYPE #{enum_name} AS enum (#{escape_enum_values(new_values)});
|
44
|
+
SQL
|
45
|
+
|
46
|
+
cols_using_enum = select_rows("SELECT table_name, column_name FROM information_schema.columns WHERE udt_name = '#{enum_name}_old'")
|
47
|
+
cols_using_enum.each do |table_name, column_name|
|
48
|
+
execute <<-SQL
|
49
|
+
ALTER TABLE #{table_name}
|
50
|
+
ALTER COLUMN #{column_name} TYPE #{enum_name} USING #{column_name}::text::#{enum_name};
|
51
|
+
SQL
|
52
|
+
end
|
53
|
+
|
54
|
+
execute <<-SQL
|
55
|
+
DROP TYPE #{enum_name}_old
|
56
|
+
SQL
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
# rename_enum_value :order_state_enum, 'accept', 'init'
|
62
|
+
def rename_enum_value(enum_name, old_value_name, new_value_name, scheme: nil)
|
63
|
+
execute <<-SQL
|
64
|
+
UPDATE pg_catalog.pg_enum
|
65
|
+
SET enumlabel = '#{new_value_name}'
|
66
|
+
WHERE enumtypid = #{enum_name_as_srt(enum_name, scheme)}::regtype::oid AND enumlabel = '#{old_value_name}'
|
67
|
+
SQL
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
# reorder_enum_values :order_state_enum, %w(lost returning obtain accept)
|
73
|
+
def reorder_enum_values(enum_name, ordered_values, scheme: nil)
|
74
|
+
all_values = select_values("SELECT enumlabel FROM pg_catalog.pg_enum WHERE enumtypid = '#{scheme}.#{enum_name}'::regtype::oid")
|
75
|
+
max_order = select_value("SELECT max(enumsortorder) FROM pg_catalog.pg_enum WHERE enumtypid = '#{scheme}.#{enum_name}'::regtype::oid").to_i + 1
|
76
|
+
|
77
|
+
ordered_sql = (ordered_values | all_values).map.with_index{|v, i| "WHEN '#{v}' THEN #{i + max_order}"}.join(' ')
|
78
|
+
|
79
|
+
execute <<-SQL
|
80
|
+
UPDATE pg_catalog.pg_enum
|
81
|
+
SET enumsortorder = CASE enumlabel #{ordered_sql} END
|
82
|
+
WHERE enumtypid = #{enum_name_as_srt(enum_name, scheme)}::regtype::oid
|
83
|
+
SQL
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
# string_to_enums :order, :state, enum_name: 'order_state_enum', definitions: %w(accept confirmed)
|
89
|
+
def string_to_enums(table, col_name, enum_name:, definitions: nil, default: nil, use_exist_enum: false)
|
90
|
+
convert_to_enum table, col_name, enum_name, definitions, col_name, default, use_exist_enum
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
# int_to_enums :users, :partner_type, enum_name: 'user_partner_type_enum', definitions: { retail: 0, affiliate: 1, wholesale: 2 }
|
96
|
+
def int_to_enums(table, col_name, enum_name:, definitions: nil, default: nil, use_exist_enum: false)
|
97
|
+
convert_sql = definitions.map {|str, int| "WHEN #{int} THEN '#{str}'" }.join(' ')
|
98
|
+
convert_sql = "CASE #{col_name} #{convert_sql} END"
|
99
|
+
|
100
|
+
convert_to_enum table, col_name, enum_name, definitions.keys, convert_sql, default, use_exist_enum
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def enum_name(name, schema)
|
108
|
+
[schema || 'public', name].map { |s|
|
109
|
+
%Q{"#{s}"}
|
110
|
+
}.join('.')
|
111
|
+
end
|
112
|
+
|
113
|
+
def enum_name_as_srt(name, schema)
|
114
|
+
[schema || 'public', name].map { |s|
|
115
|
+
%Q{'#{s}'}
|
116
|
+
}.join('.')
|
117
|
+
end
|
118
|
+
|
119
|
+
def escape_enum_value(value)
|
120
|
+
escaped_value = value.to_s.sub("'", "''")
|
121
|
+
"'#{escaped_value}'"
|
122
|
+
end
|
123
|
+
|
124
|
+
def escape_enum_values(values)
|
125
|
+
values.map { |value| escape_enum_value(value) }.join(',')
|
126
|
+
end
|
127
|
+
|
128
|
+
def convert_to_enum(table, col_name, enum_name, keys, convert_sql, default, use_exist_enum)
|
129
|
+
schema = select_value("SELECT table_schema FROM information_schema.columns WHERE table_name = '#{table}'")
|
130
|
+
|
131
|
+
execute "CREATE TYPE #{enum_name(enum_name, schema)} AS ENUM (#{escape_enum_values(keys)})" unless use_exist_enum
|
132
|
+
|
133
|
+
default_sql = ", ALTER COLUMN #{col_name} DROP DEFAULT" if default
|
134
|
+
|
135
|
+
execute <<-SQL
|
136
|
+
ALTER TABLE #{table}
|
137
|
+
ALTER COLUMN #{col_name} DROP DEFAULT,
|
138
|
+
ALTER COLUMN #{col_name} TYPE #{enum_name} USING #{convert_sql}::#{enum_name}
|
139
|
+
#{default_sql};
|
140
|
+
SQL
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'simple_form'
|
2
|
+
|
3
|
+
module EnumHelp
|
4
|
+
module SimpleForm
|
5
|
+
module BuilderExtension
|
6
|
+
|
7
|
+
def default_input_type_with_enum(*args, &block)
|
8
|
+
attr_name = (args.first || @attribute_name).to_s
|
9
|
+
options = args.last
|
10
|
+
|
11
|
+
const_for_attr = object.respond_to? "#{attr_name}_i18n"
|
12
|
+
|
13
|
+
return :enum_radio_buttons if options.is_a?(Hash) && options[:as] == :radio_buttons && const_for_attr
|
14
|
+
return :enum if (options.is_a?(Hash) ? options[:as] : @options[:as]).nil? && const_for_attr
|
15
|
+
|
16
|
+
default_input_type_without_enum(*args, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module InputExtension
|
21
|
+
|
22
|
+
def initialize(*args)
|
23
|
+
super
|
24
|
+
enum = input_options[:collection] || @builder.options[:collection]
|
25
|
+
raise "Attribute '#{attribute_name}' has no enum class" unless enum ||= object.class.const_get(attribute_name.to_s.pluralize.upcase)
|
26
|
+
|
27
|
+
collect = begin
|
28
|
+
collection = object.class.send("#{attribute_name.to_s.pluralize}_i18n")
|
29
|
+
collection.slice!(*enum) if enum
|
30
|
+
collection.invert.to_a
|
31
|
+
end
|
32
|
+
|
33
|
+
# collect.unshift [args.last[:prompt],''] if args.last.is_a?(Hash) && args.last[:prompt]
|
34
|
+
|
35
|
+
if respond_to?(:input_options)
|
36
|
+
input_options[:collection] = collect
|
37
|
+
else
|
38
|
+
@builder.options[:collection] = collect
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
class EnumHelp::SimpleForm::EnumInput < ::SimpleForm::Inputs::CollectionSelectInput
|
49
|
+
include EnumHelp::SimpleForm::InputExtension
|
50
|
+
def input_html_classes
|
51
|
+
super.push('form-control')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
class EnumHelp::SimpleForm::EnumRadioButtons < ::SimpleForm::Inputs::CollectionRadioButtonsInput
|
57
|
+
include EnumHelp::SimpleForm::InputExtension
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
SimpleForm::FormBuilder.class_eval do
|
62
|
+
include EnumHelp::SimpleForm::BuilderExtension
|
63
|
+
|
64
|
+
map_type :enum, :to => EnumHelp::SimpleForm::EnumInput
|
65
|
+
map_type :enum_radio_buttons, :to => EnumHelp::SimpleForm::EnumRadioButtons
|
66
|
+
alias_method :collection_enum_radio_buttons, :collection_radio_buttons
|
67
|
+
alias_method :collection_enum, :collection_select
|
68
|
+
alias_method_chain :default_input_type, :enum
|
69
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module RailsStringEnum
|
2
|
+
|
3
|
+
# product.rb
|
4
|
+
# string_enum :color, %w(red green yellow)
|
5
|
+
# page.rb
|
6
|
+
# string_enum :background, %w(red green), i18n_scope: 'product.color'
|
7
|
+
|
8
|
+
def string_enum(name, enums, scopes: false, i18n_scope: nil)
|
9
|
+
# create constant with all values
|
10
|
+
# Product::COLORS # => ["red", "green", "yellow"]
|
11
|
+
const_name_all_values = name.to_s.pluralize.upcase
|
12
|
+
const_set const_name_all_values, enums.map(&:to_s)
|
13
|
+
|
14
|
+
# create constant for each value
|
15
|
+
# Product::COLOR::RED # => "red"
|
16
|
+
# Product::COLOR::GREEN #=> "green"
|
17
|
+
new_module = const_set name.to_s.upcase, Module.new
|
18
|
+
enums.each { |const| new_module.const_set(const.to_s.upcase, const.to_s) }
|
19
|
+
|
20
|
+
|
21
|
+
klass = self
|
22
|
+
enums.each do |value|
|
23
|
+
# def red?() color == 'red' end
|
24
|
+
klass.send(:detect_enum_conflict!, name, "#{value}?")
|
25
|
+
define_method("#{value}?") { self[name] == value }
|
26
|
+
|
27
|
+
# def red!() update! color: :red end
|
28
|
+
klass.send(:detect_enum_conflict!, name, "#{value}!")
|
29
|
+
define_method("#{value}!") { update! name => value }
|
30
|
+
|
31
|
+
if scopes
|
32
|
+
# scope :red, -> { where color: 'red' }
|
33
|
+
klass.send(:detect_enum_conflict!, name, value, true)
|
34
|
+
klass.scope value, -> { klass.where name => value }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
define_attr_i18n_method(self, name, i18n_scope)
|
39
|
+
define_collection_i18n_method(self, name, i18n_scope)
|
40
|
+
define_collection_i18n_method_for_value(self, name, i18n_scope)
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
ENUM_CONFLICT_MESSAGE = \
|
47
|
+
"You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
|
48
|
+
"this will generate a %{type} method \"%{method}\", which is already defined " \
|
49
|
+
"by %{source}."
|
50
|
+
|
51
|
+
def detect_enum_conflict!(enum_name, method_name, klass_method = false)
|
52
|
+
if klass_method && dangerous_class_method?(method_name)
|
53
|
+
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
|
54
|
+
enum: enum_name,
|
55
|
+
klass: self.name,
|
56
|
+
type: 'class',
|
57
|
+
method: method_name,
|
58
|
+
source: 'Active Record'
|
59
|
+
}
|
60
|
+
elsif !klass_method && dangerous_attribute_method?(method_name)
|
61
|
+
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
|
62
|
+
enum: enum_name,
|
63
|
+
klass: self.name,
|
64
|
+
type: 'instance',
|
65
|
+
method: method_name,
|
66
|
+
source: 'Active Record'
|
67
|
+
}
|
68
|
+
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
|
69
|
+
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
|
70
|
+
enum: enum_name,
|
71
|
+
klass: self.name,
|
72
|
+
type: 'instance',
|
73
|
+
method: method_name,
|
74
|
+
source: 'another enum'
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# @product.color_i18n => 'Красный'
|
81
|
+
def define_attr_i18n_method(klass, attr_name, i18n_scope)
|
82
|
+
attr_i18n_method_name = "#{attr_name}_i18n"
|
83
|
+
|
84
|
+
klass.class_eval <<-METHOD, __FILE__, __LINE__
|
85
|
+
def #{attr_i18n_method_name}
|
86
|
+
if enum_label = self.send(:#{attr_name})
|
87
|
+
#{ruby_string_for_enum_label(klass, attr_name, i18n_scope)}
|
88
|
+
else
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def #{attr_i18n_method_name}_for
|
94
|
+
#{ruby_string_for_enum_label(klass, attr_name, i18n_scope)}
|
95
|
+
end
|
96
|
+
METHOD
|
97
|
+
end
|
98
|
+
|
99
|
+
# Product.color_i18n_for('red') => 'Красный'
|
100
|
+
def define_collection_i18n_method_for_value(klass, attr_name, i18n_scope)
|
101
|
+
attr_i18n_method_name = "#{attr_name}_i18n"
|
102
|
+
|
103
|
+
klass.class_eval <<-METHOD, __FILE__, __LINE__
|
104
|
+
def #{attr_i18n_method_name}_for
|
105
|
+
#{ruby_string_for_enum_label(klass, attr_name, i18n_scope)}
|
106
|
+
end
|
107
|
+
METHOD
|
108
|
+
end
|
109
|
+
|
110
|
+
# Product.colors_i18n => {green: 'Зеленый', red: 'Красный', yellow: 'Желтый'}
|
111
|
+
def define_collection_i18n_method(klass, attr_name, i18n_scope)
|
112
|
+
collection_method_name = "#{attr_name.to_s.pluralize}_i18n"
|
113
|
+
collection_const_name = "#{attr_name.to_s.pluralize.upcase}"
|
114
|
+
|
115
|
+
klass.instance_eval <<-METHOD, __FILE__, __LINE__
|
116
|
+
def #{collection_method_name}
|
117
|
+
h = HashWithIndifferentAccess.new
|
118
|
+
self::#{collection_const_name}.each do |enum_label|
|
119
|
+
h[enum_label] = #{ruby_string_for_enum_label(klass, attr_name, i18n_scope)}
|
120
|
+
end
|
121
|
+
h
|
122
|
+
end
|
123
|
+
METHOD
|
124
|
+
end
|
125
|
+
|
126
|
+
def ruby_string_for_enum_label(klass, attr_name, i18n_scope)
|
127
|
+
part_scope = i18n_scope || "#{klass.base_class.to_s.underscore}.#{attr_name}"
|
128
|
+
%Q{::I18n.t(enum_label, scope: "enums.#{part_scope}", default: enum_label)}
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rails_string_enum/version'
|
2
|
+
|
3
|
+
|
4
|
+
require 'rails_string_enum/pg_enum_migration'
|
5
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
6
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :include, PgEnumMigrations
|
7
|
+
|
8
|
+
|
9
|
+
require 'rails_string_enum/string_enum'
|
10
|
+
ActiveRecord::Base.send :extend, RailsStringEnum
|
11
|
+
|
12
|
+
|
13
|
+
require 'rails_string_enum/patch_enum_null' if Rails.version <= "4.2.1"
|
14
|
+
|
15
|
+
|
16
|
+
begin
|
17
|
+
require 'simple_form'
|
18
|
+
require 'rails_string_enum/simple_form'
|
19
|
+
rescue
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rails_string_enum/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rails_string_enum"
|
8
|
+
spec.version = RailsStringEnum::VERSION
|
9
|
+
spec.authors = ["Ermolaev Andrey"]
|
10
|
+
spec.email = ["andrey@ermolaev.me"]
|
11
|
+
|
12
|
+
spec.summary = %q{support in rails db enums or string (using as flexible enum)}
|
13
|
+
spec.description = %q{migration methods for postgresql, internationalization, simple_form}
|
14
|
+
spec.homepage = 'https://github.com/ermolaev/rails_string_enum'
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
|
25
|
+
spec.add_dependency "activerecord", "~> 4.2"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_string_enum
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ermolaev Andrey
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-05-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.9'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activerecord
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.2'
|
55
|
+
description: migration methods for postgresql, internationalization, simple_form
|
56
|
+
email:
|
57
|
+
- andrey@ermolaev.me
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- lib/rails_string_enum.rb
|
68
|
+
- lib/rails_string_enum/patch_enum_null.rb
|
69
|
+
- lib/rails_string_enum/pg_enum_migration.rb
|
70
|
+
- lib/rails_string_enum/simple_form.rb
|
71
|
+
- lib/rails_string_enum/string_enum.rb
|
72
|
+
- lib/rails_string_enum/version.rb
|
73
|
+
- rails_string_enum.gemspec
|
74
|
+
homepage: https://github.com/ermolaev/rails_string_enum
|
75
|
+
licenses:
|
76
|
+
- MIT
|
77
|
+
metadata: {}
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 2.4.5
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: support in rails db enums or string (using as flexible enum)
|
98
|
+
test_files: []
|