wunder_markdown 0.0.2
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 +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
|