springpad 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in springpad.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require 'rake/testtask'
6
+ Rake::TestTask.new do |t|
7
+ t.libs << "test"
8
+ t.test_files = FileList['./test/**/*_test.rb']
9
+ end
10
+
11
+ desc "Checks the user has appropriate credentials"
12
+ task :check_credentials do
13
+ begin
14
+ config = YAML.load(File.read(File.expand_path("~/.springpad")))
15
+ rescue
16
+ raise StandardError, "File ~/.springpad does not exist. You must create it in a YAML format with 'user', 'password' and 'token' fields."
17
+ end
18
+ unless config['user'] && config['password'] && config['token']
19
+ raise StandardError, "One of the three fields (either user, password or token) is not filled properly in ~/.springpad. Please check it."
20
+ end
21
+ end
22
+
23
+ task :default => [:check_credentials, :test]
data/Readme.md ADDED
@@ -0,0 +1,43 @@
1
+ # springpad
2
+
3
+ Command-line client for [Springpad](http://springpadit.com). Currently WIP.
4
+
5
+ ## Getting an API key
6
+
7
+ To use this, even to run the tests, you need to be registered at Springpad
8
+ (it's a free service) and also have an API key.
9
+
10
+ You can [register here](http://springpadit.com/) and [request an API key here](
11
+ http://springpadit.com/developers/oauth/register-app). They give them FAST (I
12
+ got mine the minute after sending the form, no kidding).
13
+
14
+ After that you have to create a `~/.springpad` file in a YAML format with this
15
+ data:
16
+
17
+ user: YOUR_USERNAME
18
+ password: YOUR_PASSWORD
19
+ token: YOUR_CONSUMER_KEY
20
+
21
+ ## Usage
22
+
23
+ $ gem install springpad
24
+ $ springpad list note
25
+ $ springpad list task
26
+ $ springpad --help
27
+
28
+ ## Running the tests
29
+
30
+ Once you have the config file in place, you just have to:
31
+
32
+ git clone git://github.com/txus/springpad
33
+ cd springpad
34
+ bundle install
35
+ rake
36
+
37
+ You should see some serious green stuff going on!
38
+
39
+ ## Who's this
40
+
41
+ This was made by [Josep M. Bach (Txus)](http://txustice.me) under the MIT
42
+ license. I'm [@txustice](http://twitter.com/txustice) on twitter (where you
43
+ should probably follow me!).
data/bin/springpad ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'commander/import'
5
+ require_relative "../lib/springpad"
6
+
7
+ program :version, Springpad::VERSION
8
+ program :description, 'Command-line client to the Springpad API.'
9
+ program :help, 'Author', 'Josep M. Bach (Txus) <josep.m.bach@gmail.com>'
10
+
11
+ command :list do |c|
12
+ c.syntax = 'springpad list TYPE [options]'
13
+ c.summary = 'Lists all elements of a certain TYPE.'
14
+ c.description = 'Springpad blocks have an associated type, for example Note or Task. This command lists every block you own of a certain type. The result can be filtered using [options].'
15
+ c.example 'List all private notes', 'springpad list note --private'
16
+ c.option '--private', 'Show only private blocks'
17
+ c.action do |args, options|
18
+ type = args.first
19
+ unless type
20
+ warn "A TYPE must be specified. To know about available types, run:"
21
+ warn " $ springpad types"
22
+ abort
23
+ end
24
+ Springpad.list(type, options)
25
+ end
26
+ end
27
+
28
+ command :add do |c|
29
+ c.syntax = 'springpad add type [options]'
30
+ c.summary = 'Adds an element of a certain type.'
31
+ c.description = ''
32
+ c.example 'description', 'command example'
33
+ c.option '--some-switch', 'Some switch that does something'
34
+ c.action do |args, options|
35
+ # Do something or c.when_called Springpad::Commands::Add
36
+ end
37
+ end
38
+
39
+ command :types do |c|
40
+ c.syntax = 'springpad types'
41
+ c.summary = 'Lists the supported element types.'
42
+ c.description = ''
43
+ c.action do |args, options|
44
+ types = Springpad::Blocks.types
45
+ puts "Supported types are: #{types.join(', ')}"
46
+ end
47
+ end
48
+
@@ -0,0 +1,124 @@
1
+ require 'rest-client'
2
+ require 'yaml'
3
+ require 'json'
4
+ require 'cgi'
5
+
6
+ module Springpad
7
+ # Public: The best way to communicate with the Springpad API.
8
+ #
9
+ # Examples
10
+ #
11
+ # api = Springpad::API.new
12
+ # api.notes
13
+ # # => [..., ..., ...] # Your notes on Springpad
14
+ #
15
+ class API
16
+ # Public: Initializes a new API instance with credentials stored in a
17
+ # configuration file.
18
+ def initialize
19
+ config = YAML.load(File.read(File.expand_path("~/.springpad")))
20
+ @user = config['user']
21
+ @password = config['password']
22
+ @token = config['token']
23
+ @url = "http://springpadit.com/api"
24
+ end
25
+
26
+ # Public: Gets your notes with optional filters.
27
+ #
28
+ # filters - the Hash filters to apply to the search
29
+ #
30
+ # Returns an Array of Blocks::Note.
31
+ def notes(filters={})
32
+ get_blocks("Note", filters)
33
+ end
34
+
35
+ # Public: Gets your tasks with optional filters.
36
+ #
37
+ # filters - the Hash filters to apply to the search
38
+ #
39
+ # Returns an Array of Blocks::Task.
40
+ def tasks(filters={})
41
+ get_blocks("Task", filters)
42
+ end
43
+
44
+ ## Internal methods: to be extracted
45
+
46
+ # Internal: Gets a collection of blocks of a given type applying some
47
+ # filters.
48
+ #
49
+ # type - the String type of block
50
+ # filters - the hash filters to apply to the search
51
+ #
52
+ # Returns an Array of Blocks::Block.
53
+ def get_blocks(type, filters)
54
+ json = get("/users/#{@user}/blocks",
55
+ :type => type,
56
+ :filters => filters)
57
+
58
+ Blocks.const_get(type).process(json)
59
+ end
60
+
61
+ # Internal: Performs a GET request on a generated URL using the credentials
62
+ # from the API instance.
63
+ #
64
+ # route - the String URI of the API resource
65
+ # options - a Hash of options
66
+ #
67
+ # Returns the Hash parsed JSON response.
68
+ def get(route, options={})
69
+ url = generate(route, options)
70
+ JSON.parse(
71
+ RestClient.get(
72
+ url,
73
+ "X-Spring-Username" => @user,
74
+ "X-Spring-Password" => @password,
75
+ "X-Spring-Api-Token" => @token
76
+ )
77
+ )
78
+ end
79
+
80
+ # Internal: Generates a route and applies some options to it as query
81
+ # parameters.
82
+ #
83
+ # route - the String URI of the API resource
84
+ # options - a Hash of options to narrow down the search
85
+ #
86
+ # Returns the String generated route.
87
+ def generate(route, options)
88
+ base = @url + route
89
+ return base if options.empty?
90
+ base += "?"
91
+ opts = []
92
+
93
+ opts << "type=#{options[:type]}" if options[:type]
94
+ opts << "text=#{options[:matching]}" if options[:matching]
95
+ opts += extract_filters(options[:filters])
96
+
97
+ base += opts.join("&")
98
+ base
99
+ end
100
+
101
+ # Internal: Extracts filters from a Hash to be encoded as a special form of
102
+ # query parameter.
103
+ #
104
+ # f - the Hash of filters to apply
105
+ #
106
+ # Returns the single-element Array with the query parameter.
107
+ def extract_filters(f)
108
+ filts = []
109
+
110
+ filts << "type=#{f[:type]}" if f[:type]
111
+ filts << "userAction=#{f[:userAction]}" if f[:userAction]
112
+ filts << "public=#{f[:public]}" if f[:public]
113
+ filts << "complete=#{f[:complete]}" if f[:complete]
114
+ filts << "isInPast=#{f[:isInPast]}" if f[:isInPast]
115
+ filts << "flagged=#{f[:flagged]}" if f[:flagged]
116
+
117
+ if filts.any?
118
+ ["filter=#{CGI.escape(filts.join(" and "))}"]
119
+ else
120
+ []
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,53 @@
1
+ require 'highline'
2
+ require 'stringio'
3
+
4
+ module Springpad
5
+ module Blocks
6
+ # Public: Maps to a Springpad Note block.
7
+ #
8
+ # Examples
9
+ #
10
+ # Blocks::Note.process(json_notes)
11
+ # # => [#<Blocks::Note>..., #<Blocks::Note...>,]
12
+ #
13
+ class Note
14
+ attr_reader :name, :text
15
+ # Public: Converts a Hash of JSON note blocks to an Array of actual Note
16
+ # instances.
17
+ #
18
+ # json - the Hash JSON with the note blocks
19
+ #
20
+ # Returns an Array of Notes.
21
+ def self.process(json)
22
+ json.map do |note|
23
+ Note.new(
24
+ note['name'] || "(no title)",
25
+ note['properties']['text']
26
+ )
27
+ end
28
+ end
29
+
30
+ # Internal: Initializes a new Note.
31
+ #
32
+ # name - the String name
33
+ # text - the String text content
34
+ def initialize(name, text)
35
+ @name = name
36
+ @text = text
37
+ end
38
+
39
+ # Public: Renders a note to the standard output.
40
+ #
41
+ # Returns nothing.
42
+ def render
43
+ out = HighLine.new
44
+ out.wrap_at = 78
45
+ out.say <<-RENDER
46
+ <%=color("#{@name}", :bold)%>
47
+ <%='-'*#{@name.length}%>
48
+ #{@text}
49
+ RENDER
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,53 @@
1
+ module Springpad
2
+ module Blocks
3
+ # Public: Maps to a Springpad Task block.
4
+ #
5
+ # Examples
6
+ #
7
+ # Blocks::Task.process(json_tasks)
8
+ # # => [#<Blocks::Task>..., #<Blocks::Task...>,]
9
+ #
10
+ class Task
11
+ attr_reader :name, :description, :category
12
+ # Public: Converts a Hash of JSON task blocks to an Array of actual Task
13
+ # instances.
14
+ #
15
+ # json - the Hash JSON with the task blocks
16
+ #
17
+ # Returns an Array of Tasks.
18
+ def self.process(json)
19
+ json.map do |task|
20
+ Task.new(
21
+ task['name'],
22
+ task['properties']['description'],
23
+ task['properties']['category']['name']
24
+ )
25
+ end
26
+ end
27
+
28
+ # Internal: Initializes a new Task.
29
+ #
30
+ # name - the String name
31
+ # description - the String description
32
+ # category - the String category
33
+ def initialize(name, description, category)
34
+ @name = name
35
+ @description = description
36
+ @category = category
37
+ end
38
+
39
+ # Public: Renders a task to the standard output.
40
+ #
41
+ # Returns nothing.
42
+ def render
43
+ out = HighLine.new
44
+ out.wrap_at = 78
45
+ out.say <<-RENDER
46
+ <%=color("#{@name}", :bold)%> [#{@category.upcase}]
47
+ <%='-'*#{@name.length}%>
48
+ #{@description}
49
+ RENDER
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,14 @@
1
+ module Springpad
2
+ module Blocks
3
+ def self.types
4
+ [
5
+ "note",
6
+ "task",
7
+ ]
8
+ end
9
+ end
10
+ end
11
+
12
+ Springpad::Blocks.types.each do |type|
13
+ require_relative "blocks/#{type}"
14
+ end
@@ -0,0 +1,15 @@
1
+ require 'tempfile'
2
+
3
+ module Springpad
4
+ class CLI
5
+ # Public: Edits a file with $EDITOR and returns the contents.
6
+ #
7
+ # Returns an Array with the first line and an array of the other lines.
8
+ def edit
9
+ temp_file = Tempfile.new('block')
10
+ system("$EDITOR #{temp_file.path}")
11
+ contents = temp_file.read.chomp.split("\n").reject(&:empty?).map(&:strip)
12
+ [contents.first, contents[1..-1]]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Springpad
2
+ VERSION = "0.0.2"
3
+ end
data/lib/springpad.rb ADDED
@@ -0,0 +1,29 @@
1
+ require_relative "springpad/version"
2
+ require_relative "springpad/api"
3
+ require_relative "springpad/blocks"
4
+ require_relative "springpad/cli"
5
+
6
+ module Springpad
7
+ def self.list(type, options)
8
+ api = Springpad::API.new
9
+ options = {}
10
+ options.merge({:public => false}) if options[:private]
11
+ case type
12
+ when "note"
13
+ render api.notes(options)
14
+ when "task"
15
+ render api.tasks(options)
16
+ end
17
+ end
18
+
19
+ def self.render(elements)
20
+ elements.each do |element|
21
+ element.render
22
+ puts
23
+ end
24
+ end
25
+
26
+ def self.add
27
+
28
+ end
29
+ end
data/springpad.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "springpad/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "springpad"
7
+ s.version = Springpad::VERSION
8
+ s.authors = ["Josep M. Bach"]
9
+ s.email = ["josep.m.bach@gmail.com"]
10
+ s.homepage = "http://github.com/txus/springpad"
11
+ s.summary = %q{Command-line client for Springpad.}
12
+ s.description = %q{Command-line client for Springpad.}
13
+
14
+ s.rubyforge_project = "springpad"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "minitest"
23
+ s.add_development_dependency "mocha"
24
+ s.add_development_dependency "vcr"
25
+ s.add_development_dependency "webmock"
26
+ s.add_runtime_dependency "rest-client"
27
+ s.add_runtime_dependency "commander"
28
+ end
@@ -0,0 +1,26 @@
1
+ require 'test_helper'
2
+
3
+ module Springpad
4
+ describe API do
5
+ let(:config) { YAML.load(File.read(File.expand_path("~/.springpad"))) }
6
+ let(:api) { API.new }
7
+
8
+ it 'initializes a user and password from ~/.springpad' do
9
+ api.instance_variable_get(:@user).must_equal config['user']
10
+ api.instance_variable_get(:@password).must_equal config['password']
11
+ api.instance_variable_get(:@token).must_equal config['token']
12
+ end
13
+
14
+ it "fetches the notes of the user" do
15
+ notes = api.notes(:public => false)
16
+ notes.length.must_be :>, 0
17
+ notes.first.must_be_kind_of Blocks::Note
18
+ end
19
+
20
+ it "fetches the tasks of the user" do
21
+ tasks = api.tasks(:public => false)
22
+ tasks.length.must_be :>, 0
23
+ tasks.first.must_be_kind_of Blocks::Task
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+
3
+ module Springpad::Blocks
4
+ describe Note do
5
+ describe '.process' do
6
+ let(:json) do
7
+ [
8
+ {
9
+ "name" => "Foo",
10
+ "properties" => {
11
+ "text" => "Foo bar baz"
12
+ }
13
+ },
14
+
15
+ {
16
+ "name" => "Bar",
17
+ "properties" => {
18
+ "text" => "Foo bar baz"
19
+ }
20
+ }
21
+ ]
22
+ end
23
+
24
+ it 'processes JSON notes and outputs Note objects' do
25
+ notes = Note.process(json)
26
+ notes.length.must_equal 2
27
+
28
+ notes.first.name.must_equal "Foo"
29
+ notes.first.text.must_equal "Foo bar baz"
30
+
31
+ notes.last.name.must_equal "Bar"
32
+ notes.last.text.must_equal "Foo bar baz"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ require 'test_helper'
2
+
3
+ module Springpad::Blocks
4
+ describe Task do
5
+ describe '.process' do
6
+ let(:json) do
7
+ [
8
+ {
9
+ "name" => "Foo",
10
+ "properties" => {
11
+ "description" => "Foo bar baz",
12
+ "category" => {
13
+ "name" => "Learning"
14
+ }
15
+ }
16
+ },
17
+
18
+ {
19
+ "name" => "Bar",
20
+ "properties" => {
21
+ "description" => "Foo bar baz",
22
+ "category" => {
23
+ "name" => "Learning"
24
+ }
25
+ }
26
+ }
27
+ ]
28
+ end
29
+
30
+ it 'processes JSON tasks and outputs Task objects' do
31
+ tasks = Task.process(json)
32
+ tasks.length.must_equal 2
33
+
34
+ tasks.first.name.must_equal "Foo"
35
+ tasks.first.description.must_equal "Foo bar baz"
36
+ tasks.first.category.must_equal "Learning"
37
+
38
+ tasks.last.name.must_equal "Bar"
39
+ tasks.last.description.must_equal "Foo bar baz"
40
+ tasks.last.category.must_equal "Learning"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,24 @@
1
+ require 'test_helper'
2
+
3
+ module Springpad
4
+ describe CLI do
5
+ describe "#edit" do
6
+ it 'edits a file and returns the contents' do
7
+ cli = CLI.new
8
+
9
+ Tempfile.stubs(:new).returns file = stub_everything
10
+ file.stubs(:path).returns "/tmp/something"
11
+ file.stubs(:read).returns """Name of the task \n\n This is a description of the task\nThis is more body for the task\n"
12
+ cli.expects(:system).with("$EDITOR /tmp/something")
13
+
14
+ cli.edit.must_equal [
15
+ "Name of the task",
16
+ [
17
+ "This is a description of the task",
18
+ "This is more body for the task",
19
+ ]
20
+ ]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ gem 'minitest'
2
+ require 'minitest/spec'
3
+ require 'minitest/autorun'
4
+ require 'mocha'
5
+ require_relative '../lib/springpad'
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: springpad
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Josep M. Bach
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitest
16
+ requirement: &70242399978500 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70242399978500
25
+ - !ruby/object:Gem::Dependency
26
+ name: mocha
27
+ requirement: &70242399977980 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70242399977980
36
+ - !ruby/object:Gem::Dependency
37
+ name: vcr
38
+ requirement: &70242399977480 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70242399977480
47
+ - !ruby/object:Gem::Dependency
48
+ name: webmock
49
+ requirement: &70242399976500 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70242399976500
58
+ - !ruby/object:Gem::Dependency
59
+ name: rest-client
60
+ requirement: &70242399975780 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70242399975780
69
+ - !ruby/object:Gem::Dependency
70
+ name: commander
71
+ requirement: &70242399975160 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *70242399975160
80
+ description: Command-line client for Springpad.
81
+ email:
82
+ - josep.m.bach@gmail.com
83
+ executables:
84
+ - springpad
85
+ extensions: []
86
+ extra_rdoc_files: []
87
+ files:
88
+ - .gitignore
89
+ - Gemfile
90
+ - Rakefile
91
+ - Readme.md
92
+ - bin/springpad
93
+ - lib/springpad.rb
94
+ - lib/springpad/api.rb
95
+ - lib/springpad/blocks.rb
96
+ - lib/springpad/blocks/note.rb
97
+ - lib/springpad/blocks/task.rb
98
+ - lib/springpad/cli.rb
99
+ - lib/springpad/version.rb
100
+ - springpad.gemspec
101
+ - test/springpad/api_test.rb
102
+ - test/springpad/blocks/note_test.rb
103
+ - test/springpad/blocks/task_test.rb
104
+ - test/springpad/cli_test.rb
105
+ - test/test_helper.rb
106
+ homepage: http://github.com/txus/springpad
107
+ licenses: []
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project: springpad
126
+ rubygems_version: 1.8.10
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: Command-line client for Springpad.
130
+ test_files:
131
+ - test/springpad/api_test.rb
132
+ - test/springpad/blocks/note_test.rb
133
+ - test/springpad/blocks/task_test.rb
134
+ - test/springpad/cli_test.rb
135
+ - test/test_helper.rb