quandl_client 2.7.9 → 2.7.11

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