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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 57bc486e2e61cd1b390da2fb6cba28705479531f
4
- data.tar.gz: 2f1a36ef4888500b8933201f7cd533a0a774a20d
3
+ metadata.gz: 23e8b6ee599e35ecfbadc672faf9bb3d17e9bdc6
4
+ data.tar.gz: 9158848fba5dc0be73da1e2534a08654967291e8
5
5
  SHA512:
6
- metadata.gz: f45e348902daac3c71b825bcddbf8c7797ee3aed492a0718684062fd4ff56237c6fe4e62f9cf02a74bf0744caf9c39961c173d00c6f26f6034670d93a1ea9e25
7
- data.tar.gz: 13aee26ca775e5e626688967d2365ce9314197f15ac0dc1cb49c27f2fffeab6d0bc7f5ec9e9fcd31a09605070690b9befd518a6c2514b785ab6a872829a8e04c
6
+ metadata.gz: d3ce02ec03e6b53432af094df4b4b690e8a1f9c0b1941e1ff53368554316818733822fdddb7f4567f12a1f0d33d678ef7b067237f1618ddc6364d79ad20c728d
7
+ data.tar.gz: 694ca655b9a00c205474194e69ba7847a90160bcc79c5f4b20cdbe5f7a41919db73a2ff37f25c731ea5a0dcbc2e109d325d9d7df3ef84b1baae3fb144bbb6e0c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.1.3
4
+
5
+ - added global animate option
6
+ - improvements to reactivity logic
7
+
8
+ ## 0.1.2
9
+
10
+ - various tweaks
11
+
3
12
  ## 0.1.1
4
13
 
5
14
  - now uses opal-highcharts gem
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
- http://www.highcharts.com/products/highcharts
10
- http://github.com/balmoral/volt-highcharts
11
- https://rubygems.org/gems/volt-highcharts
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
- To query or modify multiple chart(s) on the page a unique :id should be set in each chart's options.
62
+ For example, for your controller:
57
63
 
58
- For example:
59
64
  ```
60
- def fruit_chart_options
61
- Volt::Model.new( {
62
- # to identity the chart in volt
63
- id: 'fruit_chart',
64
-
65
- # highcharts options
66
- chart: {
67
- type: 'bar'
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
- text: 'Fruit Consumption'
86
+ text: 'Fruit eaten'
87
+ }
88
+ },
89
+ series: [
90
+ {
91
+ name: 'Jane',
92
+ data: [1, 0, 4]
71
93
  },
72
- xAxis: {
73
- categories: %w(Apples Bananas Oranges)
94
+ {
95
+ name: 'John',
96
+ data: [5, 7, 3]
74
97
  },
75
- yAxis: {
76
- title: {
77
- text: 'Fruit eaten'
78
- }
79
- },
80
- series: [
81
- {
82
- name: 'Jane',
83
- data: [1, 0, 4]
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
- You can later find the chart in page._charts, the elements of which are Volt::Model's each having an _id and a _chart attribute.
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
- As reactivity support is improved, there should be less need for direct manipulation of the chart.
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. remove debug traces
115
- 2. chart option/configuration checks
116
- 3. improved documentation
117
- 4. improved error handling
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/stock/highstock.src.js'
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
- update_page
19
+ destroy_chart
20
20
  @chart = nil
21
21
  end
22
22
 
23
- private
23
+ protected
24
24
 
25
25
  def set_model
26
26
  options = attrs.options
27
27
  unless options
28
- raise ArgumentError, 'no options attribute set for :highcharts component'
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 ia an array of Volt::Models with an id and a chart attribute.
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
- watches << -> do
65
- setup_dependencies(_title)
66
- setup_dependencies(_subtitle)
67
- log_change "#{self.class.name}##{__method__}:#{__LINE__} : chart.set_title(#{_title.to_h} #{_subtitle.to_h})"
68
- chart.set_title(_title.to_h, _subtitle.to_h, true) # redraw
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
- @series_size = _series.size
74
- watches << -> do
75
- size = _series.size
76
- if size == @series_size
77
- _series.each_with_index do |a_series, index|
78
- watches << -> do
79
- log_change "@@@ _series[#{index}] changed", a_series
80
- watches << -> do
81
- data = a_series._data
82
- log_change "@@@ _series[#{index}]._data changed", data
83
- chart.series[index].set_data(data.to_a)
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.watch!
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.each_with_index do |a_series, index|
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
- end
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 = @watch_counts = nil
138
+ @watches = nil
138
139
  end
139
140
 
140
- def update_page
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
- debug __method__, __LINE__, "deleted='#{deleted}' page._charts.size=#{page._charts.size}"
147
- deleted._chart.destroy
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 debug(method, line, s)
158
- Volt.logger.debug "#{self.class.name}##{method}[#{line}] : #{s}"
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 log_change(label, object = 'nil')
162
- Volt.logger.debug "#{label} : #{object}"
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
@@ -1,5 +1,5 @@
1
1
  module Volt
2
2
  module Highcharts
3
- VERSION = '0.1.2'
3
+ VERSION = '0.1.3'
4
4
  end
5
5
  end
data/push CHANGED
@@ -2,4 +2,4 @@ sh build
2
2
  # git remote add origin https://github.com/balmoral/volt-highcharts.git
3
3
  # git push -u origin master
4
4
  git push
5
- gem push volt-highcharts-0.1.2.gem
5
+ gem push volt-highcharts-0.1.3.gem
@@ -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 JavaScript library.}
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.2
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-06 00:00:00.000000000 Z
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: '0'
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 JavaScript library.
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