output_mode 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 459750a9a32c10353ffca23bcf3f9aa1bdb7da651570a324e82ba224c260df87
4
+ data.tar.gz: 5a1661e01600478c767d79ad27b2b499440f3d610d4255088c7c76fff0842e28
5
+ SHA512:
6
+ metadata.gz: 4bddfb1d70102c00543a73d6d2bdd56dfcd7f248fd0ea57b222dc428bb1cb6928168592663f07c7bd5d18e22a16ae70af59dc2fbf5281de19004971cb9b26737
7
+ data.tar.gz: 94d113f12f084cf82a29b4a0f1e8528c3dbd8aa2138237d4dcb5ecbf8cc24352ab25e0528f6edb23a501dc2bdcec1e729698df7bb3f643d2666f8008ff0ece85
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /vendor/
10
+
11
+ Gemfile.lock
12
+
13
+
14
+ # rspec failure tracking
15
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,13 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.5
7
+ - 2.7.0
8
+ before_install: gem install bundler -v 2.0.2
9
+ script: bundle exec rspec --format documentation --order random
10
+ branches:
11
+ only:
12
+ - master
13
+ - develop
data/Gemfile ADDED
@@ -0,0 +1,30 @@
1
+ #==============================================================================
2
+ # Copyright 2020 William McCumstie
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # 1. Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ # POSSIBILITY OF SUCH DAMAGE.
25
+ #==============================================================================
26
+
27
+ source "https://rubygems.org"
28
+
29
+ # Specify your gem's dependencies in output_mode.gemspec
30
+ gemspec
@@ -0,0 +1,18 @@
1
+ This project is partially dual licensed under BSD-2-Clause OR 0BSD. Source code originating from a BSD-2-Clause licensed file must comply with terms contained within the license header block. Source code originating from files without a license header block maybe redistributed under either license.
2
+
3
+ The following is a copy of the BSD-2-Clause license:
4
+
5
+ Copyright 2020 William McCumstie
6
+
7
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
10
+
11
+ 2. 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.
12
+
13
+ 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 HOLDER 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.
14
+
15
+ The following is a copy of the 0BSD license:
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
+
@@ -0,0 +1,67 @@
1
+ [![Build Status](https://travis-ci.com/WilliamMcCumstie/output_mode.svg?branch=master)](https://travis-ci.com/WilliamMcCumstie/output_mode)
2
+
3
+ # OutputMode
4
+
5
+ Provides a set of wrapper `Outputs` to common libraries: `TTY::Table`, `CSV`, and `ERB`. Focus on "what" you want to print to a terminal instead of "how" it should be formatted.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'output_mode'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install output_mode
22
+
23
+ ## Usage
24
+
25
+ Checkout the [demo script](bin/demo) for a basic getting started example. It uses the two prefabricated modules:
26
+ * `OutputMode::TLDR::Index` - Tabulate the data models for humans and tab ("\t") delimit it for machines
27
+ * `OutputMode::TLDR::Show` - List the data model(s) for humans and tab ("\t") delimit it for machines
28
+
29
+ The `TLDR` modules are designed for a fairly limited use case, where:
30
+ * The humanized/machine outputs is toggled if `StandardOut` is connected to a `TTY`,
31
+ * Certain columns/fields need to be hidden based on a user supplied verbosity toggle.
32
+
33
+ A basic use case would be:
34
+
35
+ ```
36
+ class Foo
37
+ extend OutputMode::TLDR::Index
38
+
39
+ # Adds a "column" to the output. Fundamentally the "column" is a block transform function
40
+ register_callable(header: 'ID') { |model| model.id }
41
+ register_callable(header: 'Name') { |model| model.name }
42
+
43
+ # Show different date formats according to verbosity, only one column will be displayed
44
+ register_callable(header: 'Create Date', verbose: true) { |m| m.create_date.to_rfc3339 }
45
+ register_callable(header: 'Create Date', verbose false) { |m| m.create_date.strftime("%F") }
46
+ end
47
+
48
+ data = [... data models ...]
49
+ puts Foo.build_output.render(*data)
50
+ ```
51
+
52
+ If this use case becomes to restrictive, look at the internals of the `TLDR` modules on how they are implemented. This will give you ideas on how to implement the `outputs`/`modes` for your bespoke use case.
53
+
54
+ ## Development
55
+
56
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
57
+
58
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
59
+
60
+ ## Contributing
61
+
62
+ Bug reports and pull requests are welcome on GitHub at https://github.com/WilliamMcCumstie/output_mode.
63
+
64
+ ## Copyright and License
65
+
66
+ See [LICENSE](LICENSE.txt) for dual licensing details.
67
+
@@ -0,0 +1,32 @@
1
+ #==============================================================================
2
+ # Copyright 2020 William McCumstie
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # 1. Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ # POSSIBILITY OF SUCH DAMAGE.
25
+ #==============================================================================
26
+
27
+ require "bundler/gem_tasks"
28
+ require "rspec/core/rake_task"
29
+
30
+ RSpec::Core::RakeTask.new(:spec)
31
+
32
+ task :default => :spec
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ #==============================================================================
3
+ # Copyright 2020 William McCumstie
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # 1. Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
+ # POSSIBILITY OF SUCH DAMAGE.
26
+ #==============================================================================
27
+
28
+ require "bundler/setup"
29
+ require "output_mode"
30
+
31
+ # You can add fixtures and/or initialization code here to make experimenting
32
+ # with your gem easier. You can also use a different console, if you like.
33
+
34
+ # (If you use this, don't forget to add pry to your Gemfile!)
35
+ require "pry"
36
+ Pry.start
37
+
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ #==============================================================================
3
+ # Refer to LICENSE.txt for licensing terms
4
+ #==============================================================================
5
+
6
+
7
+ require "bundler/setup"
8
+ require "output_mode"
9
+
10
+ module DemoIndex
11
+ extend OutputMode::TLDR::Index
12
+
13
+ register_callable(header: 'Integer') { |i| i }
14
+ register_callable(header: 'Standard') { 'always visible' }
15
+ register_callable(header: 'Verbose', verbose: true) { 'verbose visible' }
16
+ register_callable(header: 'Simplified', verbose: false) { 'simplified visible' }
17
+ register_callable(header: 'Yes/True') { true }
18
+ register_callable(header: 'No/False') { false }
19
+ register_callable(header: 'Missing') { nil }
20
+ end
21
+
22
+ module DemoShow
23
+ extend OutputMode::TLDR::Show
24
+
25
+ register_callable(header: 'Integer') { |i| i }
26
+ register_callable(header: 'Standard') { 'always visible' }
27
+ register_callable(header: 'Verbose', verbose: true) { 'verbose visible' }
28
+ register_callable(header: 'Simplified', verbose: false) { 'simplified visible' }
29
+ register_callable(header: 'Yes/True') { true }
30
+ register_callable(header: 'No/False') { false }
31
+ register_callable(header: 'Missing') { nil }
32
+ end
33
+
34
+ data = [1, 2, 3]
35
+
36
+ puts <<~EOF
37
+ #==============================================================================
38
+ # Demo Verbose Index
39
+ #==============================================================================
40
+ #{DemoIndex.build_output(verbose: true).render(*data)}
41
+
42
+ #==============================================================================
43
+ # Demo "Simplified" Index
44
+ # NOTE: Disabled for non-interactive shell, shows the verbose output instead
45
+ #==============================================================================
46
+ #{DemoIndex.build_output(verbose: false).render(*data)}
47
+
48
+ #==============================================================================
49
+ # Demo Verbose Show
50
+ #==============================================================================
51
+ #{DemoShow.build_output(verbose: true).render(*data)}
52
+
53
+ #==============================================================================
54
+ # Demo "Simplified" Show
55
+ # NOTE: Disabled for non-interactive shell, shows the verbose output instead
56
+ #==============================================================================
57
+ #{DemoShow.build_output(verbose: false).render(*data)}
58
+ EOF
59
+
60
+
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+ #==============================================================================
3
+ # Copyright 2020 William McCumstie
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # 1. Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
+ # POSSIBILITY OF SUCH DAMAGE.
26
+ #==============================================================================
27
+
28
+ set -euo pipefail
29
+ IFS=$'\n\t'
30
+ set -vx
31
+
32
+ bundle install
33
+
34
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,34 @@
1
+ #==============================================================================
2
+ # Copyright 2020 William McCumstie
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # 1. Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ # POSSIBILITY OF SUCH DAMAGE.
25
+ #==============================================================================
26
+
27
+ require "output_mode/version"
28
+ require 'output_mode/errors'
29
+
30
+ require 'output_mode/outputs'
31
+ require 'output_mode/callable'
32
+ require 'output_mode/builder_dsl'
33
+ require 'output_mode/tldr'
34
+
@@ -0,0 +1,50 @@
1
+ #==============================================================================
2
+ # Copyright 2020 William McCumstie
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # 1. Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ # POSSIBILITY OF SUCH DAMAGE.
25
+ #==============================================================================
26
+
27
+ module OutputMode
28
+ module BuilderDSL
29
+ # The callable objects an `output` can be built from
30
+ def output_callables
31
+ @output_callables ||= Callables.new
32
+ end
33
+
34
+ # Adds a new callable object to {output_callables}
35
+ # @abstract This maybe overridden to restrict the method signature
36
+ # @param config Directly provided to {OutputMode::Callable#initialize}
37
+ # @yield Directly provided to {OutputMode::Callable#initialize}
38
+ def register_callable(**config, &b)
39
+ output_callables << Callable.new(**config, &b)
40
+ end
41
+
42
+ # Provides the base method signature
43
+ # @abstract Must be implemented by the main class/module
44
+ # @return OutputMode::Output The newly created output
45
+ def build_output(**config)
46
+ raise NotImplementedError
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,138 @@
1
+ #==============================================================================
2
+ # Copyright 2020 William McCumstie
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # 1. Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ # POSSIBILITY OF SUCH DAMAGE.
25
+ #==============================================================================
26
+
27
+ module OutputMode
28
+ # Internal array like object that will convert procs to Callable
29
+ class Callables < Array
30
+ # @api private
31
+ def initialize(callables = nil)
32
+ case callables
33
+ when Array
34
+ super().tap do |all|
35
+ callables.each { |c| all << c }
36
+ end
37
+ when nil
38
+ super()
39
+ else
40
+ raise "Can not convert #{callables.class} into a #{self.class}"
41
+ end
42
+ end
43
+
44
+ def <<(item)
45
+ if item.is_a? Callable
46
+ super
47
+ elsif item.respond_to?(:call)
48
+ super(Callable.new(&item))
49
+ else
50
+ raise Error, "#{item.class} is not callable"
51
+ end
52
+ end
53
+ end
54
+
55
+ class Callable
56
+ # @!attribute [r] modes
57
+ # @return [Hash<Symbol => Boolean>] Returns the configured modes
58
+ # @!attribute [r] callable
59
+ # @return [#call] Returns the underlining block
60
+ # @!attribute [r] config
61
+ # @return [Hash] An arbitrary hash of key-value pairs
62
+ attr_reader :modes, :callable, :config
63
+
64
+ # Wraps a block/ callable object with mode query methods
65
+ # @overload initialize(modes: {}, **config)
66
+ # @param [Hash<Symbol => Boolean>] modes: Provide the preconfigured modes
67
+ # @param **config An arbitrary hash to be stored on the object
68
+ # @yield Executed by the {#call} method
69
+ # @yieldparam *a The arguments provided to {#call}
70
+ # @overload initialize(modes: []) { |*a| ... }
71
+ # @param [Array] modes: The preconfigured modes as an array, this will be converted to a hash
72
+ def initialize(modes: {}, **config, &block)
73
+ @callable = block
74
+ @modes = if modes.is_a? Hash
75
+ modes.reject { |_, v| v.nil? }
76
+ .map { |k, v| [k, v ? true : false] }
77
+ .to_h
78
+ else
79
+ modes.map { |k| [k, true] }.to_h
80
+ end
81
+ @config = config
82
+ end
83
+
84
+ # Handles the dynamic +<query>?+ and +<explicit-negation>!+ methods
85
+ #
86
+ # The +<query>?+ methods check if the mode has been set on the object. If
87
+ # +query+ is a defined mode, then the value is directly pulled from #{modes}.
88
+ # Undefined modes will return +false+.
89
+ #
90
+ # The +<explicit-negation>!+ methods are similar to queries, but undefined modes
91
+ # will return +true+. This means +<explicit-negation>!+ methods only return +false+
92
+ # if the +explicit-negation+ mode has been set to +false+ in {#modes}.
93
+ #
94
+ # @return [Boolean] The result of the query or explicit-negation
95
+ # @raises [NoMethodError] All other method calls
96
+ def method_missing(s, *a, &b)
97
+ mode = s[0..-2].to_sym
98
+ case method_char(s)
99
+ when '?'
100
+ modes.fetch(mode, false)
101
+ when '!'
102
+ modes.fetch(mode, true)
103
+ else
104
+ super
105
+ end
106
+ end
107
+
108
+ # Responds +true+ for valid dynamic methods
109
+ # @param [Symbol] s The method to be tested
110
+ # @return [Boolean] The truthiness of the underlining call to {#method_char}
111
+ def respond_to_missing?(s, *_)
112
+ method_char(s) ? true : false
113
+ end
114
+
115
+ # Determines the "type" associated with a dynamic method
116
+ # @overload method_char(bang!)
117
+ # @param bang! A symbol/string ending with !
118
+ # @return ['!']
119
+ # @overload method_char(question?)
120
+ # @param question? A symbol/string ending with ?
121
+ # @return ['?']
122
+ # @overload method_char(other)
123
+ # @param other Any other symbol/string
124
+ # @return [Nil]
125
+ def method_char(s)
126
+ char = s[-1]
127
+ ['?', '!'].include?(char) ? char : nil
128
+ end
129
+
130
+ # Calls the underlining block
131
+ # @param *a The arguments to be provided to {#callable}
132
+ # @return The results from the block
133
+ def call(*a)
134
+ callable.call(*a)
135
+ end
136
+ end
137
+ end
138
+