volt-highcharts 0.1.2 → 0.1.3
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 +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +76 -45
- data/app/highcharts/config/dependencies.rb +3 -1
- data/app/highcharts/controllers/main_controller.rb +123 -67
- data/lib/volt/highcharts/version.rb +1 -1
- data/push +1 -1
- data/volt-highcharts.gemspec +3 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23e8b6ee599e35ecfbadc672faf9bb3d17e9bdc6
|
4
|
+
data.tar.gz: 9158848fba5dc0be73da1e2534a08654967291e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3ce02ec03e6b53432af094df4b4b690e8a1f9c0b1941e1ff53368554316818733822fdddb7f4567f12a1f0d33d678ef7b067237f1618ddc6364d79ad20c728d
|
7
|
+
data.tar.gz: 694ca655b9a00c205474194e69ba7847a90160bcc79c5f4b20cdbe5f7a41919db73a2ff37f25c731ea5a0dcbc2e109d325d9d7df3ef84b1baae3fb144bbb6e0c
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,20 @@
|
|
1
1
|
# Volt::Highcharts
|
2
2
|
|
3
|
+
[](https://gitter.im/balmoral/volt-highcharts?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
4
|
+
|
3
5
|
A Volt component wrapping the Highcharts javascript charting tool.
|
4
6
|
|
5
7
|
It depends on opal-highcharts, a gem which wraps most Highcharts and Highstock functionality in a client-side Ruby API.
|
6
8
|
|
7
9
|
Highcharts is free for non-commercial use.
|
8
10
|
|
9
|
-
|
10
|
-
http://
|
11
|
-
|
11
|
+
See
|
12
|
+
- http://www.highcharts.com
|
13
|
+
- http://github.com/balmoral/volt-highcharts-app
|
14
|
+
- http://github.com/balmoral/volt-highcharts
|
15
|
+
- http://github.com/balmoral/opal-highcharts
|
16
|
+
- https://rubygems.org/gems/volt-highcharts
|
17
|
+
- https://rubygems.org/gems/opal-highcharts
|
12
18
|
|
13
19
|
## Installation
|
14
20
|
|
@@ -42,59 +48,81 @@ Pass a Ruby hash containing chart options in the appropriate view html file:
|
|
42
48
|
|
43
49
|
where `chart_options` is a Volt::Model or Hash provided by your controller or model.
|
44
50
|
|
45
|
-
Reactivity is now supported.
|
51
|
+
**Reactivity** is now supported.
|
46
52
|
|
47
53
|
To implement a reactive chart, the options provided on chart creation should be wrapped in a Volt::Model.
|
48
54
|
|
49
|
-
NB reactivity is currently limited to chart titles, number of series, and individual series options and data. More coming soon.
|
55
|
+
**NB** reactivity is currently limited to chart titles, number of series, and individual series options and data. More coming soon.
|
50
56
|
|
51
57
|
Documentation for Highcharts options can be found at: http://api.highcharts.com/highcharts#chart.
|
52
58
|
|
53
59
|
For convenience, the last chart added can be accessed as ```page._chart```.
|
54
60
|
The object returned is a Highcharts::Chart, which can be used to directly query and manipulate the chart (see opal-highcharts).
|
55
61
|
|
56
|
-
|
62
|
+
For example, for your controller:
|
57
63
|
|
58
|
-
For example:
|
59
64
|
```
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
class ChartController < Volt::ModelController
|
66
|
+
...
|
67
|
+
def chart_options
|
68
|
+
Volt::Model.new( {
|
69
|
+
|
70
|
+
# identity the chart in volt
|
71
|
+
id: 'fruit',
|
72
|
+
|
73
|
+
# highcharts options
|
74
|
+
chart: {
|
75
|
+
type: 'bar',
|
76
|
+
renderTo: 'fruit_chart'
|
77
|
+
},
|
78
|
+
title: {
|
79
|
+
text: 'Fruit Consumption'
|
80
|
+
},
|
81
|
+
xAxis: {
|
82
|
+
categories: %w(Apples Bananas Oranges)
|
83
|
+
},
|
84
|
+
yAxis: {
|
69
85
|
title: {
|
70
|
-
|
86
|
+
text: 'Fruit eaten'
|
87
|
+
}
|
88
|
+
},
|
89
|
+
series: [
|
90
|
+
{
|
91
|
+
name: 'Jane',
|
92
|
+
data: [1, 0, 4]
|
71
93
|
},
|
72
|
-
|
73
|
-
|
94
|
+
{
|
95
|
+
name: 'John',
|
96
|
+
data: [5, 7, 3]
|
74
97
|
},
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
},
|
85
|
-
{
|
86
|
-
name: 'John',
|
87
|
-
data: [5, 7, 3]
|
88
|
-
},
|
89
|
-
...
|
90
|
-
]
|
91
|
-
} )
|
92
|
-
end
|
98
|
+
...
|
99
|
+
]
|
100
|
+
} )
|
101
|
+
end
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
and your views .html file
|
106
|
+
|
93
107
|
```
|
108
|
+
<:Title>
|
109
|
+
volt-highcharts
|
110
|
+
<:Body>
|
111
|
+
<span id="fruit_chart" >
|
112
|
+
<:highcharts options="{{ chart_options }}" />
|
113
|
+
</span>
|
114
|
+
```
|
115
|
+
|
116
|
+
The html element which contains the chart must have an id which matches the `chart: { renderTo: } ` value.
|
94
117
|
|
95
|
-
|
118
|
+
(At present Volt 0.9.5 does not support view bindings for setting element ids, so string literals are required.)
|
119
|
+
|
120
|
+
To later query or modify multiple chart(s) on a page, a unique `:id` value should be set.
|
121
|
+
|
122
|
+
You can then find the chart in page._charts, the elements of which are Volt::Model's each having an _id and a _chart attribute.
|
123
|
+
|
124
|
+
For example, in your controller you might have a method to return a Highcharts::Chart:
|
96
125
|
|
97
|
-
For example, in your controller you might have a method to return the native chart:
|
98
126
|
```
|
99
127
|
def find_chart(id)
|
100
128
|
# NB use detect, not find
|
@@ -102,20 +130,23 @@ For example, in your controller you might have a method to return the native cha
|
|
102
130
|
e ? e._chart : nil
|
103
131
|
end
|
104
132
|
```
|
133
|
+
|
105
134
|
If you only have one chart on the page use ```page._chart```.
|
106
135
|
|
107
136
|
With opal-highcharts, which completely wraps the Highcharts API in client-side Ruby (and comes bundled with volt-highcharts),
|
108
137
|
you now have simple access to query and modify methods on the chart and all of its elements. No Native wraps or backticks required.
|
138
|
+
However, as reactivity support is improved, there should be less need for direct manipulation of the chart.
|
109
139
|
|
110
|
-
|
140
|
+
### Example
|
111
141
|
|
142
|
+
An example application can be found at https://github.com/balmoral/volt-highcharts-app.
|
143
|
+
|
112
144
|
## To do
|
113
145
|
|
114
|
-
1.
|
115
|
-
2.
|
116
|
-
3.
|
117
|
-
4.
|
118
|
-
5. finer grained reactivity?
|
146
|
+
1. chart option/configuration checks
|
147
|
+
2. improved documentation
|
148
|
+
3. reactivity for more chart options
|
149
|
+
4. Highstock specific support
|
119
150
|
|
120
151
|
## Contributing
|
121
152
|
|
@@ -2,5 +2,7 @@
|
|
2
2
|
|
3
3
|
# highcharts.js is not required if highstock is loaded
|
4
4
|
# javascript_file 'http://code.highcharts.com/highcharts.src.js'
|
5
|
-
javascript_file 'http://code.highcharts.com/
|
5
|
+
javascript_file 'http://code.highcharts.com/highcharts.src.js'
|
6
|
+
javascript_file 'http://code.highcharts.com/highcharts-more.js'
|
7
|
+
javascript_file 'http://code.highcharts.com/modules/exporting.js'
|
6
8
|
# javascript_file 'http://code.highcharts.com/maps/highmaps.src.js'
|
@@ -16,16 +16,16 @@ module Highcharts
|
|
16
16
|
|
17
17
|
def before_index_remove
|
18
18
|
stop_watching
|
19
|
-
|
19
|
+
destroy_chart
|
20
20
|
@chart = nil
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
protected
|
24
24
|
|
25
25
|
def set_model
|
26
26
|
options = attrs.options
|
27
27
|
unless options
|
28
|
-
raise ArgumentError, '
|
28
|
+
raise ArgumentError, 'options attribute must be given for :highcharts component'
|
29
29
|
end
|
30
30
|
# if the options are a Hash then convert to a Volt::Model
|
31
31
|
if options.is_a?(Volt::Model)
|
@@ -36,11 +36,10 @@ module Highcharts
|
|
36
36
|
end
|
37
37
|
# set controller's model to options, which captures its methods for self
|
38
38
|
self.model = options
|
39
|
-
debug __method__, __LINE__, "model._id = #{_id}"
|
40
39
|
end
|
41
40
|
|
42
41
|
# Create the chart and add it to the page._charts.
|
43
|
-
# page._charts
|
42
|
+
# page._charts is an array of Volt::Models each having an _id and a _chart (Highcharts::Chart) attribute.
|
44
43
|
# Also set page._chart to the newly (last) created Highcharts::Chart.
|
45
44
|
# Also set page._char_id to the id of the new (last) chart.
|
46
45
|
def create_chart
|
@@ -52,53 +51,70 @@ module Highcharts
|
|
52
51
|
|
53
52
|
# To be reactive we must watch for model changes
|
54
53
|
def start_watching
|
54
|
+
@in_start = true
|
55
55
|
@watches = []
|
56
|
-
@watch_counts = {}
|
57
56
|
if reactive
|
57
|
+
watch_animation
|
58
58
|
watch_titles
|
59
59
|
watch_series
|
60
60
|
end
|
61
|
+
@in_start = false
|
62
|
+
end
|
63
|
+
|
64
|
+
def watch_animation
|
65
|
+
watch(->{ _animate }) do
|
66
|
+
# debug __method__, __LINE__, "_animate=#{_animate} : refresh_all_series"
|
67
|
+
refresh_all_series
|
68
|
+
end
|
61
69
|
end
|
62
70
|
|
63
71
|
def watch_titles
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end.watch!
|
72
|
+
[->{ _title }, ->{ _subtitle }].each do |computation|
|
73
|
+
watch(computation, descend: true) do
|
74
|
+
chart.set_title(_title.to_h, _subtitle.to_h, true)
|
75
|
+
end
|
76
|
+
end
|
70
77
|
end
|
71
78
|
|
72
79
|
def watch_series
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
end.watch!
|
85
|
-
watches << -> do
|
86
|
-
title = a_series._title
|
87
|
-
log_change "@@@ _series[#{index}]._title changed", title
|
88
|
-
end.watch!
|
89
|
-
watches << -> do
|
90
|
-
setup_dependencies(a_series, nest: true, except: [:title, :data])
|
91
|
-
log_change "@@@ _series[#{index}] something other than _title or _data changed", nil
|
92
|
-
# chart.series[index].update(_series.to_h)
|
93
|
-
end
|
94
|
-
end.watch!
|
95
|
-
end
|
96
|
-
else
|
97
|
-
log_change "@@@ _series.size changed to ", size
|
98
|
-
@series_size = size
|
99
|
-
refresh_all_series
|
80
|
+
# watch_series_size
|
81
|
+
watch_series_data
|
82
|
+
watch_series_visibility
|
83
|
+
watch_series_other
|
84
|
+
end
|
85
|
+
|
86
|
+
def watch_series_other
|
87
|
+
_series.each_with_index do |a_series, i|
|
88
|
+
watch ->{ a_series }, descend: true, tag: i, except: [:_data, :visible] do |tag, val|
|
89
|
+
# debug __method__, __LINE__, "chart.series[#{tag}].update(#{val.to_h}, true)"
|
90
|
+
chart.series[tag].update(val.to_h, true)
|
100
91
|
end
|
101
|
-
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def watch_series_data
|
96
|
+
_series.each_with_index do |a_series, i|
|
97
|
+
watch ->{ a_series._data }, tag: i do |tag, val|
|
98
|
+
# debug __method__, __LINE__, "chart.series[#{tag}].set_data(#{val.to_a}, true, #{_animate})"
|
99
|
+
chart.series[tag].set_data(val.to_a, true, _animate)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def watch_series_visibility
|
105
|
+
_series.each_with_index do |a_series, i|
|
106
|
+
watch ->{ a_series._visible }, tag: i do |tag, val|
|
107
|
+
# debug __method__, __LINE__, "chart.series[#{tag}].set_visible(#{val}, true)"
|
108
|
+
chart.series[tag].set_data(val.to_a, true)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def watch_series_size
|
114
|
+
watch_attributes("_series", _series, recurse: false) do |key, value|
|
115
|
+
# debug __method__, __LINE__, "_series.#{key} changed"
|
116
|
+
refresh_all_series
|
117
|
+
end
|
102
118
|
end
|
103
119
|
|
104
120
|
# Do complete refresh of all series:
|
@@ -106,60 +122,100 @@ module Highcharts
|
|
106
122
|
# 2. add all series in model to chart with no redraw
|
107
123
|
# 3. redraw chart
|
108
124
|
def refresh_all_series
|
125
|
+
stop_watching
|
109
126
|
until chart.series.empty? do
|
110
|
-
debug __method__, __LINE__, "chart.series[#{chart.series.size-1}].remove"
|
111
127
|
chart.series.last.remove(false)
|
112
128
|
end
|
113
|
-
_series.
|
114
|
-
debug __method__, __LINE__, "chart.add_series ##{index}"
|
129
|
+
_series.each do |a_series|
|
115
130
|
chart.add_series(a_series.to_h, false)
|
116
131
|
end
|
117
|
-
debug __method__, __LINE__, "chart.redraw"
|
118
132
|
chart.redraw
|
119
|
-
|
120
|
-
|
121
|
-
# Force computation dependencies for attributes of a model
|
122
|
-
# TODO: must be better or built-in way ??
|
123
|
-
def setup_dependencies(model, nest: true, except: [])
|
124
|
-
model.attributes.each { |key, val|
|
125
|
-
unless except.include?(key)
|
126
|
-
debug __method__, __LINE__, "#{model}.send(#{key})"
|
127
|
-
model.send :"_#{key}"
|
128
|
-
end
|
129
|
-
if nest && val.is_a?(Volt::Model)
|
130
|
-
setup_dependencies(val, nest: true, except: except)
|
131
|
-
end
|
132
|
-
}
|
133
|
+
start_watching
|
133
134
|
end
|
134
135
|
|
135
136
|
def stop_watching
|
136
137
|
@watches.each {|w| w.stop}
|
137
|
-
@watches =
|
138
|
+
@watches = nil
|
138
139
|
end
|
139
140
|
|
140
|
-
def
|
141
|
-
debug __method__, _LINE__, Time.now.to_s
|
141
|
+
def destroy_chart
|
142
142
|
# clear all references to this chart
|
143
143
|
i = page._charts.find_index { |e| e._id == _id }
|
144
144
|
if i
|
145
145
|
deleted = page._charts.delete_at(i)
|
146
|
-
|
147
|
-
|
146
|
+
begin
|
147
|
+
deleted._chart.destroy # TODO: sometimes this fails - seems ok since volt-0.9.5.pre4 after 2015-08-10
|
148
|
+
rescue Exception => x
|
149
|
+
debug __method__, __LINE__, "chart._destroy failed: #{x}"
|
150
|
+
end
|
148
151
|
deleted._chart = nil
|
149
152
|
end
|
150
153
|
if page._chart_id == _id
|
151
154
|
last = page._charts.last
|
155
|
+
# last = page._charts.empty? ? nil : page._charts.last # bug in ReactiveArray
|
152
156
|
page._chart_id = last ? last._id : nil
|
153
157
|
page._chart = last ? last._chart : nil
|
154
158
|
end
|
155
159
|
end
|
156
160
|
|
157
|
-
def
|
158
|
-
|
161
|
+
def watch(computation, descend: false, tag: nil, except: nil, &block)
|
162
|
+
@watches ||= []
|
163
|
+
@watches << -> do
|
164
|
+
val = compute(computation, descend, except)
|
165
|
+
unless @in_start
|
166
|
+
if block.arity == 0
|
167
|
+
block.call
|
168
|
+
elsif block.arity == 1
|
169
|
+
block.call(tag ? tag : val)
|
170
|
+
elsif block.arity == 2
|
171
|
+
block.call(tag, val)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end.watch!
|
175
|
+
end
|
176
|
+
|
177
|
+
def compute(computation, descend, except)
|
178
|
+
v = computation.call
|
179
|
+
descend(v, except) if descend
|
180
|
+
v
|
181
|
+
end
|
182
|
+
|
183
|
+
def descend(o, except)
|
184
|
+
if o.is_a?(Volt::Model)
|
185
|
+
descend_model(o, except)
|
186
|
+
elsif o.is_a?(Volt::ReactiveArray)
|
187
|
+
descend_array(o, except)
|
188
|
+
elsif o.is_a?(Volt::ReactiveHash)
|
189
|
+
descend_hash(o, except)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def descend_array(array, except)
|
194
|
+
# this way to force dependency
|
195
|
+
array.size.times do |i|
|
196
|
+
descend(array[i], except)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def descend_hash(hash, except)
|
201
|
+
hash.each_key do |k|
|
202
|
+
# this way to force dependency
|
203
|
+
descend(hash[k], except)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def descend_model(model, except)
|
208
|
+
model.attributes.each_key do |attr|
|
209
|
+
# this way to force dependency
|
210
|
+
_attr = :"_#{attr}"
|
211
|
+
unless except && except.include?(_attr)
|
212
|
+
descend(model.send(_attr), except)
|
213
|
+
end
|
214
|
+
end
|
159
215
|
end
|
160
216
|
|
161
|
-
def
|
162
|
-
Volt.logger.debug "#{
|
217
|
+
def debug(method, line, s = nil)
|
218
|
+
Volt.logger.debug "#{self.class.name}##{method}[#{line}] : #{s}"
|
163
219
|
end
|
164
220
|
|
165
221
|
end
|
data/push
CHANGED
data/volt-highcharts.gemspec
CHANGED
@@ -8,7 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Volt::Highcharts::VERSION
|
9
9
|
spec.authors = ["Colin Gunn"]
|
10
10
|
spec.email = ["colgunn@icloud.com"]
|
11
|
-
spec.summary = %q{Volt component wrapping Highcharts
|
11
|
+
spec.summary = %q{Volt component providing reactive Ruby wrapping of Highcharts + Highstock javascript library.}
|
12
|
+
spec.description = %q{Volt component providing reactive Ruby wrapping of Highcharts + Highstock javascript library.}
|
12
13
|
spec.homepage = "https://github.com/balmoral/volt-highcharts"
|
13
14
|
spec.license = "MIT"
|
14
15
|
|
@@ -17,6 +18,7 @@ Gem::Specification.new do |spec|
|
|
17
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
19
|
spec.require_paths = ["lib"]
|
19
20
|
|
21
|
+
spec.required_ruby_version = '>= 2.1'
|
20
22
|
spec.add_runtime_dependency 'opal-highcharts', '~> 0.1.0'
|
21
23
|
|
22
24
|
# spec.add_development_dependency "volt", "~> 0.9.5.pre3"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: volt-highcharts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Colin Gunn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opal-highcharts
|
@@ -24,7 +24,8 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.1.0
|
27
|
-
description:
|
27
|
+
description: Volt component providing reactive Ruby wrapping of Highcharts + Highstock
|
28
|
+
javascript library.
|
28
29
|
email:
|
29
30
|
- colgunn@icloud.com
|
30
31
|
executables: []
|
@@ -60,7 +61,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
61
|
requirements:
|
61
62
|
- - ">="
|
62
63
|
- !ruby/object:Gem::Version
|
63
|
-
version: '
|
64
|
+
version: '2.1'
|
64
65
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
66
|
requirements:
|
66
67
|
- - ">="
|
@@ -71,7 +72,8 @@ rubyforge_project:
|
|
71
72
|
rubygems_version: 2.4.6
|
72
73
|
signing_key:
|
73
74
|
specification_version: 4
|
74
|
-
summary: Volt component wrapping Highcharts
|
75
|
+
summary: Volt component providing reactive Ruby wrapping of Highcharts + Highstock
|
76
|
+
javascript library.
|
75
77
|
test_files:
|
76
78
|
- spec/spec_helper.rb
|
77
79
|
- spec/volt/highcharts_spec.rb
|