selectable_attr_rails 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/MIT-LICENSE +20 -0
- data/README +198 -0
- data/Rakefile +57 -0
- data/VERSION.yml +4 -0
- data/init.rb +5 -0
- data/install.rb +1 -0
- data/lib/selectable_attr_i18n.rb +38 -0
- data/lib/selectable_attr_rails/db_loadable.rb +65 -0
- data/lib/selectable_attr_rails/helpers/abstract_selection_helper.rb +55 -0
- data/lib/selectable_attr_rails/helpers/check_box_group_helper.rb +63 -0
- data/lib/selectable_attr_rails/helpers/radio_button_group_helper.rb +55 -0
- data/lib/selectable_attr_rails/helpers/select_helper.rb +72 -0
- data/lib/selectable_attr_rails/helpers.rb +7 -0
- data/lib/selectable_attr_rails/validatable/base.rb +33 -0
- data/lib/selectable_attr_rails/validatable/enum.rb +16 -0
- data/lib/selectable_attr_rails/validatable.rb +8 -0
- data/lib/selectable_attr_rails/version.rb +3 -0
- data/lib/selectable_attr_rails.rb +43 -0
- data/selectable_attr_rails.gemspec +74 -0
- data/spec/database.yml +6 -0
- data/spec/fixtures/.gitignore +0 -0
- data/spec/introduction_spec.rb +397 -0
- data/spec/schema.rb +30 -0
- data/spec/selectable_attr_i18n_spec.rb +293 -0
- data/spec/spec_helper.rb +30 -0
- data/uninstall.rb +1 -0
- metadata +123 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'selectable_attr_rails/helpers/abstract_selection_helper'
|
2
|
+
module SelectableAttrRails::Helpers
|
3
|
+
module CheckBoxGroupHelper
|
4
|
+
class Builder < SelectableAttrRails::Helpers::AbstractSelectionBuilder
|
5
|
+
|
6
|
+
def initialize(object, object_name, method, options, template)
|
7
|
+
super(object, object_name, method, options, template)
|
8
|
+
@entry_hash_array ||= enum_hash_array_from_object
|
9
|
+
@param_name = "#{@base_name}_ids"
|
10
|
+
@check_box_options = @options.delete(:check_box) || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def each(&block)
|
14
|
+
@entry_hash_array.each do |@entry_hash|
|
15
|
+
@tag_value = @entry_hash[:id].to_s.gsub(/\s/, "_").gsub(/\W/, "")
|
16
|
+
@check_box_id = "#{@object_name}_#{@param_name}_#{@tag_value}"
|
17
|
+
yield(self)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def check_box(options = nil)
|
22
|
+
options = update_options({
|
23
|
+
:id => @check_box_id, :type => 'checkbox', :value => @tag_value,
|
24
|
+
:name => "#{@object_name}[#{@param_name}][]"
|
25
|
+
}, @check_box_options, options)
|
26
|
+
options[:checked] = 'checked' if @entry_hash[:select]
|
27
|
+
@template.content_tag("input", nil, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def label(text = nil, options = nil)
|
31
|
+
@template.content_tag("label", text || @entry_hash[:name],
|
32
|
+
update_options({:for => @check_box_id}, options))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Base
|
37
|
+
def check_box_group(object_name, method, options = nil, &block)
|
38
|
+
object = (options || {})[:object] || instance_variable_get("@#{object_name}")
|
39
|
+
builder = Builder.new(object, object_name, method, options, @template)
|
40
|
+
if block_given?
|
41
|
+
yield(builder)
|
42
|
+
return nil
|
43
|
+
else
|
44
|
+
result = ''
|
45
|
+
builder.each do
|
46
|
+
result << builder.check_box
|
47
|
+
result << ' '
|
48
|
+
result << builder.label
|
49
|
+
result << ' '
|
50
|
+
end
|
51
|
+
return result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module FormBuilder
|
57
|
+
def check_box_group(method, options = nil, &block)
|
58
|
+
@template.check_box_group(@object_name, method,
|
59
|
+
(options || {}).merge(:object => @object), &block)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module SelectableAttrRails::Helpers
|
2
|
+
module RadioButtonGroupHelper
|
3
|
+
class Builder < SelectableAttrRails::Helpers::AbstractSelectionBuilder
|
4
|
+
|
5
|
+
def initialize(object, object_name, method, options, template)
|
6
|
+
super(object, object_name, method, options, template)
|
7
|
+
@entry_hash_array ||= enum_hash_array_from_class
|
8
|
+
end
|
9
|
+
|
10
|
+
def each(&block)
|
11
|
+
@entry_hash_array.each do |entry_hash|
|
12
|
+
@entry_hash = entry_hash
|
13
|
+
tag_value = @entry_hash[:id].to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
|
14
|
+
@radio_button_id = "#{@object_name}_#{@method}_#{tag_value}"
|
15
|
+
yield(self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def radio_button(options = nil)
|
20
|
+
@template.radio_button(@object_name, @method, @entry_hash[:id],
|
21
|
+
update_options({:id => @radio_button_id}, options))
|
22
|
+
end
|
23
|
+
|
24
|
+
def label(text = nil, options = nil)
|
25
|
+
@template.content_tag("label", text || @entry_hash[:name],
|
26
|
+
update_options({:for => @radio_button_id}, options))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Base
|
31
|
+
def radio_button_group(object_name, method, options = nil, &block)
|
32
|
+
object = (options || {})[:object] || instance_variable_get("@#{object_name}")
|
33
|
+
builder = Builder.new(object, object_name, method, options, self)
|
34
|
+
if block_given?
|
35
|
+
yield(builder)
|
36
|
+
return nil
|
37
|
+
else
|
38
|
+
result = ''
|
39
|
+
builder.each do
|
40
|
+
result << builder.radio_button
|
41
|
+
result << builder.label
|
42
|
+
end
|
43
|
+
return result
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module FormBuilder
|
49
|
+
def radio_button_group(method, options = nil, &block)
|
50
|
+
@template.radio_button_group(@object_name, method,
|
51
|
+
(options || {}).merge(:object => @object), &block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module SelectableAttrRails::Helpers
|
2
|
+
module SelectHelper
|
3
|
+
module Base
|
4
|
+
def self.included(base)
|
5
|
+
base.module_eval do
|
6
|
+
alias_method_chain :select, :attr_enumeable
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# def select_with_attr_enumeable(object, method, choices, options = {}, html_options = {})
|
11
|
+
def select_with_attr_enumeable(object_name, method, *args, &block)
|
12
|
+
if args.length > 3
|
13
|
+
raise ArgumentError, "argument must be " <<
|
14
|
+
"(object, method, choices, options = {}, html_options = {}) or " <<
|
15
|
+
"(object, method, options = {}, html_options = {})"
|
16
|
+
end
|
17
|
+
return select_without_attr_enumeable(object_name, method, *args, &block) if args.length == 3
|
18
|
+
return select_without_attr_enumeable(object_name, method, *args, &block) if args.first.is_a?(Array)
|
19
|
+
options, html_options = *args
|
20
|
+
options = update_enum_select_options(options, object_name, method)
|
21
|
+
object, base_name = options[:object], options[:base_name]
|
22
|
+
return multi_enum_select(object_name, method, options, html_options, &block) if object.respond_to?("#{base_name}_hash_array")
|
23
|
+
return single_enum_select(object_name, method, options, html_options, &block) if object.class.respond_to?("#{base_name}_hash_array")
|
24
|
+
raise ArgumentError, "invaliad argument"
|
25
|
+
end
|
26
|
+
|
27
|
+
def single_enum_select(object_name, method, options = {}, html_options = {}, &block)
|
28
|
+
options = update_enum_select_options(options, object_name, method)
|
29
|
+
object = options.delete(:object)
|
30
|
+
base_name = options.delete(:base_name)
|
31
|
+
entry_hash_array = options.delete(:entry_hash_array) || object.class.send("#{base_name}_hash_array")
|
32
|
+
container = entry_hash_array.map{|hash| [hash[:name].to_s, hash[:id]]}
|
33
|
+
select_without_attr_enumeable(object_name, method, container, options, html_options || {}, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def multi_enum_select(object_name, method, options = {}, html_options = {}, &block)
|
37
|
+
html_options = {:size => 5, :multiple => 'multiple'}.update(html_options || {})
|
38
|
+
options = update_enum_select_options(options, object_name, method)
|
39
|
+
object = options.delete(:object)
|
40
|
+
base_name = options.delete(:base_name)
|
41
|
+
entry_hash_array = options.delete(:entry_hash_array) || object.send("#{base_name}_hash_array")
|
42
|
+
container = entry_hash_array.map{|hash| [hash[:name].to_s, hash[:id].to_s]}
|
43
|
+
attr = "#{base_name}_ids"
|
44
|
+
select_without_attr_enumeable(object_name, attr, container, options, html_options, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_enum_select_options(options, object_name, method)
|
48
|
+
options ||= {}
|
49
|
+
object = (options[:object] ||= instance_variable_get("@#{object_name}"))
|
50
|
+
options[:base_name] ||= object.class.enum_base_name(method.to_s)
|
51
|
+
options
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module FormBuilder
|
56
|
+
def self.included(base)
|
57
|
+
base.module_eval do
|
58
|
+
alias_method_chain :select, :attr_enumeable
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# def select_with_attr_enumeable(method, choices, options = {}, html_options = {}, &block)
|
63
|
+
def select_with_attr_enumeable(method, *args, &block)
|
64
|
+
options = args.first.is_a?(Array) ? (args[1] ||= {}) : (args[0] ||= {})
|
65
|
+
object = (options || {}).delete(:object) || @object || instance_variable_get(@object_name) rescue nil
|
66
|
+
options.update({:object => object})
|
67
|
+
options[:selected] = object.send(method) if object
|
68
|
+
@template.select(@object_name, method, *args, &block)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module SelectableAttrRails
|
2
|
+
module Helpers
|
3
|
+
autoload :SelectHelper, 'selectable_attr_rails/helpers/select_helper'
|
4
|
+
autoload :CheckBoxGroupHelper, 'selectable_attr_rails/helpers/check_box_group_helper'
|
5
|
+
autoload :RadioButtonGroupHelper, 'selectable_attr_rails/helpers/radio_button_group_helper'
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'selectable_attr_rails/validatable'
|
2
|
+
|
3
|
+
module SelectableAttrRails
|
4
|
+
module Validatable
|
5
|
+
module Base
|
6
|
+
def self.included(mod)
|
7
|
+
mod.extend(ClassMethods)
|
8
|
+
mod.instance_eval do
|
9
|
+
alias :define_enum_without_validatable :define_enum
|
10
|
+
alias :define_enum :define_enum_with_validatable
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def define_enum_with_validatable(context)
|
16
|
+
enum = context[:enum]
|
17
|
+
if options = enum.validates_format_options
|
18
|
+
options[:with] = Regexp.union(*enum.entries.map{|entry| /#{Regexp.escape(entry.id)}/})
|
19
|
+
entry_format = options.delete(:entry_format) || '#{entry.name}'
|
20
|
+
entries = enum.entries.map{|entry| instance_eval("\"#{entry_format}\"")}.join(', ')
|
21
|
+
message = options.delete(:message) || 'is invalid, must be one of #{entries}'
|
22
|
+
options[:message] = instance_eval("\"#{message}\"")
|
23
|
+
validates_format_of(context[:attr], options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'selectable_attr_rails/validatable'
|
2
|
+
|
3
|
+
module SelectableAttrRails
|
4
|
+
module Validatable
|
5
|
+
module Enum
|
6
|
+
def validates_format_options
|
7
|
+
@validates_format_options
|
8
|
+
end
|
9
|
+
def validates_format(options = nil)
|
10
|
+
return @validates_format_options = nil if options == false
|
11
|
+
@validates_format_options = options || {}
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'selectable_attr'
|
3
|
+
|
4
|
+
module SelectableAttrRails
|
5
|
+
autoload :Helpers, 'selectable_attr_rails/helpers'
|
6
|
+
autoload :DbLoadable, 'selectable_attr_rails/db_loadable'
|
7
|
+
autoload :Validatable, 'selectable_attr_rails/validatable'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def add_features_to_active_record
|
12
|
+
puts "SelectableAttrRails.add_features_to_active_record"
|
13
|
+
ActiveRecord::Base.module_eval do
|
14
|
+
include ::SelectableAttr::Base
|
15
|
+
include ::SelectableAttrRails::Validatable::Base
|
16
|
+
end
|
17
|
+
SelectableAttr::Enum.module_eval do
|
18
|
+
include ::SelectableAttrRails::DbLoadable
|
19
|
+
include ::SelectableAttrRails::Validatable::Enum
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_features_to_action_view
|
24
|
+
ActionView::Base.module_eval do
|
25
|
+
include ::SelectableAttrRails::Helpers::SelectHelper::Base
|
26
|
+
include ::SelectableAttrRails::Helpers::CheckBoxGroupHelper::Base
|
27
|
+
include ::SelectableAttrRails::Helpers::RadioButtonGroupHelper::Base
|
28
|
+
end
|
29
|
+
ActionView::Helpers::FormBuilder.module_eval do
|
30
|
+
include ::SelectableAttrRails::Helpers::SelectHelper::FormBuilder
|
31
|
+
include ::SelectableAttrRails::Helpers::CheckBoxGroupHelper::FormBuilder
|
32
|
+
include ::SelectableAttrRails::Helpers::RadioButtonGroupHelper::FormBuilder
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_features_to_rails
|
37
|
+
puts "SelectableAttrRails.add_features_to_rails"
|
38
|
+
add_features_to_active_record
|
39
|
+
add_features_to_action_view
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{selectable_attr_rails}
|
5
|
+
s.version = "0.3.7"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Takeshi Akima"]
|
9
|
+
s.date = %q{2009-08-17}
|
10
|
+
s.description = %q{selectable_attr_rails makes possible to use selectable_attr in rails application}
|
11
|
+
s.email = %q{akima@gmail.com}
|
12
|
+
s.extra_rdoc_files = [
|
13
|
+
"README"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
".gitignore",
|
17
|
+
"MIT-LICENSE",
|
18
|
+
"README",
|
19
|
+
"Rakefile",
|
20
|
+
"VERSION.yml",
|
21
|
+
"init.rb",
|
22
|
+
"install.rb",
|
23
|
+
"lib/selectable_attr_i18n.rb",
|
24
|
+
"lib/selectable_attr_rails.rb",
|
25
|
+
"lib/selectable_attr_rails/db_loadable.rb",
|
26
|
+
"lib/selectable_attr_rails/helpers.rb",
|
27
|
+
"lib/selectable_attr_rails/helpers/abstract_selection_helper.rb",
|
28
|
+
"lib/selectable_attr_rails/helpers/check_box_group_helper.rb",
|
29
|
+
"lib/selectable_attr_rails/helpers/radio_button_group_helper.rb",
|
30
|
+
"lib/selectable_attr_rails/helpers/select_helper.rb",
|
31
|
+
"lib/selectable_attr_rails/version.rb",
|
32
|
+
"selectable_attr_rails.gemspec",
|
33
|
+
"spec/database.yml",
|
34
|
+
"spec/fixtures/.gitignore",
|
35
|
+
"spec/introduction_spec.rb",
|
36
|
+
"spec/schema.rb",
|
37
|
+
"spec/selectable_attr_i18n_spec.rb",
|
38
|
+
"spec/spec_helper.rb",
|
39
|
+
"uninstall.rb"
|
40
|
+
]
|
41
|
+
s.homepage = %q{http://github.com/akm/selectable_attr_rails/}
|
42
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
43
|
+
s.require_paths = ["lib"]
|
44
|
+
s.rubygems_version = %q{1.3.4}
|
45
|
+
s.summary = %q{selectable_attr_rails makes possible to use selectable_attr in rails application}
|
46
|
+
s.test_files = [
|
47
|
+
"spec/introduction_spec.rb",
|
48
|
+
"spec/schema.rb",
|
49
|
+
"spec/selectable_attr_i18n_spec.rb",
|
50
|
+
"spec/spec_helper.rb"
|
51
|
+
]
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
55
|
+
s.specification_version = 3
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
58
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 2.0.2"])
|
59
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 2.0.2"])
|
60
|
+
s.add_runtime_dependency(%q<actionpack>, [">= 2.0.2"])
|
61
|
+
s.add_runtime_dependency(%q<akm-selectable_attr>, [">= 0.3.5"])
|
62
|
+
else
|
63
|
+
s.add_dependency(%q<activesupport>, [">= 2.0.2"])
|
64
|
+
s.add_dependency(%q<activerecord>, [">= 2.0.2"])
|
65
|
+
s.add_dependency(%q<actionpack>, [">= 2.0.2"])
|
66
|
+
s.add_dependency(%q<akm-selectable_attr>, [">= 0.3.5"])
|
67
|
+
end
|
68
|
+
else
|
69
|
+
s.add_dependency(%q<activesupport>, [">= 2.0.2"])
|
70
|
+
s.add_dependency(%q<activerecord>, [">= 2.0.2"])
|
71
|
+
s.add_dependency(%q<actionpack>, [">= 2.0.2"])
|
72
|
+
s.add_dependency(%q<akm-selectable_attr>, [">= 0.3.5"])
|
73
|
+
end
|
74
|
+
end
|
data/spec/database.yml
ADDED
File without changes
|
@@ -0,0 +1,397 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
3
|
+
|
4
|
+
describe SelectableAttr do
|
5
|
+
|
6
|
+
def assert_product_discount(klass)
|
7
|
+
# productsテーブルのデータから安売り用の価格は
|
8
|
+
# product_type_cd毎に決められた割合をpriceにかけて求めます。
|
9
|
+
p1 = klass.new(:name => '実践Rails', :product_type_cd => '01', :price => 3000)
|
10
|
+
p1.discount_price.should == 2400
|
11
|
+
p2 = klass.new(:name => '薔薇の名前', :product_type_cd => '02', :price => 1500)
|
12
|
+
p2.discount_price.should == 300
|
13
|
+
p3 = klass.new(:name => '未来派野郎', :product_type_cd => '03', :price => 3000)
|
14
|
+
p3.discount_price.should == 1500
|
15
|
+
end
|
16
|
+
|
17
|
+
# 定数をガンガン定義した場合
|
18
|
+
# 大文字が多くて読みにくいし、関連するデータ(ここではDISCOUNT)が増える毎に定数も増えていきます。
|
19
|
+
class LegacyProduct1 < ActiveRecord::Base
|
20
|
+
set_table_name 'products'
|
21
|
+
|
22
|
+
PRODUCT_TYPE_BOOK = '01'
|
23
|
+
PRODUCT_TYPE_DVD = '02'
|
24
|
+
PRODUCT_TYPE_CD = '03'
|
25
|
+
PRODUCT_TYPE_OTHER = '09'
|
26
|
+
|
27
|
+
PRODUCT_TYPE_OPTIONS = [
|
28
|
+
['書籍', PRODUCT_TYPE_BOOK],
|
29
|
+
['DVD', PRODUCT_TYPE_DVD],
|
30
|
+
['CD', PRODUCT_TYPE_CD],
|
31
|
+
['その他', PRODUCT_TYPE_OTHER]
|
32
|
+
]
|
33
|
+
|
34
|
+
DISCOUNT = {
|
35
|
+
PRODUCT_TYPE_BOOK => 0.8,
|
36
|
+
PRODUCT_TYPE_DVD => 0.2,
|
37
|
+
PRODUCT_TYPE_CD => 0.5,
|
38
|
+
PRODUCT_TYPE_OTHER => 1
|
39
|
+
}
|
40
|
+
|
41
|
+
def discount_price
|
42
|
+
(DISCOUNT[product_type_cd] * price).to_i
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "test_legacy_product" do
|
47
|
+
assert_product_discount(LegacyProduct1)
|
48
|
+
|
49
|
+
# 選択肢を表示するためのデータは以下のように取得できる
|
50
|
+
LegacyProduct1::PRODUCT_TYPE_OPTIONS.should ==
|
51
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
# できるだけ定数定義をまとめた場合
|
58
|
+
# 結構すっきりするけど、同じことをいろんなモデルで書くかと思うと気が重い。
|
59
|
+
class LegacyProduct2 < ActiveRecord::Base
|
60
|
+
set_table_name 'products'
|
61
|
+
|
62
|
+
PRODUCT_TYPE_DEFS = [
|
63
|
+
{:id => '01', :name => '書籍', :discount => 0.8},
|
64
|
+
{:id => '02', :name => 'DVD', :discount => 0.2},
|
65
|
+
{:id => '03', :name => 'CD', :discount => 0.5},
|
66
|
+
{:id => '09', :name => 'その他', :discount => 1}
|
67
|
+
]
|
68
|
+
|
69
|
+
PRODUCT_TYPE_OPTIONS = PRODUCT_TYPE_DEFS.map{|t| [t[:name], t[:id]]}
|
70
|
+
DISCOUNT = PRODUCT_TYPE_DEFS.inject({}){|dest, t|
|
71
|
+
dest[t[:id]] = t[:discount]; dest}
|
72
|
+
|
73
|
+
def discount_price
|
74
|
+
(DISCOUNT[product_type_cd] * price).to_i
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it "test_legacy_product" do
|
79
|
+
assert_product_discount(LegacyProduct2)
|
80
|
+
|
81
|
+
# 選択肢を表示するためのデータは以下のように取得できる
|
82
|
+
LegacyProduct2::PRODUCT_TYPE_OPTIONS.should ==
|
83
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
84
|
+
end
|
85
|
+
|
86
|
+
# selectable_attrを使った場合
|
87
|
+
# 定義は一カ所にまとめられて、任意の属性(ここでは:discount)も一緒に書くことができてすっきり〜
|
88
|
+
class Product1 < ActiveRecord::Base
|
89
|
+
set_table_name 'products'
|
90
|
+
|
91
|
+
selectable_attr :product_type_cd do
|
92
|
+
entry '01', :book, '書籍', :discount => 0.8
|
93
|
+
entry '02', :dvd, 'DVD', :discount => 0.2
|
94
|
+
entry '03', :cd, 'CD', :discount => 0.5
|
95
|
+
entry '09', :other, 'その他', :discount => 1
|
96
|
+
validates_format :allow_nil => true, :message => 'は次のいずれかでなければなりません。 #{entries}'
|
97
|
+
end
|
98
|
+
|
99
|
+
def discount_price
|
100
|
+
(product_type_entry[:discount] * price).to_i
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it "test_product1" do
|
105
|
+
assert_product_discount(Product1)
|
106
|
+
# 選択肢を表示するためのデータは以下のように取得できる
|
107
|
+
Product1.product_type_options.should ==
|
108
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# selectable_attrが定義するインスタンスメソッドの詳細
|
113
|
+
it "test_product_type_instance_methods" do
|
114
|
+
p1 = Product1.new
|
115
|
+
p1.product_type_cd.should be_nil
|
116
|
+
p1.product_type_key.should be_nil
|
117
|
+
p1.product_type_name.should be_nil
|
118
|
+
# idを変更すると得られるキーも名称も変わります
|
119
|
+
p1.product_type_cd = '02'
|
120
|
+
p1.product_type_cd.should == '02'
|
121
|
+
p1.product_type_key.should == :dvd
|
122
|
+
p1.product_type_name.should == 'DVD'
|
123
|
+
# キーを変更すると得られるidも名称も変わります
|
124
|
+
p1.product_type_key = :book
|
125
|
+
p1.product_type_cd.should == '01'
|
126
|
+
p1.product_type_key.should == :book
|
127
|
+
p1.product_type_name.should == '書籍'
|
128
|
+
# id、キー、名称以外の任意の属性は、entryの[]メソッドで取得します。
|
129
|
+
p1.product_type_key = :cd
|
130
|
+
p1.product_type_entry[:discount].should == 0.5
|
131
|
+
end
|
132
|
+
|
133
|
+
# selectable_attrが定義するクラスメソッドの詳細
|
134
|
+
it "test_product_type_class_methods" do
|
135
|
+
# キーからid、名称を取得できます
|
136
|
+
Product1.product_type_id_by_key(:book).should == '01'
|
137
|
+
Product1.product_type_id_by_key(:dvd).should == '02'
|
138
|
+
Product1.product_type_id_by_key(:cd).should == '03'
|
139
|
+
Product1.product_type_id_by_key(:other).should == '09'
|
140
|
+
Product1.product_type_name_by_key(:book).should == '書籍'
|
141
|
+
Product1.product_type_name_by_key(:dvd).should == 'DVD'
|
142
|
+
Product1.product_type_name_by_key(:cd).should == 'CD'
|
143
|
+
Product1.product_type_name_by_key(:other).should == 'その他'
|
144
|
+
# 存在しないキーの場合はnilを返します
|
145
|
+
Product1.product_type_id_by_key(nil).should be_nil
|
146
|
+
Product1.product_type_name_by_key(nil).should be_nil
|
147
|
+
Product1.product_type_id_by_key(:unexist).should be_nil
|
148
|
+
Product1.product_type_name_by_key(:unexist).should be_nil
|
149
|
+
|
150
|
+
# idからキー、名称を取得できます
|
151
|
+
Product1.product_type_key_by_id('01').should == :book
|
152
|
+
Product1.product_type_key_by_id('02').should == :dvd
|
153
|
+
Product1.product_type_key_by_id('03').should == :cd
|
154
|
+
Product1.product_type_key_by_id('09').should == :other
|
155
|
+
Product1.product_type_name_by_id('01').should == '書籍'
|
156
|
+
Product1.product_type_name_by_id('02').should == 'DVD'
|
157
|
+
Product1.product_type_name_by_id('03').should == 'CD'
|
158
|
+
Product1.product_type_name_by_id('09').should == 'その他'
|
159
|
+
# 存在しないidの場合はnilを返します
|
160
|
+
Product1.product_type_key_by_id(nil).should be_nil
|
161
|
+
Product1.product_type_name_by_id(nil).should be_nil
|
162
|
+
Product1.product_type_key_by_id('99').should be_nil
|
163
|
+
Product1.product_type_name_by_id('99').should be_nil
|
164
|
+
|
165
|
+
# id、キー、名称の配列を取得できます
|
166
|
+
Product1.product_type_ids.should == ['01', '02', '03', '09']
|
167
|
+
Product1.product_type_keys.should == [:book, :dvd, :cd, :other]
|
168
|
+
Product1.product_type_names.should == ['書籍', 'DVD', 'CD', 'その他']
|
169
|
+
# 一部のものだけ取得することも可能です。
|
170
|
+
Product1.product_type_ids(:cd, :dvd).should == ['03', '02' ]
|
171
|
+
Product1.product_type_keys('02', '03').should == [:dvd, :cd ]
|
172
|
+
Product1.product_type_names('02', '03').should == ['DVD', 'CD']
|
173
|
+
Product1.product_type_names(:cd, :dvd).should == ['CD', 'DVD']
|
174
|
+
|
175
|
+
# select_tagなどのoption_tagsを作るための配列なんか一発っす
|
176
|
+
Product1.product_type_options.should ==
|
177
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
178
|
+
end
|
179
|
+
|
180
|
+
it "validate with entries" do
|
181
|
+
p1 = Product1.new
|
182
|
+
p1.product_type_cd.should == nil
|
183
|
+
p1.valid?.should == true
|
184
|
+
p1.errors.empty?.should == true
|
185
|
+
|
186
|
+
p1.product_type_key = :book
|
187
|
+
p1.product_type_cd.should == '01'
|
188
|
+
p1.valid?.should == true
|
189
|
+
p1.errors.empty?.should == true
|
190
|
+
|
191
|
+
p1.product_type_cd = 'XX'
|
192
|
+
p1.product_type_cd.should == 'XX'
|
193
|
+
p1.valid?.should == false
|
194
|
+
p1.errors.on(:product_type_cd).should == "は次のいずれかでなければなりません。 書籍, DVD, CD, その他"
|
195
|
+
end
|
196
|
+
|
197
|
+
# selectable_attrのエントリ名をDB上に保持するためのモデル
|
198
|
+
class ItemMaster < ActiveRecord::Base
|
199
|
+
end
|
200
|
+
|
201
|
+
# selectable_attrを使った場合その2
|
202
|
+
# アクセス時に毎回アクセス時にDBから項目名を取得します。
|
203
|
+
class ProductWithDB1 < ActiveRecord::Base
|
204
|
+
set_table_name 'products'
|
205
|
+
|
206
|
+
selectable_attr :product_type_cd do
|
207
|
+
update_by(
|
208
|
+
"select item_cd, name from item_masters where category_name = 'product_type_cd' order by item_no",
|
209
|
+
:when => :everytime)
|
210
|
+
entry '01', :book, '書籍', :discount => 0.8
|
211
|
+
entry '02', :dvd, 'DVD', :discount => 0.2
|
212
|
+
entry '03', :cd, 'CD', :discount => 0.5
|
213
|
+
entry '09', :other, 'その他', :discount => 1
|
214
|
+
end
|
215
|
+
|
216
|
+
def discount_price
|
217
|
+
(product_type_entry[:discount] * price).to_i
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
it "test_update_entry_name" do
|
222
|
+
# DBに全くデータがなくてもコードで記述してあるエントリは存在します。
|
223
|
+
ItemMaster.delete_all("category_name = 'product_type_cd'")
|
224
|
+
ProductWithDB1.product_type_entries.length.should == 4
|
225
|
+
ProductWithDB1.product_type_name_by_key(:book).should == '書籍'
|
226
|
+
ProductWithDB1.product_type_name_by_key(:dvd).should == 'DVD'
|
227
|
+
ProductWithDB1.product_type_name_by_key(:cd).should == 'CD'
|
228
|
+
ProductWithDB1.product_type_name_by_key(:other).should == 'その他'
|
229
|
+
|
230
|
+
assert_product_discount(ProductWithDB1)
|
231
|
+
|
232
|
+
# DBからエントリの名称を動的に変更できます
|
233
|
+
item_book = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 1, :item_cd => '01', :name => '本')
|
234
|
+
ProductWithDB1.product_type_entries.length.should == 4
|
235
|
+
ProductWithDB1.product_type_name_by_key(:book).should == '本'
|
236
|
+
ProductWithDB1.product_type_name_by_key(:dvd).should == 'DVD'
|
237
|
+
ProductWithDB1.product_type_name_by_key(:cd).should == 'CD'
|
238
|
+
ProductWithDB1.product_type_name_by_key(:other).should == 'その他'
|
239
|
+
ProductWithDB1.product_type_options.should ==
|
240
|
+
[['本', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
241
|
+
|
242
|
+
# DBからエントリの並び順を動的に変更できます
|
243
|
+
item_book.item_no = 4;
|
244
|
+
item_book.save!
|
245
|
+
item_other = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 1, :item_cd => '09', :name => 'その他')
|
246
|
+
item_dvd = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 2, :item_cd => '02') # nameは指定しなかったらデフォルトが使われます。
|
247
|
+
item_cd = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 3, :item_cd => '03') # nameは指定しなかったらデフォルトが使われます。
|
248
|
+
ProductWithDB1.product_type_options.should ==
|
249
|
+
[['その他', '09'], ['DVD', '02'], ['CD', '03'], ['本', '01']]
|
250
|
+
|
251
|
+
# DBからエントリを動的に追加することも可能です。
|
252
|
+
item_toys = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 5, :item_cd => '04', :name => 'おもちゃ')
|
253
|
+
ProductWithDB1.product_type_options.should ==
|
254
|
+
[['その他', '09'], ['DVD', '02'], ['CD', '03'], ['本', '01'], ['おもちゃ', '04']]
|
255
|
+
ProductWithDB1.product_type_key_by_id('04').should == :entry_04
|
256
|
+
|
257
|
+
# DBからレコードを削除してもコードで定義したentryは削除されません。
|
258
|
+
# 順番はDBからの取得順で並び替えられたものの後になります
|
259
|
+
item_dvd.destroy
|
260
|
+
ProductWithDB1.product_type_options.should ==
|
261
|
+
[['その他', '09'], ['CD', '03'], ['本', '01'], ['おもちゃ', '04'], ['DVD', '02']]
|
262
|
+
|
263
|
+
# DB上で追加したレコードを削除すると、エントリも削除されます
|
264
|
+
item_toys.destroy
|
265
|
+
ProductWithDB1.product_type_options.should ==
|
266
|
+
[['その他', '09'], ['CD', '03'], ['本', '01'], ['DVD', '02']]
|
267
|
+
|
268
|
+
# 名称を指定していたDBのレコードを削除したら元に戻ります。
|
269
|
+
item_book.destroy
|
270
|
+
ProductWithDB1.product_type_options.should ==
|
271
|
+
[['その他', '09'], ['CD', '03'], ['書籍', '01'], ['DVD', '02']]
|
272
|
+
|
273
|
+
# エントリに該当するレコードを全部削除したら、元に戻ります。
|
274
|
+
ItemMaster.delete_all("category_name = 'product_type_cd'")
|
275
|
+
ProductWithDB1.product_type_options.should ==
|
276
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
277
|
+
|
278
|
+
assert_product_discount(ProductWithDB1)
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
|
283
|
+
|
284
|
+
# Q: product_type_cd の'_cd'はどこにいっちゃったの?
|
285
|
+
# A: デフォルトでは、/(_cd$|_code$|_cds$|_codes$)/ を削除したものをbase_nameとして
|
286
|
+
# 扱い、それに_keyなどを付加してメソッド名を定義します。もしこのルールを変更したい場合、
|
287
|
+
# selectable_attrを使う前に selectable_attr_name_pattern で新たなルールを指定してください。
|
288
|
+
class Product2 < ActiveRecord::Base
|
289
|
+
set_table_name 'products'
|
290
|
+
self.selectable_attr_name_pattern = /^product_|_cd$/
|
291
|
+
|
292
|
+
selectable_attr :product_type_cd do
|
293
|
+
entry '01', :book, '書籍', :discount => 0.8
|
294
|
+
entry '02', :dvd, 'DVD', :discount => 0.2
|
295
|
+
entry '03', :cd, 'CD', :discount => 0.5
|
296
|
+
entry '09', :other, 'その他', :discount => 1
|
297
|
+
end
|
298
|
+
|
299
|
+
def discount_price
|
300
|
+
(type_entry[:discount] * price).to_i
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
it "test_product2" do
|
305
|
+
assert_product_discount(Product2)
|
306
|
+
# 選択肢を表示するためのデータは以下のように取得できる
|
307
|
+
Product2.type_options.should ==
|
308
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
309
|
+
|
310
|
+
p2 = Product2.new
|
311
|
+
p2.product_type_cd.should be_nil
|
312
|
+
p2.type_key.should be_nil
|
313
|
+
p2.type_name.should be_nil
|
314
|
+
# idを変更すると得られるキーも名称も変わります
|
315
|
+
p2.product_type_cd = '02'
|
316
|
+
p2.product_type_cd.should == '02'
|
317
|
+
p2.type_key.should == :dvd
|
318
|
+
p2.type_name.should == 'DVD'
|
319
|
+
# キーを変更すると得られるidも名称も変わります
|
320
|
+
p2.type_key = :book
|
321
|
+
p2.product_type_cd.should == '01'
|
322
|
+
p2.type_key.should == :book
|
323
|
+
p2.type_name.should == '書籍'
|
324
|
+
# id、キー、名称以外の任意の属性は、entryの[]メソッドで取得します。
|
325
|
+
p2.type_key = :cd
|
326
|
+
p2.type_entry[:discount].should == 0.5
|
327
|
+
|
328
|
+
Product2.type_id_by_key(:book).should == '01'
|
329
|
+
Product2.type_id_by_key(:dvd).should == '02'
|
330
|
+
Product2.type_name_by_key(:cd).should == 'CD'
|
331
|
+
Product2.type_name_by_key(:other).should == 'その他'
|
332
|
+
Product2.type_key_by_id('09').should == :other
|
333
|
+
Product2.type_name_by_id('01').should == '書籍'
|
334
|
+
Product2.type_keys.should == [:book, :dvd, :cd, :other]
|
335
|
+
Product2.type_names.should == ['書籍', 'DVD', 'CD', 'その他']
|
336
|
+
Product2.type_keys('02', '03').should == [:dvd, :cd]
|
337
|
+
Product2.type_names(:cd, :dvd).should == ['CD', 'DVD']
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
|
342
|
+
|
343
|
+
# Q: selectable_attrの呼び出し毎にbase_bname(って言うの?)を指定したいんだけど。
|
344
|
+
# A: base_nameオプションを指定してください。
|
345
|
+
class Product3 < ActiveRecord::Base
|
346
|
+
set_table_name 'products'
|
347
|
+
|
348
|
+
selectable_attr :product_type_cd, :base_name => 'type' do
|
349
|
+
entry '01', :book, '書籍', :discount => 0.8
|
350
|
+
entry '02', :dvd, 'DVD', :discount => 0.2
|
351
|
+
entry '03', :cd, 'CD', :discount => 0.5
|
352
|
+
entry '09', :other, 'その他', :discount => 1
|
353
|
+
end
|
354
|
+
|
355
|
+
def discount_price
|
356
|
+
(type_entry[:discount] * price).to_i
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
it "test_product3" do
|
361
|
+
assert_product_discount(Product3)
|
362
|
+
# 選択肢を表示するためのデータは以下のように取得できる
|
363
|
+
Product3.type_options.should ==
|
364
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
365
|
+
|
366
|
+
p3 = Product3.new
|
367
|
+
p3.product_type_cd.should be_nil
|
368
|
+
p3.type_key.should be_nil
|
369
|
+
p3.type_name.should be_nil
|
370
|
+
# idを変更すると得られるキーも名称も変わります
|
371
|
+
p3.product_type_cd = '02'
|
372
|
+
p3.product_type_cd.should == '02'
|
373
|
+
p3.type_key.should == :dvd
|
374
|
+
p3.type_name.should == 'DVD'
|
375
|
+
# キーを変更すると得られるidも名称も変わります
|
376
|
+
p3.type_key = :book
|
377
|
+
p3.product_type_cd.should == '01'
|
378
|
+
p3.type_key.should == :book
|
379
|
+
p3.type_name.should == '書籍'
|
380
|
+
# id、キー、名称以外の任意の属性は、entryの[]メソッドで取得します。
|
381
|
+
p3.type_key = :cd
|
382
|
+
p3.type_entry[:discount].should == 0.5
|
383
|
+
|
384
|
+
Product3.type_id_by_key(:book).should == '01'
|
385
|
+
Product3.type_id_by_key(:dvd).should == '02'
|
386
|
+
Product3.type_name_by_key(:cd).should == 'CD'
|
387
|
+
Product3.type_name_by_key(:other).should == 'その他'
|
388
|
+
Product3.type_key_by_id('09').should == :other
|
389
|
+
Product3.type_name_by_id('01').should == '書籍'
|
390
|
+
Product3.type_keys.should == [:book, :dvd, :cd, :other]
|
391
|
+
Product3.type_names.should == ['書籍', 'DVD', 'CD', 'その他']
|
392
|
+
Product3.type_keys('02', '03').should == [:dvd, :cd]
|
393
|
+
Product3.type_names(:cd, :dvd).should == ['CD', 'DVD']
|
394
|
+
end
|
395
|
+
|
396
|
+
end
|
397
|
+
|