dashing 0.1.0 → 0.1.2

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.
Files changed (44) hide show
  1. data/README.md +2 -7
  2. data/javascripts/batman.jquery.js +52 -0
  3. data/javascripts/batman.js +3863 -3307
  4. data/javascripts/dashing.coffee +19 -6
  5. data/lib/dashing.rb +3 -1
  6. data/templates/project/Gemfile +1 -1
  7. data/templates/project/assets/images/logo.png +0 -0
  8. data/templates/project/assets/javascripts/application.coffee +6 -6
  9. data/templates/project/assets/stylesheets/application.scss +33 -7
  10. data/templates/project/dashboards/sample.erb +3 -3
  11. data/templates/project/dashboards/sampletv.erb +56 -0
  12. data/templates/project/jobs/buzzwords.rb +1 -1
  13. data/templates/project/jobs/convergence.rb +1 -0
  14. data/templates/project/jobs/sample.rb +4 -0
  15. data/templates/project/jobs/twitter.rb +17 -0
  16. data/templates/project/widgets/clock/clock.coffee +18 -0
  17. data/templates/project/widgets/clock/clock.html +2 -0
  18. data/templates/project/widgets/clock/clock.scss +13 -0
  19. data/templates/project/widgets/comments/comments.coffee +24 -0
  20. data/templates/project/widgets/comments/comments.html +7 -0
  21. data/templates/project/widgets/comments/comments.scss +33 -0
  22. data/templates/project/widgets/graph/graph.coffee +12 -3
  23. data/templates/project/widgets/graph/graph.html +2 -2
  24. data/templates/project/widgets/graph/graph.scss +25 -11
  25. data/templates/project/widgets/iframe/iframe.coffee +9 -0
  26. data/templates/project/widgets/iframe/iframe.html +1 -0
  27. data/templates/project/widgets/iframe/iframe.scss +8 -0
  28. data/templates/project/widgets/image/image.coffee +9 -0
  29. data/templates/project/widgets/image/image.html +1 -0
  30. data/templates/project/widgets/image/image.scss +13 -0
  31. data/templates/project/widgets/list/list.coffee +4 -11
  32. data/templates/project/widgets/list/list.html +4 -1
  33. data/templates/project/widgets/list/list.scss +11 -3
  34. data/templates/project/widgets/meter/meter.html +3 -1
  35. data/templates/project/widgets/meter/meter.scss +9 -11
  36. data/templates/project/widgets/number/number.coffee +2 -4
  37. data/templates/project/widgets/number/number.html +5 -3
  38. data/templates/project/widgets/number/number.scss +9 -5
  39. data/templates/project/widgets/text/text.html +4 -2
  40. data/templates/project/widgets/text/text.scss +12 -5
  41. data/templates/widget/%name%/%name%.coffee.tt +1 -2
  42. metadata +41 -23
  43. data/templates/project/lib/.empty_directory +0 -1
  44. data/templates/project/public/.empty_directory +0 -1
@@ -14,7 +14,7 @@ Batman.Filters.dashize = (str) ->
14
14
  return str.replace(dashes_rx1, '$1_$2').replace(dashes_rx2, '$1_$2').replace('_', '-').toLowerCase()
15
15
 
16
16
  Batman.Filters.shortenedNumber = (num) ->
17
- return if isNaN(num)
17
+ return num if isNaN(num)
18
18
  if num >= 1000000000
19
19
  (num / 1000000000).toFixed(1) + 'B'
20
20
  else if num >= 1000000
@@ -25,7 +25,8 @@ Batman.Filters.shortenedNumber = (num) ->
25
25
  num
26
26
 
27
27
  class window.Dashing extends Batman.App
28
- @root ->
28
+ @root ->
29
+ Dashing.params = Batman.URI.paramsFromQuery(window.location.search.slice(1));
29
30
 
30
31
  class Dashing.Widget extends Batman.View
31
32
  constructor: ->
@@ -41,23 +42,32 @@ class Dashing.Widget extends Batman.View
41
42
  type = Batman.Filters.dashize(@view)
42
43
  $(@node).addClass("widget widget-#{type} #{@id}")
43
44
 
45
+ @accessor 'updatedAtMessage', ->
46
+ if updatedAt = @get('updatedAt')
47
+ timestamp = updatedAt.toString().match(/\d*:\d*/)[0]
48
+ "Last updated at #{timestamp}"
44
49
 
45
50
  @::on 'ready', ->
46
51
  Dashing.Widget.fire 'ready'
47
52
 
48
- onData: (data) =>
53
+ receiveData: (data) =>
49
54
  @mixin(data)
55
+ @onData(data)
50
56
 
57
+ onData: (data) =>
58
+ # Widgets override this to handle incoming data
51
59
 
52
60
  Dashing.AnimatedValue =
53
61
  get: Batman.Property.defaultAccessor.get
54
62
  set: (k, to) ->
55
- if isNaN(to)
63
+ if !to? || isNaN(to)
56
64
  @[k] = to
57
65
  else
58
66
  timer = "interval_#{k}"
59
- num = if !isNaN(@[k]) then @[k] else 0
67
+ num = if (!isNaN(@[k]) && @[k]?) then @[k] else 0
60
68
  unless @[timer] || num == to
69
+ to = parseFloat(to)
70
+ num = parseFloat(num)
61
71
  up = to > num
62
72
  num_interval = Math.abs(num - to) / 90
63
73
  @[timer] =
@@ -74,6 +84,7 @@ Dashing.AnimatedValue =
74
84
 
75
85
  Dashing.widgets = widgets = {}
76
86
  Dashing.lastEvents = lastEvents = {}
87
+ Dashing.debugMode = false
77
88
 
78
89
  source = new EventSource('/events')
79
90
  source.addEventListener 'open', (e) ->
@@ -86,10 +97,12 @@ source.addEventListener 'error', (e)->
86
97
 
87
98
  source.addEventListener 'message', (e) =>
88
99
  data = JSON.parse(e.data)
100
+ if Dashing.debugMode
101
+ console.log("Received data for #{data.id}", data)
89
102
  lastEvents[data.id] = data
90
103
  if widgets[data.id]?.length > 0
91
104
  for widget in widgets[data.id]
92
- widget.onData(data)
105
+ widget.receiveData(data)
93
106
 
94
107
 
95
108
  $(document).ready ->
@@ -81,7 +81,8 @@ end
81
81
 
82
82
  def send_event(id, body)
83
83
  body["id"] = id
84
- event = format_event(JSON.unparse(body))
84
+ body["updatedAt"] = Time.now
85
+ event = format_event(body.to_json)
85
86
  settings.history[id] = event
86
87
  settings.connections.each { |out| out << event }
87
88
  end
@@ -103,6 +104,7 @@ def first_dashboard
103
104
  end
104
105
 
105
106
  Dir[File.join(settings.root, 'lib', '**', '*.rb')].each {|file| require file }
107
+ {}.to_json # Forces your json codec to initialize (in the event that it is lazily loaded). Does this before job threads start.
106
108
 
107
109
  job_path = ENV["JOB_PATH"] || 'jobs'
108
110
  files = Dir[File.join(settings.root, job_path, '/*.rb')]
@@ -1,3 +1,3 @@
1
1
  source :rubygems
2
2
 
3
- gem 'dashing', '0.1.0', :git => 'git@github.com:Shopify/dashing.git'
3
+ gem 'dashing'
@@ -8,17 +8,17 @@
8
8
  console.log("Yeah! The dashboard has started!")
9
9
 
10
10
  Dashing.on 'ready', ->
11
- widget_margins = [5, 5]
12
- widget_base_dimensions = [240, 330]
13
- numColumns = 4
11
+ Dashing.widget_margins ||= [5, 5]
12
+ Dashing.widget_base_dimensions ||= [300, 360]
13
+ Dashing.numColumns ||= 4
14
14
 
15
- contentWidth = (widget_base_dimensions[0] + widget_margins[0] * 2) * numColumns
15
+ contentWidth = (Dashing.widget_base_dimensions[0] + Dashing.widget_margins[0] * 2) * Dashing.numColumns
16
16
 
17
17
  Batman.setImmediate ->
18
18
  $('.gridster').width(contentWidth)
19
19
  $('.gridster ul:first').gridster
20
- widget_margins: widget_margins
21
- widget_base_dimensions: widget_base_dimensions
20
+ widget_margins: Dashing.widget_margins
21
+ widget_base_dimensions: Dashing.widget_base_dimensions
22
22
  avoid_overlapped_widgets: !Dashing.customGridsterLayout
23
23
  draggable:
24
24
  stop: Dashing.showGridsterInstructions
@@ -95,11 +95,11 @@ h1 {
95
95
  margin-bottom: 12px;
96
96
  text-align: center;
97
97
  font-size: 30px;
98
- font-weight: 300;
98
+ font-weight: 400;
99
99
  }
100
100
  h2 {
101
101
  text-transform: uppercase;
102
- font-size: 80px;
102
+ font-size: 76px;
103
103
  font-weight: 700;
104
104
  color: $text-color;
105
105
  }
@@ -116,6 +116,16 @@ h3 {
116
116
  margin: 0px auto;
117
117
  }
118
118
 
119
+ .icon-background {
120
+ width: 100%!important;
121
+ height: 100%;
122
+ position: absolute;
123
+ left: 0;
124
+ top: 0;
125
+ opacity: 0.1;
126
+ font-size: 275px;
127
+ }
128
+
119
129
  .list-nostyle {
120
130
  list-style: none;
121
131
  }
@@ -146,7 +156,7 @@ h3 {
146
156
  display: inline-block;
147
157
  }
148
158
 
149
- .title, .text-moreinfo {
159
+ .title, .more-info {
150
160
  color: $text-warning-color;
151
161
  }
152
162
  }
@@ -160,14 +170,25 @@ h3 {
160
170
  display: inline-block;
161
171
  }
162
172
 
163
- .title, .text-moreinfo {
173
+ .title, .more-info {
164
174
  color: $text-danger-color;
165
175
  }
166
176
  }
167
177
 
168
- .text-moreinfo {
169
- margin-top: 12px;
178
+ .more-info {
170
179
  font-size: 15px;
180
+ position: absolute;
181
+ bottom: 32px;
182
+ left: 0;
183
+ right: 0;
184
+ }
185
+
186
+ .updated-at {
187
+ font-size: 15px;
188
+ position: absolute;
189
+ bottom: 12px;
190
+ left: 0;
191
+ right: 0;
171
192
  }
172
193
 
173
194
  #save-gridster {
@@ -220,10 +241,15 @@ h3 {
220
241
  display: none;
221
242
  }
222
243
 
244
+ #container {
245
+ padding-top: 5px;
246
+ }
247
+
223
248
 
224
249
  // ----------------------------------------------------------------------------
225
250
  // Clearfix
226
251
  // ----------------------------------------------------------------------------
227
252
  .clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; }
228
253
  .clearfix:after { clear: both; }
229
- .clearfix { zoom: 1; }
254
+ .clearfix { zoom: 1; }
255
+
@@ -10,15 +10,15 @@
10
10
  </li>
11
11
 
12
12
  <li data-row="1" data-col="1" data-sizex="1" data-sizey="2">
13
- <div data-id="buzzwords" data-view="List" data-unordered="true" data-title="Buzzwords"></div>
13
+ <div data-id="buzzwords" data-view="List" data-unordered="true" data-title="Buzzwords" data-moreinfo="# of times said around the office"></div>
14
14
  </li>
15
15
 
16
16
  <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
17
- <div data-id="valuation" data-view="Number" data-title="Current Valuation" data-moreinfo="In billions"></div>
17
+ <div data-id="valuation" data-view="Number" data-title="Current Valuation" data-moreinfo="In billions" data-prefix="$"></div>
18
18
  </li>
19
19
 
20
20
  <li data-row="1" data-col="1" data-sizex="2" data-sizey="1">
21
- <div data-id="convergence" data-view="Graph" data-title="Convergence" style="background-color:#ff9618;"></div>
21
+ <div data-id="convergence" data-view="Graph" data-title="Convergence" style="background-color:#ff9618"></div>
22
22
  </li>
23
23
  </ul>
24
24
  <center><div style="font-size: 12px">Try this: curl -d '{ "auth_token": "YOUR_AUTH_TOKEN", "text": "Hey, Look what I can do!" }' \http://localhost:3000/widgets/welcome</div></center>
@@ -0,0 +1,56 @@
1
+ <script type='text/javascript'>
2
+ $(function() {
3
+ // These settings override the defaults set in application.coffee. You can do this on a per dashboard basis.
4
+ Dashing.gridsterLayout('[{"col":2,"row":1},{"col":1,"row":1},{"col":3,"row":1},{"col":2,"row":2},{"col":3,"row":2},{"col":1,"row":2},{"col":5,"row":1},{"col":4,"row":2},{"col":2,"row":3}]')
5
+ Dashing.widget_base_dimensions = [370, 340]
6
+ Dashing.numColumns = 5
7
+ });
8
+ </script>
9
+
10
+
11
+ <% content_for(:title) { "1080p dashboard" } %>
12
+
13
+ <div class="gridster">
14
+ <ul>
15
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
16
+ <div data-view="Clock"></div>
17
+ <i class="icon-time icon-background"></i>
18
+ </li>
19
+
20
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
21
+ <div data-view="Image" data-image="/logo.png"></div>
22
+ </li>
23
+
24
+ <li data-row="1" data-col="1" data-sizex="2" data-sizey="1">
25
+ <div data-id="welcome" data-view="Text" data-title="Hello" data-text="This is your shiny new 1080p dashboard." data-moreinfo="Protip: You can drag the widgets around!"></div>
26
+ </li>
27
+
28
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
29
+ <div data-id="synergy" data-view="Meter" data-title="Synergy" data-min="0" data-max="100"></div>
30
+ </li>
31
+
32
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
33
+ <div data-id="synergy" data-view="Meter" data-moreinfo="In sync with my neighbour!" data-title="Synergy" data-min="0" data-max="100"></div>
34
+ </li>
35
+
36
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="2">
37
+ <div data-id="buzzwords" data-view="List" data-unordered="true" data-title="Buzzwords" data-moreinfo="# of times said around the office"></div>
38
+ </li>
39
+
40
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
41
+ <div data-id="karma" data-view="Number" data-title="Karma" style="background-color:#96bf48;"></div>
42
+ <i class="icon-heart icon-background"></i>
43
+ </li>
44
+
45
+ <li data-row="1" data-col="1" data-sizex="2" data-sizey="2">
46
+ <div data-id="convergence" data-view="Graph" data-title="Convergence" style="background-color:#47bbb3;" data-moreinfo="poop" ></div>
47
+ </li>
48
+
49
+ <li data-row="1" data-col="1" data-sizex="2" data-sizey="1">
50
+ <div data-id="twitter_mentions" data-view="Comments" style="background-color:#ff9618;" data-moreinfo="Tweets tagged with #todayilearned"></div>
51
+ <i class="icon-twitter icon-background"></i>
52
+ </li>
53
+
54
+ </ul>
55
+ <center><div style="font-size: 12px">Try this: curl -d '{ "auth_token": "YOUR_AUTH_TOKEN", "text": "Hey, Look what I can do!" }' \http://localhost:3000/widgets/welcome</div></center>
56
+ </div>
@@ -3,7 +3,7 @@ buzzword_counts = Hash.new({ value: 0 })
3
3
 
4
4
  SCHEDULER.every '2s' do
5
5
  random_buzzword = buzzwords.sample
6
- buzzword_counts[random_buzzword] = { label: random_buzzword, value: buzzword_counts[random_buzzword][:value] + 1 }
6
+ buzzword_counts[random_buzzword] = { label: random_buzzword, value: (buzzword_counts[random_buzzword][:value] + 1) % 30 }
7
7
 
8
8
  send_event('buzzwords', { items: buzzword_counts.values })
9
9
  end
@@ -1,3 +1,4 @@
1
+ # Populate the graph with some random points
1
2
  points = []
2
3
  (1..10).each do |i|
3
4
  points << { x: i, y: rand(50) }
@@ -1,9 +1,13 @@
1
1
  current_valuation = 0
2
+ current_karma = 0
2
3
 
3
4
  SCHEDULER.every '2s' do
4
5
  last_valuation = current_valuation
6
+ last_karma = current_karma
5
7
  current_valuation = rand(100)
8
+ current_karma = rand(200000)
6
9
 
7
10
  send_event('valuation', { current: current_valuation, last: last_valuation })
11
+ send_event('karma', { current: current_karma, last: last_karma })
8
12
  send_event('synergy', { value: rand(100) })
9
13
  end
@@ -0,0 +1,17 @@
1
+ require 'net/http'
2
+ require 'json'
3
+
4
+ search_term = URI::encode('#todayilearned')
5
+
6
+ SCHEDULER.every '10m', :first_in => 0 do |job|
7
+ http = Net::HTTP.new('search.twitter.com')
8
+ response = http.request(Net::HTTP::Get.new("/search.json?q=#{search_term}"))
9
+ tweets = JSON.parse(response.body)["results"]
10
+ if tweets
11
+ tweets.map! do |tweet|
12
+ { name: tweet['from_user'], body: tweet['text'], avatar: tweet['profile_image_url_https'] }
13
+ end
14
+
15
+ send_event('twitter_mentions', comments: tweets)
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ class Dashing.Clock extends Dashing.Widget
2
+
3
+ ready: ->
4
+ setInterval(@startTime, 500)
5
+
6
+ startTime: =>
7
+ today = new Date()
8
+
9
+ h = today.getHours()
10
+ m = today.getMinutes()
11
+ s = today.getSeconds()
12
+ m = @formatTime(m)
13
+ s = @formatTime(s)
14
+ @set('time', h + ":" + m + ":" + s)
15
+ @set('date', today.toDateString())
16
+
17
+ formatTime: (i) ->
18
+ if i < 10 then "0" + i else i
@@ -0,0 +1,2 @@
1
+ <h1 data-bind="date"></h1>
2
+ <h2 data-bind="time"></h2>
@@ -0,0 +1,13 @@
1
+ // ----------------------------------------------------------------------------
2
+ // Sass declarations
3
+ // ----------------------------------------------------------------------------
4
+ $background-color: #dc5945;
5
+
6
+ // ----------------------------------------------------------------------------
7
+ // Widget-clock styles
8
+ // ----------------------------------------------------------------------------
9
+ .widget-clock {
10
+
11
+ background-color: $background-color;
12
+
13
+ }
@@ -0,0 +1,24 @@
1
+ class Dashing.Comments extends Dashing.Widget
2
+
3
+ @accessor 'quote', ->
4
+ "“#{@get('current_comment')?.body}”"
5
+
6
+ ready: ->
7
+ @currentIndex = 0
8
+ @commentElem = $(@node).find('.comment-container')
9
+ @nextComment()
10
+ @startCarousel()
11
+
12
+ onData: (data) ->
13
+ @currentIndex = 0
14
+
15
+ startCarousel: ->
16
+ setInterval(@nextComment, 8000)
17
+
18
+ nextComment: =>
19
+ comments = @get('comments')
20
+ if comments
21
+ @commentElem.fadeOut =>
22
+ @currentIndex = (@currentIndex + 1) % comments.length
23
+ @set 'current_comment', comments[@currentIndex]
24
+ @commentElem.fadeIn()
@@ -0,0 +1,7 @@
1
+ <h1 class="title" data-bind="title"></h1>
2
+ <div class="comment-container">
3
+ <h3><img data-bind-src='current_comment.avatar'/><span data-bind='current_comment.name' class="name"></span></h3>
4
+ <p class="comment" data-bind='quote'></p>
5
+ </div>
6
+
7
+ <p class="more-info" data-bind="moreinfo | raw"></p>
@@ -0,0 +1,33 @@
1
+ // ----------------------------------------------------------------------------
2
+ // Sass declarations
3
+ // ----------------------------------------------------------------------------
4
+ $background-color: #eb9c3c;
5
+
6
+ $title-color: rgba(255, 255, 255, 0.7);
7
+ $moreinfo-color: rgba(255, 255, 255, 0.7);
8
+
9
+ // ----------------------------------------------------------------------------
10
+ // Widget-comment styles
11
+ // ----------------------------------------------------------------------------
12
+ .widget-comments {
13
+
14
+ background-color: $background-color;
15
+
16
+ .title {
17
+ color: $title-color;
18
+ margin-bottom: 15px;
19
+ }
20
+
21
+ .name {
22
+ padding-left: 5px;
23
+ }
24
+
25
+ .comment-container {
26
+ display: none;
27
+ }
28
+
29
+ .more-info {
30
+ color: $moreinfo-color;
31
+ }
32
+
33
+ }
@@ -1,26 +1,35 @@
1
1
  class Dashing.Graph extends Dashing.Widget
2
2
 
3
3
  @accessor 'current', ->
4
+ return @get('displayedValue') if @get('displayedValue')
4
5
  points = @get('points')
5
6
  if points
6
7
  points[points.length - 1].y
7
8
 
8
9
  ready: ->
10
+ container = $(@node).parent()
11
+ # Gross hacks. Let's fix this.
12
+ width = (Dashing.widget_base_dimensions[0] * container.data("sizex")) + Dashing.widget_margins[0] * 2 * (container.data("sizex") - 1)
13
+ height = (Dashing.widget_base_dimensions[1] * container.data("sizey"))
9
14
  @graph = new Rickshaw.Graph(
10
15
  element: @node
11
- width: $(@node).parent().width()
16
+ width: width
17
+ height: height
12
18
  series: [
13
19
  {
14
20
  color: "#fff",
15
- data: [{ x: 0, y: 0}]
21
+ data: [{x:0, y:0}]
16
22
  }
17
23
  ]
18
24
  )
25
+
26
+ @graph.series[0].data = @get('points') if @get('points')
27
+
19
28
  x_axis = new Rickshaw.Graph.Axis.Time(graph: @graph)
29
+ y_axis = new Rickshaw.Graph.Axis.Y(graph: @graph, tickFormat: Rickshaw.Fixtures.Number.formatKMBT)
20
30
  @graph.render()
21
31
 
22
32
  onData: (data) ->
23
- super
24
33
  if @graph
25
34
  @graph.series[0].data = data.points
26
35
  @graph.render()