code-box 0.0.1 → 0.0.2

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/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