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 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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in wunder_markdown.gemspec
4
+ gemspec
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
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.pattern = "test/*_test.rb"
7
+ end
@@ -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,6 @@
1
+ require 'wunder_markdown/list'
2
+ require 'wunder_markdown/task'
3
+ require 'wunder_markdown/client'
4
+ require 'wunder_markdown/auth'
5
+ require 'wunder_markdown/cli'
6
+
@@ -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,13 @@
1
+ module WunderMarkdown
2
+ class List < Struct.new(:id, :name)
3
+ attr_accessor :tasks, :client
4
+
5
+ def to_markdown
6
+ markdown = "# #{name} \n \n"
7
+ if tasks
8
+ markdown += tasks.map(&:to_markdown).join(" \n")
9
+ end
10
+ markdown
11
+ end
12
+ end
13
+ 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
@@ -0,0 +1,3 @@
1
+ module WunderMarkdown
2
+ VERSION = "0.0.2"
3
+ end
@@ -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
@@ -0,0 +1,10 @@
1
+ require 'wunder_markdown'
2
+ require 'minitest/unit'
3
+ require 'minitest/autorun'
4
+ require 'minitest/pride'
5
+ require 'vcr'
6
+
7
+ VCR.configure do |c|
8
+ c.cassette_library_dir = 'fixtures/cassettes'
9
+ c.hook_into :webmock # or :fakeweb
10
+ end
@@ -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