model_formatter 0.0.3
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 +5 -0
- data/.travis.yml +5 -0
- data/CHANGELOG +5 -0
- data/Gemfile +8 -0
- data/MIT-LICENSE +20 -0
- data/README.md +62 -0
- data/Rakefile +23 -0
- data/lib/formatters.rb +8 -0
- data/lib/formatters/format.rb +23 -0
- data/lib/formatters/format_boolean.rb +16 -0
- data/lib/formatters/format_currency.rb +23 -0
- data/lib/formatters/format_decimal.rb +21 -0
- data/lib/formatters/format_integer.rb +21 -0
- data/lib/formatters/format_percent.rb +20 -0
- data/lib/formatters/format_phone.rb +20 -0
- data/lib/model_formatter.rb +222 -0
- data/model_formatter.gemspec +17 -0
- data/test/connection.rb +11 -0
- data/test/fixtures/entry.rb +2 -0
- data/test/fixtures/schema.rb +11 -0
- data/test/model_formatter_test.rb +136 -0
- metadata +77 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2005 Geoffrey Grosenbach
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
## Overview
|
2
|
+
|
3
|
+
The ModelFormatter module allows you to easily handle fields that need to be formatted or stripped
|
4
|
+
of formatting as they are set or retrieved from the database. You can designate one or more of your
|
5
|
+
columns as "formatted columns" like in this example:
|
6
|
+
|
7
|
+
class Widget < ActiveRecord::Base
|
8
|
+
# Set an integer field as a symbol
|
9
|
+
format_column :some_integer, :as => :integer
|
10
|
+
|
11
|
+
# Specify the type as a class
|
12
|
+
format_column :sales_tax, :as => Formatters::FormatCurrency
|
13
|
+
format_column :sales_tax, :as => Formatters::FormatCurrency.new(:precision => 4)
|
14
|
+
|
15
|
+
# Change the prefix of the generated methods and specify type as a symbol
|
16
|
+
format_column :sales_tax, :prefix => 'fmt_', :as => :currency, :options => {:precision => 4}
|
17
|
+
|
18
|
+
# Use specific procedures to convert the data +from+ and +to+ the target
|
19
|
+
format_column :area, :from => Proc.new {|value, options| number_with_delimiter sprintf('%2d', value)},
|
20
|
+
:to => Proc.new {|str, options| str.gsub(/,/, '')}
|
21
|
+
|
22
|
+
# Use a block to define the formatter methods
|
23
|
+
format_column :sales_tax do
|
24
|
+
def from(value, options = {})
|
25
|
+
number_to_currency value
|
26
|
+
end
|
27
|
+
def to(str, options = {})
|
28
|
+
str.gsub(/[\$,]/, '')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
...
|
33
|
+
end
|
34
|
+
|
35
|
+
The methods of this module are automatically included into `ActiveRecord::Base`
|
36
|
+
as class methods, so that you can use them in your models.
|
37
|
+
|
38
|
+
|
39
|
+
## Documentation
|
40
|
+
|
41
|
+
For more detailed documentation, create the rdocs with the following command:
|
42
|
+
|
43
|
+
rake rdoc
|
44
|
+
|
45
|
+
|
46
|
+
## Running Unit Tests
|
47
|
+
|
48
|
+
By default, the ModelFormatter plugin uses the mysql +test+ database, running on +localhost+. To run the tests, enter the following:
|
49
|
+
|
50
|
+
rake
|
51
|
+
|
52
|
+
|
53
|
+
## Bugs & Feedback
|
54
|
+
|
55
|
+
Please file a bug report at http://github.com/bfolkens/model-formatter/issues
|
56
|
+
|
57
|
+
[](http://travis-ci.org/bfolkens/model-formatter)
|
58
|
+
|
59
|
+
## Credits
|
60
|
+
|
61
|
+
Sebastian Kanthak's file_column plugin - used for ideas and best practices.
|
62
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
|
6
|
+
desc 'Default: run unit tests.'
|
7
|
+
task :default => :test
|
8
|
+
|
9
|
+
desc 'Test the model_formatter plugin.'
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
11
|
+
t.libs << 'lib'
|
12
|
+
t.pattern = 'test/**/*_test.rb'
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Generate documentation for the model_formatter plugin.'
|
17
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = 'ModelFormatter'
|
20
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
21
|
+
rdoc.rdoc_files.include('README')
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
23
|
+
end
|
data/lib/formatters.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
module Formatters
|
2
|
+
FORMATTERS_BASE_PATH = File.join(File.dirname(__FILE__), 'formatters') unless defined? FORMATTERS_BASE_PATH
|
3
|
+
|
4
|
+
require File.join(FORMATTERS_BASE_PATH, 'format.rb')
|
5
|
+
Dir[File.join(FORMATTERS_BASE_PATH, 'format_*.rb')].each do |formatter|
|
6
|
+
require formatter
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Formatters
|
2
|
+
class Format
|
3
|
+
def initialize(options = {})
|
4
|
+
end
|
5
|
+
|
6
|
+
def from(value, options = {})
|
7
|
+
raise FormatNotDefinedException
|
8
|
+
end
|
9
|
+
|
10
|
+
def to(str, options = {})
|
11
|
+
raise FormatNotDefinedException
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class FormattingException < Exception
|
16
|
+
end
|
17
|
+
|
18
|
+
class FormatNotDefinedException < Exception
|
19
|
+
end
|
20
|
+
|
21
|
+
class FormatNotFoundException < Exception
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'action_view'
|
3
|
+
|
4
|
+
module Formatters
|
5
|
+
class FormatCurrency < Format
|
6
|
+
include ActionView::Helpers::NumberHelper
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@precision = options[:precision] || 2
|
10
|
+
end
|
11
|
+
|
12
|
+
def from(value, options = {})
|
13
|
+
options = {:precision => @precision}.merge(options)
|
14
|
+
number_to_currency(value.to_f / (10 ** @precision), options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to(str, options = {})
|
18
|
+
return nil if str.nil? or str.empty?
|
19
|
+
val = str.gsub(/[^0-9.\-]/, '').to_f
|
20
|
+
(val * (10 ** @precision)) unless val.nil?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'action_view'
|
3
|
+
|
4
|
+
module Formatters
|
5
|
+
class FormatDecimal < Format
|
6
|
+
include ActionView::Helpers::NumberHelper
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
end
|
10
|
+
|
11
|
+
def from(value, options = {})
|
12
|
+
options = {:precision => 3, :delimiter => ','}.merge(options)
|
13
|
+
number_with_precision value, options
|
14
|
+
end
|
15
|
+
|
16
|
+
def to(str, options = {})
|
17
|
+
return nil if str.nil? or str.empty?
|
18
|
+
str.gsub(/,/, '').to_f
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'action_view'
|
3
|
+
|
4
|
+
module Formatters
|
5
|
+
class FormatInteger < Format
|
6
|
+
include ActionView::Helpers::NumberHelper
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
end
|
10
|
+
|
11
|
+
def from(value, options = {})
|
12
|
+
options = {:delimiter => ','}.merge(options)
|
13
|
+
number_with_delimiter value, options
|
14
|
+
end
|
15
|
+
|
16
|
+
def to(str, options = {})
|
17
|
+
return nil if str.nil? or str.empty?
|
18
|
+
str.gsub(/,/, '').to_i
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'action_view'
|
3
|
+
|
4
|
+
module Formatters
|
5
|
+
class FormatPercent < Format
|
6
|
+
include ActionView::Helpers::NumberHelper
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
end
|
10
|
+
|
11
|
+
def from(value, options = {})
|
12
|
+
number_to_percentage value, options
|
13
|
+
end
|
14
|
+
|
15
|
+
def to(str, options = {})
|
16
|
+
return nil if str.nil? or str.empty?
|
17
|
+
str.gsub(/[^0-9\.\-]/, '').to_f
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'action_view'
|
3
|
+
|
4
|
+
module Formatters
|
5
|
+
class FormatPhone < Format
|
6
|
+
include ActionView::Helpers::NumberHelper
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
end
|
10
|
+
|
11
|
+
def from(value, options = {})
|
12
|
+
number_to_phone value, options
|
13
|
+
end
|
14
|
+
|
15
|
+
def to(str, options = {})
|
16
|
+
return nil if str.nil? or str.empty?
|
17
|
+
str.gsub(/[^0-9]/, '').to_i
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
class Object
|
2
|
+
# The hidden singleton lurks behind everyone
|
3
|
+
def metaclass; class << self; self; end; end
|
4
|
+
def meta_eval &blk; metaclass.instance_eval &blk; end
|
5
|
+
|
6
|
+
# Adds methods to a metaclass
|
7
|
+
def meta_def name, &blk
|
8
|
+
meta_eval { define_method name, &blk }
|
9
|
+
end
|
10
|
+
|
11
|
+
# Defines an instance method within a class
|
12
|
+
def class_def name, &blk
|
13
|
+
class_eval { define_method name, &blk }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ModelFormatter # :nodoc:
|
18
|
+
DEFAULT_FORMAT_PREFIX = 'formatted_'
|
19
|
+
|
20
|
+
def self.append_features(base) # :nodoc:
|
21
|
+
super
|
22
|
+
base.extend(ClassMethods)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.init_options(defaults, model, attr) # :nodoc:
|
26
|
+
options = defaults.dup
|
27
|
+
options[:attr] = attr
|
28
|
+
options[:prefix] ||= DEFAULT_FORMAT_PREFIX
|
29
|
+
options[:formatted_attr] ||= "#{options[:prefix]}#{attr}"
|
30
|
+
|
31
|
+
# If :as is set, then it must be either a formatter Class, formatter Object, Symbol, or String
|
32
|
+
options[:formatter] = formatter_for(options[:as], options[:options]) unless options[:as].nil?
|
33
|
+
|
34
|
+
# Define the formatter from a :block if :block is defined
|
35
|
+
options[:formatter] = define_formatter(attr, &options[:block]) unless options[:block].nil?
|
36
|
+
|
37
|
+
# Define :formatter from a block based on :from and :to if they're both set
|
38
|
+
if !options[:from].nil? and !options[:to].nil?
|
39
|
+
options[:formatter] = Module.new
|
40
|
+
options[:formatter].class.send :define_method, :from, options[:from]
|
41
|
+
options[:formatter].class.send :define_method, :to, options[:to]
|
42
|
+
end
|
43
|
+
|
44
|
+
# If :as is still not defined raise an error
|
45
|
+
raise 'No formatting options have been defined.' if options[:formatter].nil?
|
46
|
+
|
47
|
+
options
|
48
|
+
end
|
49
|
+
|
50
|
+
# Define a formatter like the actual physical classes
|
51
|
+
# this could easily be done with text and an eval...
|
52
|
+
# but this should be faster
|
53
|
+
def self.define_formatter(attr, &formatter) # :nodoc:
|
54
|
+
# The convention is to name these custom formatters
|
55
|
+
# differently than the other formatting classes
|
56
|
+
class_name = "CustomFormat#{attr.to_s.camelize}"
|
57
|
+
|
58
|
+
# Create a class in the same module as the others
|
59
|
+
clazz = Class.new(Formatters::Format)
|
60
|
+
silence_warnings do
|
61
|
+
Formatters.const_set class_name, clazz
|
62
|
+
end
|
63
|
+
|
64
|
+
# Define the class body
|
65
|
+
clazz.class_eval &formatter
|
66
|
+
return clazz.new
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return the formatter for a class, formatter object, symbol, or
|
70
|
+
# string defining the name of a formatter class. If it's a symbol, check
|
71
|
+
# the Formatters module for the class that matches the camelized name
|
72
|
+
# of the symbol with 'Format' prepended. Options hash is passed to the
|
73
|
+
# instantiation of the formatter object.
|
74
|
+
def self.formatter_for(type_name, options = {}) # :nodoc:
|
75
|
+
# If the type_name is an instance of a formatter, just return with it
|
76
|
+
return type_name if type_name.is_a? Formatters::Format
|
77
|
+
|
78
|
+
# If the type_name is a class just assign it to the formatter_class for instantiation later
|
79
|
+
formatter_class = type_name if type_name.is_a? Class
|
80
|
+
|
81
|
+
# Format a symbol or string into a formatter_class
|
82
|
+
if type_name.is_a? Symbol or type_name.is_a? String
|
83
|
+
type_name = type_name.to_s.camelize
|
84
|
+
|
85
|
+
# Construct the class name from the type_name
|
86
|
+
formatter_name = "Format#{type_name}"
|
87
|
+
formatter_class = nil
|
88
|
+
begin
|
89
|
+
formatter_class = Formatters.const_get(formatter_name)
|
90
|
+
rescue NameError => ne
|
91
|
+
# Ignore this, caught below
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Make sure the name of this is found in the Formatters module and that
|
96
|
+
# it's the correct superclass
|
97
|
+
return formatter_class.new(options) unless formatter_class.nil? or
|
98
|
+
formatter_class.superclass != Formatters::Format
|
99
|
+
|
100
|
+
raise Formatters::FormatNotFoundException.new("Cannot find formatter 'Formatters::#{formatter_name}'")
|
101
|
+
end
|
102
|
+
|
103
|
+
require File.dirname(__FILE__) + '/formatters.rb'
|
104
|
+
|
105
|
+
# == Usage
|
106
|
+
# class Widget < ActiveRecord::Base
|
107
|
+
# # Set an integer field as a symbol
|
108
|
+
# format_column :some_integer, :as => :integer
|
109
|
+
#
|
110
|
+
# # Specify the type as a class
|
111
|
+
# format_column :sales_tax, :as => Formatters::FormatCurrency
|
112
|
+
# format_column :sales_tax, :as => Formatters::FormatCurrency.new(:precision => 4)
|
113
|
+
#
|
114
|
+
# # Change the prefix of the generated methods and specify type as a symbol
|
115
|
+
# format_column :sales_tax, :prefix => 'fmt_', :as => :currency, :options => {:precision => 4}
|
116
|
+
#
|
117
|
+
# # Use specific procedures to convert the data +from+ and +to+ the target
|
118
|
+
# format_column :area, :from => Proc.new {|value, options| number_with_delimiter sprintf('%2d', value)},
|
119
|
+
# :to => Proc.new {|str, options| str.gsub(/,/, '')}
|
120
|
+
#
|
121
|
+
# # Use a block to define the formatter methods
|
122
|
+
# format_column :sales_tax do
|
123
|
+
# def from(value, options = {})
|
124
|
+
# number_to_currency value
|
125
|
+
# end
|
126
|
+
# def to(str, options = {})
|
127
|
+
# str.gsub(/[\$,]/, '')
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# ...
|
132
|
+
# end
|
133
|
+
module ClassMethods
|
134
|
+
|
135
|
+
# You can override the default options with +model_formatter+'s +options+ parameter
|
136
|
+
DEFAULT_OPTIONS = {
|
137
|
+
:prefix => nil,
|
138
|
+
:formatted_attr => nil,
|
139
|
+
:as => nil,
|
140
|
+
:from => nil,
|
141
|
+
:to => nil,
|
142
|
+
:options => {}
|
143
|
+
}.freeze
|
144
|
+
|
145
|
+
# After calling "<tt>format_column :sales_tax</tt>" as in the example above, a number of instance methods
|
146
|
+
# will automatically be generated, all prefixed by "formatted_" unless :prefix or :formatter_attr
|
147
|
+
# have been set:
|
148
|
+
#
|
149
|
+
# * <tt>Widget.sales_tax_formatter(value)</tt>: Format the sales tax and return the formatted version
|
150
|
+
# * <tt>Widget.sales_tax_unformatter(str)</tt>: "Un"format sales tax and return the unformatted version
|
151
|
+
# * <tt>Widget#formatted_sales_tax=(value)</tt>: This will set the <tt>sales_tax</tt> of the widget using the value stripped of its formatting.
|
152
|
+
# * <tt>Widget#formatted_sales_tax</tt>: This will return the <tt>sales_tax</tt> of the widget using the formatter specified in the options to +format+.
|
153
|
+
# * <tt>Widget#sales_tax_formatting_options</tt>: Access the options this formatter was created with. Useful for declaring field length and later referencing it in display helpers.
|
154
|
+
#
|
155
|
+
# === Options:
|
156
|
+
# * <tt>:formatted_attr</tt> - The actual name used for the formatted attribute. By default, the formatted attribute
|
157
|
+
# name is composed of the <tt>:prefix</tt>, followed by the name of the attribute.
|
158
|
+
# * <tt>:prefix</tt> - Change the prefix prepended to the formatted columns. By default, formatted columns
|
159
|
+
# are prepended with "formatted_".
|
160
|
+
# * <tt>:as</tt> - Format the column as the specified format. This can be provided in either a String,
|
161
|
+
# a Symbol, or as a Class reference to the formatter. The class should subclass Formatters::Format and define <tt>from(value, options = {})</tt> and <tt>to(str, options = {})</tt>.
|
162
|
+
# * <tt>:from</tt> - Data coming from the attribute retriever method is sent to this +Proc+, then returned as a
|
163
|
+
# manipulated attribute.
|
164
|
+
# * <tt>:to</tt> - Data being sent to the attribute setter method is sent to this +Proc+ first to be manipulated,
|
165
|
+
# and the returned attribute is then sent to the attribute setter.
|
166
|
+
# * <tt>:options</tt> - Passed to the formatter blocks, instantiating formatter classes and/or methods as additional formatting options.
|
167
|
+
def format_column(attr, options={}, &fmt_block)
|
168
|
+
options[:block] = fmt_block if block_given?
|
169
|
+
options = DEFAULT_OPTIONS.merge(options) if options
|
170
|
+
|
171
|
+
# Create the actual options
|
172
|
+
my_options = ModelFormatter::init_options(options,
|
173
|
+
ActiveSupport::Inflector.underscore(self.name).to_s,
|
174
|
+
attr.to_s)
|
175
|
+
|
176
|
+
|
177
|
+
# Create the class methods
|
178
|
+
attr_fmt_options_accessor = "#{my_options[:formatted_attr]}_formatting_options".to_sym
|
179
|
+
attr_formatter_method = "#{my_options[:formatted_attr]}_formatter".to_sym
|
180
|
+
attr_unformatter_method = "#{my_options[:formatted_attr]}_unformatter".to_sym
|
181
|
+
|
182
|
+
metaclass.class_eval do
|
183
|
+
# Create an options accessor
|
184
|
+
define_method attr_fmt_options_accessor do
|
185
|
+
my_options
|
186
|
+
end
|
187
|
+
|
188
|
+
# Define a formatter accessor
|
189
|
+
define_method attr_formatter_method do |value|
|
190
|
+
return value if value.nil?
|
191
|
+
|
192
|
+
from_method = my_options[:formatter].method(:from)
|
193
|
+
from_method.call(value, my_options[:options])
|
194
|
+
end
|
195
|
+
|
196
|
+
# Define an unformatter accessor
|
197
|
+
define_method attr_unformatter_method do |str|
|
198
|
+
to_method = my_options[:formatter].method(:to)
|
199
|
+
to_method.call(str, my_options[:options])
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Define the instance method formatter for attr
|
204
|
+
define_method my_options[:formatted_attr] do |*params|
|
205
|
+
value = self.send(attr, *params)
|
206
|
+
self.class.send attr_formatter_method, value
|
207
|
+
end
|
208
|
+
|
209
|
+
# Define the instance method unformatter for attr
|
210
|
+
define_method my_options[:formatted_attr] + '=' do |str|
|
211
|
+
value = self.class.send(attr_unformatter_method, str)
|
212
|
+
self.send(attr.to_s + '=', value)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def is_formatted?(attr)
|
217
|
+
!public_methods.reject {|method| method !~ /#{attr.to_s}_formatting_options$/}.empty?
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
ActiveRecord::Base.send(:include, ModelFormatter)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "model_formatter"
|
6
|
+
s.version = '0.0.3'
|
7
|
+
s.authors = ["Brad Folkens", "Tyler Rick"]
|
8
|
+
s.email = ["bfolkens@gmail.com", "tyler@tylerrick.com"]
|
9
|
+
s.homepage = "https://github.com/bfolkens/model-formatter"
|
10
|
+
s.summary = %q{Allows you to easily handle fields in Rails / ActiveRecord that need to be formatted or stripped of formatting as they are set or retrieved from the database.}
|
11
|
+
s.description = s.summary
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
end
|
data/test/connection.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
create_table :entries, :force => true do |t|
|
3
|
+
t.column :some_integer, :integer
|
4
|
+
t.column :some_boolean, :boolean
|
5
|
+
t.column :sales_tax, :integer
|
6
|
+
t.column :super_precise_tax, :integer
|
7
|
+
t.column :area, :float
|
8
|
+
t.column :complex_field, :string
|
9
|
+
t.column :phone, :integer
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_record'
|
5
|
+
require 'action_view'
|
6
|
+
|
7
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
8
|
+
$:.unshift File.expand_path(File.dirname(__FILE__))
|
9
|
+
|
10
|
+
require 'connection'
|
11
|
+
require 'model_formatter'
|
12
|
+
require 'fixtures/entry'
|
13
|
+
|
14
|
+
|
15
|
+
class ModelFormatterTest < Test::Unit::TestCase
|
16
|
+
def setup
|
17
|
+
Entry.format_column :some_integer, :as => :integer
|
18
|
+
Entry.format_column :some_boolean, :prefix => 'fmt_', :as => :boolean
|
19
|
+
|
20
|
+
Entry.format_column :sales_tax, :as => :currency, :options => {:precision => 3}
|
21
|
+
Entry.format_column :super_precise_tax, :as => Formatters::FormatCurrency.new(:precision => 6)
|
22
|
+
|
23
|
+
Entry.format_column :area, :from => Proc.new {|value, options| sprintf('%2d', value) + options[:sample_format]},
|
24
|
+
:to => Proc.new {|str, options| str.gsub(/[^0-9]/, '').to_i},
|
25
|
+
:options => {:sample_format => ' sq. ft.'}
|
26
|
+
|
27
|
+
Entry.format_column :complex_field do
|
28
|
+
def from(value, options = {})
|
29
|
+
'bouya ' + value
|
30
|
+
end
|
31
|
+
|
32
|
+
def to(str, options = {})
|
33
|
+
str.gsub(/^bouya /, '')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Entry.format_column :phone, :as => :phone
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_should_define_format_accessors
|
41
|
+
assert Entry.respond_to?('formatted_some_integer_formatter')
|
42
|
+
assert Entry.respond_to?('formatted_some_integer_unformatter')
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_should_define_getters
|
46
|
+
assert Entry.new.respond_to?('formatted_some_integer')
|
47
|
+
assert Entry.new.respond_to?('fmt_some_boolean')
|
48
|
+
assert Entry.new.respond_to?('formatted_sales_tax')
|
49
|
+
assert Entry.new.respond_to?('formatted_super_precise_tax')
|
50
|
+
assert Entry.new.respond_to?('formatted_area')
|
51
|
+
assert Entry.new.respond_to?('formatted_complex_field')
|
52
|
+
assert Entry.new.respond_to?('formatted_phone')
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_should_define_setters
|
56
|
+
assert Entry.new.respond_to?('formatted_some_integer=')
|
57
|
+
assert Entry.new.respond_to?('fmt_some_boolean=')
|
58
|
+
assert Entry.new.respond_to?('formatted_sales_tax=')
|
59
|
+
assert Entry.new.respond_to?('formatted_super_precise_tax=')
|
60
|
+
assert Entry.new.respond_to?('formatted_area=')
|
61
|
+
assert Entry.new.respond_to?('formatted_complex_field=')
|
62
|
+
assert Entry.new.respond_to?('formatted_phone=')
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_should_know_formatters
|
66
|
+
assert !Entry.is_formatted?(:id)
|
67
|
+
|
68
|
+
assert Entry.is_formatted?(:some_integer)
|
69
|
+
assert Entry.is_formatted?(:some_boolean)
|
70
|
+
assert Entry.is_formatted?(:sales_tax)
|
71
|
+
|
72
|
+
assert Entry.is_formatted?('super_precise_tax')
|
73
|
+
assert Entry.is_formatted?('area')
|
74
|
+
assert Entry.is_formatted?('complex_field')
|
75
|
+
assert Entry.is_formatted?('phone')
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_should_respond_to_basic_definition
|
79
|
+
e = Entry.new
|
80
|
+
|
81
|
+
assert_equal '3,123', Entry.formatted_some_integer_formatter(3123)
|
82
|
+
e.some_integer = 3123
|
83
|
+
assert_equal '3,123', e.formatted_some_integer
|
84
|
+
|
85
|
+
assert_equal 3123, Entry.formatted_some_integer_unformatter('3,123')
|
86
|
+
e.formatted_some_integer = '3,123'
|
87
|
+
assert_equal 3123, e.some_integer
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_should_respond_to_proc_definition
|
91
|
+
e = Entry.new
|
92
|
+
|
93
|
+
assert_equal '3123 sq. ft.', Entry.formatted_area_formatter(3123)
|
94
|
+
e.area = 3123
|
95
|
+
assert_equal '3123 sq. ft.', e.formatted_area
|
96
|
+
|
97
|
+
assert_equal 3123, Entry.formatted_area_unformatter('3123 sq. ft.')
|
98
|
+
e.formatted_area = '3123 sq. ft.'
|
99
|
+
assert_equal 3123, e.area
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_should_respond_to_block_definition
|
103
|
+
e = Entry.new
|
104
|
+
|
105
|
+
assert_equal 'bouya test me', Entry.formatted_complex_field_formatter('test me')
|
106
|
+
e.complex_field = 'test me'
|
107
|
+
assert_equal 'bouya test me', e.formatted_complex_field
|
108
|
+
|
109
|
+
assert_equal 'here and bouya there', Entry.formatted_complex_field_unformatter('bouya here and bouya there')
|
110
|
+
e.formatted_complex_field = 'bouya here and bouya there'
|
111
|
+
assert_equal 'here and bouya there', e.complex_field
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_should_pass_options_upon_instantiation
|
115
|
+
e = Entry.new
|
116
|
+
|
117
|
+
# Check format accessors
|
118
|
+
assert_equal '$12,412.292', Entry.formatted_sales_tax_formatter(12412292)
|
119
|
+
assert_equal 12412292, Entry.formatted_sales_tax_unformatter('$12,412.292')
|
120
|
+
|
121
|
+
# Check symbol use
|
122
|
+
e.sales_tax = 12412292
|
123
|
+
assert_equal '$12,412.292', e.formatted_sales_tax
|
124
|
+
|
125
|
+
e.formatted_sales_tax = '$12,412.292'
|
126
|
+
assert_equal 12412292, e.sales_tax
|
127
|
+
|
128
|
+
# Check instance use
|
129
|
+
e.super_precise_tax = 12412292888
|
130
|
+
assert_equal '$12,412.292888', e.formatted_super_precise_tax
|
131
|
+
|
132
|
+
e.formatted_super_precise_tax = '$12,412.292888'
|
133
|
+
assert_equal 12412292888, e.super_precise_tax
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: model_formatter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brad Folkens
|
9
|
+
- Tyler Rick
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-10-10 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
15
|
+
description: Allows you to easily handle fields in Rails / ActiveRecord that need
|
16
|
+
to be formatted or stripped of formatting as they are set or retrieved from the
|
17
|
+
database.
|
18
|
+
email:
|
19
|
+
- bfolkens@gmail.com
|
20
|
+
- tyler@tylerrick.com
|
21
|
+
executables: []
|
22
|
+
extensions: []
|
23
|
+
extra_rdoc_files: []
|
24
|
+
files:
|
25
|
+
- .gitignore
|
26
|
+
- .travis.yml
|
27
|
+
- CHANGELOG
|
28
|
+
- Gemfile
|
29
|
+
- MIT-LICENSE
|
30
|
+
- README.md
|
31
|
+
- Rakefile
|
32
|
+
- lib/formatters.rb
|
33
|
+
- lib/formatters/format.rb
|
34
|
+
- lib/formatters/format_boolean.rb
|
35
|
+
- lib/formatters/format_currency.rb
|
36
|
+
- lib/formatters/format_decimal.rb
|
37
|
+
- lib/formatters/format_integer.rb
|
38
|
+
- lib/formatters/format_percent.rb
|
39
|
+
- lib/formatters/format_phone.rb
|
40
|
+
- lib/model_formatter.rb
|
41
|
+
- model_formatter.gemspec
|
42
|
+
- test/connection.rb
|
43
|
+
- test/fixtures/entry.rb
|
44
|
+
- test/fixtures/schema.rb
|
45
|
+
- test/model_formatter_test.rb
|
46
|
+
homepage: https://github.com/bfolkens/model-formatter
|
47
|
+
licenses: []
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
hash: 126899651837855101
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
hash: 126899651837855101
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.8.23.2
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: Allows you to easily handle fields in Rails / ActiveRecord that need to be
|
76
|
+
formatted or stripped of formatting as they are set or retrieved from the database.
|
77
|
+
test_files: []
|