dashing 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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()