signore 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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