springpad 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.
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