const_enum 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'activerecord', '~> 3.0.0'
4
+
5
+ # Add dependencies to develop your gem here.
6
+ # Include everything needed to run rake, tests, features, etc.
7
+ group :development do
8
+ gem 'rspec', '~> 2.8.0'
9
+ gem 'rdoc', '~> 3.12'
10
+ gem 'bundler', '~> 1.0.0'
11
+ gem 'jeweler', '~> 1.8.3'
12
+ gem 'simplecov'
13
+ gem 'activerecord', '~> 3.0.0'
14
+ gem 'sqlite3-ruby', :require => 'sqlite3'
15
+ gem 'database_cleaner'
16
+ end
@@ -0,0 +1,30 @@
1
+ Copyright (c) 2012, Synergy Marketing, Inc.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions
6
+ are met:
7
+
8
+ Redistributions of source code must retain the above copyright notice,
9
+ this list of conditions and the following disclaimer.
10
+
11
+ Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ Neither the name of the Synergy Marketing, Inc. nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
+ COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27
+ AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
29
+ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30
+ DAMAGE.
@@ -0,0 +1,201 @@
1
+ = const_enum
2
+
3
+ ActiveRecordの定数クラス、定数名アクセサ、NamedScope、定数値判定メソッド等を簡単なDSLで作成するためのプラグイン。
4
+ rails3.xに対応しています。
5
+
6
+
7
+ == 設定
8
+
9
+ === 定数のラベル定義ファイルを作成
10
+ # config/local/ja/labels.yml
11
+ labels:
12
+ user:
13
+ status:
14
+ disable: "無効"
15
+ enable: "有効"
16
+
17
+ === constメソッドのブロック内で定数を定義
18
+ class User < ActiveRecord::Base
19
+ const :STATUS do
20
+ ENABLE 1
21
+ DISABLE 0
22
+ end
23
+ end
24
+
25
+ == 使用例
26
+
27
+ === 定数値へのアクセス
28
+ User::STATUS::ENABLE # 1
29
+ User::STATUS::DISABLE # 0
30
+
31
+ === ActiveRecordの拡張
32
+ # データを作成
33
+ taro = User.create(:name => 'taro' , :status => Hoge::STATUS::ENABLE)
34
+ hanako = User.create(:name => 'hanako', :status => Hoge::STATUS::DISABLE)
35
+
36
+ # {ATTR}_labelメソッドが定義される
37
+ taro.status_label # "有効"
38
+ hanako.status_label # "無効"
39
+
40
+ # {ATTR}_{CONST}?メソッドが定義される
41
+ taro.status_enable? # true
42
+ taro.status_disable? # false
43
+
44
+ # {ATTR}_was_{CONST}? , #{ATTR}_{CONST}_wasメソッドが定義される
45
+ taro.status = User::STATUS::DISABLE
46
+ taro.status_enable? # false
47
+ taro.was_status_enable? # true
48
+ taro.status_label_was? # 有効
49
+
50
+ # named_scopeが定義される
51
+ User.status_enable.all
52
+
53
+ === Validationでの利用
54
+
55
+ constメソッドで定義される定数クラスはinclude?メソッドを実装しているため、
56
+ validates :inclusionで利用することができる。
57
+
58
+ class User < ActiveRecord::Base
59
+ const :STATUS do
60
+ ENABLE 1
61
+ DISABLE 0
62
+ end
63
+ validates :status, :inclusion => STATUS
64
+ end
65
+
66
+ === Viewでの利用
67
+
68
+ constメソッドで定義される定数クラスはEnumerableモジュールをインクルードしているため、
69
+ collection_selectにそのまま渡すことができる。
70
+ 値はvalue、ラベル名labelメソッドを指定する。
71
+
72
+ form_for newhoge do |f|
73
+ f.collection_select(:status, User::STATUS, :value, :label)
74
+ end
75
+
76
+ === 定数オブジェクトへのアクセス
77
+
78
+ {Class}::{ATTR}[value]で各定数オブジェクトにアクセス可能
79
+
80
+ User::STATUS[1].class # User::STATUS
81
+ User::STATUS[1].value # 1
82
+ User::STATUS[1].label # "有効"
83
+
84
+ == ActiveRecord::Base.constメソッドのオプション
85
+
86
+ === prefix: NamedScope、属性値テストメソッドの名前を変更する
87
+
88
+ constメソッドにprefixオプションを与えることで、作成されるNamedScope、属性値検証メソッドの
89
+ 名前(プレフィクス)を変更することができる。
90
+ 定数値名がモデル内で衝突しない場合は、prefixに空文字を与えるのが使いやすい。
91
+
92
+ class User < ActiveRecord::Base
93
+ const :STATUS, :prefix => '' do
94
+ ENABLE 1
95
+ DISABLE 0
96
+ end
97
+ end
98
+
99
+ user = User.enable.first
100
+ user.enable? # true
101
+ user.disable? # false
102
+
103
+
104
+ === scope: NamedScopeを作成しない
105
+
106
+ 値が直接DBにない場合、scopeオプションにfalseを与え、
107
+ 定数値を返すメソッドを定義することで各種メソッドを利用できる。
108
+
109
+ class Entry < ActiveRecord::Base
110
+ # 公開状態[予約、公開、終了]
111
+ const :STATUS, :scope => false, :prefix => '' do
112
+ SCHEDULED 1
113
+ OPENED 2
114
+ FINISHED 3
115
+ end
116
+
117
+ scope :scheduled ,lambda {time = Time.now; where('start_at > ? ', time) }
118
+ scope :opened, lambda {time = Time.now; where('start_at <= ? AND end_at > ?', time, time) }
119
+ scope :finished, lambda {time = Time.now; where('end_at <= ? ', time) }
120
+
121
+ # 公開状態
122
+ def status
123
+ now = Time.now
124
+ case
125
+ when start_at > now
126
+ STATUS::SCHEDULED
127
+ when (end_at >= now and start_at <= now)
128
+ STATUS::OPENED
129
+ else
130
+ STATUS::FINISHED
131
+ end
132
+ end
133
+
134
+ # 変更前公開状態
135
+ # status_label_wasやwas_opened?などを使わない場合は不要
136
+ def status_was
137
+ now = Time.now
138
+ case
139
+ when start_at_was > now
140
+ STATUS::SCHEDULED
141
+ when (end_at_was >= now and start_at_was <= now)
142
+ STATUS::OPENED
143
+ else
144
+ STATUS::FINISHED
145
+ end
146
+ end
147
+ end
148
+
149
+ entry = Entry.create(:start_at => Time.now, :end_at => Time.now + 10.days)
150
+ entry.status_label
151
+ entry.active?
152
+
153
+ === predicate: 属性値テストメソッドを作成しない
154
+
155
+ predicateにfalseを与えると、属性値テストメソッドの作成が行われない。
156
+
157
+
158
+ === extensions: 定数オブジェクトに追加のメソッドを定義する
159
+
160
+ ブロック内にメソッド定義を書くと、STAUTS[]やSTATUS.eachの際に取得できる定数オブジェクトに対してメソッドが定義される。
161
+ collection_selectの際に表示したいラベル名を複数パターン用意する場合などに利用可能。
162
+
163
+ class User < ActiveRecord::Base
164
+ const :STATUS do
165
+ ENABLE 1
166
+ DISABLE 0
167
+ # 定数オブジェクトには以下のメソッドが存在するため、これらのメソッドを自由に呼び出すことができる
168
+ # key 名前空間(クラス、モジュール名)を含まない各定数名のシンボル
169
+ # label ラベル名
170
+ # value 定数値
171
+ def code
172
+ '%05d'%value
173
+ end
174
+ # I18nを使ってみる。
175
+ def label2
176
+ I18n.t(key.to_s.downcase, :scope=> 'label2.modelname')
177
+ end
178
+ end
179
+ end
180
+
181
+ # 選択肢に上で定義したcodeメソッドの値を使用する
182
+ form_for enable_piyo do |f|
183
+ f.collection_select(:status, User::STATUS, :value, :code)
184
+ f.collection_select(:status, User::STATUS, :value, :label2)
185
+ end
186
+
187
+ == その他
188
+
189
+ === ラベル名を取得するメソッド名を変更する
190
+
191
+ ConstEnum::ActiveRecord.label_suffixオプションを設定することで、
192
+ ラベル名を取得すためのメソッドの名前(サフィックス)を変更することができる。
193
+
194
+ # config/initializers/const_enum.rb
195
+ require 'const_enum.rb'
196
+ ConstEnum::ActiveRecord.label_suffix = '_name' # ラベル名を返すメソッドにつけるサフィックスの指定
197
+
198
+
199
+ == Copyright
200
+
201
+ Copyright (c) 2012 SynergyMarketing.inc. See LICENSE for details.
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "const_enum"
18
+ gem.homepage = "http://github.com/techscore/const_enum"
19
+ gem.license = "BSD"
20
+ gem.summary = %Q{define ActiveRecord constants with DSL.}
21
+ gem.description = %Q{define ActiveRecord constants with DSL.and more!}
22
+ gem.email = "info@techscore.com"
23
+ gem.authors = ["yuki teraoka"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ task :simplecov do
35
+ ENV['COVERAGE'] = 'true'
36
+ Rake::Task['spec'].execute
37
+ end
38
+
39
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
40
+ spec.pattern = 'spec/**/*_spec.rb'
41
+ spec.rcov = true
42
+ end
43
+
44
+ task :default => :spec
45
+
46
+ require 'rdoc/task'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "const_enum #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,81 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "const_enum"
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["yuki teraoka"]
12
+ s.date = "2012-07-19"
13
+ s.description = "define ActiveRecord constants with DSL.and more!"
14
+ s.email = "info@techscore.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "const_enum.gemspec",
28
+ "lib/const_enum.rb",
29
+ "lib/const_enum/active_record.rb",
30
+ "lib/const_enum/base.rb",
31
+ "lib/const_enum/kernel.rb",
32
+ "lib/const_enum/with_i18n.rb",
33
+ "spec/active_record/active_record_spec.rb",
34
+ "spec/const_enum_spec.rb",
35
+ "spec/spec_helper.rb",
36
+ "spec/support/database_cleaner.rb",
37
+ "spec/support/models.rb"
38
+ ]
39
+ s.homepage = "http://github.com/techscore/const_enum"
40
+ s.licenses = ["BSD"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = "1.8.10"
43
+ s.summary = "define ActiveRecord constants with DSL."
44
+
45
+ if s.respond_to? :specification_version then
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_runtime_dependency(%q<activerecord>, ["~> 3.0.0"])
50
+ s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
51
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
52
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
53
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
54
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
55
+ s.add_development_dependency(%q<activerecord>, ["~> 3.0.0"])
56
+ s.add_development_dependency(%q<sqlite3-ruby>, [">= 0"])
57
+ s.add_development_dependency(%q<database_cleaner>, [">= 0"])
58
+ else
59
+ s.add_dependency(%q<activerecord>, ["~> 3.0.0"])
60
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
61
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
62
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
63
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
64
+ s.add_dependency(%q<simplecov>, [">= 0"])
65
+ s.add_dependency(%q<activerecord>, ["~> 3.0.0"])
66
+ s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
67
+ s.add_dependency(%q<database_cleaner>, [">= 0"])
68
+ end
69
+ else
70
+ s.add_dependency(%q<activerecord>, ["~> 3.0.0"])
71
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
72
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
73
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
74
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
75
+ s.add_dependency(%q<simplecov>, [">= 0"])
76
+ s.add_dependency(%q<activerecord>, ["~> 3.0.0"])
77
+ s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
78
+ s.add_dependency(%q<database_cleaner>, [">= 0"])
79
+ end
80
+ end
81
+
@@ -0,0 +1,5 @@
1
+ # coding: utf-8
2
+ require "const_enum/base"
3
+ require "const_enum/kernel"
4
+ require "const_enum/active_record"
5
+ require "const_enum/with_i18n"
@@ -0,0 +1,111 @@
1
+ # coding: utf-8
2
+
3
+ module ConstEnum
4
+ module ActiveRecord
5
+
6
+ mattr_accessor :label_suffix, :i18n
7
+ # ラベル名メソッドに付与するサフィックス
8
+ self.label_suffix = '_label'
9
+ # ラベル名にi18nを使用するかどうか
10
+ self.i18n = true
11
+
12
+ module ClassMethods
13
+ # 定数クラス、定数ラベルアクセサ、named_scope、predicate methodを作成します
14
+ #
15
+ # = arguments
16
+ # mod_name: 定義されるクラスの名前です
17
+ # options:
18
+ # :scope named_scopeを定義します。default: true
19
+ # :predicate 属性値の検証メソッドを定義します。default: true
20
+ # :prefix scope, predicate を作成する際のprefixを指定します。default: "#{mod_name.downcase}_"
21
+ #
22
+ # = exapmle
23
+ #
24
+ # class Hoge < ActiveRecord::Base
25
+ # const :STATUS do
26
+ # ENABLE 1, '有効'
27
+ # DISABLE 0, '無効'
28
+ # def code
29
+ # '%05d'%value
30
+ # end
31
+ # end
32
+ # end
33
+ #
34
+ # Hoge::STATUS::ENABLE # 1
35
+ # Hoge::STATUS.ENABLE.value # 1
36
+ # Hoge::STATUS.ENABLE.label # "有効"
37
+ # Hoge::STATUS.ENABLE.code # 00001
38
+ # Hoge.crate(:name => '有効なHOGE', :status => Hoge::STATUS::ENABLE)
39
+ # Hoge.crate(:name => '無効なHOGE', :status => Hoge::STATUS::DISABLE)
40
+ # enable_hoge = Hoge.status_enable.first
41
+ # enable_hoge.name # "有効なHOGE"
42
+ # enable_hoge.status_label # "有効"
43
+ # newhoge = Hoge.new(:name => '新しいHOGE', :status => Hoge::STATUS::ENABLE)
44
+ # newhoge.status_enable? # true
45
+ # newhoge.status_disable? # false
46
+ # form_for newhoge do |f|
47
+ # f.collection_select(:status, Hoge::STATUS, :value, :label)
48
+ # f.collection_select(:status, Hoge::STATUS, :value, :code)
49
+ # end
50
+ #
51
+ def const(clazz_name, options = {}, &block)
52
+ clazz_name = clazz_name.to_s
53
+ default = {:label_suffix => ConstEnum::ActiveRecord.label_suffix,:scope => true, :predicate => true, :attr=>clazz_name.downcase, :prefix => "#{clazz_name.downcase}_", :validation => false, :allow_blank => true, :i18n => true}
54
+ options = default.merge(options.symbolize_keys)
55
+ attr = options[:attr]
56
+ if ConstEnum::ActiveRecord.i18n and options[:i18n]
57
+ clazz = const_enum do
58
+ include ConstEnum::WithI18n
59
+ class_eval(&block)
60
+ end
61
+ clazz.i18n_options[:key] = [clazz.i18n_options[:key], self.name.underscore, attr].join('.')
62
+ clazz.i18n_options.merge!(ConstEnum::ActiveRecord.i18n) if Hash === ConstEnum::ActiveRecord.i18n
63
+ clazz.i18n_options.merge!(options[:i18n]) if Hash === options[:i18n]
64
+ else
65
+ clazz = const_enum(&block)
66
+ end
67
+
68
+ const_set(clazz_name, clazz)
69
+ if options[:label_suffix]
70
+ class_eval <<-"EOS", __FILE__, __LINE__
71
+ def #{attr}#{options[:label_suffix]}
72
+ #{clazz_name}[#{attr}].try(:label)
73
+ end
74
+ def #{attr}#{options[:label_suffix]}_was
75
+ #{clazz_name}[#{attr}_was].try(:label)
76
+ end
77
+ EOS
78
+ end
79
+ if options[:scope]
80
+ clazz.each do |obj|
81
+ key = clazz.key(obj.value).to_s
82
+ class_eval <<-"EOS", __FILE__, __LINE__
83
+ scope :#{options[:prefix]}#{key.downcase}, lambda { {:conditions => {'#{attr}' => #{clazz_name}::#{key} } }}
84
+ EOS
85
+ end
86
+ end
87
+ if options[:predicate]
88
+ clazz.each do |obj|
89
+ key = clazz.key(obj.value).to_s
90
+ class_eval <<-"EOS", __FILE__, __LINE__
91
+ def #{options[:prefix]}#{key.downcase}?
92
+ #{attr} == #{clazz_name}::#{key}
93
+ end
94
+ def was_#{options[:prefix]}#{key.downcase}?
95
+ #{attr}_was == #{clazz_name}::#{key}
96
+ end
97
+ EOS
98
+ end
99
+ end
100
+ if options[:validation]
101
+ validates attr.to_sym, :inclusion => {:in => clazz}, :allow_blank => (!!options[:allow_blank])
102
+ end
103
+ clazz
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ ActiveSupport.on_load(:active_record) do
110
+ ::ActiveRecord::Base.send :extend, ConstEnum::ActiveRecord::ClassMethods
111
+ end
@@ -0,0 +1,121 @@
1
+ # coding: utf-8
2
+ require "active_support/core_ext/module"
3
+ require "active_support/core_ext/array"
4
+
5
+ module ConstEnum
6
+ class Base
7
+ attr_reader :attributes, :key
8
+ delegate :from, :to, :first, :second, :third, :fourth, :fifth, :last, :forty_two, :to => :attributes
9
+
10
+ def initialize(key, *attributes)
11
+ @key = key
12
+ @attributes = attributes
13
+ @attributes.freeze
14
+ end
15
+
16
+ def [](pos)
17
+ @attributes[pos]
18
+ end
19
+
20
+ def value
21
+ @attributes.first
22
+ end
23
+
24
+ def label
25
+ @attributes.second
26
+ end
27
+
28
+ def inspect
29
+ "#{@attributes.first.inspect}:#{(@attributes[2 .. -1]||[]).unshift(label).map(&:inspect).join(', ')}"
30
+ end
31
+
32
+ def to_s
33
+ "#{@attributes.first.inspect}:#{(@attributes[2 .. -1]||[]).unshift(label).map(&:inspect).join(', ')}"
34
+ end
35
+
36
+ class << self
37
+ include Enumerable
38
+ def inherited(clazz)
39
+ hash_class = RUBY_VERSION < '1.9' ? ActiveSupport::OrderedHash : Hash
40
+ clazz.instance_variable_set(:@instances, hash_class.new)
41
+ clazz.instance_variable_set(:@keys, hash_class.new)
42
+ end
43
+
44
+ def include?(value)
45
+ (ConstEnum === value) ? @instances.include?(value) : @instances.key?(value)
46
+ end
47
+
48
+ def [](value)
49
+ @instances[value]
50
+ end
51
+
52
+ def label(value)
53
+ @instances[value].label
54
+ end
55
+
56
+ def key(value)
57
+ @keys[value]
58
+ end
59
+
60
+ def keys
61
+ @keys.values
62
+ end
63
+
64
+ def values
65
+ @instances.keys
66
+ end
67
+
68
+ def labels
69
+ @instances.values.map(&:label)
70
+ end
71
+
72
+ def size
73
+ @instances.size
74
+ end
75
+
76
+ def each
77
+ return Enumerator.new(self) unless block_given?
78
+ @instances.each {|value, obj| yield obj }
79
+ end
80
+
81
+ def each_key
82
+ return Enumerator.new(self, :each_key) unless block_given?
83
+ keys.each {|key| yield key }
84
+ end
85
+
86
+ def each_value
87
+ return Enumerator.new(self, :each_value) unless block_given?
88
+ values.each {|value| yield value }
89
+ end
90
+
91
+ def each_label
92
+ return Enumerator.new(self, :each_label) unless block_given?
93
+ labels.each {|label| yield label }
94
+ end
95
+
96
+ def inspect
97
+ to_s
98
+ end
99
+
100
+ def to_s
101
+ return super if ConstEnum::Base == self
102
+ elems = []
103
+ @keys.each do |value, key|
104
+ elems << "#{key}[#{@instances[value]}]"
105
+ end
106
+ super << " { #{elems.join(', ')} }"
107
+ end
108
+
109
+ protected
110
+ def define_const(key, value, *args)
111
+ raise "already initialized constant value #{value}. name: #{key} defined_name: #{@keys[value]}"if @instances.key? value
112
+ const_set(key, value)
113
+ obj = self.new(key, value, *args)
114
+ singleton_class.__send__(:define_method, key) { obj }
115
+ obj.freeze
116
+ @instances[value] = obj
117
+ @keys[value] = key
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+
3
+ module Kernel
4
+ # 無名の定数クラスを作成して返します
5
+ # 与えたブロック内で以下のように記述することで定数とラベルを定義できます
6
+ # CLAZZNAME = const_enum do
7
+ # HOGE 1, "ほげ"
8
+ # FUGA 2, "ふが"
9
+ # PIYO 3, "ぴよ"
10
+ # end
11
+ # puts CLAZZNAME::HOGE # 1
12
+ # puts CLAZZNAME[2] # ふが
13
+ def const_enum(&block)
14
+ clazz = Class.new(ConstEnum::Base)
15
+ clazz.singleton_class.__send__(:define_method, :method_missing) do |name, *args|
16
+ /\A[A-Z][a-zA-Z_0-9]*\z/ === name.to_s ? define_const(name, *args) : super
17
+ end
18
+ clazz.class_eval &block
19
+ clazz.singleton_class.__send__(:remove_method, :method_missing)
20
+ clazz
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ require "active_support/concern"
3
+
4
+ module ConstEnum
5
+ module WithI18n
6
+ extend ActiveSupport::Concern
7
+
8
+ DEFAULT_OPTIONS = { :key => 'labels'}
9
+
10
+ included do
11
+ self.i18n_options = DEFAULT_OPTIONS.dup
12
+ end
13
+
14
+ module InstanceMethods
15
+ def label
16
+ I18n.t(build_key(@attributes.second || key.to_s.downcase))
17
+ end
18
+
19
+ private
20
+ def build_key(key)
21
+ "#{self.class.i18n_options[:key]}.#{key}"
22
+ end
23
+ end
24
+
25
+ module ClassMethods
26
+ def i18n_options
27
+ @i18n_options
28
+ end
29
+
30
+ def i18n_options=(options)
31
+ @i18n_options = options
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,61 @@
1
+ # coding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ describe ConstEnum do
5
+ describe "ActiveRecord::Base.const" do
6
+ before do
7
+ User.create(:name=> "user1", :status=> 1, :gender => 'male' , :role=> 2)
8
+ User.create(:name=> "user2", :status=> 1, :gender => 'female', :role=> 1)
9
+ User.create(:name=> "user3", :status=> 2, :gender => 'male' , :role=> 1)
10
+ User.create(:name=> "user4", :status=> 2, :gender => 'male' , :role=> 1)
11
+ end
12
+
13
+ it do
14
+ active_user = User.status_active.first
15
+ resigned_user = User.status_resigned.first
16
+
17
+ active_user.status.should == User::STATUS::ACTIVE
18
+ resigned_user.status.should == User::STATUS::RESIGNED
19
+
20
+ active_user.status_label.should == "Active"
21
+ resigned_user.status_label.should == "Resigned"
22
+
23
+ active_user.should be_status_active
24
+ resigned_user.should be_status_resigned
25
+ active_user.should_not be_status_resigned
26
+ resigned_user.should_not be_status_active
27
+
28
+ active_user.status = User::STATUS::RESIGNED
29
+ active_user.should be_status_resigned
30
+ active_user.should be_was_status_active
31
+ active_user.should_not be_was_status_resigned
32
+ active_user.status_label_was.should == "Active"
33
+
34
+ male_user = User.male.first
35
+ female_user = User.female.first
36
+
37
+ male_user.gender_name.should == "Male"
38
+ female_user.gender_name.should == "Female"
39
+
40
+ User::GENDER[male_user.gender ].symbole_mark.should == "man"
41
+ User::GENDER[female_user.gender].symbole_mark.should == "woman"
42
+
43
+ User.male.count.should == 3
44
+ User.female.count.should == 1
45
+
46
+ male_user.gender = 'femala'
47
+ male_user.should be_invalid
48
+ male_user.gender = 'female'
49
+ male_user.should be_valid
50
+
51
+ member = User.scoped_by_role(User::ROLE::MEMBER).first
52
+ admin = User.scoped_by_role(User::ROLE::ADMIN).first
53
+ lambda{ User.role_admin }.should raise_error(NoMethodError)
54
+ lambda{ admin.role_label }.should raise_error(NoMethodError)
55
+ lambda{ admin.role_label_was }.should raise_error(NoMethodError)
56
+ lambda{ admin.role_admin? }.should raise_error(NoMethodError)
57
+ lambda{ admin.was_role_admin?}.should raise_error(NoMethodError)
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,79 @@
1
+ # coding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ describe ConstEnum do
5
+ describe "const_enum" do
6
+ before do
7
+ @enum1 = const_enum do
8
+ CONST1 1, "label1", "1_opt1", "1_opt2"
9
+ CONST2 2, "label2", "2_opt1", "2_opt2"
10
+ CONST3 3, "label3", "3_opt1", "3_opt2"
11
+ CONST4 4, "label4", "4_opt1", "4_opt2"
12
+ CONST5 5, "label5", "5_opt1", "5_opt2"
13
+
14
+ def upper_label
15
+ label.upcase
16
+ end
17
+ end
18
+ end
19
+
20
+ context "constant class methods" do
21
+ it do
22
+ @enum1.should be_include(1)
23
+ @enum1.should be_include(2)
24
+ @enum1.should_not be_include(0)
25
+ @enum1.should_not be_include(6)
26
+ @enum1.should_not be_include("1")
27
+ @enum1.label(3).should == "label3"
28
+ @enum1.key(3).should == :CONST3
29
+ @enum1.values.should == (1..5).to_a
30
+ @enum1.labels.should == (1..5).map{|i| "label#{i}"}
31
+ @enum1.keys.should == (1..5).map{|i| :"CONST#{i}"}
32
+ @enum1.size.should == 5
33
+ end
34
+ end
35
+
36
+ context "constant values" do
37
+ it do
38
+ @enum1::CONST1.should == 1
39
+ @enum1::CONST2.should == 2
40
+ @enum1::CONST3.should == 3
41
+ @enum1::CONST4.should == 4
42
+ @enum1::CONST5.should == 5
43
+ end
44
+ end
45
+
46
+ context "each constants" do
47
+ it do
48
+ @enum1.each_with_index do |obj, i|
49
+ n = i+1
50
+ @enum1[obj.value].should equal obj
51
+ obj.key.should == :"CONST#{n}"
52
+ obj.value.should == n
53
+ obj.label.should == "label#{n}"
54
+ obj[0].should == i+1
55
+ obj[1].should == "label#{n}"
56
+ obj[2].should == "#{n}_opt1"
57
+ obj[3].should == "#{n}_opt2"
58
+ obj.upper_label.should == "LABEL#{n}"
59
+ obj.should be_frozen
60
+ end
61
+ end
62
+ end
63
+
64
+ context "each items" do
65
+ it do
66
+ @enum1.each_key.to_a.should == @enum1.map(&:key)
67
+ @enum1.each_label.to_a.should == @enum1.map(&:label)
68
+ @enum1.each_value.to_a.should == @enum1.map(&:value)
69
+ end
70
+ end
71
+
72
+ context "define constant without block" do
73
+ it do
74
+ lambda{@enum1.FAILUER(10, "aaa")}.should raise_error(NoMethodError)
75
+ lambda{@enum1.define_const(:FAILUER, 10, "aaa")}.should raise_error
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,17 @@
1
+ # coding: utf-8
2
+ if ENV['COVERAGE']
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_filter '/spec/'
6
+ add_filter '/vendor/'
7
+ end
8
+ end
9
+
10
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
11
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
12
+ require 'rspec'
13
+ require 'const_enum'
14
+
15
+ # Requires supporting files with custom matchers and macros, etc,
16
+ # in ./support/ and its subdirectories.
17
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
@@ -0,0 +1,16 @@
1
+ # coding: utf-8
2
+ require 'database_cleaner'
3
+
4
+ DatabaseCleaner[:active_record].strategy = :transaction if defined? ActiveRecord
5
+
6
+ RSpec.configure do |config|
7
+ config.before :suite do
8
+ DatabaseCleaner.clean_with :truncation
9
+ end
10
+ config.before :each do
11
+ DatabaseCleaner.start
12
+ end
13
+ config.after :each do
14
+ DatabaseCleaner.clean
15
+ end
16
+ end
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+ require 'active_record'
3
+ ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => ':memory:')
4
+
5
+ class User < ActiveRecord::Base
6
+
7
+ const :STATUS do
8
+ ACTIVE 1
9
+ RESIGNED 2
10
+ end
11
+
12
+ const :GENDER, :prefix => '', :label_suffix => "_name", :i18n => false, :validation => true, :allow_blank => true do
13
+ MALE 'male', 'Male' , 'man'
14
+ FEMALE 'female', 'Female' , 'woman'
15
+
16
+ def symbole_mark
17
+ third
18
+ end
19
+
20
+ end
21
+
22
+ const :ROLE, :scope => false, :label_suffix =>false, :predicate => false do
23
+ MEMBER 1
24
+ ADMIN 2
25
+ end
26
+
27
+ end
28
+
29
+ #migrations
30
+ class CreateAllTables < ActiveRecord::Migration
31
+ def self.up
32
+ create_table(:users) {|t| t.string :name; t.integer :status; t.string :gender; t.integer :role}
33
+ end
34
+ end
35
+
36
+ I18n.backend = I18n::Backend::KeyValue.new({})
37
+ I18n.backend.store_translations :en, :labels =>{:user =>{:status => { :active => "Active", :resigned => "Resigned" }}}
38
+
39
+ ActiveRecord::Migration.verbose = false
40
+ CreateAllTables.up
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: const_enum
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - yuki teraoka
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: &12500920 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *12500920
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &12500240 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 2.8.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *12500240
36
+ - !ruby/object:Gem::Dependency
37
+ name: rdoc
38
+ requirement: &12499600 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '3.12'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *12499600
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: &12498720 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *12498720
58
+ - !ruby/object:Gem::Dependency
59
+ name: jeweler
60
+ requirement: &12497660 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 1.8.3
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *12497660
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: &14946220 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *14946220
80
+ - !ruby/object:Gem::Dependency
81
+ name: activerecord
82
+ requirement: &14943420 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ~>
86
+ - !ruby/object:Gem::Version
87
+ version: 3.0.0
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *14943420
91
+ - !ruby/object:Gem::Dependency
92
+ name: sqlite3-ruby
93
+ requirement: &14941100 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *14941100
102
+ - !ruby/object:Gem::Dependency
103
+ name: database_cleaner
104
+ requirement: &14104560 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *14104560
113
+ description: define ActiveRecord constants with DSL.and more!
114
+ email: info@techscore.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files:
118
+ - LICENSE.txt
119
+ - README.rdoc
120
+ files:
121
+ - .document
122
+ - .rspec
123
+ - Gemfile
124
+ - LICENSE.txt
125
+ - README.rdoc
126
+ - Rakefile
127
+ - VERSION
128
+ - const_enum.gemspec
129
+ - lib/const_enum.rb
130
+ - lib/const_enum/active_record.rb
131
+ - lib/const_enum/base.rb
132
+ - lib/const_enum/kernel.rb
133
+ - lib/const_enum/with_i18n.rb
134
+ - spec/active_record/active_record_spec.rb
135
+ - spec/const_enum_spec.rb
136
+ - spec/spec_helper.rb
137
+ - spec/support/database_cleaner.rb
138
+ - spec/support/models.rb
139
+ homepage: http://github.com/techscore/const_enum
140
+ licenses:
141
+ - BSD
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ! '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ segments:
153
+ - 0
154
+ hash: 3828944335919537987
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ! '>='
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubyforge_project:
163
+ rubygems_version: 1.8.10
164
+ signing_key:
165
+ specification_version: 3
166
+ summary: define ActiveRecord constants with DSL.
167
+ test_files: []