signore 0.6.0 → 0.7.0

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.
@@ -1,9 +1,13 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'lovely_rufus'
4
2
 
5
3
  module Signore
6
- Signature = Struct.new(:text, :author, :source, :subject, :tags) do
4
+ class Signature < Struct.new(:text, :author, :source, :subject, :tags)
5
+ class << self
6
+ def from_h(hash)
7
+ new(hash.map { |key, value| [key.to_sym, value] }.to_h)
8
+ end
9
+ end
10
+
7
11
  def initialize(author: '', source: '', subject: '', tags: [], text: '')
8
12
  super text, author, source, subject, tags
9
13
  end
@@ -12,20 +16,14 @@ module Signore
12
16
  to_s.empty?
13
17
  end
14
18
 
15
- undef text if defined?(:text)
16
- def text
17
- self[:text] or ''
18
- end
19
-
20
19
  def to_h
21
- super.map { |key, val| [key.to_s, val] }.to_h.keep_if do |_, value|
22
- value and not value.empty?
23
- end
20
+ super.map { |key, val| { key.to_s => val } }.reduce({}, :merge)
21
+ .compact.reject { |_, val| val.empty? }
24
22
  end
25
23
 
26
24
  def to_s
27
25
  spaced = text.gsub("\n", "\n\n")
28
- wrapped = LovelyRufus.wrap(spaced, width: 80)
26
+ wrapped = LovelyRufus.wrap(spaced, width: 50)
29
27
  squeezed = wrapped.gsub("\n\n", "\n").chomp
30
28
  squeezed + meta_for(squeezed)
31
29
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Signore
4
2
  Tags = Struct.new(:forbidden, :required) do
5
3
  def initialize(forbidden: [], required: [])
@@ -11,7 +9,7 @@ module Signore
11
9
  end
12
10
 
13
11
  def to_s
14
- (required + forbidden.map { |tag| '~' + tag }).join(' ')
12
+ (required + forbidden.map { |tag| "~#{tag}" }).join(' ')
15
13
  end
16
14
  end
17
15
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'English'
4
2
  require 'pathname'
5
3
 
@@ -12,9 +10,9 @@ Gem::Specification.new do |gem|
12
10
  gem.license = 'AGPL-3.0'
13
11
  gem.name = 'signore'
14
12
  gem.summary = 'signore: an email signature manager/randomiser'
15
- gem.version = '0.6.0'
13
+ gem.version = '0.7.0'
16
14
 
17
- gem.required_ruby_version = '~> 2.3'
15
+ gem.required_ruby_version = '~> 2.5'
18
16
 
19
17
  gem.files = `git ls-files -z`.split "\0"
20
18
  gem.executables = gem.files.grep(%r{^bin/}).map { |path| File.basename(path) }
@@ -26,11 +24,13 @@ Gem::Specification.new do |gem|
26
24
  end
27
25
 
28
26
  gem.add_dependency 'lovely_rufus', '~> 1.0'
27
+ gem.add_dependency 'procto', '~> 0.0.3'
29
28
 
30
29
  gem.add_development_dependency 'bogus', '~> 0.1.3'
31
30
  gem.add_development_dependency 'minitest', '~> 5.6'
32
31
  gem.add_development_dependency 'minitest-focus', '~> 1.1'
33
- gem.add_development_dependency 'rake', '~> 11.0'
34
- gem.add_development_dependency 'reek', '~> 4.0'
35
- gem.add_development_dependency 'rubocop', '~> 0.43.0'
32
+ gem.add_development_dependency 'overcommit', '~> 0.46.0'
33
+ gem.add_development_dependency 'rake', '~> 12.0'
34
+ gem.add_development_dependency 'reek', '~> 5.0'
35
+ gem.add_development_dependency 'rubocop', '~> 0.60.0'
36
36
  end
@@ -16,8 +16,9 @@
16
16
  - - !ruby/struct:Signore::Signature
17
17
  text: It may look like I’m just sitting here doing nothing. But I’m really actively waiting for all my problems to go away.
18
18
  - |-
19
- It may look like I’m just sitting here doing nothing. But
20
- I’m really actively waiting for all my problems to go away.
19
+ It may look like I’m just sitting here
20
+ doing nothing. But I’m really actively
21
+ waiting for all my problems to go away.
21
22
 
22
23
  # meta info is right-aligned to the longest line
23
24
  - - !ruby/struct:Signore::Signature
@@ -37,11 +38,13 @@
37
38
  author: M. Edward (Ed) Borasky, Matt Lawrence, Gregory Brown
38
39
  source: ruby-talk
39
40
  - |-
40
- ‘The Ruby community should proceed with all deliberate
41
- speed towards ISO standardization of the language.’
41
+ ‘The Ruby community should proceed
42
+ with all deliberate speed towards
43
+ ISO standardization of the language.’
42
44
  ‘Yeah, look what it did to Forth.’
43
- ‘Don’t just say it, show it. <http://vividpicture.com/aleks/atari/forth.jpg>’
44
- [M. Edward (Ed) Borasky, Matt Lawrence, Gregory Brown, ruby-talk]
45
+ ‘Don’t just say it, show it.
46
+ <http://vividpicture.com/aleks/atari/forth.jpg>’
47
+ [M. Edward (Ed) Borasky, Matt Lawrence, Gregory Brown, ruby-talk]
45
48
 
46
49
  # long meta info is flushed left
47
50
  - - !ruby/struct:Signore::Signature
@@ -53,14 +56,18 @@
53
56
  author: Spehro Pefhany, CyberCypher, Jess Askin, don groves
54
57
  source: alt.usage.english
55
58
  - |-
56
- ‘The Guardian also had a pre-election column, in which an allegedly
57
- “ironic joke” was open to other interpretations: “John Wilkes Booth,
58
- Lee Harvey Oswald, John Hinckley Jr., where are you now that we need
59
- you?” They removed the column from on-line access and apologized.’
60
- ‘They should have. Hinckley missed the vital spot, so who needs him?’
59
+ ‘The Guardian also had a pre-election column, in
60
+ which an allegedly “ironic joke” was open to other
61
+ interpretations: John Wilkes Booth, Lee Harvey
62
+ Oswald, John Hinckley Jr., where are you now
63
+ that we need you?” They removed the column
64
+ from on-line access and apologized.’
65
+ ‘They should have. Hinckley missed
66
+ the vital spot, so who needs him?’
61
67
  ‘As everybody knows, Reagan was killed in
62
68
  that attack and a lookalike was substituted.’
63
- ‘You mean we were governed all those years by an ACTOR!’
69
+ ‘You mean we were governed
70
+ all those years by an ACTOR!’
64
71
  [Spehro Pefhany, CyberCypher, Jess Askin, don groves, alt.usage.english]
65
72
 
66
73
  # wrapping doesn’t left one-letter words at the end of lines
@@ -77,10 +84,11 @@
77
84
  text: Better to teach a man to fish than to give him a fish. And if he can’t be bothered to learn to fish and starves to death, that’s a good enough outcome for me.
78
85
  author: Steve VanDevender
79
86
  - |-
80
- Better to teach a man to fish than to give him a fish.
81
- And if he can’t be bothered to learn to fish and starves
82
- to death, that’s a good enough outcome for me.
83
- [Steve VanDevender]
87
+ Better to teach a man to fish than to give
88
+ him a fish. And if he can’t be bothered
89
+ to learn to fish and starves to death,
90
+ that’s a good enough outcome for me.
91
+ [Steve VanDevender]
84
92
 
85
93
  # for two-line signatures, first line is considered for hangouts
86
94
  - - !ruby/struct:Signore::Signature
@@ -89,6 +97,7 @@
89
97
  subject: on subway engines
90
98
  source: asr
91
99
  - |-
92
- Well, the old ones go mmmmmbbbbzzzzttteeeeeep as they start up
93
- and the new ones go whupwhupwhupwhooopwhooooopwhooooooommmmmmmmmm.
94
- [Graham Reed on subway engines, asr]
100
+ Well, the old ones go mmmmmbbbbzzzzttteeeeeep
101
+ as they start up and the new ones go
102
+ whupwhupwhupwhooopwhooooopwhooooooommmmmmmmmm.
103
+ [Graham Reed on subway engines, asr]
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'pathname'
4
2
  require 'stringio'
5
3
  require 'tempfile'
@@ -25,25 +23,26 @@ module Signore
25
23
  let(:path) { Pathname.new('test/fixtures/signatures.yml') }
26
24
 
27
25
  it 'prints a signature tagged with the provided tags' do
28
- args = %w(prego tech programming)
26
+ args = %w[prego tech programming]
29
27
  out = capture_io { CLI.new(args, repo: repo).run }.first
30
- sig = "// sometimes I believe compiler ignores all my comments\n"
28
+ sig = "// sometimes I believe compiler\n// ignores all my comments\n"
31
29
  _(out).must_equal sig
32
30
  end
33
31
 
34
32
  it 'prints a signature based on allowed and forbidden tags' do
35
- args = %w(prego ~programming tech ~security)
33
+ args = %w[prego ~programming tech ~security]
36
34
  out = capture_io { CLI.new(args, repo: repo).run }.first
37
- _(out).must_equal <<-end.dedent
38
- You do have to be mad to work here, but it doesn’t help.
39
- [Gary Barnes, asr]
35
+ _(out).must_equal <<~end
36
+ You do have to be mad to work
37
+ here, but it doesn’t help.
38
+ [Gary Barnes, asr]
40
39
  end
41
40
  end
42
41
 
43
42
  it 'tells the user if no signatures are found' do
44
43
  path = Pathname.new('test/fixtures/nosignatures.yml')
45
44
  repo = Repo.new(path: path)
46
- args = %w(prego)
45
+ args = %w[prego]
47
46
  out = capture_io { CLI.new(args, repo: repo).run }.first
48
47
  _(out).must_include 'No signatures found.'
49
48
  end
@@ -51,24 +50,25 @@ module Signore
51
50
  it 'tells the user if no signatures with selected tag are found' do
52
51
  path = Pathname.new('test/fixtures/signatures.yml')
53
52
  repo = Repo.new(path: path)
54
- args = %w(prego esse ~percipi)
53
+ args = %w[prego esse ~percipi]
55
54
  out = capture_io { CLI.new(args, repo: repo).run }.first
56
55
  _(out).must_include 'Sadly no signatures are tagged esse ~percipi.'
57
56
  end
58
57
  end
59
58
 
60
59
  describe 'pronto' do
61
- let(:repo) { Repo.new(path: Pathname.new(Tempfile.new('').path)) }
60
+ let(:repo) { Repo.new(path: Pathname.new(Tempfile.new.path)) }
62
61
 
63
62
  it 'asks about signature parts and saves resulting signature' do
64
- input = StringIO.new <<-end.dedent
63
+ input = StringIO.new <<~end
65
64
  The Wikipedia page on ADHD is like 20 pages long. That’s just cruel.
66
65
 
67
66
  Mark Pilgrim\n\n\n
68
67
  end
69
- args = %w(pronto Wikipedia ADHD)
68
+ args = %w[pronto Wikipedia ADHD]
70
69
  out = capture_io { CLI.new(args, repo: repo).run input: input }.first
71
- _(out).must_equal <<-end.dedent
70
+ _(out).must_equal <<~end
71
+
72
72
  text?
73
73
 
74
74
  author?
@@ -76,26 +76,29 @@ module Signore
76
76
  subject?
77
77
 
78
78
  source?
79
- The Wikipedia page on ADHD is like 20 pages long. That’s just cruel.
80
- [Mark Pilgrim]
79
+ The Wikipedia page on ADHD is like
80
+ 20 pages long. That’s just cruel.
81
+ [Mark Pilgrim]
81
82
  end
82
- args = %w(prego Wikipedia ADHD)
83
+ args = %w[prego Wikipedia ADHD]
83
84
  out = capture_io { CLI.new(args, repo: repo).run }.first
84
- _(out).must_equal <<-end.dedent
85
- The Wikipedia page on ADHD is like 20 pages long. That’s just cruel.
86
- [Mark Pilgrim]
85
+ _(out).must_equal <<~end
86
+ The Wikipedia page on ADHD is like
87
+ 20 pages long. That’s just cruel.
88
+ [Mark Pilgrim]
87
89
  end
88
90
  end
89
91
 
90
92
  it 'handles multi-line signatures' do
91
- input = StringIO.new <<-end.dedent
93
+ input = StringIO.new <<~end
92
94
  ‘You’ve got an interesting accent. Subtle. I can’t place it.’
93
95
  ‘It’s text-to-speech… I was raised by smartphones.’
94
96
 
95
97
  Patrick Ewing\n\n\n
96
98
  end
97
99
  io = capture_io { CLI.new(['pronto'], repo: repo).run input: input }
98
- _(io.first).must_equal <<-end.dedent
100
+ _(io.first).must_equal <<~end
101
+
99
102
  text?
100
103
 
101
104
  author?
@@ -103,9 +106,11 @@ module Signore
103
106
  subject?
104
107
 
105
108
  source?
106
- ‘You’ve got an interesting accent. Subtle. I can’t place it.’
107
- ‘It’s text-to-speech… I was raised by smartphones.’
108
- [Patrick Ewing]
109
+ ‘You’ve got an interesting accent.
110
+ Subtle. I can’t place it.’
111
+ ‘It’s text-to-speech…
112
+ I was raised by smartphones.’
113
+ [Patrick Ewing]
109
114
  end
110
115
  end
111
116
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'fileutils'
4
2
  require 'pathname'
5
3
  require 'tempfile'
@@ -11,7 +9,27 @@ require_relative '../../lib/signore/tags'
11
9
 
12
10
  module Signore
13
11
  describe Repo do
14
- let(:path) { Pathname.new(Tempfile.new('').path) }
12
+ let(:path) { Pathname.new(Tempfile.new.path) }
13
+
14
+ describe '.default_path' do
15
+ it 'honours XDG_DATA_HOME if it’s set' do
16
+ old_xdg = ENV.delete('XDG_DATA_HOME')
17
+ ENV['XDG_DATA_HOME'] = Dir.mktmpdir
18
+ path = "#{ENV['XDG_DATA_HOME']}/signore/signatures.yml"
19
+ _(Repo.default_path).must_equal Pathname.new(path)
20
+ ensure
21
+ FileUtils.rmtree ENV['XDG_DATA_HOME']
22
+ old_xdg ? ENV['XDG_DATA_HOME'] = old_xdg : ENV.delete('XDG_DATA_HOME')
23
+ end
24
+
25
+ it 'defaults XDG_DATA_HOME to ~/.local/share if it’s not set' do
26
+ old_xdg = ENV.delete('XDG_DATA_HOME')
27
+ path = File.expand_path('~/.local/share/signore/signatures.yml')
28
+ _(Repo.default_path).must_equal Pathname.new(path)
29
+ ensure
30
+ ENV['XDG_DATA_HOME'] = old_xdg if old_xdg
31
+ end
32
+ end
15
33
 
16
34
  describe '.new' do
17
35
  it 'rewrites legacy file to hashes on first access' do
@@ -48,43 +66,6 @@ module Signore
48
66
  end
49
67
  end
50
68
 
51
- describe '#find' do
52
- let(:path) { Pathname.new('test/fixtures/signatures.yml') }
53
- let(:repo) { Repo.new(path: path, sig_finder: sig_finder) }
54
- let(:sig_finder) { fake(SigFinder) }
55
- let(:sigs) { repo.sigs }
56
- let(:store) { YAML::Store.new(path) }
57
-
58
- it 'returns a random signature by default' do
59
- stub(sig_finder).find(sigs, tags: Tags.new) { sigs.last }
60
- _(repo.find).must_equal sigs.last
61
- end
62
-
63
- it 'returns a random signature based on required and forbidden tags' do
64
- tags = Tags.new(forbidden: %w(programming security), required: %w(tech))
65
- stub(sig_finder).find(sigs, tags: tags) { sigs.first }
66
- _(repo.find(tags: tags)).must_equal sigs.first
67
- end
68
-
69
- it 'doesn’t blow up if the path is missing' do
70
- begin
71
- tempdir = Dir.mktmpdir
72
- path = Pathname.new("#{tempdir}/some_intermediate_dir/sigs.yml")
73
- _(Repo.new(path: path).find(tags: Tags.new)).must_equal Signature.new
74
- ensure
75
- FileUtils.rmtree tempdir
76
- end
77
- end
78
-
79
- it 'keeps working with legacy YAML files' do
80
- path = Pathname.new(Tempfile.new('').path)
81
- FileUtils.cp Pathname.new('test/fixtures/signatures.legacy.yml'), path
82
- repo = Repo.new(path: path, sig_finder: sig_finder)
83
- stub(sig_finder).find(sigs, tags: Tags.new) { sigs.last }
84
- _(repo.find).must_equal sigs.last
85
- end
86
- end
87
-
88
69
  describe '#sigs' do
89
70
  it 'returns all the Signatures from the Repo' do
90
71
  path = Pathname.new('test/fixtures/signatures.yml')
@@ -93,6 +74,23 @@ module Signore
93
74
  _(sigs.first.author).must_equal 'Gary Barnes'
94
75
  _(sigs.last.subject).must_equal 'Star Wars ending explained'
95
76
  end
77
+
78
+ it 'keeps working with legacy YAML files' do
79
+ legacy_path = Pathname.new('test/fixtures/signatures.legacy.yml')
80
+ temp_path = Pathname.new(Tempfile.new.path)
81
+ FileUtils.cp legacy_path, temp_path
82
+ legacy_repo = Repo.new(path: temp_path)
83
+ new_repo = Repo.new(path: Pathname.new('test/fixtures/signatures.yml'))
84
+ _(legacy_repo.sigs).must_equal new_repo.sigs
85
+ end
86
+
87
+ it 'doesn’t blow up if the path is missing' do
88
+ tempdir = Dir.mktmpdir
89
+ path = Pathname.new("#{tempdir}/some_intermediate_dir/sigs.yml")
90
+ _(Repo.new(path: path).sigs).must_equal []
91
+ ensure
92
+ FileUtils.rmtree tempdir
93
+ end
96
94
  end
97
95
  end
98
96
  end
@@ -1,5 +1,4 @@
1
- # frozen_string_literal: true
2
-
1
+ require 'fileutils'
3
2
  require 'pathname'
4
3
  require 'tmpdir'
5
4
  require_relative '../test_helper'
@@ -14,34 +13,10 @@ module Signore
14
13
  end
15
14
  end
16
15
 
17
- describe '#repo_path' do
18
- it 'honours XDG_DATA_HOME if it’s set' do
19
- begin
20
- old_xdg = ENV.delete('XDG_DATA_HOME')
21
- ENV['XDG_DATA_HOME'] = Dir.mktmpdir
22
- path = "#{ENV['XDG_DATA_HOME']}/signore/signatures.yml"
23
- _(Settings.new.repo_path).must_equal Pathname.new(path)
24
- ensure
25
- FileUtils.rmtree ENV['XDG_DATA_HOME']
26
- old_xdg ? ENV['XDG_DATA_HOME'] = old_xdg : ENV.delete('XDG_DATA_HOME')
27
- end
28
- end
29
-
30
- it 'defaults XDG_DATA_HOME to ~/.local/share if it’s not set' do
31
- begin
32
- old_xdg = ENV.delete('XDG_DATA_HOME')
33
- path = File.expand_path('~/.local/share/signore/signatures.yml')
34
- _(Settings.new.repo_path).must_equal Pathname.new(path)
35
- ensure
36
- ENV['XDG_DATA_HOME'] = old_xdg if old_xdg
37
- end
38
- end
39
- end
40
-
41
16
  describe '#tags' do
42
17
  it 'returns the forbidden and required tags' do
43
- tags = Tags.new(forbidden: %w(tech), required: %w(en))
44
- _(Settings.new(%w(prego ~tech en)).tags).must_equal tags
18
+ tags = Tags.new(forbidden: %w[tech], required: %w[en])
19
+ _(Settings.new(%w[prego ~tech en]).tags).must_equal tags
45
20
  end
46
21
 
47
22
  it 'doesn’t blow up on empty args' do
@@ -1,8 +1,7 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'yaml/store'
4
2
  require_relative '../test_helper'
5
3
  require_relative '../../lib/signore/sig_finder'
4
+ require_relative '../../lib/signore/signature'
6
5
  require_relative '../../lib/signore/tags'
7
6
 
8
7
  module Signore
@@ -13,36 +12,34 @@ module Signore
13
12
  Repo.new(path: Pathname.new('test/fixtures/signatures.yml')).sigs
14
13
  end
15
14
 
16
- let(:sig_finder) { SigFinder.new }
17
-
18
15
  describe '#find' do
19
16
  it 'returns a random Signature by default' do
20
- _(SigFinder.new(random: Random.new(0)).find(sigs).text)
17
+ _(SigFinder.find(sigs, random: Random.new(0)).text)
21
18
  .must_include 'Amateur fighter pilot ignores orders'
22
- _(SigFinder.new(random: Random.new(1)).find(sigs).text)
19
+ _(SigFinder.find(sigs, random: Random.new(1)).text)
23
20
  .must_equal '// sometimes I believe compiler ignores all my comments'
24
21
  end
25
22
 
26
23
  it 'returns a random signature if the tags are empty' do
27
- _(SigFinder.new(random: Random.new(0)).find(sigs, tags: Tags.new).text)
24
+ _(SigFinder.find(sigs, random: Random.new(0), tags: Tags.new).text)
28
25
  .must_include 'Amateur fighter pilot ignores orders'
29
26
  end
30
27
 
31
28
  it 'returns a random signature based on provided tags' do
32
- _(sig_finder.find(sigs, tags: Tags.new(required: %w(programming))).text)
29
+ _(SigFinder.find(sigs, tags: Tags.new(required: %w[programming])).text)
33
30
  .must_equal '// sometimes I believe compiler ignores all my comments'
34
- _(sig_finder.find(sigs, tags: Tags.new(required: %w(work))).text)
31
+ _(SigFinder.find(sigs, tags: Tags.new(required: %w[work])).text)
35
32
  .must_equal 'You do have to be mad to work here, but it doesn’t help.'
36
33
  end
37
34
 
38
35
  it 'returns a random signature based on required and forbidden tags' do
39
- tags = Tags.new(forbidden: %w(programming security), required: %w(tech))
40
- _(sig_finder.find(sigs, tags: tags).text)
36
+ tags = Tags.new(forbidden: %w[programming security], required: %w[tech])
37
+ _(SigFinder.find(sigs, tags: tags).text)
41
38
  .must_equal 'You do have to be mad to work here, but it doesn’t help.'
42
39
  end
43
40
 
44
41
  it 'returns a null object if there are no results' do
45
- _(sig_finder.find([])).must_equal Signature.new
42
+ _(SigFinder.find([])).must_equal Signature.new
46
43
  end
47
44
  end
48
45
  end