gooddata 0.6.0.pre11 → 0.6.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.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +12 -1
  3. data/.yardopts +2 -0
  4. data/README.md +6 -3
  5. data/Rakefile +24 -7
  6. data/gooddata +2 -2
  7. data/gooddata.gemspec +4 -3
  8. data/lib/gooddata.rb +17 -12
  9. data/lib/gooddata/bricks/base_downloader.rb +7 -7
  10. data/lib/gooddata/bricks/brick.rb +7 -8
  11. data/lib/gooddata/bricks/bricks.rb +4 -1
  12. data/lib/gooddata/bricks/middleware/base_middleware.rb +2 -2
  13. data/lib/gooddata/bricks/middleware/bench_middleware.rb +5 -6
  14. data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +21 -22
  15. data/lib/gooddata/bricks/middleware/fs_upload_middleware.rb +3 -4
  16. data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +14 -14
  17. data/lib/gooddata/bricks/middleware/logger_middleware.rb +6 -6
  18. data/lib/gooddata/bricks/middleware/middleware.rb +4 -1
  19. data/lib/gooddata/bricks/middleware/restforce_middleware.rb +29 -32
  20. data/lib/gooddata/bricks/middleware/stdout_middleware.rb +5 -5
  21. data/lib/gooddata/bricks/middleware/twitter_middleware.rb +6 -8
  22. data/lib/gooddata/bricks/utils.rb +3 -3
  23. data/lib/gooddata/cli/cli.rb +4 -2
  24. data/lib/gooddata/cli/commands/api_cmd.rb +6 -4
  25. data/lib/gooddata/cli/commands/auth_cmd.rb +5 -3
  26. data/lib/gooddata/cli/commands/console_cmd.rb +1 -1
  27. data/lib/gooddata/cli/commands/process_cmd.rb +6 -4
  28. data/lib/gooddata/cli/commands/profile_cmd.rb +5 -3
  29. data/lib/gooddata/cli/commands/project_cmd.rb +24 -22
  30. data/lib/gooddata/cli/commands/run_ruby_cmd.rb +12 -10
  31. data/lib/gooddata/cli/commands/scaffold_cmd.rb +8 -6
  32. data/lib/gooddata/cli/hooks.rb +4 -2
  33. data/lib/gooddata/cli/shared.rb +3 -1
  34. data/lib/gooddata/cli/terminal.rb +16 -0
  35. data/lib/gooddata/client.rb +28 -22
  36. data/lib/gooddata/commands/api.rb +43 -26
  37. data/lib/gooddata/commands/auth.rb +22 -53
  38. data/lib/gooddata/commands/base.rb +2 -0
  39. data/lib/gooddata/commands/commands.rb +3 -0
  40. data/lib/gooddata/commands/datasets.rb +39 -136
  41. data/lib/gooddata/commands/process.rb +134 -130
  42. data/lib/gooddata/commands/profile.rb +2 -0
  43. data/lib/gooddata/commands/projects.rb +91 -129
  44. data/lib/gooddata/commands/runners.rb +11 -11
  45. data/lib/gooddata/commands/scaffold.rb +28 -26
  46. data/lib/gooddata/connection.rb +61 -68
  47. data/lib/gooddata/core/core.rb +1 -2
  48. data/lib/gooddata/data/data.rb +7 -0
  49. data/lib/gooddata/data/guesser.rb +114 -0
  50. data/lib/gooddata/exceptions/command_failed.rb +7 -0
  51. data/lib/gooddata/exceptions/exceptions.rb +7 -0
  52. data/lib/gooddata/{exceptions.rb → exceptions/project_not_found.rb} +2 -2
  53. data/lib/gooddata/extensions/big_decimal.rb +5 -0
  54. data/lib/gooddata/extract.rb +2 -0
  55. data/lib/gooddata/goodzilla/goodzilla.rb +11 -12
  56. data/lib/gooddata/helpers.rb +49 -35
  57. data/lib/gooddata/models/attribute.rb +7 -5
  58. data/lib/gooddata/models/dashboard.rb +44 -45
  59. data/lib/gooddata/models/data_result.rb +10 -13
  60. data/lib/gooddata/models/data_set.rb +6 -6
  61. data/lib/gooddata/models/display_form.rb +4 -4
  62. data/lib/gooddata/models/empty_result.rb +4 -3
  63. data/lib/gooddata/models/fact.rb +5 -5
  64. data/lib/gooddata/models/links.rb +3 -1
  65. data/lib/gooddata/models/metadata.rb +34 -32
  66. data/lib/gooddata/models/metric.rb +33 -34
  67. data/lib/gooddata/models/model.rb +165 -173
  68. data/lib/gooddata/models/models.rb +3 -0
  69. data/lib/gooddata/models/process.rb +18 -17
  70. data/lib/gooddata/models/profile.rb +3 -1
  71. data/lib/gooddata/models/project.rb +107 -35
  72. data/lib/gooddata/models/project_metadata.rb +12 -12
  73. data/lib/gooddata/models/report.rb +31 -30
  74. data/lib/gooddata/models/report_data_result.rb +22 -19
  75. data/lib/gooddata/models/report_definition.rb +101 -80
  76. data/lib/gooddata/version.rb +5 -3
  77. data/lib/templates/bricks/brick.rb.erb +3 -3
  78. data/lib/templates/bricks/main.rb.erb +3 -2
  79. data/lib/templates/project/Goodfile.erb +2 -2
  80. data/lib/templates/project/model/model.rb.erb +19 -19
  81. data/spec/data/.gooddata +4 -0
  82. data/spec/helpers/blueprint_helper.rb +2 -2
  83. data/spec/helpers/cli_helper.rb +28 -0
  84. data/spec/helpers/connection_helper.rb +2 -2
  85. data/spec/integration/command_projects_spec.rb +1 -1
  86. data/spec/integration/create_from_template_spec.rb +12 -0
  87. data/spec/integration/full_project_spec.rb +2 -2
  88. data/spec/integration/partial_md_export_import_spec.rb +36 -0
  89. data/spec/logging_in_logging_out_spec.rb +1 -1
  90. data/spec/spec_helper.rb +29 -2
  91. data/spec/unit/cli/cli_spec.rb +3 -3
  92. data/spec/unit/cli/commands/cmd_api_spec.rb +21 -4
  93. data/spec/unit/cli/commands/cmd_auth_spec.rb +2 -4
  94. data/spec/unit/cli/commands/cmd_process_spec.rb +20 -4
  95. data/spec/unit/cli/commands/cmd_profile_spec.rb +9 -4
  96. data/spec/unit/cli/commands/cmd_project_spec.rb +53 -4
  97. data/spec/unit/cli/commands/cmd_run_ruby_spec.rb +2 -4
  98. data/spec/unit/cli/commands/cmd_scaffold_spec.rb +14 -4
  99. data/spec/unit/commands/command_api_spec.rb +21 -2
  100. data/spec/unit/commands/command_auth_spec.rb +62 -1
  101. data/spec/unit/commands/command_dataset_spec.rb +31 -3
  102. data/spec/unit/commands/command_process_spec.rb +75 -1
  103. data/spec/unit/commands/command_profile_spec.rb +7 -1
  104. data/spec/unit/commands/command_projects_spec.rb +1 -1
  105. data/spec/unit/commands/command_scaffold_spec.rb +46 -1
  106. data/spec/unit/core/connection_spec.rb +1 -0
  107. data/spec/unit/data/guesser_spec.rb +54 -0
  108. data/spec/unit/helpers_spec.rb +47 -0
  109. data/spec/unit/model/schema_builder_spec.rb +2 -0
  110. data/spec/unit/model/tools_spec.rb +89 -0
  111. data/test/test_upload.rb +39 -15
  112. metadata +98 -75
  113. data/test/test_commands.rb +0 -85
  114. data/test/test_guessing.rb +0 -46
  115. data/test/test_model.rb +0 -81
  116. data/test/test_rest_api_basic.rb +0 -41
@@ -1,7 +1,9 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'pp'
2
4
 
3
- require File.join(File.dirname(__FILE__), "../shared")
4
- require File.join(File.dirname(__FILE__), "../../commands/scaffold")
5
+ require_relative '../shared'
6
+ require_relative '../../commands/scaffold'
5
7
 
6
8
  GoodData::CLI.module_eval do
7
9
 
@@ -9,21 +11,21 @@ GoodData::CLI.module_eval do
9
11
  arg_name 'show'
10
12
  command :scaffold do |c|
11
13
 
12
- c.desc "Scaffold a gooddata project blueprint"
14
+ c.desc 'Scaffold a gooddata project blueprint'
13
15
  c.command :project do |project|
14
16
  project.action do |global_options, options, args|
15
17
  name = args.first
16
- fail "Name of the project has to be provided" if name.nil? || name.empty?
18
+ fail 'Name of the project has to be provided' if name.nil? || name.empty?
17
19
  GoodData::Command::Scaffold.project(name)
18
20
  end
19
21
  end
20
22
 
21
- c.desc "Scaffold a gooddata ruby brick. This is a piece of code that you can run on our platform"
23
+ c.desc 'Scaffold a gooddata ruby brick. This is a piece of code that you can run on our platform'
22
24
  c.command :brick do |brick|
23
25
  # brick.arg_name 'name'
24
26
  brick.action do |global_options, options, args|
25
27
  name = args.first
26
- fail "Name of the brick has to be provided" if name.nil? || name.empty?
28
+ fail 'Name of the brick has to be provided' if name.nil? || name.empty?
27
29
  GoodData::Command::Scaffold.brick(name)
28
30
  end
29
31
  end
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'gli'
2
4
  require 'pp'
3
5
 
@@ -36,8 +38,8 @@ GoodData::CLI.module_eval do
36
38
  # Error logic here
37
39
  # return false to skip default error handling
38
40
  # binding.pry
39
- pp exception.backtrace
41
+ # pp exception.backtrace
40
42
  pp exception
41
43
  true
42
44
  end
43
- end
45
+ end
@@ -1,6 +1,8 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'gli'
2
4
 
3
- require File.join(File.dirname(__FILE__), "../version")
5
+ require_relative '../version'
4
6
 
5
7
  include GLI::App
6
8
 
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'highline'
4
+
5
+ # Define GoodData::CLI as GLI Wrapper
6
+ module GoodData
7
+ module CLI
8
+ DEFAULT_TERMINAL = HighLine.new
9
+
10
+ class << self
11
+ def terminal
12
+ DEFAULT_TERMINAL
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,9 +1,11 @@
1
- require File.join(File.dirname(__FILE__), 'version')
2
- require File.join(File.dirname(__FILE__), 'connection')
3
- require File.join(File.dirname(__FILE__), 'helpers')
1
+ # encoding: UTF-8
2
+
3
+ require_relative 'version'
4
+ require_relative 'connection'
5
+ require_relative 'helpers'
4
6
 
5
7
  # fastercsv is built in Ruby 1.9
6
- if RUBY_VERSION < "1.9"
8
+ if RUBY_VERSION < '1.9'
7
9
  require 'fastercsv'
8
10
  else
9
11
  require 'csv'
@@ -13,7 +15,7 @@ end
13
15
  # Initializes required dynamically loaded classes
14
16
  def init_gd_module()
15
17
  # Metadata packages, such as report.rb, require this to be loaded first
16
- require File.join(File.dirname(__FILE__), '/models/metadata.rb')
18
+ require_relative 'models/metadata.rb'
17
19
 
18
20
  # Load models from models folder
19
21
  Dir[File.dirname(__FILE__) + '/models/*.rb'].each { |file| require file }
@@ -62,7 +64,10 @@ module GoodData
62
64
 
63
65
  # Dummy implementation of logger
64
66
  class NilLogger
65
- def debug(*args) ; end
67
+ def debug(*args)
68
+ ;
69
+ end
70
+
66
71
  alias :info :debug
67
72
  alias :warn :debug
68
73
  alias :error :debug
@@ -73,6 +78,7 @@ module GoodData
73
78
  GoodData.project = project
74
79
  GoodData.project
75
80
  end
81
+
76
82
  alias :use :project=
77
83
 
78
84
  class << self
@@ -88,11 +94,11 @@ module GoodData
88
94
  #
89
95
  def connect(options=nil, second_options=nil, third_options={})
90
96
  if options.is_a? Hash
91
- fail "You have to provide login and password" if ((options[:login].nil? || options[:login].empty?) && (options[:password].nil? || options[:password].empty?))
97
+ fail 'You have to provide login and password' if ((options[:login].nil? || options[:login].empty?) && (options[:password].nil? || options[:password].empty?))
92
98
  threaded[:connection] = Connection.new(options[:login], options[:password], options)
93
99
  GoodData.project = options[:project] if options[:project]
94
100
  elsif options.is_a?(String) && second_options.is_a?(String)
95
- fail "You have to provide login and password" if ((options.nil? || options.empty?) && (second_options.nil? || second_options.empty?))
101
+ fail 'You have to provide login and password' if ((options.nil? || options.empty?) && (second_options.nil? || second_options.empty?))
96
102
  threaded[:connection] = Connection.new(options, second_options, third_options)
97
103
  end
98
104
 
@@ -126,7 +132,7 @@ module GoodData
126
132
  # @param options Options get routed to connect eventually so everything that you can use there should be possible to use here.
127
133
  #
128
134
  def connect_with_sst(token, options={})
129
- create_authenticated_connection(options.merge({:cookies => {"GDCAuthSST" => token}}))
135
+ create_authenticated_connection(options.merge({:cookies => {'GDCAuthSST' => token}}))
130
136
  end
131
137
 
132
138
  # This method is aimed at creating an authenticated connection in case you do not hae pass/login but you have SST
@@ -146,7 +152,7 @@ module GoodData
146
152
  # @param project Project to use
147
153
  # @param bl Block to be performed
148
154
  def with_project(project, &bl)
149
- fail "You have to specify a project when using with_project" if project.nil? || (project.is_a?(String) && project.empty?)
155
+ fail 'You have to specify a project when using with_project' if project.nil? || (project.is_a?(String) && project.empty?)
150
156
  old_project = GoodData.project
151
157
  begin
152
158
  GoodData.use(project)
@@ -162,7 +168,7 @@ module GoodData
162
168
  #
163
169
  # @see GoodData.connect
164
170
  def connection
165
- threaded[:connection] || raise("Please authenticate with GoodData.connect first")
171
+ threaded[:connection] || raise('Please authenticate with GoodData.connect first')
166
172
  end
167
173
 
168
174
  # Sets the active project
@@ -264,12 +270,12 @@ module GoodData
264
270
  end
265
271
 
266
272
  def upload_to_user_webdav(file, options={})
267
- u = URI(connection.options[:webdav_server] || GoodData.project.links["uploads"])
268
- url = URI.join(u.to_s.chomp(u.path.to_s), "/uploads/")
273
+ u = URI(connection.options[:webdav_server] || GoodData.project.links['uploads'])
274
+ url = URI.join(u.to_s.chomp(u.path.to_s), '/uploads/')
269
275
  connection.upload(file, options.merge({
270
- :directory => options[:directory],
271
- :staging_url => url
272
- }))
276
+ :directory => options[:directory],
277
+ :staging_url => url
278
+ }))
273
279
  end
274
280
 
275
281
  def get_project_webdav_path(file, options={})
@@ -280,9 +286,9 @@ module GoodData
280
286
  def upload_to_project_webdav(file, options={})
281
287
  url = get_project_webdav_path(file, options)
282
288
  connection.upload(file, options.merge({
283
- :directory => options[:directory],
284
- :staging_url => url
285
- }))
289
+ :directory => options[:directory],
290
+ :staging_url => url
291
+ }))
286
292
  end
287
293
 
288
294
  def get_user_webdav_path(file, options={})
@@ -293,13 +299,13 @@ module GoodData
293
299
  def download_from_user_webdav(file, where, options={})
294
300
  url = get_user_webdav_path(file, options)
295
301
  connection.download(file, where, options.merge({
296
- :staging_url => url
297
- }))
302
+ :staging_url => url
303
+ }))
298
304
  end
299
305
 
300
306
  def poll(result, key, options={})
301
307
  sleep_interval = options[:sleep_interval] || 10
302
- link = result[key]["links"]["poll"]
308
+ link = result[key]['links']['poll']
303
309
  response = GoodData.get(link, :process => false)
304
310
  while response.code != 204
305
311
  sleep sleep_interval
@@ -1,34 +1,51 @@
1
- module GoodData::Command
2
- class Api
3
- class << self
4
- def info
5
- json = {}
6
- puts "GoodData API"
7
- puts " Version: #{json['releaseName']}"
8
- puts " Released: #{json['releaseDate']}"
9
- puts " For more info see #{json['releaseNotesUri']}"
10
- end
1
+ # encoding: UTF-8
2
+
3
+ require_relative '../exceptions/command_failed'
11
4
 
12
- alias :index :info
5
+ module GoodData
6
+ module Command
7
+ # Low level access to GoodData API
8
+ class Api
9
+ class << self
10
+ def info
11
+ json = {
12
+ 'releaseName' => 'N/A',
13
+ 'releaseDate' => 'N/A',
14
+ 'releaseNotesUri' => 'N/A'
15
+ }
13
16
 
14
- def test
15
- if GoodData.test_login
16
- puts "Succesfully logged in as #{GoodData.profile.user}"
17
- else
18
- puts "Unable to log in to GoodData server!"
17
+ puts 'GoodData API'
18
+ puts " Version: #{json['releaseName']}"
19
+ puts " Released: #{json['releaseDate']}"
20
+ puts " For more info see #{json['releaseNotesUri']}"
19
21
  end
20
- end
21
22
 
22
- def get(path)
23
- raise(CommandFailed, "Specify the path you want to GET.") if path.nil?
24
- result = GoodData.get path
25
- result rescue puts result
26
- end
23
+ alias_method :index, :info
27
24
 
28
- def delete(path)
29
- raise(CommandFailed, "Specify the path you want to DELETE.") if path.nil?
30
- result = GoodData.delete path
31
- result rescue puts result
25
+ # Test of login
26
+ def test
27
+ if GoodData.test_login
28
+ puts "Succesfully logged in as #{GoodData.profile.user}"
29
+ else
30
+ puts 'Unable to log in to GoodData server!'
31
+ end
32
+ end
33
+
34
+ # Get resource
35
+ # @param path Resource path
36
+ def get(path)
37
+ raise(GoodData::CommandFailed, 'Specify the path you want to GET.') if path.nil?
38
+ result = GoodData.get path
39
+ result rescue puts result
40
+ end
41
+
42
+ # Delete resource
43
+ # @param path Resource path
44
+ def delete(path)
45
+ raise(GoodData::CommandFailed, 'Specify the path you want to DELETE.') if path.nil?
46
+ result = GoodData.delete path
47
+ result rescue puts result
48
+ end
32
49
  end
33
50
  end
34
51
  end
@@ -1,57 +1,31 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'highline/import'
2
- require 'json'
4
+ require 'multi_json'
3
5
 
4
- require File.join(File.dirname(__FILE__), '../helpers')
6
+ require_relative '../cli/terminal'
7
+ require_relative '../helpers'
5
8
 
6
9
  module GoodData::Command
7
10
  class Auth
8
-
9
11
  class << self
10
- def connect
11
- unless defined? @connected
12
- GoodData.connect({
13
- :login => user,
14
- :password => password,
15
- :server => url,
16
- :auth_token => auth_token
17
- })
18
- @connected = true
19
- end
20
- @connected
21
- end
22
-
23
- def user
24
- ensure_credentials
25
- @credentials[:username]
26
- end
27
-
28
- def password
29
- ensure_credentials
30
- @credentials[:password]
31
- end
32
-
33
- def url
34
- ensure_credentials
35
- @credentials[:url]
36
- end
37
-
38
- def auth_token
39
- ensure_credentials
40
- @credentials[:auth_token]
41
- end
42
12
 
13
+ # Get path of .gooddata config
43
14
  def credentials_file
44
15
  "#{GoodData::Helpers.home_directory}/.gooddata"
45
16
  end
46
17
 
47
- def ensure_credentials
48
- return if defined? @credentials
49
- unless @credentials = read_credentials
50
- @credentials = ask_for_credentials
51
- end
52
- @credentials
18
+ # Ask for credentials
19
+ def ask_for_credentials
20
+ puts 'Enter your GoodData credentials.'
21
+ user = GoodData::CLI.terminal.ask('Email')
22
+ password = GoodData::CLI.terminal.ask('Password') { |q| q.echo = 'x' }
23
+ auth_token = GoodData::CLI.terminal.ask('Authorization Token')
24
+
25
+ {:username => user, :password => password, :auth_token => auth_token}
53
26
  end
54
27
 
28
+ # Read credentials
55
29
  def read_credentials
56
30
  if File.exists?(credentials_file) then
57
31
  config = File.read(credentials_file)
@@ -61,22 +35,16 @@ module GoodData::Command
61
35
  end
62
36
  end
63
37
 
64
- def ask_for_credentials
65
- puts "Enter your GoodData credentials."
66
- user = HighLine.new.ask("Email")
67
- password = HighLine.new.ask("Password") { |q| q.echo = "x" }
68
- auth_token = HighLine.new.ask("Authorization Token")
69
- { :username => user, :password => password, :auth_token => auth_token }
70
- end
71
-
38
+ # Ask for credentials and store them
72
39
  def store
73
40
  credentials = ask_for_credentials
74
41
 
75
42
  ovewrite = if File.exist?(credentials_file)
76
- HighLine::ask("Overwrite existing stored credentials (y/n)")
77
- else
78
- 'y'
79
- end
43
+ GoodData::CLI.terminal.ask("Overwrite existing stored credentials (y/n)")
44
+ else
45
+ 'y'
46
+ end
47
+
80
48
  if ovewrite == 'y'
81
49
  File.open(credentials_file, 'w', 0600) do |f|
82
50
  f.puts JSON.pretty_generate(credentials)
@@ -86,6 +54,7 @@ module GoodData::Command
86
54
  end
87
55
  end
88
56
 
57
+ # Delete stored credentials
89
58
  def unstore
90
59
  FileUtils.rm_f(credentials_file)
91
60
  end
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  module GoodData::Command
2
4
  class Base
3
5
  end
@@ -1,3 +1,6 @@
1
+ # encoding: UTF-8
2
+ require 'pathname'
3
+
1
4
  base = Pathname(__FILE__).dirname.expand_path
2
5
  Dir.glob(base + '*.rb').each do |file|
3
6
  require file
@@ -1,21 +1,26 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'date'
2
- require 'gooddata/extract'
4
+
5
+ require_relative '../data/guesser'
6
+ require_relative '../extract'
7
+ require_relative '../exceptions/command_failed'
3
8
 
4
9
  module GoodData
5
10
  module Command
6
11
  class Datasets
7
-
8
12
  # List all data sets present in the project specified by the --project option
9
13
  #
10
- # == Usage
14
+ # ## Usage
11
15
  #
12
- # <tt>gooddata datasets --project <projectid></tt>
13
- # <tt>gooddata datasets:list --project <projectid></tt>
16
+ # gooddata datasets --project <projectid>
17
+ # gooddata datasets:list --project <projectid>
14
18
  #
15
- # * <tt>--project</tt> - GoodData project identifier
19
+ # * `--project` - GoodData project identifier
16
20
  #
17
21
  def index
18
- connect
22
+ # TODO: Review following connect replacement/reimplementation
23
+ # connect
19
24
  with_project do |project_id|
20
25
  Project[project_id].datasets.each do |ds|
21
26
  puts "#{ds.uri}\t#{ds.identifier}\t#{ds.title}"
@@ -28,20 +33,20 @@ module GoodData
28
33
  # The command prescans the data set, picks possible LDM types for it's
29
34
  # fields and asks user for confirmation.
30
35
  #
31
- # == Usage
36
+ # ## Usage
32
37
  #
33
- # <tt>gooddata datasets:describe --file-csv <path> --name <name> --output <output path></tt>
38
+ # gooddata datasets:describe --file-csv <path> --name <name> --output <output path>
34
39
  #
35
- # * <tt>--file-csv</tt> - path to the CSV file (required)
36
- # * <tt>--name</tt> - name of the data set (user will be prompted unless provided)
37
- # * <tt>--output</tt> - name of the output JSON file with the model description (user will be prompted unless provided)
40
+ # * `--file-csv` - path to the CSV file (required)
41
+ # * `--name` - name of the data set (user will be prompted unless provided)
42
+ # * `--output` - name of the output JSON file with the model description (user will be prompted unless provided)
38
43
  #
39
44
  def describe
40
- columns = ask_for_fields
41
- name = extract_option('--name') || ask("Enter the dataset name")
42
- output = extract_option('--output') || ask("Enter path to the file where to save the model description", :default => "#{name}.json")
45
+ columns = ask_for_fields
46
+ name = extract_option('--name') || ask('Enter the dataset name')
47
+ output = extract_option('--output') || ask('Enter path to the file where to save the model description', :default => "#{name}.json")
43
48
  open output, 'w' do |f|
44
- f << JSON.pretty_generate( :title => name, :columns => columns ) + "\n"
49
+ f << JSON.pretty_generate(:title => name, :columns => columns) + "\n"
45
50
  f.flush
46
51
  end
47
52
  end
@@ -49,15 +54,16 @@ module GoodData
49
54
  # Creates a server-side model based on local model description. The model description
50
55
  # is read from a JSON file that can be generated using the +datasets:describe+ command
51
56
  #
52
- # == Usage
57
+ # ## Usage
53
58
  #
54
- # <tt>gooddata datasets:apply --project <projectid> <data set config></tt>
59
+ # gooddata datasets:apply --project <projectid> <data set config>
55
60
  #
56
- # * <tt>--project</tt> - GoodData project identifier
57
- # * <tt>data set config</tt> - JSON file with the model description (possibly generated by the <tt>datasets:describe</tt> command)
61
+ # * `--project`- GoodData project identifier
62
+ # * `data set config` - JSON file with the model description (possibly generated by the <tt>datasets:describe</tt> command)
58
63
  #
59
64
  def apply
60
- connect
65
+ # TODO: Review following connect replacement/reimplementation
66
+ # connect
61
67
  with_project do |project_id|
62
68
  cfg_file = args.shift rescue nil
63
69
  raise(CommandFailed, "Usage: #{$0} <dataset config>") unless cfg_file
@@ -67,18 +73,19 @@ module GoodData
67
73
  end
68
74
  end
69
75
 
70
- # Load a CSV file into an existing server-side data set
76
+ # Loads a CSV file into an existing server-side data set
71
77
  #
72
- # == Usage
78
+ # ## Usage
73
79
  #
74
- # <tt>gooddata datasets:load --project <projectid> <file> <dataset config></tt>
80
+ # gooddata datasets:load --project <projectid> <file> <dataset config><
75
81
  #
76
- # * <tt>--project</tt> - GoodData project identifier
77
- # * <tt>file</tt> - CSV file to load
78
- # * <tt>data set config</tt> - JSON file with the model description (possibly generated by the <tt>datasets:describe</tt> command)
82
+ # * `--project` - GoodData project identifier
83
+ # * `file` - CSV file to load
84
+ # * `data set config` - JSON file with the model description (possibly generated by the <tt>datasets:describe</tt> command)
79
85
  #
80
86
  def load
81
- connect
87
+ # TODO: Review following connect replacement/reimplementation
88
+ # connect
82
89
  with_project do |project_id|
83
90
  file, cfg_file = args
84
91
  raise(CommandFailed, "Usage: #{$0} datasets:load <file> <dataset config>") unless cfg_file
@@ -93,13 +100,13 @@ module GoodData
93
100
  def with_project
94
101
  unless @project_id
95
102
  @project_id = extract_option('--project')
96
- raise CommandFailed.new("Project not specified, use the --project switch") unless @project_id
103
+ raise CommandFailed.new('Project not specified, use the --project switch') unless @project_id
97
104
  end
98
105
  yield @project_id
99
106
  end
100
107
 
101
108
  def ask_for_fields
102
- guesser = Guesser.new create_dataset.read
109
+ guesser = GoodData::Data::Guesser.new create_dataset.read
103
110
  guess = guesser.guess(1000)
104
111
  model = []
105
112
  connection_point_set = false
@@ -107,7 +114,7 @@ module GoodData
107
114
  guesser.headers.each_with_index do |header, i|
108
115
  options = guess[header].map { |t| t.to_s }
109
116
  options = options.select { |t| t != :connection_point.to_s } if connection_point_set
110
- type = ask question_fmt % [ i + 1, header ], :answers => options
117
+ type = ask question_fmt % [i + 1, header], :answers => options
111
118
  model.push :title => header, :name => header, :type => type.upcase
112
119
  connection_point_set = true if type == :connection_point.to_s
113
120
  end
@@ -117,111 +124,7 @@ module GoodData
117
124
  def create_dataset
118
125
  file = extract_option('--file-csv')
119
126
  return Extract::CsvFile.new(file) if file
120
- raise CommandFailed.new("Unknown data set. Please specify a data set using --file-csv option (more supported data sources to come!)")
121
- end
122
- end
123
-
124
- ##
125
- # Utility class to guess data types of a data stream by looking at first couple of rows
126
- #
127
- class Guesser
128
-
129
- TYPES_PRIORITY = [ :connection_point, :fact, :date, :attribute ]
130
- attr_reader :headers
131
-
132
- class << self
133
- def sort_types(types)
134
- types.sort do |x, y|
135
- TYPES_PRIORITY.index(x) <=> TYPES_PRIORITY.index(y)
136
- end
137
- end
138
- end
139
-
140
- def initialize(reader)
141
- @reader = reader
142
- @headers = reader.shift.map! { |h| h.to_s } or raise "Empty data set"
143
- @pros = {}; @cons = {}; @seen = {}
144
- @headers.map do |h|
145
- @cons[h.to_s] = {}
146
- @pros[h.to_s] = {}
147
- @seen[h.to_s] = {}
148
- end
149
- end
150
-
151
- def guess(limit)
152
- count = 0
153
- while row = @reader.shift
154
- break unless row && !row.empty? && count < limit
155
- raise "%i fields in row %i, %i expected" % [ row.size, count + 1, @headers.size ] if row.size != @headers.size
156
- row.each_with_index do |value, j|
157
- header = @headers[j]
158
- number = check_number(header, value)
159
- date = check_date(header, value)
160
- store_guess header, { @pros => :attribute } unless number || date
161
- hash_increment @seen[header], value
162
- end
163
- count += 1
164
- end
165
- # fields with unique values are connection point candidates
166
- @seen.each do |header, values|
167
- store_guess header, { @pros => :connection_point } if values.size == count
168
- end
169
- guess_result
170
- end
171
-
172
- private
173
-
174
- def guess_result
175
- result = {}
176
- @headers.each do |header|
177
- result[header] = Guesser::sort_types @pros[header].keys.select { |type| @cons[header][type].nil? }
178
- end
179
- result
180
- end
181
-
182
- def hash_increment(hash, key)
183
- if hash[key]
184
- hash[key] += 1
185
- else
186
- hash[key] = 1
187
- end
188
- end
189
-
190
- def check_number(header, value)
191
- if value.nil? || value =~ /^[\+-]?\d*(\.\d*)?$/
192
- return store_guess(header, @pros => [ :fact, :attribute ] )
193
- end
194
- store_guess header, { @cons => :fact }
195
- end
196
-
197
- def check_date(header, value)
198
- return store_guess(header, @pros => [ :date, :attribute, :fact ]) if value.nil? || value == '0000-00-00'
199
- begin
200
- DateTime.parse value
201
- return store_guess(header, @pros => [ :date, :attribute ])
202
- rescue ArgumentError; end
203
- store_guess header, { @cons => :date }
204
- end
205
-
206
- ##
207
- # Stores a guess about given header.
208
- #
209
- # Returns true if the @pros key is present, false otherwise
210
- #
211
- # === Parameters
212
- #
213
- # * +header+ - A header name
214
- # * +guess+ - A hash with optional @pros and @cons keys
215
- #
216
- def store_guess(header, guess)
217
- result = !guess[@pros].nil?
218
- [@pros, @cons].each do |hash|
219
- if guess[hash] then
220
- guess[hash] = [ guess[hash] ] unless guess[hash].is_a? Array
221
- guess[hash].each { |type| hash_increment hash[header], type }
222
- end
223
- end
224
- result
127
+ raise CommandFailed.new('Unknown data set. Please specify a data set using --file-csv option (more supported data sources to come!)')
225
128
  end
226
129
  end
227
130
  end