gm-notepad 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce5817b9b619d272b6b534d4da74e9474fb15a5cbfc1dd21ac1094ec2aed844d
4
- data.tar.gz: a82515600b627a7b0227305cb7430a548d53d0bdf006b633390da5da80c9af8a
3
+ metadata.gz: fea81d64cf4d80ba35966cfb2cf36ed00edf206307f46bad9c7d07dc524da732
4
+ data.tar.gz: b3d9513020a6db543dd6551214aca212c0bc94124fc2a0debbe4e379357606e0
5
5
  SHA512:
6
- metadata.gz: 5c0eb020328774f1e4395f4609fef3da8bd15957f1a97acba14d2d492eee854322d7288ad55ee486ee39774464134f34a3ce9e4150900aa966913a78a4665d00
7
- data.tar.gz: 54da4151c52a779dd42324efe1520e066db65c696e108ccf2780c5f756f9b97f84bb66a3538d43b229d2f8973c818c45e1e390defe69600c45d867470e8db293
6
+ metadata.gz: 2583aa8b65971bd9c8bf7a34246ae710e5ab315ccc937e7721862bb67b331a2b911b2bce9edc15e020034b5f1e3c4725ace64a8daf15bb0b7472ec51cf835e0a
7
+ data.tar.gz: e7008f2fcb80279c398c22fafda4a182346df1d8383c95de181d6489a401d06c587e5f9f5271b39d08e3ce4615c9082cc1ddc9092e91ee00fa0748c3e9d02100
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --format documentation
2
2
  --color
3
+ --order rand
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "{}"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2019 Jeremy Friesen
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
data/README.md CHANGED
@@ -6,6 +6,29 @@ A command-line tool to help with your GM-ing.
6
6
 
7
7
  `$ gem install gm-notepad`
8
8
 
9
+ ## Background
10
+
11
+ On a commute home from work, while listening to [Judd Karlman's "Daydreaming about Dragons" podcast](https://anchor.fm/daydreaming-about-dragons/)
12
+ he wondered about ways to organize notes for NPCs. And I started thinking. How
13
+ might I organize my content for access while gaming? And what kind of
14
+ content? More importantly, what kind of tasks do I need to complete as a GM.
15
+
16
+ * Remember a character name
17
+ * Lookup their passive perception
18
+ * Lookup a random table
19
+ * Roll on a random table
20
+ * Create a random character name. Maybe based on a culture.
21
+ * Record quick notes about an NPC
22
+
23
+ I thought about `swnt`, and command line tool for Stars without Numbers.
24
+ See the [github project](https://github.com/nboughton/swnt) for more information.
25
+
26
+ And I thought about [Alex Schroeder's tools](https://alexschroeder.ch/wiki/RPG).
27
+
28
+ I have kicked this around for awhile. I made an attempt in [Rollio](https://github.com/jeremyf/rollio).
29
+ But I built that to roll on tables. I needed something more general.
30
+ _I will, however, begin converting the data._
31
+
9
32
  ## Introduction
10
33
 
11
34
  By default `gm-notepad` interacts with `$stdout` and `$stderr`. There are
@@ -30,34 +53,44 @@ Note taking tool with random table expansion.
30
53
 
31
54
  Examples:
32
55
  $ gm-notepad
33
- $ gm-notepad rolls.txt
56
+ $ gm-notepad filename
34
57
  $ echo '{name}' | gm-notepad
35
58
 
36
- Specific options:
37
- --timestamp Append a timestamp to the note (Default: false)
38
- -c, --config_reporting Dump the configuration data (Default: false)
59
+ Options:
60
+ -l, --list_tables List tables loaded and exit (Default: false)
61
+ -r, --report_config Dump the configuration data (Default: false)
62
+ -p, --path=PATH Path(s) for {table_name}.<config.table_extension> files (Default: ["."])
63
+ -f, --filesystem_directory=DIR Path to dump tables (Default: ".")
64
+ -x, --table_extension=EXT Extension to use for selecting tables (Default: ".txt")
65
+ -t, --timestamp Append a timestamp to the note (Default: false)
39
66
  -d, --defer_output Defer output until system close (Default: false)
40
- -p, --path=PATH Path for {table_name}.<config.table_extension> files (Default: ["."])
41
- -t, --table_extension=EXT Path for {table_name}.<config.table_extension> files (Default: ".txt")
42
- -l, --list_tables List tables loaded (Default: nil)
67
+
68
+ Color options:
69
+ -i, --skip-interactive-color Disable color rendering for interactive buffer (Default: false)
70
+ -o, --with-output-color Enable color rendering for output buffer (Default: false)
71
+
43
72
  -h, --help You're looking at it!
44
73
  ```
45
74
 
46
75
  At it's core, `gm-shell` interacts with named tables. A named table is a file
47
76
  found amongst the specified `paths` and has the specified `table_extension`.
48
- Let's take a look at the defaults. In a new shell, type: `$ gm-notepad -c`
77
+ Let's take a look at the defaults. In a new shell, type: `$ gm-notepad -r`
49
78
 
50
79
  Which writes the following to the `interactive` buffer (eg. `$stderr`)::
51
80
 
52
81
  ```console
53
- => # Configuration Parameters:
54
- => # config[:config_reporting] = false
55
- => # config[:defer_output] = false
56
- => # config[:interactive_buffer] = #<IO:<STDERR>>
57
- => # config[:output_buffer] = #<IO:<STDOUT>>
58
- => # config[:paths] = ["."]
59
- => # config[:table_extension] = ".txt"
60
- => # config[:with_timestamp] = false
82
+ # Configuration Parameters:
83
+ # config[:report_config] = true
84
+ # config[:defer_output] = false
85
+ # config[:filesystem_directory] = "."
86
+ # config[:interactive_buffer] = #<IO:<STDERR>>
87
+ # config[:interactive_color] = true
88
+ # config[:output_color] = false
89
+ # config[:list_tables] = false
90
+ # config[:output_buffer] = #<IO:<STDOUT>>
91
+ # config[:paths] = ["."]
92
+ # config[:table_extension] = ".txt"
93
+ # config[:with_timestamp] = false
61
94
  ```
62
95
 
63
96
  You'll need to exit out (CTRL+D).
@@ -65,11 +98,12 @@ You'll need to exit out (CTRL+D).
65
98
  By default `gm-notepad` will load as tables all files matching the following
66
99
  glob: `./**/*.txt`.
67
100
 
68
- Included in the gem's test suite are three files:
101
+ Included in the gem's test suite are four files:
69
102
 
70
103
  * `./spec/fixtures/name.txt`
71
104
  * `./spec/fixtures/first-name.txt`
72
105
  * `./spec/fixtures/last-name.txt`
106
+ * `./spec/fixtures/location.csv`
73
107
 
74
108
  When I run `gm-notepad -l`, `gm-notepad` does the following:
75
109
 
@@ -78,10 +112,11 @@ When I run `gm-notepad -l`, `gm-notepad` does the following:
78
112
  * puts the table_names to the `interactive` buffer
79
113
  * exits
80
114
 
81
- Below are the table names when you run the `gm-notepad` against the
82
- repository:
115
+ Below are the table names when you run the `gm-notepad -l` against the
116
+ repository (note when you run this command you'll get a preamble of the config):
83
117
 
84
118
  ```console
119
+ => character
85
120
  => first-name
86
121
  => last-name
87
122
  => name
@@ -99,7 +134,7 @@ You now have an interactive shell for `gm-notepad`. Type `?` and hit
99
134
  => Prefixes:
100
135
  => ? - Help (this command)
101
136
  => + - Query table names and contents
102
- => <table_name> - Write the results to the given table
137
+ => <table_name: - Write the results to the given table
103
138
  =>
104
139
  => Tokens:
105
140
  => ! - Skip expansion
@@ -114,20 +149,26 @@ the following: `+first-name`
114
149
  `gm-notepad` will write the following to `interactive` buffer (eg. `$stderr`):
115
150
 
116
151
  ```console
117
- => Frodo
118
- => Merry
119
- => Pippin
120
- => Sam
121
- => {first-name}Wise
152
+ => [1] Frodo
153
+ => [2] Merry
154
+ => [3] Pippin
155
+ => [4] Sam
156
+ => [5-6] {first-name}Wise
122
157
  ```
123
158
 
124
- These are the five table entries in the `first-name` table. Notice the fifth
125
- entry: `{first-name}Wise`. The `{first-name}` references a table named
126
- "first-name" (the same on you are looking at). Now type the following in your `gm-notepad`
127
- session: `Hello {first-name}`
159
+ These are the five table entries in the `first-name` table.
160
+ "Frodo" is at index `1`. "Merry", "Pippin", and "Sam" are at indices 2,3,4
161
+ respectively. For the fifth line there are two things happening. First the
162
+ index spans a range. Second, notice the entry: `{first-name}Wise`. The
163
+ `{first-name}` references a table named "first-name" (the same on you are
164
+ looking at). Now type the following in your `gm-notepad` session: `Hello {first-name}`
128
165
 
129
166
  `gm-notepad` will read the line and recursively expand the `{first-name}` and
130
- write the result to the `interactive` buffer and `output` buffer.
167
+ write the result to the `interactive` buffer and `output` buffer. The expander
168
+ randomly picks a name from all entries, with ranges increasing the chance of
169
+ being picked. In the above table "Frodo", "Merry", "Pippin", and "Sam" each
170
+ have a 1 in 6 chance of being picked. And "{first-name}Wise" has a 2 in 6
171
+ chance.
131
172
 
132
173
  In the session you might have something like the below:
133
174
 
@@ -145,19 +186,54 @@ session type the following: `{first-name} owes [2d6]gp to {first-name}`:
145
186
  Frodo owes 3gp to SamWise
146
187
  ```
147
188
 
148
- And there you go.
189
+ Let's take a look at the `+character` table. Your table indices need not be
190
+ numbers. And you can mix numbers and text. _If you expand the table via
191
+ `{table}` each text index counts as 1 entry. For the below table, each
192
+ entry has a 1 in 3 chance of being randomly chosen.
193
+
194
+ ```console
195
+ => [name] Grell
196
+ => [ac] 15
197
+ => [hd] 12D12
198
+ ```
199
+
200
+ ## Testing Locally
201
+
202
+ * Clone the repository
203
+ * Bundle the dependencies (`$ bundle install`)
204
+ * Run the specs (`$ bundle exec rspec`)
205
+ * Run the command from the repository (`$ bundle exec exe/gm-notepad`)
149
206
 
150
207
  ## Todo
151
208
 
152
- - [ ] Colorize puts to `interactive` buffer
209
+ - [X] Colorize puts to `interactive` buffer
210
+ - [X] Disable colors as a configuration option
211
+ - [ ] Write expected interface document
212
+ - [ ] Skip table lines that begin with `#`
213
+ - [X] Skip processing input lines that begin with `#`
214
+ - [ ] Allow configuration to specify colors
215
+ - [X] Allow configuration to specify table delimiter
216
+ - [ ] Allow option to add a table to memory (instead of writing the table)
217
+ - [ ] Add option to dump all tables to the given directory
218
+ - [X] Allow configuration for where to dump data
153
219
  - [ ] Normalize `WriteToTableHandler` to use a renderer
154
220
  - [ ] Normalize `WriteToTableHandler` to deliver on `grep` and `index` behavior
155
- - [ ] Gracefully handle requesting an entry from a table with an index that does not exist (e.g. with test data try `+name[23]`)
156
- - [ ] Gracefully handle `+name[]`, where "name" is a registered table
221
+ - [X] Gracefully handle requesting an entry from a table with an index that does not exist (e.g. with test data try `+name[23]`)
222
+ - [X] Gracefully handle `+name[]`, where "name" is a registered table
157
223
  - [ ] Add time to live for line expansion (to prevent infinite loops)
158
- - [ ] Enable "up" and "down" to scroll through history
224
+ - [X] Enable "up" and "down" to scroll through history
159
225
  - [ ] Add config that expands dice results while including the requested roll
160
226
  - [ ] Determine feasibility of adding dice to the `{}` expansion syntax (instead of the `[]` syntax)
161
- - [ ] Add index name when rendering table entries
227
+ - [X] Add index name when rendering table entries
228
+ - [ ] Gracefully handle loading a malformed data file (maybe?)
162
229
  - [ ] Add force write results to `output`
163
- - [ ] Add concept of history
230
+ - [X] Add concept of history
231
+ - [ ] When expanding tables account for line expansion (via \n and \t)
232
+ - [ ] Separate the InputHandler into pre-amble (e.g. allow overrides to where we are writing, determine what command we are writing)
233
+ - [ ] Handle a `.gm-notepadrc` to inject default configurations
234
+ - [ ] Add auto table expansion for "{}"
235
+ - [ ] Add auto table expansion for "+"
236
+ - [ ] Add auto index expansion for "["
237
+ - [ ] Create a configuration object that captures the initial input (reduce passing around parameters and persisting copies of the config)
238
+ - [ ] Aspiration: Enable `{{monster}[ac]}` to pick a random monster and then fetch that monster's AC
239
+ - [ ] Add concept of "journal entry"; its not a table (perhaps) but something that you could capture notes.
data/Rakefile CHANGED
@@ -1,6 +1,33 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
- RSpec::Core::RakeTask.new(:spec)
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec) do |t|
6
+ t.pattern = "./spec/**/*_spec.rb"
7
+ ENV['COVERAGE'] = 'true'
8
+ end
5
9
 
6
- task :default => :spec
10
+ namespace :commitment do
11
+ task :configure_test_for_code_coverage do
12
+ ENV['COVERAGE'] = 'true'
13
+ end
14
+ task :code_coverage do
15
+ require 'json'
16
+ $stdout.puts "Checking code_coverage"
17
+ lastrun_filename = File.expand_path('../coverage/.last_run.json', __FILE__)
18
+ if File.exist?(lastrun_filename)
19
+ coverage_percentage = JSON.parse(File.read(lastrun_filename)).fetch('result').fetch('covered_percent').to_i
20
+ EXPECTED_COVERAGE_GOAL = 96
21
+ if coverage_percentage < EXPECTED_COVERAGE_GOAL
22
+ abort("ERROR: Code Coverage Goal Not Met:\n\t#{coverage_percentage}%\tExpected\n\t#{EXPECTED_COVERAGE_GOAL}%\tActual")
23
+ else
24
+ $stdout.puts "Code Coverage Goal Met (at least #{EXPECTED_COVERAGE_GOAL}% coverage)"
25
+ end
26
+ else
27
+ abort "Expected #{lastrun_filename} to exist for code coverage"
28
+ end
29
+ end
30
+ end
31
+
32
+ task(default: ['commitment:configure_test_for_code_coverage', :spec, 'commitment:code_coverage'])
33
+ task(release: :default)
@@ -0,0 +1,14 @@
1
+ # Write entry `1|entry one` to in memory table `basic_example`
2
+ <#basic_example:1|entry one
3
+ # Should render to interactive the internal help
4
+ ?
5
+ # Should render to interactive the given tables
6
+ +
7
+ # Should render to interactive the table `basic_example`
8
+ +basic_example
9
+ # Should render `entry one`
10
+ {basic_example}
11
+ # Should append entry `2|entry two` into memory table `basic_example`
12
+ <basic_example:2|entry two
13
+ # Should dump basic_example onto the file system
14
+ +basic_example>
data/exe/gm-notepad CHANGED
@@ -3,11 +3,18 @@ require 'gm/notepad'
3
3
 
4
4
  require 'optparse'
5
5
  config = {
6
- config_reporting: false,
6
+ report_config: false,
7
7
  defer_output: false,
8
+ filesystem_directory: '.',
8
9
  interactive_buffer: $stderr,
10
+ interactive_color: true,
11
+ output_color: false,
12
+ list_tables: false,
9
13
  output_buffer: $stdout,
10
14
  paths: ['.'],
15
+ column_delimiter: Gm::Notepad::DEFAULT_COLUMN_DELIMITER,
16
+ shell_prompt: Gm::Notepad::DEFAULT_SHELL_PROMPT,
17
+ skip_readlines: false,
11
18
  table_extension: '.txt',
12
19
  with_timestamp: false
13
20
  }
@@ -20,37 +27,59 @@ OptionParser.new do |options|
20
27
  "Note taking tool with random table expansion.\n\n" \
21
28
  "Examples:\n" \
22
29
  "\t$ #{command_name}\n" \
23
- "\t$ #{command_name} rolls.txt\n" \
24
- "\t$ echo '{name}' | #{command_name}"
30
+ "\t$ #{command_name} filename \n" \
31
+ "\t$ echo '{name}' | #{command_name}\n\n" \
32
+ "Options:\n"
25
33
 
26
- # Separator just adds a new line with the specified text.
27
- options.separator ""
28
- options.separator "Specific options:"
29
-
30
- options.on("-t", "--timestamp", "Append a timestamp to the note (Default: #{config[:with_timestamp].inspect})") do |timestamp|
31
- config[:with_timestamp] = timestamp
34
+ options.on_head("-l", "--list_tables", "List tables loaded and exit (Default: #{config[:list_tables].inspect})") do |list_tables|
35
+ config[:list_tables] = list_tables
32
36
  end
33
37
 
34
- options.on("-c", "--config_reporting", "Dump the configuration data (Default: #{config[:config_reporting].inspect})") do |config_reporting|
35
- config[:config_reporting] = config_reporting
38
+ options.on("-r", "--report_config", "Dump the configuration data (Default: #{config[:report_config].inspect})") do |report_config|
39
+ config[:report_config] = report_config
36
40
  end
37
41
 
38
- options.on("-d", "--defer_output", "Defer output until system close (Default: #{config[:defer_output].inspect})") do |defer_output|
39
- config[:defer_output] = defer_output
42
+ options.on("-pPATH", "--path=PATH", String, "Path(s) for {table_name}.<config.table_extension> files (Default: #{config[:paths].inspect})") do |path|
43
+ config[:paths] << path
40
44
  end
41
45
 
42
- options.on("-pPATH", "--path=PATH", String, "Path for {table_name}.<config.table_extension> files (Default: #{config[:paths].inspect})") do |path|
43
- config[:paths] << path
46
+ options.on("-fDIR", "--filesystem_directory=DIR", String, "Path to dump tables (Default: #{config[:filesystem_directory].inspect})") do |filesystem_directory|
47
+ # Should we guard that this exists?
48
+ config[:filesystem_directory] = filesystem_directory
44
49
  end
45
50
 
46
- options.on("-tEXT", "--table_extension=EXT", String, "Path for {table_name}.<config.table_extension> files (Default: #{config[:table_extension].inspect})") do |table_extension|
51
+ options.on("-xEXT", "--table_extension=EXT", String, "Extension to use for selecting tables (Default: #{config[:table_extension].inspect})") do |table_extension|
47
52
  config[:table_extension] = table_extension
48
53
  end
49
54
 
50
- options.on("-l", "--list_tables", "List tables loaded (Default: #{config[:list_tables].inspect})") do |list_tables|
51
- config[:list_tables] = list_tables
55
+ options.on("-dDELIM", "--delimiter=DELIM", String, "Default column delimiter for tables (Default: #{config[:column_delimiter].inspect})") do |column_delimiter|
56
+ map = { "t" => "\t" }
57
+ config[:column_delimiter] = map.fetch(column_delimiter) { column_delimiter }
58
+ end
59
+
60
+ options.separator("")
61
+ options.separator("Output options:")
62
+ options.on("-t", "--timestamp", "Append a timestamp to the note (Default: #{config[:with_timestamp].inspect})") do |timestamp|
63
+ config[:with_timestamp] = timestamp
52
64
  end
53
65
 
66
+ options.on("--defer_output", "Defer output until system close (Default: #{config[:defer_output].inspect})") do |defer_output|
67
+ config[:defer_output] = defer_output
68
+ end
69
+
70
+ options.separator("")
71
+ options.separator("Color options:")
72
+
73
+ options.on("-i", "--skip-interactive-color", "Disable color rendering for interactive buffer (Default: #{!config[:interactive_color].inspect})") do |interactive_color|
74
+ config[:interactive_color] = !interactive_color
75
+ end
76
+
77
+ options.on("-o", "--with-output-color", "Enable color rendering for output buffer (Default: #{!config[:output_color].inspect})") do |output_color|
78
+ config[:output_color] = output_color
79
+ end
80
+
81
+ options.separator("")
82
+
54
83
  options.on_tail("-h", "--help", "You're looking at it!") do
55
84
  $stderr.puts options
56
85
  exit 1
@@ -58,26 +87,35 @@ OptionParser.new do |options|
58
87
  end.parse!
59
88
 
60
89
  if config[:list_tables]
61
- notepad = Gm::Notepad.new(config.merge(config_reporting: true))
90
+ notepad = Gm::Notepad.new(config.merge(report_config: true))
62
91
  notepad.process(input: "+")
63
92
  exit(1)
64
93
  end
65
94
 
66
95
  begin
67
96
  @notepad = Gm::Notepad.new(**config)
68
- # Keep reading lines of input as long as they're coming.
69
- while input = ARGF.gets
97
+ if config.fetch(:skip_readlines)
98
+ input_getter = -> { print "#{config.fetch(:shell_prompt)} "; ARGF.gets }
99
+ else
100
+ require 'gm/notepad/readline'
101
+ input_getter = Gm::Notepad::Readline.input_getter(**config)
102
+ end
103
+ while input = input_getter.call
104
+ # # Keep reading lines of input as long as they're coming.
70
105
  input.each_line do |input|
71
106
  begin
72
107
  @notepad.process(input: input)
73
108
  rescue Errno::EPIPE
109
+ @notepad.close!
110
+
74
111
  # sysexits(3) specifies that exit code 74 represent an IO error,
75
112
  # which is the likely situation
76
- @notepad.close!
77
113
  exit(74)
78
114
  end
79
115
  end
80
116
  end
117
+ rescue => e
118
+ $stderr.puts e
81
119
  ensure
82
120
  @notepad.close!
83
121
  end
data/gm-notepad.gemspec CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.summary = %q{A command line tool for GM-ing}
13
13
  spec.description = %q{A command line tool for GM-ing}
14
14
  spec.homepage = "https://github.com/jeremyf/gm-notepad"
15
+ spec.license = "APACHE2"
15
16
 
16
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
18
  f.match(%r{^(test|spec|features)/})
@@ -21,9 +22,11 @@ Gem::Specification.new do |spec|
21
22
  spec.require_paths = ["lib"]
22
23
 
23
24
  spec.add_dependency "dice_parser"
25
+ spec.add_dependency "term-ansicolor"
24
26
  spec.add_development_dependency "bundler"
25
27
  spec.add_development_dependency "rake"
26
28
  spec.add_development_dependency "rspec"
27
29
  spec.add_development_dependency "pry-byebug"
28
30
  spec.add_development_dependency "rspec-its"
31
+ spec.add_development_dependency "simplecov"
29
32
  end
@@ -0,0 +1,6 @@
1
+ module Gm
2
+ module Notepad
3
+ DEFAULT_SHELL_PROMPT = "🗒".freeze
4
+ DEFAULT_COLUMN_DELIMITER = "|".freeze
5
+ end
6
+ end
@@ -1,4 +1,4 @@
1
- require_relative 'input_handlers/default_handler'
1
+ require "gm/notepad/input_handlers/default_handler"
2
2
  module Gm
3
3
  module Notepad
4
4
  # Responsible for registering the various input handlers
@@ -0,0 +1,25 @@
1
+ require "gm/notepad/input_handlers/default_handler"
2
+ module Gm
3
+ module Notepad
4
+ module InputHandlers
5
+ class CommentHandler < DefaultHandler
6
+ COMMEND_PREFIX = '#'.freeze
7
+
8
+ def self.handles?(input:)
9
+ return false unless input[0] == COMMEND_PREFIX
10
+ true
11
+ end
12
+
13
+ def after_initialize!
14
+ self.to_interactive = true
15
+ self.to_output = false
16
+ self.expand_line = false
17
+ end
18
+
19
+ def lines
20
+ [input]
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,4 +1,4 @@
1
- require_relative "default_handler"
1
+ require "gm/notepad/input_handlers/default_handler"
2
2
  module Gm
3
3
  module Notepad
4
4
  module InputHandlers
@@ -21,7 +21,7 @@ module Gm
21
21
  "Prefixes:",
22
22
  "\t? - Help (this command)",
23
23
  "\t+ - Query table names and contents",
24
- "\t<table_name> - Write the results to the given table",
24
+ "\t<table_name: - Write the results to the given table",
25
25
  "",
26
26
  "Tokens:",
27
27
  "\t! - Skip expansion",
@@ -1,4 +1,4 @@
1
- require_relative "default_handler"
1
+ require "gm/notepad/input_handlers/default_handler"
2
2
  module Gm
3
3
  module Notepad
4
4
  module InputHandlers
@@ -16,6 +16,8 @@ module Gm
16
16
 
17
17
  WITH_GREP_REGEXP = %r{(?<declaration>\/(?<grep>[^\/]+)/)}
18
18
  WITH_INDEX_REGEXP = %r{(?<declaration>\[(?<index>[^\]]+)\])}
19
+ WITH_EMPTY_INDEX_REGEX = %r{(?<declaration>\[\])}
20
+ WITH_EMPTY_GREP_REGEX = %r{(?<declaration>\/\/)}
19
21
  NON_EXPANDING_CHARATER = '!'.freeze
20
22
  def after_initialize!
21
23
  self.expand_line = false
@@ -24,10 +26,13 @@ module Gm
24
26
 
25
27
  line = input[1..-1].to_s
26
28
  self.expand_line = false
27
- if match = WITH_INDEX_REGEXP.match(line)
29
+ if match = WITH_EMPTY_INDEX_REGEX.match(line)
30
+ line = line.sub(match[:declaration], '')
31
+ elsif match = WITH_INDEX_REGEXP.match(line)
32
+ line = line.sub(match[:declaration], '')
33
+ self.index = match[:index]
34
+ elsif match = WITH_EMPTY_GREP_REGEX.match(line)
28
35
  line = line.sub(match[:declaration], '')
29
- index = match[:index]
30
- self.index = index
31
36
  elsif match = WITH_GREP_REGEXP.match(line)
32
37
  line = line.sub(match[:declaration], '')
33
38
  grep = match[:grep]
@@ -50,7 +55,11 @@ module Gm
50
55
  return [message]
51
56
  end
52
57
  if index
53
- [table.lookup(index: index)]
58
+ begin
59
+ [table.lookup(index: index)]
60
+ rescue KeyError
61
+ [%(Entry with index "#{index}" not found in "#{table_name}" table)]
62
+ end
54
63
  elsif grep
55
64
  regexp = %r{#{grep}}i
56
65
  table.grep(regexp)
@@ -1,4 +1,4 @@
1
- require_relative "default_handler"
1
+ require "gm/notepad/input_handlers/default_handler"
2
2
 
3
3
  module Gm
4
4
  module Notepad
@@ -1,4 +1,4 @@
1
- require_relative "default_handler"
1
+ require "gm/notepad/input_handlers/default_handler"
2
2
 
3
3
  module Gm
4
4
  module Notepad
@@ -1,18 +1,19 @@
1
- require_relative "default_handler"
1
+ require "gm/notepad/input_handlers/default_handler"
2
2
 
3
3
  module Gm
4
4
  module Notepad
5
5
  module InputHandlers
6
6
  class WriteToTableHandler < DefaultHandler
7
+ HANDLED_PREFIX = "<".freeze
7
8
  def self.handles?(input:)
8
- return true if input[0] == "<"
9
+ return true if input[0] == HANDLED_PREFIX
9
10
  end
10
11
 
11
12
  attr_accessor :index, :grep, :table_name, :line
12
13
  NON_EXPANDING_CHARATER = '!'.freeze
13
14
  WITH_INDEX_REGEXP = %r{(?<declaration>\[(?<index>[^\]]+)\])}
14
15
  WITH_GREP_REGEXP = %r{(?<declaration>\/(?<grep>[^\/]+)/)}
15
- WITH_WRITE_TARGET_REGEXP = %r{\A<(?<table_name>[^>]+)>(?<line>.*)}
16
+ WITH_WRITE_TARGET_REGEXP = %r{\A#{HANDLED_PREFIX}(?<table_name>[^:]+):(?<line>.*)}
16
17
  def after_initialize!
17
18
  self.to_filesystem = true
18
19
  self.to_interactive = true
@@ -1,3 +1,4 @@
1
+
1
2
  module Gm
2
3
  module Notepad
3
4
  # Responsible for processing the given input into a renderable state
@@ -27,14 +28,16 @@ module Gm
27
28
  end
28
29
 
29
30
  def default_input_handler_registry
30
- require_relative "input_handler_registry"
31
- require_relative "input_handlers/help_handler"
32
- require_relative "input_handlers/query_table_handler"
33
- require_relative "input_handlers/query_table_names_handler"
34
- require_relative "input_handlers/write_to_table_handler"
35
- require_relative "input_handlers/write_line_handler"
31
+ require "gm/notepad/input_handler_registry"
32
+ require "gm/notepad/input_handlers/help_handler"
33
+ require "gm/notepad/input_handlers/comment_handler"
34
+ require "gm/notepad/input_handlers/query_table_handler"
35
+ require "gm/notepad/input_handlers/query_table_names_handler"
36
+ require "gm/notepad/input_handlers/write_to_table_handler"
37
+ require "gm/notepad/input_handlers/write_line_handler"
36
38
  InputHandlerRegistry.new do |registry|
37
39
  registry.register(handler: InputHandlers::HelpHandler)
40
+ registry.register(handler: InputHandlers::CommentHandler)
38
41
  registry.register(handler: InputHandlers::QueryTableHandler)
39
42
  registry.register(handler: InputHandlers::QueryTableNamesHandler)
40
43
  registry.register(handler: InputHandlers::WriteToTableHandler)
@@ -1,32 +1,32 @@
1
1
  require 'time'
2
+ require 'term/ansicolor'
2
3
  module Gm
3
4
  module Notepad
4
5
  # Responsible for rendering lines to the corresponding buffers
5
6
  class LineRenderer
6
- def initialize(with_timestamp: false, defer_output:, output_buffer: default_output_buffer, interactive_buffer: default_interactive_buffer)
7
- @output_buffer = output_buffer
8
- @interactive_buffer = interactive_buffer
9
- @with_timestamp = with_timestamp
7
+ def initialize(defer_output:, output_buffer: default_output_buffer, interactive_buffer: default_interactive_buffer, **config)
8
+ self.config = config
9
+ self.output_buffer = output_buffer
10
+ self.interactive_buffer = interactive_buffer
10
11
  @defer_output = defer_output
11
12
  @lines = []
12
13
  yield(self) if block_given?
13
14
  end
14
15
 
15
- def call(line, to_output: false, to_interactive: true, as_of: Time.now)
16
- render_output(line, defer_output: defer_output, as_of: as_of) if to_output
17
- render_interactive(line) if to_interactive
16
+ def call(lines, to_output: false, to_interactive: true, as_of: Time.now)
17
+ render_interactive(lines) if to_interactive
18
+ render_output(lines, defer_output: defer_output, as_of: as_of) if to_output
18
19
  end
19
20
 
20
21
  def close!
21
- if defer_output
22
- @lines.each do |line|
23
- output_buffer.puts(line)
24
- end
25
- end
22
+ interactive_buffer.close!
23
+ output_buffer.close!
26
24
  end
27
25
 
28
26
  private
29
27
 
28
+ attr_accessor :config
29
+
30
30
  # When true, we defer_output writing until we close the notepad
31
31
  # When false, we write immediately to the output buffer
32
32
  attr_reader :defer_output
@@ -34,27 +34,48 @@ module Gm
34
34
  # The receiver of interactive messages
35
35
  attr_reader :interactive_buffer
36
36
 
37
+ def interactive_buffer=(buffer)
38
+ if config[:interactive_color]
39
+ @interactive_buffer = BufferWrapper.new(buffer, color: :faint, as: :interactive)
40
+ else
41
+ @interactive_buffer = BufferWrapper.new(buffer, as: :interactive)
42
+ end
43
+ end
44
+
37
45
  # The receiver of output
38
46
  attr_reader :output_buffer
39
47
 
48
+ def output_buffer=(buffer)
49
+ if config[:output_color]
50
+ @output_buffer = BufferWrapper.new(buffer, color: :bold, as: :output)
51
+ else
52
+ @output_buffer = BufferWrapper.new(buffer, as: :output)
53
+ end
54
+ end
55
+
56
+
40
57
  # When writing to output_buffer, should we prefix with a timestamp?
41
58
  def with_timestamp?
42
- @with_timestamp
59
+ config.fetch(:with_timestamp, false)
43
60
  end
44
61
 
45
- def render_output(line, defer_output:, as_of:)
46
- if with_timestamp?
47
- line = "#{as_of}\t#{line}"
48
- end
49
- if defer_output
50
- @lines << line
51
- else
52
- output_buffer.puts(line)
62
+ def render_output(lines, defer_output:, as_of:)
63
+ Array(lines).each do |line|
64
+ if with_timestamp?
65
+ line = "#{as_of}\t#{line}"
66
+ end
67
+ if defer_output
68
+ output_buffer.defer(line)
69
+ else
70
+ output_buffer.puts(line)
71
+ end
53
72
  end
54
73
  end
55
74
 
56
- def render_interactive(line)
57
- interactive_buffer.puts("=>\t#{line}")
75
+ def render_interactive(lines)
76
+ Array(lines).each do |line|
77
+ interactive_buffer.puts("=>\t#{line}")
78
+ end
58
79
  end
59
80
 
60
81
  def default_output_buffer
@@ -65,5 +86,37 @@ module Gm
65
86
  $stderr
66
87
  end
67
88
  end
89
+
90
+ # To provide a means for colorizing the output
91
+ class BufferWrapper
92
+ attr_reader :buffer, :color, :as
93
+ def initialize(buffer, color: false, as:)
94
+ @buffer = buffer
95
+ @buffer.extend(Term::ANSIColor)
96
+ @color = color
97
+ @as = as
98
+ @lines = []
99
+ end
100
+
101
+ def puts(text)
102
+ if color
103
+ buffer.puts("#{buffer.public_send(color)}#{text}#{buffer.reset}")
104
+ else
105
+ buffer.puts("#{text}")
106
+ end
107
+ end
108
+
109
+ def defer(line)
110
+ @lines << line
111
+ end
112
+
113
+ def close!
114
+ buffer.print("\n") if as == :interactive
115
+ @lines.each do |line|
116
+ puts(line)
117
+ end
118
+ end
119
+ end
120
+ private_constant :BufferWrapper
68
121
  end
69
122
  end
@@ -27,37 +27,38 @@ module Gm
27
27
 
28
28
  attr_reader :renderer
29
29
  def open!
30
- return unless config[:config_reporting]
31
- renderer.call("# Configuration Parameters:", to_interactive: true, to_output: true)
32
- config.each do |key, value|
33
- line = "# config[#{key.inspect}] = #{value.inspect}"
34
- renderer.call(line, to_interactive: true, to_output: true)
30
+ return unless config[:report_config]
31
+ lines = ["# Configuration Parameters:"]
32
+ config.each_pair do |key, value|
33
+ lines << "# config[#{key.inspect}] = #{value.inspect}"
35
34
  end
35
+ # When running :list_tables by default I don't want to report
36
+ # that to the output buffer.
37
+ to_output = !config[:list_tables]
38
+ renderer.call(lines, to_interactive: true, to_output: to_output)
36
39
  end
37
40
 
38
41
  attr_accessor :renderer, :input_processor
39
42
  attr_writer :config, :table_registry
40
43
 
41
44
  def default_input_processor
42
- require_relative "input_processor"
45
+ require "gm/notepad/input_processor"
43
46
  InputProcessor.new(table_registry: table_registry)
44
47
  end
45
48
 
46
49
  def default_table_registry
47
- require_relative "table_registry"
48
- TableRegistry.load_for(
49
- paths: config.fetch(:paths, []),
50
- table_extension: config.fetch(:table_extension, ".txt")
51
- )
50
+ require "gm/notepad/table_registry"
51
+ TableRegistry.load_for(**config)
52
52
  end
53
53
 
54
54
  def default_renderer
55
- require_relative 'line_renderer'
55
+ require 'gm/notepad/line_renderer'
56
56
  LineRenderer.new(
57
57
  with_timestamp: config.fetch(:with_timestamp, false),
58
58
  defer_output: config.fetch(:defer_output, false),
59
59
  output_buffer: config.fetch(:output_buffer, default_output_buffer),
60
- interactive_buffer: config.fetch(:interactive_buffer, default_interactive_buffer)
60
+ interactive_buffer: config.fetch(:interactive_buffer, default_interactive_buffer),
61
+ **config
61
62
  )
62
63
  end
63
64
 
@@ -0,0 +1,31 @@
1
+ require 'readline'
2
+ module Gm
3
+ module Notepad
4
+ # A configuration module for the Readline module
5
+ module Readline
6
+
7
+ # Check history for existing matches
8
+ completion_function = proc do |string|
9
+ ::Readline::HISTORY.grep(/^#{Regexp.escape(string)}/)
10
+ end
11
+ if ::Readline.respond_to?("basic_word_break_characters=")
12
+ ::Readline.basic_word_break_characters= " \t\n`><=;|&{("
13
+ end
14
+
15
+ # With a sucessful completion add this to the end
16
+ ::Readline.completion_append_character = " "
17
+
18
+ # Without this, when I had in history the following: ["{name}"]
19
+ # And would type `{\t` into the shell, I would get the following
20
+ # result: "{{name}".
21
+ ::Readline.completer_word_break_characters = ""
22
+
23
+ # Hook-in the above completion function
24
+ ::Readline.completion_proc = completion_function
25
+
26
+ def self.input_getter(**config)
27
+ -> { ::Readline.readline("#{config.fetch(:shell_prompt, ">")} ", true) }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,10 +1,11 @@
1
- require_relative "exceptions"
2
- require_relative "table_entry"
1
+ require "gm/notepad/exceptions"
2
+ require "gm/notepad/table_entry"
3
3
 
4
4
  module Gm
5
5
  module Notepad
6
6
  class Table
7
- def initialize(table_name:, lines:, filename: nil)
7
+ def initialize(table_name:, lines:, filename: nil, **config)
8
+ self.config = config
8
9
  self.table_name = table_name
9
10
  self.filename = filename
10
11
  process(lines: lines)
@@ -19,7 +20,7 @@ module Gm
19
20
  end
20
21
 
21
22
  def all
22
- @table.values
23
+ @table.values.uniq
23
24
  end
24
25
 
25
26
  def grep(expression)
@@ -43,7 +44,7 @@ module Gm
43
44
 
44
45
  private
45
46
 
46
- attr_accessor :table_name, :filename
47
+ attr_accessor :table_name, :filename, :config
47
48
 
48
49
  def random_index
49
50
  rand(@table.size)
@@ -52,7 +53,7 @@ module Gm
52
53
  def process(lines:)
53
54
  @table = {}
54
55
  lines.each do |line|
55
- entry = TableEntry.new(line: line)
56
+ entry = TableEntry.new(line: line, **config)
56
57
  entry.lookup_range.each do |i|
57
58
  key = i.to_s
58
59
  raise DuplicateKeyError.new(key: table_name, object: self) if @table.key?(key)
@@ -1,10 +1,11 @@
1
+ require 'gm/notepad/defaults'
1
2
  module Gm
2
3
  module Notepad
3
- TABLE_ENTRY_SEPARATOR = "|".freeze
4
4
  TABLE_ENTRY_RANGE_MARKER = "-".freeze
5
5
  class TableEntry
6
- def initialize(line:)
7
- self.lookup_column, self.entry_column = line.split(TABLE_ENTRY_SEPARATOR)
6
+ def initialize(line:, column_delimiter: DEFAULT_COLUMN_DELIMITER, **config)
7
+ self.column_delimiter = column_delimiter
8
+ self.lookup_column, self.entry_column = line.split(column_delimiter)
8
9
  end
9
10
 
10
11
  include Comparable
@@ -23,11 +24,15 @@ module Gm
23
24
 
24
25
  attr_reader :lookup_column, :entry_column
25
26
 
26
- alias to_s entry_column
27
+ def to_s
28
+ "[#{lookup_column}]\t#{entry_column}"
29
+ end
27
30
  alias to_str entry_column
28
31
 
29
32
  private
30
33
 
34
+ attr_accessor :column_delimiter
35
+
31
36
  def lookup_column=(input)
32
37
  @lookup_column = input.strip.freeze
33
38
  end
@@ -1,30 +1,36 @@
1
- require_relative "table"
2
- require_relative "exceptions"
1
+ require "gm/notepad/table"
2
+ require "gm/notepad/exceptions"
3
3
 
4
4
  module Gm
5
5
  module Notepad
6
6
  # Responsible for loading and registering all of the named tables
7
7
  class TableRegistry
8
- def self.load_for(paths:, table_extension: ".txt")
9
- table_registry = new(paths: paths, table_extension: table_extension)
10
- paths.each do |path|
11
- Dir.glob(File.join(path, "**/*#{table_extension}")).each do |filename|
12
- table_name = File.basename(filename, table_extension)
13
- table_registry.register_by_filename(table_name: table_name, filename: filename)
14
- end
15
- end
8
+ def self.load_for(**config)
9
+ table_registry = new(**config)
10
+ table_registry.load!
16
11
  table_registry
17
12
  end
18
13
 
19
- def initialize(line_evaluator: default_line_evaluator, paths: [], table_extension: ".txt")
14
+ def initialize(line_evaluator: default_line_evaluator, **config)
15
+ self.config = config
20
16
  self.line_evaluator = line_evaluator
21
- self.table_extension = table_extension
22
- self.paths = paths
17
+ self.paths = config.fetch(:paths) { [] }
18
+ self.table_extension = config.fetch(:table_extension) { ".txt" }
19
+ self.filesystem_directory = config.fetch(:filesystem_directory) { "." }
23
20
  @registry = {}
24
21
  end
25
22
 
23
+ def load!
24
+ paths.each do |path|
25
+ Dir.glob(File.join(path, "**/*#{table_extension}")).each do |filename|
26
+ table_name = File.basename(filename, table_extension)
27
+ register_by_filename(table_name: table_name, filename: filename)
28
+ end
29
+ end
30
+ end
31
+
26
32
  private
27
- attr_accessor :line_evaluator, :paths, :table_extension
33
+ attr_accessor :line_evaluator, :paths, :table_extension, :filesystem_directory, :config
28
34
  public
29
35
 
30
36
  def table_names
@@ -39,8 +45,8 @@ module Gm
39
45
  table = nil
40
46
  begin
41
47
  table = fetch_table(name: table_name)
42
- rescue KeyError => e
43
- filename = File.join(paths.first || ".", "#{table_name}#{table_extension}")
48
+ rescue KeyError
49
+ filename = File.join(filesystem_directory, "#{table_name}#{table_extension}")
44
50
  table = register(table_name: table_name, lines: [], filename: filename)
45
51
  end
46
52
  table.append(line: line, write: write)
@@ -70,7 +76,7 @@ module Gm
70
76
 
71
77
  def register(table_name:, lines:, filename: nil)
72
78
  raise DuplicateKeyError.new(key: table_name, object: self) if @registry.key?(table_name)
73
- @registry[table_name] = Table.new(table_name: table_name, lines: lines, filename: filename)
79
+ @registry[table_name] = Table.new(table_name: table_name, lines: lines, filename: filename, **config)
74
80
  end
75
81
 
76
82
  def default_line_evaluator
@@ -1,5 +1,5 @@
1
1
  module Gm
2
2
  module Notepad
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
5
5
  end
data/lib/gm/notepad.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "gm/notepad/version"
2
+ require "gm/notepad/defaults"
2
3
  require "gm/notepad/pad"
3
4
  module Gm
4
5
  module Notepad
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gm-notepad
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Friesen
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: term-ansicolor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +108,20 @@ dependencies:
94
108
  - - ">="
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
97
125
  description: A command line tool for GM-ing
98
126
  email:
99
127
  - jeremy.n.friesen@gmail.com
@@ -107,15 +135,19 @@ files:
107
135
  - ".travis.yml"
108
136
  - CODE_OF_CONDUCT.md
109
137
  - Gemfile
138
+ - LICENSE
110
139
  - README.md
111
140
  - Rakefile
112
141
  - bin/console
113
142
  - bin/setup
143
+ - examples/basic_example_file
114
144
  - exe/gm-notepad
115
145
  - gm-notepad.gemspec
116
146
  - lib/gm/notepad.rb
147
+ - lib/gm/notepad/defaults.rb
117
148
  - lib/gm/notepad/exceptions.rb
118
149
  - lib/gm/notepad/input_handler_registry.rb
150
+ - lib/gm/notepad/input_handlers/comment_handler.rb
119
151
  - lib/gm/notepad/input_handlers/default_handler.rb
120
152
  - lib/gm/notepad/input_handlers/help_handler.rb
121
153
  - lib/gm/notepad/input_handlers/query_table_handler.rb
@@ -126,12 +158,14 @@ files:
126
158
  - lib/gm/notepad/line_evaluator.rb
127
159
  - lib/gm/notepad/line_renderer.rb
128
160
  - lib/gm/notepad/pad.rb
161
+ - lib/gm/notepad/readline.rb
129
162
  - lib/gm/notepad/table.rb
130
163
  - lib/gm/notepad/table_entry.rb
131
164
  - lib/gm/notepad/table_registry.rb
132
165
  - lib/gm/notepad/version.rb
133
166
  homepage: https://github.com/jeremyf/gm-notepad
134
- licenses: []
167
+ licenses:
168
+ - APACHE2
135
169
  metadata: {}
136
170
  post_install_message:
137
171
  rdoc_options: []