volt-highcharts 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Join the chat at https://gitter.im/balmoral/volt-highcharts](https://badges.gitter.im/Join%20Chat.svg)](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
|