model_formatter 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://secure.travis-ci.org/bfolkens/model-formatter.png)](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: []
|