kenai_tools 0.0.7
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 +8 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/kenai_tools.iml +40 -0
- data/.idea/misc.xml +8 -0
- data/.idea/modules.xml +9 -0
- data/.idea/vcs.xml +7 -0
- data/Gemfile +4 -0
- data/README.md +28 -0
- data/Rakefile +2 -0
- data/bin/dlutil +139 -0
- data/kenai_tools.gemspec +43 -0
- data/lib/kenai_tools/downloads_client.rb +241 -0
- data/lib/kenai_tools/kenai_client.rb +168 -0
- data/lib/kenai_tools/version.rb +3 -0
- data/lib/kenai_tools.rb +7 -0
- data/spec/downloads_client_spec.rb +302 -0
- data/spec/fixtures/data/irs_docs/irs-form-1040.pdf +0 -0
- data/spec/fixtures/data/irs_docs/irs-p555.pdf +0 -0
- data/spec/fixtures/data/sax.tgz +0 -0
- data/spec/fixtures/data/sax2/.cvsignore +9 -0
- data/spec/fixtures/data/sax2/CHANGES +245 -0
- data/spec/fixtures/data/sax2/COPYING +12 -0
- data/spec/fixtures/data/sax2/ChangeLog +666 -0
- data/spec/fixtures/data/sax2/Makefile +77 -0
- data/spec/fixtures/data/sax2/README +62 -0
- data/spec/fixtures/data/sax2/build.xml +68 -0
- data/spec/fixtures/data/sax2/src/SAXDump.java +238 -0
- data/spec/fixtures/data/sax2/src/SAXTest.java +351 -0
- data/spec/fixtures/data/sax2/src/org/xml/sax/Attributes.java +257 -0
- data/spec/fixtures/data/sax2/src/org/xml/sax/package.html +297 -0
- data/spec/fixtures/data/sax2r2.jar +0 -0
- data/spec/fixtures/data/text1.txt +4 -0
- data/spec/spec_helper.rb +9 -0
- metadata +222 -0
data/.gitignore
ADDED
data/.idea/.name
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
kenai_tools
|
data/.idea/.rakeTasks
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<Settings><!--This file was automatically generated by Ruby plugin.
|
3
|
+
You are allowed to:
|
4
|
+
1. Remove rake task
|
5
|
+
2. Add existing rake tasks
|
6
|
+
To add existing rake tasks automatically delete this file and reload the project.
|
7
|
+
--><RakeGroup description="" fullCmd="" taksId="rake"><RakeTask description="Build kenai_tools-0.0.1.gem into the pkg directory" fullCmd="build" taksId="build" /><RakeTask description="Build and install kenai_tools-0.0.1.gem into system gems" fullCmd="install" taksId="install" /><RakeTask description="Create tag v0.0.1 and build and push kenai_tools-0.0.1.gem to Rubygems" fullCmd="release" taksId="release" /></RakeGroup></Settings>
|
data/.idea/encodings.xml
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<module type="RUBY_MODULE" version="4">
|
3
|
+
<component name="GemRequirementsHolder" version="3">
|
4
|
+
<requirement>
|
5
|
+
<requirement>
|
6
|
+
<dependency name="highline" version="1.6" bound="LATEST_IN_BRANCH" git="false" path="false" doRequire="true" />
|
7
|
+
</requirement>
|
8
|
+
<source from="Gemfile" />
|
9
|
+
</requirement>
|
10
|
+
<requirement>
|
11
|
+
<requirement>
|
12
|
+
<dependency name="json" version="1.5" bound="LATEST_IN_BRANCH" git="false" path="false" doRequire="true" />
|
13
|
+
</requirement>
|
14
|
+
<source from="Gemfile" />
|
15
|
+
</requirement>
|
16
|
+
<requirement>
|
17
|
+
<requirement>
|
18
|
+
<dependency name="rest-client" version="1.6" bound="LATEST_IN_BRANCH" git="false" path="false" doRequire="true" />
|
19
|
+
</requirement>
|
20
|
+
<source from="Gemfile" />
|
21
|
+
</requirement>
|
22
|
+
</component>
|
23
|
+
<component name="NewModuleRootManager">
|
24
|
+
<content url="file://$MODULE_DIR$" />
|
25
|
+
<orderEntry type="inheritedJdk" />
|
26
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
27
|
+
<orderEntry type="library" scope="PROVIDED" name="rspec (v2.5.0, RVM: ruby-1.8.7-p302 [kenai_tools]) [gem]" level="application" />
|
28
|
+
<orderEntry type="library" scope="PROVIDED" name="rest-client (v1.6.3, RVM: ruby-1.8.7-p302 [kenai_tools]) [gem]" level="application" />
|
29
|
+
<orderEntry type="library" scope="PROVIDED" name="rspec-core (v2.5.2, RVM: ruby-1.8.7-p302 [kenai_tools]) [gem]" level="application" />
|
30
|
+
<orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.1.2, RVM: ruby-1.8.7-p302 [kenai_tools]) [gem]" level="application" />
|
31
|
+
<orderEntry type="library" scope="PROVIDED" name="highline (v1.6.2, RVM: ruby-1.8.7-p302 [kenai_tools]) [gem]" level="application" />
|
32
|
+
<orderEntry type="library" scope="PROVIDED" name="bundler (v1.0.12, RVM: ruby-1.8.7-p302 [kenai_tools]) [gem]" level="application" />
|
33
|
+
<orderEntry type="library" scope="PROVIDED" name="mime-types (v1.16, RVM: ruby-1.8.7-p302 [kenai_tools]) [gem]" level="application" />
|
34
|
+
<orderEntry type="library" scope="PROVIDED" name="json (v1.5.3, RVM: ruby-1.8.7-p302 [kenai_tools]) [gem]" level="application" />
|
35
|
+
<orderEntry type="library" scope="PROVIDED" name="gemcutter (v0.7.0, RVM: ruby-1.8.7-p302 [kenai_tools]) [gem]" level="application" />
|
36
|
+
<orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v2.5.0, RVM: ruby-1.8.7-p302 [kenai_tools]) [gem]" level="application" />
|
37
|
+
<orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v2.5.0, RVM: ruby-1.8.7-p302 [kenai_tools]) [gem]" level="application" />
|
38
|
+
</component>
|
39
|
+
</module>
|
40
|
+
|
data/.idea/misc.xml
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project version="4">
|
3
|
+
<component name="DependencyValidationManager">
|
4
|
+
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
|
5
|
+
</component>
|
6
|
+
<component name="ProjectRootManager" version="2" project-jdk-name="RVM: ruby-1.8.7-p302 [kenai_tools]" project-jdk-type="RUBY_SDK" />
|
7
|
+
</project>
|
8
|
+
|
data/.idea/modules.xml
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project version="4">
|
3
|
+
<component name="ProjectModuleManager">
|
4
|
+
<modules>
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/kenai_tools.iml" filepath="$PROJECT_DIR$/.idea/kenai_tools.iml" />
|
6
|
+
</modules>
|
7
|
+
</component>
|
8
|
+
</project>
|
9
|
+
|
data/.idea/vcs.xml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Kenai Tools
|
2
|
+
|
3
|
+
## Description
|
4
|
+
|
5
|
+
The kenai_tools gem provides command line tools to access web services for
|
6
|
+
web sites that are hosted on the Kenai application platform. Currently,
|
7
|
+
there is a dlutil tool that project admins can use to manage the downloads
|
8
|
+
feature of a project. For example, they can perform bulk uploads and
|
9
|
+
downloads of files to a site hosted on Kenai such as java.net.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
$ gem install kenai_tools
|
14
|
+
|
15
|
+
or
|
16
|
+
|
17
|
+
$ sudo gem install kenai_tools
|
18
|
+
|
19
|
+
## Usage and documentation
|
20
|
+
|
21
|
+
Please see the command usage information.
|
22
|
+
|
23
|
+
$ dlutil --help
|
24
|
+
|
25
|
+
## Problems
|
26
|
+
|
27
|
+
Some dlutil commands do not work on Windows so a unix-based OS is
|
28
|
+
recommended.
|
data/Rakefile
ADDED
data/bin/dlutil
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require "bundler/setup"
|
5
|
+
|
6
|
+
$LOAD_PATH << File.dirname(__FILE__) + '/../lib'
|
7
|
+
require 'ostruct'
|
8
|
+
require 'optparse'
|
9
|
+
require 'highline/import'
|
10
|
+
require 'kenai_tools'
|
11
|
+
|
12
|
+
PROGRAM = File.basename(__FILE__)
|
13
|
+
|
14
|
+
# Array of command info. Keys are
|
15
|
+
# :name = command line name
|
16
|
+
# :call = name of method to call instead of :name, if specified
|
17
|
+
# :result = method produces a result
|
18
|
+
# :login = method requires authentication
|
19
|
+
# :core = core, i.e. main command
|
20
|
+
COMMANDS = [{:name => :ping, :result => true, :core => true},
|
21
|
+
{:name => :login, :call => :ping, :login => true, :result => true, :core => true},
|
22
|
+
{:name => :pull, :core => true},
|
23
|
+
{:name => :push, :login => true, :core => true},
|
24
|
+
{:name => :exist?, :result => true},
|
25
|
+
{:name => :entry_type, :result => true},
|
26
|
+
{:name => :mkdir, :login => true},
|
27
|
+
{:name => :rmdir, :login => true, :result => nil},
|
28
|
+
{:name => :rm, :login => true},
|
29
|
+
{:name => :rm_r, :login => true},
|
30
|
+
{:name => :delete_feature, :login => true},
|
31
|
+
{:name => :get_or_create, :login => true, :result => true},
|
32
|
+
{:name => :downloads_name, :result => true},
|
33
|
+
{:name => :downloads_feature, :result => true},
|
34
|
+
{:name => :ls, :result => true}]
|
35
|
+
|
36
|
+
options = OpenStruct.new
|
37
|
+
@opts = OptionParser.new do |opts|
|
38
|
+
opts.banner = "Usage: #{PROGRAM} [options] COMMAND [args] ..."
|
39
|
+
|
40
|
+
def wrap(s, width = 78)
|
41
|
+
s.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
str = COMMANDS.select { |info| info[:core] }.map { |info| info[:name] }.join(', ')
|
45
|
+
opts.separator "Main commands: #{wrap(str)}"
|
46
|
+
str = COMMANDS.reject { |info| info[:core] }.map { |info| info[:name] }.join(', ')
|
47
|
+
opts.separator "Other commands: #{wrap(str)}"
|
48
|
+
opts.separator "Note: Unix OS is recommended as some commands fail on Windows"
|
49
|
+
opts.separator "Examples:"
|
50
|
+
opts.separator " #{PROGRAM} -r kenai.com,my-project pull / my-project-downloads"
|
51
|
+
opts.separator " #{PROGRAM} -r java.net,my-project push dist/ /version-3.1"
|
52
|
+
opts.separator " #{PROGRAM} -r java.net,my-project push toolkit-1.2.3.jar /"
|
53
|
+
opts.separator " #{PROGRAM} -r java.net,glassfish login"
|
54
|
+
opts.separator " #{PROGRAM} -r java.net,my-project rm /toolkit-1.2.3.jar"
|
55
|
+
opts.separator " #{PROGRAM} -r java.net,my-project rm_r /version-3.1"
|
56
|
+
opts.separator ""
|
57
|
+
opts.separator "Specific options:"
|
58
|
+
|
59
|
+
def to_url(host_or_url)
|
60
|
+
host_or_url =~ %r{^http(s?)://} ? host_or_url : "https://#{host_or_url}/"
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on("-r", "--remote ENDPOINT_SPEC", "Remote endpoint SITE,PROJECT,[DOWNLOADS_NAME]",
|
64
|
+
" SITE may be simply HOST or http(s)?://HOST:PORT") do |v|
|
65
|
+
host, project, downloads_name = v.split(',')
|
66
|
+
options.remote_valid = host && project
|
67
|
+
options.host_url = to_url(host)
|
68
|
+
options.project = project
|
69
|
+
options.downloads_name = downloads_name
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on("-p", "--password-file FILE", "Read username/password (nl-separated) from FILE") do |v|
|
73
|
+
options.password_file = v
|
74
|
+
end
|
75
|
+
|
76
|
+
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
77
|
+
options.verbose = v
|
78
|
+
end
|
79
|
+
|
80
|
+
opts.on("-t", "--timeout SECS", "Timeout in seconds") do |v|
|
81
|
+
options.timeout = v
|
82
|
+
end
|
83
|
+
|
84
|
+
opts.on("--cloak-password PASSWORD", "Cloaking password") do |v|
|
85
|
+
options.cloak_password = v
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
89
|
+
puts opts
|
90
|
+
exit
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def usage
|
95
|
+
puts @opts
|
96
|
+
exit 1
|
97
|
+
end
|
98
|
+
|
99
|
+
begin
|
100
|
+
@opts.parse!
|
101
|
+
rescue
|
102
|
+
usage
|
103
|
+
end
|
104
|
+
|
105
|
+
usage unless command = ARGV.shift and
|
106
|
+
cmd_info = COMMANDS.find { |info| info[:name] == command.to_sym } and
|
107
|
+
options.remote_valid
|
108
|
+
|
109
|
+
def get_credentials(options, dlc_opts)
|
110
|
+
user, password = nil
|
111
|
+
if options.password_file
|
112
|
+
File.open(options.password_file) do |f|
|
113
|
+
user, password = f.read.split
|
114
|
+
end
|
115
|
+
else
|
116
|
+
say "Please enter your login credentials for #{options.host_url}"
|
117
|
+
user = ask('Username: ')
|
118
|
+
password = ask('Password: ') { |q| q.echo = '*' }
|
119
|
+
end
|
120
|
+
if user
|
121
|
+
dlc_opts[:user] = user
|
122
|
+
dlc_opts[:password] = password
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
dlc_opts = {:downloads_name => options.downloads_name}
|
127
|
+
if cmd_info[:login]
|
128
|
+
get_credentials(options, dlc_opts)
|
129
|
+
end
|
130
|
+
dlc_opts[:timeout] = options.timeout if options.timeout
|
131
|
+
dlc_opts[:cloak_password] = options.cloak_password if options.cloak_password
|
132
|
+
dlc_opts[:log] = $stderr if options.verbose
|
133
|
+
dlclient = KenaiTools::DownloadsClient.new(options.host_url, options.project, dlc_opts)
|
134
|
+
|
135
|
+
method_name = cmd_info[:call] ? cmd_info[:call] : cmd_info[:name]
|
136
|
+
result = dlclient.send(method_name, *ARGV)
|
137
|
+
if cmd_info[:result]
|
138
|
+
puts String === result ? result : result.inspect
|
139
|
+
end
|
data/kenai_tools.gemspec
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "kenai_tools/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "kenai_tools"
|
7
|
+
s.version = KenaiTools::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Edwin Goei", "Project Kenai Team"]
|
10
|
+
s.email = ["edwin.goei@oracle.com"]
|
11
|
+
s.homepage = "http://kenai.com/projects/kenaiapis"
|
12
|
+
s.summary = %q{Tools for sites hosted on the Kenai platform. Use dlutil to upload and download files.}
|
13
|
+
s.description = %q{Tools for sites such as java.net that are hosted on the Kenai platform. Use dlutil to upload and download files.}
|
14
|
+
s.post_install_message = %q{
|
15
|
+
==============================================================================
|
16
|
+
|
17
|
+
Thanks for installing kenai_tools. Run the following command for what to do
|
18
|
+
next:
|
19
|
+
|
20
|
+
dlutil --help
|
21
|
+
|
22
|
+
Warning: this tool is not yet supported on Windows. Please use a unix-based
|
23
|
+
OS. For more info, see http://kenai.com/jira/browse/KENAI-2853.
|
24
|
+
|
25
|
+
==============================================================================
|
26
|
+
|
27
|
+
|
28
|
+
}
|
29
|
+
|
30
|
+
s.rubyforge_project = "kenai_tools"
|
31
|
+
|
32
|
+
s.files = `git ls-files`.split("\n")
|
33
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
34
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
35
|
+
s.require_paths = ["lib"]
|
36
|
+
|
37
|
+
s.add_development_dependency("rspec", "~> 2.5")
|
38
|
+
s.add_development_dependency("bundler", "~> 1.0")
|
39
|
+
s.add_development_dependency("gemcutter")
|
40
|
+
s.add_dependency("rest-client", "~> 1.6")
|
41
|
+
s.add_dependency("json", "~> 1.5")
|
42
|
+
s.add_dependency("highline", "~> 1.6")
|
43
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'forwardable'
|
5
|
+
require 'rest_client'
|
6
|
+
|
7
|
+
module KenaiTools
|
8
|
+
# Path arguments to public methods of this API work with Pathname objects as well as Strings
|
9
|
+
class DownloadsClient
|
10
|
+
CONTENT_TYPE_KENAI_ENTRIES = "application/vnd.com.kenai.entries+json"
|
11
|
+
|
12
|
+
extend Forwardable
|
13
|
+
def_delegators :@kc, :authenticate, :authenticated?
|
14
|
+
|
15
|
+
attr_accessor :project, :cloak_password
|
16
|
+
|
17
|
+
#
|
18
|
+
# Local options are :downloads_name, :log, :cloak_password
|
19
|
+
# Other options such as :timeout are forwarded
|
20
|
+
# DownloadClient.new("https://kenai.com/", "jruby")
|
21
|
+
# DownloadClient.new("https://testkenai.com/", "my-project", :downloads_name => 'downloads2', :log => $stderr
|
22
|
+
# :timeout => 36000, :cloak_password => 'a_secret')
|
23
|
+
#
|
24
|
+
def initialize(site, project, opts = {})
|
25
|
+
@site = site
|
26
|
+
@project = project
|
27
|
+
@specified_downloads_name = opts.delete(:downloads_name)
|
28
|
+
@cloak_password = opts.delete(:cloak_password)
|
29
|
+
RestClient.log = opts.delete(:log)
|
30
|
+
@kc = KenaiClient.new(site, opts)
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_or_create
|
34
|
+
unless ping
|
35
|
+
@specified_downloads_name = "downloads" unless @specified_downloads_name
|
36
|
+
params = {:feature => {:name => @specified_downloads_name, :service => "downloads",
|
37
|
+
:display_name => @specified_downloads_name.capitalize}}
|
38
|
+
@kc["projects/#{project}/features"].post(params, :content_type => 'application/json')
|
39
|
+
end
|
40
|
+
downloads_name
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete_feature(confirm = nil)
|
44
|
+
unless confirm == 'yes'
|
45
|
+
fail "Confirm delete project downloads feature with an argument of 'yes'"
|
46
|
+
else
|
47
|
+
# Flush cache
|
48
|
+
orig_downloads_name = downloads_name
|
49
|
+
@downloads_feature = @downloads_name = nil
|
50
|
+
@kc["projects/#{project}/features/#{orig_downloads_name}"].delete
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Returns the specified downloads feature if found, or a discovered one
|
56
|
+
# if the project only has one
|
57
|
+
#
|
58
|
+
def downloads_feature
|
59
|
+
@downloads_feature ||= begin
|
60
|
+
project = @kc.project(@project)
|
61
|
+
features = project && project['features']
|
62
|
+
if downloads_features = features && features.select { |f| f['type'] == 'downloads' }
|
63
|
+
if @specified_downloads_name
|
64
|
+
downloads_features.detect { |f| f['name'] == @specified_downloads_name }
|
65
|
+
elsif downloads_features.size == 1
|
66
|
+
downloads_features.first
|
67
|
+
else
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
else
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def downloads_name
|
77
|
+
@downloads_name ||= downloads_feature && downloads_feature['name']
|
78
|
+
end
|
79
|
+
|
80
|
+
alias_method :ping, :downloads_name
|
81
|
+
|
82
|
+
def ls(path = '/')
|
83
|
+
entry(path)
|
84
|
+
end
|
85
|
+
|
86
|
+
def exist?(path = '/')
|
87
|
+
!!entry(path)
|
88
|
+
end
|
89
|
+
|
90
|
+
def entry_type(path)
|
91
|
+
if h = entry(path)
|
92
|
+
h['entry_type']
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Pull a remote file or directory hierarchy to the local host. Use
|
98
|
+
# +remote_path+ to specify the remote file or directory hierarchy to
|
99
|
+
# download. If +remote_path+ is '/', download all content. Use
|
100
|
+
# +local_dest_dir+ to specify the target location for the content which
|
101
|
+
# defaults to the current directory.
|
102
|
+
#
|
103
|
+
# For example:
|
104
|
+
# dlclient.pull('version-1.9')
|
105
|
+
# dlclient.pull('version-1.9', '/tmp/project_downloads')
|
106
|
+
#
|
107
|
+
def pull(remote_path, local_dest_dir = '.')
|
108
|
+
dest = Pathname(local_dest_dir)
|
109
|
+
fail "Destination must be a directory" unless dest.directory?
|
110
|
+
remote_pn = clean_id_pn(remote_path)
|
111
|
+
if ent = entry(remote_pn)
|
112
|
+
display_name = ent['display_name']
|
113
|
+
basename = display_name == '/' ? '.' : display_name
|
114
|
+
local_path = dest + basename
|
115
|
+
case ent['entry_type']
|
116
|
+
when 'directory'
|
117
|
+
local_path.mkdir unless local_path.exist?
|
118
|
+
ent['children'].each do |ch|
|
119
|
+
remote_child = remote_pn + ch['display_name']
|
120
|
+
pull(remote_child, local_path)
|
121
|
+
end
|
122
|
+
when 'file'
|
123
|
+
content = get_cloaked_url(ent['content_url'])
|
124
|
+
local_path.open("w") { |f| f.write(content) }
|
125
|
+
else
|
126
|
+
puts "Warning: skipping unsupported entry type"
|
127
|
+
end
|
128
|
+
else
|
129
|
+
puts "Unknown downloads entry: #{remote_pn}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# Push a local file or directory hierarchy to the server. Use
|
135
|
+
# +local_path+ path to specify the local file or directory to upload. If
|
136
|
+
# +local_path+ is a directory that ends in '/', upload the contents of
|
137
|
+
# that directory instead of the directory and its contents. Use
|
138
|
+
# +remote_dir+ to specify the remote directory to upload to.
|
139
|
+
#
|
140
|
+
# For example:
|
141
|
+
# dlclient.push('version-1.9')
|
142
|
+
# dlclient.push('dist', '/version-1.9')
|
143
|
+
#
|
144
|
+
def push(local_path, remote_dir = '/', opts = {})
|
145
|
+
src = Pathname(local_path)
|
146
|
+
if src.directory?
|
147
|
+
if src.to_s.end_with?('/')
|
148
|
+
target_dir = remote_dir
|
149
|
+
else
|
150
|
+
target_dir = Pathname(remote_dir) + src.basename
|
151
|
+
mkdir(target_dir)
|
152
|
+
end
|
153
|
+
src.children.each do |ch|
|
154
|
+
push(ch, target_dir)
|
155
|
+
end
|
156
|
+
else
|
157
|
+
remote_id = Pathname(remote_dir) + src.basename
|
158
|
+
entry = {:content_data => File.new(src)}.merge(opts)
|
159
|
+
begin
|
160
|
+
@kc[entry_api_path(remote_id)].put(:entry => entry)
|
161
|
+
rescue => ex
|
162
|
+
err_msg = "Error: unable to upload to target '#{remote_id}'"
|
163
|
+
if server_msg = extract_error_message(ex)
|
164
|
+
err_msg += ": #{server_msg}"
|
165
|
+
end
|
166
|
+
$stderr.puts err_msg
|
167
|
+
raise ex
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def mkdir(dir, opts = {})
|
173
|
+
path = Pathname(dir)
|
174
|
+
entry = {:display_name => path.basename}
|
175
|
+
entry.merge(opts)
|
176
|
+
@kc[entry_api_path(path)].put(:entry => entry)
|
177
|
+
end
|
178
|
+
|
179
|
+
def rm_r(path)
|
180
|
+
@kc[entry_api_path(path)].delete
|
181
|
+
end
|
182
|
+
|
183
|
+
def rm(path)
|
184
|
+
if entry_type(path) == 'directory'
|
185
|
+
fail "Entry is a directory: #{path}"
|
186
|
+
else
|
187
|
+
@kc[entry_api_path(path)].delete
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def rmdir(path)
|
192
|
+
entry = entry(path)
|
193
|
+
if entry['entry_type'] == 'directory'
|
194
|
+
if entry['children'].size == 0
|
195
|
+
@kc[entry_api_path(path)].delete
|
196
|
+
else
|
197
|
+
fail "Directory not empty: #{path}"
|
198
|
+
end
|
199
|
+
else
|
200
|
+
fail "Not a directory: #{path}"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
private
|
205
|
+
|
206
|
+
# Canonicalize an entry id and return a relative Pathname or root Pathname
|
207
|
+
def clean_id_pn(remote_path)
|
208
|
+
remote_pn = Pathname(remote_path)
|
209
|
+
root = Pathname('/')
|
210
|
+
if remote_pn.absolute? && remote_pn != root
|
211
|
+
remote_pn.relative_path_from(root)
|
212
|
+
else
|
213
|
+
remote_pn
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def entry_api_path(id)
|
218
|
+
fail "Downloads feature not found" unless downloads_name
|
219
|
+
escaped_id = URI.escape(clean_id_pn(id).to_s)
|
220
|
+
"projects/#{@project}/features/#{downloads_name}/entries/#{escaped_id}"
|
221
|
+
end
|
222
|
+
|
223
|
+
def entry(path)
|
224
|
+
begin
|
225
|
+
resource = @kc[entry_api_path(path)].get
|
226
|
+
rescue RestClient::ResourceNotFound => err
|
227
|
+
return nil
|
228
|
+
end
|
229
|
+
JSON.parse(resource)
|
230
|
+
end
|
231
|
+
|
232
|
+
def get_cloaked_url(url)
|
233
|
+
opts = @cloak_password ? {:user => "dont_care", :password => @cloak_password} : {}
|
234
|
+
RestClient::Resource.new(url, opts).get
|
235
|
+
end
|
236
|
+
|
237
|
+
def extract_error_message(ex)
|
238
|
+
ex.response if ex.respond_to?(:response)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|