monotes 0.0.1 → 0.0.2

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: 1ec9e3b9a6bd126d478f6ef382f615939bc1a369
4
- data.tar.gz: e499f5aea8d926a03ba5b42d3b3883f98b72c491
3
+ metadata.gz: 1230cb7946ec444383f76e8d171a2788fc1f081e
4
+ data.tar.gz: 0a44dc0b60cb8cb6e6b834cd771d26e0ff22e8dd
5
5
  SHA512:
6
- metadata.gz: 919d10152d8b060516d5375fd82f75bd558dab0ec13f0636909d27e972d19385e278cc3f61e27d45ba85cd2b7500c752ea4e900e8af6880f81b68584cfa6950a
7
- data.tar.gz: 06e90c47a48ff5bc1c876d6e2de095f87cccea58afa3bcbfa9707fee8e85c2cf1ddbdef591f5e28e907149dc39306081e216ee83a4202dc3d32b8cc91035d937
6
+ metadata.gz: b22e8efb7f377518417941e0972cf0528b3d651a17096dfd3c79ee5b38b405c444c600f1e3d68c5d604d810ae93dfabc63c4b654a1208c8c067f4f30b864f389
7
+ data.tar.gz: 184e1e2ab900e0c9bd95204cbfea993330044971069292b75765c2f54ce100a4960b887ad152044b5697b6e3d4cd864a320e5fa73cd5715830191ebd19af1d5e
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ -fd
2
+ --color
3
+ --tty
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "2.0.0"
5
+ - "2.1.2"
6
+ - jruby-19mode # JRuby in 1.9 mode
7
+ - rbx
8
+ env:
9
+ - RUN=rake
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
+ [![Build
2
+ Status](https://travis-ci.org/schultyy/monotes.svg?branch=master)](https://travis-ci.org/schultyy/monotes)
1
3
  # Monotes
2
4
 
3
- TODO: Write a gem description
5
+ Monotes is a GitHub Issues commandline client.
4
6
 
5
7
  ## Installation
6
8
 
@@ -18,7 +20,34 @@ Or install it yourself as:
18
20
 
19
21
  ## Usage
20
22
 
21
- TODO: Write usage instructions here
23
+ ### Login to GitHub
24
+
25
+ ```bash
26
+ $ monotes login
27
+ Username: <your usename>
28
+ Password: <Password>
29
+ ```
30
+
31
+ ### Login with Two-Factor Authentication
32
+
33
+ ```bash
34
+ $ monotes login
35
+ Username: <your usename>
36
+ Password: <Password>
37
+ Your 2FA token: <Token>
38
+ ```
39
+
40
+ ### Download issues for repository
41
+
42
+ ```bash
43
+ $ monotes download 'schultyy/monotes'
44
+ ```
45
+
46
+ ### Browse downloaded issues
47
+
48
+ ```bash
49
+ $ monotes show 'schultyy/monotes'
50
+ ```
22
51
 
23
52
  ## Contributing
24
53
 
data/Rakefile CHANGED
@@ -1,2 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+ RSpec::Core::RakeTask.new(:spec)
2
4
 
5
+ task :default => [:spec]
data/bin/monotes ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'monotes'
3
+
4
+ Monotes::CLI::Application.start(ARGV)
data/lib/monotes.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require "monotes/version"
2
+ require "monotes/cli/application"
2
3
 
3
4
  module Monotes
4
- # Your code goes here...
5
5
  end
@@ -0,0 +1,28 @@
1
+ require 'monotes/app_directory'
2
+
3
+ module Monotes
4
+ module IO
5
+ class FSDelegate
6
+ include Monotes::AppDirectory
7
+
8
+ #
9
+ # issues: Issues represented as Hash
10
+ #
11
+ def save(username, repository, issues)
12
+ if !File.directory?(app_path)
13
+ Dir.mkdir(app_path)
14
+ end
15
+ user_folder = File.join(app_path, username)
16
+ Dir.mkdir(user_folder) if !File.directory?(user_folder)
17
+ File.open(File.join(user_folder, "#{repository}.yaml"), "w") do |handle|
18
+ handle.write(issues.to_yaml)
19
+ end
20
+ end
21
+
22
+ def load(username, repository)
23
+ abs_path = File.join(app_path, username, "#{repository}.yaml")
24
+ YAML.load_file(abs_path)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module Monotes
2
+ module AppDirectory
3
+ def app_path
4
+ File.expand_path("~/.monotes")
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ module Monotes
2
+ class Authenticator
3
+ ACCESS_NOTE = "Monotes access token"
4
+ def initialize(api_client_klass)
5
+ @api_client_klass = api_client_klass
6
+ end
7
+
8
+ def get_oauth_token(username, password, &acquire_two_fa)
9
+ api_client = @api_client_klass.new(:login => username, :password => password)
10
+ begin
11
+ api_client.create_authorization(:scopes => scopes, :note => ACCESS_NOTE)
12
+ rescue Octokit::OneTimePasswordRequired
13
+ two_fa_token = yield acquire_two_fa
14
+ api_client.create_authorization(:scopes => scopes, :note => ACCESS_NOTE,
15
+ :headers => { "X-GitHub-OTP" => two_fa_token })
16
+ end
17
+ end
18
+ private
19
+ def scopes
20
+ ["user", "repo"]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ require 'monotes/app_directory'
2
+
3
+ module Monotes
4
+ class BodyText
5
+ include Monotes::AppDirectory
6
+ FILENAME = "ISSUE_BODY_TEXT"
7
+
8
+ def initialize(title)
9
+ @title = title
10
+ end
11
+
12
+ def read
13
+ File.read(path)
14
+ end
15
+
16
+ def flush
17
+ File.delete(path)
18
+ end
19
+
20
+ def create_issue
21
+ edit_success = system "vim #{path}"
22
+ if edit_success
23
+ body_text = read
24
+ flush
25
+ Monotes::Models::Issue.new(:title => @title, :body => body_text)
26
+ else
27
+ nil
28
+ end
29
+ end
30
+
31
+ private
32
+ def path
33
+ File.join(app_path, FILENAME)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,122 @@
1
+ require 'thor'
2
+ require 'yaml'
3
+ require 'netrc'
4
+ require 'octokit'
5
+ require 'monotes/authenticator'
6
+ require 'monotes/issue_download'
7
+ require 'monotes/issue_repository'
8
+ require 'monotes/models/issue'
9
+ require 'monotes/app_directory'
10
+ require 'monotes/body_text'
11
+ require 'monotes/sync_list'
12
+
13
+ module Monotes
14
+ module CLI
15
+ class Application < Thor
16
+ include Monotes::AppDirectory
17
+
18
+ desc "login", "Login into GitHub"
19
+ def login
20
+ print "Username > "
21
+ username = STDIN.gets.chomp
22
+ validate!("username", username)
23
+ print "Password > "
24
+ password = STDIN.noecho(&:gets).chomp
25
+ validate!("password", password)
26
+ STDOUT.puts "\n"
27
+ authenticator = Monotes::Authenticator.new(Octokit::Client)
28
+ begin
29
+ oauth_token = authenticator.get_oauth_token(username, password) do
30
+ print "Two-Factor token > "
31
+ token = STDIN.gets.chomp
32
+ validate!("Two-Factor token", token)
33
+ token
34
+ end
35
+ rescue Octokit::Unauthorized => unauthorized
36
+ STDERR.puts "Unauthorized: #{unauthorized.message}"
37
+ exit 77
38
+ rescue Exception => e
39
+ fatal!(e)
40
+
41
+ else
42
+ write_to_netrc(username, oauth_token.token)
43
+ end
44
+ end
45
+
46
+ desc "download REPOSITORY", "Download issues for a repository"
47
+ def download(repository)
48
+ STDOUT.puts "Downloading issues for #{repository}..."
49
+ downloader = Monotes::IssueDownload.new(Octokit)
50
+ begin
51
+ issues = downloader.download(repository)
52
+ rescue Exception => exc
53
+ fatal!(exc)
54
+ end
55
+ repository = Monotes::IssueRepository.build(repository: repository)
56
+ repository.save(issues)
57
+ end
58
+
59
+ desc "show REPOSITORY", "Show downloaded issues"
60
+ def show(repository_name)
61
+ repository = Monotes::IssueRepository.build(repository: repository_name)
62
+ issues = repository.load
63
+ issues.map do |issue|
64
+ if issue.unsynced?
65
+ STDOUT.puts "(new) - #{issue.title}"
66
+ else
67
+ STDOUT.puts "#{issue.number} - #{issue.title}"
68
+ end
69
+ end
70
+ end
71
+
72
+ desc "create REPOSITORY TITLE", "Creates a new local issue"
73
+ def create(repository_name, title)
74
+ text = Monotes::BodyText.new(title)
75
+ issue = text.create_issue
76
+ repository = Monotes::IssueRepository.build(repository: repository_name)
77
+ repository.append(issue)
78
+ end
79
+
80
+ desc "sync REPOSITORY", "Synchronizes local issues with GitHub"
81
+ def sync(repository_name)
82
+ repository = Monotes::IssueRepository.build(repository: repository_name)
83
+ issues = repository.load
84
+ already_synced = issues.reject { |i| i.unsynced? }
85
+ adapter = Octokit::Client.new(netrc: true)
86
+ begin
87
+ sync_list = Monotes::SyncList.new(list: issues, repo: repository_name, adapter: adapter)
88
+ synced = sync_list.sync do |issue|
89
+ STDOUT.puts "Synced issue #{issue.title}"
90
+ end
91
+ rescue Exception => exc
92
+ fatal!(exc)
93
+ end
94
+ repository.save(already_synced.concat(synced))
95
+ end
96
+
97
+ private
98
+
99
+ def fatal!(exc)
100
+ STDERR.puts "FATAL: #{exc.message}"
101
+ exit 74
102
+ end
103
+
104
+ def validate!(name, param)
105
+ if param.nil? || param.empty?
106
+ STDERR.puts "Fatal: #{name} cannot be empty"
107
+ exit 74
108
+ end
109
+ end
110
+
111
+ def split_repository_identifier(repo)
112
+ repo.split('/')
113
+ end
114
+
115
+ def write_to_netrc(username, token)
116
+ netrc_handle = Netrc.read
117
+ netrc_handle["api.github.com"] = username, token
118
+ netrc_handle.save
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,17 @@
1
+ require 'monotes/models/issue'
2
+
3
+ module Monotes
4
+ class IssueDownload
5
+ def initialize(api_client)
6
+ @api_client = api_client
7
+ end
8
+
9
+ def download(repository)
10
+ raise ArgumentError, 'repository must not be nil' if repository.nil?
11
+
12
+ @api_client.list_issues(repository).map do |issue|
13
+ Monotes::Models::Issue.new(issue)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,37 @@
1
+ require 'yaml'
2
+ require 'monotes/IO/fs_delegate'
3
+
4
+ module Monotes
5
+ class IssueRepository
6
+
7
+ def initialize(args)
8
+ @context = args.fetch(:context)
9
+ @repository = args.fetch(:repository)
10
+ end
11
+
12
+ def save(args)
13
+ issues = Array(args).map do |issue|
14
+ issue.to_hash
15
+ end
16
+ @context.save(*@repository.split('/'), issues)
17
+ end
18
+
19
+ def append(new_issue)
20
+ raise ArgumentError, 'issue must not be nil' if new_issue.nil?
21
+ issues = load
22
+ issues << new_issue
23
+ save(issues)
24
+ end
25
+
26
+ def load
27
+ @context.load(*@repository.split('/')).map do |issue_hash|
28
+ Monotes::Models::Issue.new(issue_hash)
29
+ end
30
+ end
31
+
32
+ def self.build(args)
33
+ context = Monotes::IO::FSDelegate.new
34
+ Monotes::IssueRepository.new(args.merge(:context => context))
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ require 'virtus'
2
+
3
+ module Monotes
4
+ module Models
5
+ class Issue
6
+ include Virtus.model
7
+ attribute :url, String
8
+ attribute :id, Fixnum, :default => 0
9
+ attribute :number, Fixnum, :default => 0
10
+ attribute :title, String
11
+ attribute :state, String
12
+ attribute :created_at, DateTime
13
+ attribute :updated_at, DateTime
14
+ attribute :body, String
15
+
16
+ def unsynced?
17
+ number == 0
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'monotes/models/issue'
2
+
3
+ module Monotes
4
+ class SyncList
5
+
6
+ def initialize(args)
7
+ @list = args.fetch(:list)
8
+ @adapter = args.fetch(:adapter)
9
+ @repository = args.fetch(:repo)
10
+ end
11
+
12
+ def sync
13
+ unsynced = @list.find_all {|issue| issue.unsynced? }
14
+ unsynced.map do |issue|
15
+ result = @adapter.create_issue(@repository, issue.title, issue.body)
16
+ yield(result) if block_given?
17
+ Monotes::Models::Issue.new(result.to_hash)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Monotes
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/monotes.gemspec CHANGED
@@ -18,6 +18,12 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_dependency "thor"
22
+ spec.add_dependency "octokit", "~> 3.0"
23
+ spec.add_dependency "netrc"
24
+ spec.add_dependency "virtus"
21
25
  spec.add_development_dependency "bundler", "~> 1.6"
22
26
  spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "factory_girl", "~> 4.4.0"
23
29
  end
data/spec/factories.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'monotes/models/issue'
2
+
3
+ FactoryGirl.define do
4
+ factory :issue, class: Monotes::Models::Issue do
5
+ end
6
+ end
@@ -0,0 +1,29 @@
1
+ require 'factory_girl'
2
+
3
+ RSpec.configure do |config|
4
+ config.filter_run :focus
5
+ config.run_all_when_everything_filtered = true
6
+
7
+ if config.files_to_run.one?
8
+ config.default_formatter = 'doc'
9
+ end
10
+
11
+ config.profile_examples = 10
12
+
13
+ config.order = :random
14
+
15
+ Kernel.srand config.seed
16
+
17
+ config.include FactoryGirl::Syntax::Methods
18
+ FactoryGirl.definition_file_paths = [File.expand_path('../factories', __FILE__)]
19
+ FactoryGirl.find_definitions
20
+
21
+ config.expect_with :rspec do |expectations|
22
+ expectations.syntax = :expect
23
+ end
24
+
25
+ config.mock_with :rspec do |mocks|
26
+ mocks.syntax = :expect
27
+ mocks.verify_partial_doubles = true
28
+ end
29
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'monotes/authenticator'
3
+ require 'octokit'
4
+
5
+ describe Monotes::Authenticator do
6
+ let(:api_client_mock_class) { double('Octokit::Client') }
7
+ let(:api_client_mock) { double('Octokit::Client instance') }
8
+ let(:username) { 'Jim' }
9
+ let(:password) { 'passw' }
10
+ let(:expected_oauth_token) { 'expected_oauth_token' }
11
+
12
+ context 'without 2FA' do
13
+ subject(:authenticator) { Monotes::Authenticator.new(api_client_mock_class) }
14
+
15
+ before do
16
+ allow(api_client_mock_class).to receive(:new).with(any_args).and_return(api_client_mock)
17
+ allow(api_client_mock).to receive(:create_authorization).with(any_args).and_return(expected_oauth_token)
18
+ end
19
+
20
+ it 'authenticates without asking for 2-FA token' do
21
+ actual_token = authenticator.get_oauth_token(username, password) { raise "2-FA Block was called" }
22
+ expect(actual_token).to eq expected_oauth_token
23
+ end
24
+ end
25
+ context 'with 2FA' do
26
+ let(:two_fa_token) { '2-factor token' }
27
+ subject(:authenticator) { Monotes::Authenticator.new(api_client_mock_class) }
28
+
29
+ before do
30
+ params = { :scopes => ["user", "repo"], :note => Monotes::Authenticator::ACCESS_NOTE }
31
+ params_with_2fa = params.merge(:headers => { "X-GitHub-OTP" => two_fa_token })
32
+ allow(api_client_mock_class).to receive(:new).with(any_args).and_return(api_client_mock)
33
+ allow(api_client_mock).to receive(:create_authorization).with(params).and_raise(Octokit::OneTimePasswordRequired)
34
+ allow(api_client_mock).to receive(:create_authorization).with(params_with_2fa).and_return(expected_oauth_token)
35
+ end
36
+
37
+ it 'authenticates and asks for 2-FA token' do
38
+ block_called = false
39
+ authenticator.get_oauth_token(username, password) { block_called = true; two_fa_token }
40
+ expect(block_called).to be true
41
+ end
42
+
43
+ it 'authenticates with 2-FA token' do
44
+ actual_token = authenticator.get_oauth_token(username, password) { two_fa_token }
45
+ expect(actual_token).to eq expected_oauth_token
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'monotes/issue_download'
3
+
4
+ describe Monotes::IssueDownload do
5
+ let(:octo_mock) { double('Octokit') }
6
+ let(:issue_list) { [attributes_for(:issue)] }
7
+ let(:repository) { 'franz/franz-repo' }
8
+ subject(:downloader) { Monotes::IssueDownload.new(octo_mock) }
9
+
10
+ context '#download' do
11
+ before do
12
+ allow(octo_mock).to receive(:list_issues).and_return(issue_list)
13
+ end
14
+ it 'returns a list of issues' do
15
+ expect(downloader.download(repository).length).to be > 0
16
+ end
17
+
18
+ it 'raises error when repository is nil' do
19
+ expect { downloader.download(nil) }.to raise_error(ArgumentError)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+ require 'monotes/issue_repository'
4
+
5
+ describe Monotes::IssueRepository do
6
+ let(:issues) { build_list(:issue, 2) }
7
+ let(:context) { double('fs context') }
8
+ let(:repository_name) { 'franz/franz-seins' }
9
+ let(:issue) { issues.first }
10
+ subject(:repository) { Monotes::IssueRepository.new(repository: repository_name, context: context) }
11
+
12
+ context '#initialize' do
13
+ it 'accepts context and repository' do
14
+ expect { Monotes::IssueRepository.new(context: context, repository: repository_name) }.to_not raise_error
15
+ end
16
+
17
+ it 'raises error when context not passed' do
18
+ expect { Monotes::IssueRepository.new(repository: repository_name) }.to raise_error
19
+ end
20
+
21
+ it 'raises error when repository not passed' do
22
+ expect { Monotes::IssueRepository.new(context: context) }.to raise_error
23
+ end
24
+ end
25
+
26
+ context '#save' do
27
+ before do
28
+ allow(context).to receive(:save)
29
+ end
30
+
31
+ it 'saves a single issue' do
32
+ repository.save(issue)
33
+ expect(context).to have_received(:save).with('franz', 'franz-seins', [issue.to_hash])
34
+ end
35
+ end
36
+
37
+ context '#load' do
38
+ before do
39
+ allow(context).to receive(:load).and_return(attributes_for_list(:issue, 2))
40
+ end
41
+
42
+ it 'returns a list of issues' do
43
+ expect(repository.load.length).to be > 0
44
+ end
45
+
46
+ context 'result set' do
47
+ context 'element' do
48
+ it 'is of type Issue' do
49
+ issue = repository.load.first
50
+ expect(issue.class).to eq Monotes::Models::Issue
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ context '#append' do
57
+ before do
58
+ allow(context).to receive(:save)
59
+ allow(context).to receive(:load).and_return(attributes_for_list(:issue, 1))
60
+ repository.append(build(:issue))
61
+ end
62
+
63
+ it 'loads issues' do
64
+ expect(context).to have_received(:load)
65
+ end
66
+
67
+ it 'saves with appended issue' do
68
+ expect(context).to have_received(:save)
69
+ end
70
+
71
+ it 'raises error when issue is nil' do
72
+ expect { repository.append(nil) }.to raise_error(ArgumentError)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+ require 'monotes/models/issue'
3
+ require 'monotes/sync_list'
4
+
5
+ describe Monotes::SyncList do
6
+ let(:issue) { Monotes::Models::Issue.new }
7
+ let(:adapter) { double('OctoKit') }
8
+ let(:repo_name) { 'alice/example' }
9
+
10
+ context '#initialize' do
11
+ it 'accepts a list of issues' do
12
+ expect do
13
+ Monotes::SyncList.new(list: build_list(:issue, 1),
14
+ adapter: adapter,
15
+ repo: repo_name)
16
+ end.to_not raise_error
17
+ end
18
+
19
+ it 'raises error if no list was passed' do
20
+ expect { Monotes::SyncList.new(adapter: adapter, repo: repo_name) }.to raise_error
21
+ end
22
+
23
+ it 'raises error if no adapter was passed' do
24
+ expect { Monotes::SyncList.new(list: [issue], repo: repo_name) }.to raise_error
25
+ end
26
+
27
+ it 'raises error if no repository name was passed' do
28
+ expect { Monotes::SyncList.new(list: [issue], adapter: adapter) }.to raise_error
29
+ end
30
+ end
31
+
32
+ context '#sync' do
33
+ let(:unsynced_issues) { build_list(:issue, 1, number: 0, title: 'foo', body:'bar') }
34
+ let(:synced_issues) { build_list(:issue, 1, number: 45, title: 'baz', body:'yadda yadda') }
35
+ let(:issue_result) { attributes_for(:issue, number: 1, id: 1) }
36
+
37
+ before(:each) do
38
+ allow(adapter).to receive(:create_issue) { issue_result }
39
+ end
40
+
41
+ context 'with unsynced issues' do
42
+ subject(:sync_list) { Monotes::SyncList.new(list: unsynced_issues, adapter: adapter, repo: repo_name) }
43
+
44
+ it 'calls adapter to create issue' do
45
+ sync_list.sync
46
+ expect(adapter).to have_received(:create_issue).with(repo_name, 'foo', 'bar')
47
+ end
48
+
49
+ it 'calls block for each issue' do
50
+ block_called = false
51
+ sync_list.sync { |issue| block_called = true }
52
+ expect(block_called).to be true
53
+ end
54
+
55
+ it 'calls block with result from adapter call' do
56
+ block_result = nil
57
+ sync_list.sync { |issue| block_result = issue }
58
+ expect(block_result).to eq issue_result
59
+ end
60
+
61
+ context 'after sync' do
62
+ context 'issue' do
63
+ it 'has number' do
64
+ result = sync_list.sync.first
65
+ expect(result.unsynced?).to be false
66
+ end
67
+ it 'has id' do
68
+ result = sync_list.sync.first
69
+ expect(result.id).to_not eq 0
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'with synced and unsynced issues' do
76
+ subject(:sync_list) { Monotes::SyncList.new(list: unsynced_issues.concat(synced_issues), adapter: adapter, repo: repo_name) }
77
+
78
+ it 'calls adapter only for unsynced issue' do
79
+ sync_list.sync
80
+ expect(adapter).to have_received(:create_issue).with(repo_name, 'foo', 'bar').once
81
+ end
82
+ end
83
+ end
84
+ end
metadata CHANGED
@@ -1,15 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: monotes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Schulte
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-17 00:00:00.000000000 Z
11
+ date: 2014-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: octokit
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: netrc
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: virtus
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
13
69
  - !ruby/object:Gem::Dependency
14
70
  name: bundler
15
71
  requirement: !ruby/object:Gem::Requirement
@@ -38,22 +94,69 @@ dependencies:
38
94
  - - ">="
39
95
  - !ruby/object:Gem::Version
40
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: factory_girl
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 4.4.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 4.4.0
41
125
  description: GitHub Issues commandline client
42
126
  email:
43
127
  - schulte@unexpected-co.de
44
- executables: []
128
+ executables:
129
+ - monotes
45
130
  extensions: []
46
131
  extra_rdoc_files: []
47
132
  files:
48
133
  - ".gitignore"
134
+ - ".rspec"
135
+ - ".travis.yml"
49
136
  - Gemfile
50
137
  - LICENSE
51
138
  - LICENSE.txt
52
139
  - README.md
53
140
  - Rakefile
141
+ - bin/monotes
54
142
  - lib/monotes.rb
143
+ - lib/monotes/IO/fs_delegate.rb
144
+ - lib/monotes/app_directory.rb
145
+ - lib/monotes/authenticator.rb
146
+ - lib/monotes/body_text.rb
147
+ - lib/monotes/cli/application.rb
148
+ - lib/monotes/issue_download.rb
149
+ - lib/monotes/issue_repository.rb
150
+ - lib/monotes/models/issue.rb
151
+ - lib/monotes/sync_list.rb
55
152
  - lib/monotes/version.rb
56
153
  - monotes.gemspec
154
+ - spec/factories.rb
155
+ - spec/spec_helper.rb
156
+ - spec/unit/authenticator_spec.rb
157
+ - spec/unit/issue_download_spec.rb
158
+ - spec/unit/issue_repository_spec.rb
159
+ - spec/unit/sync_list_spec.rb
57
160
  homepage: https://github.com/schultyy/monotes
58
161
  licenses:
59
162
  - MIT
@@ -78,5 +181,11 @@ rubygems_version: 2.1.11
78
181
  signing_key:
79
182
  specification_version: 4
80
183
  summary: GitHub Issues commandline client
81
- test_files: []
184
+ test_files:
185
+ - spec/factories.rb
186
+ - spec/spec_helper.rb
187
+ - spec/unit/authenticator_spec.rb
188
+ - spec/unit/issue_download_spec.rb
189
+ - spec/unit/issue_repository_spec.rb
190
+ - spec/unit/sync_list_spec.rb
82
191
  has_rdoc: