formatting 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0eedbb12f830e0efcd539953a2161c5e56b521fe
4
+ data.tar.gz: 27a038999934b49bce503907e1aec13307729c56
5
+ SHA512:
6
+ metadata.gz: ee6f14dcfc48e9ad749c57e86fb221227bda44312ae1dbc20072bbd58d4afec0e1ec8f22a567ff98671133dfbf6da642b08b6021523f11423067a4fc03356cd9
7
+ data.tar.gz: 1228b163c277ee2d7f85e833fa9c92c975876e56e9a8f4dbb7d1bf395eb62d7d09d88a57376544710141c9e94928f0088b93e1b80f294d331e9ecaf6d4d29b07
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in formatting.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Barsoom AB
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # Formatting
2
+
3
+ Rails-less formatting for your unit-testable code.
4
+
5
+ *Does* currently depend on the `i18n` library for number separators.
6
+
7
+ Very much a work in progress currently.
8
+
9
+ Formats:
10
+ * Numbers
11
+ * Currency
12
+
13
+
14
+ ## Usage
15
+
16
+ Call methods on `Formatting`:
17
+
18
+ ``` ruby
19
+ Formatting.format_number(1234.567) # => "1,234.57"
20
+ ```
21
+
22
+ Or include the modules you want:
23
+
24
+ ``` ruby
25
+ include Formatting::Number
26
+ format_number(1234) # => "1,234"
27
+
28
+ include Formatting::Currency
29
+ format_currency("SEK", 1234) # => "1,234 SEK"
30
+ ```
31
+
32
+
33
+ ### Number
34
+
35
+ ``` ruby
36
+ Formatting.format_number(1234.567) # => "1,234.57"
37
+ Formatting.format_number(0, blank_when_zero: true) # => ""
38
+ Formatting.format_number(1, explicit_sign: true) # => "+1"
39
+ ```
40
+
41
+ ### Currency
42
+
43
+ The currency formatter should usually be passed some object that
44
+ the currency can be determined from. The idea is that even if you
45
+ only have one currency now, you may add more later.
46
+
47
+ ``` ruby
48
+ Formatting.format_currency("SEK", 1234) # => "1,234 SEK"
49
+
50
+ item = Item.new(price: 1234, currency: "SEK")
51
+ Formatting.format_currency(item, :price) # => "1,234 SEK"
52
+ Formatting.format_currency(company, item.price) # => "1,234 SEK"
53
+
54
+ Formatting.defaults[:currency] = "SEK"
55
+ item = Item.new(price: 1234) # Does not respond to "currency"
56
+ Formatting.format_currency(item, :price) # => "1,234 SEK"
57
+
58
+ item = Item.new(price: 1234, currency: "SEK")
59
+ Formatting.format_currency(item, :price, show_currency: false) # => "1,234"
60
+ Formatting.defaults[:show_currency] = false
61
+ Formatting.format_currency(item, :price) # => "1,234"
62
+ ```
63
+
64
+
65
+ ## Installation
66
+
67
+ Add this line to your application's Gemfile:
68
+
69
+ gem 'formatting'
70
+
71
+ And then execute:
72
+
73
+ $ bundle
74
+
75
+ Or install it yourself as:
76
+
77
+ $ gem install formatting
78
+
79
+
80
+ ## TODO
81
+
82
+ * Actually use i18n for separators
83
+ * Don't depend on `i18n`?
84
+ * Rename? This name is boring and also generic enough that collisions seem likely.
85
+ * Document options
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'formatting/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "formatting"
8
+ spec.version = Formatting::VERSION
9
+ spec.authors = ["Henrik Nyh"]
10
+ spec.email = ["henrik@nyh.se"]
11
+ spec.summary = %q{Rails-less formatting for your unit-testable code.}
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "i18n"
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec"
25
+ end
@@ -0,0 +1,35 @@
1
+ module Formatting
2
+ module Currency
3
+ include Number
4
+
5
+ def format_currency(record, amount_or_method, opts = {})
6
+ opts = Formatting.defaults.merge(opts)
7
+
8
+ format_string = opts.fetch(:format, "<amount> <currency>")
9
+
10
+ currency = opts.fetch(:currency) {
11
+ record.respond_to?(:currency) ? record.currency: nil
12
+ }
13
+
14
+ if amount_or_method.is_a?(Symbol)
15
+ amount = record.public_send(amount_or_method)
16
+ else
17
+ amount = amount_or_method
18
+ end
19
+
20
+ amount = format_number(amount, opts)
21
+ apply_format_string(format_string, amount, currency)
22
+ end
23
+
24
+ private
25
+
26
+ def apply_format_string(format_string, amount, currency)
27
+ out = format_string.dup
28
+ out.gsub!("<amount>", amount)
29
+ out.gsub!("<currency>", currency.to_s)
30
+ out.strip!
31
+ out.gsub!(" ", NON_BREAKING_SPACE)
32
+ out
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ module Formatting
2
+ module Number
3
+ def format_number(number, opts = {})
4
+ opts = Formatting.defaults.merge(opts)
5
+
6
+ thousands_separator = opts.fetch(:thousands_separator, NON_BREAKING_SPACE)
7
+ decimal_separator = opts.fetch(:decimal_separator, ".")
8
+ round = opts.fetch(:round, nil)
9
+ min_decimals = opts.fetch(:min_decimals, nil)
10
+ explicit_sign = opts.fetch(:explicit_sign, false)
11
+ blank_when_zero = opts.fetch(:blank_when_zero, false)
12
+
13
+ if blank_when_zero
14
+ return "" if number.zero?
15
+ end
16
+
17
+ # Avoid negative zero.
18
+ number = 0 if number.zero?
19
+
20
+ if round
21
+ number = number.round(round)
22
+ end
23
+
24
+ integer, decimals = number.to_s.split(".")
25
+ decimals ||= "0"
26
+
27
+ integer.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{thousands_separator}")
28
+
29
+ if explicit_sign
30
+ integer = "+#{integer}" if number > 0
31
+ end
32
+
33
+ if min_decimals
34
+ decimals = decimals.ljust(min_decimals, "0")
35
+ end
36
+
37
+ [integer, decimals].join(decimal_separator)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module Formatting
2
+ VERSION = "0.0.1"
3
+ end
data/lib/formatting.rb ADDED
@@ -0,0 +1,16 @@
1
+ require "formatting/version"
2
+ require "formatting/number"
3
+ require "formatting/currency"
4
+
5
+ module Formatting
6
+ NON_BREAKING_SPACE = "\xc2\xa0"
7
+
8
+ class << self
9
+ attr_accessor :defaults
10
+ end
11
+
12
+ self.defaults = {}
13
+
14
+ extend Number
15
+ extend Currency
16
+ end
@@ -0,0 +1,83 @@
1
+ require "spec_helper"
2
+
3
+ describe Formatting::Currency do
4
+ it "can be included as a module" do
5
+ object = Object.new
6
+ object.extend Formatting::Currency
7
+ expect(object).to respond_to(:format_currency)
8
+ end
9
+ end
10
+
11
+ describe Formatting do
12
+ describe ".format_currency" do
13
+ let(:item) { double }
14
+
15
+ context "method signature" do
16
+ it "can take a record and a value" do
17
+ expect_formatted(item, 1).to eq space_to_nbsp("1.0")
18
+ end
19
+
20
+ it "can take a record and a method name" do
21
+ item.stub(price: 2)
22
+ expect_formatted(item, :price).to eq space_to_nbsp("2.0")
23
+ end
24
+ end
25
+
26
+ context "formatting" do
27
+ it "formats numbers" do
28
+ expect_formatted(item, 1234.56).to include space_to_nbsp("1 234.56")
29
+ end
30
+
31
+ it "passes on number formatting options" do
32
+ expect_formatted(item, 1.567, round: 2).to include "1.57"
33
+ end
34
+
35
+ it "applies default options" do
36
+ Formatting.defaults = { currency: "FOO" }
37
+ expect_formatted(item, 1).to eq space_to_nbsp("1.0 FOO")
38
+ end
39
+ end
40
+
41
+ context "currency option" do
42
+ it "is added if provided" do
43
+ expect_formatted(item, 1234.56, currency: "XYZ").to eq space_to_nbsp("1 234.56 XYZ")
44
+ end
45
+
46
+ it "is read from the record's #currency if present" do
47
+ item.stub(currency: "SEK")
48
+ expect_formatted(item, 1).to eq space_to_nbsp("1.0 SEK")
49
+ end
50
+
51
+ it "is not added if the record's #currency is blank" do
52
+ item.stub(currency: "")
53
+ expect_formatted(item, 1).to eq space_to_nbsp("1.0")
54
+ end
55
+
56
+ it "is not added if the record does not respond to #currency" do
57
+ expect_formatted(item, 1).to eq space_to_nbsp("1.0")
58
+ end
59
+ end
60
+
61
+ context "format string option" do
62
+ it "is used if provided" do
63
+ expect_formatted(item, 123, format: "C: <currency> A: <amount>", currency: "XYZ").
64
+ to eq space_to_nbsp("C: XYZ A: 123.0")
65
+ end
66
+
67
+ it "will have spaces turned into non-breaking spaces" do
68
+ expect_actual = expect_formatted(item, 123, format: "<amount> <currency>", currency: "XYZ")
69
+ expect_actual.to eq space_to_nbsp("123.0 XYZ")
70
+ expect_actual.not_to eq "123.0 XYZ"
71
+ end
72
+
73
+ it "is stripped" do
74
+ expect_formatted(item, 123, format: "<amount> <currency>", currency: nil).
75
+ to eq space_to_nbsp("123.0")
76
+ end
77
+ end
78
+ end
79
+
80
+ def expect_formatted(record, value, opts = {})
81
+ expect(Formatting.format_currency(record, value, opts))
82
+ end
83
+ end
@@ -0,0 +1,14 @@
1
+ require "spec_helper"
2
+
3
+ describe Formatting do
4
+ describe "defaults" do
5
+ it "starts out as an empty hash" do
6
+ expect(Formatting.defaults).to eq({})
7
+ end
8
+
9
+ it "can get and set them" do
10
+ Formatting.defaults = { hello: "world" }
11
+ expect(Formatting.defaults).to eq({ hello: "world" })
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,76 @@
1
+ require "spec_helper"
2
+
3
+ describe Formatting::Number do
4
+ it "can be included as a module" do
5
+ object = Object.new
6
+ object.extend Formatting::Number
7
+ expect(object).to respond_to(:format_number)
8
+ end
9
+ end
10
+
11
+ describe Formatting do
12
+ describe ".format_number" do
13
+ it "groups thousands" do
14
+ expect_formatted(1234567.89).to eq space_to_nbsp("1 234 567.89")
15
+ end
16
+
17
+ it "applies default options" do
18
+ Formatting.defaults = { round: 3 }
19
+ expect_formatted(12.3456789).to eq "12.346"
20
+ end
21
+
22
+ context "rounding" do
23
+ it "doesn't round by default" do
24
+ expect_formatted(12.3456789).to eq "12.3456789"
25
+ end
26
+
27
+ it "rounds to the given number of decimals" do
28
+ expect_formatted(12.3456789, round: 2).to eq "12.35"
29
+ end
30
+ end
31
+
32
+ context "blanking when zero" do
33
+ it "does not happen by default" do
34
+ expect_formatted(0).to eq "0.0"
35
+ end
36
+
37
+ it "can be enforced" do
38
+ expect_formatted(0, blank_when_zero: true).to eq ""
39
+ end
40
+ end
41
+
42
+
43
+ context "minimum number of decimals" do
44
+ it "is not enforced by default" do
45
+ expect_formatted(12).to eq "12.0"
46
+ expect_formatted(12.3).to eq "12.3"
47
+ end
48
+
49
+ it "can be enforced" do
50
+ expect_formatted(12.3, min_decimals: 2).to eq "12.30"
51
+ end
52
+ end
53
+
54
+ context "explicit sign" do
55
+ it "is not included by default" do
56
+ expect_formatted(1).to eq "1.0"
57
+ expect_formatted(0).to eq "0.0"
58
+ expect_formatted(-1).to eq "-1.0"
59
+ end
60
+
61
+ it "never shows 0 as negative" do
62
+ expect_formatted(-0.0).to eq "0.0"
63
+ end
64
+
65
+ it "can be enforced" do
66
+ expect_formatted(1, explicit_sign: true).to eq "+1.0"
67
+ expect_formatted(0, explicit_sign: true).to eq "0.0"
68
+ expect_formatted(-1, explicit_sign: true).to eq "-1.0"
69
+ end
70
+ end
71
+ end
72
+
73
+ def expect_formatted(value, opts = {})
74
+ expect(Formatting.format_number(value, opts))
75
+ end
76
+ end
@@ -0,0 +1,15 @@
1
+ require "formatting"
2
+
3
+ module Helpers
4
+ def space_to_nbsp(value)
5
+ value.gsub(" ", "\xc2\xa0")
6
+ end
7
+ end
8
+
9
+ RSpec.configure do |config|
10
+ config.include Helpers
11
+
12
+ config.before do
13
+ Formatting.defaults = {}
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: formatting
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Henrik Nyh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: i18n
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - henrik@nyh.se
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - formatting.gemspec
82
+ - lib/formatting.rb
83
+ - lib/formatting/currency.rb
84
+ - lib/formatting/number.rb
85
+ - lib/formatting/version.rb
86
+ - spec/currency_spec.rb
87
+ - spec/formatting_spec.rb
88
+ - spec/number_spec.rb
89
+ - spec/spec_helper.rb
90
+ homepage: ''
91
+ licenses:
92
+ - MIT
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.0.3
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Rails-less formatting for your unit-testable code.
114
+ test_files:
115
+ - spec/currency_spec.rb
116
+ - spec/formatting_spec.rb
117
+ - spec/number_spec.rb
118
+ - spec/spec_helper.rb
119
+ has_rdoc: