gooddata 0.6.0.pre8 → 0.6.0.pre9

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/bin/gooddata CHANGED
@@ -55,11 +55,11 @@ arg_name 'logger'
55
55
  switch [:l,:logger]
56
56
 
57
57
 
58
- desc 'Describe list here'
58
+ desc 'Work with deployed processes'
59
59
  arg_name 'Describe arguments to list here'
60
60
  command :process do |c|
61
61
 
62
- c.desc 'Use when you need to redeploy a process'
62
+ c.desc 'Use when you need to redeploy a specific process'
63
63
  c.default_value nil
64
64
  c.flag :process_id
65
65
 
@@ -75,6 +75,7 @@ command :process do |c|
75
75
  c.default_value nil
76
76
  c.flag :name
77
77
 
78
+ c.desc "Lists all user's processes deployed on the plaform"
78
79
  c.command :list do |list|
79
80
  list.action do |global_options,options,args|
80
81
  opts = options.merge(global_options)
@@ -83,6 +84,7 @@ command :process do |c|
83
84
  end
84
85
  end
85
86
 
87
+ c.desc "Gives you some basic info about the process"
86
88
  c.command :get do |get|
87
89
  get.action do |global_options,options,args|
88
90
  opts = options.merge(global_options)
@@ -91,6 +93,7 @@ command :process do |c|
91
93
  end
92
94
  end
93
95
 
96
+ c.desc "Deploys provided directory to the server"
94
97
  c.command :deploy do |deploy|
95
98
  deploy.action do |global_options,options,args|
96
99
  opts = options.merge(global_options)
@@ -105,6 +108,7 @@ desc 'Some basic API stuff directly from CLI'
105
108
  arg_name 'info|test|get|delete'
106
109
  command :api do |c|
107
110
 
111
+ c.desc "Info about the API version etc"
108
112
  c.command :info do |info|
109
113
  info.action do |global_options,options,args|
110
114
  opts = options.merge(global_options)
@@ -113,6 +117,7 @@ command :api do |c|
113
117
  end
114
118
  end
115
119
 
120
+ c.desc "GET request on our API"
116
121
  c.command :get do |get|
117
122
  get.action do |global_options,options,args|
118
123
  opts = options.merge(global_options)
@@ -128,6 +133,7 @@ desc 'Describe add here'
128
133
  arg_name 'show'
129
134
  command :profile do |c|
130
135
 
136
+ c.desc "Show your profile"
131
137
  c.command :show do |show|
132
138
  show.action do |global_options,options,args|
133
139
  opts = options.merge(global_options)
@@ -142,6 +148,7 @@ desc 'Scaffold things'
142
148
  arg_name 'show'
143
149
  command :scaffold do |c|
144
150
 
151
+ c.desc "Scaffold a gooddata project blueprint"
145
152
  c.command :project do |project|
146
153
  project.action do |global_options, options, args|
147
154
  name = args.first
@@ -150,6 +157,7 @@ command :scaffold do |c|
150
157
  end
151
158
  end
152
159
 
160
+ c.desc "Scaffold a gooddata ruby brick. This is a piece of code that you can run on our platform"
153
161
  c.command :brick do |brick|
154
162
  # brick.arg_name 'name'
155
163
  brick.action do |global_options, options, args|
@@ -164,6 +172,7 @@ desc 'Manage your projects'
164
172
  arg_name 'project_command'
165
173
  command :project do |c|
166
174
 
175
+ c.desc "Lists user's projects"
167
176
  c.command :list do |list|
168
177
  list.action do |global_options,options,args|
169
178
  opts = options.merge(global_options)
@@ -173,6 +182,7 @@ command :project do |c|
173
182
  end
174
183
  end
175
184
 
185
+ c.desc "If you are in a gooddata project blueprint or if you provide a project id it will start an interactive session inside that project"
176
186
  c.command :jack_in do |jack|
177
187
  jack.action do |global_options,options,args|
178
188
  goodfile_path = GoodData::Helpers.find_goodfile(Pathname('.'))
@@ -202,6 +212,7 @@ command :project do |c|
202
212
  end
203
213
  end
204
214
 
215
+ c.desc "Create a gooddata project"
205
216
  c.command :create do |create|
206
217
  create.action do |global_options,options,args|
207
218
  title = ask "Project name"
@@ -221,6 +232,7 @@ command :project do |c|
221
232
  end
222
233
  end
223
234
 
235
+ c.desc "Delete a project. Be careful this is impossible to revert"
224
236
  c.command :delete do |delete|
225
237
  delete.action do |global_options,options,args|
226
238
  id = global_options[:project_id]
@@ -230,6 +242,7 @@ command :project do |c|
230
242
  end
231
243
  end
232
244
 
245
+ c.desc "Clones a project. Useful for testing"
233
246
  c.command :clone do |clone|
234
247
  clone.desc 'Name of the new project'
235
248
  clone.default_value nil
@@ -246,6 +259,7 @@ command :project do |c|
246
259
  end
247
260
  end
248
261
 
262
+ c.desc "Shows basic info about a project"
249
263
  c.command :show do |show|
250
264
  show.action do |global_options,options,args|
251
265
  id = global_options[:project_id]
@@ -256,6 +270,7 @@ command :project do |c|
256
270
  end
257
271
  end
258
272
 
273
+ c.desc "If you are in a gooddata project blueprint it will apply the changes. If you do not provide a project id it will build it from scratch and create a project for you."
259
274
  c.command :build do |show|
260
275
  show.action do |global_options,options,args|
261
276
  goodfile = JSON.parse(File.read(GoodData::Helpers.find_goodfile(Pathname('.'))), :symbolize_names => true)
@@ -279,9 +294,10 @@ command :project do |c|
279
294
 
280
295
  end
281
296
 
282
- desc 'Work with your credentials'
297
+ desc 'Work with your locally stored credentials'
283
298
  command :auth do |c|
284
299
 
300
+ c.desc "Store your credentials to ~/.gooddata so client does not have to ask you every single time"
285
301
  c.command :store do |store|
286
302
  store.action do |global_options,options,args|
287
303
  GoodData::Command::Auth.store
@@ -290,7 +306,7 @@ command :auth do |c|
290
306
 
291
307
  end
292
308
 
293
- desc 'Run ruby bricks'
309
+ desc 'Run ruby bricks either locally or remotely deployed on our server'
294
310
  # arg_name 'show'
295
311
  command :run_ruby do |c|
296
312
 
@@ -313,7 +329,6 @@ command :run_ruby do |c|
313
329
  c.default_value nil
314
330
  c.flag [:n, :name]
315
331
 
316
-
317
332
  c.action do |global_options, options, args|
318
333
  options[:expanded_params] = if (options[:params])
319
334
  JSON.parse(File.read(options[:params]), :symbolize_names => true)
@@ -322,10 +337,10 @@ command :run_ruby do |c|
322
337
  end
323
338
 
324
339
  opts = options.merge(global_options).merge({:type => "RUBY"})
340
+ GoodData.connect(opts)
325
341
  if options[:remote]
326
342
  fail "You have to specify name of the deploy when deploying remotely" if options[:name].nil? || options[:name].empty?
327
343
  require 'gooddata/commands/process'
328
- GoodData.connect(opts)
329
344
  GoodData::Command::Process.run(options[:dir], opts) do
330
345
  puts "would run"
331
346
  end
@@ -25,9 +25,14 @@ module GoodData::SmallGoodZilla
25
25
  end
26
26
 
27
27
  def self.interpolate_ids(*ids)
28
- res = GoodData::MdObject.identifier_to_uri(*ids.flatten)
29
- fail "Not all of the identifiers were resolved" if (Array(res).size != ids.flatten.size)
30
- res
28
+ ids = ids.flatten
29
+ if ids.empty?
30
+ []
31
+ else
32
+ res = GoodData::MdObject.identifier_to_uri(*ids)
33
+ fail "Not all of the identifiers were resolved" if (Array(res).size != ids.size)
34
+ res
35
+ end
31
36
  end
32
37
 
33
38
  def self.interpolate_values(keys, values)
@@ -95,6 +95,15 @@ module GoodData
95
95
  puts "Done loading"
96
96
  end
97
97
 
98
+ def merge_dataset_columns(a_schema_blueprint, b_schema_blueprint)
99
+ d = Marshal.load(Marshal.dump(a_schema_blueprint))
100
+ d[:columns] = d[:columns] + b_schema_blueprint[:columns]
101
+ d[:columns].uniq!
102
+ columns_that_failed_to_merge = d[:columns].group_by {|x| x[:name]}.map {|k, v| [k, v.count]}.find_all {|x| x[1] > 1}
103
+ fail "Columns #{columns_that_failed_to_merge} failed to merge. When merging columns with the same name they have to be identical." unless columns_that_failed_to_merge.empty?
104
+ d
105
+ end
106
+
98
107
  end
99
108
 
100
109
  class ProjectBlueprint
@@ -122,6 +131,28 @@ module GoodData
122
131
  @data = init_data
123
132
  end
124
133
 
134
+ def model_validate
135
+ if datasets.count == 1
136
+ []
137
+ else
138
+ x = datasets.reduce([]) {|memo, schema| schema.has_anchor? ? memo << [schema.name, schema.anchor[:name]] : memo }
139
+ refs = datasets.reduce([]) do |memo, dataset|
140
+ memo.concat(dataset.references)
141
+ end
142
+ refs.reduce([]) do |memo, ref|
143
+ x.include?([ref[:dataset], ref[:reference]]) ? memo : memo.concat([ref])
144
+ end
145
+ end
146
+ end
147
+
148
+ def model_valid?
149
+ errors = model_validate
150
+ errors.empty? ? true :false
151
+ end
152
+
153
+ def title
154
+ data[:title]
155
+ end
125
156
  end
126
157
 
127
158
  class SchemaBlueprint
@@ -162,6 +193,34 @@ module GoodData
162
193
  data[:columns]
163
194
  end
164
195
 
196
+ def has_anchor?
197
+ columns.any? {|c| c[:type] == :anchor}
198
+ end
199
+
200
+ def anchor
201
+ find_column_by_type(:anchor, :first)
202
+ end
203
+
204
+ def references
205
+ find_column_by_type(:reference)
206
+ end
207
+
208
+ def find_column_by_type(type, all=:all)
209
+ if all == :all
210
+ columns.find_all {|c| c[:type] == type}
211
+ else
212
+ columns.find {|c| c[:type] == type}
213
+ end
214
+ end
215
+
216
+ def find_column_by_name(type, all=:all)
217
+ if all == :all
218
+ columns.find_all {|c| c[:name] == type}
219
+ else
220
+ columns.find {|c| c[:name] == type}
221
+ end
222
+ end
223
+
165
224
  def to_schema
166
225
  Schema.new(to_hash)
167
226
  end
@@ -177,6 +236,11 @@ module GoodData
177
236
  printer.text columns.map {|c| " #{c[:name]}: #{c[:type]}"}.join("\n")
178
237
  end
179
238
 
239
+ def dup
240
+ deep_copy = Marshal.load(Marshal.dump(data))
241
+ SchemaBlueprint.new(deep_copy)
242
+ end
243
+
180
244
  end
181
245
 
182
246
  class ProjectBuilder
@@ -218,7 +282,15 @@ module GoodData
218
282
  def add_dataset(name, &block)
219
283
  builder = GoodData::Model::SchemaBuilder.new(name)
220
284
  block.call(builder)
221
- @datasets << builder.to_hash
285
+ if @datasets.any? {|item| item[:name] == name}
286
+ ds = @datasets.find {|item| item[:name] == name}
287
+ index = @datasets.index(ds)
288
+ stuff = GoodData::Model.merge_dataset_columns(ds, builder.to_hash)
289
+ @datasets.delete_at(index)
290
+ @datasets.insert(index, stuff)
291
+ else
292
+ @datasets << builder.to_hash
293
+ end
222
294
  end
223
295
 
224
296
  def add_report(title, options={})
@@ -25,13 +25,13 @@ module GoodData
25
25
  def create(options={})
26
26
  if options.is_a?(String)
27
27
  expression = options
28
- extended_notation = true
28
+ extended_notation = false
29
29
  title = nil
30
30
  else
31
31
  title = options[:title]
32
32
  summary = options[:summary]
33
33
  expression = options[:expression] || fail("Metric has to have its expression defined")
34
- extended_notation = options[:extended_notation]
34
+ extended_notation = options[:extended_notation] || false
35
35
  end
36
36
 
37
37
  expression = if extended_notation
@@ -62,12 +62,20 @@ module GoodData
62
62
  end
63
63
 
64
64
  def execute(expression, options={})
65
- m = GoodData::Metric.create({:title => "Temporary metric to be deleted", :expression => expression}.merge(options))
65
+ m = if expression.is_a?(String)
66
+ GoodData::Metric.create({:title => "Temporary metric to be deleted", :expression => expression}.merge(options))
67
+ else
68
+ GoodData::Metric.create({:title => "Temporary metric to be deleted"}.merge(expression))
69
+ end
66
70
  m.execute
67
71
  end
68
72
 
69
73
  def xexecute(expression)
70
- execute(expression, {:extended_notation => true})
74
+ if expression.is_a?(String)
75
+ execute({:expression => expression, :extended_notation => true})
76
+ else
77
+ execute(expression.merge({:extended_notation => true}))
78
+ end
71
79
  end
72
80
 
73
81
  end
@@ -1,3 +1,3 @@
1
1
  module GoodData
2
- VERSION = "0.6.0.pre8"
2
+ VERSION = "0.6.0.pre9"
3
3
  end
@@ -0,0 +1,92 @@
1
+ require 'gooddata/model'
2
+ require 'pry'
3
+
4
+ describe GoodData::Model::ProjectBlueprint do
5
+
6
+ before(:each) do
7
+ @valid_blueprint = blueprint = GoodData::Model::ProjectBlueprint.new(
8
+ {
9
+ :title => "x",
10
+ :datasets => [
11
+ {
12
+ :name=>"payments",
13
+ :columns => [
14
+ {:type=>:attribute, :name=>"id"},
15
+ {:type=>:fact, :name=>"amount"},
16
+ {:type=>:reference, :name=>"user_id", :dataset => "users", :reference => "user_id"},
17
+ ]
18
+ },
19
+ {
20
+ :name=>"users",
21
+ :columns => [
22
+ {:type=>:anchor, :name=>"user_id"},
23
+ {:type=>:fact, :name=>"amount"}]
24
+ }
25
+ ]})
26
+
27
+ @invalid_blueprint = blueprint = GoodData::Model::ProjectBlueprint.new(
28
+ {
29
+ :title => "x",
30
+ :datasets => [
31
+ {
32
+ :name=>"payments",
33
+ :columns => [
34
+ {:type=>:attribute, :name=>"id"},
35
+ {:type=>:fact, :name=>"amount"},
36
+ {:type=>:reference, :name=>"user_id", :dataset => "users", :reference => "user_id"},
37
+ ]
38
+ },
39
+ {
40
+ :name=>"users",
41
+ :columns => [
42
+ {:type=>:attribute, :name=>"user_id"},
43
+ {:type=>:fact, :name=>"amount"}]
44
+ }
45
+ ]})
46
+
47
+ end
48
+
49
+ it "valid blueprint should be marked as valid" do
50
+ @valid_blueprint.model_valid?.should == true
51
+ end
52
+
53
+ it "valid blueprint should give you empty array of errors" do
54
+ expect(@valid_blueprint.model_validate).to be_empty
55
+ end
56
+
57
+ it "invalid blueprint should be marked as invalid" do
58
+ @invalid_blueprint.model_valid?.should == false
59
+ end
60
+
61
+ it "invalid blueprint should give you list of violating references" do
62
+ errors = @invalid_blueprint.model_validate
63
+ errors.size.should == 1
64
+ errors.first.should == {:type=>:reference,
65
+ :name=>"user_id",
66
+ :dataset=>"users",
67
+ :reference=>"user_id"}
68
+ end
69
+
70
+ it "references return empty array if there is no reference" do
71
+ refs = @valid_blueprint.get_dataset("users").references
72
+ expect(refs).to be_empty
73
+ end
74
+
75
+ it "should be able to get dataset by name" do
76
+ ds = @valid_blueprint.get_dataset("users")
77
+ ds.name.should == "users"
78
+ end
79
+
80
+ it "should tell you it has anchor when it does" do
81
+ ds = @valid_blueprint.get_dataset("users")
82
+ ds.has_anchor?.should == true
83
+ end
84
+
85
+ it "should tell you it does not have anchor when it does not" do
86
+ ds = @invalid_blueprint.get_dataset("users")
87
+ ds.has_anchor?.should == false
88
+ end
89
+
90
+
91
+
92
+ end
@@ -51,4 +51,21 @@ describe "Spin a project", :constraint => 'slow' do
51
51
  k.should include("manifest_devs")
52
52
  end
53
53
  end
54
+
55
+ it "should be able to interpolate metric based on" do
56
+ GoodData.with_project(@project) do |p|
57
+ res = GoodData::Metric.xexecute "SELECT SUM(![fact.commits.lines_changed])"
58
+ res.should == 9
59
+
60
+ res = GoodData::Metric.xexecute({:expression => "SELECT SUM(![fact.commits.lines_changed])"})
61
+ res.should == 9
62
+
63
+ res = GoodData::Metric.execute({:expression => "SELECT SUM(![fact.commits.lines_changed])", :extended_notation => true})
64
+ res.should == 9
65
+
66
+ res = GoodData::Metric.execute("SELECT SUM(![fact.commits.lines_changed])", :extended_notation => true)
67
+ res.should == 9
68
+ end
69
+ end
70
+
54
71
  end
@@ -0,0 +1,89 @@
1
+ require 'gooddata/model'
2
+ require 'pry'
3
+
4
+ describe GoodData::Model::ProjectBlueprint do
5
+
6
+ before(:each) do
7
+ @base_blueprint = blueprint = GoodData::Model::ProjectBlueprint.new(
8
+ {
9
+ :title => "x",
10
+ :datasets => [
11
+ {
12
+ :name=>"payments",
13
+ :columns => [
14
+ {:type=>:attribute, :name=>"id"},
15
+ {:type=>:fact, :name=>"amount"},
16
+ {:type=>:reference, :name=>"user_id", :dataset => "users", :reference => "user_id"},
17
+ ]
18
+ },
19
+ {
20
+ :name=>"users",
21
+ :columns => [
22
+ {:type=>:anchor, :name=>"user_id"},
23
+ {:type=>:fact, :name=>"amount"}]
24
+ }
25
+ ]})
26
+
27
+ @additional_blueprint = GoodData::Model::ProjectBlueprint.new(
28
+ {
29
+ :title => "x",
30
+ :datasets => [
31
+ {
32
+ :name=>"users",
33
+ :columns => [
34
+ {:type=>:attribute, :name=>"region"}
35
+ ]
36
+ }
37
+ ]})
38
+
39
+ @blueprint_with_duplicate = GoodData::Model::ProjectBlueprint.new(
40
+ {
41
+ :title => "x",
42
+ :datasets => [
43
+ {
44
+ :name=>"users",
45
+ :columns => [
46
+ {:type=>:fact, :name=>"amount"}
47
+ ]
48
+ }
49
+ ]})
50
+
51
+ @conflicting_blueprint = GoodData::Model::ProjectBlueprint.new(
52
+ {
53
+ :title => "x",
54
+ :datasets => [
55
+ {
56
+ :name=>"users",
57
+ :columns => [
58
+ {:type=>:attribute, :name=>"amount"}
59
+ ]
60
+ }
61
+ ]})
62
+ end
63
+
64
+ it "should be possible to merge Schema blueprints" do
65
+ first_dataset = @base_blueprint.get_dataset("users").to_hash
66
+ additional_blueprint = @additional_blueprint.get_dataset("users").to_hash
67
+ stuff = GoodData::Model.merge_dataset_columns(first_dataset, additional_blueprint)
68
+ stuff[:columns].include?({:type=>:attribute, :name=>"region"}).should == true
69
+ stuff[:columns].include?({:type=>:fact, :name=>"amount"}).should == true
70
+ end
71
+
72
+ it "should pass when merging 2 columns with the same name if all attributes are identical" do
73
+ first_dataset = @base_blueprint.get_dataset("users").to_hash
74
+ additional_blueprint = @blueprint_with_duplicate.get_dataset("users").to_hash
75
+ stuff = GoodData::Model.merge_dataset_columns(first_dataset, additional_blueprint)
76
+
77
+ stuff[:columns].count.should == 2
78
+ stuff[:columns].include?({:type=>:fact, :name=>"amount"}).should == true
79
+ stuff[:columns].include?({:type=>:anchor, :name=>"user_id"}).should == true
80
+ end
81
+
82
+ it "should pass when merging 2 columns with the same name if all attributes are identical" do
83
+ first_dataset = @base_blueprint.get_dataset("users").to_hash
84
+ additional_blueprint = @conflicting_blueprint.get_dataset("users").to_hash
85
+
86
+ expect{GoodData::Model.merge_dataset_columns(first_dataset, additional_blueprint)}.to raise_error
87
+ end
88
+
89
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gooddata
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0.pre8
4
+ version: 0.6.0.pre9
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -321,8 +321,10 @@ files:
321
321
  - lib/templates/project/data/devs.csv
322
322
  - lib/templates/project/data/repos.csv
323
323
  - lib/templates/project/model/model.rb.erb
324
+ - spec/blueprint_spec.rb
324
325
  - spec/full_project_spec.rb
325
326
  - spec/goodzilla_spec.rb
327
+ - spec/merging_blueprints_spec.rb
326
328
  - spec/model_dsl_spec.rb
327
329
  - spec/test_project_model_spec.json
328
330
  - test/test_commands.rb