volt-highcharts 0.1.0 → 0.1.2

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: c3d80171d3265558b6ae56c93561728293fbaf8e
4
- data.tar.gz: 625467100a4c7cd3aa655cb3674ee42d0a825a64
3
+ metadata.gz: 57bc486e2e61cd1b390da2fb6cba28705479531f
4
+ data.tar.gz: 2f1a36ef4888500b8933201f7cd533a0a774a20d
5
5
  SHA512:
6
- metadata.gz: e56df08f4f33341d83895f5cb9973393c73079149f4432a95ff8ebe67493e96d9ce6ab0311e2fc3c00f879d8455ca9f914c6e165575cf5f73a20d19825a6f5fd
7
- data.tar.gz: c1911fe995e81aef5efc4b1281eb63f44ed5e82295f6c4c51125d0b8b3d9ed4cc1165574a35d290e533132787767576646a22f8319584553124b77bb9fb34191
6
+ metadata.gz: f45e348902daac3c71b825bcddbf8c7797ee3aed492a0718684062fd4ff56237c6fe4e62f9cf02a74bf0744caf9c39961c173d00c6f26f6034670d93a1ea9e25
7
+ data.tar.gz: 13aee26ca775e5e626688967d2365ce9314197f15ac0dc1cb49c27f2fffeab6d0bc7f5ec9e9fcd31a09605070690b9befd518a6c2514b785ab6a872829a8e04c
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Change Log
2
+
3
+ ## 0.1.1
4
+
5
+ - now uses opal-highcharts gem
6
+ - first steps towards reactivitiy
7
+ - a chart is reactive if options are provided as a Volt::Model
8
+ - a chart is non-reactive if options are provided as a Hash
9
+
10
+ ### opal-highcharts
11
+ a gem which wraps most Highcharts and Highstock functionality in a client-side Ruby API.
12
+ - https://github.com/balmoral/opal-highcharts
13
+ - https://rubygems.org/gems/opal-highcharts
14
+
15
+
16
+
17
+
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015 balmoral
1
+ Copyright (c) 2015 Colin Gunn
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -2,9 +2,13 @@
2
2
 
3
3
  A Volt component wrapping the Highcharts javascript charting tool.
4
4
 
5
+ It depends on opal-highcharts, a gem which wraps most Highcharts and Highstock functionality in a client-side Ruby API.
6
+
5
7
  Highcharts is free for non-commercial use.
6
8
 
7
9
  http://www.highcharts.com/products/highcharts
10
+ http://github.com/balmoral/volt-highcharts
11
+ https://rubygems.org/gems/volt-highcharts
8
12
 
9
13
  ## Installation
10
14
 
@@ -36,20 +40,25 @@ Pass a Ruby hash containing chart options in the appropriate view html file:
36
40
  <:highcharts chart="{{ chart_options }}" />
37
41
  ```
38
42
 
39
- where `chart_options` is provided by your controller or model. Any object which responds to #to_h may be used, including of course a Volt::Model.
43
+ where `chart_options` is a Volt::Model or Hash provided by your controller or model.
44
+
45
+ Reactivity is now supported.
40
46
 
47
+ To implement a reactive chart, the options provided on chart creation should be wrapped in a Volt::Model.
48
+
49
+ NB reactivity is currently limited to chart titles, number of series, and individual series options and data. More coming soon.
50
+
41
51
  Documentation for Highcharts options can be found at: http://api.highcharts.com/highcharts#chart.
42
52
 
43
- At present only a copy of the chart options are passed to Highcharts so the binding will not update the chart automatically.
44
-
45
- For convenience, the last chart added can simply be accessed as ```page._chart```, wrapped by Opal's Native().
53
+ For convenience, the last chart added can be accessed as ```page._chart```.
54
+ The object returned is a Highcharts::Chart, which can be used to directly query and manipulate the chart (see opal-highcharts).
46
55
 
47
56
  To query or modify multiple chart(s) on the page a unique :id should be set in each chart's options.
48
57
 
49
58
  For example:
50
59
  ```
51
60
  def fruit_chart_options
52
- {
61
+ Volt::Model.new( {
53
62
  # to identity the chart in volt
54
63
  id: 'fruit_chart',
55
64
 
@@ -79,7 +88,7 @@ For example:
79
88
  },
80
89
  ...
81
90
  ]
82
- }
91
+ } )
83
92
  end
84
93
  ```
85
94
 
@@ -95,36 +104,22 @@ For example, in your controller you might have a method to return the native cha
95
104
  ```
96
105
  If you only have one chart on the page use ```page._chart```.
97
106
 
98
- You can dynamically query or modify the chart(s) using Opal's Native() or inline scripting.
99
-
100
- The chart object(s) found in ```page._chart``` and ```page._charts``` have been wrapped in Opal's Native(). This is because Volt::Model and Volt::ArrayModel can only hold Ruby objects.
101
-
102
- Opal's Native() wraps a JS object to provide access to properties and functions in the JS object via Ruby method calls. As of writing (July 30, 2015) Native has not yet been documented. If you prefer to use backticks or %x{} to inline JS code you can get the JS object using #to_n.
103
-
104
- For example, to change a series in a chart using Native(), you might do:
105
- ```
106
- def update_sales
107
- e = page._charts.find { |e| e._id == 'sales' }
108
- series = Native(e._chart.series)
109
- Native(series[0]).setData(sales_data.to_n)
110
- end
111
- ```
112
- The equivalent using backticks is:
113
- ```
114
- def update_sales
115
- native_chart = page._chart.to_n # get the native JS chart
116
- native_data = sales_data.to_n # get native sales data
117
- `native_chart.series[0].setData(native_data)`
118
- end
119
- ```
107
+ With opal-highcharts, which completely wraps the Highcharts API in client-side Ruby (and comes bundled with volt-highcharts),
108
+ you now have simple access to query and modify methods on the chart and all of its elements. No Native wraps or backticks required.
120
109
 
121
- Always use #to_n to convert Ruby data to JS when passing to Highcharts.
110
+ As reactivity support is improved, there should be less need for direct manipulation of the chart.
111
+
112
+ ## To do
122
113
 
123
- In the future we hope to provide a fully wrapped Ruby implementation of Highcharts.
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?
124
119
 
125
120
  ## Contributing
126
121
 
127
- The author is new to things javascript, opal and volt, so contributions and suggestions are very welcome.
122
+ Contributions, comments and suggestions are welcome.
128
123
 
129
124
  1. Fork it ( http://github.com/balmoral/volt-highcharts/fork )
130
125
  2. Create your feature branch (`git checkout -b my-new-feature`)
@@ -1,2 +1,6 @@
1
1
  # Component dependencies
2
- javascript_file 'http://code.highcharts.com/highcharts.js'
2
+
3
+ # highcharts.js is not required if highstock is loaded
4
+ # javascript_file 'http://code.highcharts.com/highcharts.src.js'
5
+ javascript_file 'http://code.highcharts.com/stock/highstock.src.js'
6
+ # javascript_file 'http://code.highcharts.com/maps/highmaps.src.js'
@@ -1,72 +1,168 @@
1
+ if RUBY_PLATFORM == 'opal'
2
+
3
+ require 'native'
4
+ require 'opal-highcharts'
5
+
1
6
  module Highcharts
2
7
  class MainController < Volt::ModelController
3
8
 
9
+ attr_reader :chart, :watches, :watch_counts, :reactive
10
+
4
11
  def index_ready
5
- Volt.logger.debug("#{self.class.name}##{__method__}:#{__LINE__} : #{Time.now}")
6
-
7
- # Get the containing html element.
8
- container = self.container
9
- Volt.logger.debug("#{self.class.name}##{__method__}:#{__LINE__} : container='#{container}'")
10
-
11
- opts = attrs.chart ? attrs.chart.to_h : missing_chart
12
-
13
- # If the view hasn't set a container id then
14
- # use the id in the chart options if present
15
- # otherwise to a random id.
16
- id = `$(container).prop("id")`
17
- Volt.logger.debug("#{self.class.name}##{__method__}:#{__LINE__} : initial container id is '#{id}'")
18
- unless id == nil || id == 'undefined'
19
- id = opts[:id] || random_id
20
- Volt.logger.debug("#{self.class.name}##{__method__}:#{__LINE__} : char container id is undefined - setting to '#{id}'")
21
- `$(container).prop("id", id)`
12
+ set_model
13
+ create_chart
14
+ start_watching
15
+ end
16
+
17
+ def before_index_remove
18
+ stop_watching
19
+ update_page
20
+ @chart = nil
21
+ end
22
+
23
+ private
24
+
25
+ def set_model
26
+ options = attrs.options
27
+ unless options
28
+ raise ArgumentError, 'no options attribute set for :highcharts component'
29
+ end
30
+ # if the options are a Hash then convert to a Volt::Model
31
+ if options.is_a?(Volt::Model)
32
+ @reactive = true
33
+ else
34
+ options = Volt::Model.new(options)
35
+ @reactive = false
36
+ end
37
+ # set controller's model to options, which captures its methods for self
38
+ self.model = options
39
+ debug __method__, __LINE__, "model._id = #{_id}"
40
+ end
41
+
42
+ # 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.
44
+ # Also set page._chart to the newly (last) created Highcharts::Chart.
45
+ # Also set page._char_id to the id of the new (last) chart.
46
+ def create_chart
47
+ @chart = Highcharts::Chart.new(model.to_h)
48
+ page._charts << {id: _id, chart: @chart}
49
+ page._chart = @chart
50
+ page._chart_id = _id
51
+ end
52
+
53
+ # To be reactive we must watch for model changes
54
+ def start_watching
55
+ @watches = []
56
+ @watch_counts = {}
57
+ if reactive
58
+ watch_titles
59
+ watch_series
22
60
  end
61
+ end
62
+
63
+ 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!
70
+ end
23
71
 
24
- # If :renderTo has been set in the options then it
25
- # should match the id of the container, otherwise
26
- # a Highcharts error #13 will occur. It should be
27
- # unique in the page. We do not check here.
28
- #
29
- # If :renderTo has not been set in the options then
30
- # it will be set here to the container id.
31
- unless opts[:chart] && opts[:chart][:renderTo]
32
- Volt.logger.debug("#{self.class.name}##{__method__}:#{__LINE__} : setting opts[:chart][:renderTo]='#{id}'")
33
- (opts[:chart] ||= {})[:renderTo] = id
72
+ 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
100
+ end
101
+ end.watch!
102
+ end
103
+
104
+ # Do complete refresh of all series:
105
+ # 1. remove all series from chart with no redraw
106
+ # 2. add all series in model to chart with no redraw
107
+ # 3. redraw chart
108
+ def refresh_all_series
109
+ until chart.series.empty? do
110
+ debug __method__, __LINE__, "chart.series[#{chart.series.size-1}].remove"
111
+ chart.series.last.remove(false)
34
112
  end
113
+ _series.each_with_index do |a_series, index|
114
+ debug __method__, __LINE__, "chart.add_series ##{index}"
115
+ chart.add_series(a_series.to_h, false)
116
+ end
117
+ debug __method__, __LINE__, "chart.redraw"
118
+ chart.redraw
119
+ end
35
120
 
36
- # Render the chart and add it to the page._charts.
37
- # _charts ia an array of Volt::Models with an id and a chart attribute.
38
- @id = opts[:id] || id
39
- chart = Native(`new Highcharts.Chart( #{opts.to_n} )`) # needs to wrapped for Volt::Model
40
- page._charts << Volt::Model.new({id: @id, chart: chart})
41
- page._chart = chart # a simple way for later access
42
- page._chart_id = id # so we know whether it's me
43
- Volt.logger.debug("#{self.class.name}##{__method__}:#{__LINE__} : page._charts='#{page._charts}' page._charts.size=#{page._charts.size}")
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
+ }
44
133
  end
45
134
 
46
- def before_index_remove
47
- Volt.logger.debug("#{self.class.name}##{__method__}:#{__LINE__} #{Time.now}")
135
+ def stop_watching
136
+ @watches.each {|w| w.stop}
137
+ @watches = @watch_counts = nil
138
+ end
139
+
140
+ def update_page
141
+ debug __method__, _LINE__, Time.now.to_s
48
142
  # clear all references to this chart
49
- i = page._charts.find_index { |e| e._id == @id }
143
+ i = page._charts.find_index { |e| e._id == _id }
50
144
  if i
51
145
  deleted = page._charts.delete_at(i)
52
- Volt.logger.debug("#{self.class.name}##{__method__}:#{__LINE__} : deleted='#{deleted}' page._charts.size=#{page._charts.size}")
146
+ debug __method__, __LINE__, "deleted='#{deleted}' page._charts.size=#{page._charts.size}"
53
147
  deleted._chart.destroy
54
148
  deleted._chart = nil
55
149
  end
56
- page._chart = nil if page._chart_id == @id
57
- @id = nil
150
+ if page._chart_id == _id
151
+ last = page._charts.last
152
+ page._chart_id = last ? last._id : nil
153
+ page._chart = last ? last._chart : nil
154
+ end
58
155
  end
59
156
 
60
- private
61
-
62
- # generate a unique id for chart container
63
- def random_id
64
- "highcharts#{(rand * 1000000000).to_i}"
157
+ def debug(method, line, s)
158
+ Volt.logger.debug "#{self.class.name}##{method}[#{line}] : #{s}"
65
159
  end
66
160
 
67
- def missing_chart
68
- { chart: { title: 'no chart attribute set for :highcharts component' } }
161
+ def log_change(label, object = 'nil')
162
+ Volt.logger.debug "#{label} : #{object}"
69
163
  end
70
164
 
71
165
  end
72
- end
166
+ end
167
+
168
+ end # RUBY_PLATFORM == 'opal'
@@ -1,5 +1,5 @@
1
1
  module Volt
2
2
  module Highcharts
3
- VERSION = '0.1.0'
3
+ VERSION = '0.1.2'
4
4
  end
5
5
  end
data/push ADDED
@@ -0,0 +1,5 @@
1
+ sh build
2
+ # git remote add origin https://github.com/balmoral/volt-highcharts.git
3
+ # git push -u origin master
4
+ git push
5
+ gem push volt-highcharts-0.1.2.gem
@@ -17,6 +17,8 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
+ spec.add_runtime_dependency 'opal-highcharts', '~> 0.1.0'
21
+
20
22
  # spec.add_development_dependency "volt", "~> 0.9.5.pre3"
21
23
  # spec.add_development_dependency 'rspec', '~> 3.2.0'
22
24
  # spec.add_development_dependency "rake"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: volt-highcharts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
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-07-30 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2015-08-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: opal-highcharts
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.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.1.0
13
27
  description:
14
28
  email:
15
29
  - colgunn@icloud.com
@@ -18,6 +32,7 @@ extensions: []
18
32
  extra_rdoc_files: []
19
33
  files:
20
34
  - ".rspec"
35
+ - CHANGELOG.md
21
36
  - Gemfile
22
37
  - LICENSE.txt
23
38
  - README.md
@@ -29,6 +44,7 @@ files:
29
44
  - app/highcharts/views/main/index.html
30
45
  - lib/volt/highcharts.rb
31
46
  - lib/volt/highcharts/version.rb
47
+ - push
32
48
  - spec/spec_helper.rb
33
49
  - spec/volt/highcharts_spec.rb
34
50
  - volt-highcharts.gemspec