format_engine 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f68de35267cd2cec34ca42b8638cc5035f64ff42
4
+ data.tar.gz: d0c21b2ba3cd709f75d9e8e036d5848238abbafa
5
+ SHA512:
6
+ metadata.gz: 493f6d819bb9c0ceae8eb2c743c5d99a7d0d3fadc537e69e90e540e06c688e3f303a417f5fd8da9174419a8febac8d65ef21209398cc66c27867ee1de325f212
7
+ data.tar.gz: 25823819e6fc80c713e1f95cfb0fcd25c3b9bf9034cf7eb0755865217a43a77ebbc6ee0a68dc55ceb22225b9ca1bb234b37fb2e6e64d610ce765546a55a0ce00
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ docs/.~lock.*
16
+ *.bat
17
+ *.zip
18
+ *.tmp
19
+ rdoc
20
+ test/tmp
21
+ test/version_tmp
22
+ tmp
23
+ temp.txt
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in format_engine.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Peter Camilleri
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,84 @@
1
+ # FormatEngine
2
+
3
+ The FormatEngine gem contains the common support code needed to support
4
+ string formatting and parsing routines like strftime and strptime.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'format_engine'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install format_engine
21
+
22
+ ## Usage
23
+
24
+ ```ruby
25
+ require 'format_engine'
26
+
27
+ #A demo class for the format_engine gem.
28
+ class Customer
29
+ extend FormatEngine::AttrFormatter
30
+ extend FormatEngine::AttrParser
31
+
32
+ #Demo customer first name.
33
+ attr_reader :first_name
34
+
35
+ #Demo customer last name
36
+ attr_reader :last_name
37
+
38
+ attr_formatter :strfmt,
39
+ {"%f" => lambda {cat src.first_name.ljust(fmt.width) },
40
+ "%l" => lambda {cat src.last_name.ljust(fmt.width) } }
41
+
42
+ attr_parser :strprs,
43
+ {"%f" => lambda { hsh[:fn] = found if parse(/(\w)+/ ) },
44
+ "%l" => lambda { hsh[:ln] = found if parse(/(\w)+/ ) },
45
+ :after => lambda { set dst.new(hsh[:fn], hsh[:ln]) } }
46
+
47
+ #Create an instance of the demo customer.
48
+ def initialize(first_name, last_name)
49
+ @first_name, @last_name = first_name, last_name
50
+ end
51
+ end
52
+
53
+ #Then later in the code...
54
+
55
+ cust = Customer.new("Jane", "Doe")
56
+ puts cust.strfmt("%f, %l")) #"Jane, Doe"
57
+
58
+ #And elsewhere in Gotham City...
59
+
60
+ in_str = "Jane, Doe"
61
+ agent = Customer.strprs(in_str, "%f, %l")
62
+
63
+ #Etc, etc, etc ...
64
+
65
+ ```
66
+
67
+ ## Philosophy
68
+
69
+ When designing this gem, a concerted effort has been applied to keeping it as
70
+ simple as possible. To this end, many subtle programming techniques have been
71
+ avoided in favor of simpler, more obvious approaches. This is based on the
72
+ observation that I don't trust code that I don't understand and I avoid code
73
+ I don't trust.
74
+
75
+ Feedback on the convenience/clarity balance as well as any other topics are
76
+ most welcomed.
77
+
78
+ ## Contributing
79
+
80
+ 1. Fork it ( https://github.com/[my-github-username]/format_engine/fork )
81
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
82
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
83
+ 4. Push to the branch (`git push origin my-new-feature`)
84
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env rake
2
+ # coding: utf-8
3
+
4
+ require 'rake/testtask'
5
+ require 'rdoc/task'
6
+ require 'bundler/gem_tasks'
7
+
8
+ #Generate internal documentation with rdoc.
9
+ RDoc::Task.new do |rdoc|
10
+ rdoc.rdoc_dir = "rdoc"
11
+
12
+ #List out all the files to be documented.
13
+ rdoc.rdoc_files.include("lib/**/*.rb", "mocks/**/demo*.rb", "license.txt", "README.md")
14
+
15
+ #Make all access levels visible.
16
+ rdoc.options << '--visibility' << 'private'
17
+ #rdoc.options << '--verbose'
18
+ #rdoc.options << '--coverage-report'
19
+
20
+ #Set a title.
21
+ rdoc.options << '--title' << 'fOOrth Language Internals'
22
+
23
+ end
24
+
25
+ #Run the format_engine unit test suite.
26
+ Rake::TestTask.new do |t|
27
+ #List out all the test files.
28
+ t.test_files = FileList['tests/**/*.rb']
29
+ t.verbose = false
30
+ end
31
+
32
+ desc "Run a scan for smelly code!"
33
+ task :reek do |t|
34
+ `reek --no-color lib > reek.txt`
35
+ end
36
+
37
+ desc "Run an IRB Session with format_engine loaded."
38
+ task :console do
39
+ require 'irb'
40
+ require 'irb/completion'
41
+ require './lib/format_engine'
42
+ puts "Starting an IRB console with format_engine."
43
+ puts "Use 'quit' to exit."
44
+ puts
45
+ ARGV.clear
46
+ IRB.start
47
+ end
48
+
49
+ desc "What version of code is this?"
50
+ task :vers do |t|
51
+ puts
52
+ puts "format_engine version = #{FormatEngine::VERSION}"
53
+ end
54
+
55
+
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'format_engine/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "format_engine"
8
+ spec.version = FormatEngine::VERSION
9
+ spec.authors = ["Peter Camilleri"]
10
+ spec.email = ["peter.c.camilleri@gmail.com"]
11
+ spec.summary = %q{An engine for string formatting and parsing.}
12
+ spec.description = %q{An engine for string formatting and parsing like the strftime and strptime methods.}
13
+ spec.homepage = "http://teuthida-technologies.com/"
14
+ spec.license = "MIT"
15
+
16
+ raw_list = `git ls-files`.split($/)
17
+ spec.files = raw_list.keep_if {|entry| !entry.start_with?("docs") }
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.7"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency 'minitest', "~> 5.5.1"
25
+ spec.add_development_dependency 'minitest_visible', "~> 0.0.1"
26
+ spec.add_development_dependency 'rdoc', "~> 4.0.1"
27
+ end
@@ -0,0 +1,109 @@
1
+ ---
2
+ Attribute:
3
+ enabled: false
4
+ exclude: []
5
+ BooleanParameter:
6
+ enabled: true
7
+ exclude: []
8
+ ClassVariable:
9
+ enabled: true
10
+ exclude: []
11
+ ControlParameter:
12
+ enabled: true
13
+ exclude: []
14
+ DataClump:
15
+ enabled: true
16
+ exclude: []
17
+ max_copies: 2
18
+ min_clump_size: 2
19
+ DuplicateMethodCall:
20
+ enabled: true
21
+ exclude: []
22
+ max_calls: 1
23
+ allow_calls: []
24
+ FeatureEnvy:
25
+ enabled: true
26
+ exclude: []
27
+ IrresponsibleModule:
28
+ enabled: true
29
+ exclude: []
30
+ LongParameterList:
31
+ enabled: true
32
+ exclude: []
33
+ max_params: 3
34
+ overrides:
35
+ initialize:
36
+ max_params: 5
37
+ LongYieldList:
38
+ enabled: true
39
+ exclude: []
40
+ max_params: 3
41
+ NestedIterators:
42
+ enabled: true
43
+ exclude: []
44
+ max_allowed_nesting: 1
45
+ ignore_iterators: []
46
+ NilCheck:
47
+ enabled: true
48
+ exclude: []
49
+ PrimaDonnaMethod:
50
+ enabled: true
51
+ exclude: []
52
+ RepeatedConditional:
53
+ enabled: true
54
+ exclude: []
55
+ max_ifs: 2
56
+ TooManyInstanceVariables:
57
+ enabled: true
58
+ exclude: []
59
+ max_instance_variables: 9
60
+ TooManyMethods:
61
+ enabled: true
62
+ exclude: []
63
+ max_methods: 25
64
+ TooManyStatements:
65
+ enabled: true
66
+ exclude:
67
+ - initialize
68
+ max_statements: 7
69
+ UncommunicativeMethodName:
70
+ enabled: true
71
+ exclude: []
72
+ reject:
73
+ - !ruby/regexp /^[a-z]$/
74
+ - !ruby/regexp /[0-9]$/
75
+ - !ruby/regexp /[A-Z]/
76
+ accept: []
77
+ UncommunicativeModuleName:
78
+ enabled: true
79
+ exclude: []
80
+ reject:
81
+ - !ruby/regexp /^.$/
82
+ - !ruby/regexp /[0-9]$/
83
+ accept:
84
+ - Inline::C
85
+ UncommunicativeParameterName:
86
+ enabled: true
87
+ exclude: []
88
+ reject:
89
+ - !ruby/regexp /^.$/
90
+ - !ruby/regexp /[0-9]$/
91
+ - !ruby/regexp /[A-Z]/
92
+ - !ruby/regexp /^_/
93
+ accept: []
94
+ UncommunicativeVariableName:
95
+ enabled: true
96
+ exclude: []
97
+ reject:
98
+ - !ruby/regexp /^.$/
99
+ - !ruby/regexp /[0-9]$/
100
+ - !ruby/regexp /[A-Z]/
101
+ accept:
102
+ - _
103
+ UnusedParameters:
104
+ enabled: true
105
+ exclude: []
106
+ UtilityFunction:
107
+ enabled: true
108
+ exclude: []
109
+ max_helper_calls: 1
@@ -0,0 +1,32 @@
1
+
2
+ module FormatEngine
3
+
4
+ # This module adds the \attr_formatter extension to a class.
5
+ module AttrFormatter
6
+
7
+ #Define a formatter for instances of the current class.
8
+ #<br>Parameters
9
+ #* method - A symbol used to name the formatting method created by this method.
10
+ #* library - A hash of formatting rules that define the formatting
11
+ # capabilities supported by this formatter.
12
+ #<br>Meta-effects
13
+ #* Creates a method (named after the symbol in method) that formats the
14
+ # instance of the class. The created method takes one parameter:
15
+ #<br>Meta-method Parameters
16
+ #* spec_str - A format specification string with %x etc qualifiers.
17
+ #<br>Meta-method Returns
18
+ #* A formatted string
19
+ def attr_formatter(method, library)
20
+ engine = Engine.new(library)
21
+
22
+ define_method(method) do |spec_str|
23
+ spec = FormatEngine::FormatSpec.get_spec(spec_str)
24
+
25
+ engine.do_format(self, spec)
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,33 @@
1
+ module FormatEngine
2
+
3
+ # This module adds the \attr_parser extension to a class.
4
+ module AttrParser
5
+
6
+ #Define a parser for the current class.
7
+ #<br>Parameters
8
+ #* method - A symbol used to name the parsing method created by this method.
9
+ #* library - A hash of parsing rules the define the parsing
10
+ # capabilities supported by this parser.
11
+ #<br>Meta-effects
12
+ #* Creates a class method (named after the symbol in method) that parses in
13
+ # a string and creates an instance of the class. The created method takes
14
+ # two parameters:
15
+ #<br>Meta-method Parameters
16
+ #* src - A string of formatted data to be parsed.
17
+ #* spec_str - A format specification string with %x etc qualifiers.
18
+ #<br>Meta-method Returns
19
+ #* An instance of the host class.
20
+ def attr_parser(method, library)
21
+ engine = Engine.new(library)
22
+
23
+ define_singleton_method(method) do |src, spec_str|
24
+ spec = FormatEngine::FormatSpec.get_spec(spec_str)
25
+
26
+ engine.do_parse(src, self, spec)
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,59 @@
1
+ module FormatEngine
2
+
3
+ # The engine class of the format engine.
4
+ class Engine
5
+
6
+ #Set up base data structures.
7
+ def initialize(library)
8
+ @lib = library
9
+
10
+ #Set up defaults for pre and post amble blocks.
11
+ nop = lambda { }
12
+ @lib[:before] ||= nop
13
+ @lib[:after] ||= nop
14
+ end
15
+
16
+ # Get an entry from the library
17
+ def [](index)
18
+ @lib[index]
19
+ end
20
+
21
+ #Do the actual work of building the formatted output.
22
+ #<br>Parameters
23
+ #* src - The source object being formatted.
24
+ #* format_spec - The format specification.
25
+ def do_format(src, format_spec)
26
+ due_process(src, "", format_spec, :do_format)
27
+ end
28
+
29
+ #Do the actual work of parsing the formatted input.
30
+ #<br>Parameters
31
+ #* src - The source string being parsed.
32
+ #* dst - The class of the object being created.
33
+ #* format_spec - The format specification.
34
+ def do_parse(*args)
35
+ due_process(*args, :do_parse)
36
+ end
37
+
38
+ #Do the actual work of parsing the formatted input.
39
+ #<br>Parameters
40
+ #* src - The input to the process.
41
+ #* dst - The output of the process.
42
+ #* format_spec - The format specification.
43
+ #* sym - The symbol applied to each format specification.
44
+ #<br>Endemic Code Smells
45
+ #* :reek:LongParameterList
46
+ def due_process(src, dst, format_spec, sym)
47
+ spec_info = SpecInfo.new(src, dst, nil, self, {})
48
+ spec_info.instance_exec(&self[:before])
49
+
50
+ format_spec.validate(self).specs.each do |fmt|
51
+ fmt.send(sym, spec_info)
52
+ end
53
+
54
+ spec_info.instance_exec(&self[:after])
55
+ spec_info.dst
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,36 @@
1
+ module FormatEngine
2
+
3
+ # A format engine literal specification.
4
+ class FormatLiteral
5
+
6
+ # The literal text of this literal specification.
7
+ attr_reader :literal
8
+
9
+ # Set up a literal format specification.
10
+ def initialize(literal)
11
+ @literal = literal
12
+ end
13
+
14
+ # Is this literal supported by the engine? YES!
15
+ def validate(_engine)
16
+ self
17
+ end
18
+
19
+ # Format onto the output string
20
+ def do_format(spec_info)
21
+ spec_info.dst << @literal
22
+ end
23
+
24
+ # Parse from the input string
25
+ def do_parse(spec_info)
26
+ spec_info.parse!(literal)
27
+ end
28
+
29
+ # Inspect for debugging.
30
+ def inspect
31
+ "Literal(#{literal.inspect})"
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,64 @@
1
+ module FormatEngine
2
+
3
+ # A format engine variable specification.
4
+ class FormatVariable
5
+
6
+ # The fixed part of this variable specification.
7
+ attr_reader :format
8
+
9
+ # The (optional) numeric format parameters.
10
+ attr_reader :parms
11
+
12
+ # Setup a variable format specification.
13
+ def initialize(format)
14
+ if format =~ /(\d+(\.\d+)?)/
15
+ @format = $PREMATCH + $POSTMATCH
16
+
17
+ if (digits = $MATCH) =~ /\./
18
+ @parms = [$PREMATCH, $POSTMATCH]
19
+ else
20
+ @parms = [digits]
21
+ end
22
+
23
+ else
24
+ @parms = nil
25
+ @format = format
26
+ end
27
+ end
28
+
29
+ # Is this variable supported by the engine?
30
+ def validate(engine)
31
+ fail "Unsupported tag = #{format.inspect}" unless engine[format]
32
+ self
33
+ end
34
+
35
+ # Get the width parameter.
36
+ def width
37
+ parms ? parms[0].to_i : 0
38
+ end
39
+
40
+ # Get the precision parameter.
41
+ def prec
42
+ (parms && parms.length > 1) ? parms[1].to_i : 0
43
+ end
44
+
45
+ # Format onto the output string
46
+ def do_format(spec_info)
47
+ spec_info.fmt = self
48
+ spec_info.instance_exec(&spec_info.engine[self.format])
49
+ end
50
+
51
+ # Parse from the input string
52
+ def do_parse(spec_info)
53
+ spec_info.fmt = self
54
+ spec_info.instance_exec(&spec_info.engine[self.format])
55
+ end
56
+
57
+ # Inspect for debugging.
58
+ def inspect
59
+ "Variable(#{format.inspect}, #{parms.inspect})"
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,61 @@
1
+ require_relative 'format_spec/literal'
2
+ require_relative 'format_spec/variable'
3
+
4
+ # Format String Specification Syntax (BNF):
5
+ # spec = { text | item }+
6
+ # item = "%" {flag}* {parm {"." parm}?}? {command}
7
+ # flag = { "~" | "@" | "#" | "&" | "^" |
8
+ # "&" | "*" | "-" | "+" | "=" |
9
+ # "?" | "_" | "<" | ">" | "\\" |
10
+ # "/" | "." | "," | "|" | "!" }
11
+ # parm = { "0" .. "9" }+
12
+ # command = { "a" .. "z" | "A" .. "Z" }
13
+ # Sample: x = FormatSpec.get_spec "Elapsed = %*03.1H:%M:%S!"
14
+
15
+ module FormatEngine
16
+
17
+ #The format string parser.
18
+ class FormatSpec
19
+ #Don't use new, use get_spec instead.
20
+ private_class_method :new
21
+
22
+ #Either get a format specification from the pool or create one.
23
+ def self.get_spec(fmt_string)
24
+ @spec_pool ||= {}
25
+ @spec_pool[fmt_string] ||= new(fmt_string)
26
+ end
27
+
28
+ #The array of specifications that were extracted.
29
+ attr_reader :specs
30
+
31
+ #Set up an instance of a format specification.
32
+ #<br>Note
33
+ #This is a private method (rdoc gets it wrong). To create new instances of
34
+ #\FormatSpec do not use \FormatSpec.new, but use \FormatSpec.get_spec instead.
35
+ def initialize(fmt_string)
36
+ @specs = []
37
+ scan_spec(fmt_string)
38
+ end
39
+
40
+ #Scan the format string extracting literals and variables.
41
+ def scan_spec(fmt_string)
42
+ until fmt_string.empty?
43
+ if fmt_string =~ /%[~@#$^&*\-+=?_<>\\\/\.,\|!]*(\d+(\.\d+)?)?[a-zA-Z]/
44
+ @specs << FormatLiteral.new($PREMATCH) unless $PREMATCH.empty?
45
+ @specs << FormatVariable.new($MATCH)
46
+ fmt_string = $POSTMATCH
47
+ else
48
+ @specs << FormatLiteral.new(fmt_string)
49
+ fmt_string = ""
50
+ end
51
+ end
52
+ end
53
+
54
+ #Validate the specs of this format against the engine.
55
+ def validate(engine)
56
+ specs.each {|item| item.validate(engine)}
57
+ self
58
+ end
59
+
60
+ end
61
+ end