modalh 1.0.0
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/.document +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +31 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +97 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/lib/h/active_record_extensions.rb +101 -0
- data/lib/h/fields.rb +142 -0
- data/lib/h/h.rb +264 -0
- data/lib/h/helpers.rb +17 -0
- data/lib/h/units.rb +82 -0
- data/lib/modalh.rb +22 -0
- data/modalh.gemspec +67 -0
- data/test/helper.rb +18 -0
- data/test/test_modalh.rb +7 -0
- metadata +146 -0
data/.document
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
git (1.2.5)
|
5
|
+
jeweler (1.8.3)
|
6
|
+
bundler (~> 1.0)
|
7
|
+
git (>= 1.2.5)
|
8
|
+
rake
|
9
|
+
rdoc
|
10
|
+
json (1.6.6)
|
11
|
+
modalsupport (0.8.3)
|
12
|
+
rake (0.9.2.2)
|
13
|
+
rdoc (3.12)
|
14
|
+
json (~> 1.4)
|
15
|
+
shoulda (3.0.1)
|
16
|
+
shoulda-context (~> 1.0.0)
|
17
|
+
shoulda-matchers (~> 1.0.0)
|
18
|
+
shoulda-context (1.0.0)
|
19
|
+
shoulda-matchers (1.0.0)
|
20
|
+
units-system (0.2.1)
|
21
|
+
modalsupport
|
22
|
+
|
23
|
+
PLATFORMS
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
bundler (~> 1)
|
28
|
+
jeweler (~> 1.8.3)
|
29
|
+
rdoc (~> 3.12)
|
30
|
+
shoulda
|
31
|
+
units-system
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Javier Goizueta
|
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.rdoc
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
= ModalH
|
2
|
+
|
3
|
+
This is a plugin for localization & delocalization (parsing localized text representation) of data values (numbers, dates, etc.).
|
4
|
+
It also handles values with units (referred to as 'measures')
|
5
|
+
|
6
|
+
It lives inside the H (for human) namespace; the localization operation is referred to as H.to or to_h
|
7
|
+
(conversion of a value to a localized text representation for human consumption)
|
8
|
+
and delocalization (parsing) is referred to as H.from or from_h (conversion of a localized text for humans to a data value).
|
9
|
+
|
10
|
+
It can be integrated with ModalFields[https://github.com/jgoizueta/modalfields] to simplify the declaration of attributes
|
11
|
+
to be localized/delocalized.
|
12
|
+
|
13
|
+
Code for date parsing has been _borrowed_ from delocalize[https://github.com/clemens/delocalize].
|
14
|
+
|
15
|
+
== Localization/delocalization of general data values
|
16
|
+
|
17
|
+
In views and controllers the method @to_h@ can be used to localize a value for human consumption
|
18
|
+
and @from_h@ to parse human input into a value: (note that @I18n.locale@ will be use as a default)
|
19
|
+
|
20
|
+
puts to_h(3.4, :locale=>:es, :precision=>3) # => 3,400
|
21
|
+
puts from_h('3,400', :locale=>:es, :type=>Float) # => 3.4
|
22
|
+
|
23
|
+
There are also more type-specific methods @number_to_h@, @number_from_h@, @date_to_h@, @date_from_h@, etc.
|
24
|
+
|
25
|
+
In models, or any other place outside views or controller the forms @H.to@ (or @H.to_h@) and @H.from@ (or @H.from_h@)
|
26
|
+
are available with the same functionality:
|
27
|
+
|
28
|
+
puts H.to(3.4, :locale=>:es, :precision=>3) # => 3,400
|
29
|
+
puts H.from('3,400', :locale=>:es, :type=>Float) # => 3.4
|
30
|
+
|
31
|
+
Again, type-specific versions exist @H.number_to@, @H.number_from@, etc.
|
32
|
+
|
33
|
+
Data values can be formatted with units with the @magnitude@ variants:
|
34
|
+
|
35
|
+
puts H.magnitude_to(3.233, :units=>'km/h', :precision=>2) # => "3,23 km/h"
|
36
|
+
|
37
|
+
User input with optional units can be parsed and converted to de desired units:
|
38
|
+
|
39
|
+
puts H.magnitude_from('20 m/s', :units=>'km/h') # => 72.0
|
40
|
+
|
41
|
+
Inconsistent units will raise an error.
|
42
|
+
|
43
|
+
== Localization/delocalization of Model attributes
|
44
|
+
|
45
|
+
An attribute @attr@ can be declared to be localized in its model class like so:
|
46
|
+
|
47
|
+
number_h :attr, :precision=>3
|
48
|
+
|
49
|
+
This can also be handled automatically with ModalFields (see the h/fields.rb documentation).
|
50
|
+
|
51
|
+
When an attributes is declared to be localized, a localized representation of it is available as @attr_h@:
|
52
|
+
|
53
|
+
puts record.attr_h
|
54
|
+
|
55
|
+
For example, in a form we could have:
|
56
|
+
|
57
|
+
f.text_field :attr_h
|
58
|
+
|
59
|
+
To assign localized text to an attribute @attr_h=@ can be used:
|
60
|
+
|
61
|
+
record.attr_h = params[:model][:attr_h]
|
62
|
+
|
63
|
+
So, forms will work as usual by simply using the @_h@ variant of the fields that need localization.
|
64
|
+
|
65
|
+
When using ModalFields, the desired precision for a localized numeric attribute, if different from that of the database field,
|
66
|
+
can be specified with the @:h_precision@ parameter:
|
67
|
+
|
68
|
+
attr :decimal, :precision=>3, :h_precision=>2
|
69
|
+
|
70
|
+
An attribute can be declared to have units in its model using this syntax:
|
71
|
+
|
72
|
+
units_h :attr, :precision=>3, :units=>'mm'
|
73
|
+
|
74
|
+
With ModalFields and using the provided automatic units detection, an attribute simply has to be named with the units name
|
75
|
+
as a suffix (separated by an underscore):
|
76
|
+
|
77
|
+
attr_mm :decimal, :precision=>3
|
78
|
+
|
79
|
+
In this case, to prevent an attribute with such suffix from having units, a parameter @:units=>nil@ can be passed.
|
80
|
+
|
81
|
+
The localized form of an attribute with units, @attr_h@ will include its units as when using @H.magnitude_to@.
|
82
|
+
|
83
|
+
A localized attribute with units, when assigned through its @attr_h=@ form will accept units in the localized text
|
84
|
+
and will perform the proper conversion. Inconsistent units will produce a nil value for the attribute and will
|
85
|
+
fail to validate.
|
86
|
+
|
87
|
+
== TODO
|
88
|
+
|
89
|
+
* Tests
|
90
|
+
* Document translations used
|
91
|
+
* New attribute types: bank account number, credit card number, NIF, money, ...
|
92
|
+
|
93
|
+
== Copyright
|
94
|
+
|
95
|
+
Copyright (c) 2012 Javier Goizueta. See LICENSE.txt for
|
96
|
+
further details.
|
97
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "modalh"
|
18
|
+
gem.homepage = "http://github.com/jgoizueta/modalh"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Rails plugin for localization & delocalization}
|
21
|
+
gem.description = %Q{Rails plugin for localization & delocalization of data values}
|
22
|
+
gem.email = "jgoizueta@gmail.com"
|
23
|
+
gem.authors = ["Javier Goizueta"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :test
|
36
|
+
|
37
|
+
require 'rdoc/task'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "modalh #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module H
|
2
|
+
|
3
|
+
# Allow declaration of H conversion for Model attributes like this:
|
4
|
+
#
|
5
|
+
# date_h :start
|
6
|
+
# number_h :speed, :precision=>3
|
7
|
+
#
|
8
|
+
# The first argumet to each declaration is the attribute name; the rest of parameters are passed to the H conversion methods
|
9
|
+
module ActiveRecordExtensions
|
10
|
+
|
11
|
+
# Generic H field declarator
|
12
|
+
def _h(prefix, attr, *options)
|
13
|
+
|
14
|
+
instance_variable_set :"@#{attr}_h_options", options
|
15
|
+
|
16
|
+
class_eval do
|
17
|
+
|
18
|
+
validates_each attr do |record, attr_name, value|
|
19
|
+
record.errors.add attr_name.to_s if record.send(:"\#{attr_name}_h_invalid")
|
20
|
+
end
|
21
|
+
|
22
|
+
# attr=(v)
|
23
|
+
define_method :"#{attr}=" do |v|
|
24
|
+
write_attribute attr, v
|
25
|
+
instance_variable_set "@#{attr}_h", nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# attr_h
|
29
|
+
define_method :"#{attr}_h" do
|
30
|
+
# don't cache it, because the locale may change
|
31
|
+
# unless instance_variable_defined? "@#{attr}_h"
|
32
|
+
instance_variable_set "@#{attr}_h", H.send(:"#{prefix}_to", send(attr), *self.class.instance_variable_get(:"@#{attr}_h_options"))
|
33
|
+
# end
|
34
|
+
instance_variable_get "@#{attr}_h"
|
35
|
+
end
|
36
|
+
|
37
|
+
# attr_h=(txt)
|
38
|
+
define_method :"#{attr}_h=" do |txt|
|
39
|
+
instance_variable_set "@#{attr}_h", txt
|
40
|
+
instance_variable_set "@#{attr}_h_invalid", false
|
41
|
+
write_attribute attr, nil
|
42
|
+
unless txt.blank?
|
43
|
+
begin
|
44
|
+
v = H.send(:"#{prefix}_from", txt, *self.class.instance_variable_get(:"@#{attr}_h_options"))
|
45
|
+
rescue
|
46
|
+
v = nil
|
47
|
+
end
|
48
|
+
instance_variable_set "@#{attr}_h_invalid", true if v.nil?
|
49
|
+
end
|
50
|
+
send :"#{attr}=", v
|
51
|
+
end
|
52
|
+
|
53
|
+
# attr_h? (returns true if it is valid and not blank)
|
54
|
+
define_method :"#{attr}_h?" do
|
55
|
+
!instance_variable_get("@#{attr}_h_invalid") && send(:"#{attr}_h")
|
56
|
+
end
|
57
|
+
|
58
|
+
# attr_h_invalid?
|
59
|
+
define_method :"#{attr}_h_invalid?" do
|
60
|
+
instance_variable_get "@#{attr}_h_invalid"
|
61
|
+
end
|
62
|
+
|
63
|
+
# attr_h_valid?
|
64
|
+
define_method :"#{attr}_h_valid?" do
|
65
|
+
!instance_variable_get "@#{attr}_h_invalid"
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
[:number, :integer, :date, :logical, :time, :datetime].each do |prefix|
|
73
|
+
define_method :"#{prefix}_h" do |attr, *options|
|
74
|
+
_h prefix, attr, *options
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# TODO: support special suffix form of units, e.g. _m2 for m^2, for attribute names
|
79
|
+
|
80
|
+
def units_h(name, units, options={})
|
81
|
+
norm_units = H::Units.normalize_units(units)
|
82
|
+
raise ArgumentError, "invalid units #{units}" unless norm_units
|
83
|
+
options[:units] = norm_units
|
84
|
+
_h :magnitude, name, options
|
85
|
+
short_name = name.to_s.chomp("_#{units}")
|
86
|
+
class_eval do
|
87
|
+
define_method :"#{short_name}_measure" do
|
88
|
+
# ::Units::Measure[send(name), units.to_s]
|
89
|
+
v = send(name)
|
90
|
+
v && v*::Units.u(norm_units)
|
91
|
+
end
|
92
|
+
define_method :"#{short_name}_measure=" do |v|
|
93
|
+
# Units::Measure[send(name), units.to_s]
|
94
|
+
send :"#{name}=", v && v.in(norm_units)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
data/lib/h/fields.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# :encoding: utf-8
|
2
|
+
|
3
|
+
module H
|
4
|
+
|
5
|
+
# Helpers to use in ModalFields hooks to automatically generate _h declarations
|
6
|
+
# Examples of use
|
7
|
+
#
|
8
|
+
# ModalFields.hook do
|
9
|
+
# decimal do |model, declaration|
|
10
|
+
# H::Fields.declare_numeric_field model, declaration
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# float do |model, declaration|
|
14
|
+
# H::Fields.declare_numeric_field model, declaration
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# integer do |model, declaration|
|
18
|
+
# H::Fields.declare_integer_field model, declaration
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# ModalFields.hook do
|
23
|
+
# all_fields do |model, declaration|
|
24
|
+
# H::Fields.declare_auto_units_field model, declaration
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# ModalFields.hook do
|
29
|
+
# all_fields do |model, declaration|
|
30
|
+
# H::Fields.declare_auto_field_with_units model, declaration
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
module Fields
|
35
|
+
class <<self
|
36
|
+
|
37
|
+
# # For numeric types, a display precision different from the stored precision can be selected with
|
38
|
+
# # the :h_precision attribute:
|
39
|
+
def declare_numeric_field(model, declaration)
|
40
|
+
options = declaration.attributes.slice(:precision)
|
41
|
+
if precision = declaration.attributes[:h_precision]
|
42
|
+
options[:precision] = precision
|
43
|
+
end
|
44
|
+
declaration.remove_attributes! :h_precision
|
45
|
+
model.number_h declaration.name, options
|
46
|
+
end
|
47
|
+
|
48
|
+
# Integers can also be assigned a precision and presented as generic numbers
|
49
|
+
def declare_integer_field(model, declaration)
|
50
|
+
precision = declaration.attributes[:h_precision] || declaration.attributes[:precision] || 0
|
51
|
+
declaration.remove_attributes! :h_precision, :precision
|
52
|
+
if precision==0
|
53
|
+
model.integer_h declaration.name
|
54
|
+
else
|
55
|
+
model.number_h declaration.name, :precision=>precision
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def declare_units_field(model, declaration, suffix_units=nil)
|
60
|
+
options = declaration.attributes.slice(:precision, :units)
|
61
|
+
options[:units] ||= suffix_units
|
62
|
+
if precision = declaration.attributes[:h_precision]
|
63
|
+
options[:precision] = precision
|
64
|
+
end
|
65
|
+
options[:precision] ||= {'m'=>1, 'mm'=>0, 'cm'=>0, 'km'=>3}[options[:units]] # TODO AppSettings
|
66
|
+
declaration.remove_attributes! :h_precision, :units
|
67
|
+
model.units_h declaration.name, options[:units], options
|
68
|
+
end
|
69
|
+
|
70
|
+
def declare_date_field(model, declaration)
|
71
|
+
model.date_h declaration.name
|
72
|
+
end
|
73
|
+
|
74
|
+
def declare_time_field(model, declaration)
|
75
|
+
model.time_h declaration.name
|
76
|
+
end
|
77
|
+
|
78
|
+
def declare_datetime_field(model, declaration)
|
79
|
+
model.datetime_h declaration.name
|
80
|
+
end
|
81
|
+
|
82
|
+
def declare_boolean_field(model, declaration)
|
83
|
+
model.logical_h declaration.name
|
84
|
+
end
|
85
|
+
|
86
|
+
# This is handy to be used in all_fields to make any field with a :units parameter or a valid units suffix a units_h field
|
87
|
+
# (and make other numberic fields _h too); If a field with a suffix corresponding to valid units should not be a measure,
|
88
|
+
# a :units=>nil parameter should be added.
|
89
|
+
def declare_auto_units_field(model, declaration)
|
90
|
+
if declaration.type==:float || declaration.type==:decimal || declaration.type==:integer
|
91
|
+
units = declaration.attributes[:units]
|
92
|
+
unless declaration.attributes.has_key?(:units)
|
93
|
+
units = declaration.name.to_s.split('_').last
|
94
|
+
end
|
95
|
+
if units && H::Units.valid?(units)
|
96
|
+
declare_units_field model, declaration, units
|
97
|
+
else
|
98
|
+
raise ArgumentError, "Invalid units #{declaration.attributes[:units]} in declaration of #{model.name}" if declaration.attributes[:units]
|
99
|
+
if declaration.type==:integer
|
100
|
+
declare_integer_field model, declaration
|
101
|
+
else
|
102
|
+
declare_numeric_field model, declaration
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def declare_auto_field_with_units(model, declaration)
|
109
|
+
case declaration.type
|
110
|
+
when :date
|
111
|
+
declare_date_field model, declaration
|
112
|
+
when :time
|
113
|
+
declare_time_field model, declaration
|
114
|
+
when :datetime
|
115
|
+
declare_datetime_field model, declaration
|
116
|
+
when :boolean
|
117
|
+
declare_boolean_field model, declaration
|
118
|
+
else
|
119
|
+
declare_auto_units_field model, declaration
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def declare_auto_field_without_units(model, declaration)
|
124
|
+
case declaration.type
|
125
|
+
when :date
|
126
|
+
declare_date_field model, declaration
|
127
|
+
when :time
|
128
|
+
declare_time_field model, declaration
|
129
|
+
when :datetime
|
130
|
+
declare_datetime_field model, declaration
|
131
|
+
when :boolean
|
132
|
+
declare_boolean_field model, declaration
|
133
|
+
when :integer
|
134
|
+
declare_integer_field model, declaration
|
135
|
+
when :float, :decimal
|
136
|
+
declare_numeric_field model, declaration
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
data/lib/h/h.rb
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
# :encoding: utf-8
|
2
|
+
|
3
|
+
# Localized formatting for Human-iteraction
|
4
|
+
module H
|
5
|
+
|
6
|
+
class <<self
|
7
|
+
# localized conversions
|
8
|
+
|
9
|
+
# Produce data from human-localized text (from user interface)
|
10
|
+
def from(txt, options={})
|
11
|
+
type = options[:type] || Float
|
12
|
+
type = Float if type==:number
|
13
|
+
type = check_type(type)
|
14
|
+
if type.ancestors.include?(Numeric)
|
15
|
+
number_from(txt, options)
|
16
|
+
elsif type.respond_to?(:strftime)
|
17
|
+
date_from(txt, options)
|
18
|
+
elsif type==:logical || type==:boolean
|
19
|
+
logical_from(txt, options)
|
20
|
+
else
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Generate human-localized text (for user interface) from data
|
26
|
+
def to(value, options={})
|
27
|
+
case value
|
28
|
+
when Numeric
|
29
|
+
number_to(value, options)
|
30
|
+
when Time, Date, DateTime
|
31
|
+
date_to(value, options)
|
32
|
+
when TrueClass, FalseClass
|
33
|
+
logical_to(value, options)
|
34
|
+
else
|
35
|
+
options[:blank] || ''
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def number_to(value, options={})
|
40
|
+
options = I18n.translate(:'number.format', :locale => options[:locale]).except(:precision).merge(options)
|
41
|
+
precision = options[:precision]
|
42
|
+
|
43
|
+
return options[:blank] || '' if value.nil?
|
44
|
+
unless value.kind_of?(String)
|
45
|
+
value = round(value,precision)
|
46
|
+
if value.try.nan?
|
47
|
+
return options[:nan] || "--"
|
48
|
+
elsif value.try.infinite?
|
49
|
+
inf = options[:inf] || '∞'
|
50
|
+
return value<0 ? "-#{inf}" : inf
|
51
|
+
else
|
52
|
+
value = value.to_i if precision==0
|
53
|
+
value = value.to_s
|
54
|
+
value = value[0...-2] if value.ends_with?('.0')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
if options[:delimiter]
|
58
|
+
txt = value.to_s.tr(' ','').tr('.,',options[:separator]+options[:delimiter]).tr(options[:delimiter],'')
|
59
|
+
else
|
60
|
+
txt = value.to_s.tr(' ,','').tr('.',options[:separator])
|
61
|
+
end
|
62
|
+
raise ArgumentError, "Invalid number #{txt}" unless /\A[+-]?\d+(?:#{Regexp.escape(options[:separator])}\d*)?(?:[eE][+-]?\d+)?\Z/.match(txt)
|
63
|
+
if precision && precision>0
|
64
|
+
p = txt.index(options[:separator])
|
65
|
+
if p.nil?
|
66
|
+
txt << options[:separator]
|
67
|
+
p = txt.size - 1
|
68
|
+
end
|
69
|
+
p += 1
|
70
|
+
txt << "0"*(precision-txt.size+p) if txt.size-p < precision
|
71
|
+
end
|
72
|
+
digit_grouping txt, 3, options[:delimiter], txt.index(/\d/), txt.index(options[:separator]) || txt.size
|
73
|
+
end
|
74
|
+
|
75
|
+
def number_from(txt, options={})
|
76
|
+
options = I18n.translate(:'number.format', :locale => options[:locale]).except(:precision).merge(options)
|
77
|
+
type = check_type(options[:type] || (options[:precision]==0 ? Integer : Float))
|
78
|
+
|
79
|
+
return nil if txt.to_s.strip.empty? || txt==options[:blank]
|
80
|
+
|
81
|
+
if options[:delimiter]
|
82
|
+
txt = txt.tr(' ','').tr(options[:delimiter]+options[:separator], ',.').tr(',','')
|
83
|
+
else
|
84
|
+
txt = txt.tr(' ','').tr(options[:separator], '.')
|
85
|
+
end
|
86
|
+
raise ArgumentError, "Invalid number #{txt}" unless /\A[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?\Z/.match(txt)
|
87
|
+
if type==Float
|
88
|
+
txt.to_f
|
89
|
+
elsif type==Integer
|
90
|
+
txt.to_i
|
91
|
+
else
|
92
|
+
type.new txt
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def integer_to(value, options={})
|
97
|
+
options = I18n.translate(:'number.format', :locale => options[:locale]).merge(options)
|
98
|
+
if value.nil?
|
99
|
+
options[:blank] || ''
|
100
|
+
else
|
101
|
+
value = value.to_s
|
102
|
+
digit_grouping value, 3, options[:delimiter], value.index(/\d/), value.size
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def integer_from(txt)
|
107
|
+
if txt.to_s.strip.empty? || txt==options[:blank]
|
108
|
+
nil
|
109
|
+
else
|
110
|
+
txt = txt.tr(' ','')
|
111
|
+
txt = txt.tr(options[:delimiter],'') if options[:delimiter]
|
112
|
+
txt = txt.tr(options[:separator],'.')
|
113
|
+
end
|
114
|
+
raise ArgumentError, "Invalid integer #{txt}" unless /\A[+-]?\d+(?:\.0*)?\Z/.match(txt)
|
115
|
+
txt.to_i
|
116
|
+
end
|
117
|
+
|
118
|
+
def date_to(value, options={})
|
119
|
+
I18n.l(value, options)
|
120
|
+
end
|
121
|
+
|
122
|
+
def date_from(txt, options={})
|
123
|
+
options = I18n.translate(:'number.format', :locale => options[:locale]).merge(options)
|
124
|
+
type = check_type(options[:type] || Date)
|
125
|
+
|
126
|
+
return nil if txt.to_s.strip.empty? || txt==options[:blank]
|
127
|
+
return txt if txt.respond_to?(:strftime)
|
128
|
+
|
129
|
+
translate_month_and_day_names! txt, options[:locale]
|
130
|
+
input_formats(type).each do |original_format|
|
131
|
+
next unless txt =~ /^#{apply_regex(original_format)}$/
|
132
|
+
|
133
|
+
txt = DateTime.strptime(txt, original_format)
|
134
|
+
return Date == type ?
|
135
|
+
txt.to_date :
|
136
|
+
Time.zone.local(txt.year, txt.mon, txt.mday, txt.hour, txt.min, txt.sec)
|
137
|
+
end
|
138
|
+
default_parse(txt, type)
|
139
|
+
end
|
140
|
+
|
141
|
+
def time_to(value, options={})
|
142
|
+
date_to value, options.reverse_merge(:type=>Time)
|
143
|
+
end
|
144
|
+
|
145
|
+
def time_from(txt, options={})
|
146
|
+
date_from value, options.reverse_merge(:type=>Time)
|
147
|
+
end
|
148
|
+
|
149
|
+
def datetime_to(value, options={})
|
150
|
+
date_to value, options.reverse_merge(:type=>DateTime)
|
151
|
+
end
|
152
|
+
|
153
|
+
def datetime_from(txt, options={})
|
154
|
+
date_from value, options.reverse_merge(:type=>DateTime)
|
155
|
+
end
|
156
|
+
|
157
|
+
def logical_to(value, options={})
|
158
|
+
options = I18n.translate(:'logical.format', :locale => options[:locale]).merge(options)
|
159
|
+
value.nil? ? options[:blank] : (value ? options[:true] : options[:false])
|
160
|
+
end
|
161
|
+
|
162
|
+
def logical_from(txt, options={})
|
163
|
+
options = I18n.translate(:'logical.format', :locale => options[:locale]).merge(options)
|
164
|
+
txt = normalize_txt(txt)
|
165
|
+
trues = options[:trues]
|
166
|
+
trues ||= [normalize_txt(options[:true])]
|
167
|
+
falses = options[:falses]
|
168
|
+
falses ||= [normalize_txt(options[:falses])]
|
169
|
+
trues.include?(txt) ? true : falses.include?(txt) ? false : nil
|
170
|
+
end
|
171
|
+
|
172
|
+
# TODO: currency, money, bank accounts, credit card numbers, ...
|
173
|
+
|
174
|
+
private
|
175
|
+
# include ActionView::Helpers::NumberHelper
|
176
|
+
|
177
|
+
def round(v, ndec)
|
178
|
+
return v if v.try.nan? || v.try.infinite?
|
179
|
+
if ndec
|
180
|
+
case v
|
181
|
+
when BigDecimal
|
182
|
+
v = v.round(ndec)
|
183
|
+
when Float
|
184
|
+
k = 10**ndec
|
185
|
+
v = (k*v).round.to_f/k
|
186
|
+
end
|
187
|
+
end
|
188
|
+
v
|
189
|
+
end
|
190
|
+
|
191
|
+
# pos0 first digit, pos1 one past last integral digit
|
192
|
+
def digit_grouping(txt,n,sep,pos0,pos1)
|
193
|
+
if sep
|
194
|
+
while pos1>pos0
|
195
|
+
pos1 -= n
|
196
|
+
txt.insert pos1, sep if pos1>pos0
|
197
|
+
end
|
198
|
+
end
|
199
|
+
txt
|
200
|
+
end
|
201
|
+
|
202
|
+
# Date-parsing has been taken from https://github.com/clemens/delocalize
|
203
|
+
|
204
|
+
REGEXPS = {
|
205
|
+
'%B' => "(#{Date::MONTHNAMES.compact.join('|')})", # long month name
|
206
|
+
'%b' => "(#{Date::ABBR_MONTHNAMES.compact.join('|')})", # short month name
|
207
|
+
'%m' => "(\\d{1,2})", # numeric month
|
208
|
+
'%A' => "(#{Date::DAYNAMES.join('|')})", # full day name
|
209
|
+
'%a' => "(#{Date::ABBR_DAYNAMES.join('|')})", # short day name
|
210
|
+
'%Y' => "(\\d{4})", # long year
|
211
|
+
'%y' => "(\\d{2})", # short year
|
212
|
+
'%e' => "(\\s\\d|\\d{2})", # short day
|
213
|
+
'%d' => "(\\d{1,2})", # full day
|
214
|
+
'%H' => "(\\d{2})", # hour (24)
|
215
|
+
'%M' => "(\\d{2})", # minute
|
216
|
+
'%S' => "(\\d{2})" # second
|
217
|
+
}
|
218
|
+
|
219
|
+
def default_parse(datetime, type)
|
220
|
+
return if datetime.blank?
|
221
|
+
begin
|
222
|
+
today = Date.current
|
223
|
+
parsed = Date._parse(datetime)
|
224
|
+
return if parsed.empty? # the datetime value is invalid
|
225
|
+
# set default year, month and day if not found
|
226
|
+
parsed.reverse_merge!(:year => today.year, :mon => today.mon, :mday => today.mday)
|
227
|
+
datetime = Time.zone.local(*parsed.values_at(:year, :mon, :mday, :hour, :min, :sec))
|
228
|
+
Date == type ? datetime.to_date : datetime
|
229
|
+
rescue
|
230
|
+
datetime
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def translate_month_and_day_names!(datetime, locale=nil)
|
235
|
+
translated = I18n.t([:month_names, :abbr_month_names, :day_names, :abbr_day_names], :scope => :date, :locale=>locale).flatten.compact
|
236
|
+
original = (Date::MONTHNAMES + Date::ABBR_MONTHNAMES + Date::DAYNAMES + Date::ABBR_DAYNAMES).compact
|
237
|
+
translated.each_with_index { |name, i| datetime.gsub!(name, original[i]) }
|
238
|
+
end
|
239
|
+
|
240
|
+
def input_formats(type, locale=nil)
|
241
|
+
# Date uses date formats, all others use time formats
|
242
|
+
type = type == Date ? :date : :time
|
243
|
+
I18n.t(:"#{type}.formats", :locale=>locale).slice(*I18n.t(:"#{type}.input.formats", :locale=>locale)).values
|
244
|
+
end
|
245
|
+
|
246
|
+
def apply_regex(format)
|
247
|
+
format.gsub(/(#{REGEXPS.keys.join('|')})/) { |s| REGEXPS[$1] }
|
248
|
+
end
|
249
|
+
|
250
|
+
def normalize_txt(txt)
|
251
|
+
txt.mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/n,'').downcase.strip.to_s
|
252
|
+
end
|
253
|
+
|
254
|
+
def check_type(type)
|
255
|
+
orig_type = type
|
256
|
+
type = type.to_s.camelcase.safe_constantize if type.kind_of?(Symbol)
|
257
|
+
raise ArgumentError, "Invalid type #{orig_type}" unless type && type.class==Class
|
258
|
+
type
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
data/lib/h/helpers.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module H
|
2
|
+
|
3
|
+
# Define methods to_h for H.to, from_h from H.from, xxx_to_h from H.xxx_to, xxx_from_h from H.xxx_from
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
(%w{date number integer logical magnitude}<<nil).each do |type|
|
7
|
+
%w{from to}.each do |kind|
|
8
|
+
name = [type,kind].compact*'_'
|
9
|
+
define_method "#{name}_h" do |*args|
|
10
|
+
H.send name, *args
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/lib/h/units.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# :encoding: utf-8
|
2
|
+
|
3
|
+
module H
|
4
|
+
|
5
|
+
module Units
|
6
|
+
|
7
|
+
UNIT_SYN = {
|
8
|
+
'm2'=>'m**2',
|
9
|
+
'kp/m2'=>'kp/m**2',
|
10
|
+
'"'=>:inch,
|
11
|
+
'\''=>:ft,
|
12
|
+
"''"=>:inch,
|
13
|
+
'in'=>:inch
|
14
|
+
}
|
15
|
+
|
16
|
+
class <<self
|
17
|
+
|
18
|
+
def valid?(u)
|
19
|
+
u = normalize_units(u)
|
20
|
+
u && ::Units.u(u) rescue nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# Convert a units expression to a Ruby expression valid for units-syste
|
24
|
+
def normalize_units(u)
|
25
|
+
if u.blank?
|
26
|
+
u = nil
|
27
|
+
else
|
28
|
+
u = u.to_s
|
29
|
+
u = UNIT_SYN[u] || u
|
30
|
+
u = u.to_s.gsub('^','**').tr(' ','*')
|
31
|
+
begin
|
32
|
+
::Units.u(u)
|
33
|
+
rescue
|
34
|
+
u = nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
u
|
38
|
+
end
|
39
|
+
|
40
|
+
# Convert a units expression to the format to be presented to the user
|
41
|
+
def denormalize_units(u)
|
42
|
+
if u.blank?
|
43
|
+
u = nil
|
44
|
+
else
|
45
|
+
u = u.to_s.gsub('**','^').tr('*',' ')
|
46
|
+
end
|
47
|
+
u
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
class <<self
|
55
|
+
|
56
|
+
def magnitude_to(v, options={})
|
57
|
+
return options[:blank] || '' if v.nil?
|
58
|
+
norm_units = options[:units]
|
59
|
+
txt = number_to(v, options)
|
60
|
+
txt << " #{H::Units.denormalize_units(norm_units)}"
|
61
|
+
txt
|
62
|
+
end
|
63
|
+
|
64
|
+
def magnitude_from(txt, options={})
|
65
|
+
return nil if txt.to_s.strip.empty? || txt==options[:blank]
|
66
|
+
norm_units = options[:units]
|
67
|
+
if txt.match(/^\s*([0-9\.,+-]+)\s*([a-zA-Z\"\'][a-zA-Z1-3\_\/\*\^\"\']*)\s*$/)
|
68
|
+
txt = $1
|
69
|
+
from_units = $2 || norm_units
|
70
|
+
else
|
71
|
+
from_units = norm_units
|
72
|
+
end
|
73
|
+
from_units = H::Units.normalize_units(from_units)
|
74
|
+
raise ArgumentError, "Invalid units for #{norm_units}: #{from_units}}" unless from_units
|
75
|
+
v = number_from(txt, options)
|
76
|
+
v *= ::Units.u(from_units)
|
77
|
+
v.in(norm_units)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
data/lib/modalh.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'h/h'
|
2
|
+
require 'h/units'
|
3
|
+
require 'h/helpers'
|
4
|
+
require 'h/active_record_extensions'
|
5
|
+
require 'h/fields'
|
6
|
+
|
7
|
+
# Define xxxx_h field declarators for Model classes
|
8
|
+
if defined?(ActiveRecord::Base)
|
9
|
+
ActiveRecord::Base.extend H::ActiveRecordExtensions
|
10
|
+
end
|
11
|
+
|
12
|
+
# Add controller & view xxx_to_h/xxx_from_h helpers
|
13
|
+
if defined?(ActionController::Base)
|
14
|
+
class ActionController::Base
|
15
|
+
include H::Helpers
|
16
|
+
end
|
17
|
+
ActionController::Base.helper_method *H::Helpers.instance_methods
|
18
|
+
end
|
19
|
+
|
20
|
+
# Make same helpers accessible with the H prefix from elsewhere
|
21
|
+
# (not really needed; can use H.to/H.from instead)
|
22
|
+
H.extend H::Helpers
|
data/modalh.gemspec
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "modalh"
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Javier Goizueta"]
|
12
|
+
s.date = "2012-04-21"
|
13
|
+
s.description = "Rails plugin for localization & delocalization of data values"
|
14
|
+
s.email = "jgoizueta@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"lib/h/active_record_extensions.rb",
|
28
|
+
"lib/h/fields.rb",
|
29
|
+
"lib/h/h.rb",
|
30
|
+
"lib/h/helpers.rb",
|
31
|
+
"lib/h/units.rb",
|
32
|
+
"lib/modalh.rb",
|
33
|
+
"modalh.gemspec",
|
34
|
+
"test/helper.rb",
|
35
|
+
"test/test_modalh.rb"
|
36
|
+
]
|
37
|
+
s.homepage = "http://github.com/jgoizueta/modalh"
|
38
|
+
s.licenses = ["MIT"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = "1.8.21"
|
41
|
+
s.summary = "Rails plugin for localization & delocalization"
|
42
|
+
|
43
|
+
if s.respond_to? :specification_version then
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_runtime_dependency(%q<units-system>, [">= 0"])
|
48
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
50
|
+
s.add_development_dependency(%q<bundler>, ["~> 1"])
|
51
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<units-system>, [">= 0"])
|
54
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
55
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
56
|
+
s.add_dependency(%q<bundler>, ["~> 1"])
|
57
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
58
|
+
end
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<units-system>, [">= 0"])
|
61
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
62
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
63
|
+
s.add_dependency(%q<bundler>, ["~> 1"])
|
64
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'modalh'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
data/test/test_modalh.rb
ADDED
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: modalh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Javier Goizueta
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: units-system
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: shoulda
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rdoc
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.12'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.12'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: jeweler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.8.3
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.8.3
|
94
|
+
description: Rails plugin for localization & delocalization of data values
|
95
|
+
email: jgoizueta@gmail.com
|
96
|
+
executables: []
|
97
|
+
extensions: []
|
98
|
+
extra_rdoc_files:
|
99
|
+
- LICENSE.txt
|
100
|
+
- README.rdoc
|
101
|
+
files:
|
102
|
+
- .document
|
103
|
+
- Gemfile
|
104
|
+
- Gemfile.lock
|
105
|
+
- LICENSE.txt
|
106
|
+
- README.rdoc
|
107
|
+
- Rakefile
|
108
|
+
- VERSION
|
109
|
+
- lib/h/active_record_extensions.rb
|
110
|
+
- lib/h/fields.rb
|
111
|
+
- lib/h/h.rb
|
112
|
+
- lib/h/helpers.rb
|
113
|
+
- lib/h/units.rb
|
114
|
+
- lib/modalh.rb
|
115
|
+
- modalh.gemspec
|
116
|
+
- test/helper.rb
|
117
|
+
- test/test_modalh.rb
|
118
|
+
homepage: http://github.com/jgoizueta/modalh
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
127
|
+
requirements:
|
128
|
+
- - ! '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
segments:
|
132
|
+
- 0
|
133
|
+
hash: -2675616586541666340
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
none: false
|
136
|
+
requirements:
|
137
|
+
- - ! '>='
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
requirements: []
|
141
|
+
rubyforge_project:
|
142
|
+
rubygems_version: 1.8.21
|
143
|
+
signing_key:
|
144
|
+
specification_version: 3
|
145
|
+
summary: Rails plugin for localization & delocalization
|
146
|
+
test_files: []
|