gooddata 0.2.0

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (BSD License)
2
+
3
+ Copyright (c) 2010 Thomas Watson Steen & GoodData Corporation. All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided
6
+ that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and
9
+ the following disclaimer.
10
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
11
+ and the following disclaimer in the documentation and/or other materials provided with the distribution.
12
+ * Neither the name of the GoodData Corporation nor the names of its contributors may be used to endorse
13
+ or promote products derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
16
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
18
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,97 @@
1
+ = GoodData Ruby wrapper and CLI
2
+
3
+ A convenient Ruby wrapper around the GoodData RESTful API.
4
+
5
+ Use the Gooddata::Client class to integrate GoodData into your own application
6
+ or use the CLI to work with GoodData directly from the command line.
7
+
8
+ The best documentation for the API can be found using these resources:
9
+ * http://developer.gooddata.com/api
10
+ * https://secure.gooddata.com/gdc
11
+
12
+ == Usage
13
+
14
+ Since this project is in its early stages, no gem have been released to rubygems.org yet.
15
+ To install the gooddata gem, you instead have to first checkout this repository. Then install
16
+ the gem using the rake task:
17
+
18
+ rake install
19
+
20
+ Now you are ready to use either the Ruby wrapper class or the CLI.
21
+
22
+ Alternatively you can run the source code directly. For details about this approach, see {the
23
+ Development Wiki page}[http://github.com/gooddata/gooddata-ruby/wiki/Development].
24
+
25
+ === Wrapper class usage
26
+
27
+ For details of how to use the Ruby wrapper class, see the Gooddata::Client RDoc.
28
+
29
+ === CLI Usage
30
+
31
+ After installing the gooddata gem, GoodData is available from your command line using
32
+ the <tt>gooddata</tt> command. To get a complete overview of possible options type:
33
+
34
+ gooddata help
35
+
36
+ The examples and descriptions below does not cover all the options available via the CLI.
37
+ So remember to refer back to the <tt>help</tt> command.
38
+
39
+ Before you do anything else, a good idea is to see if your account is set up correctly and
40
+ that you can log in. To do this, use the <tt>api:test</tt> command:
41
+
42
+ gooddata api:test
43
+
44
+ ==== Authentication
45
+
46
+ As you saw if you ran the above test command <tt>gooddata</tt> will prompt you
47
+ for your GoodData username and password. If you don't wish to write your
48
+ credentials each time you connect to GoodData using <tt>gooddata</tt>, you can
49
+ create a simple gooddata credentials file called <tt>.gooddata</tt> in the root
50
+ of your home directory. To make it easy you can just run the credentials file
51
+ generator command which will create the file for you:
52
+
53
+ gooddata auth:store
54
+
55
+ ==== List available projects
56
+
57
+ To get a list of projects available to your GoodData user account, run:
58
+
59
+ gooddata projects
60
+
61
+ The output from the above command will look similar to this:
62
+
63
+ 521 Some project
64
+ 3521 Some other project
65
+ 3642 Some third project
66
+
67
+ The first column contains the project-key. You need this if you wan't to either
68
+ see more details about the project using the <tt>projects:show</tt> comamnd or
69
+ if you wish to delete the project using the <tt>projects:delete</tt> command.
70
+
71
+ ==== Create a new project
72
+
73
+ To create a new project under on the GoodData servers, run:
74
+
75
+ gooddata projects:create
76
+
77
+ You will then be asked about the desired project name and summary before it's created.
78
+
79
+ == Note on Patches/Pull Requests
80
+
81
+ * Fork the project.
82
+ * Make your feature addition or bug fix.
83
+ * Add tests for it. This is important so I don't break it in a
84
+ future version unintentionally.
85
+ * Commit, do not mess with rakefile, version, or history.
86
+ (if you want to have your own version, that is fine but bump version in a commit by itself we can ignore when we pull)
87
+ * Send us a pull request. Bonus points for topic branches.
88
+
89
+ == Credits
90
+
91
+ This project is developed and maintained by Pavel Kolesnikov [ mailto:pavel@gooddata.com
92
+ / {@koles}[http:/twitter.com/koles] ] and Thomas Watson Steen [ mailto:w@tson.dk /
93
+ {@wa7son}[http://twitter.com/wa7son] ]
94
+
95
+ == Copyright
96
+
97
+ Copyright (c) 2010 - 2011 GoodData Corporation and Thomas Watson Steen. See LICENSE for details.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'logger'
4
+
5
+ require 'gooddata'
6
+ require 'gooddata/command'
7
+
8
+ args = ARGV.dup
9
+ ARGV.clear
10
+ command = args.shift.strip rescue 'help'
11
+
12
+ GoodData.logger = Logger.new(STDOUT) if ENV['DEBUG']
13
+ GoodData::Command.run command, args
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'gooddata'
4
+ require 'gooddata/command'
5
+
6
+ require 'irb'
7
+
8
+ include GoodData
9
+
10
+ module IRB
11
+ def IRB.start2(bind, ap_path)
12
+ IRB.setup(ap_path)
13
+ irb = Irb.new(WorkSpace.new(bind))
14
+ @CONF[:MAIN_CONTEXT] = irb.context
15
+ trap("SIGINT") do
16
+ irb.signal_handle
17
+ end
18
+ catch(:IRB_EXIT) do
19
+ irb.eval_input
20
+ end
21
+ end
22
+ end
23
+
24
+ param = ARGV.shift if ARGV.length
25
+ if param == '--debug' then
26
+ require 'logger'
27
+ GoodData.logger = Logger.new STDOUT
28
+ end
29
+ Command.connect
30
+ puts "Logged into GoodData as #{GoodData.profile.user} (#{GoodData.profile['login']})"
31
+ puts
32
+ IRB::start2 binding, $STDIN
33
+ puts "Logging out"
@@ -0,0 +1,3 @@
1
+ module GoodData; end
2
+
3
+ require 'gooddata/client'
@@ -0,0 +1,196 @@
1
+ require 'gooddata/version'
2
+ require 'gooddata/connection'
3
+ Dir[File.dirname(__FILE__) + '/models/*.rb'].each { |file| require file }
4
+ Dir[File.dirname(__FILE__) + '/collections/*.rb'].each { |file| require file }
5
+
6
+ # = GoodData API wrapper
7
+ #
8
+ # A convenient Ruby wrapper around the GoodData RESTful API.
9
+ #
10
+ # The best documentation for the API can be found using these resources:
11
+ # * http://developer.gooddata.com/api
12
+ # * https://secure.gooddata.com/gdc
13
+ #
14
+ # == Usage
15
+ #
16
+ # To communicate with the API you first need a personal GoodData account.
17
+ # {Sign up here}[https://secure.gooddata.com/registration.html] if you havent already.
18
+ #
19
+ # Now it is just a matter of initializing the GoodData connection via the connect
20
+ # method:
21
+ #
22
+ # GoodData.connect 'gooddata_user', 'gooddata_password'
23
+ #
24
+ # This GoodData object can now be utalized to retrieve your GoodData profile, the available
25
+ # projects etc.
26
+ #
27
+ # == Logging
28
+ #
29
+ # GoodData.logger = Logger.new(STDOUT)
30
+ #
31
+ # For details about the logger options and methods, see the
32
+ # {Logger module documentation}[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
33
+ #
34
+ module GoodData
35
+ module Threaded
36
+ # Used internally for thread safety
37
+ def threaded
38
+ Thread.current[:goooddata] ||= {}
39
+ end
40
+ end
41
+
42
+ class NilLogger
43
+ def debug(*args) ; end
44
+ alias :info :debug
45
+ alias :warn :debug
46
+ alias :error :debug
47
+ end
48
+
49
+ def project=(project)
50
+ GoodData.project = project
51
+ GoodData.project
52
+ end
53
+ alias :use :project=
54
+
55
+ class << self
56
+ include Threaded
57
+
58
+ RELEASE_INFO_PATH = '/gdc/releaseInfo'
59
+
60
+ def version
61
+ VERSION
62
+ end
63
+
64
+ def gem_version_string
65
+ "gooddata-gem/#{version}"
66
+ end
67
+
68
+ # Connect to the GoodData API
69
+ #
70
+ # === Parameters
71
+ #
72
+ # * +user+ - A GoodData username
73
+ # * +password+ - A GoodData password
74
+ #
75
+ def connect(user, password, url = nil)
76
+ threaded[:connection] = Connection.new user, password, url
77
+ end
78
+
79
+ # Returns the active GoodData connection earlier initialized via
80
+ # GoodData.connect call
81
+ #
82
+ # @see GoodData.connect
83
+ #
84
+ def connection
85
+ threaded[:connection]
86
+ end
87
+
88
+ # Sets the active project
89
+ #
90
+ # === Parameters
91
+ #
92
+ # * +project+ - a project identifier
93
+ #
94
+ # === Examples
95
+ #
96
+ # The following calls are equivalent:
97
+ # * GoodData.project = 'afawtv356b6usdfsdf34vt'
98
+ # * GoodData.use 'afawtv356b6usdfsdf34vt'
99
+ # * GoodData.use '/gdc/projects/afawtv356b6usdfsdf34vt'
100
+ # * GoodData.project = Project['afawtv356b6usdfsdf34vt']
101
+ #
102
+ def project=(project)
103
+ if project.is_a? Project
104
+ threaded[:project] = project
105
+ else
106
+ threaded[:project] = Project[project]
107
+ end
108
+ end
109
+
110
+ alias :use :project=
111
+
112
+ # Returns the active project
113
+ #
114
+ def project
115
+ threaded[:project]
116
+ end
117
+
118
+ # Performs a HTTP GET request.
119
+ #
120
+ # Retuns the JSON response formatted as a Hash object.
121
+ #
122
+ # === Parameters
123
+ #
124
+ # * +path+ - The HTTP path on the GoodData server (must be prefixed with a forward slash)
125
+ # === Examples
126
+ #
127
+ # GoodData.get '/gdc/projects'
128
+ def get(path)
129
+ connection.get(path)
130
+ end
131
+
132
+ # Performs a HTTP POST request.
133
+ #
134
+ # Retuns the JSON response formatted as a Hash object.
135
+ #
136
+ # === Parameters
137
+ #
138
+ # * +path+ - The HTTP path on the GoodData server (must be prefixed with a forward slash)
139
+ # * +data+ - The payload data in the format of a Hash object
140
+ #
141
+ # === Examples
142
+ #
143
+ # GoodData.post '/gdc/projects', { ... }
144
+ def post(path, data)
145
+ connection.post path, data
146
+ end
147
+
148
+ # Performs a HTTP DELETE request.
149
+ #
150
+ # Retuns the JSON response formatted as a Hash object.
151
+ #
152
+ # === Parameters
153
+ #
154
+ # * +path+ - The HTTP path on the GoodData server (must be prefixed with a forward slash)
155
+ #
156
+ # === Examples
157
+ #
158
+ # GoodData.delete '/gdc/project/1'
159
+ def delete(path)
160
+ connection.delete path
161
+ end
162
+
163
+ def test_login
164
+ connection.connect!
165
+ connection.logged_in?
166
+ end
167
+
168
+ # Returns the currently logged in user Profile.
169
+ def profile
170
+ threaded[:profile] ||= Profile.load
171
+ end
172
+
173
+ # Returns information about the GoodData API as a Hash (e.g. version, release time etc.)
174
+ def release_info
175
+ @release_info ||= @connection.get(RELEASE_INFO_PATH)['release']
176
+ end
177
+
178
+ # Returns the logger instance. The default implementation
179
+ # does not log anything
180
+ # For some serious logging, set the logger instance using
181
+ # the logger= method
182
+ #
183
+ # === Example
184
+ #
185
+ # require 'logger'
186
+ # GoodData.logger = Logger.new(STDOUT)
187
+ def logger
188
+ @logger ||= NilLogger.new
189
+ end
190
+
191
+ # Sets the logger instance
192
+ def logger=(logger)
193
+ @logger = logger
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,75 @@
1
+ require 'pp'
2
+ require 'gooddata/helpers'
3
+ require 'gooddata/commands/base'
4
+ Dir[File.dirname(__FILE__) + '/commands/*.rb'].each { |file| require file unless file =~ /\/base.rb$/ }
5
+
6
+ module GoodData::Command
7
+ class InvalidCommand < RuntimeError; end
8
+ class InvalidOption < RuntimeError; end
9
+ class CommandFailed < RuntimeError; end
10
+
11
+ extend GoodData::Helpers
12
+
13
+ class << self
14
+ def run(command, args)
15
+ begin
16
+ run_internal command, args.dup
17
+ rescue InvalidCommand
18
+ error "Unknown command. Run 'gooddata help' for usage information."
19
+ rescue InvalidOption
20
+ error "Unknown option."
21
+ rescue RestClient::Unauthorized
22
+ error "Authentication failure"
23
+ rescue RestClient::ResourceNotFound => e
24
+ error extract_not_found(e.http_body)
25
+ rescue RestClient::RequestFailed => e
26
+ error extract_error(e.http_body)
27
+ rescue RestClient::RequestTimeout
28
+ error "API request timed out. Please try again, or contact support@gooddata.com if this issue persists."
29
+ rescue CommandFailed => e
30
+ error e.message
31
+ rescue Interrupt => e
32
+ error "\n[canceled]"
33
+ end
34
+ end
35
+
36
+ def run_internal(command, args)
37
+ klass, method = parse(command)
38
+ runner = klass.new(args)
39
+ raise InvalidCommand unless runner.respond_to?(method)
40
+ runner.send(method)
41
+ end
42
+
43
+ def parse(command)
44
+ parts = command.split(':')
45
+
46
+ parts << :index if parts.size == 1
47
+ raise InvalidCommand if parts.size > 2
48
+
49
+ begin
50
+ return GoodData::Command.const_get(parts[0].capitalize), parts[1]
51
+ rescue NameError
52
+ raise InvalidCommand
53
+ end
54
+ end
55
+
56
+ def extract_not_found(body)
57
+ body =~ /^[\w\s]+ not found$/ ? body : "Resource not found"
58
+ end
59
+
60
+ def extract_error(body)
61
+ msg = parse_error_json(body) || 'Internal server error'
62
+ msg.split("\n").map { |line| ' ! ' + line }.join("\n")
63
+ end
64
+
65
+ def parse_error_json(body)
66
+ begin
67
+ error = JSON.parse(body.to_s)['error']
68
+ return error['message'] if !error['parameters']
69
+ return error['message'] % error['parameters'] rescue error
70
+ rescue
71
+ return body
72
+ end
73
+ end
74
+ end
75
+ end