wunder_markdown 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/Rakefile +7 -0
- data/bin/wundermarkdown +43 -0
- data/fixtures/cassettes/client_list.yml +66 -0
- data/fixtures/cassettes/client_login.yml +59 -0
- data/fixtures/cassettes/client_tasks.yml +64 -0
- data/lib/wunder_markdown.rb +6 -0
- data/lib/wunder_markdown/auth.rb +21 -0
- data/lib/wunder_markdown/cli.rb +79 -0
- data/lib/wunder_markdown/client.rb +54 -0
- data/lib/wunder_markdown/list.rb +13 -0
- data/lib/wunder_markdown/task.rb +28 -0
- data/lib/wunder_markdown/version.rb +3 -0
- data/test/client_test.rb +71 -0
- data/test/test_helper.rb +10 -0
- data/wunder_markdown.gemspec +27 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c0abf06b0a34395b20bb2cb0794271ace43f139f
|
4
|
+
data.tar.gz: 5f9110504184b78476a6170abd9fed9852a7454b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 55f4f877bff07602d75a0ed4bea18b615f1bfc6fe0385cce07e0071b4254f541b803c8608b95b76fe1738125000751f4989debd2e9d4128038bcfbe0a11cbfdf
|
7
|
+
data.tar.gz: d3d8e4c7524251c2df029a92afd49ee524eb24d9f8732fef5527bf69a5c8aa1cb4b04f2ed84f0a51fb66e9f7d4a46eb5b1c9de00e5a45854edd42317b710aefe
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Yannick Schutz
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# WunderMarkdown
|
2
|
+
|
3
|
+
**Dump your wunderlists to markdown**
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
$ gem install wunder_markdown
|
8
|
+
|
9
|
+
## Configuration
|
10
|
+
|
11
|
+
$ wundermarkdown config -e <email>
|
12
|
+
|
13
|
+
It will prompt you for your wunderlist password to retrieve a token.
|
14
|
+
-e option is optionnal, your email will be asked if not given.
|
15
|
+
|
16
|
+
**It does not store your password**
|
17
|
+
|
18
|
+
It uses netrc internally to store the user token.
|
19
|
+
See `~/.netrc` file after configuration.
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
$ wundermarkdown dump <list_name>
|
24
|
+
|
25
|
+
It will dump a markdown formatted version of your wunderlist list.
|
26
|
+
The output will be directed to standard output.
|
27
|
+
It can be redirected to a file as you know.
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
1. Fork it ( http://github.com/ys/wunder_markdown/fork )
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/wundermarkdown
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# # WunderMarkdown
|
4
|
+
#
|
5
|
+
# **Dump your wunderlists to markdown**
|
6
|
+
#
|
7
|
+
# ## Installation
|
8
|
+
#
|
9
|
+
# $ gem install wunder_markdown
|
10
|
+
#
|
11
|
+
# ## Configuration
|
12
|
+
#
|
13
|
+
# $ wundermarkdown config -e <email>
|
14
|
+
#
|
15
|
+
# It will prompt you for your wunderlist password to retrieve a token.
|
16
|
+
# -e option is optionnal, your email will be asked if not given.
|
17
|
+
#
|
18
|
+
# **It does not store your password**
|
19
|
+
#
|
20
|
+
# It uses netrc internally to store the user token.
|
21
|
+
# See `~/.netrc` file after configuration.
|
22
|
+
#
|
23
|
+
# ## Usage
|
24
|
+
#
|
25
|
+
# $ wundermarkdown dump <list_name>
|
26
|
+
#
|
27
|
+
# It will dump a markdown formatted version of your wunderlist list.
|
28
|
+
# The output will be directed to standard output.
|
29
|
+
# It can be redirected to a file as you know.
|
30
|
+
#
|
31
|
+
# ## Contributing
|
32
|
+
#
|
33
|
+
# 1. Fork it ( http://github.com/ys/wunder_markdown/fork )
|
34
|
+
# 2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
# 3. Commit your changes (`git commit -am 'Add some feature'`)
|
36
|
+
# 4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
# 5. Create new Pull Request
|
38
|
+
|
39
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
40
|
+
|
41
|
+
require 'wunder_markdown/cli'
|
42
|
+
|
43
|
+
WunderMarkdown::CLI.run(ARGV)
|
@@ -0,0 +1,66 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.wunderlist.com/me/lists
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- Faraday v0.8.8
|
12
|
+
Content-Type:
|
13
|
+
- application/json
|
14
|
+
Authorization:
|
15
|
+
- token
|
16
|
+
Accept-Encoding:
|
17
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
18
|
+
Accept:
|
19
|
+
- "*/*"
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 200
|
23
|
+
message: OK
|
24
|
+
headers:
|
25
|
+
Cache-Control:
|
26
|
+
- must-revalidate, private, max-age=0
|
27
|
+
Content-Type:
|
28
|
+
- application/json
|
29
|
+
Date:
|
30
|
+
- Fri, 10 Jan 2014 23:34:22 GMT
|
31
|
+
Etag:
|
32
|
+
- "\"49389ea8ccf6632e5c10d27fc973e7be\""
|
33
|
+
Server:
|
34
|
+
- nginx
|
35
|
+
Status:
|
36
|
+
- 200 OK
|
37
|
+
Vary:
|
38
|
+
- Accept-Encoding
|
39
|
+
X-6w-Client-Status:
|
40
|
+
- unknown
|
41
|
+
X-6w-Timestamp:
|
42
|
+
- '1389396862'
|
43
|
+
X-Rack-Cache:
|
44
|
+
- miss
|
45
|
+
X-Ratelimit-Delay-Hard:
|
46
|
+
- '15'
|
47
|
+
X-Ratelimit-Delay-Soft:
|
48
|
+
- '4401'
|
49
|
+
X-Request-Id:
|
50
|
+
- 88e9881e58077e399ce371a291c9aef6
|
51
|
+
X-Server:
|
52
|
+
- ip-10-33-130-18
|
53
|
+
Content-Length:
|
54
|
+
- '657'
|
55
|
+
Connection:
|
56
|
+
- keep-alive
|
57
|
+
body:
|
58
|
+
encoding: UTF-8
|
59
|
+
string: "[{\"id\":\"ABNSAAGm924\",\"type\":\"List\",\"local_identifier\":null,\"owner_id\":\"AANSAAAHLAY\",\"position\":11.0,\"title\":\"courses\",\"created_at\":\"2012-12-06T01:22:38Z\",\"updated_at\":\"2013-09-14T15:08:12Z\"},{\"id\":\"ABNSAAGqg1c\",\"type\":\"List\",\"local_identifier\":null,\"owner_id\":\"AANSAAAHLAY\",\"position\":3.0,\"title\":\"Bruxelles\",\"created_at\":\"2012-12-06T01:29:12Z\",\"updated_at\":\"2013-09-14T15:08:12Z\"},{\"id\":\"ABNSAAGqg_4\",\"type\":\"List\",\"local_identifier\":null,\"owner_id\":\"AANSAAAHLAY\",\"position\":12.0,\"title\":\"Voiture\",\"created_at\":\"2012-12-06T01:28:49Z\",\"updated_at\":\"2013-09-14T15:08:12Z\"},{\"id\":\"ABNSAAHtFpY\",\"type\":\"List\",\"local_identifier\":null,\"owner_id\":\"AANSAAAHLAY\",\"position\":14.0,\"title\":\"Clo\",\"created_at\":\"2012-12-06T03:39:21Z\",\"updated_at\":\"2013-09-14T15:08:12Z\"},{\"id\":\"ABNSAAIMbCM\",\"type\":\"List\",\"local_identifier\":null,\"owner_id\":\"AANSAAAHLAY\",\"position\":15.0,\"title\":\"Software
|
60
|
+
to watch\",\"created_at\":\"2012-12-06T04:46:27Z\",\"updated_at\":\"2013-09-14T15:08:12Z\"},{\"id\":\"ABNSAAIop5E\",\"type\":\"List\",\"local_identifier\":null,\"owner_id\":\"AANSAAAHLAY\",\"position\":24.0,\"title\":\"FUTURE\",\"created_at\":\"2012-12-06T05:48:26Z\",\"updated_at\":\"2013-09-14T15:08:12Z\"},{\"id\":\"ABNSAARBAsg\",\"type\":\"List\",\"local_identifier\":\"localId:Mac:AANSAAAHLAY:List:2C2760D4-4830-4D2D-940B-C84A121D0743\",\"owner_id\":\"AANSAAAHLAY\",\"position\":28.0002,\"title\":\"Things
|
61
|
+
to read this year\",\"created_at\":\"2013-02-19T21:08:12Z\",\"updated_at\":\"2013-09-14T15:08:12Z\"},{\"id\":\"ABNSAARYjd4\",\"type\":\"List\",\"local_identifier\":\"localId:prepopulated:AANSAAAHLAY:List:wishlist_1363347220630\",\"owner_id\":\"AANSAAAHLAY\",\"position\":40.0002,\"title\":\"Wishlist\",\"created_at\":\"2013-03-15T11:33:47Z\",\"updated_at\":\"2013-09-14T15:08:12Z\"},{\"id\":\"ABNSAASiysQ\",\"type\":\"List\",\"local_identifier\":\"localId:Mac:AANSAAAHLAY:List:6F4EC046-A152-4B1E-BE7D-2544A2896ADA\",\"owner_id\":\"AANSAAAHLAY\",\"position\":60.0002,\"title\":\"Languages
|
62
|
+
to learn\",\"created_at\":\"2013-06-03T16:40:11Z\",\"updated_at\":\"2013-09-14T15:08:12Z\"},{\"id\":\"ABNSAAVm7xE\",\"type\":\"List\",\"local_identifier\":\"localId:Mac:AANSAAAHLAY:List:920B0CC5-8DC5-4BE1-B21E-B7D4F11F3F4C\",\"owner_id\":\"AANSAAAHLAY\",\"position\":27.5002,\"title\":\"good
|
63
|
+
work places\",\"created_at\":\"2013-12-14T16:19:31Z\",\"updated_at\":\"2014-01-08T10:13:41Z\"},{\"id\":\"ABNSAAWFdcY\",\"type\":\"List\",\"local_identifier\":\"localId:Mac:AANSAAAHLAY:List:8A261AD7-C7AA-42C7-B094-AF6FAA598226\",\"owner_id\":\"AANSAAAHLAY\",\"position\":70.00020000000001,\"title\":\"projects\",\"created_at\":\"2014-01-08T18:45:00Z\",\"updated_at\":\"2014-01-08T18:45:00Z\"}]"
|
64
|
+
http_version:
|
65
|
+
recorded_at: Fri, 10 Jan 2014 23:34:22 GMT
|
66
|
+
recorded_with: VCR 2.8.0
|
@@ -0,0 +1,59 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://api.wunderlist.com/login
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: "{ \"email\": \"yannick.schutz@gmail.com\", \"password\": \"1234\"}"
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- Faraday v0.8.8
|
12
|
+
Content-Type:
|
13
|
+
- application/json
|
14
|
+
Accept-Encoding:
|
15
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
16
|
+
Accept:
|
17
|
+
- "*/*"
|
18
|
+
response:
|
19
|
+
status:
|
20
|
+
code: 200
|
21
|
+
message: OK
|
22
|
+
headers:
|
23
|
+
Cache-Control:
|
24
|
+
- max-age=0, private, must-revalidate
|
25
|
+
Content-Type:
|
26
|
+
- application/json; charset=utf-8
|
27
|
+
Date:
|
28
|
+
- Fri, 10 Jan 2014 23:31:15 GMT
|
29
|
+
Etag:
|
30
|
+
- "\"e6203653b533e125ab9eee0886682445\""
|
31
|
+
Server:
|
32
|
+
- nginx
|
33
|
+
Status:
|
34
|
+
- 200 OK
|
35
|
+
Vary:
|
36
|
+
- Accept-Encoding
|
37
|
+
X-6w-Client-Status:
|
38
|
+
- unknown
|
39
|
+
X-Rack-Cache:
|
40
|
+
- invalidate, pass
|
41
|
+
X-Ratelimit-Delay-Hard:
|
42
|
+
- '15'
|
43
|
+
X-Ratelimit-Delay-Soft:
|
44
|
+
- '2897'
|
45
|
+
X-Request-Id:
|
46
|
+
- 120111c5f76f02ebb41695d2751742fd
|
47
|
+
X-Server:
|
48
|
+
- ip-10-33-130-18
|
49
|
+
Content-Length:
|
50
|
+
- '1072'
|
51
|
+
Connection:
|
52
|
+
- keep-alive
|
53
|
+
body:
|
54
|
+
encoding: UTF-8
|
55
|
+
string: "{\"id\":\"AANSAAAHLAY\",\"created_at\":\"2013-05-14T20:31:16Z\",\"updated_at\":\"2013-05-14T20:31:16Z\",\"name\":\"Yannick
|
56
|
+
Schutz\",\"type\":\"User\",\"avatar\":\"https://wunderlist-files.s3.amazonaws.com/uploads/user/avatar/d75ba7a7e0ffaf955e77609bb8b2184913a54d8c/avatar_64x64_localId_Mac_AANSAAAHLAY_User_8CB11CB3-F804-45B5-9B66-83D8B4595F44.png\",\"email\":\"yannick.schutz@gmail.com\",\"token\":\"token\",\"terms_accepted_at\":null,\"confirmation_state\":\"confirmed_email\",\"email_confirmed\":true,\"channel\":\"me.updates.60c12f433efa627e1cdcc88c962d1b4da3b988c7\",\"product\":null,\"group_product\":null,\"facebook\":\"834956678\",\"settings\":{\"account_locale\":\"en\",\"background\":\"wlbackground13\",\"campaign_iyf4_notification\":\"true\",\"campaign_iyf4_notification_last_date\":\"2013-11-12T14:02:23Z\",\"campaign_iyf4_notification_variation\":\"d\",\"confirm_delete_entity\":\"true\",\"consumed_quota_assigning_daily\":\"0\",\"consumed_quota_assigning_daily_date\":\"2013-11-23\",\"consumed_quota_assigning_overall\":\"0\",\"consumed_quota_comments_daily\":\"0\",\"consumed_quota_comments_daily_date\":\"2013-11-23\",\"consumed_quota_comments_overall\":\"0\",\"consumed_quota_files_daily\":\"0\",\"consumed_quota_files_daily_date\":\"2013-11-23\",\"consumed_quota_files_overall\":\"0\",\"experiment_chatter_notifications\":\"compacted\",\"inbox_name\":\"Inbox\",\"migrated_wunderlist_one_user\":\"false\",\"newsletter_subscription_enabled\":\"false\",\"new_task_location\":\"top\",\"notifications_desktop_enabled\":\"true\",\"notifications_email_enabled\":\"false\",\"print_completed_items\":\"false\",\"setting\":\"{}\",\"show_completed_items\":\"true\",\"smartlist_visibility_all\":\"hidden\",\"smartlist_visibility_assigned_to_me\":\"auto\",\"smartlist_visibility_conversations\":\"auto\",\"smartlist_visibility_done\":\"hidden\",\"smartlist_visibility_starred\":\"hidden\",\"smartlist_visibility_today\":\"hidden\",\"smartlist_visibility_week\":\"hidden\",\"sound_checkoff_enabled\":\"true\",\"sound_notification_enabled\":\"true\",\"start_of_week\":\"mon\",\"use_badge_icon\":\"notifications\",\"web_app_open_count\":\"1\",\"web_chrome_rating_later\":\"false\",\"web_coach_mark_inbox\":\"completed\",\"web_coach_mark_smart_list\":\"completed\",\"web_extension_last_selected_list\":\"ABNSAAVDowU\",\"web_extension_read_later_list_id\":\"wishlist_1363347220630\",\"web_last_app_open_date\":\"1389308400000\",\"web_last_used_release\":\"2.3.4.1\",\"web_new_installation\":\"true\",\"web_significant_event_count\":\"5\",\"web_uuid\":\"308c3f4d-b476-4bfe-9d9a-073b05a6406c\",\"wl1:inbox_id\":\"5590906\",\"wunderlist_timezone_offset\":\"1.0\"}}"
|
57
|
+
http_version:
|
58
|
+
recorded_at: Fri, 10 Jan 2014 23:31:15 GMT
|
59
|
+
recorded_with: VCR 2.8.0
|
@@ -0,0 +1,64 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.wunderlist.com/ABNSAAWFdcY/tasks
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- Faraday v0.8.8
|
12
|
+
Content-Type:
|
13
|
+
- application/json
|
14
|
+
Authorization:
|
15
|
+
- token
|
16
|
+
Accept-Encoding:
|
17
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
18
|
+
Accept:
|
19
|
+
- "*/*"
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 200
|
23
|
+
message: OK
|
24
|
+
headers:
|
25
|
+
Cache-Control:
|
26
|
+
- must-revalidate, private, max-age=0
|
27
|
+
Content-Type:
|
28
|
+
- application/json
|
29
|
+
Date:
|
30
|
+
- Fri, 10 Jan 2014 23:40:07 GMT
|
31
|
+
Etag:
|
32
|
+
- "\"2667836597eb1e6c5da2e750d2756119\""
|
33
|
+
Server:
|
34
|
+
- nginx
|
35
|
+
Status:
|
36
|
+
- 200 OK
|
37
|
+
Vary:
|
38
|
+
- Accept-Encoding
|
39
|
+
X-6w-Client-Status:
|
40
|
+
- unknown
|
41
|
+
X-6w-Timestamp:
|
42
|
+
- '1389397207'
|
43
|
+
X-Rack-Cache:
|
44
|
+
- miss
|
45
|
+
X-Ratelimit-Delay-Hard:
|
46
|
+
- '15'
|
47
|
+
X-Ratelimit-Delay-Soft:
|
48
|
+
- '4587'
|
49
|
+
X-Request-Id:
|
50
|
+
- bdd9b80ab70a9932c4f6ed7eb80dc11c
|
51
|
+
X-Server:
|
52
|
+
- ip-10-33-130-18
|
53
|
+
Content-Length:
|
54
|
+
- '401'
|
55
|
+
Connection:
|
56
|
+
- keep-alive
|
57
|
+
body:
|
58
|
+
encoding: UTF-8
|
59
|
+
string: "[{\"assignee_id\":null,\"completed_at\":null,\"completed_by_id\":null,\"created_at\":\"2013-11-21T19:28:26Z\",\"created_by_id\":\"AANSAAAHLAY\",\"deleted_at\":null,\"due_date\":null,\"id\":\"ACNSABwHiMA\",\"list_id\":\"ABNSAAWFdcY\",\"local_identifier\":\"localId:iPhone:AANSAAAHLAY:Task:A5F4C2ED-838B-4A96-97EB-4DCB6DEE9103\",\"note\":null,\"owner_id\":\"AANSAAAHLAY\",\"parent_id\":null,\"position\":-159.99984,\"recurrence_count\":null,\"recurrence_type\":null,\"recurring_parent_id\":null,\"starred\":false,\"title\":\"Issues
|
60
|
+
lists\",\"type\":\"Task\",\"updated_at\":\"2014-01-08T18:45:02Z\",\"updated_by_id\":\"AANSAAAHLAY\",\"user_id\":\"AANSAAAHLAY\"},{\"assignee_id\":null,\"completed_at\":null,\"completed_by_id\":null,\"created_at\":\"2013-11-21T19:28:26Z\",\"created_by_id\":\"AANSAAAHLAY\",\"deleted_at\":null,\"due_date\":null,\"id\":\"ACNSABwHiL0\",\"list_id\":\"ABNSAAWFdcY\",\"local_identifier\":\"localId:iPhone:AANSAAAHLAY:Task:FE0192CB-1AB4-4AB6-8927-DFC39AE6EED8\",\"note\":null,\"owner_id\":\"AANSAAAHLAY\",\"parent_id\":null,\"position\":-149.99979,\"recurrence_count\":null,\"recurrence_type\":null,\"recurring_parent_id\":null,\"starred\":false,\"title\":\"Dot
|
61
|
+
files aggregator\",\"type\":\"Task\",\"updated_at\":\"2014-01-08T18:45:01Z\",\"updated_by_id\":\"AANSAAAHLAY\",\"user_id\":\"AANSAAAHLAY\"}]"
|
62
|
+
http_version:
|
63
|
+
recorded_at: Fri, 10 Jan 2014 23:40:07 GMT
|
64
|
+
recorded_with: VCR 2.8.0
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'netrc'
|
2
|
+
|
3
|
+
module WunderMarkdown
|
4
|
+
API_ENDPOINT = 'api.wunderlist.com'
|
5
|
+
class Auth
|
6
|
+
def initialize
|
7
|
+
@netrc = Netrc.read
|
8
|
+
end
|
9
|
+
|
10
|
+
def save(user, password)
|
11
|
+
@netrc[API_ENDPOINT] = user, password
|
12
|
+
@netrc.save
|
13
|
+
password
|
14
|
+
end
|
15
|
+
|
16
|
+
def get
|
17
|
+
_, token = @netrc[API_ENDPOINT]
|
18
|
+
token
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'wunder_markdown/client'
|
2
|
+
require 'wunder_markdown/auth'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module WunderMarkdown
|
6
|
+
class CLI
|
7
|
+
attr_reader :options, :args, :command
|
8
|
+
|
9
|
+
def self.run(args)
|
10
|
+
new.call(args)
|
11
|
+
# rescue StandardError
|
12
|
+
# $stderr.puts 'Woops, Something went wrong.'
|
13
|
+
# exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(args)
|
17
|
+
args, options = preparse(args)
|
18
|
+
command = args.shift
|
19
|
+
public_send(command, args, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def preparse(args)
|
23
|
+
options = OpenStruct.new
|
24
|
+
opt_parse = OptionParser.new do |opts|
|
25
|
+
opts.banner = "Usage: wundermarkdown <command> <args> [options]"
|
26
|
+
opts.on("-e EMAIL", "--email EMAIL", "Email address") do |email|
|
27
|
+
options.email = email
|
28
|
+
end
|
29
|
+
end
|
30
|
+
opt_parse.parse!(args)
|
31
|
+
unless args.count >= 1
|
32
|
+
$stderr.puts opt_parse
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
[args, options]
|
36
|
+
end
|
37
|
+
|
38
|
+
def config(args, options)
|
39
|
+
$stdout.puts 'Wunderlist Credentials: We will not store your password'
|
40
|
+
email = options[:email]
|
41
|
+
if ! email
|
42
|
+
$stdout.puts 'email:'
|
43
|
+
email = $stdin.gets.chomp
|
44
|
+
end
|
45
|
+
$stdout.puts 'Password:'
|
46
|
+
system 'stty -echo'
|
47
|
+
password = $stdin.gets.chomp
|
48
|
+
system 'stty echo'
|
49
|
+
WunderMarkdown::Auth.new.save(*client.login(email, password))
|
50
|
+
end
|
51
|
+
|
52
|
+
def dump(args, options)
|
53
|
+
list_name = args.shift
|
54
|
+
unless list_name
|
55
|
+
$stderr.puts 'Usage: wundermarkdown dump <list_name>'
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
list = client.list(list_name)
|
59
|
+
list.tasks = group_tasks(client.tasks(list))
|
60
|
+
$stdout.puts list.to_markdown
|
61
|
+
end
|
62
|
+
|
63
|
+
def group_tasks(tasks)
|
64
|
+
root_tasks = tasks.select { |task| task.root? }
|
65
|
+
root_tasks.map do |task|
|
66
|
+
task.children = tasks.select { |t| t.parent_id == task.id }
|
67
|
+
task
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def client
|
72
|
+
@client ||= WunderMarkdown::Client.new(token)
|
73
|
+
end
|
74
|
+
|
75
|
+
def token
|
76
|
+
@token ||= WunderMarkdown::Auth.new.get
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'json'
|
3
|
+
require 'wunder_markdown/task'
|
4
|
+
require 'wunder_markdown/list'
|
5
|
+
|
6
|
+
module WunderMarkdown
|
7
|
+
class Client
|
8
|
+
API_ENDPOINT = 'api.wunderlist.com'
|
9
|
+
API_URL = "https://#{API_ENDPOINT}"
|
10
|
+
attr_accessor :token, :conn
|
11
|
+
|
12
|
+
def initialize(token = nil)
|
13
|
+
@token = token
|
14
|
+
@conn = Faraday.new(:url => API_URL)
|
15
|
+
end
|
16
|
+
|
17
|
+
def login(email, password)
|
18
|
+
login_response = conn.post do |req|
|
19
|
+
req.url '/login'
|
20
|
+
req.headers['Content-Type'] = 'application/json'
|
21
|
+
req.body = '{ "email": "'+ email +'", "password": "'+ password +'"}'
|
22
|
+
end
|
23
|
+
@token = JSON.parse(login_response.body)['token']
|
24
|
+
[email, @token]
|
25
|
+
end
|
26
|
+
|
27
|
+
def lists
|
28
|
+
@lists ||= get('me/lists')
|
29
|
+
end
|
30
|
+
|
31
|
+
def list(list_name)
|
32
|
+
json = lists.detect { |l| l['title'] == list_name }
|
33
|
+
List.new(json['id'], json['title'])
|
34
|
+
end
|
35
|
+
|
36
|
+
def tasks(list)
|
37
|
+
json = get("#{list.id}/tasks")
|
38
|
+
json.map do |task_json|
|
39
|
+
if task_json['completed_at'] == nil
|
40
|
+
Task.new(task_json['id'], task_json['title'], task_json['note'], task_json['parent_id'])
|
41
|
+
end
|
42
|
+
end.compact
|
43
|
+
end
|
44
|
+
|
45
|
+
def get(url)
|
46
|
+
response = conn.get do |req|
|
47
|
+
req.url url
|
48
|
+
req.headers['Content-Type'] = 'application/json'
|
49
|
+
req.headers['Authorization'] = token
|
50
|
+
end
|
51
|
+
JSON.parse(response.body)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module WunderMarkdown
|
2
|
+
class Task < Struct.new(:id, :title, :note, :parent_id)
|
3
|
+
attr_accessor :children, :client
|
4
|
+
|
5
|
+
def root?
|
6
|
+
parent_id.nil?
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_markdown
|
10
|
+
if root?
|
11
|
+
markdown = "## #{title} \n"
|
12
|
+
if note && note != ''
|
13
|
+
markdown += " \n"
|
14
|
+
note.chars.each_slice(80) do |slice|
|
15
|
+
markdown += "> #{slice.join}\n"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
if children.any?
|
19
|
+
markdown += " \n"
|
20
|
+
markdown += children.map(&:to_markdown).join(" \n")
|
21
|
+
end
|
22
|
+
markdown
|
23
|
+
else
|
24
|
+
"* #{title} \n"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/test/client_test.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ClientLoginTest < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
client = WunderMarkdown::Client.new
|
7
|
+
VCR.use_cassette('client_login') do
|
8
|
+
@email, @token = client.login("yannick.schutz@gmail.com", "1234")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_login_returns_email
|
13
|
+
assert_equal @email, 'yannick.schutz@gmail.com'
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_login_returns_token
|
17
|
+
assert_equal @token, 'token'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class ClientListTest < MiniTest::Unit::TestCase
|
22
|
+
|
23
|
+
def setup
|
24
|
+
client = WunderMarkdown::Client.new("token")
|
25
|
+
VCR.use_cassette('client_list') do
|
26
|
+
@list = client.list("projects")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_list_is_correct_instance
|
31
|
+
assert_instance_of WunderMarkdown::List, @list
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_list_has_correct_id
|
35
|
+
assert_equal @list.id, "ABNSAAWFdcY"
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_list_has_correct_name
|
39
|
+
assert_equal @list.name, "projects"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class ClientTasksTest < MiniTest::Unit::TestCase
|
44
|
+
|
45
|
+
def setup
|
46
|
+
client = WunderMarkdown::Client.new("token")
|
47
|
+
VCR.use_cassette('client_tasks') do
|
48
|
+
list = MiniTest::Mock.new
|
49
|
+
list.expect(:id, "ABNSAAWFdcY")
|
50
|
+
@tasks = client.tasks(list)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_tasks_is_an_array
|
55
|
+
assert_instance_of Array, @tasks
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_tasks_has_2_elements
|
59
|
+
assert_equal 2, @tasks.size
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_tasks_are_correct_instances
|
63
|
+
@tasks.each do |task|
|
64
|
+
assert_instance_of WunderMarkdown::Task, task
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_task_has_correct_name
|
69
|
+
assert_equal @tasks.first.title, "Issues lists"
|
70
|
+
end
|
71
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'wunder_markdown/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "wunder_markdown"
|
8
|
+
spec.version = WunderMarkdown::VERSION
|
9
|
+
spec.authors = ["Yannick Schutz"]
|
10
|
+
spec.email = ["yannick.schutz@gmail.com"]
|
11
|
+
spec.summary = "Mardown dump your wunderlists"
|
12
|
+
spec.description = "Mardown dump your wunderlists"
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "webmock", "~> 1.16"
|
23
|
+
spec.add_development_dependency "vcr", "~> 2.8"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_runtime_dependency "faraday", "~> 0.8"
|
26
|
+
spec.add_runtime_dependency "netrc", "~> 0.7"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wunder_markdown
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yannick Schutz
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: webmock
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.16'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.16'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: vcr
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: faraday
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.8'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.8'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: netrc
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.7'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.7'
|
97
|
+
description: Mardown dump your wunderlists
|
98
|
+
email:
|
99
|
+
- yannick.schutz@gmail.com
|
100
|
+
executables:
|
101
|
+
- wundermarkdown
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".gitignore"
|
106
|
+
- Gemfile
|
107
|
+
- LICENSE.txt
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- bin/wundermarkdown
|
111
|
+
- fixtures/cassettes/client_list.yml
|
112
|
+
- fixtures/cassettes/client_login.yml
|
113
|
+
- fixtures/cassettes/client_tasks.yml
|
114
|
+
- lib/wunder_markdown.rb
|
115
|
+
- lib/wunder_markdown/auth.rb
|
116
|
+
- lib/wunder_markdown/cli.rb
|
117
|
+
- lib/wunder_markdown/client.rb
|
118
|
+
- lib/wunder_markdown/list.rb
|
119
|
+
- lib/wunder_markdown/task.rb
|
120
|
+
- lib/wunder_markdown/version.rb
|
121
|
+
- test/client_test.rb
|
122
|
+
- test/test_helper.rb
|
123
|
+
- wunder_markdown.gemspec
|
124
|
+
homepage: ''
|
125
|
+
licenses:
|
126
|
+
- MIT
|
127
|
+
metadata: {}
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
requirements: []
|
143
|
+
rubyforge_project:
|
144
|
+
rubygems_version: 2.2.0
|
145
|
+
signing_key:
|
146
|
+
specification_version: 4
|
147
|
+
summary: Mardown dump your wunderlists
|
148
|
+
test_files:
|
149
|
+
- test/client_test.rb
|
150
|
+
- test/test_helper.rb
|