redpomo 0.0.1
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.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +8 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/redpomo +5 -0
- data/lib/redpomo/cli.rb +72 -0
- data/lib/redpomo/config.rb +50 -0
- data/lib/redpomo/entries_printer.rb +33 -0
- data/lib/redpomo/entry.rb +49 -0
- data/lib/redpomo/file_cache.rb +40 -0
- data/lib/redpomo/fuzzy_converter.rb +68 -0
- data/lib/redpomo/issue.rb +24 -0
- data/lib/redpomo/null_cache.rb +9 -0
- data/lib/redpomo/numeric_ext.rb +29 -0
- data/lib/redpomo/puller.rb +35 -0
- data/lib/redpomo/pusher.rb +59 -0
- data/lib/redpomo/task.rb +88 -0
- data/lib/redpomo/task_list.rb +50 -0
- data/lib/redpomo/tracker.rb +108 -0
- data/lib/redpomo/version.rb +3 -0
- data/lib/redpomo.rb +15 -0
- data/redpomo.gemspec +31 -0
- data/spec/file_cache_spec.rb +17 -0
- data/spec/fixtures/cassettes/cli_close.yml +51 -0
- data/spec/fixtures/cassettes/cli_pull.yml +1222 -0
- data/spec/fixtures/cassettes/cli_push.yml +109 -0
- data/spec/fixtures/cassettes/close_issue.yml +50 -0
- data/spec/fixtures/cassettes/issues.yml +449 -0
- data/spec/fixtures/close_results.txt +2 -0
- data/spec/fixtures/printer_output.txt +16 -0
- data/spec/fixtures/pull_results.txt +20 -0
- data/spec/fixtures/timelog.csv +6 -0
- data/spec/lib/redpomo/cli_spec.rb +101 -0
- data/spec/lib/redpomo/fuzzy_converter_spec.rb +65 -0
- data/spec/lib/redpomo/task_spec.rb +39 -0
- data/spec/lib/redpomo/tracker_spec.rb +38 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/capture.rb +13 -0
- metadata +235 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'todo-txt/list'
|
2
|
+
require 'redpomo/task'
|
3
|
+
|
4
|
+
module Redpomo
|
5
|
+
class TaskList < Array
|
6
|
+
|
7
|
+
def self.find(task_number)
|
8
|
+
list = TaskList.new(Config.todo_path)
|
9
|
+
list.find(task_number)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.pull_from_trackers!
|
13
|
+
list = TaskList.new(Config.todo_path)
|
14
|
+
list.pull_from_trackers!
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(path)
|
18
|
+
@path = path
|
19
|
+
File.read(path).split("\n").each do |line|
|
20
|
+
push Task.new(self, line)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def find(task_number)
|
25
|
+
slice(task_number.to_i - 1)
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove!(task)
|
29
|
+
delete(task)
|
30
|
+
write!
|
31
|
+
end
|
32
|
+
|
33
|
+
def pull_from_trackers!
|
34
|
+
issue_tasks = Tracker.all.map(&:issues).flatten.map(&:to_task)
|
35
|
+
delete_if do |task|
|
36
|
+
task.tracker.present?
|
37
|
+
end
|
38
|
+
self << issue_tasks
|
39
|
+
self.flatten!
|
40
|
+
write!
|
41
|
+
end
|
42
|
+
|
43
|
+
def write!
|
44
|
+
File.open(@path, 'w') do |file|
|
45
|
+
file.write map(&:orig).join("\n") + "\n"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'active_support/core_ext/hash'
|
2
|
+
require 'redpomo/issue'
|
3
|
+
require 'redpomo/config'
|
4
|
+
|
5
|
+
module Redpomo
|
6
|
+
class Tracker
|
7
|
+
|
8
|
+
def self.find(name)
|
9
|
+
if data = Config.trackers_data[name.to_sym]
|
10
|
+
Tracker.new(name.to_sym, data)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.all
|
15
|
+
Config.trackers_data.map do |name, data|
|
16
|
+
Tracker.new(name.to_sym, data)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :name, :base_url
|
21
|
+
|
22
|
+
def initialize(name, options)
|
23
|
+
options.symbolize_keys!
|
24
|
+
@name = name
|
25
|
+
@base_url = options[:url]
|
26
|
+
@api_key = options[:token]
|
27
|
+
@default_project = options[:default_project]
|
28
|
+
@closed_status_id = options[:closed_status].to_i
|
29
|
+
end
|
30
|
+
|
31
|
+
def issues
|
32
|
+
data = get("/issues", assigned_to_id: current_user_id, status_id: "open")
|
33
|
+
data["issues"].map do |issue|
|
34
|
+
issue["project"] = project_identifier_for(issue["project"]["id"])
|
35
|
+
Issue.new(self, issue)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def push_entry!(entry)
|
40
|
+
task = entry.to_task
|
41
|
+
time_entry = {}
|
42
|
+
|
43
|
+
if issue = task.issue
|
44
|
+
time_entry[:issue_id] = issue
|
45
|
+
elsif project = task.project
|
46
|
+
time_entry[:project_id] = project
|
47
|
+
else
|
48
|
+
time_entry[:project_id] = @default_project
|
49
|
+
end
|
50
|
+
|
51
|
+
time_entry[:spent_on] = entry.datetime
|
52
|
+
time_entry[:hours] = entry.duration / 3600.0
|
53
|
+
time_entry[:comments] = task.text
|
54
|
+
|
55
|
+
post("/time_entries", time_entry: time_entry)
|
56
|
+
end
|
57
|
+
|
58
|
+
def close_issue!(id, message = nil)
|
59
|
+
issue = { status_id: @closed_status_id }
|
60
|
+
issue[:notes] = message if message.present?
|
61
|
+
put("/issues/#{id}", issue: issue)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def project_identifier_for(project_id)
|
67
|
+
Config.cache.get("#{@name}:#{project_id}:identifier") do
|
68
|
+
data = get("/projects/#{project_id}")
|
69
|
+
data["project"]["identifier"]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def current_user_id
|
74
|
+
get("/users/current")["user"]["id"]
|
75
|
+
end
|
76
|
+
|
77
|
+
def get(url, params = {})
|
78
|
+
request(:get, url, params)
|
79
|
+
end
|
80
|
+
|
81
|
+
def post(url, data)
|
82
|
+
request(:post, url, body: data)
|
83
|
+
end
|
84
|
+
|
85
|
+
def put(url, data)
|
86
|
+
request(:put, url, body: data)
|
87
|
+
end
|
88
|
+
|
89
|
+
def request(type, url, params = {})
|
90
|
+
require 'rest_client'
|
91
|
+
require 'json'
|
92
|
+
args = []
|
93
|
+
args << @base_url + url + ".json"
|
94
|
+
args << params.delete(:body).to_json unless type == :get
|
95
|
+
args << {
|
96
|
+
accept: :json,
|
97
|
+
content_type: :json,
|
98
|
+
params: params.merge(key: @api_key)
|
99
|
+
}
|
100
|
+
parse RestClient.send(type, *args)
|
101
|
+
end
|
102
|
+
|
103
|
+
def parse(json)
|
104
|
+
json && json.length >= 2 ? JSON.parse(json) : nil
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
data/lib/redpomo.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# require "active_support/core_ext"
|
2
|
+
# require "rest-client"
|
3
|
+
# require "todo-txt"
|
4
|
+
# require "yaml"
|
5
|
+
# require "json"
|
6
|
+
# require "csv"
|
7
|
+
# require "launchy"
|
8
|
+
# require "applescript"
|
9
|
+
|
10
|
+
require "redpomo/version"
|
11
|
+
# require "redpomo/ext"
|
12
|
+
# require "redpomo/cli"
|
13
|
+
|
14
|
+
module Redpomo
|
15
|
+
end
|
data/redpomo.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/redpomo/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Stefano Verna"]
|
6
|
+
gem.email = ["stefano.verna@welaika.com"]
|
7
|
+
gem.description = %q{A nice little gem that integrates Redmine, Todo.txt and Pomodoro.app}
|
8
|
+
gem.summary = %q{A nice little gem that integrates Redmine, Todo.txt and Pomodoro.app}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "redpomo"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Redpomo::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "activesupport"
|
19
|
+
gem.add_dependency "thor"
|
20
|
+
gem.add_dependency "todo-txt"
|
21
|
+
gem.add_dependency "rest-client"
|
22
|
+
gem.add_dependency "launchy"
|
23
|
+
gem.add_dependency "applescript"
|
24
|
+
gem.add_dependency "terminal-table"
|
25
|
+
|
26
|
+
gem.add_development_dependency "rspec"
|
27
|
+
gem.add_development_dependency "vcr"
|
28
|
+
gem.add_development_dependency "webmock"
|
29
|
+
gem.add_development_dependency "mocha"
|
30
|
+
gem.add_development_dependency "simplecov"
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'redpomo/file_cache'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
describe Redpomo::FileCache do
|
6
|
+
|
7
|
+
describe "#get" do
|
8
|
+
it "executes the block on cache miss" do
|
9
|
+
Redpomo::FileCache.instance.cache_path = Tempfile.new('cache').path
|
10
|
+
counter = Redpomo::FileCache.get("foobar") { 5 }
|
11
|
+
counter.should == 5
|
12
|
+
counter = Redpomo::FileCache.get("foobar") { 10 }
|
13
|
+
counter.should == 5
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: put
|
5
|
+
uri: https://project.cantierecreativo.net/issues/838.json?key=CANTIERE_TOKEN
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: ! '{"issue":{"status_id":5}}'
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- application/json
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip, deflate
|
14
|
+
Content-Type:
|
15
|
+
- application/json
|
16
|
+
Content-Length:
|
17
|
+
- '25'
|
18
|
+
User-Agent:
|
19
|
+
- Ruby
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 200
|
23
|
+
message: OK
|
24
|
+
headers:
|
25
|
+
Date:
|
26
|
+
- Sat, 05 May 2012 12:50:41 GMT
|
27
|
+
Server:
|
28
|
+
- Apache/2.2.9 (Debian) DAV/2 PHP/5.2.6-1+lenny10 with Suhosin-Patch mod_python/3.3.1
|
29
|
+
Python/2.5.2 mod_ssl/2.2.9 OpenSSL/0.9.8g Phusion_Passenger/3.0.0
|
30
|
+
X-Powered-By:
|
31
|
+
- Phusion Passenger (mod_rails/mod_rack) 3.0.0
|
32
|
+
X-Runtime:
|
33
|
+
- '2868'
|
34
|
+
Cache-Control:
|
35
|
+
- no-cache
|
36
|
+
Set-Cookie:
|
37
|
+
- _redmine_session=BAh7BzoPc2Vzc2lvbl9pZCIlYTRjNzRjYzVmMDllNDJmYTNlMzU0NmEwMWQwYTEwYzUiCmZsYXNoSUM6J0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7BjoLbm90aWNlIhlNb2RpZmljYSBlZmZldHR1YXRhLgY6CkB1c2VkewY7B0Y%3D--1337564c1bf1f5b34c2a505039c279d6f7074532;
|
38
|
+
path=/; HttpOnly
|
39
|
+
- autologin=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT
|
40
|
+
Content-Length:
|
41
|
+
- '1'
|
42
|
+
Status:
|
43
|
+
- '200'
|
44
|
+
Content-Type:
|
45
|
+
- application/json; charset=utf-8
|
46
|
+
body:
|
47
|
+
encoding: US-ASCII
|
48
|
+
string: ! ' '
|
49
|
+
http_version:
|
50
|
+
recorded_at: Sat, 05 May 2012 12:50:46 GMT
|
51
|
+
recorded_with: VCR 2.0.1
|