has_enum 0.6.1 → 0.7.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.
data/.gitignore CHANGED
@@ -6,3 +6,4 @@ pkg/*
6
6
  spec/*.db
7
7
  *.swp
8
8
  Gemfile.lock
9
+ bin
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source "http://rubygems.org"
1
+ source "http://gems.openteam.ru"
2
2
 
3
3
  gemspec
@@ -6,8 +6,8 @@ Gem::Specification.new do |s|
6
6
  s.name = "has_enum"
7
7
  s.version = HasEnum::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Andreas Korth", "Konstantin Shabanov"]
10
- s.email = ["kes.eclipse@gmail.com"]
9
+ s.authors = ["Andreas Korth", "Konstantin Shabanov", "Dmitry Lihachev"]
10
+ s.email = ["lda@openteam.ru"]
11
11
  s.homepage = "http://github.com/openteam/has_enum"
12
12
  s.summary = %q{Gem for Rails to easily handle enumeration attributes in models}
13
13
  s.description = %q{Gem for and Rails3 to easily handle enumeration attributes in ActiveRecord's models}
@@ -1,4 +1,40 @@
1
- require 'has_enum/active_record'
2
- require 'has_enum/helpers'
1
+ module HasEnum
3
2
 
4
- ActiveRecord::Base.send(:include, HasEnum::ActiveRecord)
3
+ autoload :ClassMethods, 'has_enum/class_methods'
4
+
5
+ def self.included(base)
6
+ base.write_inheritable_attribute(:enums, HashWithIndifferentAccess.new)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ end
11
+
12
+ class ActiveRecord::Base
13
+ include HasEnum
14
+ end
15
+
16
+ begin
17
+ require 'formtastic'
18
+
19
+ Formtastic::SemanticFormBuilder.class_eval do
20
+
21
+ def enum_input(method, options = {})
22
+ value = @object.send(method)
23
+ options.reverse_merge! :as => :select,
24
+ :collection => @object.class.human_enums[method].invert.to_a
25
+ self.input(method, options).gsub(/class="select/, 'class="enum')
26
+ end
27
+
28
+ def input_with_enum(method, options={})
29
+ if @object.class.respond_to?(:enums) && @object.class.enums[method] && !options[:as]
30
+ enum_input(method, options)
31
+ else
32
+ input_without_enum(method, options)
33
+ end
34
+ end
35
+
36
+ alias_method_chain :input, :enum
37
+
38
+ end
39
+ rescue LoadError
40
+ end
@@ -0,0 +1,79 @@
1
+ module HasEnum::ClassMethods
2
+
3
+ def enums
4
+ @enums ||= read_inheritable_attribute(:enums)
5
+ end
6
+
7
+ def enum_values(attribute)
8
+ enums[attribute]
9
+ end
10
+
11
+ def has_enum(*params)
12
+ options = params.extract_options!
13
+ options.assert_valid_keys(:query_methods, :scopes, :presence)
14
+
15
+ raise ArgumentError, "Empty arguments list for has_enum call" if params.empty?
16
+
17
+ default_values = nil
18
+ case params.second
19
+ when NilClass # has_enum :enum
20
+ attributes = [*params.first]
21
+ when Array # has_enum :enum, %w[foo bar]
22
+ attributes = [*params.first]
23
+ default_values = params.second.map(&:to_s)
24
+ else # has_enum :state, :status
25
+ attributes = params
26
+ end
27
+
28
+ attributes.map(&:to_sym).each do | attribute |
29
+ values = default_values || human_enum_values(attribute).keys.map(&:to_s)
30
+
31
+ enums[attribute] = values
32
+
33
+ validates attribute, :inclusion => { :in => values + [nil]}
34
+ validates attribute, :presence => options[:presence] if options[:presence]
35
+
36
+ values.each do |val|
37
+ scope "#{attribute}_#{val}", where(attribute => val)
38
+ end if options[:scopes]
39
+
40
+ values.each do |val|
41
+ define_method "#{attribute}_#{val}?" do
42
+ self.send(attribute) == val.to_s
43
+ end
44
+ end if options[:query_methods] != false
45
+
46
+ define_method "human_#{attribute}" do
47
+ self.class.human_enums[attribute][self[attribute]]
48
+ end
49
+
50
+ define_method "#{attribute}=" do | value |
51
+ value = value.to_s
52
+ value = nil if value == ''
53
+ self[attribute] = value
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ def human_enums
60
+ @human_enums ||= enums.keys.inject HashWithIndifferentAccess.new do | hash, enum |
61
+ hash[enum] = human_enum_values enum
62
+ hash
63
+ end
64
+ end
65
+
66
+ def human_enum_values(enum)
67
+ begin
68
+ options = {:default => nil, :raise => true, :count => nil}
69
+ HashWithIndifferentAccess.new(human_attribute_name("#{enum}_enum", options))
70
+ rescue I18n::MissingTranslationData
71
+ (enums[enum] || []).inject HashWithIndifferentAccess.new do |hash, value|
72
+ hash[value] = value.humanize
73
+ hash
74
+ end
75
+ end
76
+ end
77
+
78
+ end
79
+
@@ -1,10 +1,12 @@
1
1
  module ActionView::Helpers::FormHelper
2
2
  def radio_button_enum(object_name, method, options = {})
3
- ActionView::Helpers::InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_enum_tag(options)
3
+ ActionView::Helpers::InstanceTag.new(object_name, method, self, options.delete(:object)).
4
+ to_radio_button_enum_tag(options)
4
5
  end
5
6
 
6
7
  def select_enum(object_name, method, options = {})
7
- ActionView::Helpers::InstanceTag.new(object_name, method, self, options.delete(:object)).to_select_enum_tag(options)
8
+ ActionView::Helpers::InstanceTag.new(object_name, method, self, options.delete(:object)).
9
+ to_select_enum_tag(options)
8
10
  end
9
11
  end
10
12
 
@@ -13,7 +15,7 @@ class ActionView::Helpers::InstanceTag
13
15
  def to_radio_button_enum_tag(options = {})
14
16
  values_for_enum_tag.map do |val|
15
17
  radio_button = to_radio_button_tag(val.last, options)
16
- [ radio_button, to_label_tag(val.first, :for => radio_button.match(/ id="(.*?)"/)[1]) ] * $/
18
+ [radio_button, to_label_tag(val.first, :for => radio_button.match(/ id="(.*?)"/)[1])] * $/
17
19
  end.join($/)
18
20
  end
19
21
 
@@ -23,13 +25,7 @@ class ActionView::Helpers::InstanceTag
23
25
  end
24
26
 
25
27
  def values_for_enum_tag
26
- values = object.class.enum(method_name.to_sym)
27
- begin
28
- translation = I18n.translate("activerecord.attributes.#{object.class.name.i18nize}.#{method_name}_enum", :raise => true)
29
- values.map { |val| [translation[val.to_sym], val] }
30
- rescue I18n::MissingTranslationData
31
- values.map { |val| [val.humanize, val] }
32
- end
28
+ object.class.human_enums[method_name].invert.to_a
33
29
  end
34
30
  end
35
31
 
@@ -42,4 +38,5 @@ class ActionView::Helpers::FormBuilder
42
38
  def select_enum(method, options = {})
43
39
  @template.select_enum(@object_name, method, objectify_options(options.merge(:include_blank => true)))
44
40
  end
45
- end
41
+ end
42
+
@@ -1,3 +1,3 @@
1
1
  module HasEnum
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -2,149 +2,192 @@
2
2
 
3
3
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
4
 
5
- describe HasEnum::ActiveRecord do
6
- before :each do
7
- @model = TestModel.new(:category => 'misc', :color => 'blue', :foo => 'bar', :status => :pending)
5
+ describe HasEnum do
6
+ let :model do
7
+ TestModel.new(:category => :stuff, :status => :pending, :state => :done)
8
+ end
9
+
10
+ let :human_enums do
11
+ {
12
+ :category => {
13
+ :stuff => 'Stuff',
14
+ :things => 'Things',
15
+ :misc => 'Misc'
16
+ },
17
+ :color => {
18
+ :red => 'Red',
19
+ :green => 'Green',
20
+ :blue => 'Blue'
21
+ },
22
+ :size => {
23
+ :small => "Маленький",
24
+ :medium => "Средний",
25
+ :large => "Большой"
26
+ },
27
+ :status => {
28
+ :pending => 'На рассмотрении',
29
+ :failed => "Обработано с ошибкой",
30
+ :done => "Завершено"
31
+ },
32
+ :state => {
33
+ :pending => 'Pending',
34
+ :failed => 'Failed',
35
+ :done => 'Done'
36
+ },
37
+ }.with_indifferent_access
38
+ end
39
+
40
+ it "should return values by name symbol" do
41
+ TestModel.enums[:state].should eql human_enums[:state].stringify_keys.keys
42
+ end
43
+
44
+ it "should return values by name string" do
45
+ TestModel.enums["state"].should eql human_enums[:state].stringify_keys.keys
8
46
  end
9
47
 
10
48
  it "should return the values for a given enum attribute" do
11
- TestModel.enum[:category].should eql(%w(stuff things misc))
49
+ TestModel.enums[:category].should eql human_enums[:category].stringify_keys.keys
12
50
  end
13
51
 
14
52
  it "should return hash of values with it's translated equivalent" do
15
- I18n.reload!
16
- TestModel.human_enum[:size].should eql Hash[:small => "Маленький",
17
- :medium => "Средний",
18
- :large => "Большой"]
19
- TestModel.human_enum[:status].should eql Hash[:pending => "На рассмотрении",
20
- :failed =>"Обработано с ошибкой",
21
- :done =>"Завершено"]
22
- TestModel.human_enum[:category].should eql Hash[:stuff => 'Stuff',
23
- :things => 'Things',
24
- :misc => 'Misc']
53
+ TestModel.human_enums[:size].should eql human_enums[:size]
54
+ TestModel.human_enums[:status].should eql human_enums[:status]
55
+ TestModel.human_enums[:category].should eql human_enums[:category]
25
56
  end
26
-
57
+
27
58
  it "should return hash of enums with hashes of attributes and theirs translated equivalent" do
28
- I18n.reload!
29
- TestModel.human_enum.should eql Hash[:category => {:stuff=>"Stuff",
30
- :things=>"Things",
31
- :misc=>"Misc"},
32
- :color => {:red=>"Red",
33
- :green=>"Green",
34
- :blue=>"Blue"},
35
- :size => {:small=>"Маленький",
36
- :medium=>"Средний",
37
- :large=>"Большой"},
38
- :status => {:pending=>"На рассмотрении",
39
- :failed=>"Обработано с ошибкой",
40
- :done=>"Завершено"}]
59
+ TestModel.human_enums.should eql human_enums
41
60
  end
42
61
 
43
62
  it "should return translated value for attribute" do
44
- I18n.reload!
45
- TestModel.human_enum[:size][:large].should eql "Большой"
46
- TestModel.human_enum[:color][:red].should eql "Red"
47
- TestModel.human_enum[:status][:done].should eql "Завершено"
63
+ TestModel.human_enums[:size][:large].should eql "Большой"
64
+ TestModel.human_enums[:color][:red].should eql "Red"
65
+ TestModel.human_enums[:status][:done].should eql "Завершено"
48
66
  end
49
67
 
50
68
  describe "category enum" do
51
- it "should accept enum values for the attribute" do
69
+ it "should accept string enum values" do
52
70
  %w(stuff things misc).each do |value|
53
- @model.category = value
54
- @model.should be_valid
71
+ model.category = value
72
+ model.should be_valid
55
73
  end
56
74
  end
57
75
 
58
- it "should accept nil value for the attribute" do
59
- @model.category = nil
60
- @model.category.should be_nil
76
+ it "should accept symbol enum values" do
77
+ %w(stuff things misc).each do |value|
78
+ model.category = value.to_sym
79
+ model.should be_valid
80
+ end
61
81
  end
62
82
 
63
- it "should reject non enum values for the attribute" do
64
- @model.category = 'objects'
65
- @model.errors[:category].size.should eql(1)
83
+ it "should reject not enum values" do
84
+ model.category = "not_listed_in_enum"
85
+ model.should_not be_valid
86
+ end
87
+
88
+ it "should accept nil value for the attribute on create" do
89
+ model.category = nil
90
+ model.should be_valid
91
+ end
92
+
93
+ it "should not accept nil value for the attribute on update" do
94
+ model.save
95
+ model.category = nil
96
+ model.should_not be_valid
97
+ end
98
+
99
+ it "should not accept blank value for the attribute" do
100
+ model.category = ' '
101
+ model.should_not be_valid
102
+ end
103
+
104
+ it "should normalize empty value for the attribute" do
105
+ model.category = ''
106
+ model.category.should be_nil
66
107
  end
67
108
 
68
109
  it "should define query methods for enum values" do
69
- %w( stuff things misc ).each do |value|
70
- @model.should respond_to(:"category_#{value}?")
110
+ %w[stuff things misc].each do |value|
111
+ model.should respond_to(:"category_#{value}?")
71
112
  end
72
113
  end
73
- end
74
114
 
75
- describe "color enum" do
76
- it "should accept any value for the attribute" do
77
- %w(red orange yellow).each do |value|
78
- @model.color = value
79
- @model.should be_valid
80
- end
115
+ it "query methods should works" do
116
+ model.category = :stuff
117
+ model.should be_category_stuff
118
+ model.should_not be_category_things
119
+ model.should_not be_category_misc
120
+
121
+ model.category = :things
122
+ model.should_not be_category_stuff
123
+ model.should be_category_things
124
+ model.should_not be_category_misc
125
+
126
+ model.category = :misc
127
+ model.should_not be_category_stuff
128
+ model.should_not be_category_things
129
+ model.should be_category_misc
81
130
  end
82
131
 
83
- it "should reject an empty value for the attribute" do
84
- @model.color = ''
85
- @model.errors[:color].size.should eql(1)
132
+ it "should not define a scope methods for each enum value" do
133
+ TestModel.should_not respond_to :category_stuff
134
+ TestModel.should_not respond_to :category_things
135
+ TestModel.should_not respond_to :category_misc
86
136
  end
87
137
 
88
- it "should define a query method for each enum value" do
89
- @model.color = 'green'
90
- @model.category = 'stuff'
91
- @model.should be_category_stuff
92
- @model.should be_color_green
93
- @model.should_not be_color_red
94
- @model.should_not be_color_blue
138
+ it "should translate category by human_category method" do
139
+ model.human_category.should eql 'Stuff'
95
140
  end
141
+ end
96
142
 
143
+ describe "color enum" do
97
144
  it "should define a scope for each enum value" do
98
- @model.color = 'red'
99
- @model.save
100
- @model2 = TestModel.new(:color => 'red')
101
- @model2.save
145
+ model.color = 'red'
146
+ model.save
147
+
148
+ model2 = model.clone
149
+ model2.color = :blue
150
+ model2.save
102
151
 
103
152
  TestModel.color_red.all.should eql TestModel.where(:color => 'red').all
104
- TestModel.color_green.all.should be_empty
153
+
154
+ TestModel.color_red.should be_one
155
+ TestModel.color_blue.should be_one
156
+ TestModel.color_green.count.should be_zero
105
157
  end
106
- end
107
158
 
108
- describe "size enum" do
109
- it "should accept any value for the attribute" do
110
- ['orange', 'medium', 'misc', '', nil].each do |value|
111
- @model.size = value
112
- @model.should be_valid
113
- end
159
+ it "should accept nil value for the attribute" do
160
+ model.color = nil
161
+ model.should be_valid
162
+ model.save
163
+ model.should be_valid
164
+ end
165
+
166
+ it "should accept blank value for the attribute" do
167
+ model.color = ''
168
+ model.should be_valid
169
+ model.save
170
+ model.should be_valid
114
171
  end
172
+ end
115
173
 
174
+ describe "size enum" do
116
175
  it "should not define query methods for enum values" do
117
176
  %w(small medium large).each do |value|
118
- @model.should_not respond_to(:"size_#{value}?")
177
+ model.should_not respond_to(:"size_#{value}?")
119
178
  end
120
179
  end
121
-
122
- it "should return humanized translation if not localized" do
123
- @model.category = 'stuff'
124
- @model.human_category.should eql("Stuff")
125
- end
126
180
  end
127
181
 
128
182
  describe "status enum" do
129
- it "should accept symbols as symbols" do
130
- @model.status = :pending
131
- @model.status.should eql(:pending)
132
- end
133
-
134
- it "should define query methods for enum values" do
135
- @model.status = :pending
136
- @model.should be_status_pending
137
- end
138
-
139
- it "should accept nil value for the attribute" do
140
- @model.status = nil
141
- @model.status.should be_nil
183
+ it "should not accept nil value for the attribute" do
184
+ model.status = nil
185
+ model.should_not be_valid
142
186
  end
143
187
 
144
- it "should translate values for the enum" do
145
- I18n.reload!
146
- @model.status = :pending
147
- @model.human_status.should eql("На рассмотрении")
188
+ it "should not accept blank value for the attribute" do
189
+ model.status = ''
190
+ model.should_not be_valid
148
191
  end
149
192
  end
150
193
  end
@@ -15,7 +15,7 @@ ActiveRecord::Schema.define(:version => 0) do
15
15
  t.string "color"
16
16
  t.string "size"
17
17
  t.string "status"
18
- t.string "foo"
18
+ t.string "state"
19
19
  end
20
20
 
21
21
  create_table "schema_info", :id => false, :force => true do |t|
@@ -26,4 +26,4 @@ end
26
26
  I18n.load_path << 'spec/ru.yml'
27
27
  I18n.default_locale = :ru
28
28
 
29
- require File.dirname(__FILE__) + "/test_model" unless defined?(Model)
29
+ require File.dirname(__FILE__) + "/test_model" unless defined?(Model)
@@ -1,6 +1,12 @@
1
1
  class TestModel < ActiveRecord::Base
2
- has_enum :category, %w( stuff things misc )
3
- has_enum :color , %w( red green blue ) , :scopes => true
4
- has_enum :size , %w( small medium large ) , :query_methods => false
5
- has_enum :status , [:pending, :failed, :done]
6
- end
2
+
3
+ has_enum :category, %w[stuff things misc], :presence => {:on => :update}
4
+
5
+ has_enum :color, %w[red green blue], :scopes => true
6
+
7
+ has_enum :size, :query_methods => false
8
+
9
+ has_enum ["status", :state], [:pending, :failed, :done], :presence => true
10
+
11
+ end
12
+
metadata CHANGED
@@ -2,16 +2,17 @@
2
2
  name: has_enum
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.6.1
5
+ version: 0.7.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Andreas Korth
9
9
  - Konstantin Shabanov
10
+ - Dmitry Lihachev
10
11
  autorequire:
11
12
  bindir: bin
12
13
  cert_chain: []
13
14
 
14
- date: 2011-02-08 00:00:00 +06:00
15
+ date: 2011-03-24 00:00:00 +06:00
15
16
  default_executable:
16
17
  dependencies:
17
18
  - !ruby/object:Gem::Dependency
@@ -49,7 +50,7 @@ dependencies:
49
50
  version_requirements: *id003
50
51
  description: Gem for and Rails3 to easily handle enumeration attributes in ActiveRecord's models
51
52
  email:
52
- - kes.eclipse@gmail.com
53
+ - lda@openteam.ru
53
54
  executables: []
54
55
 
55
56
  extensions: []
@@ -67,7 +68,7 @@ files:
67
68
  - Rakefile
68
69
  - has_enum.gemspec
69
70
  - lib/has_enum.rb
70
- - lib/has_enum/active_record.rb
71
+ - lib/has_enum/class_methods.rb
71
72
  - lib/has_enum/helpers.rb
72
73
  - lib/has_enum/version.rb
73
74
  - spec/has_enum_spec.rb
@@ -99,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
99
100
  requirements: []
100
101
 
101
102
  rubyforge_project:
102
- rubygems_version: 1.5.0
103
+ rubygems_version: 1.6.1
103
104
  signing_key:
104
105
  specification_version: 3
105
106
  summary: Gem for Rails to easily handle enumeration attributes in models
@@ -1,86 +0,0 @@
1
- module HasEnum
2
- module ActiveRecord
3
- def self.included(base)
4
- base.write_inheritable_hash(:enum, {})
5
- base.extend(ClassMethods)
6
- end
7
-
8
- module ClassMethods
9
- def enum(attribute = nil)
10
- @enum ||= read_inheritable_attribute(:enum)
11
- attribute ? @enum[attribute.to_sym] : @enum
12
- end
13
-
14
- def has_enum(attribute, values, options = {})
15
- options.assert_valid_keys(:query_methods, :scopes)
16
- enum[attribute] = values.freeze
17
-
18
- values.each do |val|
19
- scope :"#{attribute}_#{val}", where(:"#{attribute}" => "#{val}")
20
- end if options[:scopes]
21
-
22
- values.each do |val|
23
- define_method(:"#{attribute}_#{val}?") { self.send(attribute) == val }
24
- end if options[:query_methods] != false
25
-
26
- define_method(:"#{attribute}=") do |value|
27
- if value.nil? or values.find{ |val| val == value }
28
- set_value = lambda { |value| value.is_a?(Symbol) ? Marshal.dump(value) : value }
29
- self[:"#{attribute}"] = value.nil? ? nil : set_value.call(value)
30
- else
31
- errors.add(:"#{attribute}", "#{value} is not in enum")
32
- end
33
- end
34
-
35
- define_method(:"#{attribute}") do
36
- load_value = lambda { |value| value[0].getbyte(0) == 4 ? Marshal.load(value) : value }
37
- value = self[:"#{attribute}"]
38
- value ? load_value.call(value) : nil
39
- end
40
-
41
- define_method "human_#{attribute}" do
42
- return nil unless self.send(attribute)
43
-
44
- defaults = ["activerecord.attributes.#{self.class.klass_key}.#{attribute}_enum.#{self.send(attribute)}"]
45
- defaults << self.send(attribute).to_s.humanize
46
- I18n.translate(defaults.shift, :default => defaults, :raise => true)
47
- end
48
- end
49
-
50
- def human_enum
51
- scope = "activerecord.attributes.#{self.klass_key}"
52
- i18n = lambda { |key| I18n.t(key, :scope => scope, :raise => true) }
53
- trans_hash = {}
54
-
55
- enum.each_pair do |attribute, values|
56
- begin
57
- trans_hash[attribute] = i18n.call("#{attribute}_enum")
58
- rescue I18n::MissingTranslationData
59
- hash = {}
60
- values.each do |value|
61
- hash[value.to_sym] = value.humanize
62
- end
63
- trans_hash[attribute] = hash
64
- end
65
- end
66
-
67
- trans_hash
68
- end
69
-
70
- def values_for_select_tag(enum)
71
- values = enum(enum)
72
- begin
73
- translation = I18n.translate("activerecord.attributes.#{self.klass_key}.#{enum}_enum", :raise => true)
74
-
75
- values.map { |value| [translation[value.to_sym], value] }
76
- rescue I18n::MissingTranslationData
77
- values.map { |value| [value.to_s.humanize, value] }
78
- end
79
- end
80
-
81
- def klass_key
82
- self.model_name.respond_to?(:i18n_key) ? self.model_name.i18n_key : self.name.underscore
83
- end
84
- end
85
- end
86
- end