signore 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e60e737742002b39f1c5cc08199c6d254494f44b
4
- data.tar.gz: 0c13a2461cd5b9c5c6b981516b0050b63937ff87
3
+ metadata.gz: b5b9c7abacbf5730fdbadf79e7d5fe61ec37cdd3
4
+ data.tar.gz: 587a3996c7fabb7e31b3bd27250310749b588f0c
5
5
  SHA512:
6
- metadata.gz: f880a98957f0312852ef253226f67d3c123832b0efe2f0d4eeeeb8438384b40e03e4a8d8a89f6671bc300ad0f35a839162cce14551002839ed1034323bcbcf4e
7
- data.tar.gz: a8427764969f46768ddea7be551f2f3b53c0b4bfdd8085244fe2d8aa4f0d39c221b630dafd6e9261695a0aff5ac1ce74b481292ae2cd0c37e87db8d2c5232da2
6
+ metadata.gz: 0f04915b49b5d7f3678bd178def6607a7bd20727f2bcf1db2731ee86ef8e058c5107004183d7ac4fd47c5aa418b00ccc5df3f53cee0f0f1de35a8c5ca2c3ad14
7
+ data.tar.gz: b37a86a56e2309943664de9ad3a5b1934d037fca249d123dcfb82cf6bd05f29357c91fa177c8d0e8bd0eb4ab14571c451dbf6014365c9c50568d17ef8158a1c2
data/.rubocop.yml CHANGED
@@ -1,20 +1,14 @@
1
- AccessModifierIndentation:
2
- Enabled: false
3
-
4
1
  AndOr:
5
2
  Enabled: false
6
3
 
7
4
  Documentation:
8
5
  Enabled: false
9
6
 
10
- EndAlignment:
11
- Enabled: false
12
-
13
- IndentationWidth:
7
+ Not:
14
8
  Enabled: false
15
9
 
16
- MethodDefParentheses:
17
- EnforcedStyle: require_no_parentheses
18
-
19
10
  SingleSpaceBeforeFirstArg:
20
11
  Enabled: false
12
+
13
+ TrailingComma:
14
+ EnforcedStyleForMultiline: comma
data/Gemfile.lock CHANGED
@@ -2,66 +2,71 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  signore (0.2.2)
5
- lovely_rufus (~> 0.1.2)
5
+ lovely_rufus (~> 0.2.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- ast (1.1.0)
11
- celluloid (0.15.2)
12
- timers (~> 1.1.0)
13
- celluloid-io (0.15.0)
14
- celluloid (>= 0.15.0)
15
- nio4r (>= 0.5.0)
10
+ ast (2.0.0)
11
+ astrolabe (1.3.0)
12
+ parser (>= 2.2.0.pre.3, < 3.0)
13
+ bogus (0.1.5)
14
+ dependor (>= 0.0.4)
15
+ celluloid (0.16.0)
16
+ timers (~> 4.0.0)
17
+ dependor (1.0.1)
16
18
  ffi (1.9.3)
17
- json (1.8.1)
18
- listen (2.7.1)
19
+ hitimes (1.2.2)
20
+ listen (2.7.9)
19
21
  celluloid (>= 0.15.2)
20
- celluloid-io (>= 0.15.0)
21
22
  rb-fsevent (>= 0.9.3)
22
23
  rb-inotify (>= 0.9)
23
- lovely_rufus (0.1.2)
24
- minitest (5.3.2)
25
- nio4r (1.0.0)
26
- parser (2.1.7)
27
- ast (~> 1.1)
24
+ lovely_rufus (0.2.0)
25
+ minitest (5.4.1)
26
+ minitest-focus (1.1.0)
27
+ minitest (>= 4, < 6)
28
+ parser (2.2.0.pre.4)
29
+ ast (>= 1.1, < 3.0)
28
30
  slop (~> 3.4, >= 3.4.5)
29
31
  powerpack (0.0.9)
30
32
  rainbow (2.0.0)
31
- rake (10.2.2)
33
+ rake (10.3.2)
32
34
  rb-fsevent (0.9.4)
33
- rb-inotify (0.9.3)
35
+ rb-inotify (0.9.5)
34
36
  ffi (>= 0.5.0)
35
- reek (1.3.7)
36
- rainbow
37
- ruby2ruby (~> 2.0.8)
37
+ reek (1.3.8)
38
+ rainbow (>= 1.99, < 3.0)
39
+ ruby2ruby (>= 2.0.8, < 3.0)
38
40
  ruby_parser (~> 3.3)
39
41
  sexp_processor
40
- rerun (0.9.0)
41
- listen (~> 2.7)
42
- rubocop (0.20.1)
43
- json (>= 1.7.7, < 2)
44
- parser (~> 2.1.7)
42
+ rerun (0.10.0)
43
+ listen (~> 2.7, >= 2.7.3)
44
+ rubocop (0.26.0)
45
+ astrolabe (~> 1.3)
46
+ parser (>= 2.2.0.pre.4, < 3.0)
45
47
  powerpack (~> 0.0.6)
46
48
  rainbow (>= 1.99.1, < 3.0)
47
49
  ruby-progressbar (~> 1.4)
48
- ruby-progressbar (1.4.2)
49
- ruby2ruby (2.0.8)
50
+ ruby-progressbar (1.5.1)
51
+ ruby2ruby (2.1.2)
50
52
  ruby_parser (~> 3.1)
51
53
  sexp_processor (~> 4.0)
52
- ruby_parser (3.5.0)
54
+ ruby_parser (3.6.2)
53
55
  sexp_processor (~> 4.1)
54
- sexp_processor (4.4.3)
55
- slop (3.5.0)
56
- timers (1.1.0)
56
+ sexp_processor (4.4.4)
57
+ slop (3.6.0)
58
+ timers (4.0.1)
59
+ hitimes
57
60
 
58
61
  PLATFORMS
59
62
  ruby
60
63
 
61
64
  DEPENDENCIES
65
+ bogus (~> 0.1.3)
62
66
  minitest (~> 5.0)
67
+ minitest-focus (~> 1.1)
63
68
  rake (~> 10.1)
64
69
  reek (~> 1.3)
65
- rerun (~> 0.9.0)
66
- rubocop (~> 0.20.0)
70
+ rerun (~> 0.10.0)
71
+ rubocop (~> 0.26.0)
67
72
  signore!
data/README.md CHANGED
@@ -68,18 +68,6 @@ Another aproach would be to create a named pipe that your email program reads fr
68
68
 
69
69
 
70
70
 
71
- Options
72
- -------
73
-
74
- signore supports a single option, `--database` (or `-d` for short), which can be used to use a non-default signature database location:
75
-
76
- $ signore prego -d spec/fixtures/signatures.yml
77
- Amateur fighter pilot ignores orders, listens to
78
- the voices in his head and slaughters thousands.
79
- [Star Wars ending explained]
80
-
81
-
82
-
83
71
  Properties
84
72
  ----------
85
73
 
data/Rakefile CHANGED
@@ -2,9 +2,9 @@ require 'rake/testtask'
2
2
  require 'reek/rake/task'
3
3
  require 'rubocop/rake_task'
4
4
 
5
- task default: %i[spec rubocop reek]
5
+ task default: %i(spec rubocop reek)
6
6
 
7
- Rake::TestTask.new :spec do |task|
7
+ Rake::TestTask.new(:spec) do |task|
8
8
  task.test_files = FileList['spec/**/*_spec.rb']
9
9
  task.warning = true
10
10
  end
@@ -15,4 +15,6 @@ Reek::Rake::Task.new do |task|
15
15
  task.reek_opts = '--quiet'
16
16
  end
17
17
 
18
- Rubocop::RakeTask.new
18
+ RuboCop::RakeTask.new do |task|
19
+ task.options << '--display-cop-names'
20
+ end
data/bin/signore CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require_relative '../lib/signore'
4
- Signore::Executable.new.run
4
+ Signore::CLI.new.run
data/config/reek.yml CHANGED
@@ -1,14 +1,17 @@
1
1
  DuplicateMethodCall:
2
2
  exclude:
3
- - Signore::Database#find
3
+ - Signore::SigFinder#find_tagged
4
+
5
+ FeatureEnvy:
6
+ exclude:
7
+ - Signore::Settings#forbidden
4
8
 
5
9
  IrresponsibleModule:
6
10
  enabled: false
7
11
 
8
12
  NestedIterators:
9
- exclude:
10
- - Signore::Database#find
11
13
  max_allowed_nesting: 2
12
14
 
13
- UnusedParameters:
14
- enabled: false
15
+ UtilityFunction:
16
+ exclude:
17
+ - Signore::Settings#db_path
@@ -0,0 +1,28 @@
1
+ require_relative 'database'
2
+ require_relative 'settings'
3
+ require_relative 'sig_from_stream'
4
+
5
+ module Signore
6
+ class CLI
7
+ def initialize(args = ARGV, db: Database.new)
8
+ @settings = Settings.new(args)
9
+ @db = db
10
+ end
11
+
12
+ def run(input: $stdin)
13
+ case settings.action
14
+ when 'prego'
15
+ puts db.find(tags: settings.tags)
16
+ when 'pronto'
17
+ sig = SigFromStream.sig_from(input, tags: settings.tags)
18
+ db << sig
19
+ puts sig
20
+ else
21
+ abort 'usage: signore prego|pronto [tag, …]'
22
+ end
23
+ end
24
+
25
+ attr_reader :db, :settings
26
+ private :db, :settings
27
+ end
28
+ end
@@ -1,24 +1,37 @@
1
- module Signore class Database
2
- def initialize path
3
- @store = YAML::Store.new path
4
- end
1
+ require 'fileutils'
2
+ require 'yaml/store'
3
+ require_relative 'settings'
4
+ require_relative 'sig_finder'
5
+ require_relative 'tags'
5
6
 
6
- def << sig
7
- store.transaction do
8
- store['signatures'] ||= []
9
- store['signatures'] << sig
7
+ module Signore
8
+ class Database
9
+ def initialize(path: Settings.new.db_path, sig_finder: SigFinder)
10
+ @path = path
11
+ @sig_finder = sig_finder
12
+ initialise_store if path.zero? or not path.exist?
13
+ @store = YAML::Store.new(path)
10
14
  end
11
- end
12
15
 
13
- def find forbidden_tags: [], random: Random.new, required_tags: []
14
- store.transaction true do
15
- store['signatures']
16
- .select { |sig| required_tags.all? { |tag| sig.tagged_with? tag } }
17
- .reject { |sig| forbidden_tags.any? { |tag| sig.tagged_with? tag } }
18
- .sample random: random
16
+ def <<(sig)
17
+ store.transaction { store['signatures'] << sig }
18
+ sig
19
+ end
20
+
21
+ def find(tags: Tags.new)
22
+ sigs = store.transaction(true) { store['signatures'] }
23
+ sig_finder.find(sigs, tags: tags)
19
24
  end
20
- end
21
25
 
22
- attr_reader :store
23
- private :store
24
- end end
26
+ attr_reader :path, :sig_finder, :store
27
+ private :path, :sig_finder, :store
28
+
29
+ private
30
+
31
+ def initialise_store
32
+ FileUtils.mkdir_p path.dirname
33
+ FileUtils.touch path
34
+ YAML::Store.new(path).transaction { |store| store['signatures'] = [] }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,36 @@
1
+ require 'pathname'
2
+ require_relative 'tags'
3
+
4
+ module Signore
5
+ class Settings
6
+ def initialize(args = [])
7
+ @args = args
8
+ end
9
+
10
+ def action
11
+ args.first
12
+ end
13
+
14
+ def db_path
15
+ db_dir = ENV.fetch('XDG_DATA_HOME') { File.expand_path('~/.local/share') }
16
+ Pathname.new("#{db_dir}/signore/signatures.yml")
17
+ end
18
+
19
+ def tags
20
+ Tags.new(forbidden: forbidden, required: required)
21
+ end
22
+
23
+ attr_reader :args
24
+ private :args
25
+
26
+ private
27
+
28
+ def forbidden
29
+ args[1..-1].select { |tag| tag.start_with?('~') }.map { |tag| tag[1..-1] }
30
+ end
31
+
32
+ def required
33
+ args[1..-1].reject { |tag| tag.start_with?('~') }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'tags'
2
+
3
+ module Signore
4
+ class SigFinder
5
+ def self.find(sigs, random: Random.new, tags: Tags.new)
6
+ new(sigs, random: random).find_tagged(tags: tags)
7
+ end
8
+
9
+ def initialize(sigs, random: Random.new)
10
+ @random = random
11
+ @sigs = sigs
12
+ end
13
+
14
+ def find_tagged(tags: Tags.new)
15
+ sigs
16
+ .select { |sig| tags.required.all? { |tag| sig.tagged_with?(tag) } }
17
+ .reject { |sig| tags.forbidden.any? { |tag| sig.tagged_with?(tag) } }
18
+ .sample(random: random) or Signature.new
19
+ end
20
+
21
+ attr_reader :random, :sigs
22
+ private :random, :sigs
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ require_relative 'signature'
2
+ require_relative 'tags'
3
+
4
+ module Signore
5
+ class SigFromStream
6
+ Params = Struct.new(*%i(text author subject source))
7
+
8
+ def self.sig_from(input, tags: Tags.new)
9
+ new(input, tags: tags).to_sig
10
+ end
11
+
12
+ def initialize(input, tags: Tags.new)
13
+ @input = input
14
+ @tags = tags
15
+ end
16
+
17
+ def to_sig
18
+ Signature.new(params.text, author: params.author, source: params.source,
19
+ subject: params.subject, tags: tags.required)
20
+ end
21
+
22
+ attr_reader :input, :tags
23
+ private :input, :tags
24
+
25
+ private
26
+
27
+ def get_param(param)
28
+ puts "#{param}?"
29
+ ''.tap do |value|
30
+ value << input.gets until value.lines.to_a.last == "\n"
31
+ end.strip
32
+ end
33
+
34
+ def params
35
+ @params ||= Params.new(*Params.members.map { |name| get_param(name) })
36
+ end
37
+ end
38
+ end
@@ -1,39 +1,37 @@
1
- module Signore Signature = Struct.new(*%i(text author source subject tags)) do
2
- class << self
3
- undef :[]
4
- end
5
-
6
- def self.[] author: nil, source: nil, subject: nil, tags: nil, text: nil
7
- new text, author, source, subject, tags
8
- end
9
-
10
- def tagged_with? tag
11
- tags and tags.include? tag
12
- end
13
-
14
- def to_s
15
- wrapped = LovelyRufus::TextWrapper.wrap text.gsub("\n", "\n\n"), width: 80
16
- squeezed = wrapped.gsub("\n\n", "\n").chomp
17
- squeezed + meta_for(squeezed)
18
- end
19
-
20
- private
21
-
22
- def indent_size_for text
23
- indent = text_width(text) - meta.size - 2
24
- indent < 0 ? 0 : indent
25
- end
26
-
27
- def meta
28
- stem = [author, subject].compact.join ' '
29
- stem.empty? ? "#{source}" : [stem, source].compact.join(', ')
30
- end
31
-
32
- def meta_for text
33
- meta.empty? ? '' : "\n#{' ' * indent_size_for(text)}[#{meta}]"
34
- end
35
-
36
- def text_width text
37
- text.split("\n").map(&:size).max
38
- end
39
- end end
1
+ require 'lovely_rufus'
2
+
3
+ module Signore
4
+ Signature = Struct.new(*%i(text author source subject tags)) do
5
+ def initialize(text = '', author: nil, source: nil, subject: nil, tags: nil)
6
+ super text, author, source, subject, tags
7
+ each_pair { |key, value| self[key] = nil if value and value.empty? }
8
+ end
9
+
10
+ def tagged_with?(tag)
11
+ tags and tags.include?(tag)
12
+ end
13
+
14
+ def to_s
15
+ spaced = text.gsub("\n", "\n\n")
16
+ wrapped = LovelyRufus.wrap(spaced, width: 80)
17
+ squeezed = wrapped.gsub("\n\n", "\n").chomp
18
+ squeezed + meta_for(squeezed)
19
+ end
20
+
21
+ private
22
+
23
+ def indent_size_for(text)
24
+ indent = text.split("\n").map(&:size).max - meta.size - 2
25
+ indent < 0 ? 0 : indent
26
+ end
27
+
28
+ def meta
29
+ stem = [author, subject].compact.join(' ')
30
+ stem.empty? ? "#{source}" : [stem, source].compact.join(', ')
31
+ end
32
+
33
+ def meta_for(text)
34
+ meta.empty? ? '' : "\n#{' ' * indent_size_for(text)}[#{meta}]"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ module Signore
2
+ Tags = Struct.new(:forbidden, :required) do
3
+ def initialize(forbidden: [], required: [])
4
+ super forbidden, required
5
+ end
6
+ end
7
+ end
data/lib/signore.rb CHANGED
@@ -1,9 +1 @@
1
- require 'fileutils'
2
- require 'lovely_rufus'
3
- require 'optparse'
4
- require 'ostruct'
5
- require 'yaml/store'
6
-
7
- require_relative 'signore/database'
8
- require_relative 'signore/executable'
9
- require_relative 'signore/signature'
1
+ require_relative 'signore/cli'
data/signore.gemspec CHANGED
@@ -1,22 +1,25 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.author = 'Piotr Szotkowski'
3
- gem.description = 'signore helps manage email signatures and select random ones based on their tags'
3
+ gem.description = 'signore helps manage email signatures and select ' \
4
+ 'random ones based on their tags'
4
5
  gem.email = 'chastell@chastell.net'
5
6
  gem.homepage = 'http://github.com/chastell/signore'
6
7
  gem.license = 'AGPL-3.0'
7
8
  gem.name = 'signore'
8
9
  gem.summary = 'signore: an email signature manager/randomiser'
9
- gem.version = '0.2.2'
10
+ gem.version = '0.2.3'
10
11
 
11
12
  gem.files = `git ls-files -z`.split "\0"
12
- gem.executables = gem.files.grep(%r{^bin/}).map { |path| File.basename path }
13
- gem.test_files = gem.files.grep %r{^spec/.*\.rb$}
13
+ gem.executables = gem.files.grep(/^bin\//).map { |path| File.basename(path) }
14
+ gem.test_files = gem.files.grep(/^spec\/.*\.rb$/)
14
15
 
15
- gem.add_dependency 'lovely_rufus', '~> 0.1.2'
16
+ gem.add_dependency 'lovely_rufus', '~> 0.2.0'
16
17
 
17
- gem.add_development_dependency 'minitest', '~> 5.0'
18
- gem.add_development_dependency 'rake', '~> 10.1'
19
- gem.add_development_dependency 'reek', '~> 1.3'
20
- gem.add_development_dependency 'rerun', '~> 0.9.0'
21
- gem.add_development_dependency 'rubocop', '~> 0.20.0'
18
+ gem.add_development_dependency 'bogus', '~> 0.1.3'
19
+ gem.add_development_dependency 'minitest', '~> 5.0'
20
+ gem.add_development_dependency 'minitest-focus', '~> 1.1'
21
+ gem.add_development_dependency 'rake', '~> 10.1'
22
+ gem.add_development_dependency 'reek', '~> 1.3'
23
+ gem.add_development_dependency 'rerun', '~> 0.10.0'
24
+ gem.add_development_dependency 'rubocop', '~> 0.26.0'
22
25
  end
@@ -0,0 +1,92 @@
1
+ require 'pathname'
2
+ require 'stringio'
3
+ require 'tempfile'
4
+ require 'tmpdir'
5
+ require_relative '../spec_helper'
6
+ require_relative '../../lib/signore/cli'
7
+
8
+ module Signore
9
+ describe CLI do
10
+ describe '#run' do
11
+ it 'prints usage if no command is given' do
12
+ out = capture_io { -> { CLI.new([]).run }.must_raise SystemExit }.last
13
+ out.must_include 'usage: signore prego|pronto [tag, …]'
14
+ end
15
+
16
+ it 'prints usage if a bogus command is given' do
17
+ out = capture_io do
18
+ -> { CLI.new(['bogus']).run }.must_raise SystemExit
19
+ end.last
20
+ out.must_include 'usage: signore prego|pronto [tag, …]'
21
+ end
22
+
23
+ describe 'prego' do
24
+ let(:db) { Database.new(path: path) }
25
+ let(:path) { Pathname.new('spec/fixtures/signatures.yml') }
26
+
27
+ it 'prints a signature tagged with the provided tags' do
28
+ args = %w(prego tech programming)
29
+ out = capture_io { CLI.new(args, db: db).run }.first
30
+ sig = "// sometimes I believe compiler ignores all my comments\n"
31
+ out.must_equal sig
32
+ end
33
+
34
+ it 'prints a signature based on allowed and forbidden tags' do
35
+ args = %w(prego ~programming tech ~security)
36
+ out = capture_io { CLI.new(args, db: db).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]
40
+ end
41
+ end
42
+ end
43
+
44
+ describe 'pronto' do
45
+ let(:db) { Database.new(path: Pathname.new(Tempfile.new('').path)) }
46
+
47
+ it 'asks about signature parts and saves resulting signature' do
48
+ input = StringIO.new <<-end.dedent
49
+ The Wikipedia page on ADHD is like 20 pages long. That’s just cruel.
50
+
51
+ Mark Pilgrim\n\n\n
52
+ end
53
+ args = %w(pronto Wikipedia ADHD)
54
+ out = capture_io { CLI.new(args, db: db).run input: input }.first
55
+ out.must_equal <<-end.dedent
56
+ text?
57
+ author?
58
+ subject?
59
+ source?
60
+ The Wikipedia page on ADHD is like 20 pages long. That’s just cruel.
61
+ [Mark Pilgrim]
62
+ end
63
+ args = %w(prego Wikipedia ADHD)
64
+ out = capture_io { CLI.new(args, db: db).run }.first
65
+ out.must_equal <<-end.dedent
66
+ The Wikipedia page on ADHD is like 20 pages long. That’s just cruel.
67
+ [Mark Pilgrim]
68
+ end
69
+ end
70
+
71
+ it 'handles multi-line signatures' do
72
+ input = StringIO.new <<-end.dedent
73
+ ‘You’ve got an interesting accent. Subtle. I can’t place it.’
74
+ ‘It’s text-to-speech… I was raised by smartphones.’
75
+
76
+ Patrick Ewing\n\n\n
77
+ end
78
+ io = capture_io { CLI.new(['pronto'], db: db).run input: input }
79
+ io.first.must_equal <<-end.dedent
80
+ text?
81
+ author?
82
+ subject?
83
+ source?
84
+ ‘You’ve got an interesting accent. Subtle. I can’t place it.’
85
+ ‘It’s text-to-speech… I was raised by smartphones.’
86
+ [Patrick Ewing]
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end