signore 0.2.2 → 0.2.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
  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