gooddata 0.6.0.pre11 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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