gooddata 0.6.0 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (208) hide show
  1. checksums.yaml +13 -5
  2. data/.rubocop.yml +23 -0
  3. data/.travis.yml +9 -4
  4. data/CLI.md +439 -0
  5. data/Gemfile +0 -1
  6. data/README.md +2 -2
  7. data/Rakefile +60 -8
  8. data/doc/templates/default/module/setup.rb +1 -1
  9. data/examples.rb +2 -0
  10. data/gooddata +2 -0
  11. data/gooddata.gemspec +12 -8
  12. data/lib/gooddata.rb +0 -2
  13. data/lib/gooddata/bricks/base_downloader.rb +52 -47
  14. data/lib/gooddata/bricks/brick.rb +20 -31
  15. data/lib/gooddata/bricks/bricks.rb +1 -1
  16. data/lib/gooddata/bricks/middleware/base_middleware.rb +9 -7
  17. data/lib/gooddata/bricks/middleware/bench_middleware.rb +12 -10
  18. data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +28 -28
  19. data/lib/gooddata/bricks/middleware/fs_upload_middleware.rb +20 -16
  20. data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +21 -19
  21. data/lib/gooddata/bricks/middleware/logger_middleware.rb +10 -8
  22. data/lib/gooddata/bricks/middleware/restforce_middleware.rb +36 -34
  23. data/lib/gooddata/bricks/middleware/stdout_middleware.rb +11 -9
  24. data/lib/gooddata/bricks/middleware/twitter_middleware.rb +14 -12
  25. data/lib/gooddata/bricks/pipeline.rb +28 -0
  26. data/lib/gooddata/bricks/utils.rb +10 -8
  27. data/lib/gooddata/cli/cli.rb +1 -6
  28. data/lib/gooddata/cli/commands/auth_cmd.rb +1 -1
  29. data/lib/gooddata/cli/commands/console_cmd.rb +7 -5
  30. data/lib/gooddata/cli/commands/domain_cmd.rb +45 -0
  31. data/lib/gooddata/cli/commands/process_cmd.rb +42 -5
  32. data/lib/gooddata/cli/commands/project_cmd.rb +96 -36
  33. data/lib/gooddata/cli/commands/projects_cmd.rb +21 -0
  34. data/lib/gooddata/cli/commands/role_cmd.rb +28 -0
  35. data/lib/gooddata/cli/commands/run_ruby_cmd.rb +5 -5
  36. data/lib/gooddata/cli/commands/scaffold_cmd.rb +1 -1
  37. data/lib/gooddata/cli/commands/{profile_cmd.rb → user_cmd.rb} +7 -9
  38. data/lib/gooddata/cli/shared.rb +3 -2
  39. data/lib/gooddata/client.rb +16 -304
  40. data/lib/gooddata/commands/api.rb +13 -5
  41. data/lib/gooddata/commands/auth.rb +47 -40
  42. data/lib/gooddata/commands/base.rb +4 -2
  43. data/lib/gooddata/commands/commands.rb +1 -1
  44. data/lib/gooddata/commands/datasets.rb +20 -7
  45. data/lib/gooddata/commands/domain.rb +23 -0
  46. data/lib/gooddata/commands/process.rb +23 -117
  47. data/lib/gooddata/commands/project.rb +147 -0
  48. data/lib/gooddata/commands/projects.rb +8 -102
  49. data/lib/gooddata/commands/role.rb +26 -0
  50. data/lib/gooddata/commands/runners.rb +41 -38
  51. data/lib/gooddata/commands/scaffold.rb +46 -43
  52. data/lib/gooddata/commands/user.rb +33 -0
  53. data/lib/gooddata/connection.rb +43 -353
  54. data/lib/gooddata/core/connection.rb +389 -0
  55. data/lib/gooddata/core/core.rb +5 -4
  56. data/lib/gooddata/core/logging.rb +48 -0
  57. data/lib/gooddata/core/nil_logger.rb +13 -0
  58. data/lib/gooddata/core/project.rb +70 -0
  59. data/lib/gooddata/core/rest.rb +120 -0
  60. data/lib/gooddata/core/threaded.rb +14 -0
  61. data/lib/gooddata/core/user.rb +19 -0
  62. data/lib/gooddata/data/data.rb +2 -1
  63. data/lib/gooddata/data/guesser.rb +16 -12
  64. data/lib/gooddata/exceptions/command_failed.rb +1 -1
  65. data/lib/gooddata/exceptions/exceptions.rb +2 -1
  66. data/lib/gooddata/exceptions/no_project_error.rb +11 -0
  67. data/lib/gooddata/exceptions/project_not_found.rb +1 -1
  68. data/lib/gooddata/extensions/big_decimal.rb +6 -2
  69. data/lib/gooddata/extract.rb +10 -8
  70. data/lib/gooddata/goodzilla/goodzilla.rb +61 -59
  71. data/lib/gooddata/helpers.rb +15 -9
  72. data/lib/gooddata/models/account_settings.rb +124 -0
  73. data/lib/gooddata/models/attributes/anchor.rb +37 -0
  74. data/lib/gooddata/models/attributes/attributes.rb +8 -0
  75. data/lib/gooddata/models/attributes/date_attribute.rb +25 -0
  76. data/lib/gooddata/models/attributes/time_attribute.rb +24 -0
  77. data/lib/gooddata/models/columns/attribute.rb +71 -0
  78. data/lib/gooddata/models/columns/columns.rb +8 -0
  79. data/lib/gooddata/models/columns/date_column.rb +63 -0
  80. data/lib/gooddata/models/columns/fact_model.rb +54 -0
  81. data/lib/gooddata/models/columns/label.rb +55 -0
  82. data/lib/gooddata/models/columns/reference.rb +57 -0
  83. data/lib/gooddata/models/dashboard_builder.rb +26 -0
  84. data/lib/gooddata/models/data_result.rb +10 -9
  85. data/lib/gooddata/models/domain.rb +131 -0
  86. data/lib/gooddata/models/empty_result.rb +5 -8
  87. data/lib/gooddata/models/facts/facts.rb +8 -0
  88. data/lib/gooddata/models/facts/time_fact.rb +20 -0
  89. data/lib/gooddata/models/folders/attribute_folder.rb +20 -0
  90. data/lib/gooddata/models/folders/fact_folder.rb +20 -0
  91. data/lib/gooddata/models/folders/folders.rb +8 -0
  92. data/lib/gooddata/models/invitation.rb +78 -0
  93. data/lib/gooddata/models/links.rb +6 -6
  94. data/lib/gooddata/models/md_object.rb +25 -0
  95. data/lib/gooddata/models/metadata.rb +160 -62
  96. data/lib/gooddata/models/metadata/attribute.rb +81 -0
  97. data/lib/gooddata/models/metadata/column.rb +61 -0
  98. data/lib/gooddata/models/{dashboard.rb → metadata/dashboard.rb} +12 -7
  99. data/lib/gooddata/models/{data_set.rb → metadata/data_set.rb} +5 -4
  100. data/lib/gooddata/models/metadata/date_dimension.rb +26 -0
  101. data/lib/gooddata/models/metadata/display_form.rb +61 -0
  102. data/lib/gooddata/models/metadata/fact.rb +36 -0
  103. data/lib/gooddata/models/metadata/folder.rb +24 -0
  104. data/lib/gooddata/models/metadata/metadata.rb +8 -0
  105. data/lib/gooddata/models/metadata/metric.rb +197 -0
  106. data/lib/gooddata/models/metadata/report.rb +115 -0
  107. data/lib/gooddata/models/{report_definition.rb → metadata/report_definition.rb} +16 -10
  108. data/lib/gooddata/models/metadata/schema.rb +227 -0
  109. data/lib/gooddata/models/model.rb +38 -1339
  110. data/lib/gooddata/models/models.rb +5 -2
  111. data/lib/gooddata/models/module_constants.rb +29 -0
  112. data/lib/gooddata/models/process.rb +142 -13
  113. data/lib/gooddata/models/profile.rb +4 -6
  114. data/lib/gooddata/models/project.rb +406 -136
  115. data/lib/gooddata/models/project_blueprint.rb +221 -0
  116. data/lib/gooddata/models/project_builder.rb +136 -0
  117. data/lib/gooddata/models/project_creator.rb +138 -0
  118. data/lib/gooddata/models/project_metadata.rb +11 -10
  119. data/lib/gooddata/models/project_role.rb +92 -0
  120. data/lib/gooddata/models/references/date_reference.rb +44 -0
  121. data/lib/gooddata/models/references/references.rb +8 -0
  122. data/lib/gooddata/models/references/time_reference.rb +13 -0
  123. data/lib/gooddata/models/report_data_result.rb +11 -11
  124. data/lib/gooddata/models/schedule.rb +284 -0
  125. data/lib/gooddata/models/schema_blueprint.rb +158 -0
  126. data/lib/gooddata/models/schema_builder.rb +81 -0
  127. data/lib/gooddata/models/tab_builder.rb +23 -0
  128. data/lib/gooddata/models/user.rb +165 -0
  129. data/lib/gooddata/version.rb +1 -1
  130. data/lib/templates/project/data/devs.csv +1 -1
  131. data/lib/templates/project/data/repos.csv +1 -1
  132. data/lib/templates/project/model/model.rb.erb +7 -11
  133. data/spec/bricks/bricks_spec.rb +2 -0
  134. data/spec/data/test-ci-data.csv +2 -0
  135. data/spec/data/test_project_model_spec.json +7 -27
  136. data/spec/helpers/blueprint_helper.rb +2 -0
  137. data/spec/helpers/cli_helper.rb +2 -0
  138. data/spec/helpers/connection_helper.rb +14 -1
  139. data/spec/helpers/project_helper.rb +16 -0
  140. data/spec/helpers/schema_helper.rb +16 -0
  141. data/spec/integration/command_projects_spec.rb +7 -7
  142. data/spec/integration/create_from_template_spec.rb +2 -2
  143. data/spec/integration/full_project_spec.rb +160 -7
  144. data/spec/integration/partial_md_export_import_spec.rb +3 -3
  145. data/spec/logging_in_logging_out_spec.rb +2 -1
  146. data/spec/spec_helper.rb +26 -4
  147. data/spec/unit/bricks/bricks_spec.rb +15 -7
  148. data/spec/unit/bricks/middleware/bench_middleware_spec.rb +2 -0
  149. data/spec/unit/bricks/middleware/bulk_salesforce_middleware_spec.rb +2 -0
  150. data/spec/unit/bricks/middleware/gooddata_middleware_spec.rb +2 -0
  151. data/spec/unit/bricks/middleware/logger_middleware_spec.rb +2 -0
  152. data/spec/unit/bricks/middleware/restforce_middleware_spec.rb +2 -0
  153. data/spec/unit/bricks/middleware/stdout_middleware_spec.rb +2 -0
  154. data/spec/unit/bricks/middleware/twitter_middleware_spec.rb +2 -0
  155. data/spec/unit/cli/cli_spec.rb +2 -0
  156. data/spec/unit/cli/commands/cmd_api_spec.rb +23 -15
  157. data/spec/unit/cli/commands/cmd_auth_spec.rb +8 -4
  158. data/spec/unit/cli/commands/cmd_domain_spec.rb +82 -0
  159. data/spec/unit/cli/commands/cmd_process_spec.rb +29 -13
  160. data/spec/unit/cli/commands/cmd_project_spec.rb +51 -30
  161. data/spec/unit/cli/commands/cmd_role_spec.rb +44 -0
  162. data/spec/unit/cli/commands/cmd_run_ruby_spec.rb +8 -4
  163. data/spec/unit/cli/commands/cmd_scaffold_spec.rb +48 -11
  164. data/spec/unit/cli/commands/cmd_user_spec.rb +29 -0
  165. data/spec/unit/commands/command_api_spec.rb +1 -1
  166. data/spec/unit/commands/command_auth_spec.rb +100 -18
  167. data/spec/unit/commands/command_dataset_spec.rb +4 -0
  168. data/spec/unit/commands/command_process_spec.rb +9 -4
  169. data/spec/unit/commands/command_projects_spec.rb +10 -6
  170. data/spec/unit/commands/command_scaffold_spec.rb +5 -1
  171. data/spec/unit/commands/command_user_spec.rb +22 -0
  172. data/spec/unit/core/connection_spec.rb +35 -6
  173. data/spec/unit/core/logging_spec.rb +65 -0
  174. data/spec/unit/core/nil_logger_spec.rb +9 -0
  175. data/spec/unit/core/project_spec.rb +51 -0
  176. data/spec/unit/core/rest_spec.rb +33 -0
  177. data/spec/unit/data/guesser_spec.rb +5 -0
  178. data/spec/unit/godzilla/goodzilla_spec.rb +2 -0
  179. data/spec/unit/models/account_settings_spec.rb +28 -0
  180. data/spec/unit/models/anchor_spec.rb +32 -0
  181. data/spec/unit/models/attribute_column_spec.rb +7 -0
  182. data/spec/unit/models/domain_spec.rb +45 -0
  183. data/spec/unit/models/invitation_spec.rb +13 -0
  184. data/spec/unit/models/md_object_spec.rb +47 -0
  185. data/spec/unit/models/metric.rb +92 -0
  186. data/spec/unit/{model → models}/model_spec.rb +9 -7
  187. data/spec/unit/models/project_blueprint_spec.rb +202 -0
  188. data/spec/unit/models/project_creator.rb +73 -0
  189. data/spec/unit/models/project_role_spec.rb +90 -0
  190. data/spec/unit/models/project_spec.rb +143 -0
  191. data/spec/unit/models/schedule_spec.rb +491 -0
  192. data/spec/unit/{model → models}/schema_builder_spec.rb +2 -0
  193. data/spec/unit/{model → models}/tools_spec.rb +13 -7
  194. data/spec/unit/models/user_spec.rb +16 -0
  195. data/test/test_upload.rb +2 -0
  196. metadata +189 -86
  197. data/lib/gooddata/commands/profile.rb +0 -11
  198. data/lib/gooddata/models/attribute.rb +0 -29
  199. data/lib/gooddata/models/display_form.rb +0 -9
  200. data/lib/gooddata/models/fact.rb +0 -19
  201. data/lib/gooddata/models/metric.rb +0 -99
  202. data/lib/gooddata/models/report.rb +0 -89
  203. data/spec/data/blueprint_valid.json +0 -37
  204. data/spec/unit/cli/commands/cmd_profile_spec.rb +0 -16
  205. data/spec/unit/commands/command_profile_spec.rb +0 -18
  206. data/spec/unit/core/core_spec.rb +0 -7
  207. data/spec/unit/model/blueprint_spec.rb +0 -132
  208. data/spec/unit/model/project_blueprint_spec.rb +0 -44
@@ -8,9 +8,9 @@ module GoodData
8
8
  @data = {}
9
9
  items.values[0]['links'].each do |item|
10
10
  category = item['category']
11
- if @data[category] then
12
- if @data[category]['category'] == category then
13
- @data[category] = {@data[category]['identifier'] => @data[category]}
11
+ if @data[category]
12
+ if @data[category]['category'] == category
13
+ @data[category] = { @data[category]['identifier'] => @data[category] }
14
14
  end
15
15
  @data[category][item['identifier']] = item
16
16
  else
@@ -29,12 +29,12 @@ module GoodData
29
29
  @data[category]
30
30
  end
31
31
 
32
- def is_unique?(category)
32
+ def unique?(category)
33
33
  @data[category]['link'].is_a? String
34
34
  end
35
35
 
36
- def is_ambiguous?(category)
37
- !is_unique?(category)
36
+ def ambiguous?(category)
37
+ !unique?(category)
38
38
  end
39
39
 
40
40
  def get(category, identifier)
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+
3
+ module GoodData
4
+ module Model
5
+ class MdObject
6
+ attr_accessor :name, :title
7
+
8
+ def visual
9
+ "TITLE \"#{title_esc}\""
10
+ end
11
+
12
+ def title_esc
13
+ title.gsub(/"/, "\\\"")
14
+ end
15
+
16
+ ##
17
+ # Generates an identifier from the object name by transliterating
18
+ # non-Latin character and then dropping non-alphanumerical characters.
19
+ #
20
+ def identifier
21
+ @identifier ||= "#{type_prefix}.#{name}"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,38 +1,62 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require_relative 'model'
3
+ require_relative '../core/connection'
4
+ require_relative '../core/project'
4
5
 
5
6
  module GoodData
6
7
  class MdObject
7
8
  MD_OBJ_CTG = 'obj'
8
9
  IDENTIFIERS_CFG = 'instance-identifiers'
9
10
 
11
+ attr_reader :json
12
+
13
+ alias_method :raw_data, :json
14
+ alias_method :to_hash, :json
15
+ alias_method :data, :json
16
+
10
17
  class << self
11
18
  def root_key(a_key)
12
- define_method :root_key, Proc.new { a_key.to_s }
19
+ define_method :root_key, proc { a_key.to_s }
20
+ end
21
+
22
+ def metadata_property_reader(*props)
23
+ props.each do |prop|
24
+ define_method prop, proc { meta[prop.to_s] }
25
+ end
26
+ end
27
+
28
+ def metadata_property_writer(*props)
29
+ props.each do |prop|
30
+ define_method "#{prop}=", proc { |val| meta[prop.to_s] = val }
31
+ end
13
32
  end
14
33
 
15
- def [](id)
16
- raise "Cannot search for nil #{self.class}" unless id
17
- uri = if id.is_a? Integer or id =~ /^\d+$/
34
+ # Returns either list of objects or a specific object. This method is reimplemented in subclasses to leverage specific implementation for specific type of objects. Options is used in subclasses specifically to provide shorthand for getting a full objects after getting a list of hashes from query resource
35
+ # @param [Object] id id can be either a number a String (as a URI). Subclasses should also be abel to deal with getting the instance of MdObject already and a :all symbol
36
+ # @param [Hash] options the options hash
37
+ # @option options [Boolean] :full if passed true the subclass can decide to pull in full objects. This is desirable from the usability POV but unfortunately has negative impact on performance so it is not the default
38
+ # @return [MdObject] if id is a String or number single object is returned
39
+ # @return [Array] if :all was provided as an id, list of objects should be returned. Note that this is implemented only in the subclasses. MdObject does not support this since API has no means to return list of all types of objects
40
+ def [](id, options = {})
41
+ fail "Cannot search for nil #{self.class}" unless id
42
+ uri = if id.is_a?(Integer) || id =~ /^\d+$/
18
43
  "#{GoodData.project.md[MD_OBJ_CTG]}/#{id}"
19
44
  elsif id !~ /\//
20
45
  identifier_to_uri id
21
46
  elsif id =~ /^\//
22
47
  id
23
48
  else
24
- raise 'Unexpected object id format: expected numeric ID, identifier with no slashes or an URI starting with a slash'
49
+ fail 'Unexpected object id format: expected numeric ID, identifier with no slashes or an URI starting with a slash'
25
50
  end
26
- self.new(GoodData.get uri) unless uri.nil?
51
+ new(GoodData.get uri) unless uri.nil?
27
52
  end
28
53
 
29
- def find_by_tag(tag)
30
- self[:all].find_all { |r| r['tags'].split(',').include?(tag) }
54
+ def all(options = {})
55
+ self[:all, options]
31
56
  end
32
57
 
33
- def get_by_id(id)
34
- uri = GoodData::MdObject.id_to_uri(id)
35
- self[uri] unless uri.nil?
58
+ def find_by_tag(tag)
59
+ self[:all].select { |r| r['tags'].split(',').include?(tag) }
36
60
  end
37
61
 
38
62
  def find_first_by_title(title)
@@ -45,10 +69,26 @@ module GoodData
45
69
  self[item['link']] unless item.nil?
46
70
  end
47
71
 
72
+ # Finds a specific type of the object by title. Returns all matches. Returns full object.
73
+ #
74
+ # @param title [String] title that has to match exactly
75
+ # @param title [Regexp] regular expression that has to match
76
+ # @return [Array<GoodData::MdObject>] Array of MdObject
77
+ def find_by_title(title)
78
+ all = self[:all]
79
+ items = if title.is_a?(Regexp)
80
+ all.select { |r| r['title'] =~ title }
81
+ else
82
+ all.select { |r| r['title'] == title }
83
+ end
84
+ items.map { |item| self[item['link']] unless item.nil? }
85
+ end
86
+
87
+ # TODO: Add test
48
88
  def identifier_to_uri(*ids)
49
- raise NoProjectError.new 'Connect to a project before searching for an object' unless GoodData.project
89
+ fail(NoProjectError, 'Connect to a project before searching for an object') unless GoodData.project
50
90
  uri = GoodData.project.md[IDENTIFIERS_CFG]
51
- response = GoodData.post uri, {'identifierToUri' => ids}
91
+ response = GoodData.post uri, 'identifierToUri' => ids
52
92
  if response['identifiers'].empty?
53
93
  nil
54
94
  else
@@ -57,11 +97,20 @@ module GoodData
57
97
  end
58
98
  end
59
99
 
60
- alias :id_to_uri :identifier_to_uri
100
+ alias_method :id_to_uri, :identifier_to_uri
101
+
102
+ alias_method :get_by_id, :[]
61
103
  end
62
104
 
63
- def initialize(json)
64
- @json = json
105
+ metadata_property_reader :uri, :identifier, :title, :summary, :tags, :deprecated, :category
106
+ metadata_property_writer :tags, :summary, :title
107
+
108
+ def root_key
109
+ raw_data.keys.first
110
+ end
111
+
112
+ def initialize(data)
113
+ @json = data.to_hash
65
114
  end
66
115
 
67
116
  def delete
@@ -72,13 +121,13 @@ module GoodData
72
121
  end
73
122
  end
74
123
 
75
- def refresh
76
- if saved?
77
- @json = GoodData.get(uri)
78
- end
124
+ def reload!
125
+ @json = GoodData.get(uri) if saved?
79
126
  self
80
127
  end
81
128
 
129
+ alias_method :refresh, :reload!
130
+
82
131
  def obj_id
83
132
  uri.split('/').last
84
133
  end
@@ -87,40 +136,30 @@ module GoodData
87
136
  data['links']
88
137
  end
89
138
 
90
- def uri
91
- meta && meta['uri']
92
- end
93
-
94
139
  def browser_uri
95
140
  GoodData.connection.url + meta['uri']
96
141
  end
97
142
 
98
- def identifier
99
- meta['identifier']
100
- end
101
-
102
- def title
103
- meta['title']
104
- end
105
-
106
- def summary
107
- meta['summary']
108
- end
109
-
110
- def title=(a_title)
111
- data['meta']['title'] = a_title
143
+ def updated
144
+ Time.parse(meta['updated'])
112
145
  end
113
146
 
114
- def summary=(a_summary)
115
- data['meta']['summary'] = a_summary
147
+ def created
148
+ Time.parse(meta['created'])
116
149
  end
117
150
 
118
- def tags
119
- data['meta']['tags']
151
+ def deprecated=(flag)
152
+ if flag == '1' || flag == 1
153
+ meta['deprecated'] = '1'
154
+ elsif flag == '0' || flag == 0
155
+ meta['deprecated'] = '0'
156
+ else
157
+ fail 'You have to provide flag as either 1 or "1" or 0 or "0"'
158
+ end
120
159
  end
121
160
 
122
- def tags=(list_of_tags)
123
- data['meta']['tags'] = list_of_tags
161
+ def data
162
+ raw_data[root_key]
124
163
  end
125
164
 
126
165
  def meta
@@ -132,34 +171,36 @@ module GoodData
132
171
  end
133
172
 
134
173
  def project
135
- @project ||= Project[uri.gsub(/\/obj\/\d+$/, '')]
174
+ @project ||= Project[uri.gsub(%r{\/obj\/\d+$}, '')]
136
175
  end
137
176
 
138
- def get_usedby
139
- result = GoodData.get "#{GoodData.project.md['usedby2']}/#{obj_id}"
140
- result['entries']
177
+ def usedby(key = nil)
178
+ dependency("#{GoodData.project.md['usedby2']}/#{obj_id}", key)
141
179
  end
142
180
 
143
- def get_using
144
- result = GoodData.get "#{GoodData.project.md['using2']}/#{obj_id}"
145
- result['entries']
181
+ alias_method :used_by, :usedby
182
+
183
+ def using(key = nil)
184
+ dependency("#{GoodData.project.md['using2']}/#{obj_id}", key)
146
185
  end
147
186
 
148
- def to_json
149
- @json.to_json
187
+ def usedby?(obj)
188
+ dependency?(:usedby, obj)
150
189
  end
151
190
 
152
- def raw_data
153
- @json
191
+ alias_method :used_by?, :usedby?
192
+
193
+ def using?(obj)
194
+ dependency?(:using, obj)
154
195
  end
155
196
 
156
- def data
157
- key = methods.include?(:root_key) ? root_key : raw_data.keys.first
158
- raw_data[key]
197
+ def to_json
198
+ @json.to_json
159
199
  end
160
200
 
161
201
  def saved?
162
- !!uri
202
+ res = uri.nil?
203
+ !res
163
204
  end
164
205
 
165
206
  def save
@@ -168,15 +209,36 @@ module GoodData
168
209
  if saved?
169
210
  GoodData.put(uri, to_json)
170
211
  else
212
+ explicit_identifier = meta['identifier']
213
+ # Pre-check to provide a user-friendly error rather than
214
+ # failing later
215
+ if explicit_identifier && MdObject[explicit_identifier]
216
+ fail "Identifier '#{explicit_identifier}' already in use"
217
+ end
171
218
  result = GoodData.post(GoodData.project.md['obj'], to_json)
172
219
  saved_object = self.class[result['uri']]
220
+ # TODO: add test for explicitly provided identifier
173
221
  @json = saved_object.raw_data
222
+ if explicit_identifier
223
+ # Object creation API discards the identifier. If an identifier
224
+ # was explicitely provided in the origina object, we need to set
225
+ # it explicitly with an extra PUT call.
226
+ meta['identifier'] = explicit_identifier
227
+ begin
228
+ GoodData.put(uri, to_json)
229
+ rescue => e
230
+ # Cannot change the identifier (perhaps because it's in use
231
+ # already?), cleaning up.
232
+ GoodData.delete(uri)
233
+ raise e
234
+ end
235
+ end
174
236
  end
175
237
  self
176
238
  end
177
239
 
178
240
  def ==(other)
179
- other.uri == uri
241
+ other.uri == uri && other.respond_to?(:to_hash) && other.to_hash == to_hash
180
242
  end
181
243
 
182
244
  def validate
@@ -187,5 +249,41 @@ module GoodData
187
249
  false
188
250
  end
189
251
 
252
+ # TODO: generate fill for other subtypes
253
+ def fact?
254
+ false
255
+ end
256
+
257
+ def attribute?
258
+ false
259
+ end
260
+
261
+ def metric?
262
+ false
263
+ end
264
+
265
+ private
266
+
267
+ def dependency(uri, key = nil)
268
+ result = GoodData.get("#{uri}/#{obj_id}")['entries']
269
+ if key.nil?
270
+ result
271
+ elsif key.respond_to?(:category)
272
+ result.select { |item| item['category'] == key.category }
273
+ else
274
+ result.select { |item| item['category'] == key }
275
+ end
276
+ end
277
+
278
+ def dependency?(type, uri)
279
+ objs = case type
280
+ when :usedby
281
+ usedby
282
+ when :using
283
+ using
284
+ end
285
+ uri = uri.respond_to?(:uri) ? uri.uri : uri
286
+ objs.any? { |obj| obj['link'] == uri }
287
+ end
190
288
  end
191
- end
289
+ end
@@ -0,0 +1,81 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative 'metadata'
4
+
5
+ module GoodData
6
+ class Attribute < MdObject
7
+ root_key :attribute
8
+
9
+ ATTRIBUTE_BASE_AGGREGATIONS = [:count]
10
+
11
+ class << self
12
+ def [](id, options = {})
13
+ if id == :all
14
+ attrs = GoodData.get(GoodData.project.md['query'] + '/attributes/')['query']['entries']
15
+ options[:full] ? attrs.map { |a| Attribute[a['link']] } : attrs
16
+ else
17
+ super
18
+ end
19
+ end
20
+
21
+ def find_element_value(uri)
22
+ matches = uri.match(/(.*)\/elements\?id=(\d+)$/)
23
+ Attribute[matches[1]].primary_label.find_element_value(matches[2].to_i)
24
+ end
25
+ end
26
+
27
+ def display_forms
28
+ content['displayForms'].map { |df| GoodData::DisplayForm[df['meta']['uri']] }
29
+ end
30
+ alias_method :labels, :display_forms
31
+
32
+ # Returns the first display form which is the primary one
33
+ # @return [GoodData::DisplayForm] Primary label
34
+ def primary_display_form
35
+ labels.first
36
+ end
37
+ alias_method :primary_label, :primary_display_form
38
+
39
+ def attribute?
40
+ true
41
+ end
42
+
43
+ def create_metric(options = {})
44
+ an_attribute = options[:attribute]
45
+ a_type = options[:type] || :count
46
+ fail "Suggested aggreagtion function (#{a_type}) does not exist for base metric created out of attribute. You can use only one of #{ATTRIBUTE_BASE_AGGREGATIONS.map { |x| ":" + x.to_s }.join(',')}" unless ATTRIBUTE_BASE_AGGREGATIONS.include?(a_type)
47
+ a_title = options[:title] || "#{a_type} of #{title}"
48
+ if an_attribute
49
+ an_attribute = Attribute[an_attribute] if an_attribute.is_a?(String)
50
+ Metric.xcreate(:expression => "SELECT #{a_type.to_s.upcase}(![#{identifier}], ![#{an_attribute.identifier}])", :title => a_title)
51
+ else
52
+ Metric.xcreate(:expression => "SELECT #{a_type.to_s.upcase}(![#{identifier}])", :title => a_title)
53
+ end
54
+ end
55
+
56
+ # For an element id find values (titles) for all display forms. Element id can be given as both number id or URI as a string beginning with /
57
+ # @param [Object] element_id Element identifier either Number or a uri as a String
58
+ # @return [Array] list of values for certain element. Returned in the same order as is the order of labels
59
+ def values_for(element_id)
60
+ element_id = element_id.is_a?(String) ? element_id.match(/\?id=(\d)/)[1] : element_id
61
+ labels.map do |label|
62
+ label.find_element_value(element_id)
63
+ end
64
+ end
65
+
66
+ # Returns all values for all labels. This is for inspection purposes only since obviously there can be huge number of elements.
67
+ # @param [Hash] options the options to pass to the value list
68
+ # @option options [Number] :limit limits the number of values to certain number. Default is 100
69
+ # @return [Array]
70
+ def values(options = {})
71
+ results = labels.map do |label|
72
+ label.values(options)
73
+ end
74
+ results.first.zip(*results[1..-1])
75
+ end
76
+
77
+ def label_by_name(name)
78
+ labels.find { |label| label.title.downcase =~ /#{name}/ || label.identifier.downcase =~ /#{name}/ }
79
+ end
80
+ end
81
+ end