liquidplanner 0.0.2 → 0.0.3

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/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