liquidplanner 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -1,3 +1,11 @@
1
1
  0.0.1
2
2
  -----
3
- Initial release
3
+ * Initial release
4
+
5
+ 0.0.2
6
+ -----
7
+ * Ensure non beta ActiveResource libraries are used
8
+
9
+ 0.0.3
10
+ -----
11
+ * Updated for LiquidPlanner 3.0
@@ -1,14 +1,27 @@
1
- All of the examples expect you to pass in your account name and workspace id,
1
+ Command Line Examples:
2
+ ======================
3
+
4
+ Files: create_task.rb, list_tasks.rb, track_time.rb
5
+
6
+ All of these examples expect you to pass in your account name and workspace id,
2
7
  for instance:
3
8
 
4
- ruby examples/list_tasks.rb alice@example.com 7
9
+ ruby examples/list_tasks.rb alice@example.com 7
5
10
 
6
11
  If you set the VERBOSE environment variable, the examples will print out their
7
12
  http requests to the console:
8
13
 
9
- ruby examples/list_tasks.rb alice@example.com 7 --verbose
14
+ ruby examples/list_tasks.rb alice@example.com 7 --verbose
10
15
 
11
16
  These examples all use highline library for prompting the user. Install the
12
17
  gem with:
13
18
 
14
- sudo gem install highline
19
+ sudo gem install highline
20
+
21
+ Source Control Management Examples:
22
+ ===================================
23
+
24
+ Files: post-commit.rb, svn-post-commit.sh
25
+
26
+ This is a working example of integrating LiquidPlanner with SVN. See
27
+ post-commit.rb for more information.
@@ -13,6 +13,11 @@ email, password, space_id = get_credentials!
13
13
  lp = LiquidPlanner::Base.new(:email=>email, :password=>password)
14
14
  workspace = lp.workspaces(space_id)
15
15
 
16
+ unless parent = workspace.packages(:first) || workspace.projects(:first)
17
+ say "There are no packages or projects in this workspace; cannot add a task."
18
+ exit
19
+ end
20
+
16
21
  # Ask for a task's name and estimate
17
22
  say "Add a new task to '#{workspace.name}'"
18
23
  name = ask("New task name")
@@ -21,8 +26,8 @@ high = ask("Max effort", Float){|q| q.above = low}
21
26
 
22
27
  # Submit the task and estimate
23
28
  say "Submitting: '#{name}' [#{low} - #{high}] to LiquidPlanner"
24
- task = workspace.create_task(:name=>name)
29
+ task = workspace.create_task(:name=>name, :parent_id => parent.id)
25
30
  task.create_estimate(:low=>low, :high=>high)
26
31
 
27
32
  # All done
28
- say "Added task"
33
+ say "Added task"
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This script is intended as a commit (or check-in, push, etc.) message
4
+ # processor with your SCM (Subversion, git, etc.) of choice. It scans the
5
+ # message for link(s) to tasks in LiquidPlanner, and creates a comment
6
+ # on each such task containing the text of the commit message.
7
+ #
8
+ # == Prerequisites
9
+ #
10
+ # You'll need a LiquidPlanner account and the liquidplanner gem.
11
+ #
12
+ # == Usage
13
+ #
14
+ # The script takes a single command-line parameter, which is the path to a
15
+ # YAML config file containing your LiquidPlanner credentials.
16
+ #
17
+ # So, you might create a post-commit.yml file containing:
18
+ #
19
+ # email: your_email_address@example.com
20
+ # password: your_liquidplanner_password
21
+ #
22
+ # Then you pass the commit message to the script on its standard input.
23
+ #
24
+ # Initially, try placing a test message (containing at least one link to a
25
+ # task in your LiquidPlanner workspace) in a file named commit_message.txt.
26
+ # Then run a command:
27
+ #
28
+ # cat commit_message.txt | ruby ./post-commit.rb post-commit.yml
29
+ #
30
+ # This should create a comment on the linked task.
31
+ #
32
+ # == Integrating with your SCM
33
+ #
34
+ # Now it's just a matter of invoking this script on each commit to your SCM.
35
+ #
36
+ # Refer to the docs for your system of choice; we've included an example for
37
+ # Subversion in svn-post-commit.sh
38
+ #
39
+
40
+ require 'rubygems'
41
+ require 'liquidplanner'
42
+ require 'cgi'
43
+
44
+ class LiquidPlannerCommitMessenger
45
+
46
+ # pattern for identifying task IDs in the message; this matches URLs copied
47
+ # from LiquidPlanner, but you could choose to adopt a more compact notation
48
+ # and modify this pattern accordingly
49
+ #
50
+ LINK_PATTERN = %r{https://app.liquidplanner.com/space/([0-9]+)/[^\/]+/show\?id=([0-9]+)}
51
+
52
+ def initialize(email, password)
53
+ @lp = LiquidPlanner::Base.new(:email => email, :password => password)
54
+ end
55
+
56
+ # scan the message for one or more links to a LiquidPlanner task; for each
57
+ # such link, create a comment on the referenced task containing the text of
58
+ # the message
59
+ def process_commit_message(message)
60
+ comment = convert_message_to_comment(message)
61
+ message.scan(LINK_PATTERN) do |space_id, task_id|
62
+ add_comment(space_id, task_id, comment)
63
+ end
64
+ end
65
+
66
+ protected
67
+
68
+ # create a comment on the given task in the given space,
69
+ # if they exist
70
+ def add_comment(space_id, task_id, comment)
71
+ if space = @lp.workspaces(space_id)
72
+ if task = space.tasks(task_id)
73
+ task.create_comment(:comment => comment)
74
+ end
75
+ end
76
+ end
77
+
78
+ # convert the commit message to HTML comment
79
+ def convert_message_to_comment(message)
80
+ # escape HTML, so that it can be read as comment text rather than
81
+ # interpreted as markup
82
+ m = CGI::escapeHTML(message)
83
+
84
+ # compress runs of newlines, and convert to br tags
85
+ m = m.gsub(/([\r?\n\r?]){3,}/, "\n\n").gsub(/\r?\n\r?/, "<br/>")
86
+
87
+ m
88
+ end
89
+
90
+ end
91
+
92
+ if $0 == __FILE__
93
+
94
+ config = YAML.load(File.read(ARGV[0]))
95
+ email = config["email"]
96
+ password = config["password"]
97
+
98
+ message = STDIN.read
99
+ LiquidPlannerCommitMessenger.new(email, password).process_commit_message(message)
100
+
101
+ end
102
+
@@ -0,0 +1,11 @@
1
+ #!/bin/sh
2
+ #
3
+ # simple SVN post-commit hook to LiquidPlanner
4
+ #
5
+ PATH=/usr/local/bin:/usr/bin
6
+
7
+ REPO="$1"
8
+ REV="$2"
9
+
10
+ svn log -r $REV $REPO | ./post-commit.rb post-commit.yml
11
+
@@ -2,7 +2,7 @@ $:.unshift(File.dirname(__FILE__)) unless
2
2
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
4
  module LiquidPlanner
5
- VERSION = "0.0.2"
5
+ VERSION = "0.0.3"
6
6
  API_BASE_URL = "https://app.liquidplanner.com/api"
7
7
  end
8
8
 
@@ -20,17 +20,18 @@ require "liquidplanner/resources/account"
20
20
  require "liquidplanner/resources/member"
21
21
  require "liquidplanner/resources/workspace"
22
22
 
23
- require "liquidplanner/resources/relative_resource"
24
- require "liquidplanner/resources/priority"
25
- require "liquidplanner/resources/order"
23
+ require "liquidplanner/resources/move_or_package"
24
+ require "liquidplanner/resources/movable"
25
+ require "liquidplanner/resources/packageable"
26
26
 
27
- require "liquidplanner/resources/item"
27
+ require "liquidplanner/resources/treeitem"
28
28
  require "liquidplanner/resources/container"
29
29
  require "liquidplanner/resources/leaf"
30
+ require "liquidplanner/resources/root"
30
31
  require "liquidplanner/resources/task"
31
32
  require "liquidplanner/resources/event"
32
33
  require "liquidplanner/resources/milestone"
33
- require "liquidplanner/resources/tasklist"
34
+ require "liquidplanner/resources/package"
34
35
  require "liquidplanner/resources/folder"
35
36
  require "liquidplanner/resources/project"
36
37
  require "liquidplanner/resources/client"
@@ -46,4 +47,4 @@ require "liquidplanner/resources/activity"
46
47
 
47
48
  require "liquidplanner/ext/hash"
48
49
  require "liquidplanner/ext/connection"
49
- require "liquidplanner/ext/exceptions"
50
+ require "liquidplanner/ext/exceptions"
@@ -1,7 +1,7 @@
1
1
  module LiquidPlanner
2
2
  # Print out all the outgoing requests from the LiquidPlanner API
3
3
  def self.watch_requests!(&block)
4
- ActiveSupport::Notifications.subscribe('active_resource.request') do |name, time, stamp, id, payload|
4
+ ActiveSupport::Notifications.subscribe('request.active_resource') do |name, time, stamp, id, payload|
5
5
  method = payload[:method]
6
6
  request = payload[:request_uri]
7
7
 
@@ -13,6 +13,10 @@ module LiquidPlanner
13
13
  @prefix_options.merge!(initial_prefix_options){|k, old_attr,new_attr| old_attr || new_attr }
14
14
  self
15
15
  end
16
+
17
+ def get_raw(custom_method_name, options = {})
18
+ connection.get_raw(custom_method_element_url(custom_method_name, options), self.class.headers)
19
+ end
16
20
 
17
21
  private
18
22
  # Override the default instantiate_record to support polymorphic types
@@ -41,7 +45,7 @@ module LiquidPlanner
41
45
  # workspace.create_task(:name=>'new task').create_estimate(:low_effort=>1, :high_effort=>3)
42
46
  def method_missing(name, *args)
43
47
  if name.to_s =~ /^(create|build)_(.+)/
44
- operation = $1.to_sym
48
+ operation = $1.to_sym
45
49
  resource = $2
46
50
  attributes = args.shift || {}
47
51
  klass = LiquidPlanner::Resources.const_get(resource.classify)
@@ -1,7 +1,6 @@
1
1
  module LiquidPlanner
2
2
  module Resources
3
- class Client < Container
4
- include Order
3
+ class Client < Treeitem
5
4
  end
6
5
  end
7
6
  end
@@ -1,6 +1,7 @@
1
1
  module LiquidPlanner
2
2
  module Resources
3
- class Container < Item
3
+ class Container < Treeitem
4
+ include Movable
4
5
  end
5
6
  end
6
7
  end
@@ -29,10 +29,6 @@ module LiquidPlanner
29
29
  end
30
30
  end
31
31
 
32
- def get_raw(custom_method_name, options = {})
33
- connection.get_raw(custom_method_element_url(custom_method_name, options), self.class.headers)
34
- end
35
-
36
32
  def download
37
33
  get_raw(:download)
38
34
  end
@@ -1,7 +1,6 @@
1
1
  module LiquidPlanner
2
2
  module Resources
3
3
  class Folder < Container
4
- include Order
5
4
  end
6
5
  end
7
6
  end
@@ -1,18 +1,22 @@
1
1
  #------------------------------------------------------------------------
2
2
  # leaves
3
3
  #------------------------------------------------------------------------
4
-
5
4
  module LiquidPlanner
6
5
  module Resources
7
- class Leaf < Item
8
- include Priority
9
- include Order
6
+ class Leaf < Treeitem
7
+ include Movable
8
+ include Packageable
10
9
 
11
10
  TRACK_TIME_KEYS = [ :work, :activity_id, :member_id, :low, :high, :is_done, :done_on, :work_performed_on, :comment ].freeze
12
11
  def track_time( options={} )
13
12
  options.assert_valid_keys( *TRACK_TIME_KEYS )
14
13
  request_body = options.to_json
15
- response = post(:track_time, {}, request_body)
14
+ # ActiveResource post() by default:
15
+ # response = post(:track_time, {}, request_body)
16
+ # will set this route with 'new': /api/workspaces/36/tasks/new/activities.json
17
+ # it's because of how it sets @persisted = true by default, calling custom_method_new_element_url()
18
+ # what we want is: /api/workspaces/36/tasks/:id/activities.json, which is accomplished here:
19
+ response = connection.post(custom_method_element_url(:track_time, options), request_body)
16
20
  load( self.class.format.decode( response.body ) )
17
21
  end
18
22
 
@@ -2,6 +2,9 @@ module LiquidPlanner
2
2
  module Resources
3
3
  class Member < LiquidPlanner::LiquidPlannerResource
4
4
  self.prefix = "/api/workspaces/:workspace_id/"
5
+ def avatar
6
+ get_raw(:avatar)
7
+ end
5
8
  end
6
9
  end
7
10
  end
@@ -0,0 +1,13 @@
1
+ module LiquidPlanner
2
+ # For resources that can be moved
3
+ module Movable
4
+ include MoveOrPackage
5
+ def move_before(item, is_packaged_other=false)
6
+ move_or_package :move, :before, item, is_packaged_other
7
+ end
8
+
9
+ def move_after(item, is_packaged_other=false)
10
+ move_or_package :move, :after, item, is_packaged_other
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module LiquidPlanner
2
+ module MoveOrPackage
3
+ def move_or_package(motion, relative, other, is_packaged_other=false)
4
+
5
+ raise ArgumentError.new("motion must be move or package") unless [:move, :package].include?(motion)
6
+ raise ArgumentError.new("relative must be before or after") unless [:before, :after].include?(relative)
7
+ action = "#{motion}_#{relative}"
8
+
9
+ other_id = other.is_a?(LiquidPlanner::LiquidPlannerResource) ? other.id : other
10
+
11
+ params = {}
12
+ if is_packaged_other && :move == motion
13
+ params[:packaged_other_id] = other_id
14
+ else
15
+ params[:other_id] = other_id
16
+ end
17
+
18
+ response = post(action, params)
19
+
20
+ load( self.class.format.decode( response.body ) )
21
+ end
22
+ end
23
+ end
@@ -1,7 +1,7 @@
1
1
  module LiquidPlanner
2
2
  module Resources
3
- class Tasklist < Container
4
- include Priority
3
+ class Package < Container
4
+ include Movable
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,13 @@
1
+ module LiquidPlanner
2
+ # For resources that can be packaged
3
+ module Packageable
4
+ include MoveOrPackage
5
+ def package_before(item)
6
+ move_or_package :package, :before, item
7
+ end
8
+
9
+ def package_after(item)
10
+ move_or_package :package, :after, item
11
+ end
12
+ end
13
+ end
@@ -1,7 +1,6 @@
1
1
  module LiquidPlanner
2
2
  module Resources
3
- class Project < Item
4
- include Order
3
+ class Project < Container
5
4
  end
6
5
  end
7
6
  end
@@ -0,0 +1,6 @@
1
+ module LiquidPlanner
2
+ module Resources
3
+ class Root < Container
4
+ end
5
+ end
6
+ end
@@ -3,16 +3,24 @@
3
3
  #------------------------------------------------------------------------
4
4
  module LiquidPlanner
5
5
  module Resources
6
- class Item < LiquidPlanner::LiquidPlannerResource
6
+ class Treeitem < LiquidPlanner::LiquidPlannerResource
7
7
 
8
8
  self.prefix = "/api/workspaces/:workspace_id/"
9
9
 
10
- def folder
11
- Folder.find( :one, :from => "/api/workspaces/#{workspace_id}/folders/#{folder_id}" )
10
+ def parent
11
+ parent_id && Treeitem.find( :one, :from => "/api/workspaces/#{workspace_id}/treeitems/#{parent_id}" )
12
12
  end
13
13
 
14
- def tasklist
15
- Tasklist.find( :one, :from => "/api/workspaces/#{workspace_id}/tasklists/#{tasklist_id}" )
14
+ def package
15
+ package_id && Package.find( :one, :from => "/api/workspaces/#{workspace_id}/packages/#{package_id}" )
16
+ end
17
+
18
+ def project
19
+ project_id && Project.find( :one, :from => "/api/workspaces/#{workspace_id}/projects/#{project_id}" )
20
+ end
21
+
22
+ def client
23
+ client_id && Client.find( :one, :from => "/api/workspaces/#{workspace_id}/clients/#{client_id}" )
16
24
  end
17
25
 
18
26
  def note
@@ -20,7 +28,11 @@ module LiquidPlanner
20
28
  n.prefix_options = luggage_params
21
29
  end
22
30
  end
23
-
31
+
32
+ def activities( scope=:all )
33
+ Activity.find( scope, :params => luggage_params )
34
+ end
35
+
24
36
  def comments( scope=:all )
25
37
  Comment.find( scope, :params => luggage_params )
26
38
  end
@@ -6,19 +6,40 @@ module LiquidPlanner
6
6
  Member.find( scope, :params => { :workspace_id => self.id }.merge(options) )
7
7
  end
8
8
 
9
+ def treeitems( scope=:all, options={} )
10
+ Treeitem.find( scope, :params => { :workspace_id => self.id, :flat => true }.merge(options) )
11
+ end
12
+
9
13
  def tasks( scope=:all, options={} )
10
14
  Task.find( scope, :params => { :workspace_id => self.id }.merge(options) )
11
15
  end
12
16
 
13
- def folders( scope=:all, options={} )
14
- Folder.find( scope, :params => { :workspace_id => self.id, :flat => true }.merge(options) )
17
+ def events( scope=:all, options={} )
18
+ Event.find( scope, :params => { :workspace_id => self.id }.merge(options) )
19
+ end
20
+
21
+ def milestones( scope=:all, options={} )
22
+ Milestone.find( scope, :params => { :workspace_id => self.id }.merge(options) )
23
+ end
24
+
25
+ def clients( scope=:all, options={} )
26
+ Client.find( scope, :params => { :workspace_id => self.id, :flat => true }.merge(options) )
15
27
  end
16
28
 
17
- def tasklists( scope=:all, options={} )
18
- Tasklist.find( scope, :params => { :workspace_id => self.id, :flat => true }.merge(options) )
29
+ def projects( scope=:all, options={} )
30
+ Project.find( scope, :params => { :workspace_id => self.id, :flat => true }.merge(options) )
31
+ end
32
+
33
+ def packages( scope=:all, options={} )
34
+ Package.find( scope, :params => { :workspace_id => self.id, :flat => true }.merge(options) )
35
+ end
36
+
37
+ def folders( scope=:all, options={} )
38
+ Folder.find( scope, :params => { :workspace_id => self.id, :flat => true }.merge(options) )
19
39
  end
20
40
 
21
- protected
41
+ protected
42
+
22
43
  # create a new instance of klass (Task, Folder, etc.),
23
44
  # with the workspace_id set as a prefix option
24
45
  #
@@ -29,6 +50,7 @@ module LiquidPlanner
29
50
  item.prefix_options[:workspace_id] = self.id
30
51
  end
31
52
  end
53
+
32
54
  end
33
55
  end
34
56
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: liquidplanner
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brett Bender
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-09-02 00:00:00 -07:00
19
+ date: 2012-03-14 00:00:00 -07:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -137,7 +137,9 @@ files:
137
137
  - examples/README
138
138
  - examples/create_task.rb
139
139
  - examples/list_tasks.rb
140
+ - examples/post-commit.rb
140
141
  - examples/support/helper.rb
142
+ - examples/svn-post-commit.sh
141
143
  - examples/track_time.rb
142
144
  - lib/liquidplanner.rb
143
145
  - lib/liquidplanner/base.rb
@@ -155,20 +157,21 @@ files:
155
157
  - lib/liquidplanner/resources/estimate.rb
156
158
  - lib/liquidplanner/resources/event.rb
157
159
  - lib/liquidplanner/resources/folder.rb
158
- - lib/liquidplanner/resources/item.rb
159
160
  - lib/liquidplanner/resources/leaf.rb
160
161
  - lib/liquidplanner/resources/link.rb
161
162
  - lib/liquidplanner/resources/luggage.rb
162
163
  - lib/liquidplanner/resources/member.rb
163
164
  - lib/liquidplanner/resources/milestone.rb
165
+ - lib/liquidplanner/resources/movable.rb
166
+ - lib/liquidplanner/resources/move_or_package.rb
164
167
  - lib/liquidplanner/resources/note.rb
165
- - lib/liquidplanner/resources/order.rb
166
- - lib/liquidplanner/resources/priority.rb
168
+ - lib/liquidplanner/resources/package.rb
169
+ - lib/liquidplanner/resources/packageable.rb
167
170
  - lib/liquidplanner/resources/project.rb
168
- - lib/liquidplanner/resources/relative_resource.rb
171
+ - lib/liquidplanner/resources/root.rb
169
172
  - lib/liquidplanner/resources/snapshot.rb
170
173
  - lib/liquidplanner/resources/task.rb
171
- - lib/liquidplanner/resources/tasklist.rb
174
+ - lib/liquidplanner/resources/treeitem.rb
172
175
  - lib/liquidplanner/resources/workspace.rb
173
176
  - test/liquidplanner_resource_test.rb
174
177
  - test/workspace_test.rb
@@ -177,8 +180,8 @@ homepage: http://github.com/liquidplanner/liquidplanner
177
180
  licenses: []
178
181
 
179
182
  post_install_message:
180
- rdoc_options:
181
- - --charset=UTF-8
183
+ rdoc_options: []
184
+
182
185
  require_paths:
183
186
  - lib
184
187
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -1,13 +0,0 @@
1
- module LiquidPlanner
2
- # For resources that can be prioritized
3
- module Order
4
- include RelativeResource
5
- def organize_before(item)
6
- move_relative_to :organize, :before, item
7
- end
8
-
9
- def organize_after(item)
10
- move_relative_to :organize, :after, item
11
- end
12
- end
13
- end
@@ -1,13 +0,0 @@
1
- module LiquidPlanner
2
- # For resources that can be prioritized
3
- module Priority
4
- include RelativeResource
5
- def prioritize_before(item)
6
- move_relative_to :prioritize, :before, item
7
- end
8
-
9
- def prioritize_after(item)
10
- move_relative_to :prioritize, :after, item
11
- end
12
- end
13
- end
@@ -1,12 +0,0 @@
1
- module LiquidPlanner
2
- module RelativeResource
3
- def move_relative_to(tree, relative, other)
4
- raise ArgumentError.new("tree must be prioritize or organize") unless [:prioritize, :organize].include?(tree)
5
- raise ArgumentError.new("relative must be before or after") unless [:before, :after].include?(relative)
6
- other_id = other.is_a?(LiquidPlanner::LiquidPlannerResource) ? other.id : other
7
-
8
- response = post("#{tree}_#{relative}", :other_id=>other_id)
9
- load( self.class.format.decode( response.body ) )
10
- end
11
- end
12
- end