quandl_cassinatra 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/README.md +8 -8
  2. data/UPGRADE.md +10 -0
  3. data/lib/quandl/cassinatra.rb +9 -15
  4. data/lib/quandl/cassinatra/base.rb +58 -0
  5. data/lib/quandl/cassinatra/base/attributes.rb +51 -0
  6. data/lib/quandl/cassinatra/base/model.rb +14 -0
  7. data/lib/quandl/cassinatra/base/search.rb +96 -0
  8. data/lib/quandl/cassinatra/{concerns/properties.rb → base/validation.rb} +9 -31
  9. data/lib/quandl/cassinatra/models/dataset.rb +33 -0
  10. data/lib/quandl/cassinatra/models/dataset/attributes.rb +61 -0
  11. data/lib/quandl/cassinatra/models/dataset/searchable.rb +68 -0
  12. data/lib/quandl/cassinatra/models/multiset.rb +27 -0
  13. data/lib/quandl/cassinatra/version.rb +1 -1
  14. data/lib/quandl/her/remove_method_data.rb +11 -0
  15. data/spec/factories/dataset.rb +1 -1
  16. data/spec/quandl/cassinatra/dataset/attributes_spec.rb +27 -0
  17. data/spec/quandl/cassinatra/dataset/delete_spec.rb +2 -2
  18. data/spec/quandl/cassinatra/dataset/persistence_spec.rb +26 -25
  19. data/spec/quandl/cassinatra/dataset/trim_spec.rb +2 -2
  20. data/spec/quandl/cassinatra/multiset_spec.rb +23 -14
  21. data/spec/spec_helper.rb +3 -1
  22. metadata +14 -14
  23. data/lib/quandl/cassinatra/concerns.rb +0 -17
  24. data/lib/quandl/cassinatra/concerns/search.rb +0 -26
  25. data/lib/quandl/cassinatra/core_ext.rb +0 -1
  26. data/lib/quandl/cassinatra/core_ext/string.rb +0 -8
  27. data/lib/quandl/cassinatra/her.rb +0 -32
  28. data/lib/quandl/cassinatra/model.rb +0 -27
  29. data/lib/quandl/cassinatra/model/dataset.rb +0 -56
  30. data/lib/quandl/cassinatra/model/dataset/searchable.rb +0 -96
  31. data/lib/quandl/cassinatra/model/dataset_attribute.rb +0 -13
  32. data/lib/quandl/cassinatra/model/multiset.rb +0 -36
  33. data/lib/quandl/her/patch.rb +0 -30
data/README.md CHANGED
@@ -11,7 +11,7 @@ include Quandl::Cassinatra
11
11
  ```
12
12
 
13
13
 
14
- ### Quandl::Cassinatra::Model::Dataset
14
+ ### Quandl::Cassinatra::Dataset
15
15
 
16
16
  #### Find / Scope
17
17
 
@@ -19,8 +19,8 @@ include Quandl::Cassinatra
19
19
 
20
20
  require 'quandl/cassinatra'
21
21
 
22
- d = Quandl::Cassinatra::Model::Dataset.limit(10).transform(:rdiff).collapse(:weekly).find(293)
23
- d.data_table
22
+ d = Quandl::Cassinatra::Dataset.limit(10).transform(:rdiff).collapse(:weekly).find(293)
23
+ d.data
24
24
  => [...]
25
25
 
26
26
  d.column_ids
@@ -41,25 +41,25 @@ d.updated_at
41
41
 
42
42
  require 'quandl/cassinatra'
43
43
 
44
- d = Quandl::Cassinatra::Model::Dataset.new
44
+ d = Quandl::Cassinatra::Dataset.new
45
45
  d.id = 5893853
46
46
  d.data = Quandl::Fabricate::Data::Table.rand(rows: 10, columns: 4, nils: false).to_csv
47
47
  d.save
48
48
 
49
- d = Quandl::Cassinatra::Model::Dataset.find(5893853)
50
- d.data_table
49
+ d = Quandl::Cassinatra::Dataset.find(5893853)
50
+ d.data
51
51
  => [...]
52
52
 
53
53
  ```
54
54
 
55
55
 
56
- ### Quandl::Cassinatra::Model::Multiset
56
+ ### Quandl::Cassinatra::Multiset
57
57
 
58
58
  ```ruby
59
59
 
60
60
  require 'quandl/cassinatra'
61
61
 
62
- m = Quandl::Cassinatra::Model::Multiset.with_columns('5893853.1,293.2').limit(10).collapse(:monthly)
62
+ m = Quandl::Cassinatra::Multiset.with_columns('5893853.1,293.2').limit(10).collapse(:monthly)
63
63
  m.data
64
64
  => [...]
65
65
 
data/UPGRADE.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 0.2.3
2
+
3
+ * multiset#reload
4
+ * revise multiset
5
+ * split dataset into attributes.
6
+ * fetch data once
7
+ * Cassinatra::Dataset#data
8
+ * feature/refactor_dataset
9
+
10
+
1
11
  ## 0.2.2
2
12
 
3
13
  * delete uses destroy_existing
@@ -1,26 +1,20 @@
1
1
  require "quandl/cassinatra/version"
2
2
 
3
- require 'quandl/logger'
4
-
5
- require "active_support"
6
- require "active_support/inflector"
7
- require "active_support/core_ext/hash"
8
- require "active_support/core_ext/object"
9
-
10
- require 'her'
11
- require 'quandl/her/patch'
12
3
  require 'scope_composer'
4
+ require 'her'
5
+ require 'quandl/her/remove_method_data'
6
+ require 'quandl/logger'
13
7
  require "quandl/data"
14
8
 
15
- require "quandl/cassinatra/core_ext"
16
9
  require 'quandl/cassinatra/middleware'
17
-
18
- require 'quandl/cassinatra/concerns'
19
- require 'quandl/cassinatra/her'
20
- require 'quandl/cassinatra/model'
10
+ require 'quandl/cassinatra/base'
11
+ require 'quandl/cassinatra/models/dataset'
12
+ require 'quandl/cassinatra/models/multiset'
21
13
 
22
14
  module Quandl
23
15
  module Cassinatra
24
- include Quandl::Cassinatra::Model
16
+ def self.use(url)
17
+ Quandl::Cassinatra::Base.use(url)
18
+ end
25
19
  end
26
20
  end
@@ -0,0 +1,58 @@
1
+ require "active_support"
2
+ require "active_support/inflector"
3
+ require "active_support/core_ext/hash"
4
+ require "active_support/core_ext/object"
5
+
6
+ require 'quandl/cassinatra/base/model'
7
+ require 'quandl/cassinatra/base/attributes'
8
+ require 'quandl/cassinatra/base/validation'
9
+ require 'quandl/cassinatra/base/search'
10
+
11
+ class Quandl::Cassinatra::Base
12
+
13
+ class << self
14
+
15
+ attr_accessor :url
16
+
17
+ def use(url)
18
+ self.url = url
19
+ models_use_her_api!
20
+ end
21
+
22
+ def her_api
23
+ Her::API.new.setup url: url do |c|
24
+ c.use Faraday::Request::UrlEncoded
25
+ c.use Quandl::Cassinatra::Middleware::ParseJSON
26
+ c.use Faraday::Adapter::NetHttp
27
+ end
28
+ end
29
+
30
+ def url
31
+ @url ||= 'http://localhost:9292/'
32
+ end
33
+
34
+ def inherited(subclass)
35
+ # remember models that inherit from base
36
+ models << subclass unless models.include?(subclass)
37
+ # include model behaviour
38
+ subclass.class_eval do
39
+ include Quandl::Cassinatra::Base::Model
40
+ include Quandl::Cassinatra::Base::Attributes
41
+ include Quandl::Cassinatra::Base::Validation
42
+ include Quandl::Cassinatra::Base::Search
43
+ end
44
+ end
45
+
46
+ def models
47
+ @@models ||= []
48
+ end
49
+
50
+ protected
51
+
52
+ def models_use_her_api!
53
+ models.each{|m| m.use_api( her_api ) }
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,51 @@
1
+ class Quandl::Cassinatra::Base
2
+ module Attributes
3
+
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+
8
+ def find_existing(id)
9
+ self.new(id: id)
10
+ end
11
+
12
+ end
13
+
14
+ def inspect
15
+ "#{self.class.name} " + attributes.inspect
16
+ end
17
+
18
+ def imprint(record)
19
+ # ensure matching classes
20
+ raise(ArgumentError, "record must be kind_of?(#{self.class})") unless record.kind_of?( self.class )
21
+ # copy variables
22
+ copy_instance_variables(record)
23
+ end
24
+
25
+ protected
26
+
27
+ def copy_instance_variables(record)
28
+ # copy attributes if record was found
29
+ @attributes = record.attributes if record.exists?
30
+ # copy metadata and errors
31
+ [:metadata, :response_errors].each do |ivar|
32
+ self.instance_variable_set("@#{ivar}", record.instance_variable_get("@#{ivar}") )
33
+ end
34
+ self
35
+ end
36
+
37
+ def write_attributes(hash)
38
+ @attributes = hash
39
+ end
40
+
41
+ def write_attribute(attribute, value)
42
+ self.send(:"#{attribute}_will_change!") if @attributes[:"#{attribute}"] != value
43
+ @attributes[:"#{attribute}"] = value
44
+ end
45
+
46
+ def read_attribute(attribute)
47
+ @attributes[:"#{attribute}"]
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,14 @@
1
+ class Quandl::Cassinatra::Base
2
+ module Model
3
+
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+
8
+ include Her::Model
9
+ use_api Quandl::Cassinatra::Base.her_api
10
+
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,96 @@
1
+ class Quandl::Cassinatra::Base
2
+ module Search
3
+
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+
8
+ include ScopeComposer::Model
9
+
10
+ has_scope_composer
11
+
12
+ scope.class_eval do
13
+
14
+ def fetch_once
15
+ @fetch_once ||= fetch
16
+ end
17
+
18
+ def fetch
19
+ result = find(attributes[:id])
20
+ result = references.imprint( result ) unless references.nil?
21
+ result
22
+ end
23
+
24
+ def find(id)
25
+ begin
26
+ t1 = Time.now
27
+ result = self.class.parent.where(attributes).find(id)
28
+ Quandl::Logger.debug "#{self.class.name}.where(#{attributes.to_param if attributes.respond_to?(:to_param)}).find(#{id}) (#{t1.elapsed_ms})"
29
+ rescue => error
30
+ Quandl::Logger.error "#{self.class.name}.find #{error}"
31
+ end
32
+ result = self.class.parent.new(id: id) if result.nil?
33
+ result
34
+ end
35
+
36
+ def references(*args)
37
+ return @references if args.first.nil?
38
+ @references = args.first
39
+ attributes[:id] = @references.id
40
+ self
41
+ end
42
+
43
+ protected
44
+
45
+ def format_trim_date(date, start_or_end)
46
+ # parse
47
+ date = parse_date(date)
48
+ # format
49
+ if date.is_a?(Date)
50
+ date = date.send("#{start_or_end}_of_frequency", collapse) if collapse.present?
51
+ date.jd
52
+ else
53
+ nil
54
+ end
55
+ end
56
+
57
+ def parse_date( date )
58
+ begin
59
+ date = Date.jd(date.to_i) if date.kind_of?(String) && numeric?(date)
60
+ date = Date.jd(date) if date.is_a?(Integer)
61
+ date = Date.parse(date) if date.is_a?(String) && date =~ /^[0-9]{4}\-[0-9]{2}\-[0-9]{2}$/
62
+ date
63
+ rescue
64
+ nil
65
+ end
66
+ end
67
+
68
+ def numeric?(string)
69
+ string = string.to_s
70
+ if string =~ /^\d+$/
71
+ true
72
+ else
73
+ Float(string).is_a?(Float) rescue false
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
81
+ def reload
82
+ # reset attributes
83
+ @attributes = { 'id' => self.id }
84
+ @metadata = {}
85
+ # return self
86
+ true
87
+ end
88
+
89
+ module ClassMethods
90
+ def scope_names
91
+ self.scope.scope_names
92
+ end
93
+ end
94
+
95
+ end
96
+ end
@@ -1,18 +1,10 @@
1
- module Quandl
2
- module Cassinatra
3
- module Concerns
1
+ class Quandl::Cassinatra::Base
2
+ module Validation
4
3
 
5
- module Properties
6
4
  extend ActiveSupport::Concern
7
5
 
8
6
  included do
9
-
10
- include Her::Model
11
- use_api Quandl::Cassinatra.her_api
12
7
 
13
- before_save :touch_save_time
14
- after_save :log_save_time
15
-
16
8
  before_save :halt_unless_valid!
17
9
 
18
10
  def valid_with_server?
@@ -42,6 +34,10 @@ module Properties
42
34
  status >= 200 && status <= 210
43
35
  end
44
36
 
37
+ def queried?
38
+ status > 0
39
+ end
40
+
45
41
  def status
46
42
  metadata[:status].to_i
47
43
  end
@@ -69,30 +65,12 @@ module Properties
69
65
  response_errors.present? ? { response_errors: response_errors } : {}
70
66
  end
71
67
 
72
-
73
68
  protected
74
69
 
75
70
  def halt_unless_valid!
76
71
  return false unless valid?
77
72
  end
78
-
79
- private
80
-
81
- def save_timer
82
- @save_timer
83
- end
84
-
85
- def touch_save_time
86
- @save_timer = Time.now
87
- end
88
-
89
- def log_save_time
90
- Quandl::Logger.info("#{self.class.name}.save (#{save_timer.elapsed_ms})")
91
- end
92
-
93
- end
94
- end
95
-
96
- end
97
- end
73
+
74
+ end
98
75
  end
76
+ end
@@ -0,0 +1,33 @@
1
+ class Quandl::Cassinatra::Dataset < Quandl::Cassinatra::Base
2
+
3
+ require 'quandl/cassinatra/models/dataset/searchable'
4
+ require 'quandl/cassinatra/models/dataset/attributes'
5
+
6
+ include Quandl::Cassinatra::Dataset::Searchable
7
+ include Quandl::Cassinatra::Dataset::Attributes
8
+
9
+ def rows_count
10
+ count_data
11
+ end
12
+
13
+ def count_data
14
+ @count_data ||= self.class.get("datasets/#{id}/count").attributes[:count]
15
+ end
16
+
17
+ def delete_data
18
+ # cant delete unsaved records
19
+ return false if new_record?
20
+ # delete and return success / failure
21
+ self.class.destroy_existing("#{id}/data").saved?
22
+ end
23
+
24
+ def delete_rows(*dates)
25
+ # cant delete unsaved records
26
+ return false if new_record?
27
+ # collect dates
28
+ query = { dates: Array(dates).flatten }.to_query
29
+ # delete and return success / failure
30
+ self.class.destroy_existing("#{id}/data/rows?#{query}").saved?
31
+ end
32
+
33
+ end
@@ -0,0 +1,61 @@
1
+ module Quandl::Cassinatra::Dataset::Attributes
2
+
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+
7
+ end
8
+
9
+ included do
10
+
11
+ attributes :id, :data, :trim_start, :trim_end, :columns, :frequency,
12
+ :rows_count, :type, :updated_at, :created_at
13
+
14
+ end
15
+
16
+ def column_units
17
+ @column_units ||= columns.collect{|c| c['units'] } if columns.present?
18
+ end
19
+
20
+ def column_names
21
+ @column_names ||= columns.collect{|c| c['name'] } if columns.present?
22
+ end
23
+
24
+ def column_ids
25
+ @column_ids ||= columns.collect{|c| c['id'] } if columns.present?
26
+ end
27
+
28
+ def trim_start
29
+ @trim_start ||= parse_julian_date( read_attribute(:trim_start) )
30
+ end
31
+
32
+ def trim_end
33
+ @trim_end ||= parse_julian_date( read_attribute(:trim_end) )
34
+ end
35
+
36
+ def created_at
37
+ @created_at ||= Time.parse(read_attribute(:created_at)) rescue nil
38
+ end
39
+
40
+ def updated_at
41
+ @updated_at ||= Time.parse(read_attribute(:updated_at)) rescue nil
42
+ end
43
+
44
+ def reload
45
+ # reset instance variables
46
+ [:column_units, :column_names, :column_ids, :count_data, :scope,
47
+ :updated_at, :created_at, :trim_start, :trim_end
48
+ ].each{|n| instance_variable_set("@#{n}", nil) }
49
+ # onwards
50
+ super
51
+ end
52
+
53
+ protected
54
+
55
+ def parse_julian_date(date)
56
+ date = date.to_i unless date.is_a?(Integer)
57
+ date = ( date > 0 ) ? Date.jd(date) : nil
58
+ date
59
+ end
60
+
61
+ end
@@ -0,0 +1,68 @@
1
+ module Quandl::Cassinatra::Dataset::Searchable
2
+
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+
7
+ def search_scopes
8
+ @search_scopes ||+ search_scope.scopes.keys
9
+ end
10
+
11
+ end
12
+
13
+ included do
14
+
15
+ scope.class_eval do
16
+ delegate :each, :collect, :to_a, :to_h, :count, :inspect, :[], to: :to_table, allow_nil: true
17
+ delegate *Quandl::Data::Table.forwardable_methods, to: :to_table, allow_nil: true
18
+ end
19
+
20
+ scope_helper :to_table, -> { fetch_once.data }
21
+
22
+ scope :row, ->(v) { where( row: v.to_i ) }
23
+ scope :limit, ->(v) { where( limit: v.to_i ) }
24
+ scope :offset, ->(v) { where( offset: v.to_i ) }
25
+ scope :accuracy, ->(v) { where( accuracy: v.to_i ) }
26
+ scope :column, ->(v) { where( column: v.to_i ) }
27
+ scope :order, ->(dir) { where( order: (dir.to_sym == :asc) ? 'asc' : 'desc' ) }
28
+ scope :trim_start, ->(date) { where( trim_start: format_trim_date(date, :start)) }
29
+ scope :trim_end, ->(date) { where( trim_end: format_trim_date(date, :end)) }
30
+ scope :with_id, ->(value) { where( id: value.to_i )}
31
+ scope :transform, ->(value) { where( transform: value.to_sym ) if Quandl::Operation::Transform.valid_transformation?( value.try(:to_sym) ) }
32
+
33
+ scope :collapse, ->(value){
34
+ # set collapse
35
+ where( collapse: value ) if Quandl::Operation::Collapse.valid_collapse?( value.try(:to_sym) )
36
+ # reset trims to force them to edge of frequency
37
+ trim_start(trim_start) if trim_start
38
+ trim_end(trim_end) if trim_end
39
+ }
40
+
41
+ end
42
+
43
+ def data
44
+ return read_data if data?
45
+ return scope unless queried?
46
+ nil
47
+ end
48
+
49
+ def data=(value)
50
+ write_data(value)
51
+ end
52
+
53
+ def scope
54
+ @scope ||= self.class.scope.new.references( self )
55
+ end
56
+
57
+
58
+ protected
59
+
60
+ def read_data
61
+ Quandl::Data::Table.new( read_attribute(:data) )
62
+ end
63
+
64
+ def write_data(value )
65
+ write_attribute(:data, Quandl::Data::Table.new(value).to_csv )
66
+ end
67
+
68
+ end
@@ -0,0 +1,27 @@
1
+ class Quandl::Cassinatra::Multiset < Quandl::Cassinatra::Base
2
+
3
+ include Quandl::Cassinatra::Dataset::Searchable
4
+
5
+ # scope_helper :record, -> { find('multiset') }
6
+
7
+ scope :columns, ->(v){ where( columns: v ) }
8
+
9
+ attributes :id, :data, :trim_start, :trim_end, :columns, :frequency, :type, :updated_at, :created_at
10
+
11
+ def column_units
12
+ @column_units ||= columns.collect{|c| c['units'] }
13
+ end
14
+
15
+ def column_names
16
+ @column_names ||= columns.collect{|c| c['name'] }
17
+ end
18
+
19
+ def column_ids
20
+ @column_ids ||= columns.collect{|c| c['id'] }
21
+ end
22
+
23
+ def save
24
+ false
25
+ end
26
+
27
+ end
@@ -1,5 +1,5 @@
1
1
  module Quandl
2
2
  module Cassinatra
3
- VERSION = '0.2.2'
3
+ VERSION = '0.2.3'
4
4
  end
5
5
  end
@@ -0,0 +1,11 @@
1
+ require 'yajl'
2
+
3
+ module Her
4
+ module Model
5
+ # remove deprecated data method since cassinatra returns data: []
6
+ module DeprecatedMethods
7
+ remove_method( :data ) if method_defined?( :data )
8
+ remove_method( :data= ) if method_defined?( :data= )
9
+ end
10
+ end
11
+ end
@@ -2,7 +2,7 @@ FactoryGirl.define do
2
2
 
3
3
  factory :dataset do
4
4
  sequence(:id) { |n| (Time.now.to_f * 1000).to_i + n }
5
- data_table{ Quandl::Fabricate::Data::Table.rand( rows: 730, columns: 3, nils: false ) }
5
+ data{ Quandl::Fabricate::Data::Table.rand( rows: 730, columns: 3, nils: false ) }
6
6
  end
7
7
 
8
8
  end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Dataset do
5
+ subject{ Dataset.find_existing(1) }
6
+ its(:attributes){ should eq({ 'id' => 1 }) }
7
+ its(:id){ should eq 1 }
8
+
9
+ context "#reload" do
10
+ before(:each){ subject.reload }
11
+ its(:attributes){ should eq({ 'id' => 1 }) }
12
+ its(:id){ should eq 1 }
13
+ end
14
+
15
+ context "#id" do
16
+ before(:each){ subject.id = 2 }
17
+ its(:attributes){ should eq({ 'id' => 2 }) }
18
+ its(:id){ should eq 2 }
19
+
20
+ context "#reload" do
21
+ before(:each){ subject.reload }
22
+ its(:attributes){ should eq({ 'id' => 2 }) }
23
+ its(:id){ should eq 2 }
24
+ end
25
+
26
+ end
27
+ end
@@ -17,14 +17,14 @@ describe Dataset do
17
17
 
18
18
  describe "#delete_rows" do
19
19
 
20
- let(:dates_slice){ dataset.data_table.to_h.keys[10..19] }
20
+ let(:dates_slice){ dataset.data.to_h.keys[10..19] }
21
21
 
22
22
  before(:each){ subject.delete_rows( dates_slice ) }
23
23
 
24
24
  its(:status){ should eq 200 }
25
25
 
26
26
  it "data count should be 720" do
27
- Dataset.find( dataset.id ).data_table.count.should eq 720
27
+ Dataset.find( dataset.id ).data.count.should eq 720
28
28
  end
29
29
 
30
30
  end
@@ -3,41 +3,42 @@ require 'spec_helper'
3
3
 
4
4
  describe Dataset do
5
5
 
6
- context "before save" do
7
-
8
- subject{ build(:dataset) }
6
+ let(:dataset){ build(:dataset) }
9
7
 
10
- it { should respond_to :columns_count }
11
- it { should respond_to :data }
12
- it { should respond_to :data_table }
13
-
14
- it "should have data" do
15
- subject.data_table.count.should eq 730
16
- end
17
-
18
- it "should have four columns" do
19
- subject.data_table[0].count.should eq 4
20
- end
21
-
8
+ subject{ dataset }
9
+
10
+ it { should respond_to :data }
11
+
12
+ describe "#data" do
13
+ subject{ dataset.data }
14
+ its(:count){ should eq 730 }
22
15
  end
23
16
 
24
- context "after save" do
17
+ context "#save" do
18
+
19
+ before(:each){ dataset.save }
25
20
 
26
- subject{ d = create(:dataset); Dataset.find(d.id) }
21
+ subject{ Dataset.find(dataset.id) }
27
22
 
23
+ its(:count_data){ should eq dataset.data.count }
24
+
25
+ describe "#columns" do
26
+ subject{ dataset.columns }
27
+ its(:count){ should eq 3 }
28
+ end
29
+
28
30
  describe "#column_ids" do
29
- it "should have 3 columns" do
30
- subject.column_ids.count.should eq 3
31
- end
32
- it "should each be present" do
33
- subject.column_ids.each{|cid| cid.present?.should be_true }
31
+ subject{ dataset.column_ids }
32
+ its(:count){ should eq 3 }
33
+
34
+ it "each should be present" do
35
+ subject.each{|cid| cid.present?.should be_true }
34
36
  end
35
37
  end
36
38
 
37
39
  describe "#data" do
38
- it "should have data" do
39
- subject.data.count.should eq 730
40
- end
40
+ subject{ dataset.data }
41
+ its(:count){ should eq 730 }
41
42
  end
42
43
 
43
44
  end
@@ -14,7 +14,7 @@ describe Dataset do
14
14
  context "after_save" do
15
15
  before(:each){ subject.save }
16
16
  it "should equal the last date" do
17
- subject.trim_start.should eq subject.data_table[-1][0]
17
+ subject.trim_start.should eq subject.data.to_date[-1][0]
18
18
  end
19
19
  end
20
20
  end
@@ -28,7 +28,7 @@ describe Dataset do
28
28
  context "after_save" do
29
29
  before(:each){ subject.save }
30
30
  it "should equal the first date" do
31
- subject.trim_end.should eq subject.data_table[0][0]
31
+ subject.trim_end.should eq subject.data.to_date[0][0]
32
32
  end
33
33
  end
34
34
  end
@@ -2,15 +2,26 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  describe Multiset do
5
- let(:d1){ create(:dataset, data_table: Quandl::Fabricate::Data::Table.rand( rows: 10, columns: 4, nils: false ) ) }
6
- let(:d2){ create(:dataset, data_table: Quandl::Fabricate::Data::Table.rand( rows: 10, columns: 4, nils: false ) ) }
5
+ let(:d1){ create(:dataset, data: Quandl::Fabricate::Data::Table.rand( rows: 10, columns: 4, nils: false ) ) }
6
+ let(:d2){ create(:dataset, data: Quandl::Fabricate::Data::Table.rand( rows: 10, columns: 4, nils: false ) ) }
7
+ let(:multiset){ Multiset.find_existing('ephemeral') }
8
+ subject{ multiset }
9
+
10
+ describe "#data" do
11
+ subject{ multiset.data.columns("#{d1.id}.1,#{d2.id}.2") }
12
+
13
+ its(:count){ should eq 10 }
14
+ its(:to_h){ should be_present }
15
+ its(:to_a){ should be_present }
16
+
17
+ end
18
+
7
19
 
8
20
  context "given ascending columns" do
9
21
 
10
22
  it "should return the expected column_ids" do
11
- [d1, d2]
12
- m = Multiset.columns("#{d2.id.to_i}.1,#{d1.id.to_i}.1,#{d2.id.to_i}.3,#{d1.id.to_i}.2").to_dataset
13
- m.column_ids.should eq [
23
+ subject.data.columns("#{d2.id.to_i}.1,#{d1.id.to_i}.1,#{d2.id.to_i}.3,#{d1.id.to_i}.2").to_a
24
+ subject.column_ids.should eq [
14
25
  Dataset.find(d2.id).column_ids[0],
15
26
  Dataset.find(d1.id).column_ids[0],
16
27
  Dataset.find(d2.id).column_ids[2],
@@ -19,13 +30,11 @@ describe Multiset do
19
30
  end
20
31
 
21
32
  it "should return the expected columns" do
22
- [d1, d2]
23
- sleep 1
24
- m = Multiset.columns("#{d2.id.to_i}.1,#{d1.id.to_i}.1,#{d2.id.to_i}.3,#{d1.id.to_i}.2").to_dataset
25
- m.data_table[0][1].should eq d2.data_table[0][1]
26
- m.data_table[0][2].should eq d1.data_table[0][1]
27
- m.data_table[0][3].should eq d2.data_table[0][3]
28
- m.data_table[0][4].should eq d1.data_table[0][2]
33
+ subject.data.columns("#{d2.id.to_i}.1,#{d1.id.to_i}.1,#{d2.id.to_i}.3,#{d1.id.to_i}.2")
34
+ subject.data[0][1].should eq d2.data[0][1]
35
+ subject.data[0][2].should eq d1.data[0][1]
36
+ subject.data[0][3].should eq d2.data[0][3]
37
+ subject.data[0][4].should eq d1.data[0][2]
29
38
  end
30
39
 
31
40
  end
@@ -33,8 +42,8 @@ describe Multiset do
33
42
  context "given descending columns" do
34
43
 
35
44
  it "should return the expected columns" do
36
- m = Multiset.columns("#{d1.id.to_i}.2,#{d1.id.to_i}.1").to_dataset
37
- m.data_table[0][1].should_not eq m.data_table[0][2]
45
+ subject.data.columns("#{d1.id.to_i}.2,#{d1.id.to_i}.1")
46
+ subject.data[0][1].should_not eq subject.data[0][2]
38
47
  end
39
48
 
40
49
  end
@@ -15,4 +15,6 @@ require "quandl/fabricate"
15
15
  require "quandl/cassinatra"
16
16
  Quandl::Cassinatra.use 'http://192.168.33.10:8983/wikiposit_cassandra/'
17
17
  # Quandl::Cassinatra.use 'http://localhost:9292/'
18
- include Quandl::Cassinatra
18
+ include Quandl::Cassinatra
19
+
20
+ # binding.pry
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quandl_cassinatra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-10 00:00:00.000000000 Z
12
+ date: 2013-10-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -236,23 +236,22 @@ files:
236
236
  - Rakefile
237
237
  - UPGRADE.md
238
238
  - lib/quandl/cassinatra.rb
239
- - lib/quandl/cassinatra/concerns.rb
240
- - lib/quandl/cassinatra/concerns/properties.rb
241
- - lib/quandl/cassinatra/concerns/search.rb
242
- - lib/quandl/cassinatra/core_ext.rb
243
- - lib/quandl/cassinatra/core_ext/string.rb
244
- - lib/quandl/cassinatra/her.rb
239
+ - lib/quandl/cassinatra/base.rb
240
+ - lib/quandl/cassinatra/base/attributes.rb
241
+ - lib/quandl/cassinatra/base/model.rb
242
+ - lib/quandl/cassinatra/base/search.rb
243
+ - lib/quandl/cassinatra/base/validation.rb
245
244
  - lib/quandl/cassinatra/middleware.rb
246
245
  - lib/quandl/cassinatra/middleware/parse_json.rb
247
- - lib/quandl/cassinatra/model.rb
248
- - lib/quandl/cassinatra/model/dataset.rb
249
- - lib/quandl/cassinatra/model/dataset/searchable.rb
250
- - lib/quandl/cassinatra/model/dataset_attribute.rb
251
- - lib/quandl/cassinatra/model/multiset.rb
246
+ - lib/quandl/cassinatra/models/dataset.rb
247
+ - lib/quandl/cassinatra/models/dataset/attributes.rb
248
+ - lib/quandl/cassinatra/models/dataset/searchable.rb
249
+ - lib/quandl/cassinatra/models/multiset.rb
252
250
  - lib/quandl/cassinatra/version.rb
253
- - lib/quandl/her/patch.rb
251
+ - lib/quandl/her/remove_method_data.rb
254
252
  - quandl_cassinatra.gemspec
255
253
  - spec/factories/dataset.rb
254
+ - spec/quandl/cassinatra/dataset/attributes_spec.rb
256
255
  - spec/quandl/cassinatra/dataset/delete_spec.rb
257
256
  - spec/quandl/cassinatra/dataset/persistence_spec.rb
258
257
  - spec/quandl/cassinatra/dataset/searchable_spec.rb
@@ -286,6 +285,7 @@ specification_version: 3
286
285
  summary: Cassinatra rest orm.
287
286
  test_files:
288
287
  - spec/factories/dataset.rb
288
+ - spec/quandl/cassinatra/dataset/attributes_spec.rb
289
289
  - spec/quandl/cassinatra/dataset/delete_spec.rb
290
290
  - spec/quandl/cassinatra/dataset/persistence_spec.rb
291
291
  - spec/quandl/cassinatra/dataset/searchable_spec.rb
@@ -1,17 +0,0 @@
1
- require "active_support"
2
- require "active_support/inflector"
3
- require "active_support/core_ext/hash"
4
- require "active_support/core_ext/object"
5
-
6
- require 'quandl/cassinatra/concerns/search'
7
- require 'quandl/cassinatra/concerns/properties'
8
-
9
- module Quandl
10
- module Cassinatra
11
-
12
- module Concerns
13
-
14
- end
15
-
16
- end
17
- end
@@ -1,26 +0,0 @@
1
- module Quandl
2
- module Cassinatra
3
- module Concerns
4
-
5
- module Search
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
-
10
- include ScopeComposer::Model
11
-
12
- scope_composer_for :search
13
-
14
- search_helper :all, ->{ connection.where(attributes).fetch }
15
- search_helper :connection, -> { self.class.parent }
16
-
17
- search_scope.class_eval do
18
- delegate *Array.forwardable_methods, to: :all
19
- end
20
-
21
- end
22
- end
23
-
24
- end
25
- end
26
- end
@@ -1 +0,0 @@
1
- require "quandl/cassinatra/core_ext/string"
@@ -1,8 +0,0 @@
1
- class String
2
-
3
- def numeric?
4
- return true if self =~ /^\d+$/
5
- true if Float(self) rescue false
6
- end
7
-
8
- end
@@ -1,32 +0,0 @@
1
- module Quandl
2
- module Cassinatra
3
- class << self
4
-
5
- def use(url)
6
- self.rest_url = url
7
- end
8
-
9
- def her_api
10
- # setup api
11
- api = Her::API.new
12
- api.setup url: rest_url do |c|
13
- c.use Faraday::Request::UrlEncoded
14
- c.use Quandl::Cassinatra::Middleware::ParseJSON
15
- c.use Faraday::Adapter::NetHttp
16
- end
17
- end
18
-
19
- def rest_url
20
- @rest_url ||= 'http://localhost:9292/'
21
- end
22
-
23
- def rest_url=(url)
24
- url = "http://#{url}" if ( url =~ /^http:\/\// ) == nil
25
- @rest_url = url
26
- Model.use_api( her_api )
27
- @rest_url
28
- end
29
-
30
- end
31
- end
32
- end
@@ -1,27 +0,0 @@
1
- require 'quandl/cassinatra/model/dataset'
2
- require 'quandl/cassinatra/model/dataset_attribute'
3
- require 'quandl/cassinatra/model/multiset'
4
-
5
- module Quandl
6
- module Cassinatra
7
- module Model
8
- class << self
9
-
10
- def use_api(api)
11
- each{|k| k.use_api(api) }
12
- end
13
-
14
- def each(&block)
15
- models.each do |k|
16
- block.call(k)
17
- end
18
- end
19
-
20
- def models
21
- [Dataset, DatasetAttribute, Multiset]
22
- end
23
-
24
- end
25
- end
26
- end
27
- end
@@ -1,56 +0,0 @@
1
- require 'quandl/cassinatra/model/dataset/searchable'
2
-
3
- module Quandl
4
- module Cassinatra
5
- module Model
6
-
7
- class Dataset
8
-
9
- include Concerns::Properties
10
- include Searchable
11
-
12
- attributes :id, :data, :trim_start, :trim_end, :columns
13
-
14
- delegate :columns_count, :created_at, :frequency, :rows_count, :type, :updated_at, to: :dataset_attribute
15
-
16
- def column_units
17
- @column_units ||= columns.collect{|c| c['units'] }
18
- end
19
-
20
- def column_names
21
- @column_names ||= columns.collect{|c| c['name'] }
22
- end
23
-
24
- def column_ids
25
- @column_ids ||= columns.collect{|c| c['id'] }
26
- end
27
-
28
- def data_table
29
- @data_table ||= Data::Table.new( self.data || [] )
30
- end
31
- def data_table=(value)
32
- self.data = Data::Table.new(value).to_csv
33
- end
34
-
35
- def dataset_attribute(*args)
36
- return @dataset_attribute if @dataset_attribute
37
- # options
38
- options = args.extract_options!
39
- count = options[:count] == true
40
- # grab dataset_attribute
41
- @dataset_attribute = DatasetAttribute.where( count: count ).find(id)
42
- end
43
-
44
- def delete_data
45
- Dataset.destroy_existing("#{id.to_i}/data").status
46
- end
47
-
48
- def delete_rows(*dates)
49
- query = { dates: Array(dates).flatten }.to_query
50
- Dataset.destroy_existing("#{id.to_i}/data/rows?#{query}").status
51
- end
52
-
53
- end
54
- end
55
- end
56
- end
@@ -1,96 +0,0 @@
1
- module Quandl
2
- module Cassinatra
3
- module Model
4
- class Dataset
5
-
6
- module Searchable
7
-
8
- extend ActiveSupport::Concern
9
-
10
- module ClassMethods
11
-
12
- def search_scopes
13
- @search_scopes ||+ search_scope.scopes.keys
14
- end
15
-
16
- end
17
-
18
- included do
19
-
20
- include Quandl
21
- include ScopeComposer::Model
22
-
23
- scope_composer_for :search
24
-
25
- search_helper :column_ids, -> { dataset.column_ids }
26
- search_helper :data_table, -> { dataset.data_table }
27
-
28
- search_helper :dataset, -> { find(attributes[:id]) }
29
-
30
- search_scope.class_eval do
31
- delegate *Array.forwardable_methods, to: :data_table
32
- delegate *Data::Table.forwardable_methods, to: :data_table
33
- end
34
-
35
- search_scope :row, ->(v) { where( row: v.to_i ) }
36
- search_scope :limit, ->(v) { where( limit: v.to_i ) }
37
- search_scope :offset, ->(v) { where( offset: v.to_i ) }
38
- search_scope :accuracy, ->(v) { where( accuracy: v.to_i ) }
39
- search_scope :column, ->(v) { where( column: v.to_i ) }
40
- search_scope :order, ->(dir) { where( order: (dir.to_sym == :asc) ? 'asc' : 'desc' ) }
41
- search_scope :trim_start, ->(date) { where( trim_start: format_trim_date(date, :start)) }
42
- search_scope :trim_end, ->(date) { where( trim_end: format_trim_date(date, :end)) }
43
- search_scope :with_id, ->(value) { where( id: value.to_i )}
44
- search_scope :transform, ->(value) { where( transform: value.to_sym ) if Quandl::Operation::Transform.valid_transformation?( value.try(:to_sym) ) }
45
-
46
- search_scope :collapse, ->(value){
47
- # set collapse
48
- where( collapse: value ) if Quandl::Operation::Collapse.valid_collapse?( value.try(:to_sym) )
49
- # reset trims to force them to edge of frequency
50
- trim_start(trim_start) if trim_start
51
- trim_end(trim_end) if trim_end
52
- }
53
-
54
- search_helper :format_trim_date, ->( date, start_or_end ){
55
- # parse
56
- date = parse_date(date)
57
- # format
58
- if date.is_a?(Date)
59
- date = date.send("#{start_or_end}_of_frequency", collapse) if collapse.present?
60
- date.jd
61
- else
62
- nil
63
- end
64
- }
65
-
66
- search_helper :parse_date, ->( date ){
67
- begin
68
- date = Date.jd(date.to_i) if date.kind_of?(String) && date.numeric?
69
- date = Date.jd(date) if date.is_a?(Integer)
70
- date = Date.parse(date) if date.is_a?(String) && date =~ /^[0-9]{4}\-[0-9]{2}\-[0-9]{2}$/
71
- date
72
- rescue
73
- nil
74
- end
75
- }
76
-
77
- search_helper :find, ->(id){
78
- begin
79
- t1 = Time.now
80
- result = self.class.parent.where(attributes).find(id)
81
- Quandl::Logger.debug "Quandl::Cassinatra::Dataset.where(#{attributes.to_param if attributes.respond_to?(:to_param)}).find(#{id}) (#{t1.elapsed_ms})"
82
- rescue => error
83
- Quandl::Logger.error "Quandl::Cassinatra::Dataset.find #{error}"
84
- end
85
- result = self.class.parent.new(id: id) if result.nil?
86
- result
87
- }
88
-
89
- end
90
-
91
- end
92
-
93
- end
94
- end
95
- end
96
- end
@@ -1,13 +0,0 @@
1
- module Quandl
2
- module Cassinatra
3
- module Model
4
-
5
- class DatasetAttribute
6
-
7
- include Concerns::Properties
8
-
9
- end
10
-
11
- end
12
- end
13
- end
@@ -1,36 +0,0 @@
1
- module Quandl
2
- module Cassinatra
3
- module Model
4
-
5
- class Multiset
6
-
7
- include Model::Dataset::Searchable
8
- include Concerns::Properties
9
-
10
-
11
- search_helper :to_dataset, -> { dataset }
12
- search_helper :dataset, -> { find('multiset') }
13
-
14
- search_scope :columns, ->(v){ where( columns: v ) }
15
-
16
- attributes :id, :column_ids, :data
17
-
18
- delegate :columns_count, :created_at, :frequency, :rows_count, :type, :updated_at, to: :dataset_attribute
19
-
20
- def data_table
21
- Data::Table.new( self.data || [] )
22
- end
23
- def dataset_attribute(*args)
24
- return @dataset_attribute if @dataset_attribute
25
- # options
26
- options = args.extract_options!
27
- count = options[:count] == true
28
- # grab dataset_attribute
29
- @dataset_attribute = DatasetAttribute.where( count: count ).find(id)
30
- end
31
-
32
- end
33
-
34
- end
35
- end
36
- end
@@ -1,30 +0,0 @@
1
- require 'yajl'
2
-
3
- module Her
4
- module Model
5
- # remove deprecated data method since cassinatra returns data: []
6
- module DeprecatedMethods
7
- remove_method :data
8
- remove_method :data=
9
- end
10
- end
11
- module Middleware
12
- class ParseJSON < Faraday::Response::Middleware
13
- # @private
14
- def parse_json(body = nil)
15
- body ||= '{}'
16
- message = "Response from the API must behave like a Hash or an Array (last JSON response was #{body.inspect})"
17
-
18
- json = begin
19
- Yajl.load(body, :symbolize_keys => true)
20
- rescue Yajl::ParseError
21
- raise Her::Errors::ParseError, message
22
- end
23
-
24
- raise Her::Errors::ParseError, message unless json.is_a?(Hash) or json.is_a?(Array)
25
-
26
- json
27
- end
28
- end
29
- end
30
- end