stringformat 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/LICENSE +10 -0
- data/lib/stringformat.rb +164 -0
- data/test/tc_stringformat.rb +89 -0
- metadata +48 -0
data/lib/LICENSE
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Copyright (c) 2007 Erik Hollensbe
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
7
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
8
|
+
* Neither the name of the NGSLib nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
9
|
+
|
10
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/lib/stringformat.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
#
|
2
|
+
# StringFormat: A string format library that's programmable.
|
3
|
+
#
|
4
|
+
# Synopsis:
|
5
|
+
#
|
6
|
+
# # up-front interface
|
7
|
+
# f = StringFormat.new(
|
8
|
+
# "d" => proc { |item, num| num + ' ' + item } # %0.2d
|
9
|
+
# "s" => proc { |item, num| item } # %s
|
10
|
+
# )
|
11
|
+
#
|
12
|
+
# s = f.new_string "my_string %s"
|
13
|
+
#
|
14
|
+
# # StringFormat is just a subclass of String, so this works:
|
15
|
+
#
|
16
|
+
# s % "hello there" # => "my_string hello there"
|
17
|
+
#
|
18
|
+
# # something more complex (using base-class String methods):
|
19
|
+
#
|
20
|
+
# s[s.length - 2 .. s.length - 1] % "something new" # => "something new"
|
21
|
+
#
|
22
|
+
# # add iteratively -- works with generated Format#new_string objects as well
|
23
|
+
# f.add_format("f") do |item, num|
|
24
|
+
# num + ' ' + item
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# f.del_format("d")
|
28
|
+
#
|
29
|
+
# # add a format that does not require an argument:
|
30
|
+
# f.add_format("e") do
|
31
|
+
# Date.new.strftime("%s")
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# # and use it like such:
|
35
|
+
#
|
36
|
+
# s = f.clone("string %e %f")
|
37
|
+
# s % [1.2] => "string 12345667787 1.2"
|
38
|
+
# s = f.clone("string %e")
|
39
|
+
# s % nil => "string 12345667787"
|
40
|
+
#
|
41
|
+
# # these also work on the string objects themselves, so you can add on for specific strings:
|
42
|
+
#
|
43
|
+
# s.add_format("s") do |item, num|
|
44
|
+
# "monkeys around"
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# s % "foobar" # => "my_string monkeys around"
|
48
|
+
#
|
49
|
+
# An improperly formatted string (one that is passed too many or too little
|
50
|
+
# arguments for what is expected) will raise a StringFormat::FormatError
|
51
|
+
# exception.
|
52
|
+
#
|
53
|
+
# Author:: Erik Hollensbe <erik@hollensbe.org>
|
54
|
+
#--
|
55
|
+
# TODO LIST
|
56
|
+
#
|
57
|
+
# Spec conflict checking
|
58
|
+
#++
|
59
|
+
|
60
|
+
class StringFormat < String
|
61
|
+
|
62
|
+
VERSION = "0.0.1"
|
63
|
+
|
64
|
+
class FormatError < Exception
|
65
|
+
end
|
66
|
+
|
67
|
+
attr_accessor :format_leader
|
68
|
+
|
69
|
+
# takes a listing of specifications and procs. For a description of how to
|
70
|
+
# define formats, see Format#add_format.
|
71
|
+
def initialize(*args)
|
72
|
+
@format_table = args[0] || { }
|
73
|
+
@format_table.to_hash unless @format_table.respond_to? :keys
|
74
|
+
@format_leader = "%"
|
75
|
+
|
76
|
+
super ""
|
77
|
+
end
|
78
|
+
|
79
|
+
# Factory method for producing new strings. Strings will be of class Format
|
80
|
+
# and contain all the existing Format definitions at the time this is called.
|
81
|
+
#
|
82
|
+
# StringFormat#clone is an alias for this method.
|
83
|
+
def new_string(s="")
|
84
|
+
retval = self.dup
|
85
|
+
retval.replace(s)
|
86
|
+
return retval
|
87
|
+
end
|
88
|
+
|
89
|
+
# Takes a spec and a block to process.
|
90
|
+
#
|
91
|
+
# "Specs" are one character strings that get transformed into a standard
|
92
|
+
# printf()-style format string. The "spec" actually gets used as a
|
93
|
+
# combination of the format_leader (usually "%"), any following
|
94
|
+
# numeric-like values, and the spec. f.e., the spec "s" would match "%s",
|
95
|
+
# or "%03s" or "%1.3s".
|
96
|
+
#
|
97
|
+
# The block (or in the case of Format.new, the proc) takes two explicit
|
98
|
+
# arguments. First, the item that is to be formatted, and second is the
|
99
|
+
# numeric-like portion of the format, to be utilized by the callback during
|
100
|
+
# processing. The return value (last value evaluated) will be used as the
|
101
|
+
# replacement for the format string.
|
102
|
+
#
|
103
|
+
# If the block does not take any arguments, this format does not take any
|
104
|
+
# arguments and it will be skipped in the argument list.
|
105
|
+
#
|
106
|
+
# Please see the top-level documentation for the Format class for examples.
|
107
|
+
#
|
108
|
+
def add_format(spec, &block)
|
109
|
+
@format_table[spec] = block
|
110
|
+
end
|
111
|
+
|
112
|
+
# Removes a format, given a spec.
|
113
|
+
def del_format(spec)
|
114
|
+
@format_table.delete spec
|
115
|
+
end
|
116
|
+
|
117
|
+
# Processes the format and returns a string result. Items passed to this call will
|
118
|
+
# correspond to the formats in order from left to right in the string.
|
119
|
+
#
|
120
|
+
# An improperly formatted string (one that is passed too many or too
|
121
|
+
# little required arguments for what is expected) will raise a
|
122
|
+
# StringFormat::FormatError exception.
|
123
|
+
def format(arg)
|
124
|
+
arg = [arg] unless arg.kind_of? Array
|
125
|
+
|
126
|
+
if arg == [nil]
|
127
|
+
arg = []
|
128
|
+
end
|
129
|
+
|
130
|
+
count = 0
|
131
|
+
|
132
|
+
new_string = self.dup
|
133
|
+
scan(/#{Regexp.quote(@format_leader)}[\d.]*\w/).each do |format|
|
134
|
+
tabled_format = format.sub(/^#{Regexp.quote(@format_leader)}[\d.]*/, '')
|
135
|
+
unless @format_table[tabled_format]
|
136
|
+
raise FormatError, "Invalid format passed in format string"
|
137
|
+
end
|
138
|
+
|
139
|
+
num = nil
|
140
|
+
|
141
|
+
if matches = /([\d.]+)/.match(format)
|
142
|
+
num = matches[1]
|
143
|
+
end
|
144
|
+
|
145
|
+
proc_args = []
|
146
|
+
if @format_table[tabled_format].arity == 2
|
147
|
+
proc_args = [arg[count], num]
|
148
|
+
count += 1
|
149
|
+
end
|
150
|
+
|
151
|
+
new_string.sub!(/#{Regexp.quote(format)}/, @format_table[tabled_format].call(*proc_args).to_s)
|
152
|
+
end
|
153
|
+
|
154
|
+
if arg.length != count
|
155
|
+
raise FormatError, "Not enough formats: #{count} processed when #{arg.length} arguments were attempted"
|
156
|
+
end
|
157
|
+
|
158
|
+
return new_string
|
159
|
+
end
|
160
|
+
|
161
|
+
alias :clone :new_string
|
162
|
+
alias :% :format
|
163
|
+
alias :string= :replace
|
164
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'stringformat'
|
3
|
+
require 'date'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
class StringFormatTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
# test a basic "%s" style stringformat.
|
9
|
+
def test_01_basic_format
|
10
|
+
f = StringFormat.new(
|
11
|
+
"s" => proc do |item, num|
|
12
|
+
if num == num.to_i
|
13
|
+
item[0..num.to_i - 1]
|
14
|
+
elsif num
|
15
|
+
snum = num.to_s
|
16
|
+
left, right = snum.split(/\./).collect { |x| x.to_i }
|
17
|
+
new_string = ""
|
18
|
+
|
19
|
+
if left && left > 0
|
20
|
+
new_string += item[0..left - 1]
|
21
|
+
end
|
22
|
+
|
23
|
+
if right && right < item.length
|
24
|
+
new_string += item[item.length - right..item.length-1]
|
25
|
+
end
|
26
|
+
new_string
|
27
|
+
else
|
28
|
+
item
|
29
|
+
end
|
30
|
+
end
|
31
|
+
)
|
32
|
+
|
33
|
+
s = f.clone("string %s %s")
|
34
|
+
|
35
|
+
assert_equal(s % %w(asdf asdf2), "string asdf asdf2")
|
36
|
+
|
37
|
+
s = f.new_string("string %s %s")
|
38
|
+
|
39
|
+
assert_equal(s % %w(asdf asdf2), "string asdf asdf2")
|
40
|
+
|
41
|
+
# ensure that the formatted strings are copies and not modified references
|
42
|
+
assert_not_equal(s % %w(asdf asdf2), s)
|
43
|
+
|
44
|
+
# ensure that a lack of enough (or too many) formats raises an error
|
45
|
+
assert_raise(StringFormat::FormatError) do
|
46
|
+
s % %w(asdf)
|
47
|
+
end
|
48
|
+
|
49
|
+
assert_raise(StringFormat::FormatError) do
|
50
|
+
s % %w(asdf asdf adsf)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# test if the base class string methods are still working.
|
55
|
+
def test_02_string_methods
|
56
|
+
f = StringFormat.new()
|
57
|
+
s = f.new_string "blah"
|
58
|
+
|
59
|
+
assert_equal(s, s.to_s)
|
60
|
+
assert_equal(s + "foo", "#{s}foo")
|
61
|
+
assert_equal(s.sub(/blah/, 'foo'), "foo")
|
62
|
+
end
|
63
|
+
|
64
|
+
# test more esoteric formats.
|
65
|
+
def test_03_fancy_formats
|
66
|
+
# here's some time formats - test our arity functionality
|
67
|
+
f = StringFormat.new(
|
68
|
+
't' => proc do
|
69
|
+
Time.new.rfc2822
|
70
|
+
end,
|
71
|
+
'e' => proc do
|
72
|
+
Date.new.strftime("%s")
|
73
|
+
end,
|
74
|
+
's' => proc do |item, num|
|
75
|
+
item
|
76
|
+
end
|
77
|
+
)
|
78
|
+
|
79
|
+
assert_nothing_raised do
|
80
|
+
s = f.clone("epoch %e") % nil
|
81
|
+
s = f.clone("epoch %e %s") % ["asdf"]
|
82
|
+
end
|
83
|
+
|
84
|
+
assert_equal(f.clone("epoch %e") % nil, "epoch #{Date.new.strftime('%s')}")
|
85
|
+
assert_equal(f.clone("epoch %e %s") % ["asdf"], "epoch #{Date.new.strftime('%s')} asdf")
|
86
|
+
assert_equal(f.clone("epoch %e %s %e %s") % %w(asdf asdf2), "epoch #{Date.new.strftime('%s')} asdf #{Date.new.strftime('%s')} asdf2")
|
87
|
+
assert_equal(f.clone("rfc2822 %t") % nil, "rfc2822 #{Time.new.rfc2822}")
|
88
|
+
end
|
89
|
+
end
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: stringformat
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2007-09-07 00:00:00 -07:00
|
8
|
+
summary: StringFormat is a string formatter library that's intended to be highly flexible
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: erik@hollensbe.com
|
12
|
+
homepage: http://rubyforge.org/projects/ngslib
|
13
|
+
rubyforge_project: ngslib
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Erik Hollensbe
|
31
|
+
files:
|
32
|
+
- lib/LICENSE
|
33
|
+
- lib/stringformat.rb
|
34
|
+
- test/tc_stringformat.rb
|
35
|
+
test_files: []
|
36
|
+
|
37
|
+
rdoc_options: []
|
38
|
+
|
39
|
+
extra_rdoc_files:
|
40
|
+
- lib/LICENSE
|
41
|
+
executables: []
|
42
|
+
|
43
|
+
extensions: []
|
44
|
+
|
45
|
+
requirements: []
|
46
|
+
|
47
|
+
dependencies: []
|
48
|
+
|