quandl_client 2.7.9 → 2.7.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/.gitignore +7 -7
  2. data/.rspec +1 -1
  3. data/.travis.yml +20 -20
  4. data/.yardopts +2 -2
  5. data/Gemfile +12 -12
  6. data/Guardfile +8 -8
  7. data/LICENSE +7 -7
  8. data/README.md +303 -303
  9. data/Rakefile +31 -31
  10. data/UPGRADE.md +214 -210
  11. data/VERSION +1 -1
  12. data/examples/create.rb +32 -32
  13. data/examples/find.rb +17 -17
  14. data/examples/login.rb +12 -12
  15. data/examples/search.rb +12 -12
  16. data/examples/trims.rb +15 -15
  17. data/lib/quandl/client.rb +53 -49
  18. data/lib/quandl/client/base.rb +91 -91
  19. data/lib/quandl/client/base/attributes.rb +15 -15
  20. data/lib/quandl/client/base/model.rb +40 -40
  21. data/lib/quandl/client/base/search.rb +74 -74
  22. data/lib/quandl/client/base/validation.rb +101 -101
  23. data/lib/quandl/client/middleware.rb +9 -9
  24. data/lib/quandl/client/middleware/parse_json.rb +85 -85
  25. data/lib/quandl/client/models/dataset.rb +269 -269
  26. data/lib/quandl/client/models/dataset/data.rb +57 -57
  27. data/lib/quandl/client/models/location.rb +10 -10
  28. data/lib/quandl/client/models/report.rb +14 -14
  29. data/lib/quandl/client/models/scraper.rb +16 -16
  30. data/lib/quandl/client/models/sheet.rb +50 -50
  31. data/lib/quandl/client/models/source.rb +48 -48
  32. data/lib/quandl/client/models/superset.rb +65 -65
  33. data/lib/quandl/client/models/user.rb +7 -7
  34. data/lib/quandl/client/version.rb +14 -14
  35. data/lib/quandl/her/remove_method_data.rb +8 -8
  36. data/lib/quandl/pattern.rb +37 -37
  37. data/lib/quandl/pattern/client.rb +8 -8
  38. data/quandl_client.gemspec +33 -33
  39. data/spec/factories/dataset.rb +10 -10
  40. data/spec/factories/sheet.rb +7 -7
  41. data/spec/factories/source.rb +9 -9
  42. data/spec/fixtures/scraper.rb +5 -5
  43. data/spec/lib/quandl/client/dataset/attributes_spec.rb +63 -63
  44. data/spec/lib/quandl/client/dataset/data_spec.rb +92 -92
  45. data/spec/lib/quandl/client/dataset/location_spec.rb +65 -65
  46. data/spec/lib/quandl/client/dataset/persistence_spec.rb +104 -104
  47. data/spec/lib/quandl/client/dataset/search_spec.rb +19 -19
  48. data/spec/lib/quandl/client/dataset/source_spec.rb +47 -47
  49. data/spec/lib/quandl/client/dataset/trim_spec.rb +35 -35
  50. data/spec/lib/quandl/client/dataset/validation_spec.rb +68 -68
  51. data/spec/lib/quandl/client/dataset_spec.rb +57 -57
  52. data/spec/lib/quandl/client/scraper_spec.rb +71 -71
  53. data/spec/lib/quandl/client/sheet_spec.rb +37 -37
  54. data/spec/lib/quandl/client/source_spec.rb +51 -51
  55. data/spec/spec_helper.rb +30 -30
  56. metadata +4 -27
@@ -1,10 +1,10 @@
1
- require 'faraday'
2
- require 'quandl/client/middleware/parse_json'
3
-
4
- module Quandl
5
- module Client
6
- module Middleware
7
-
8
- end
9
- end
1
+ require 'faraday'
2
+ require 'quandl/client/middleware/parse_json'
3
+
4
+ module Quandl
5
+ module Client
6
+ module Middleware
7
+
8
+ end
9
+ end
10
10
  end
@@ -1,86 +1,86 @@
1
- require 'json'
2
-
3
- module Quandl
4
- module Client
5
- module Middleware
6
-
7
- class ParseJSON < Faraday::Response::Middleware
8
-
9
- def on_complete(env)
10
- env[:body] = case env[:status]
11
- when 204
12
- parse('{}', env)
13
- else
14
- parse(env[:body], env)
15
- end
16
- end
17
-
18
- def parse(body, env)
19
- json = parse_json(body, env)
20
- json.has_key?(:docs) ? format_collection( json, env ) : format_record( json, env )
21
- end
22
-
23
- def format_record(json, env)
24
- errors = json.delete(:errors) || {}
25
- metadata = json.delete(:metadata) || {}
26
- # collect some response data
27
- metadata.merge!({
28
- status: env[:status],
29
- headers: env[:response_headers],
30
- })
31
- # return object
32
- object = {
33
- :data => json,
34
- :errors => errors,
35
- :metadata => metadata
36
- }
37
- env[:status] = 200
38
- object
39
- end
40
-
41
- def format_collection(json, env)
42
- errors = json.delete(:errors) || {}
43
- metadata = json.delete(:metadata) || {}
44
- docs = json.delete(:docs)
45
- # collect some response data
46
- metadata.merge!(json).merge!({
47
- status: env[:status],
48
- headers: env[:response_headers],
49
- })
50
- # each doc metadata references metadata
51
- docs.each{|d| d[:metadata] = metadata }
52
- # return object
53
- object = {
54
- :data => docs,
55
- :errors => errors,
56
- :metadata => metadata
57
- }
58
- env[:status] = 200
59
- object
60
- end
61
-
62
- def parse_json(body = nil, env)
63
- body ||= '{}'
64
- json = begin
65
- JSON.parse(body).symbolize_keys!
66
- rescue JSON::ParserError
67
- nil
68
- end
69
- # invalid json body?
70
- if json.blank?
71
- # fallback to error message
72
- json = {
73
- id: 1,
74
- errors: {
75
- parse_errors: [ "Quandl::Client::ParseJSON error. status: #{env[:status]}, body: #{body.inspect}" ]
76
- }
77
- }
78
- end
79
- json
80
- end
81
-
82
- end
83
-
84
- end
85
- end
1
+ require 'json'
2
+
3
+ module Quandl
4
+ module Client
5
+ module Middleware
6
+
7
+ class ParseJSON < Faraday::Response::Middleware
8
+
9
+ def on_complete(env)
10
+ env[:body] = case env[:status]
11
+ when 204
12
+ parse('{}', env)
13
+ else
14
+ parse(env[:body], env)
15
+ end
16
+ end
17
+
18
+ def parse(body, env)
19
+ json = parse_json(body, env)
20
+ json.has_key?(:docs) ? format_collection( json, env ) : format_record( json, env )
21
+ end
22
+
23
+ def format_record(json, env)
24
+ errors = json.delete(:errors) || {}
25
+ metadata = json.delete(:metadata) || {}
26
+ # collect some response data
27
+ metadata.merge!({
28
+ status: env[:status],
29
+ headers: env[:response_headers],
30
+ })
31
+ # return object
32
+ object = {
33
+ :data => json,
34
+ :errors => errors,
35
+ :metadata => metadata
36
+ }
37
+ env[:status] = 200
38
+ object
39
+ end
40
+
41
+ def format_collection(json, env)
42
+ errors = json.delete(:errors) || {}
43
+ metadata = json.delete(:metadata) || {}
44
+ docs = json.delete(:docs)
45
+ # collect some response data
46
+ metadata.merge!(json).merge!({
47
+ status: env[:status],
48
+ headers: env[:response_headers],
49
+ })
50
+ # each doc metadata references metadata
51
+ docs.each{|d| d[:metadata] = metadata }
52
+ # return object
53
+ object = {
54
+ :data => docs,
55
+ :errors => errors,
56
+ :metadata => metadata
57
+ }
58
+ env[:status] = 200
59
+ object
60
+ end
61
+
62
+ def parse_json(body = nil, env)
63
+ body ||= '{}'
64
+ json = begin
65
+ JSON.parse(body).symbolize_keys!
66
+ rescue JSON::ParserError
67
+ nil
68
+ end
69
+ # invalid json body?
70
+ if json.blank?
71
+ # fallback to error message
72
+ json = {
73
+ id: 1,
74
+ errors: {
75
+ parse_errors: [ "Quandl::Client::ParseJSON error. status: #{env[:status]}, body: #{body.inspect}" ]
76
+ }
77
+ }
78
+ end
79
+ json
80
+ end
81
+
82
+ end
83
+
84
+ end
85
+ end
86
86
  end
@@ -1,270 +1,270 @@
1
- class Quandl::Client::Dataset < Quandl::Client::Base
2
-
3
- require 'quandl/client/models/dataset/data'
4
-
5
- ##########
6
- # SCOPES #
7
- ##########
8
- class << self
9
- def touch_existing(id)
10
- put(File.join(Quandl::Client::Base.url_with_version, "datasets/#{id}/touch")).exists?
11
- end
12
-
13
- def find(value)
14
- # preformat
15
- value = format_id(value)
16
- # short-circuit if value is illegal
17
- return nil unless value.is_a?(Integer) || value.to_s =~ %r{^#{Quandl::Pattern.full_code}$}
18
- # search
19
- super(value)
20
- end
21
-
22
- def format_id(value)
23
- # enforce code formatting
24
- if value.is_a?(String)
25
- # strip extra whitespace
26
- value = value.strip.rstrip
27
- # ensure slashes are forward facing
28
- value = value.gsub("\\","/").gsub(".","/")
29
- # ensure uppercase
30
- value = value.upcase
31
- end
32
- value
33
- end
34
-
35
- end
36
-
37
- # SEARCH
38
- scope :query, :rows, :owner
39
- scope :page, ->(p){ where( page: p.to_i )}
40
- scope :source_code, ->(c){ where( code: c.to_s.upcase )}
41
-
42
- ###############
43
- # ASSOCIATIONS #
44
- ###############
45
-
46
- def source
47
- @source ||= Quandl::Client::Source.find(self.source_code)
48
- end
49
-
50
- ###############
51
- # VALIDATIONS #
52
- ###############
53
-
54
- validates :code, presence: true, format: { with: Quandl::Pattern.code, message: "is invalid. Expected format: #{Quandl::Pattern.code.to_example}" }
55
- validates :display_url, allow_blank: true, url: true
56
- validate :data_should_be_valid!
57
- validate :dataset_data_should_be_valid!
58
- validate :data_row_count_should_match_column_count!
59
- validate :data_columns_should_not_exceed_column_names!
60
- validate :data_rows_should_have_equal_columns!
61
- validate :ambiguous_code_requires_source_code!
62
-
63
- validate :source_code_should_exist!
64
-
65
-
66
- ##############
67
- # PROPERTIES #
68
- ##############
69
-
70
- attributes :source_code, :code, :name, :urlize_name,
71
- :description, :updated_at, :frequency,
72
- :from_date, :to_date, :column_names, :private, :type,
73
- :display_url, :column_spec, :import_spec, :import_url,
74
- :locations_attributes, :availability_delay, :refreshed_at
75
-
76
- before_save :enforce_required_formats
77
-
78
- after_save :save_dataset_data
79
-
80
- alias_method :locations, :locations_attributes
81
- alias_method :locations=, :locations_attributes=
82
-
83
- def reference_url
84
- self.display_url
85
- end
86
- def reference_url=(value)
87
- value = "http://#{value}" if value.present? && !(value =~ /:\/\//)
88
- self.display_url = value
89
- end
90
-
91
- def full_url
92
- File.join(Quandl::Client::Base.url.gsub(/api\/?/, ''), full_code)
93
- end
94
-
95
- def full_code
96
- File.join(self.source_code.to_s, self.code.to_s)
97
- end
98
-
99
- # DATA
100
-
101
- def data
102
- defined?(@data) ? @data : data_scope
103
- end
104
-
105
- def data=(value)
106
- @data = Quandl::Data.new(value).sort_descending
107
- end
108
-
109
- def data?
110
- @data.is_a?(Quandl::Data)
111
- end
112
-
113
- def code=(v)
114
- write_attribute(:code, sanitize_code(v) )
115
- end
116
-
117
- def source_code=(v)
118
- write_attribute(:source_code, sanitize_code(v) )
119
- end
120
-
121
- def sanitize_code(code)
122
- code.to_s.upcase.gsub(',','')
123
- end
124
-
125
- def delete_data
126
- # cant delete unsaved records
127
- return false if new_record?
128
- # delete and return success / failure
129
- self.class.destroy_existing("#{id}/data").saved?
130
- end
131
-
132
- def delete_rows(*dates)
133
- # cant delete unsaved records
134
- return false if new_record?
135
- # collect dates
136
- query = { dates: Array(dates).flatten }.to_query
137
- # delete and return success / failure
138
- self.class.destroy_existing("#{id}/data/rows?#{query}").saved?
139
- end
140
-
141
- def data_scope
142
- @data_scope ||= Quandl::Client::Dataset::Data.with_id(id)
143
- end
144
-
145
- def dataset_data
146
- @dataset_data ||= Quandl::Client::Dataset::Data.new( id: id )
147
- end
148
-
149
- def dataset_data?
150
- @dataset_data.is_a?(Quandl::Client::Dataset::Data)
151
- end
152
-
153
- def reload
154
- @dataset_data = nil
155
- @data_scope = nil
156
- @full_code = nil
157
- end
158
-
159
- protected
160
-
161
- def data_should_be_valid!
162
- if data? && !data.valid?
163
- data.errors.each{|k,v| self.errors.add( k,v ) }
164
- return false
165
- end
166
- true
167
- end
168
-
169
- def dataset_data_should_be_valid!
170
- if dataset_data? && !dataset_data.valid?
171
- dataset_data.errors.each{|k,v| self.errors.add( k,v ) }
172
- return false
173
- end
174
- true
175
- end
176
-
177
- def source_code_should_exist!
178
- if source_code.present?
179
- Quandl::Client::Source.cached[source_code] = Quandl::Client::Source.find(source_code) unless Quandl::Client::Source.cached.has_key?(source_code)
180
- source = Quandl::Client::Source.cached[source_code]
181
- self.errors.add( :source_code, "Could not find a source with the source_code '#{source_code}'" ) if source.blank? || source.code.blank?
182
- return false
183
- end
184
- true
185
- end
186
-
187
- def ambiguous_code_requires_source_code!
188
- if code.to_s.numeric? && source_code.blank?
189
- message = %Q{Pure numerical codes like "#{code}" are not allowed unless you include a source code. Do this:\nsource_code: <USERNAME>\ncode: #{code}}
190
- self.errors.add( :data, message )
191
- return false
192
- end
193
- true
194
- end
195
-
196
- def data_columns_should_not_exceed_column_names!
197
- if errors.size == 0 && data? && data.present? && column_names.present? && data.first.count != column_names.count
198
- self.errors.add( :data, "You may not change the number of columns in a dataset. This dataset has #{column_names.count} columns but you tried to send #{data.first.count} columns." )
199
- return false
200
- end
201
- true
202
- end
203
-
204
- def data_rows_should_have_equal_columns!
205
- # skip validation unless data is present
206
- return true unless data? && data.present?
207
- # use first row as expected column count
208
- column_count = data[0].count
209
- # check each row
210
- data.each_with_index do |row, index|
211
- # the row is valid if it's count matches the first row's count
212
- next if row.count == column_count
213
- # the row is invalid if the count is mismatched
214
- self.errors.add( :data, "Unexpected number of points in this row:\n#{row.join(',')}\nFound #{row.size-1} but expected #{data[0].size-1} based on precedent from the first row (#{data[0].join(',')})" )
215
- # return validation failure
216
- return false
217
- end
218
- true
219
- end
220
-
221
- def data_row_count_should_match_column_count!
222
- # skip validation unless data and column_names present
223
- return true unless data? && data.present? && column_names.present?
224
- # count the number of expected columns
225
- column_count = column_names.count
226
- # check each row
227
- data.each_with_index do |row, index|
228
- # the row is valid if it's count matches the first row's count
229
- next if row.count == column_count
230
- # the row is invalid if the count is mismatched
231
- self.errors.add( :data, "Unexpected number of points in this row:\n#{row.join(',')}\nFound #{row.size-1} but expected #{column_names.count-1} based on precedent from the header row (#{column_names.join(',')})" )
232
- # return validation failure
233
- return false
234
- end
235
- true
236
- end
237
-
238
- def save_dataset_data
239
- return if (!saved? && id.blank?)
240
- return if !data? || data.blank?
241
-
242
- dataset_data.id = id
243
- dataset_data.data = data.to_csv
244
- dataset_data.save
245
- # update dataset's attributes with dataset_data's attributes
246
- attributes.each{|k,v| attributes[k] = dataset_data.attributes[k] if dataset_data.attributes.has_key?(k) }
247
- # update dataset errors with dataset_data
248
- @metadata[:status] = dataset_data.status unless dataset_data.saved?
249
- # inherit_errors(dataset_data) unless dataset_data.saved?
250
- end
251
-
252
- def inherit_errors(object)
253
- return unless object.respond_to?(:response_errors) && object.response_errors.respond_to?(:each)
254
- object.response_errors.each do |key, messages|
255
- if messages.respond_to?(:each)
256
- messages.each{|message| errors.add(key, message) }
257
- end
258
- end
259
- @metadata[:status] = object.status
260
- object
261
- end
262
-
263
- def enforce_required_formats
264
- # self.data = Quandl::Data.new(data).to_csv
265
- self.source_code = self.source_code.to_s.upcase
266
- self.code = self.code.to_s.upcase
267
- self.locations_attributes = locations_attributes.to_json if locations_attributes.respond_to?(:to_json) && !locations_attributes.kind_of?(String)
268
- end
269
-
1
+ class Quandl::Client::Dataset < Quandl::Client::Base
2
+
3
+ require 'quandl/client/models/dataset/data'
4
+
5
+ ##########
6
+ # SCOPES #
7
+ ##########
8
+ class << self
9
+ def touch_existing(id)
10
+ put(File.join(Quandl::Client::Base.url_with_version, "datasets/#{id}/touch")).exists?
11
+ end
12
+
13
+ def find(value)
14
+ # preformat
15
+ value = format_id(value)
16
+ # short-circuit if value is illegal
17
+ return nil unless value.is_a?(Integer) || value.to_s =~ %r{^#{Quandl::Pattern.full_code}$}
18
+ # search
19
+ super(value)
20
+ end
21
+
22
+ def format_id(value)
23
+ # enforce code formatting
24
+ if value.is_a?(String)
25
+ # strip extra whitespace
26
+ value = value.strip.rstrip
27
+ # ensure slashes are forward facing
28
+ value = value.gsub("\\","/").gsub(".","/")
29
+ # ensure uppercase
30
+ value = value.upcase
31
+ end
32
+ value
33
+ end
34
+
35
+ end
36
+
37
+ # SEARCH
38
+ scope :query, :rows, :owner
39
+ scope :page, ->(p){ where( page: p.to_i )}
40
+ scope :source_code, ->(c){ where( code: c.to_s.upcase )}
41
+
42
+ ###############
43
+ # ASSOCIATIONS #
44
+ ###############
45
+
46
+ def source
47
+ @source ||= Quandl::Client::Source.find(self.source_code)
48
+ end
49
+
50
+ ###############
51
+ # VALIDATIONS #
52
+ ###############
53
+
54
+ validates :code, presence: true, format: { with: Quandl::Pattern.code, message: "is invalid. Expected format: #{Quandl::Pattern.code.to_example}" }
55
+ validates :display_url, allow_blank: true, url: true
56
+ validate :data_should_be_valid!
57
+ validate :dataset_data_should_be_valid!
58
+ validate :data_row_count_should_match_column_count!
59
+ validate :data_columns_should_not_exceed_column_names!
60
+ validate :data_rows_should_have_equal_columns!
61
+ validate :ambiguous_code_requires_source_code!
62
+
63
+ validate :source_code_should_exist!
64
+
65
+
66
+ ##############
67
+ # PROPERTIES #
68
+ ##############
69
+
70
+ attributes :source_code, :code, :name, :urlize_name,
71
+ :description, :updated_at, :frequency,
72
+ :from_date, :to_date, :column_names, :private, :type,
73
+ :display_url, :column_spec, :import_spec, :import_url,
74
+ :locations_attributes, :availability_delay, :refreshed_at
75
+
76
+ before_save :enforce_required_formats
77
+
78
+ after_save :save_dataset_data
79
+
80
+ alias_method :locations, :locations_attributes
81
+ alias_method :locations=, :locations_attributes=
82
+
83
+ def reference_url
84
+ self.display_url
85
+ end
86
+ def reference_url=(value)
87
+ value = "http://#{value}" if value.present? && !(value =~ /:\/\//)
88
+ self.display_url = value
89
+ end
90
+
91
+ def full_url
92
+ File.join(Quandl::Client::Base.url.gsub(/api\/?/, ''), full_code)
93
+ end
94
+
95
+ def full_code
96
+ File.join(self.source_code.to_s, self.code.to_s)
97
+ end
98
+
99
+ # DATA
100
+
101
+ def data
102
+ defined?(@data) ? @data : data_scope
103
+ end
104
+
105
+ def data=(value)
106
+ @data = Quandl::Data.new(value).sort_descending
107
+ end
108
+
109
+ def data?
110
+ @data.is_a?(Quandl::Data)
111
+ end
112
+
113
+ def code=(v)
114
+ write_attribute(:code, sanitize_code(v) )
115
+ end
116
+
117
+ def source_code=(v)
118
+ write_attribute(:source_code, sanitize_code(v) )
119
+ end
120
+
121
+ def sanitize_code(code)
122
+ code.to_s.upcase.gsub(',','')
123
+ end
124
+
125
+ def delete_data
126
+ # cant delete unsaved records
127
+ return false if new_record?
128
+ # delete and return success / failure
129
+ self.class.destroy_existing("#{id}/data").saved?
130
+ end
131
+
132
+ def delete_rows(*dates)
133
+ # cant delete unsaved records
134
+ return false if new_record?
135
+ # collect dates
136
+ query = { dates: Array(dates).flatten }.to_query
137
+ # delete and return success / failure
138
+ self.class.destroy_existing("#{id}/data/rows?#{query}").saved?
139
+ end
140
+
141
+ def data_scope
142
+ @data_scope ||= Quandl::Client::Dataset::Data.with_id(id)
143
+ end
144
+
145
+ def dataset_data
146
+ @dataset_data ||= Quandl::Client::Dataset::Data.new( id: id )
147
+ end
148
+
149
+ def dataset_data?
150
+ @dataset_data.is_a?(Quandl::Client::Dataset::Data)
151
+ end
152
+
153
+ def reload
154
+ @dataset_data = nil
155
+ @data_scope = nil
156
+ @full_code = nil
157
+ end
158
+
159
+ protected
160
+
161
+ def data_should_be_valid!
162
+ if data? && !data.valid?
163
+ data.errors.each{|k,v| self.errors.add( k,v ) }
164
+ return false
165
+ end
166
+ true
167
+ end
168
+
169
+ def dataset_data_should_be_valid!
170
+ if dataset_data? && !dataset_data.valid?
171
+ dataset_data.errors.each{|k,v| self.errors.add( k,v ) }
172
+ return false
173
+ end
174
+ true
175
+ end
176
+
177
+ def source_code_should_exist!
178
+ if source_code.present?
179
+ Quandl::Client::Source.cached[source_code] = Quandl::Client::Source.find(source_code) unless Quandl::Client::Source.cached.has_key?(source_code)
180
+ source = Quandl::Client::Source.cached[source_code]
181
+ self.errors.add( :source_code, "Could not find a source with the source_code '#{source_code}'" ) if source.blank? || source.code.blank?
182
+ return false
183
+ end
184
+ true
185
+ end
186
+
187
+ def ambiguous_code_requires_source_code!
188
+ if code.to_s.numeric? && source_code.blank?
189
+ message = %Q{Pure numerical codes like "#{code}" are not allowed unless you include a source code. Do this:\nsource_code: <USERNAME>\ncode: #{code}}
190
+ self.errors.add( :data, message )
191
+ return false
192
+ end
193
+ true
194
+ end
195
+
196
+ def data_columns_should_not_exceed_column_names!
197
+ if errors.size == 0 && data? && data.present? && column_names.present? && data.first.count != column_names.count
198
+ self.errors.add( :data, "You may not change the number of columns in a dataset. This dataset has #{column_names.count} columns but you tried to send #{data.first.count} columns." )
199
+ return false
200
+ end
201
+ true
202
+ end
203
+
204
+ def data_rows_should_have_equal_columns!
205
+ # skip validation unless data is present
206
+ return true unless data? && data.present?
207
+ # use first row as expected column count
208
+ column_count = data[0].count
209
+ # check each row
210
+ data.each_with_index do |row, index|
211
+ # the row is valid if it's count matches the first row's count
212
+ next if row.count == column_count
213
+ # the row is invalid if the count is mismatched
214
+ self.errors.add( :data, "Unexpected number of points in this row:\n#{row.join(',')}\nFound #{row.size-1} but expected #{data[0].size-1} based on precedent from the first row (#{data[0].join(',')})" )
215
+ # return validation failure
216
+ return false
217
+ end
218
+ true
219
+ end
220
+
221
+ def data_row_count_should_match_column_count!
222
+ # skip validation unless data and column_names present
223
+ return true unless data? && data.present? && column_names.present?
224
+ # count the number of expected columns
225
+ column_count = column_names.count
226
+ # check each row
227
+ data.each_with_index do |row, index|
228
+ # the row is valid if it's count matches the first row's count
229
+ next if row.count == column_count
230
+ # the row is invalid if the count is mismatched
231
+ self.errors.add( :data, "Unexpected number of points in this row:\n#{row.join(',')}\nFound #{row.size-1} but expected #{column_names.count-1} based on precedent from the header row (#{column_names.join(',')})" )
232
+ # return validation failure
233
+ return false
234
+ end
235
+ true
236
+ end
237
+
238
+ def save_dataset_data
239
+ return if (!saved? && id.blank?)
240
+ return if !data? || data.blank?
241
+
242
+ dataset_data.id = id
243
+ dataset_data.data = data.to_csv
244
+ dataset_data.save
245
+ # update dataset's attributes with dataset_data's attributes
246
+ attributes.each{|k,v| attributes[k] = dataset_data.attributes[k] if dataset_data.attributes.has_key?(k) }
247
+ # update dataset errors with dataset_data
248
+ @metadata[:status] = dataset_data.status unless dataset_data.saved?
249
+ # inherit_errors(dataset_data) unless dataset_data.saved?
250
+ end
251
+
252
+ def inherit_errors(object)
253
+ return unless object.respond_to?(:response_errors) && object.response_errors.respond_to?(:each)
254
+ object.response_errors.each do |key, messages|
255
+ if messages.respond_to?(:each)
256
+ messages.each{|message| errors.add(key, message) }
257
+ end
258
+ end
259
+ @metadata[:status] = object.status
260
+ object
261
+ end
262
+
263
+ def enforce_required_formats
264
+ # self.data = Quandl::Data.new(data).to_csv
265
+ self.source_code = self.source_code.to_s.upcase
266
+ self.code = self.code.to_s.upcase
267
+ self.locations_attributes = locations_attributes.to_json if locations_attributes.respond_to?(:to_json) && !locations_attributes.kind_of?(String)
268
+ end
269
+
270
270
  end