chartnado 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/chartnado.gemspec +2 -1
- data/lib/chartnado/helpers/chart_helper.rb +28 -3
- data/lib/chartnado/renderer.rb +24 -24
- data/lib/chartnado/series.rb +7 -140
- data/lib/chartnado/series/wrap.rb +201 -0
- data/lib/chartnado/version.rb +1 -1
- data/spec/rails_helper.rb +1 -0
- data/spec/renderer_spec.rb +18 -0
- data/spec/series_spec.rb +33 -3
- metadata +20 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b20127157066903c9837361a882f6ed75b614895
|
4
|
+
data.tar.gz: 40ad1dcaeea450bddfefe6c345999b5122fa6c10
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70848f7173ba8bc4c6b2e7d754b978e767ec0b8e09a1aa8d7891609fa49c09a515a6329a7b0787f644d4c4b1c12a5ae08885f8dcd1df49f107dc141818b1f474
|
7
|
+
data.tar.gz: 2a898be78fe58e7530e3985b181f2d61f5ef315e982e8cdd921b8680661d03e037f7605fd44ce96ca657a07a31a4a095f84adba09ec2ac966e290c6d94986343
|
data/README.md
CHANGED
@@ -10,12 +10,14 @@ In your controller, add the following to tell the controller to respond to json
|
|
10
10
|
include Chartnado
|
11
11
|
```
|
12
12
|
|
13
|
-
Then in your views,
|
13
|
+
Then in your views, you can write an expression to show the average tasks completed per today relative to total tasks like this:
|
14
14
|
|
15
15
|
```ruby
|
16
16
|
<%= line_chart { Task.group_by_day(:completed_at).count / Task.count } %>
|
17
17
|
```
|
18
18
|
|
19
|
+
See the demo of chartkick-remote and Chartnado at http://chartkick-remote-demo.heroku.com.
|
20
|
+
|
19
21
|
## Totals
|
20
22
|
|
21
23
|
By default chartnado adds totals to pie and stacked area charts using some hacky settings for google charts. To get the total to appear, you need to use the chartnado version of the chartkick javascript called `chartkick-chartnado.js` instead of `chartkick.js`. If you are including the javascript in sprockets manifest file, this:
|
data/chartnado.gemspec
CHANGED
@@ -21,8 +21,9 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.add_dependency "activesupport", '>= 3'
|
23
23
|
spec.add_dependency "chartkick", '>= 1.0'
|
24
|
-
spec.add_dependency "chartkick-remote", '>= 1.
|
24
|
+
spec.add_dependency "chartkick-remote", '>= 1.3'
|
25
25
|
spec.add_dependency "railties", ">= 3.1"
|
26
|
+
spec.add_development_dependency "responders", '~> 2.0'
|
26
27
|
spec.add_development_dependency "bundler", '~> 1.3'
|
27
28
|
spec.add_development_dependency "rake", '~> 10.3'
|
28
29
|
spec.add_development_dependency "rspec", '~> 3.0'
|
@@ -14,7 +14,12 @@ module Chartnado::Helpers
|
|
14
14
|
new_options = chartkick_options.reverse_merge(
|
15
15
|
stacked: true,
|
16
16
|
library: {
|
17
|
-
focusTarget: 'category'
|
17
|
+
focusTarget: 'category'
|
18
|
+
}
|
19
|
+
)
|
20
|
+
new_json_options = json_options.reverse_merge(show_total: true, reverse_sort: true)
|
21
|
+
new_options.reverse_merge!(
|
22
|
+
library: {
|
18
23
|
series: {
|
19
24
|
0 => {
|
20
25
|
lineWidth: 0,
|
@@ -23,9 +28,29 @@ module Chartnado::Helpers
|
|
23
28
|
}
|
24
29
|
}
|
25
30
|
}
|
26
|
-
)
|
31
|
+
) if new_json_options[:show_total]
|
27
32
|
area_chart_without_chartnado(**new_options) do
|
28
|
-
data_block.call(
|
33
|
+
data_block.call(new_json_options)
|
34
|
+
end
|
35
|
+
end.render(*args, **options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def pie_chart_with_chartnado(*args, **options, &block)
|
39
|
+
Chartnado::Renderer.new(self, block) do |chartkick_options, json_options, data_block|
|
40
|
+
new_json_options = json_options.reverse_merge(show_total: true)
|
41
|
+
new_options = chartkick_options.reverse_merge!(
|
42
|
+
library: {
|
43
|
+
series: {
|
44
|
+
0 => {
|
45
|
+
lineWidth: 0,
|
46
|
+
pointSize: 0,
|
47
|
+
visibleInLegend: false
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
) if new_json_options[:show_total]
|
52
|
+
pie_chart_without_chartnado(**new_options) do
|
53
|
+
data_block.call(new_json_options)
|
29
54
|
end
|
30
55
|
end.render(*args, **options)
|
31
56
|
end
|
data/lib/chartnado/renderer.rb
CHANGED
@@ -11,7 +11,7 @@ class Chartnado::Renderer
|
|
11
11
|
|
12
12
|
delegate :controller, to: :context
|
13
13
|
|
14
|
-
def render(*args, **options)
|
14
|
+
def render(*args, ** options)
|
15
15
|
json_options = {}
|
16
16
|
chartkick_options = options.dup
|
17
17
|
|
@@ -40,40 +40,42 @@ class Chartnado::Renderer
|
|
40
40
|
}
|
41
41
|
|
42
42
|
if options[:wrapper_proc]
|
43
|
-
context.instance_exec(*args, renderer, **options, &options[:wrapper_proc])
|
43
|
+
context.instance_exec(*args, renderer, ** options, &options[:wrapper_proc])
|
44
44
|
else
|
45
45
|
renderer.call
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
49
|
def chart_json(series, show_total: false, reverse_sort: false, percentage: false)
|
50
|
-
series =
|
51
|
-
if
|
52
|
-
|
50
|
+
series = Chartnado::Series::Wrap[series]
|
51
|
+
series *= 100.0 if percentage
|
52
|
+
if series.has_separate_named_series?
|
53
|
+
series = series.to_a
|
54
|
+
if series.first.second.respond_to?(:map)
|
53
55
|
totals = Hash.new(0.0)
|
54
|
-
new_series = series.
|
56
|
+
new_series = series.sort_by { |item| item.first.to_s }
|
55
57
|
new_series = new_series.reverse if reverse_sort
|
56
58
|
|
57
59
|
new_series = new_series.map do |name, data|
|
58
60
|
{
|
59
61
|
name: name,
|
60
62
|
data: data.map do |k, v|
|
61
|
-
totals[k
|
62
|
-
[k
|
63
|
+
totals[k] += v if show_total
|
64
|
+
[k, v]
|
63
65
|
end
|
64
66
|
}
|
65
67
|
end
|
66
68
|
|
67
69
|
if show_total
|
68
70
|
[{name: 'Total',
|
69
|
-
data: totals.map {|k,v| [k, 0] },
|
70
|
-
tooltip: totals.map {|k,v| [k, v] }
|
71
|
+
data: totals.map { |k, v| [k, 0] },
|
72
|
+
tooltip: totals.map { |k, v| [k, v] }
|
71
73
|
}] + new_series
|
72
74
|
else
|
73
75
|
new_series
|
74
76
|
end
|
75
77
|
else
|
76
|
-
new_series = series.sort_by { |
|
78
|
+
new_series = series.sort_by { |item| item.first.to_s }
|
77
79
|
new_series = new_series.reverse if reverse_sort
|
78
80
|
|
79
81
|
if show_total
|
@@ -82,32 +84,32 @@ class Chartnado::Renderer
|
|
82
84
|
new_series
|
83
85
|
end
|
84
86
|
end
|
85
|
-
elsif series.
|
86
|
-
if series.first.
|
87
|
+
elsif series.hash?
|
88
|
+
if (key = series.keys.first) and key.is_a?(Array) and key.size == 2
|
87
89
|
totals = Hash.new(0.0)
|
88
|
-
new_series = series.sort_by { |
|
90
|
+
new_series = series.group_by { |k, v| k[0] }.sort_by { |k| k.to_s }
|
89
91
|
new_series = new_series.reverse if reverse_sort
|
90
92
|
|
91
93
|
new_series = new_series.map do |name, data|
|
92
94
|
{
|
93
95
|
name: name,
|
94
96
|
data: data.map do |k, v|
|
95
|
-
totals[k] += v if show_total
|
96
|
-
[k, v]
|
97
|
+
totals[k[1]] += v if show_total
|
98
|
+
[k[1], v]
|
97
99
|
end
|
98
100
|
}
|
99
101
|
end
|
100
102
|
|
101
103
|
if show_total
|
102
104
|
[{name: 'Total',
|
103
|
-
data: totals.map {|k,v| [k, 0] },
|
104
|
-
tooltip: totals.map {|k,v| [k, v] }
|
105
|
+
data: totals.map { |k, v| [k, 0] },
|
106
|
+
tooltip: totals.map { |k, v| [k, v] }
|
105
107
|
}] + new_series
|
106
108
|
else
|
107
109
|
new_series
|
108
110
|
end
|
109
111
|
else
|
110
|
-
new_series = series.sort_by { |
|
112
|
+
new_series = series.sort_by { |key| key.to_s }
|
111
113
|
new_series = new_series.reverse if reverse_sort
|
112
114
|
|
113
115
|
if show_total
|
@@ -116,12 +118,10 @@ class Chartnado::Renderer
|
|
116
118
|
new_series
|
117
119
|
end
|
118
120
|
end
|
121
|
+
elsif series.respond_to?(:map)
|
122
|
+
series
|
119
123
|
else
|
120
|
-
|
121
|
-
series
|
122
|
-
else
|
123
|
-
[['Total', series]]
|
124
|
-
end
|
124
|
+
[['Total', series]]
|
125
125
|
end
|
126
126
|
end
|
127
127
|
end
|
data/lib/chartnado/series.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
require 'active_support/dependencies/autoload'
|
2
|
+
require 'active_support/deprecation'
|
1
3
|
require 'active_support/core_ext'
|
4
|
+
require 'chartnado/series/wrap'
|
2
5
|
|
3
6
|
module Chartnado
|
4
7
|
module Series
|
@@ -9,35 +12,7 @@ module Chartnado
|
|
9
12
|
#
|
10
13
|
# @return [Series/Multiple-Series]
|
11
14
|
def series_product(val, series, precision: 2)
|
12
|
-
|
13
|
-
return series_product(series, val)
|
14
|
-
end
|
15
|
-
|
16
|
-
return with_precision(precision, val.to_f * series.to_f) unless series.respond_to?(:length)
|
17
|
-
return series unless series.length > 0
|
18
|
-
|
19
|
-
if is_an_array_of_named_series?(series) || series.is_a?(Array) && series.first.is_a?(Array)
|
20
|
-
series.map { |(name, data)| [name, series_product(val, data)] }
|
21
|
-
elsif series.is_a?(Hash)
|
22
|
-
series.to_a.reduce({}) do |hash, (key, value)|
|
23
|
-
if val.is_a?(Hash)
|
24
|
-
if key.is_a?(Array)
|
25
|
-
scalar = val[key.second]
|
26
|
-
else
|
27
|
-
scalar = val[key]
|
28
|
-
end
|
29
|
-
else
|
30
|
-
scalar = val
|
31
|
-
end
|
32
|
-
scalar ||= 0
|
33
|
-
hash[key] = scalar * value
|
34
|
-
hash
|
35
|
-
end
|
36
|
-
else
|
37
|
-
series.map do |value|
|
38
|
-
val * value
|
39
|
-
end
|
40
|
-
end
|
15
|
+
Wrap[series].times(val, precision: precision)
|
41
16
|
end
|
42
17
|
|
43
18
|
# @api public
|
@@ -46,46 +21,8 @@ module Chartnado
|
|
46
21
|
#
|
47
22
|
# @return [Series/Multiple-Series]
|
48
23
|
def series_ratio(top_series, bottom_series, multiplier: 1.0, precision: 2)
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
if has_multiple_series?(top_series) && !has_multiple_series?(bottom_series)
|
53
|
-
top_series_by_name = data_by_name(top_series)
|
54
|
-
if is_an_array_of_named_series?(top_series)
|
55
|
-
top_series_by_name.map do |name, top_values|
|
56
|
-
[
|
57
|
-
name,
|
58
|
-
series_ratio(top_values, bottom_series, multiplier: multiplier, precision: precision)
|
59
|
-
]
|
60
|
-
end
|
61
|
-
else
|
62
|
-
bottom_series.reduce({}) do |hash, (key, value)|
|
63
|
-
top_series_by_name.keys.each do |name|
|
64
|
-
top_key = [name, *key]
|
65
|
-
top_value = top_series_by_name[name][top_key]
|
66
|
-
if top_value
|
67
|
-
hash[top_key] = series_ratio(top_value, value, multiplier: multiplier, precision: precision)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
hash
|
71
|
-
end
|
72
|
-
end
|
73
|
-
elsif is_an_array_of_named_series?(bottom_series)
|
74
|
-
top_series_by_name = data_by_name(top_series)
|
75
|
-
bottom_series.map do |(name, data)|
|
76
|
-
[
|
77
|
-
name,
|
78
|
-
series_ratio(top_series_by_name[name], data, multiplier: multiplier, precision: precision)
|
79
|
-
]
|
80
|
-
end
|
81
|
-
elsif bottom_series.respond_to?(:reduce)
|
82
|
-
bottom_series.reduce({}) do |hash, (key, value)|
|
83
|
-
hash[key] = series_ratio(top_series[key] || 0, value, multiplier: multiplier, precision: precision)
|
84
|
-
hash
|
85
|
-
end
|
86
|
-
else
|
87
|
-
with_precision(precision, top_series.to_f * multiplier.to_f / bottom_series.to_f)
|
88
|
-
end
|
24
|
+
Wrap[top_series].
|
25
|
+
over(bottom_series, multiplier: multiplier, precision: precision)
|
89
26
|
end
|
90
27
|
|
91
28
|
# @api public
|
@@ -98,34 +35,7 @@ module Chartnado
|
|
98
35
|
#
|
99
36
|
# @return [Series/Multiple-Series/Scalar]
|
100
37
|
def series_sum(*series, scalar_sum: 0.0)
|
101
|
-
|
102
|
-
|
103
|
-
(series, scalars) = series.partition { |s| s.respond_to?(:map) }
|
104
|
-
scalar_sum += scalars.reduce(:+) || 0.0
|
105
|
-
|
106
|
-
if series.first.is_a?(Hash)
|
107
|
-
keys = series.map(&:keys).flatten(1).uniq
|
108
|
-
keys.reduce({}) do |hash, key|
|
109
|
-
hash[key] = (series.map { |s| s[key] }.compact.reduce(:+) || 0) + scalar_sum
|
110
|
-
hash
|
111
|
-
end
|
112
|
-
elsif is_an_array_of_named_series?(series.first)
|
113
|
-
series.flatten(1).group_by(&:first).map do |name, values|
|
114
|
-
data = values.map(&:second).reduce(Hash.new(scalar_sum)) do |hash, values|
|
115
|
-
values.each do |key, value|
|
116
|
-
hash[key] += value
|
117
|
-
end
|
118
|
-
hash
|
119
|
-
end
|
120
|
-
[
|
121
|
-
name, data
|
122
|
-
]
|
123
|
-
end
|
124
|
-
elsif series.first.is_a?(Array)
|
125
|
-
series.map { |s| s.reduce(:+) + scalar_sum }
|
126
|
-
else
|
127
|
-
scalar_sum
|
128
|
-
end
|
38
|
+
Wrap[series.shift].add(*series, scalar_sum: scalar_sum)
|
129
39
|
end
|
130
40
|
|
131
41
|
# @api public
|
@@ -139,48 +49,5 @@ module Chartnado
|
|
139
49
|
len = sorted.length
|
140
50
|
(sorted[(len - 1) / 2] + sorted[len / 2]) / 2.0
|
141
51
|
end
|
142
|
-
|
143
|
-
private
|
144
|
-
|
145
|
-
def data_by_name(series)
|
146
|
-
if is_an_array_of_named_series?(series)
|
147
|
-
series.reduce({}) do |hash, value|
|
148
|
-
hash[value.first] = value.second
|
149
|
-
hash
|
150
|
-
end
|
151
|
-
else
|
152
|
-
series.reduce({}) do |hash, (key, value)|
|
153
|
-
new_key = Array.wrap(key.first).first
|
154
|
-
hash[new_key] = {key => value }
|
155
|
-
hash
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
def series_names(series)
|
161
|
-
series.map { |key| key.first }.uniq
|
162
|
-
end
|
163
|
-
|
164
|
-
def has_multiple_series?(series)
|
165
|
-
is_an_array_of_named_series?(series) || series.is_a?(Hash) && series.first && series.first[0].is_a?(Array) && series.first[0].length > 1
|
166
|
-
end
|
167
|
-
|
168
|
-
def is_an_array_of_named_series?(series)
|
169
|
-
series.is_a?(Array) && series.first.second.is_a?(Hash)
|
170
|
-
end
|
171
|
-
|
172
|
-
def dimensions(series)
|
173
|
-
return 1 unless series.respond_to?(:length)
|
174
|
-
if series.first && series.first.is_a?(Array)
|
175
|
-
3
|
176
|
-
else
|
177
|
-
2
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def with_precision(precision, value)
|
182
|
-
value = value.round(precision) if precision
|
183
|
-
value
|
184
|
-
end
|
185
52
|
end
|
186
53
|
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
module Chartnado
|
2
|
+
module Series
|
3
|
+
class Wrap < SimpleDelegator
|
4
|
+
def self.[](series)
|
5
|
+
series.class == self ? series : new(series)
|
6
|
+
end
|
7
|
+
|
8
|
+
def *(val)
|
9
|
+
times(val, precision: nil)
|
10
|
+
end
|
11
|
+
|
12
|
+
def times(factor, precision: 2)
|
13
|
+
factor = wrap(factor)
|
14
|
+
|
15
|
+
return factor.times(self, precision: precision) if factor.dimensions > dimensions
|
16
|
+
return with_precision(precision, factor.to_f * to_f) unless dimensions > 1
|
17
|
+
return self unless length > 0
|
18
|
+
|
19
|
+
if has_separate_named_series? || array? && first.is_a?(Array)
|
20
|
+
result = map { |(name, data)| [name, wrap(data) * factor] }
|
21
|
+
elsif hash?
|
22
|
+
result = to_a.reduce({}) do |hash, (key, value)|
|
23
|
+
if factor.hash?
|
24
|
+
if key.is_a?(Array)
|
25
|
+
scalar = factor[key.second]
|
26
|
+
else
|
27
|
+
scalar = factor[key]
|
28
|
+
end
|
29
|
+
else
|
30
|
+
scalar = factor
|
31
|
+
end
|
32
|
+
scalar ||= 0
|
33
|
+
hash[key] = scalar * value
|
34
|
+
hash
|
35
|
+
end
|
36
|
+
else
|
37
|
+
result = map do |value|
|
38
|
+
factor * value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
wrap(result)
|
43
|
+
end
|
44
|
+
|
45
|
+
def add(*series, scalar_sum: 0.0)
|
46
|
+
(series, scalars) = [__getobj__, *series].partition { |s| s.respond_to?(:map) }
|
47
|
+
scalar_sum += scalars.reduce(:+) || 0.0
|
48
|
+
return wrap(scalar_sum) unless series.present?
|
49
|
+
|
50
|
+
if wrap(series.first).has_separate_named_series?
|
51
|
+
result = series.map(&:to_a).flatten(1).group_by(&:first).map do |name, values|
|
52
|
+
data = values.map(&:second).reduce(Hash.new(scalar_sum)) do |hash, values|
|
53
|
+
values.each do |key, value|
|
54
|
+
hash[key] += value
|
55
|
+
end
|
56
|
+
hash
|
57
|
+
end
|
58
|
+
[
|
59
|
+
name, data
|
60
|
+
]
|
61
|
+
end
|
62
|
+
elsif series.first.is_a?(Hash)
|
63
|
+
keys = series.flat_map(&:keys).uniq
|
64
|
+
result = keys.reduce({}) do |hash, key|
|
65
|
+
hash[key] = (series.map { |s| s[key] }.compact.reduce(:+) || 0) + scalar_sum
|
66
|
+
hash
|
67
|
+
end
|
68
|
+
elsif series.first.is_a?(Array)
|
69
|
+
result = series.map { |s| s.reduce(:+) + scalar_sum }
|
70
|
+
else
|
71
|
+
result = scalar_sum
|
72
|
+
end
|
73
|
+
|
74
|
+
wrap(result)
|
75
|
+
end
|
76
|
+
|
77
|
+
def over(bottom, multiplier: 1.0, precision: 2)
|
78
|
+
bottom = wrap(bottom)
|
79
|
+
return times(1.0 * multiplier / bottom, precision: precision) if bottom.dimensions == 1
|
80
|
+
|
81
|
+
if dimensions > bottom.dimensions
|
82
|
+
top_series_by_name = data_by_name
|
83
|
+
if has_separate_named_series?
|
84
|
+
data_by_name.map do |name, top_values|
|
85
|
+
[
|
86
|
+
name,
|
87
|
+
wrap(top_values).
|
88
|
+
over(bottom, multiplier: multiplier, precision: precision)
|
89
|
+
]
|
90
|
+
end
|
91
|
+
else
|
92
|
+
bottom.reduce({}) do |hash, (key, value)|
|
93
|
+
top_series_by_name.keys.each do |name|
|
94
|
+
top_key = [name, *Array.wrap(key)]
|
95
|
+
top_value = top_series_by_name[name][top_key]
|
96
|
+
if top_value
|
97
|
+
hash[top_key] = wrap(top_value).
|
98
|
+
over(value, multiplier: multiplier, precision: precision)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
hash
|
102
|
+
end
|
103
|
+
end
|
104
|
+
elsif array_of_named_series?
|
105
|
+
top_series_by_name = data_by_name
|
106
|
+
bottom.map do |(name, data)|
|
107
|
+
[
|
108
|
+
name,
|
109
|
+
wrap(top_series_by_name[name]).
|
110
|
+
over(data, multiplier: multiplier, precision: precision)
|
111
|
+
]
|
112
|
+
end
|
113
|
+
elsif bottom.respond_to?(:reduce)
|
114
|
+
bottom.reduce({}) do |hash, (key, value)|
|
115
|
+
hash[key] = wrap(self[key] || 0).
|
116
|
+
over(value, multiplier: multiplier, precision: precision)
|
117
|
+
hash
|
118
|
+
end
|
119
|
+
else
|
120
|
+
with_precision(precision, to_f * multiplier.to_f / bottom.to_f)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def has_multiple_series?
|
125
|
+
array_of_named_series? || is_a?(Hash) && begin
|
126
|
+
first_series = series.first
|
127
|
+
first_series[0].is_a?(Array) && first_series[0].length > 1 || first_series[1].respond_to?(:length)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def hash?
|
132
|
+
__getobj__.is_a?(Hash)
|
133
|
+
end
|
134
|
+
|
135
|
+
def array?
|
136
|
+
__getobj__.is_a?(Array)
|
137
|
+
end
|
138
|
+
|
139
|
+
def array_of_named_series?
|
140
|
+
array? && first.second.is_a?(Hash)
|
141
|
+
end
|
142
|
+
|
143
|
+
def hash_of_named_series?
|
144
|
+
hash? && values.first && values.first.is_a?(Hash)
|
145
|
+
end
|
146
|
+
|
147
|
+
def dimensions
|
148
|
+
return 1 unless respond_to?(:length)
|
149
|
+
if hash?
|
150
|
+
if keys.first && keys.first.is_a?(Array) || hash_of_named_series?
|
151
|
+
3
|
152
|
+
else
|
153
|
+
2
|
154
|
+
end
|
155
|
+
else
|
156
|
+
if first && first.is_a?(Array)
|
157
|
+
3
|
158
|
+
else
|
159
|
+
2
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def has_separate_named_series?
|
165
|
+
hash_of_named_series? || array_of_named_series?
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
def data_by_name
|
171
|
+
return self if hash_of_named_series?
|
172
|
+
result = if array_of_named_series?
|
173
|
+
reduce({}) do |hash, (name, values)|
|
174
|
+
hash[name] = values
|
175
|
+
hash
|
176
|
+
end
|
177
|
+
else
|
178
|
+
hash = Hash.new { |hash, key| hash[key] = {} }
|
179
|
+
x = reduce(hash) do |hash, (key, value)|
|
180
|
+
p key.first
|
181
|
+
new_key = Array.wrap(key.first).first
|
182
|
+
hash[new_key][key] = value
|
183
|
+
hash
|
184
|
+
end
|
185
|
+
p x
|
186
|
+
x
|
187
|
+
end
|
188
|
+
wrap(result)
|
189
|
+
end
|
190
|
+
|
191
|
+
def with_precision(precision, value)
|
192
|
+
value = value.round(precision) if precision
|
193
|
+
value
|
194
|
+
end
|
195
|
+
|
196
|
+
def wrap(val)
|
197
|
+
self.class[val]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
data/lib/chartnado/version.rb
CHANGED
data/spec/rails_helper.rb
CHANGED
data/spec/renderer_spec.rb
CHANGED
@@ -17,6 +17,12 @@ describe Chartnado::Renderer do
|
|
17
17
|
{name: :a, data: [[1, 10]]},
|
18
18
|
{name: :b, data: [[1, 20]]}]
|
19
19
|
end
|
20
|
+
it "can add multiply by 100 to create a percentage" do
|
21
|
+
expect(chart_json({[:a, 1] => 0.1, [:b, 1] => 0.2}, percentage: true, show_total: true)).
|
22
|
+
to eq [{name: 'Total', data: [[1, 0]], tooltip: [[1, 30.0]]},
|
23
|
+
{name: :a, data: [[1, 10.0]]},
|
24
|
+
{name: :b, data: [[1, 20.0]]}]
|
25
|
+
end
|
20
26
|
describe "with multiple scalar series" do
|
21
27
|
it "can handle scalars" do
|
22
28
|
expect(chart_json({:a => 10, :b => 20})).
|
@@ -40,6 +46,18 @@ describe Chartnado::Renderer do
|
|
40
46
|
{name: :b, data: [[1, 20]]}]
|
41
47
|
end
|
42
48
|
end
|
49
|
+
describe "for multiple series organized as a hash" do
|
50
|
+
it "can generate chartkick compatible series" do
|
51
|
+
expect(chart_json({:a => {1 => 10}, :b => {1 => 20}})).
|
52
|
+
to eq [{name: :a, data: [[1, 10]]}, {name: :b, data: [[1,20]]}]
|
53
|
+
end
|
54
|
+
it "can add totals" do
|
55
|
+
expect(chart_json({:a => {1 => 10}, :b => {1 => 20}}, show_total: true)).
|
56
|
+
to eq [{name: 'Total', data: [[1, 0]], tooltip: [[1, 30.0]]},
|
57
|
+
{name: :a, data: [[1, 10]]},
|
58
|
+
{name: :b, data: [[1, 20]]}]
|
59
|
+
end
|
60
|
+
end
|
43
61
|
describe "for data that is just a scalar" do
|
44
62
|
it "shows the scalar as the total" do
|
45
63
|
expect(chart_json(10)).
|
data/spec/series_spec.rb
CHANGED
@@ -66,6 +66,16 @@ describe Chartnado::Series do
|
|
66
66
|
expect(series_sum(2,[[:a, {0 => 3}]])).to eq ([[:a, {0 => 5}]])
|
67
67
|
end
|
68
68
|
end
|
69
|
+
describe "adding a scalar to an hash of named series" do
|
70
|
+
it "returns each item of the array with a scalar added" do
|
71
|
+
expect(series_sum(2,{'a' => {0 => 3}})).to eq ([['a', {0 => 5}]])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
describe "adding a scalar to a hash with 2 dimensional keys" do
|
75
|
+
it "returns each item of the array with a scalar added" do
|
76
|
+
expect(series_sum({['a', 0] => 3}, 1)).to eq ({['a', 0] => 4})
|
77
|
+
end
|
78
|
+
end
|
69
79
|
describe "adding two hashes" do
|
70
80
|
it "returns each item of the array with a scalar added" do
|
71
81
|
expect(series_sum({0 => 1},{0 => 2})).to eq ({0 => 3})
|
@@ -76,6 +86,11 @@ describe Chartnado::Series do
|
|
76
86
|
expect(series_sum({0 => 1},{0 => 2}, 5)).to eq ({0 => 8})
|
77
87
|
end
|
78
88
|
end
|
89
|
+
describe "adding nothing but a scalar sum" do
|
90
|
+
it "returns the scalar sum" do
|
91
|
+
expect(series_sum(scalar_sum: 2)).to eq 2
|
92
|
+
end
|
93
|
+
end
|
79
94
|
end
|
80
95
|
|
81
96
|
describe "#series_ratio" do
|
@@ -103,8 +118,16 @@ describe Chartnado::Series do
|
|
103
118
|
end
|
104
119
|
describe "ratio of a named series to a non-named series" do
|
105
120
|
it "returns the ratio" do
|
106
|
-
expect(series_ratio({[:series_a, 0] => 1},
|
107
|
-
{0 => 2})).to eq ({[:series_a, 0] => 0.5})
|
121
|
+
expect(series_ratio({[:series_a, 0] => 1, [:series_a, 1] => 3},
|
122
|
+
{0 => 2, 1 => 4})).to eq ({[:series_a, 0] => 0.5, [:series_a, 1] => 0.75})
|
123
|
+
end
|
124
|
+
describe "when the keys are time values" do
|
125
|
+
let(:t1) { Time.parse('2014-09-15 07:00:00 UTC') }
|
126
|
+
let(:t2) { Time.parse('2014-09-22 07:00:00 UTC') }
|
127
|
+
it "still returns the ratio" do
|
128
|
+
expect(series_ratio({[:series_a, t1] => 1, [:series_a, t2] => 3},
|
129
|
+
{t1 => 2, t2 => 4})).to eq ({[:series_a, t1] => 0.5, [:series_a, t2] => 0.75})
|
130
|
+
end
|
108
131
|
end
|
109
132
|
end
|
110
133
|
describe "ratio of an array of named series to a non-named series" do
|
@@ -113,8 +136,15 @@ describe Chartnado::Series do
|
|
113
136
|
{0 => 2})).to eq [[:series_a, {0 => 0.5}]]
|
114
137
|
end
|
115
138
|
end
|
139
|
+
describe "ratio of a hash of named series to a non-named series" do
|
140
|
+
it "returns the ratio" do
|
141
|
+
expect(series_ratio({:series_a => {0 => 1}},
|
142
|
+
{0 => 2})).to eq [[:series_a, {0 => 0.5}]]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
116
146
|
describe "ratio of a series to a scalar" do
|
117
|
-
|
147
|
+
it "returns the ratio" do
|
118
148
|
expect(series_ratio({0 => 1}, 2)).to eq ({0 => 0.5})
|
119
149
|
end
|
120
150
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chartnado
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew S. Brown
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
47
|
+
version: '1.3'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
54
|
+
version: '1.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: railties
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '3.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: responders
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: bundler
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -220,6 +234,7 @@ files:
|
|
220
234
|
- lib/chartnado/helpers/series_helper.rb
|
221
235
|
- lib/chartnado/renderer.rb
|
222
236
|
- lib/chartnado/series.rb
|
237
|
+
- lib/chartnado/series/wrap.rb
|
223
238
|
- lib/chartnado/version.rb
|
224
239
|
- spec/controllers/controller_spec.rb
|
225
240
|
- spec/dsl_spec.rb
|
@@ -249,7 +264,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
249
264
|
version: '0'
|
250
265
|
requirements: []
|
251
266
|
rubyforge_project:
|
252
|
-
rubygems_version: 2.
|
267
|
+
rubygems_version: 2.4.6
|
253
268
|
signing_key:
|
254
269
|
specification_version: 4
|
255
270
|
summary: Chartkick charts with extras
|