legacy_enum 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # LegacyEnum
2
+
3
+ Allows your Rails app to interact with C-style integer-backed enumeration db columns using a more Ruby-ish syntax.
4
+
5
+ ```ruby
6
+ class Employee < ActiveRecord::Base
7
+ legacy_enum :payroll_type, lookup: :payroll_type_id do |e|
8
+ e.salaried 1
9
+ e.full_time 2
10
+ e.part_time 3
11
+ end
12
+ end
13
+
14
+ # You can use a symbol syntax for more readable code.
15
+ employee = Employee.new
16
+ employee.payroll_type = :salaried
17
+
18
+ # The database still sees the column as an integer
19
+ p employee.payroll_type_id
20
+ # >> 1
21
+
22
+ # Labels are provided for humanized display of your enum values
23
+ p employee.payroll_type_label
24
+ # >> Salaried
25
+
26
+ # You can still address the column as a integer field (if you have to)
27
+ employee.payroll_type_id = 3
28
+ p employee.payroll_type
29
+ # >> :part_time
30
+ ```
31
+
32
+ ## Requires
33
+
34
+ * Rails >= 3.0
35
+ * Ruby >= 1.9.2
36
+
37
+ ## Why?
38
+
39
+ Lots of legacy apps written in C/C++/C#/Java have integer columns in the database that represent enumerated values in the system.
40
+
41
+ For example, in a legacy system dealing with employees, an employee might be classified with a C-style enumeration like:
42
+
43
+ ```c
44
+ enum PayrollType { Salaried = 1, FullTime, PartTime }
45
+ // stored in a db column named "PayrollTypeID" as its integer value
46
+ ```
47
+
48
+ This could be accessed using legacy_enum like so:
49
+
50
+ ```ruby
51
+ legacy_enum :payroll_type do |e|
52
+ e.salaried 1
53
+ e.full_time 2
54
+ e.part_time 3
55
+ end
56
+ ```
57
+
58
+ ## Usage
59
+
60
+ ```ruby
61
+ legacy_enum [rails_friendly_name], options do |e|
62
+ e.enumerated_name value, [options]
63
+ ...
64
+ end
65
+ ```
66
+
67
+ ## Options
68
+
69
+ #### Lookups
70
+
71
+ Conventionally, legacy_enum assumes that the backing int column is named the same as your field name, capitalized and postfixed with "ID". If that isn't the case, use the "lookup" option.
72
+
73
+ ```ruby
74
+ legacy_enum :payroll_type, lookup: :unconventional_id_column_name do |e|
75
+ ...
76
+ end
77
+ ```
78
+
79
+ #### Values
80
+
81
+ Legacy_enum requires that a backing value be provided for each enumerated name. A value can be an integer or a string.
82
+
83
+ ```ruby
84
+ legacy_enum :int_values do |e|
85
+ e.some_value 32
86
+ e.another_value 64
87
+ end
88
+
89
+ legacy_enum :string_values do |e|
90
+ e.a_string_value_is_ok 'zip_zop_zoobity_bop'
91
+ e.another_string 'here_i_go_down_the_slope'
92
+ end
93
+ ```
94
+
95
+ #### Labels
96
+
97
+ Labels are automatically created and conventionally have the name [legacy_enum_field]_label. For instance, this definition would have a label named 'foo_label'
98
+
99
+ ```ruby
100
+ legacy_enum :foo do |e|
101
+ e.some_value 1
102
+ end
103
+ ```
104
+
105
+ Each enumerated name, by default, has a label that is just the ActiveSupport#Titleized version of the enum name. This can be overridden using the 'label' option for each value.
106
+
107
+ ```ruby
108
+ legacy_enum :foo do |e|
109
+ e.crazy_label 1, label: 'Roflcopter'
110
+ end
111
+
112
+ foo = :crazy_label
113
+ p foo_label
114
+ # >> 'Roflcopter'
115
+ ```
116
+
117
+ #### Scopes
118
+
119
+ ActiveRecord scopes can be created for your enumerated field, although by default they are not. The 'scope' option supports two values, :many and :one. 'Many' creates a scope with the enumeration name that accepts symbol values for the scope.
120
+
121
+ ```ruby
122
+ class Employee < ActiveRecord::Base
123
+ legacy_enum :payroll_type, lookup: :payroll_type_id, scope: :many do |e|
124
+ e.salaried 1
125
+ e.full_time 2
126
+ e.part_time 3
127
+ end
128
+ end
129
+
130
+ # The following scopes are created
131
+ Employee.payroll_type(:salaried)
132
+ # 'SELECT * FROM employees WHERE payroll_type_id = 1'
133
+ Employee.salaried
134
+ # 'SELECT * FROM employees WHERE payroll_type_id = 1'
135
+ Employee.full_time
136
+ # 'SELECT * FROM employees WHERE payroll_type_id = 2'
137
+ Employee.part_time
138
+ # 'SELECT * FROM employees WHERE payroll_type_id = 3'
139
+ ```
140
+
141
+ ## Who?
142
+
143
+ legacy_enum was written by [Sean Scally](http://github.com/anydiem) for [AutoRevo](http://www.autorevo.com) with code contributed by [Matt Shannon](http://github.com/dmshann0n).
144
+
145
+ legacy_enum was inspired by jeffp's [enumerated_attribute](http://github.com/jeffp/enumerated_attribute).
146
+
147
+ ## License
148
+
149
+ Released under the MIT license:
150
+
151
+ * http://www.opensource.org/licenses/MIT
@@ -1,84 +1,79 @@
1
1
  module LegacyEnum
2
2
  module ClassMethods
3
3
  def legacy_enum(name, *options, &block)
4
- values_name = "@@#{name}_values".downcase
5
- options = options.extract_options!
6
- id_attr_name = options[:lookup].try(:to_s) || "#{name.to_s.capitalize}ID"
4
+ extracted_options = options.extract_options!
5
+ id_attr_name = extracted_options[:lookup].try(:to_s) || "#{name.to_s.capitalize}ID"
7
6
 
8
7
  config = MethodDefinitionDSL.new
9
8
  config.instance_eval(&block)
10
9
 
11
10
  cattr_accessor :enum_config unless defined? self.enum_config
12
11
  self.enum_config ||= {}
13
- self.enum_config[name] = config.enum_def
14
12
 
15
- #TODO make more lightweight by conditionally adding these methods on first use via method_missing
13
+ self.enum_config[name] = { values: config.enum_def, lookup: id_attr_name }
14
+
16
15
  class_eval do
17
- if options[:scope]
18
- scope name.to_sym, lambda { |enum_val|
19
- { :conditions => { id_attr_name.to_sym => enums[name.to_sym][enum_val] } }
20
- }
21
- self.enums[name].keys.each do |value|
22
- if options[:scope] == :one
23
- (class << self; self; end).instance_eval do
24
- define_method value.to_sym, lambda {
25
- send(name.to_sym, value.to_sym).first
26
- }
27
- end
28
- else
29
- (class << self; self; end).instance_eval do
30
- define_method value.to_sym, lambda {
31
- send(name.to_sym, value.to_sym)
32
- }
33
- end
34
- end
35
- end
16
+
17
+ define_method name do
18
+ enum_config[name][:values].valued(legacy_value(name))[:name]
36
19
  end
37
20
 
38
- find_enum_entry = Proc.new do |_self, name, sym|
39
- enum_entry = _self.enum_config[name].find do |hash|
40
- attr_value = _self.send(id_attr_name.to_sym)
41
- attr_value.to_s.upcase == hash[:value].to_s.upcase
42
- end
21
+ define_method "#{name}=" do |value|
22
+ set_value = enum_config[name][:values].named(value)[:value]
23
+ set_legacy_value name, set_value
24
+ end
43
25
 
44
- enum_entry[sym] unless enum_entry.blank?
26
+ define_method "#{name}_label" do
27
+ enum_config[name][:values].valued(legacy_value(name))[:label]
45
28
  end
46
29
 
47
- define_method name do
48
- find_enum_entry.call(self, name, :name)
30
+ def legacy_value(name)
31
+ send enum_config[name][:lookup].to_sym
49
32
  end
50
33
 
51
- define_method "#{name}=" do |value|
52
- self.send("#{id_attr_name}=".to_sym, nil) if value.blank?
53
- enum_entry = self.enum_config[name].find { |hash| hash[:name] == (value.blank? ? value : value.to_sym) }
54
- unless enum_entry.blank?
55
- self.send("#{id_attr_name}=".to_sym, enum_entry[:value])
56
- end
34
+ def set_legacy_value(name, value)
35
+ send "#{enum_config[name][:lookup]}=".to_sym, value
57
36
  end
58
37
 
59
- define_method "#{name}_label" do
60
- find_enum_entry.call(self, name, :label)
38
+ return unless extracted_options[:scope]
39
+
40
+ scope name.to_sym,
41
+ lambda { |enum_value| where(id_attr_name => enum_config[name][:values].named(enum_value)[:value] ) }
42
+
43
+ enum_config[name][:values].each do |config|
44
+ singleton_class.instance_eval do
45
+ if extracted_options[:scope] == :one
46
+ define_method config[:name].to_sym, lambda { send(name, config[:name]).first }
47
+ else
48
+ define_method config[:name].to_sym, lambda { send(name, config[:name]) }
49
+ end
50
+ end
61
51
  end
52
+
62
53
  end
54
+
63
55
  end
64
56
 
57
+ # Returns all enumerations for the class by enum_name and then name => value
65
58
  def enums
66
59
  inject_block(:name, :value)
67
60
  end
68
61
 
62
+ # Returns all enumerations for the class by enum_name and then name => label
69
63
  def labels
70
64
  inject_block(:name, :label)
71
65
  end
72
66
 
67
+ # Returns all enumerations for the class by enum_name and then value => name
73
68
  def enum_ids
74
69
  inject_block(:value, :name)
75
70
  end
76
71
 
77
72
  private
78
- #this name sux, refactor it
73
+ # Restructures the enum_config around a key/value pair
79
74
  def inject_block(key, value)
80
75
  self.enum_config.inject({}) do |acc, (name,config)|
81
- acc.merge( { name => config.inject({}) do |inner_acc, enum_item|
76
+ acc.merge( { name => config[:values].inject({}) do |inner_acc, enum_item|
82
77
  inner_acc.merge( { enum_item[key] => enum_item[value] } )
83
78
  end })
84
79
  end
@@ -0,0 +1,21 @@
1
+ module LegacyEnum
2
+ module ConfigurationSearch
3
+ def named(name)
4
+ find { |config| config[:name] == name } || null_definition
5
+ end
6
+
7
+ def valued(value)
8
+ search_value = value.to_s.upcase
9
+ find { |config| config[:value].to_s.upcase === search_value } || null_definition
10
+ end
11
+
12
+ def labelled(label)
13
+ find { |config| config[:label] == label } || null_definition
14
+ end
15
+
16
+ private
17
+ def null_definition
18
+ { name: nil, value: nil, label: nil }
19
+ end
20
+ end
21
+ end
@@ -4,6 +4,7 @@ module LegacyEnum
4
4
 
5
5
  def method_missing(symbol, *args)
6
6
  @enum_def ||= []
7
+ @enum_def.singleton_class.send :include, ConfigurationSearch
7
8
 
8
9
  options = args.extract_options!
9
10
 
@@ -1,3 +1,3 @@
1
1
  module LegacyEnum
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/legacy_enum.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'active_record'
2
2
 
3
+ require 'legacy_enum/configuration_search'
3
4
  require 'legacy_enum/method_definition_dsl'
4
5
  require 'legacy_enum/class_methods'
5
6
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legacy_enum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-08 00:00:00.000000000 Z
12
+ date: 2012-02-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &70179477187020 !ruby/object:Gem::Requirement
16
+ requirement: &70249318379200 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>'
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.0.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70179477187020
24
+ version_requirements: *70249318379200
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: sqlite3
27
- requirement: &70179477186600 !ruby/object:Gem::Requirement
27
+ requirement: &70249318378160 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70179477186600
35
+ version_requirements: *70249318378160
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70179477186140 !ruby/object:Gem::Requirement
38
+ requirement: &70249318376680 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70179477186140
46
+ version_requirements: *70249318376680
47
47
  description: Allows you to address enumerated integer columns as more sane and readable
48
48
  symbols
49
49
  email:
@@ -53,13 +53,14 @@ extensions: []
53
53
  extra_rdoc_files: []
54
54
  files:
55
55
  - lib/legacy_enum/class_methods.rb
56
+ - lib/legacy_enum/configuration_search.rb
56
57
  - lib/legacy_enum/method_definition_dsl.rb
57
58
  - lib/legacy_enum/version.rb
58
59
  - lib/legacy_enum.rb
59
60
  - lib/tasks/legacy_enum_tasks.rake
60
61
  - MIT-LICENSE
61
62
  - Rakefile
62
- - README.rdoc
63
+ - README.md
63
64
  homepage: http://github.com/anydiem/legacy_enum
64
65
  licenses: []
65
66
  post_install_message:
data/README.rdoc DELETED
@@ -1,5 +0,0 @@
1
- = LegacyEnum
2
-
3
- = TODO
4
- - Refactor test cases
5
- - More test coverage