gooddata 0.6.3 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -1
  3. data/CHANGELOG.markdown +6 -0
  4. data/README.md +1 -0
  5. data/gooddata.gemspec +2 -1
  6. data/lib/gooddata.rb +4 -1
  7. data/lib/gooddata/bricks/base_downloader.rb +33 -19
  8. data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +49 -25
  9. data/lib/gooddata/bricks/middleware/restforce_middleware.rb +36 -33
  10. data/lib/gooddata/cli/commands/project_cmd.rb +6 -4
  11. data/lib/gooddata/client.rb +1 -1
  12. data/lib/gooddata/commands/api.rb +1 -1
  13. data/lib/gooddata/commands/auth.rb +1 -1
  14. data/lib/gooddata/connection.rb +13 -10
  15. data/lib/gooddata/core/connection.rb +1 -1
  16. data/lib/gooddata/core/user.rb +11 -3
  17. data/lib/gooddata/exceptions/validation_error.rb +12 -0
  18. data/lib/gooddata/extensions/extensions.rb +6 -0
  19. data/lib/gooddata/goodzilla/goodzilla.rb +2 -2
  20. data/lib/gooddata/helpers/csv_helper.rb +57 -0
  21. data/lib/gooddata/{helpers.rb → helpers/global_helpers.rb} +0 -0
  22. data/lib/gooddata/helpers/helpers.rb +6 -0
  23. data/lib/gooddata/models/domain.rb +134 -24
  24. data/lib/gooddata/models/membership.rb +402 -0
  25. data/lib/gooddata/models/metadata.rb +64 -7
  26. data/lib/gooddata/models/metadata/attribute.rb +27 -12
  27. data/lib/gooddata/models/metadata/column.rb +1 -1
  28. data/lib/gooddata/models/metadata/dashboard.rb +7 -6
  29. data/lib/gooddata/models/metadata/display_form.rb +17 -2
  30. data/lib/gooddata/models/metadata/fact.rb +13 -7
  31. data/lib/gooddata/models/metadata/metric.rb +9 -9
  32. data/lib/gooddata/models/metadata/report.rb +7 -8
  33. data/lib/gooddata/models/metadata/report_definition.rb +10 -11
  34. data/lib/gooddata/models/metadata/schema.rb +1 -1
  35. data/lib/gooddata/models/model.rb +1 -1
  36. data/lib/gooddata/models/process.rb +44 -25
  37. data/lib/gooddata/models/profile.rb +365 -13
  38. data/lib/gooddata/models/project.rb +245 -35
  39. data/lib/gooddata/models/project_blueprint.rb +42 -18
  40. data/lib/gooddata/models/project_creator.rb +4 -1
  41. data/lib/gooddata/models/project_role.rb +7 -7
  42. data/lib/gooddata/models/schedule.rb +17 -1
  43. data/lib/gooddata/models/schema_blueprint.rb +19 -2
  44. data/lib/gooddata/version.rb +1 -1
  45. data/out.txt +0 -0
  46. data/spec/data/users.csv +12 -0
  47. data/spec/helpers/connection_helper.rb +1 -0
  48. data/spec/helpers/csv_helper.rb +12 -0
  49. data/spec/helpers/project_helper.rb +1 -1
  50. data/spec/integration/full_project_spec.rb +136 -3
  51. data/spec/spec_helper.rb +9 -0
  52. data/spec/unit/commands/command_user_spec.rb +1 -1
  53. data/spec/unit/extensions/hash_spec.rb +19 -0
  54. data/spec/unit/godzilla/goodzilla_spec.rb +15 -0
  55. data/spec/unit/helpers/csv_helper_spec.rb +18 -0
  56. data/spec/unit/models/domain_spec.rb +47 -4
  57. data/spec/unit/models/md_object_spec.rb +8 -0
  58. data/spec/unit/models/membership_spec.rb +128 -0
  59. data/spec/unit/models/metadata_spec.rb +38 -0
  60. data/spec/unit/models/profile_spec.rb +212 -0
  61. data/spec/unit/models/project_blueprint_spec.rb +35 -8
  62. data/spec/unit/models/project_role_spec.rb +6 -6
  63. data/spec/unit/models/project_spec.rb +226 -13
  64. data/spec/unit/models/schedule_spec.rb +58 -0
  65. data/tmp/.gitkeepme +0 -0
  66. metadata +36 -11
  67. data/lib/gooddata/models/account_settings.rb +0 -124
  68. data/lib/gooddata/models/user.rb +0 -165
  69. data/spec/unit/models/account_settings_spec.rb +0 -28
  70. data/spec/unit/models/user_spec.rb +0 -16
@@ -7,7 +7,11 @@ module GoodData
7
7
 
8
8
  def self.from_json(spec)
9
9
  if spec.is_a?(String)
10
- ProjectBlueprint.new(MultiJson.load(File.read(spec), :symbolize_keys => true))
10
+ if File.file?(spec)
11
+ ProjectBlueprint.new(MultiJson.load(File.read(spec), :symbolize_keys => true))
12
+ else
13
+ ProjectBlueprint.new(MultiJson.load(spec, :symbolize_keys => true))
14
+ end
11
15
  else
12
16
  ProjectBlueprint.new(spec)
13
17
  end
@@ -21,7 +25,8 @@ module GoodData
21
25
  end
22
26
 
23
27
  def datasets
24
- data[:datasets].map { |d| DatasetBlueprint.new(d) }
28
+ sets = data[:datasets] || []
29
+ sets.map { |d| DatasetBlueprint.new(d) }
25
30
  end
26
31
 
27
32
  def add_dataset(a_dataset, index = nil)
@@ -38,6 +43,13 @@ module GoodData
38
43
  data[:datasets].delete_at(index)
39
44
  end
40
45
 
46
+ # Is this a project blueprint?
47
+ #
48
+ # @return [Boolean] if it is
49
+ def project_blueprint?
50
+ true
51
+ end
52
+
41
53
  def date_dimensions
42
54
  data[:date_dimensions]
43
55
  end
@@ -53,10 +65,24 @@ module GoodData
53
65
  DatasetBlueprint.new(ds)
54
66
  end
55
67
 
68
+ # Constructor
69
+ #
70
+ # @param init_data [ProjectBlueprint | Hash] Blueprint or a blueprint definition. If passed a hash it is used as data for new instance. If there is a ProjectBlueprint passed it is duplicated and a new instance is created.
71
+ # @return [ProjectBlueprint] A new project blueprint instance
56
72
  def initialize(init_data)
57
- @data = init_data
58
- end
59
-
73
+ some_data = if init_data.respond_to?(:project_blueprint?) && init_data.project_blueprint?
74
+ init_data.to_hash
75
+ elsif init_data.respond_to?(:to_blueprint)
76
+ init_data.to_blueprint.to_hash
77
+ else
78
+ init_data
79
+ end
80
+ @data = some_data.deep_dup
81
+ end
82
+
83
+ # Validate the blueprint in particular if all references reference existing datasets and valid fields inside them.
84
+ #
85
+ # @return [Array] array of errors
60
86
  def validate_references
61
87
  if datasets.count == 1
62
88
  []
@@ -71,21 +97,20 @@ module GoodData
71
97
  end
72
98
  end
73
99
 
74
- def validate_labels_references
75
- datasets.reduce([]) { |a, e| a.concat(e.validate_label_references) }
76
- end
77
-
78
- def validate_model
100
+ # Validate the blueprint and all its datasets return array of errors that are found.
101
+ #
102
+ # @return [Array] array of errors
103
+ def validate
79
104
  refs_errors = validate_references
80
- labels_errors = validate_labels_references
105
+ labels_errors = datasets.reduce([]) { |a, e| a.concat(e.validate) }
81
106
  refs_errors.concat(labels_errors)
82
107
  end
83
108
 
84
- def model_valid?
85
- refs_errors = validate_references
86
- labels_errors = validate_labels_references
87
- errors = refs_errors.concat(labels_errors)
88
- errors.empty? ? true : false
109
+ # Validate the blueprint and all its datasets and return true if model is valid. False otherwise.
110
+ #
111
+ # @return [Boolean] is model valid?
112
+ def valid?
113
+ validate.empty?
89
114
  end
90
115
 
91
116
  def referenced_by(dataset)
@@ -185,8 +210,7 @@ module GoodData
185
210
  end
186
211
 
187
212
  def dup
188
- deep_copy = Marshal.load(Marshal.dump(data))
189
- ProjectBlueprint.new(deep_copy)
213
+ ProjectBlueprint.new(data.deep_dup)
190
214
  end
191
215
 
192
216
  def title
@@ -11,7 +11,10 @@ module GoodData
11
11
  class << self
12
12
  def migrate(options = {})
13
13
  spec = options[:spec] || fail('You need to provide spec for migration')
14
- spec = spec.to_hash
14
+ bp = ProjectBlueprint.new(spec)
15
+ spec = bp.to_hash
16
+
17
+ fail GoodData::ValidationError, "Blueprint is invalid #{bp.validate.inspect}" unless bp.valid?
15
18
 
16
19
  token = options[:token]
17
20
  project = options[:project] || GoodData::Project.create(:title => spec[:title], :auth_token => token)
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require_relative 'account_settings'
3
+ require_relative 'profile'
4
4
 
5
5
  module GoodData
6
6
  class ProjectRole
@@ -17,20 +17,20 @@ module GoodData
17
17
 
18
18
  # Gets Project Role Author
19
19
  #
20
- # @return [GoodData::AccountSettings] Project Role author
20
+ # @return [GoodData::Profile] Project Role author
21
21
  def author
22
22
  url = @json['projectRole']['meta']['author']
23
23
  tmp = GoodData.get url
24
- GoodData::AccountSettings.new(tmp)
24
+ GoodData::Profile.new(tmp)
25
25
  end
26
26
 
27
27
  # Gets Project Role Contributor
28
28
  #
29
- # @return [GoodData::AccountSettings] Project Role Contributor
29
+ # @return [GoodData::Profile] Project Role Contributor
30
30
  def contributor
31
31
  url = @json['projectRole']['meta']['contributor']
32
32
  tmp = GoodData.get url
33
- GoodData::AccountSettings.new(tmp)
33
+ GoodData::Profile.new(tmp)
34
34
  end
35
35
 
36
36
  # Gets DateTime time when created
@@ -70,14 +70,14 @@ module GoodData
70
70
 
71
71
  # Gets Users with this Role
72
72
  #
73
- # @return [Array<GoodData::AccountSettings>] List of users
73
+ # @return [Array<GoodData::Profile>] List of users
74
74
  def users
75
75
  res = []
76
76
  url = @json['projectRole']['links']['roleUsers']
77
77
  tmp = GoodData.get url
78
78
  tmp['associatedUsers']['users'].each do |user_url|
79
79
  user = GoodData.get user_url
80
- res << GoodData::AccountSettings.new(user)
80
+ res << GoodData::Profile.new(user)
81
81
  end
82
82
  res
83
83
  end
@@ -48,6 +48,7 @@ module GoodData
48
48
  },
49
49
  :hidden_params => {}
50
50
  }
51
+ default_opts.merge!(:reschedule => options[:reschedule])
51
52
 
52
53
  inject_schema = {
53
54
  :hidden_params => 'hiddenParams'
@@ -183,6 +184,21 @@ module GoodData
183
184
  @dirty = true
184
185
  end
185
186
 
187
+ # Returns reschedule settings
188
+ #
189
+ # @return [Integer] Reschedule settings
190
+ def reschedule
191
+ @json['schedule']['reschedule']
192
+ end
193
+
194
+ # Assigns execution reschedule settings
195
+ #
196
+ # @param new_reschedule [Integer] Reschedule settings to be set
197
+ def reschedule=(new_reschedule)
198
+ @json['schedule']['reschedule'] = new_reschedule
199
+ @dirty = true
200
+ end
201
+
186
202
  # Returns execution process ID
187
203
  #
188
204
  # @return [String] Process ID
@@ -265,8 +281,8 @@ module GoodData
265
281
  'hiddenParams' => @json['schedule']['hiddenParams']
266
282
  }
267
283
  }
284
+ update_json['schedule'].merge!('reschedule' => @json['schedule']['reschedule'])
268
285
  res = GoodData.put uri, update_json
269
-
270
286
  @json = res
271
287
  @dirty = false
272
288
  return true
@@ -120,8 +120,7 @@ module GoodData
120
120
  end
121
121
 
122
122
  def dup
123
- deep_copy = Marshal.load(Marshal.dump(data))
124
- DatasetBlueprint.new(deep_copy)
123
+ DatasetBlueprint.new(data.deep_dup)
125
124
  end
126
125
 
127
126
  def to_wire_model
@@ -148,6 +147,24 @@ module GoodData
148
147
  end
149
148
  end
150
149
 
150
+ # Validate the blueprint return array of errors that are found.
151
+ #
152
+ # @return [Array] array of errors
153
+ def validate
154
+ more_than_one_anchor = find_column_by_type(:anchor, :all).count > 1 ? [{ :anchor => 2 }] : []
155
+ validate_label_references.concat(more_than_one_anchor)
156
+ end
157
+
158
+ # Validate the blueprint and return true if model is valid. False otherwise.
159
+ #
160
+ # @return [Boolean] is model valid?
161
+ def valid?
162
+ validate.empty?
163
+ end
164
+
165
+ # Validate the that any labels are pointing to the existing attribute. If not returns the list of errors. Currently just violating labels.
166
+ #
167
+ # @return [Array] array of errors
151
168
  def validate_label_references
152
169
  labels.select do |label|
153
170
  find_column_by_name(label[:reference]).empty?
@@ -2,7 +2,7 @@
2
2
 
3
3
  # GoodData Module
4
4
  module GoodData
5
- VERSION = '0.6.3'
5
+ VERSION = '0.6.4'
6
6
 
7
7
  class << self
8
8
  # Version
data/out.txt ADDED
File without changes
@@ -0,0 +1,12 @@
1
+ First Name,Last Name,Email,Password,MUF,MUF ID,Role,Role ID,Project ID,Domain
2
+ Gem,Tester,svarovsky+gem_tester@gooddata.com,jindrisska,Edit,,adminRole,5,wgqhml3se0035s8n5byqdq0j0ob5jam4,gooddata-tomas-svarovsky
3
+ Test,User0,test.user.110@example.com,password0,Full,,adminRole,5,wgqhml3se0035s8n5byqdq0j0ob5jam4,gooddata-tomas-svarovsky
4
+ Test,User1,test.user.111@example.com,password1,Full,,connectorsSystemRole,5,wgqhml3se0035s8n5byqdq0j0ob5jam4,gooddata-tomas-svarovsky
5
+ Test,User2,test.user.112@example.com,password2,Full,,editorRole,5,wgqhml3se0035s8n5byqdq0j0ob5jam4,gooddata-tomas-svarovsky
6
+ Test,User3,test.user.113@example.com,password3,Full,,dashboardOnlyRole,5,wgqhml3se0035s8n5byqdq0j0ob5jam4,gooddata-tomas-svarovsky
7
+ Test,User4,test.user.114@example.com,password4,Full,,unverifiedAdminRole,5,wgqhml3se0035s8n5byqdq0j0ob5jam4,gooddata-tomas-svarovsky
8
+ Test,User5,test.user.115@example.com,password5,Full,,readOnlyUserRole,5,wgqhml3se0035s8n5byqdq0j0ob5jam4,gooddata-tomas-svarovsky
9
+ Test,User6,test.user.116@example.com,password6,Full,,adminRole,5,wgqhml3se0035s8n5byqdq0j0ob5jam4,gooddata-tomas-svarovsky
10
+ Test,User7,test.user.117@example.com,password7,Full,,connectorsSystemRole,5,wgqhml3se0035s8n5byqdq0j0ob5jam4,gooddata-tomas-svarovsky
11
+ Test,User8,test.user.118@example.com,password8,Full,,editorRole,5,wgqhml3se0035s8n5byqdq0j0ob5jam4,gooddata-tomas-svarovsky
12
+ Test,User9,test.user.119@example.com,password9,Full,,dashboardOnlyRole,5,wgqhml3se0035s8n5byqdq0j0ob5jam4,gooddata-tomas-svarovsky
@@ -6,6 +6,7 @@ module ConnectionHelper
6
6
  DEFAULT_USERNAME = "svarovsky+gem_tester@gooddata.com"
7
7
  DEFAULT_PASSWORD = "jindrisska"
8
8
  DEFAULT_DOMAIN = 'gooddata-tomas-svarovsky'
9
+ DEFAULT_USER_URL = '/gdc/account/profile/3cea1102d5584813506352a2a2a00d95'
9
10
 
10
11
  # Creates connection using default credentials or supplied one
11
12
  #
@@ -0,0 +1,12 @@
1
+ # encoding: UTF-8
2
+
3
+ # Global requires
4
+ require 'multi_json'
5
+
6
+ # Local requires
7
+ require 'gooddata/models/models'
8
+
9
+ module CsvHelper
10
+ CSV_PATH_EXPORT = 'out.txt'
11
+ CSV_PATH_IMPORT = File.join(File.dirname(__FILE__), '..', 'data', 'users.csv')
12
+ end
@@ -7,7 +7,7 @@ require 'multi_json'
7
7
  require 'gooddata/models/models'
8
8
 
9
9
  module ProjectHelper
10
- PROJECT_ID = 'wgqhml3se0035s8n5byqdq0j0ob5jam4'
10
+ PROJECT_ID = 'we1vvh4il93r0927r809i3agif50d7iz'
11
11
  PROJECT_URL = "/gdc/projects/#{PROJECT_ID}"
12
12
 
13
13
  def self.get_default_project
@@ -1,8 +1,9 @@
1
1
  require 'gooddata'
2
2
 
3
- describe "Ful project implementation", :constraint => 'slow' do
3
+ describe "Full project implementation", :constraint => 'slow' do
4
4
  before(:all) do
5
5
  @spec = JSON.parse(File.read("./spec/data/test_project_model_spec.json"), :symbolize_names => true)
6
+ @invalid_spec = JSON.parse(File.read("./spec/data/blueprint_invalid.json"), :symbolize_names => true)
6
7
  ConnectionHelper::create_default_connection
7
8
  @project = GoodData::Model::ProjectCreator.migrate({:spec => @spec, :token => ConnectionHelper::GD_PROJECT_TOKEN})
8
9
  end
@@ -11,6 +12,12 @@ describe "Ful project implementation", :constraint => 'slow' do
11
12
  @project.delete unless @project.nil?
12
13
  end
13
14
 
15
+ it "should not build an invalid model" do
16
+ expect {
17
+ GoodData::Model::ProjectCreator.migrate({:spec => @invalid_spec, :token => ConnectionHelper::GD_PROJECT_TOKEN})
18
+ }.to raise_error(GoodData::ValidationError)
19
+ end
20
+
14
21
  it "should contain datasets" do
15
22
  GoodData.with_project(@project) do |p|
16
23
  p.datasets.count.should == 4
@@ -79,10 +86,62 @@ describe "Ful project implementation", :constraint => 'slow' do
79
86
  end
80
87
  end
81
88
 
89
+ it "should throw an exception if trying to access object without explicitely specifying a project" do
90
+ expect do
91
+ GoodData::Metric[:all]
92
+ end.to raise_exception(GoodData::NoProjectError)
93
+ end
94
+
95
+ it "should be possible to get all metrics" do
96
+ GoodData.with_project(@project) do |p|
97
+ metrics1 = GoodData::Metric[:all]
98
+ metrics2 = GoodData::Metric.all
99
+ metrics1.should == metrics2
100
+ end
101
+ end
102
+
103
+ it "should be possible to get all metrics with full objects" do
104
+ GoodData.with_project(@project) do |p|
105
+ metrics1 = GoodData::Metric[:all, :full => true]
106
+ metrics2 = GoodData::Metric.all :full => true
107
+ metrics1.should == metrics2
108
+ end
109
+ end
110
+
111
+ it "should be able to get a metric by identifier" do
112
+ GoodData.with_project(@project) do |p|
113
+ metrics = GoodData::Metric.all :full => true
114
+ metric = GoodData::Metric[metrics.first.identifier]
115
+ metric.identifier == metrics.first.identifier
116
+ metrics.first == metric
117
+ end
118
+ end
119
+
120
+ it "should be able to get a metric by uri" do
121
+ GoodData.with_project(@project) do |p|
122
+ metrics = GoodData::Metric.all :full => true
123
+ metric = GoodData::Metric[metrics.first.uri]
124
+ metric.uri == metrics.first.uri
125
+ metrics.first == metric
126
+ end
127
+ end
128
+
129
+ it "should be able to get a metric by object id" do
130
+ GoodData.with_project(@project) do |p|
131
+ metrics = GoodData::Metric.all :full => true
132
+ metric = GoodData::Metric[metrics.first.obj_id]
133
+ metric.obj_id == metrics.first.obj_id
134
+ metrics.first == metric
135
+ end
136
+ end
137
+
82
138
  it "should exercise the object relations and getting them in various ways" do
83
139
  GoodData.with_project(@project) do |p|
140
+
84
141
  # Find a metric by name
85
142
  metric = GoodData::Metric.find_first_by_title('My metric')
143
+ the_same_metric = GoodData::Metric[metric]
144
+ metric.should == metric
86
145
 
87
146
  # grab fact in several different ways
88
147
  fact1 = GoodData::Fact.find_first_by_title('Lines changed')
@@ -148,6 +207,11 @@ describe "Ful project implementation", :constraint => 'slow' do
148
207
 
149
208
  res = GoodData::Metric.execute("SELECT SUM(![fact.commits.lines_changed])", :extended_notation => true)
150
209
  res.should == 9
210
+
211
+ fact = GoodData::Fact.find_first_by_title('Lines changed')
212
+ fact.fact?.should == true
213
+ res = fact.create_metric(:type => :sum).execute
214
+ res.should == 9
151
215
  end
152
216
  end
153
217
 
@@ -163,7 +227,9 @@ describe "Ful project implementation", :constraint => 'slow' do
163
227
 
164
228
  it "should have more users" do
165
229
  GoodData.with_project(@project) do |p|
166
- GoodData::Attribute['attr.devs.dev_id'].create_metric.execute.should == 4
230
+ attribute = GoodData::Attribute['attr.devs.dev_id']
231
+ attribute.attribute?.should == true
232
+ attribute.create_metric.execute.should == 4
167
233
  end
168
234
  end
169
235
 
@@ -188,6 +254,14 @@ describe "Ful project implementation", :constraint => 'slow' do
188
254
  end
189
255
  end
190
256
 
257
+ it "Should be able to compute count o different datasets" do
258
+ GoodData.with_project(@project) do |p|
259
+ attribute = GoodData::Attribute['attr.devs.dev_id']
260
+ dataset_attribute = GoodData::Attribute['attr.commits.id']
261
+ attribute.create_metric(:attribute => dataset_attribute).execute.should == 3
262
+ end
263
+ end
264
+
191
265
  it "should be able to tell you if a value is contained in a metric" do
192
266
  GoodData.with_project(@project) do |p|
193
267
  attribute = GoodData::Attribute['attr.devs.dev_id']
@@ -213,10 +287,69 @@ describe "Ful project implementation", :constraint => 'slow' do
213
287
  end
214
288
  end
215
289
 
216
- it "should be able to lookup the attributes by regexp and return a collectio" do
290
+ it "should be able to lookup the attributes by regexp and return a collection" do
217
291
  GoodData.with_project(@project) do |p|
218
292
  attrs = GoodData::Attribute.find_by_title(/Date/i)
219
293
  attrs.count.should == 1
220
294
  end
221
295
  end
296
+
297
+ it "should be able to give you values of the label as an array of hashes" do
298
+ GoodData.with_project(@project) do |p|
299
+ attribute = GoodData::Attribute['attr.devs.dev_id']
300
+ label = attribute.primary_label
301
+ label.values.map {|v| v[:value]}.should == [
302
+ 'jirka@gooddata.com',
303
+ 'josh@gooddata.com',
304
+ 'petr@gooddata.com',
305
+ 'tomas@gooddata.com'
306
+ ]
307
+ end
308
+ end
309
+
310
+ it "should be able to give you values for" do
311
+ GoodData.with_project(@project) do |p|
312
+ attribute = GoodData::Attribute['attr.devs.dev_id']
313
+ attribute.values_for(2).should == ["tomas@gooddata.com", "1"]
314
+ end
315
+ end
316
+
317
+ it "should be able to find specific element and give you the primary label value" do
318
+ GoodData.with_project(@project) do |p|
319
+ attribute = GoodData::Attribute['attr.devs.dev_id']
320
+ GoodData::Attribute.find_element_value("#{attribute.uri}/elements?id=2").should == 'tomas@gooddata.com'
321
+ end
322
+ end
323
+
324
+ it "should be able to give you label by name" do
325
+ GoodData.with_project(@project) do |p|
326
+ attribute = GoodData::Attribute['attr.devs.dev_id']
327
+ label = attribute.label_by_name('email')
328
+ label.label?.should == true
329
+ label.title.should == 'Email'
330
+ label.identifier.should == "label.devs.dev_id.email"
331
+ label.attribute_uri.should == attribute.uri
332
+ label.attribute.should == attribute
333
+ end
334
+ end
335
+
336
+ it "should be able to return values of the attribute for inspection" do
337
+ GoodData.with_project(@project) do |p|
338
+ attribute = GoodData::Attribute['attr.devs.dev_id']
339
+ vals = attribute.values
340
+ vals.count.should == 4
341
+ vals.first.count.should == 2
342
+ vals.first.first[:value].should == "jirka@gooddata.com"
343
+ end
344
+ end
345
+
346
+ it "should be able to save_as a metric" do
347
+ GoodData.with_project(@project) do |p|
348
+ m = GoodData::Metric.find_first_by_title("My test metric")
349
+ cloned = m.save_as
350
+ m_cloned = GoodData::Metric.find_first_by_title("Clone of My test metric")
351
+ m_cloned.should == cloned
352
+ m_cloned.execute.should == cloned.execute
353
+ end
354
+ end
222
355
  end