keen-csv 1.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b9fe8e0dce221ce5b5e2f0b915e85bd2e8084b36
4
+ data.tar.gz: 1ab02862ac7b47d6fffb82c8b22633e09dc50fe5
5
+ SHA512:
6
+ metadata.gz: e2df446da5ab8e23eb305d2555487a37cd131dbef5650cff34d1ad1234fa91c501ef3505ea4502f220ca5ece89dde98ccdf561f925b21b9a55c7216bac441c45
7
+ data.tar.gz: 9b009e1685d3d5f4c99daedef05991d406cbd1e32aaf4ad1c7cb8e22558bde26354c31a645481c6344b032aa963f07c53ca87f67bf944505bd5f4905759bd845
@@ -0,0 +1,31 @@
1
+ require('keen')
2
+
3
+ # Monkeypatch the Keen gem to wrap :query and steal the :csv option
4
+ module Keen
5
+ class << self
6
+
7
+ def query_wrapper(*args)
8
+ # let's optimize for lowest-possible overhead in case 'csv' output isn't requested
9
+ if args[-1][:csv]
10
+ # CSV ouput was requested.
11
+ # First, remove the :csv option to keep from messing up the Keen gem
12
+ options = args[-1].delete(:csv)
13
+ # and perform the query
14
+ response = naked_query(*args)
15
+
16
+ # and now for the main event . . . generating the CSV output
17
+ keenCSV = Keen::CSV.new(response, options)
18
+ return keenCSV.csvString
19
+ else
20
+ # not CSV; just pass through to the naked query
21
+ return naked_query(*args)
22
+ end
23
+ end
24
+
25
+ # Wrap :query with :query_wrapper via aliases
26
+ alias :naked_query :query
27
+ alias :query :query_wrapper
28
+
29
+ end
30
+
31
+ end
data/lib/keen-csv.rb ADDED
@@ -0,0 +1,135 @@
1
+ require_relative('keen/query_wrapper')
2
+
3
+ class Keen::CSV
4
+ @@defaultOptions = {
5
+ delimiter: ',',
6
+ delimiterSub: '.',
7
+ nestedDelimiter: '.',
8
+ filteredColumns: nil,
9
+ }
10
+
11
+ def initialize(response, options = {})
12
+ @rawResponse = response
13
+ @options = options.is_a?(Hash) ? @@defaultOptions.merge(options) : @@defaultOptions
14
+ end
15
+
16
+ # ---------------------------------------------------------------------------
17
+ # generateResultColumns
18
+ # Transforms the Keen result columnar Map, keyed by header
19
+ # ---------------------------------------------------------------------------
20
+ def generateResultColumns
21
+ resultColumns = {
22
+ columns: {},
23
+ maxRowIndex: 0 # We're going to count the rows, for future use
24
+ }
25
+
26
+ # Using a lambda to add the right columns into resultColumns, and keep track
27
+ # of maxRowIndex
28
+ setColumnValue = lambda do |column, rowIndex, value|
29
+ unless self.columnIsFiltered?(column)
30
+ resultColumns[:columns][column] ||= []
31
+ resultColumns[:columns][column][rowIndex] = value
32
+ end
33
+
34
+ # Gotta keep track of how many rows we're working with.
35
+ resultColumns[:maxRowIndex] = rowIndex if rowIndex > resultColumns[:maxRowIndex]
36
+ end
37
+
38
+ # Exit early if this is a simple math operation
39
+ if @rawResponse.is_a? Numeric
40
+ resultColumns[:columns]['result'] = [@rawResponse]
41
+ resultColumns[:maxRowIndex] = 1
42
+ return resultColumns
43
+ end
44
+
45
+ rowIndex = 0
46
+ @rawResponse.each do |object|
47
+ if object["value"].is_a? Array
48
+ # This result is grouped! We're gonna have to create alot more columns and rows
49
+ object["value"].each do |group|
50
+
51
+ # iterate over each value grouping, and store the values
52
+ self.flatten(group).each do |column, value|
53
+ setColumnValue.call(column, rowIndex, value)
54
+ end
55
+ if object["timeframe"]
56
+ self.flatten({"timeframe" => object["timeframe"]}).each do |column, value|
57
+ setColumnValue.call(column, rowIndex, value)
58
+ end
59
+ end
60
+ rowIndex += 1
61
+
62
+ end
63
+ else
64
+ # Not grouped: This either an Extraction or a math operation on an interval.
65
+ self.flatten(object).each do |column, value|
66
+ setColumnValue.call(column, rowIndex, value)
67
+ end
68
+ rowIndex += 1
69
+
70
+ end
71
+ end
72
+
73
+ resultColumns
74
+ end
75
+
76
+ # ---------------------------------------------------------------------------
77
+ # columnIsFiltered?
78
+ # Takes a column header, and determines whether that column should be
79
+ # filtered out
80
+ # ---------------------------------------------------------------------------
81
+ def columnIsFiltered?(header)
82
+ return @options[:filteredColumns] &&
83
+ @options[:filteredColumns].is_a?(Array) &&
84
+ @options[:filteredColumns].include?(header)
85
+ end
86
+
87
+ # ---------------------------------------------------------------------------
88
+ # csvString
89
+ # Generates and returns a CSV for this Keen response
90
+ # ---------------------------------------------------------------------------
91
+ def csvString
92
+ resultColumns = self.generateResultColumns
93
+ headers = resultColumns[:columns].keys
94
+ # Start off instantiating the csv string with the header values
95
+ csvString = headers.map{|s| self.filterValue(s)}.join(@options[:delimiter])
96
+
97
+ # Now iterate over each row, sticking its value under each header
98
+ (0..resultColumns[:maxRowIndex]).each do |rowIndex|
99
+ csvString << "\r\n"
100
+ csvString << headers.map{ |header|
101
+ self.filterValue(resultColumns[:columns][header][rowIndex])
102
+ }.join(@options[:delimiter])
103
+ end
104
+ return csvString
105
+ end
106
+
107
+ # ---------------------------------------------------------------------------
108
+ # filterValue
109
+ # Takes a scalar value, and returns a CSV-compatible one
110
+ # ---------------------------------------------------------------------------
111
+ def filterValue(value)
112
+ if value == nil
113
+ return ''
114
+ else
115
+ return value.to_s.gsub(/#{Regexp.escape(@options[:delimiter])}/, @options[:delimiterSub])
116
+ end
117
+ end
118
+
119
+ # ---------------------------------------------------------------------------
120
+ # flatten
121
+ # Converts any nested dictionaries into a flattened/delimited one.
122
+ # ---------------------------------------------------------------------------
123
+ def flatten(object, flattened = {}, prefix = "")
124
+ object.each do |key, value|
125
+ if value.is_a?(Hash) || value.is_a?(Array)
126
+ # recurse!
127
+ flatten(value, flattened, prefix + key + @options[:nestedDelimiter])
128
+ else
129
+ flattened[prefix + key] = value
130
+ end
131
+ end
132
+
133
+ return flattened
134
+ end
135
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: keen-csv
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jevon Wild
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: keen
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Builds a CSV string from a Keen IO response
28
+ email: jevon@keenio
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/keen-csv.rb
34
+ - lib/keen/query_wrapper.rb
35
+ homepage: https://keen.io/
36
+ licenses:
37
+ - MIT
38
+ metadata: {}
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 2.4.8
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: CSV output for Keen IO
59
+ test_files: []