format_engine 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ module FormatEngine
2
+
3
+ #A little package of info about the engine's progress.
4
+ #In the context of a formatting / parsing block, the
5
+ #"self" of that block is an instance of SpecInfo.
6
+ #<br>
7
+ #<br>The components of that instance Struct are:
8
+ #<br>
9
+ #<br>When Formatting:
10
+ #* src - The object that is the source of the data.
11
+ #* dst - A string that receives the formatted output.
12
+ #* fmt - The format specification currently being processed.
13
+ #* engine - The formatting engine. Mostly for access to the library.
14
+ #* hsh - A utility hash so that the formatting process can retain state.
15
+ #<br>Methods
16
+ #* cat - Append the string that follows to the formatted output.
17
+ #<br>
18
+ #<br>When Parsing:
19
+ #* src - A string that is the source of formatted input.
20
+ #* dst - The class of the object being created.
21
+ #* fmt - The parse specification currently being processed.
22
+ #* engine - The parsing engine. Mostly for access to the library.
23
+ #* hsh - A utility hash so that the parsing process can retain state.
24
+ #<br>Methods
25
+ #* set - Set the return value of the parsing operation to the value that follows.
26
+ #* parse - Look for the string or regex parm that follows. Return the data found or nil.
27
+ #* parse! - Like parse but raises an exception (with optional msg) if not found.
28
+ #* found? - Did the last parse succeed?
29
+ #* found - The text found by the last parse (or parse!) operation.
30
+ class SpecInfo
31
+
32
+ # General readers
33
+ attr_reader :src, :dst, :engine, :hsh
34
+
35
+ #General accessors
36
+ attr_accessor :fmt
37
+
38
+ # Set up the spec info.
39
+ def initialize(src, dst, fmt, engine, hsh = {})
40
+ @src, @dst, @fmt, @engine, @hsh = src, dst, fmt, engine, hsh
41
+ end
42
+
43
+ # Concatenate onto the formatted output string.
44
+ def cat(str)
45
+ @dst << str
46
+ end
47
+
48
+ # Set the result of this parsing operation.
49
+ def set(obj)
50
+ @dst = obj
51
+ end
52
+
53
+ # Parse the source string for a string or regex
54
+ def parse(tgt)
55
+ @result = src.partition(tgt)
56
+
57
+ if found?
58
+ @src = @result[2]
59
+ @result[1]
60
+ else
61
+ nil
62
+ end
63
+ end
64
+
65
+ # Parse the source string for a string or regex or raise error.
66
+ def parse!(tgt, msg = "#{tgt.inspect} not found")
67
+ fail "Parse error: #{msg}" unless parse(tgt)
68
+ found
69
+ end
70
+
71
+ # Was the last parse a success?
72
+ def found?
73
+ @result[0].empty? && !@result[1].empty?
74
+ end
75
+
76
+ # What was found by the last parse?
77
+ def found
78
+ @result[1]
79
+ end
80
+
81
+ end
82
+
83
+ end
@@ -0,0 +1,5 @@
1
+
2
+ module FormatEngine
3
+ # The version of the format_engine gem.
4
+ VERSION = "0.0.1"
5
+ end
@@ -0,0 +1,11 @@
1
+ #The format_engine gem top source file.
2
+
3
+ require 'English'
4
+
5
+ require 'format_engine/format_spec'
6
+ require 'format_engine/engine'
7
+ require 'format_engine/spec_info'
8
+ require 'format_engine/attr_formatter'
9
+ require 'format_engine/attr_parser'
10
+
11
+ require 'format_engine/version'
data/mocks/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # FormatEngine - The mocks folder.
2
+
3
+ This is a new idea that is being evaluated. In order to keep code DRY and
4
+ encourage proper care and maintenance of mocks and stubs, this folder
5
+ will contain those mocks and stubs. The idea is that by making them into
6
+ separate entities in their own right, they just might get a little more
7
+ respect.
8
+
9
+ In addition, it may encourage the sharing and increased scrutiny of these
10
+ testing tools and thus improve productivity and quality.
@@ -0,0 +1,13 @@
1
+ #* mocks/demo/demo_formatting.rb - Formatting specifications for the \Customer class.
2
+ class Customer
3
+
4
+ extend FormatEngine::AttrFormatter
5
+
6
+ ##
7
+ #The specification of the formatter method of the demo \Customer class.
8
+
9
+ attr_formatter :strfmt,
10
+ {"%f" => lambda {cat src.first_name.ljust(fmt.width) },
11
+ "%l" => lambda {cat src.last_name.ljust(fmt.width) } }
12
+
13
+ end
@@ -0,0 +1,14 @@
1
+ #* mocks/demo/demo_parse.rb - Parsing specifications for the \Customer class.
2
+ class Customer
3
+
4
+ extend FormatEngine::AttrParser
5
+
6
+ ##
7
+ #The specification of the parser method of the demo \Customer class.
8
+
9
+ attr_parser :strprs,
10
+ {"%f" => lambda { hsh[:fn] = found if parse(/(\w)+/ ) },
11
+ "%l" => lambda { hsh[:ln] = found if parse(/(\w)+/ ) },
12
+ :after => lambda { set dst.new(hsh[:fn], hsh[:ln]) } }
13
+
14
+ end
data/mocks/demo.rb ADDED
@@ -0,0 +1,18 @@
1
+ # A simple class of customer info.
2
+
3
+ require_relative 'demo/demo_format'
4
+ require_relative 'demo/demo_parse'
5
+
6
+ #A demo class for the format_engine gem.
7
+ class Customer
8
+ #Demo customer first name.
9
+ attr_reader :first_name
10
+
11
+ #Demo customer last name
12
+ attr_reader :last_name
13
+
14
+ #Create an instance of the demo customer.
15
+ def initialize(first_name, last_name)
16
+ @first_name, @last_name = first_name, last_name
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+
2
+ # A mock class to test formatting
3
+ class TestPerson
4
+ attr_reader :first_name
5
+ attr_reader :last_name
6
+
7
+ def initialize(first_name, last_name)
8
+ @first_name, @last_name = first_name, last_name
9
+ end
10
+ end
data/reek.txt ADDED
@@ -0,0 +1,2 @@
1
+
2
+ 0 total warnings
@@ -0,0 +1,18 @@
1
+ require_relative '../lib/format_engine'
2
+ gem 'minitest'
3
+ require 'minitest/autorun'
4
+ require 'minitest_visible'
5
+
6
+ class EngineBaseTester < Minitest::Test
7
+
8
+ #Track mini-test progress.
9
+ MinitestVisible.track self, __FILE__
10
+
11
+ def test_that_it_has_a_library
12
+ test = FormatEngine::Engine.new({"%A" => 42})
13
+
14
+ assert_equal(42, test["%A"])
15
+ assert_equal(nil, test["%B"])
16
+ end
17
+
18
+ end
@@ -0,0 +1,29 @@
1
+ require_relative '../lib/format_engine'
2
+ gem 'minitest'
3
+ require 'minitest/autorun'
4
+ require 'minitest_visible'
5
+
6
+ require_relative '../mocks/demo'
7
+
8
+ # A full test of the formatter/parser engine.
9
+ class FormatEngineTester < Minitest::Test
10
+
11
+ #Track mini-test progress.
12
+ MinitestVisible.track self, __FILE__
13
+
14
+ def test_basic_formatting
15
+ cust = Customer.new("Jane", "Doe")
16
+
17
+ assert_equal("Jane, Doe", cust.strfmt("%f, %l"))
18
+ end
19
+
20
+
21
+ def test_basic_parsing
22
+ cust = Customer.strprs("Jane, Doe", "%f, %l")
23
+
24
+ assert_equal(Customer, cust.class)
25
+ assert_equal("Jane", cust.first_name)
26
+ assert_equal("Doe", cust.last_name)
27
+ end
28
+
29
+ end
@@ -0,0 +1,120 @@
1
+ require_relative '../lib/format_engine'
2
+ gem 'minitest'
3
+ require 'minitest/autorun'
4
+ require 'minitest_visible'
5
+
6
+ class FormatSpecTester < Minitest::Test
7
+
8
+ #Track mini-test progress.
9
+ MinitestVisible.track self, __FILE__
10
+
11
+ def test_that_it_scans_literal_formats
12
+ test = FormatEngine::FormatSpec.get_spec "ABCDEFG!"
13
+ assert_equal(Array, test.specs.class)
14
+ assert_equal(1, test.specs.length)
15
+ assert_equal(FormatEngine::FormatLiteral, test.specs[0].class)
16
+ assert_equal("ABCDEFG!", test.specs[0].literal)
17
+ end
18
+
19
+ def test_that_it_scans_simple_variable_formats
20
+ test = FormatEngine::FormatSpec.get_spec "%A"
21
+ assert_equal(Array, test.specs.class)
22
+ assert_equal(1, test.specs.length)
23
+ assert_equal(FormatEngine::FormatVariable, test.specs[0].class)
24
+ assert_equal("%A", test.specs[0].format)
25
+ assert_equal(nil, test.specs[0].parms)
26
+ end
27
+
28
+ def test_that_it_scans_option_variable_formats
29
+ "~@#&^&*-+=?_<>\\/.,|".each_char do |char|
30
+ test = FormatEngine::FormatSpec.get_spec "%#{char}A"
31
+ assert_equal(Array, test.specs.class)
32
+ assert_equal(1, test.specs.length)
33
+ assert_equal(FormatEngine::FormatVariable, test.specs[0].class)
34
+ assert_equal("%#{char}A", test.specs[0].format)
35
+ end
36
+ end
37
+
38
+ def test_that_it_scans_single_variable_formats
39
+ test = FormatEngine::FormatSpec.get_spec "%123A"
40
+ assert_equal(Array, test.specs.class)
41
+ assert_equal(1, test.specs.length)
42
+ assert_equal(FormatEngine::FormatVariable, test.specs[0].class)
43
+ assert_equal("%A", test.specs[0].format)
44
+
45
+ assert_equal(Array, test.specs[0].parms.class)
46
+ assert_equal(1, test.specs[0].parms.length)
47
+ assert_equal("123", test.specs[0].parms[0])
48
+ end
49
+
50
+ def test_that_it_scans_double_variable_formats
51
+ test = FormatEngine::FormatSpec.get_spec "%123.456A"
52
+ assert_equal(Array, test.specs.class)
53
+ assert_equal(1, test.specs.length)
54
+ assert_equal(FormatEngine::FormatVariable, test.specs[0].class)
55
+ assert_equal("%A", test.specs[0].format)
56
+
57
+ assert_equal(Array, test.specs[0].parms.class)
58
+ assert_equal(2, test.specs[0].parms.length)
59
+ assert_equal("123", test.specs[0].parms[0])
60
+ assert_equal("456", test.specs[0].parms[1])
61
+ end
62
+
63
+ def test_multipart_formats
64
+ test = FormatEngine::FormatSpec.get_spec "T(%+02A:%3B:%4.1C)"
65
+
66
+ assert_equal(Array, test.specs.class)
67
+ assert_equal(7, test.specs.length)
68
+
69
+ assert_equal(FormatEngine::FormatLiteral, test.specs[0].class)
70
+ assert_equal("T(", test.specs[0].literal)
71
+
72
+ assert_equal(FormatEngine::FormatVariable, test.specs[1].class)
73
+ assert_equal("%+A", test.specs[1].format)
74
+ assert_equal(1, test.specs[1].parms.length)
75
+ assert_equal("02", test.specs[1].parms[0])
76
+
77
+ assert_equal(FormatEngine::FormatLiteral, test.specs[2].class)
78
+ assert_equal(":", test.specs[2].literal)
79
+
80
+ assert_equal(FormatEngine::FormatVariable, test.specs[3].class)
81
+ assert_equal("%B", test.specs[3].format)
82
+ assert_equal(1, test.specs[3].parms.length)
83
+ assert_equal("3", test.specs[3].parms[0])
84
+
85
+ assert_equal(FormatEngine::FormatLiteral, test.specs[4].class)
86
+ assert_equal(":", test.specs[4].literal)
87
+
88
+ assert_equal(FormatEngine::FormatVariable, test.specs[5].class)
89
+ assert_equal("%C", test.specs[5].format)
90
+ assert_equal(2, test.specs[5].parms.length)
91
+ assert_equal("4", test.specs[5].parms[0])
92
+ assert_equal("1", test.specs[5].parms[1])
93
+
94
+ assert_equal(FormatEngine::FormatLiteral, test.specs[6].class)
95
+ assert_equal(")", test.specs[6].literal)
96
+ end
97
+
98
+ def test_that_it_validates
99
+ engine = { "%A" => true, "%B" => true }
100
+
101
+ test = FormatEngine::FormatSpec.get_spec "%A and %B"
102
+ assert_equal(test, test.validate(engine))
103
+
104
+ test = FormatEngine::FormatSpec.get_spec "%A and %C"
105
+ assert_raises(RuntimeError) { test.validate(engine) }
106
+ end
107
+
108
+ def test_that_it_caches
109
+ t1 = FormatEngine::FormatSpec.get_spec "%123.456A"
110
+ t2 = FormatEngine::FormatSpec.get_spec "%123.456A"
111
+ assert(t1.object_id == t2.object_id)
112
+
113
+ t3 = FormatEngine::FormatSpec.get_spec "%123.457A"
114
+ assert(t1.object_id != t3.object_id)
115
+
116
+ t4 = FormatEngine::FormatSpec.get_spec "%123.457A"
117
+ assert(t4.object_id == t3.object_id)
118
+ end
119
+
120
+ end
@@ -0,0 +1,71 @@
1
+ require_relative '../lib/format_engine'
2
+ gem 'minitest'
3
+ require 'minitest/autorun'
4
+ require 'minitest_visible'
5
+
6
+ require_relative '../mocks/test_person_mock'
7
+
8
+ # Test the internals of the formatter engine. This is not the normal interface.
9
+ class FormatterTester < Minitest::Test
10
+
11
+ #Track mini-test progress.
12
+ MinitestVisible.track self, __FILE__
13
+
14
+ def make_formatter
15
+ FormatEngine::Engine.new(
16
+ "%f" => lambda {cat src.first_name.ljust(fmt.width) },
17
+ "%-f" => lambda {cat src.first_name.rjust(fmt.width) },
18
+ "%F" => lambda {cat src.first_name.upcase.ljust(fmt.width) },
19
+ "%-F" => lambda {cat src.first_name.upcase.rjust(fmt.width) },
20
+ "%l" => lambda {cat src.last_name.ljust(fmt.width)},
21
+ "%-l" => lambda {cat src.last_name.rjust(fmt.width)},
22
+ "%L" => lambda {cat src.last_name.upcase.ljust(fmt.width) },
23
+ "%-L" => lambda {cat src.last_name.upcase.rjust(fmt.width) })
24
+ end
25
+
26
+ def make_person
27
+ TestPerson.new("Squidly", "Jones")
28
+ end
29
+
30
+ def make_spec(str)
31
+ FormatEngine::FormatSpec.get_spec str
32
+ end
33
+
34
+ def make_all(str)
35
+ [make_formatter, make_person, make_spec(str)]
36
+ end
37
+
38
+ def test_that_it_can_format_normally
39
+ engine, obj, spec = make_all("Name = %f %l")
40
+ assert_equal("Name = Squidly Jones", engine.do_format(obj, spec))
41
+ end
42
+
43
+ def test_that_it_can_format_shouting
44
+ engine, obj, spec = make_all("Name = %F %L")
45
+ assert_equal("Name = SQUIDLY JONES", engine.do_format(obj, spec))
46
+ end
47
+
48
+ def test_that_it_can_format_wider
49
+ engine, obj, spec = make_all("Name = %10f %10l")
50
+ assert_equal("Name = Squidly Jones ", engine.do_format(obj, spec))
51
+ end
52
+
53
+ def test_that_it_can_format_right
54
+ engine, obj, spec = make_all("Name = %-10f %-10l")
55
+ assert_equal("Name = Squidly Jones", engine.do_format(obj, spec))
56
+ end
57
+
58
+ def test_that_it_calls_before_and_after
59
+ engine = FormatEngine::Engine.new(
60
+ :before => lambda {dst << "((" },
61
+ :after => lambda {dst << "))" })
62
+
63
+ spec = FormatEngine::FormatSpec.get_spec "Test"
64
+ assert_equal("((Test))", engine.do_format(nil, spec))
65
+ end
66
+
67
+ def test_that_it_rejects_bad_specs
68
+ engine, obj, spec = make_all("Name = %f %j")
69
+ assert_raises(RuntimeError) { engine.do_format(obj, spec) }
70
+ end
71
+ end
@@ -0,0 +1,23 @@
1
+ require_relative '../lib/format_engine'
2
+ gem 'minitest'
3
+ require 'minitest/autorun'
4
+ require 'minitest_visible'
5
+
6
+ class LiteralSpecTester < Minitest::Test
7
+
8
+ #Track mini-test progress.
9
+ MinitestVisible.track self, __FILE__
10
+
11
+ def test_that_it_validates_always
12
+ test = FormatEngine::FormatLiteral.new("Test 1 2 3")
13
+ assert_equal(test, test.validate(nil))
14
+ end
15
+
16
+ def test_that_it_formats
17
+ spec_info = FormatEngine::SpecInfo.new(nil, "", nil, nil, {})
18
+ test = FormatEngine::FormatLiteral.new("Test 1 2 3")
19
+ test.do_format(spec_info)
20
+
21
+ assert_equal("Test 1 2 3", spec_info.dst)
22
+ end
23
+ end
@@ -0,0 +1,95 @@
1
+ require_relative '../lib/format_engine'
2
+ gem 'minitest'
3
+ require 'minitest/autorun'
4
+ require 'minitest_visible'
5
+
6
+ require_relative '../mocks/test_person_mock'
7
+
8
+ # Test the internals of the parser engine. This is not the normal interface.
9
+ class ParserTester < Minitest::Test
10
+
11
+ #Track mini-test progress.
12
+ MinitestVisible.track self, __FILE__
13
+
14
+ def make_parser
15
+ FormatEngine::Engine.new(
16
+ "%f" => lambda { hsh[:fn] = found if parse(/(\w)+/ ) },
17
+ "%F" => lambda { hsh[:fn] = found.upcase if parse(/(\w)+/) },
18
+ "%-F" => lambda { hsh[:fn] = found.capitalize if parse(/(\w)+/) },
19
+ "%l" => lambda { hsh[:ln] = found if parse(/(\w)+/ ) },
20
+ "%L" => lambda { hsh[:ln] = found.upcase if parse(/(\w)+/) },
21
+ "%-L" => lambda { hsh[:ln] = found.capitalize if parse(/(\w)+/) },
22
+ "%s" => lambda { parse(/\s+/) },
23
+ "%,s" => lambda { parse(/[,\s]\s*/) },
24
+ "%t" => lambda { parse("\t") },
25
+ "%!t" => lambda { parse!("\t") },
26
+
27
+ :after => lambda { set TestPerson.new(hsh[:fn], hsh[:ln]) })
28
+ end
29
+
30
+ def test_that_it_can_parse
31
+ engine = make_parser
32
+ spec = FormatEngine::FormatSpec.get_spec "%f, %l"
33
+ result = engine.do_parse("Squidly, Jones", TestPerson, spec)
34
+
35
+ assert_equal(TestPerson, result.class)
36
+ assert_equal("Squidly", result.first_name)
37
+ assert_equal("Jones", result.last_name)
38
+ end
39
+
40
+ def test_that_it_can_parse_loudly
41
+ engine = make_parser
42
+ spec = FormatEngine::FormatSpec.get_spec "%F, %L"
43
+ result = engine.do_parse("Squidly, Jones", TestPerson, spec)
44
+
45
+ assert_equal(TestPerson, result.class)
46
+ assert_equal("SQUIDLY", result.first_name)
47
+ assert_equal("JONES", result.last_name)
48
+ end
49
+
50
+ def test_that_it_can_fix_shouting
51
+ engine = make_parser
52
+ spec = FormatEngine::FormatSpec.get_spec "%-F, %-L"
53
+ result = engine.do_parse("SQUIDLY, JONES", TestPerson, spec)
54
+
55
+ assert_equal(TestPerson, result.class)
56
+ assert_equal("Squidly", result.first_name)
57
+ assert_equal("Jones", result.last_name)
58
+ end
59
+
60
+ def test_that_it_can_flex_parse
61
+ engine = make_parser
62
+ spec = FormatEngine::FormatSpec.get_spec "%f%,s%l"
63
+ result = engine.do_parse("Squidly, Jones", TestPerson, spec)
64
+
65
+ assert_equal(TestPerson, result.class)
66
+ assert_equal("Squidly", result.first_name)
67
+ assert_equal("Jones", result.last_name)
68
+ end
69
+
70
+ def test_that_it_can_tab_parse
71
+ engine = make_parser
72
+ spec = FormatEngine::FormatSpec.get_spec "%f%t%l"
73
+ result = engine.do_parse("Squidly\tJones", TestPerson, spec)
74
+
75
+ assert_equal(TestPerson, result.class)
76
+ assert_equal("Squidly", result.first_name)
77
+ assert_equal("Jones", result.last_name)
78
+ end
79
+
80
+ def test_that_it_can_detect_errors
81
+ engine = make_parser
82
+ spec = FormatEngine::FormatSpec.get_spec "%f, %l"
83
+
84
+ assert_raises(RuntimeError) do
85
+ engine.do_parse("Squidly Jones", TestPerson, spec)
86
+ end
87
+
88
+ spec = FormatEngine::FormatSpec.get_spec "%f%!t%l"
89
+
90
+ assert_raises(RuntimeError) do
91
+ engine.do_parse("Squidly Jones", TestPerson, spec)
92
+ end
93
+ end
94
+
95
+ end
@@ -0,0 +1,34 @@
1
+ require_relative '../lib/format_engine'
2
+ gem 'minitest'
3
+ require 'minitest/autorun'
4
+ require 'minitest_visible'
5
+
6
+ class VariableSpecTester < Minitest::Test
7
+
8
+ #Track mini-test progress.
9
+ MinitestVisible.track self, __FILE__
10
+
11
+ def test_that_it_validates
12
+ engine = { "%A" => true }
13
+
14
+ test = FormatEngine::FormatVariable.new("%A")
15
+ assert_equal(test, test.validate(engine))
16
+
17
+ test = FormatEngine::FormatVariable.new("%B")
18
+ assert_raises(RuntimeError) { test.validate(engine) }
19
+ end
20
+
21
+ def test_the_parms
22
+ test = FormatEngine::FormatVariable.new("%B")
23
+ assert_equal(0, test.width)
24
+ assert_equal(0, test.prec)
25
+
26
+ test = FormatEngine::FormatVariable.new("%10B")
27
+ assert_equal(10, test.width)
28
+ assert_equal(0, test.prec)
29
+
30
+ test = FormatEngine::FormatVariable.new("%10.5B")
31
+ assert_equal(10, test.width)
32
+ assert_equal(5, test.prec)
33
+ end
34
+ end