code-box 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -6,7 +6,7 @@ TODO: Write a gem description
6
6
 
7
7
  Add this line to your application's Gemfile:
8
8
 
9
- gem 'code-attr'
9
+ gem 'code-box'
10
10
 
11
11
  And then execute:
12
12
 
@@ -14,11 +14,62 @@ And then execute:
14
14
 
15
15
  Or install it yourself as:
16
16
 
17
- $ gem install code-attr
17
+ $ gem install code-box
18
18
 
19
19
  ## Usage
20
20
 
21
- TODO: Write usage instructions here
21
+ ### Specifying attributes as codes
22
+
23
+ There are cases you want to store 'named codes' instead artificial keys.
24
+ Codes make sense for stable references and better readability of the raw data.
25
+
26
+ There are several options to specify an attribute as a code:
27
+ 1. Attribute is a code. There is no associated object involved, but simple I18n translation of the code
28
+ 1. Attribute is a code. There exists a code object that is looked up on access.
29
+ 1. Attribute is a code. There exists an AR code object that is looked up through AR association.
30
+
31
+ #### Lookup through I18n
32
+
33
+ Example
34
+
35
+ class Person
36
+ iclude CodeBox::CodeAttribute
37
+
38
+ attr_accessor :nationality_code
39
+ end
40
+
41
+ The include will create the following methods in Person:
42
+
43
+ #nationality Will return the nationality looked up through I18n on key: 'activerecord.values.person.nationality_code.de: Germany', where de would 'de' the nationality code.
44
+
45
+
46
+
47
+ #### Lookup through code object
48
+
49
+ Example
50
+
51
+ class Person
52
+ iclude CodeBox::CodeAttribute
53
+
54
+ attr_accessor :nationality_code, :lookup_type => :lookup
55
+ end
56
+
57
+ class Code::Nationality
58
+ attr_accessor :code, :name
59
+
60
+ def lookup(code)
61
+ return the correct Code::Nationality for the passed code
62
+ end
63
+ end
64
+
65
+ The include will create the following methods in Person:
66
+
67
+ #nationality Will return the nationality looked through lookup on the associated code object.
68
+
69
+
70
+ #### Lookup through associated AR Code Object
71
+ to be completed ...
72
+
22
73
 
23
74
  ## Contributing
24
75
 
@@ -6,67 +6,125 @@ module CodeBox
6
6
  module ActsAsCode
7
7
  @opts = {}
8
8
 
9
- def self.[](options={})
9
+ def self.[](*options)
10
10
  @opts = options
11
11
  self
12
12
  end
13
13
 
14
14
  def self.included(base)
15
15
  base.extend(ClassMethods)
16
- base.acts_as_code(@opts)
16
+ base.acts_as_code(*@opts) if @opts.size > 0
17
17
  end
18
18
 
19
19
 
20
20
  module ClassMethods
21
21
  DefaultOptions = {
22
+ :model_type => :poro,
22
23
  :code_attribute => :code,
23
24
  :polymorphic => false,
24
- :position_attribute => :position,
25
- :uniqueness_case_sensitive => false
25
+ :uniqueness_case_sensitive => true,
26
+ :position_attr => :position,
26
27
  }
27
28
 
28
- def acts_as_code(options={})
29
+ def acts_as_code(*codes_and_or_options)
30
+ options = codes_and_or_options.extract_options!
31
+ codes = codes_and_or_options
29
32
  opts = DefaultOptions.merge(options)
30
33
  code_attr = opts[:code_attribute]
31
34
  position_attr = opts[:position_attribute]
32
35
  case_sensitive = opts[:uniqueness_case_sensitive]
36
+ model_type = opts.delete(:model_type)
33
37
 
34
- order_expression = if position_attr then
35
- "coalesce(#{position_attr.to_s}, #{code_attr.to_s})"
36
- else
37
- code_attr.to_s
38
+ # Create a constant for each code
39
+ codes.each do |code|
40
+ const_set("Code#{code.to_s.camelize}", code)
38
41
  end
39
42
 
40
- class_eval <<-CODE
41
- validates_presence_of :#{code_attr}
42
- validates_uniqueness_of :#{code_attr}#{opts[:polymorphic] ? ', :scope => :type' : ' '}, :case_sensitive => #{case_sensitive}
43
+ case model_type
43
44
 
44
- default_scope order('#{order_expression}')
45
-
46
- def self.initialize_cache
47
- all.inject({}) {|hash, obj| hash[obj.#{code_attr}] = obj; hash }
48
- end
49
-
50
- def self.for(code)
51
- code_cache[code]
52
- end
53
-
54
- def hash
55
- (self.class.name + '#' + #{code_attr}).hash
56
- end
45
+ when :active_record
46
+ order_expression = if self.attribute_names.include?(position_attr) then
47
+ "coalesce(#{position_attr.to_s}, #{code_attr.to_s})"
48
+ else
49
+ code_attr.to_s
50
+ end
57
51
 
58
- def equal?(other)
59
- other && is_a?(other.class) && #{code_attr} == other.#{code_attr}
60
- end
61
- CODE
52
+ class_eval <<-CODE
53
+ validates_presence_of :#{code_attr}
54
+ validates_uniqueness_of :#{code_attr}#{opts[:polymorphic] ? ', :scope => :type' : ' '}, :case_sensitive => #{case_sensitive}
55
+
56
+ default_scope order('#{order_expression}')
57
+
58
+ def self.initialize_cache
59
+ all.inject({}) {|hash, obj| hash[obj.#{code_attr}] = obj; hash }
60
+ end
61
+
62
+ def self.lookup(code)
63
+ code_cache[code]
64
+ end
65
+
66
+ def hash
67
+ (self.class.name + '#' + #{code_attr}).hash
68
+ end
69
+
70
+ def equal?(other)
71
+ other && is_a?(other.class) && #{code_attr} == other.#{code_attr}
72
+ end
73
+
74
+ def ==(other)
75
+ self.equal? other
76
+ end
77
+ CODE
78
+
79
+ instance_eval <<-CODE
80
+ class << self
81
+ def code_cache
82
+ @code_cache ||= initialize_cache
83
+ end
84
+ end
85
+ CODE
86
+
87
+ when :poro
88
+ order_attr = position_attr ? position_attr.to_s : code_attr.to_s
89
+
90
+ class_eval <<-CODE
91
+ def self.initialize_cache
92
+ all.inject({}) {|hash, obj| hash[obj.#{code_attr}] = obj; hash }
93
+ end
94
+
95
+ def self.lookup(code)
96
+ code_cache[code]
97
+ end
98
+
99
+ def hash
100
+ (self.class.name + '#' + #{code_attr}).hash
101
+ end
102
+
103
+ def equal?(other)
104
+ other && is_a?(other.class) && #{code_attr} == other.#{code_attr}
105
+ end
106
+
107
+ def ==(other)
108
+ self.equal? other
109
+ end
110
+
111
+ def self.all
112
+ raise "Sublass responsibility. You should implement '.all' returning all codes"
113
+ end
114
+ CODE
115
+
116
+ instance_eval <<-CODE
117
+ class << self
118
+ def code_cache
119
+ @code_cache ||= initialize_cache
120
+ end
121
+ end
122
+ CODE
123
+
124
+ else
125
+ raise ArgumentError, "'#{model_type}' is not a valid type. Use :active_record or :poro(default) instead"
126
+ end
62
127
 
63
- instance_eval <<-CODE
64
- class << self
65
- def code_cache
66
- @code_cache ||= initialize_cache
67
- end
68
- end
69
- CODE
70
128
  end
71
129
  end
72
130
  end
@@ -12,14 +12,14 @@ module CodeBox
12
12
  module ClassMethods
13
13
  DefaultOptions = {
14
14
  :foreign_code_attribute => :code,
15
- :lookup => :i18n,
15
+ :lookup_type => :i18n,
16
16
  :code_attribute_suffix => 'code'
17
17
  }
18
18
 
19
19
  def code_attribute(*code_names)
20
20
  options = code_names.extract_options!
21
21
  opts = DefaultOptions.merge(options)
22
- lookup_type = opts.delete(:lookup)
22
+ lookup_type = opts.delete(:lookup_type)
23
23
  code_attr_suffix = (opts.delete(:code_attribute_suffix) || "code").to_s
24
24
  foreign_code_attr_name = opts.delete(:foreign_code_attribute)
25
25
 
@@ -29,11 +29,12 @@ module CodeBox
29
29
  code_class_name = opts_copy.delete(:class_name) || "::Codes::#{code_name.to_s.camelize}"
30
30
 
31
31
  case lookup_type
32
- when :cache
32
+
33
+ when :lookup
33
34
  class_eval <<-RUBY_
34
35
  # getter
35
36
  def #{code_name}
36
- code_class_name.constantize.for(value)
37
+ #{code_class_name}.lookup(#{code_attr_name})
37
38
  end
38
39
 
39
40
  # setter
@@ -42,26 +43,39 @@ module CodeBox
42
43
  #{code_attr_name} = value
43
44
  end
44
45
  RUBY_
45
- when :association
46
+
47
+ when :associated
46
48
  association_options = opts_copy.merge({
47
49
  :class_name => "#{code_class_name}",
48
50
  :foreign_key => "#{code_attr_name}".to_sym,
49
51
  :primary_key => "#{foreign_code_attr_name}"
50
52
  })
51
53
  belongs_to "#{code_name}".to_sym, association_options
54
+
52
55
  when :i18n
53
56
  class_eval <<-RUBY_
54
57
  # getter
55
58
  def #{code_name}(locale=I18n.locale)
56
- value = self.#{code_attr_name}
57
- value_key = value.nil? ? :null_value : value
58
- I18n.t("activerecord.\#{self.class.name.underscore}.values.#{code_attr_name}.\#{value_key}", :locale => locale)
59
+ code = self.#{code_attr_name}
60
+ self.class.translate_#{code_attr_name}(code, locale)
59
61
  end
60
62
 
61
63
  # setter
62
64
  def #{code_name}=(code)
63
65
  raise "#{code_name} is a i18n code and can not be set. Use the the correct method '#{code_attr_name}='' instead."
64
66
  end
67
+
68
+ # translator
69
+ class << self
70
+ def translate_#{code_attr_name}(code, locale=I18n.locale)
71
+ codes = Array(code)
72
+ tranlsated_codes = codes.map { |code|
73
+ code_key = code.nil? ? :null_value : code
74
+ I18n.t("activerecord.\#{self.name.underscore}.values.#{code_attr_name}.\#{code_key}", :locale => locale)
75
+ }
76
+ tranlsated_codes.size == 1 ? tranlsated_codes.first : tranlsated_codes
77
+ end
78
+ end
65
79
  RUBY_
66
80
  else
67
81
  raise ArgumentError, "'#{lookup_type}' is not valid. Must be one of [:code_cache, :association]"
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module CodeBox
4
- VERSION = "0.0.1"
4
+ VERSION = "0.0.2"
5
5
  end
@@ -4,11 +4,33 @@ require 'helper'
4
4
 
5
5
  class TestActsAsCode < Test::Unit::TestCase
6
6
 
7
- def setup
8
7
 
8
+ def test_constants
9
+ assert_equal 'single', Codes::CivilStatus::CodeSingle
9
10
  end
10
11
 
11
- def test_author_attributes_available
12
- assert_equal 1,1
12
+ def test_constants
13
+ assert_equal 2, Codes::CivilStatus::all.size
14
+
15
+ assert_equal Codes::CivilStatus.lookup('single'), Codes::CivilStatus::all.first
16
+ assert_equal Codes::CivilStatus.lookup('married'), Codes::CivilStatus::all.last
17
+ end
18
+
19
+
20
+ def test_ar_code_all
21
+ Codes::ArCode.create(:code => 'code_1', :name => "Code_1_name")
22
+ Codes::ArCode.create(:code => 'code_2', :name => "Code_2_name")
23
+
24
+ assert_equal 2, Codes::ArCode.all.size
13
25
  end
26
+
27
+
28
+ def test_ar_code_lookup
29
+ code_1 = Codes::ArCode.create(:code => 'code_1', :name => "Code_1_name")
30
+ code_2 = Codes::ArCode.create(:code => 'code_2', :name => "Code_2_name")
31
+
32
+ assert_equal code_2, Codes::ArCode.lookup('code_2')
33
+ end
34
+
35
+
14
36
  end
@@ -7,8 +7,10 @@ class TestCodeAttribute < Test::Unit::TestCase
7
7
  def setup
8
8
  end
9
9
 
10
- def test_acts_as_code_i18n_lookup
11
- obj = Code::SampleClass.new(gender_code: 'f', country_iso: 'de')
10
+ # :type => :i18n -------------------------------------------------------------------
11
+ def test_code_attribute_i18n_lookup
12
+ obj = Codes::SampleClass.new(gender_code: 'f', country_iso: 'de')
13
+ I18n.locale =:en
12
14
 
13
15
  assert_equal('f', obj.gender_code)
14
16
  assert_equal('female', obj.gender)
@@ -22,14 +24,64 @@ class TestCodeAttribute < Test::Unit::TestCase
22
24
  assert_equal('de', obj.country_iso)
23
25
  assert_equal('Deutschland', obj.country(:de))
24
26
 
25
- # I18n.locale = :de
27
+ end
28
+
29
+ def test_code_attribute_i18n_translator_with_single_code
30
+ I18n.locale = :de
31
+ translation = Codes::SampleClass.translate_gender_code('f')
32
+ assert_equal('weiblich', translation)
33
+
34
+ translation = Codes::SampleClass.translate_gender_code('f', :en)
35
+ assert_equal('female', translation)
36
+ end
37
+
38
+ def test_code_attribute_i18n_translator_with_multiple_codes
39
+ I18n.locale = :de
40
+ translation = Codes::SampleClass.translate_gender_code(['f', 'm'])
26
41
 
27
- # assert_equal(obj.gender_code, 'f')
28
- # assert_equal(obj.gender, 'weiblich')
42
+ assert translation.kind_of? Array
43
+ assert_equal('weiblich', translation.first)
44
+ assert_equal('männlich', translation.last)
29
45
 
30
- # assert_equal(obj.country_iso, 'de')
31
- # assert_equal(obj.country, 'Deutschland')
46
+ translation = Codes::SampleClass.translate_gender_code(['f', 'm'], :en)
47
+
48
+ assert translation.kind_of? Array
49
+ assert_equal('female', translation.first)
50
+ assert_equal('male', translation.last)
32
51
  end
33
52
 
34
53
 
54
+ # :type => :lookup -------------------------------------------------------------------
55
+ def test_code_attribute_lookup_default
56
+ code_single = Codes::CivilStatus.lookup('single')
57
+ code_married = Codes::CivilStatus.lookup('married')
58
+
59
+ code_client = Codes::SampleClass.new(:civil_status_code => 'single')
60
+
61
+ assert_equal('single', code_client.civil_status_code)
62
+ assert_equal(code_single, code_client.civil_status)
63
+ end
64
+
65
+ def test_code_attribute_lookup_custom_code_name
66
+ code_child = Codes::AgerType.new('child')
67
+ code_teenager = Codes::AgerType.new('teenager')
68
+
69
+ code_client = Codes::SampleClass.new(:ager_type_code => 'child')
70
+
71
+ assert_equal('child', code_client.ager_type_code)
72
+ assert_equal(code_child, code_client.ager_type)
73
+ end
74
+
75
+
76
+ # :type => :associated ----------------------------------------------------------------
77
+ def test_code_attribute_lookup_associated
78
+ code_ch = Codes::Country.create(:code => 'CH', :name => 'Switzerland')
79
+ code_de = Codes::Country.create(:code => 'DE', :name => 'Germany')
80
+
81
+ code_client = Codes::SampleClass.new(:country_2_code => 'CH')
82
+
83
+ assert_equal('CH', code_client.country_2_code)
84
+ assert_equal(code_ch, code_client.country_2)
85
+ end
86
+
35
87
  end
@@ -2,7 +2,7 @@
2
2
  ---
3
3
  de:
4
4
  activerecord:
5
- code/sample_class:
5
+ codes/sample_class:
6
6
  values:
7
7
  gender_code:
8
8
  f: weiblich
@@ -2,7 +2,7 @@
2
2
  ---
3
3
  en:
4
4
  activerecord:
5
- 'code/sample_class':
5
+ codes/sample_class:
6
6
  values:
7
7
  gender_code:
8
8
  f: female
@@ -1,13 +1,67 @@
1
1
  # ------------------------------------------------------
2
2
  # Defined the respective AR Models
3
3
  # ------------------------------------------------------
4
- module Code
4
+ module Codes
5
+
5
6
  class SampleClass < ActiveRecord::Base
6
- self.table_name = :code_sample_class
7
+ self.table_name = :codes_sample_class
7
8
 
8
9
  include CodeBox::CodeAttribute
9
10
 
11
+ # i18n codes
10
12
  code_attribute :gender
11
- code_attribute :country, :lookup => :i18n, :code_attribute_suffix => 'iso'
13
+ code_attribute :country, :lookup_type => :i18n, :code_attribute_suffix => 'iso'
14
+
15
+ # lookup codes
16
+ code_attribute :civil_status, :lookup_type => :lookup, :class_name => 'Codes::CivilStatus'
17
+ code_attribute :ager_type, :lookup_type => :lookup, :foreign_code_attribute => 'code_id'
18
+
19
+ code_attribute :country_2, :lookup_type => :associated, :class_name => 'Codes::Country'
20
+ end
21
+
22
+ class CivilStatus
23
+ include CodeBox::ActsAsCode['single', 'married', :type => :poro]
24
+
25
+ attr_accessor :code
26
+
27
+ def initialize(code)
28
+ @code = code
29
+ end
30
+
31
+ def self.all
32
+ [
33
+ Codes::CivilStatus.new('single'),
34
+ Codes::CivilStatus.new('married'),
35
+ ]
36
+ end
12
37
  end
38
+
39
+ class AgerType
40
+ @@code_cache = {}
41
+ attr_accessor :code_id
42
+
43
+ def initialize(code)
44
+ @code_id = code
45
+ self.class.cache_code(self)
46
+ end
47
+
48
+ def self.cache_code(code_obj)
49
+ @@code_cache[code_obj.code_id] = code_obj
50
+ end
51
+
52
+ def self.lookup(code)
53
+ @@code_cache[code]
54
+ end
55
+ end
56
+
57
+ class Country < ActiveRecord::Base
58
+ self.table_name = :codes_country
59
+ end
60
+
61
+
62
+ class ArCode < ActiveRecord::Base
63
+ include CodeBox::ActsAsCode[:model_type => :active_record]
64
+ self.table_name = :codes_ar_code
65
+ end
66
+
13
67
  end
@@ -3,10 +3,25 @@
3
3
  # ------------------------------------------------------
4
4
  ActiveRecord::Schema.define(:version => 0) do
5
5
 
6
- create_table :code_sample_class, :force => true do |t|
6
+ create_table :codes_sample_class, :force => true do |t|
7
7
  t.string :gender_code
8
8
  t.string :country_iso
9
+
10
+ t.string :civil_status_code
11
+ t.string :ager_type_code
12
+
13
+ t.string :country_2_code
9
14
  end
10
15
 
16
+ create_table :codes_country, :force => true do |t|
17
+ t.string :code
18
+ t.string :name
19
+ end
20
+
21
+ create_table :codes_ar_code, :force => true do |t|
22
+ t.string :code
23
+ t.string :name
24
+ t.integer :position
25
+ end
11
26
 
12
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code-box
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-14 00:00:00.000000000 Z
12
+ date: 2012-06-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord