dashing 1.3.2 → 1.3.3
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/bin/dashing +6 -110
- data/lib/dashing.rb +4 -165
- data/lib/dashing/app.rb +167 -0
- data/lib/dashing/cli.rb +109 -0
- data/lib/dashing/downloader.rb +18 -0
- metadata +160 -51
- metadata.gz.sig +3 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7c59e687451b697ac161b49e89e26ed71b62dcf
|
4
|
+
data.tar.gz: dce2b7d38136bc5330b770b49ca49ae32cc2b0e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb422e90480391126aea639a2a6da7c646d80bcdc8e6459c6d41c12263a198f9acf0abf7ef025445f1899503ec0eac070ee97d45f9834874248bc76c4423758e
|
7
|
+
data.tar.gz: 0f19a88567bfcb2b00bda112650f812c4414abc9396502913e93078e79704f2f35a6913e8cb2a59f5bed8fedaabeae20666c10f372aace016ac78ff751ec4ad4
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
Binary file
|
data/bin/dashing
CHANGED
@@ -1,112 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
require "pathname"
|
3
|
+
bin_file = Pathname.new(__FILE__).realpath
|
4
|
+
$:.unshift File.expand_path("../../lib", bin_file)
|
2
5
|
|
3
|
-
require '
|
4
|
-
|
5
|
-
|
6
|
-
require 'open-uri'
|
7
|
-
|
8
|
-
class MockScheduler
|
9
|
-
def method_missing(*args)
|
10
|
-
yield
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
SCHEDULER = MockScheduler.new
|
15
|
-
|
16
|
-
module Dashing
|
17
|
-
|
18
|
-
class CLI < Thor
|
19
|
-
include Thor::Actions
|
20
|
-
|
21
|
-
class << self
|
22
|
-
attr_accessor :auth_token
|
23
|
-
|
24
|
-
def hyphenate(str)
|
25
|
-
return str.downcase if str =~ /^[A-Z-]+$/
|
26
|
-
str.gsub('_', '-').gsub(/\B[A-Z]/, '-\&').squeeze('-').downcase
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
attr_accessor :name
|
31
|
-
|
32
|
-
no_tasks do
|
33
|
-
['widget', 'dashboard', 'job'].each do |type|
|
34
|
-
define_method "generate_#{type}" do |name|
|
35
|
-
@name = Thor::Util.snake_case(name)
|
36
|
-
directory type.to_sym, File.join("#{type}s")
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.source_root
|
42
|
-
File.expand_path('../../templates', __FILE__)
|
43
|
-
end
|
44
|
-
|
45
|
-
desc "new PROJECT_NAME", "Sets up ALL THE THINGS needed for your dashboard project."
|
46
|
-
def new(name)
|
47
|
-
@name = Thor::Util.snake_case(name)
|
48
|
-
directory :project, @name
|
49
|
-
end
|
50
|
-
|
51
|
-
desc "generate (widget/dashboard/job) NAME", "Creates a new widget, dashboard, or job."
|
52
|
-
def generate(type, name)
|
53
|
-
send("generate_#{type}".to_sym, name)
|
54
|
-
rescue NoMethodError => e
|
55
|
-
puts "Invalid generator. Either use widget, dashboard, or job"
|
56
|
-
end
|
57
|
-
map "g" => :generate
|
58
|
-
|
59
|
-
desc "install GIST_ID", "Installs a new widget from a gist."
|
60
|
-
def install(gist_id)
|
61
|
-
public_url = "https://gist.github.com/#{gist_id}"
|
62
|
-
gist = JSON.parse(open("https://api.github.com/gists/#{gist_id}").read)
|
63
|
-
|
64
|
-
gist['files'].each do |filename, contents|
|
65
|
-
if filename.end_with?(".rb")
|
66
|
-
create_file File.join(Dir.pwd, 'jobs', filename), contents['content']
|
67
|
-
elsif filename.end_with?(".coffee", ".html", ".scss")
|
68
|
-
widget_name = File.basename(filename, '.*')
|
69
|
-
create_file File.join(Dir.pwd, 'widgets', widget_name, filename), contents['content']
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
print set_color("Don't forget to edit the ", :yellow)
|
74
|
-
print set_color("Gemfile ", :yellow, :bold)
|
75
|
-
print set_color("and run ", :yellow)
|
76
|
-
print set_color("bundle install ", :yellow, :bold)
|
77
|
-
say set_color("if needed. More information for this widget can be found at #{public_url}", :yellow)
|
78
|
-
|
79
|
-
rescue OpenURI::HTTPError => e
|
80
|
-
say set_color("Could not find gist at #{public_url}"), :red
|
81
|
-
end
|
82
|
-
map "i" => :install
|
83
|
-
|
84
|
-
desc "start", "Starts the server in style!"
|
85
|
-
method_option :job_path, :desc => "Specify the directory where jobs are stored"
|
86
|
-
def start(*args)
|
87
|
-
port_option = args.include?('-p')? '' : ' -p 3030'
|
88
|
-
args = args.join(" ")
|
89
|
-
command = "bundle exec thin -R config.ru start #{port_option} #{args}"
|
90
|
-
command.prepend "export JOB_PATH=#{options[:job_path]}; " if options[:job_path]
|
91
|
-
system(command)
|
92
|
-
end
|
93
|
-
map "s" => :start
|
94
|
-
|
95
|
-
desc "stop", "Stops the thin server"
|
96
|
-
def stop
|
97
|
-
command = "bundle exec thin stop"
|
98
|
-
system(command)
|
99
|
-
end
|
100
|
-
|
101
|
-
desc "job JOB_NAME AUTH_TOKEN(optional)", "Runs the specified job. Make sure to supply your auth token if you have one set."
|
102
|
-
def job(name, auth_token = "")
|
103
|
-
Dir[File.join(Dir.pwd, 'lib/**/*.rb')].each {|file| require file }
|
104
|
-
self.class.auth_token = auth_token
|
105
|
-
f = File.join(Dir.pwd, "jobs", "#{name}.rb")
|
106
|
-
require f
|
107
|
-
end
|
108
|
-
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
Dashing::CLI.start
|
6
|
+
require 'dashing'
|
7
|
+
Dashing::CLI.source_root(File.expand_path('../../templates', bin_file))
|
8
|
+
Dashing::CLI.start(ARGV)
|
data/lib/dashing.rb
CHANGED
@@ -1,167 +1,6 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require 'rufus/scheduler'
|
5
|
-
require 'coffee-script'
|
6
|
-
require 'sass'
|
7
|
-
require 'json'
|
8
|
-
require 'yaml'
|
1
|
+
require 'dashing/cli'
|
2
|
+
require 'dashing/downloader'
|
3
|
+
require 'dashing/app'
|
9
4
|
|
10
|
-
|
11
|
-
|
12
|
-
set :root, Dir.pwd
|
13
|
-
|
14
|
-
set :sprockets, Sprockets::Environment.new(settings.root)
|
15
|
-
set :assets_prefix, '/assets'
|
16
|
-
set :digest_assets, false
|
17
|
-
['assets/javascripts', 'assets/stylesheets', 'assets/fonts', 'assets/images', 'widgets', File.expand_path('../../javascripts', __FILE__)]. each do |path|
|
18
|
-
settings.sprockets.append_path path
|
19
|
-
end
|
20
|
-
|
21
|
-
set server: 'thin', connections: [], history_file: 'history.yml'
|
22
|
-
|
23
|
-
# Persist history in tmp file at exit
|
24
|
-
at_exit do
|
25
|
-
File.open(settings.history_file, 'w') do |f|
|
26
|
-
f.puts settings.history.to_yaml
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
if File.exists?(settings.history_file)
|
31
|
-
set history: YAML.load_file(settings.history_file)
|
32
|
-
else
|
33
|
-
set history: {}
|
34
|
-
end
|
35
|
-
|
36
|
-
set :public_folder, File.join(settings.root, 'public')
|
37
|
-
set :views, File.join(settings.root, 'dashboards')
|
38
|
-
set :default_dashboard, nil
|
39
|
-
set :auth_token, nil
|
40
|
-
|
41
|
-
helpers Sinatra::ContentFor
|
42
|
-
helpers do
|
43
|
-
def protected!
|
44
|
-
# override with auth logic
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
get '/events', provides: 'text/event-stream' do
|
49
|
-
protected!
|
50
|
-
response.headers['X-Accel-Buffering'] = 'no' # Disable buffering for nginx
|
51
|
-
stream :keep_open do |out|
|
52
|
-
settings.connections << out
|
53
|
-
out << latest_events
|
54
|
-
out.callback { settings.connections.delete(out) }
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
get '/' do
|
59
|
-
protected!
|
60
|
-
begin
|
61
|
-
redirect "/" + (settings.default_dashboard || first_dashboard).to_s
|
62
|
-
rescue NoMethodError => e
|
63
|
-
raise Exception.new("There are no dashboards in your dashboard directory.")
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
get '/:dashboard' do
|
68
|
-
protected!
|
69
|
-
tilt_html_engines.each do |suffix, _|
|
70
|
-
file = File.join(settings.views, "#{params[:dashboard]}.#{suffix}")
|
71
|
-
return render(suffix.to_sym, params[:dashboard].to_sym) if File.exist? file
|
72
|
-
end
|
73
|
-
|
74
|
-
halt 404
|
5
|
+
module Dashing
|
75
6
|
end
|
76
|
-
|
77
|
-
get '/views/:widget?.html' do
|
78
|
-
protected!
|
79
|
-
tilt_html_engines.each do |suffix, engines|
|
80
|
-
file = File.join(settings.root, "widgets", params[:widget], "#{params[:widget]}.#{suffix}")
|
81
|
-
return engines.first.new(file).render if File.exist? file
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
post '/dashboards/:id' do
|
86
|
-
request.body.rewind
|
87
|
-
body = JSON.parse(request.body.read)
|
88
|
-
body['dashboard'] ||= params['id']
|
89
|
-
auth_token = body.delete("auth_token")
|
90
|
-
if !settings.auth_token || settings.auth_token == auth_token
|
91
|
-
send_event(params['id'], body, 'dashboards')
|
92
|
-
204 # response without entity body
|
93
|
-
else
|
94
|
-
status 401
|
95
|
-
"Invalid API key\n"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
post '/widgets/:id' do
|
100
|
-
request.body.rewind
|
101
|
-
body = JSON.parse(request.body.read)
|
102
|
-
auth_token = body.delete("auth_token")
|
103
|
-
if !settings.auth_token || settings.auth_token == auth_token
|
104
|
-
send_event(params['id'], body)
|
105
|
-
204 # response without entity body
|
106
|
-
else
|
107
|
-
status 401
|
108
|
-
"Invalid API key\n"
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
not_found do
|
113
|
-
send_file File.join(settings.public_folder, '404.html')
|
114
|
-
end
|
115
|
-
|
116
|
-
def development?
|
117
|
-
ENV['RACK_ENV'] == 'development'
|
118
|
-
end
|
119
|
-
|
120
|
-
def production?
|
121
|
-
ENV['RACK_ENV'] == 'production'
|
122
|
-
end
|
123
|
-
|
124
|
-
def send_event(id, body, target=nil)
|
125
|
-
body[:id] = id
|
126
|
-
body[:updatedAt] ||= Time.now.to_i
|
127
|
-
event = format_event(body.to_json, target)
|
128
|
-
Sinatra::Application.settings.history[id] = event unless target == 'dashboards'
|
129
|
-
Sinatra::Application.settings.connections.each { |out| out << event }
|
130
|
-
end
|
131
|
-
|
132
|
-
def format_event(body, name=nil)
|
133
|
-
str = ""
|
134
|
-
str << "event: #{name}\n" if name
|
135
|
-
str << "data: #{body}\n\n"
|
136
|
-
end
|
137
|
-
|
138
|
-
def latest_events
|
139
|
-
settings.history.inject("") do |str, (id, body)|
|
140
|
-
str << body
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def first_dashboard
|
145
|
-
files = Dir[File.join(settings.views, '*')].collect { |f| File.basename(f, '.*') }
|
146
|
-
files -= ['layout']
|
147
|
-
files.sort.first
|
148
|
-
end
|
149
|
-
|
150
|
-
def tilt_html_engines
|
151
|
-
Tilt.mappings.select do |_, engines|
|
152
|
-
default_mime_type = engines.first.default_mime_type
|
153
|
-
default_mime_type.nil? || default_mime_type == 'text/html'
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
settings_file = File.join(settings.root, 'config/settings.rb')
|
158
|
-
if (File.exists?(settings_file))
|
159
|
-
require settings_file
|
160
|
-
end
|
161
|
-
|
162
|
-
Dir[File.join(settings.root, 'lib', '**', '*.rb')].each {|file| require file }
|
163
|
-
{}.to_json # Forces your json codec to initialize (in the event that it is lazily loaded). Does this before job threads start.
|
164
|
-
|
165
|
-
job_path = ENV["JOB_PATH"] || 'jobs'
|
166
|
-
files = Dir[File.join(settings.root, job_path, '**', '/*.rb')]
|
167
|
-
files.each { |job| require(job) }
|
data/lib/dashing/app.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'sprockets'
|
3
|
+
require 'sinatra/content_for'
|
4
|
+
require 'rufus/scheduler'
|
5
|
+
require 'coffee-script'
|
6
|
+
require 'sass'
|
7
|
+
require 'json'
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
SCHEDULER = Rufus::Scheduler.new
|
11
|
+
|
12
|
+
def development?
|
13
|
+
ENV['RACK_ENV'] == 'development'
|
14
|
+
end
|
15
|
+
|
16
|
+
def production?
|
17
|
+
ENV['RACK_ENV'] == 'production'
|
18
|
+
end
|
19
|
+
|
20
|
+
helpers Sinatra::ContentFor
|
21
|
+
helpers do
|
22
|
+
def protected!
|
23
|
+
# override with auth logic
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
set :root, Dir.pwd
|
28
|
+
set :sprockets, Sprockets::Environment.new(settings.root)
|
29
|
+
set :assets_prefix, '/assets'
|
30
|
+
set :digest_assets, false
|
31
|
+
set server: 'thin', connections: [], history_file: 'history.yml'
|
32
|
+
set :public_folder, File.join(settings.root, 'public')
|
33
|
+
set :views, File.join(settings.root, 'dashboards')
|
34
|
+
set :default_dashboard, nil
|
35
|
+
set :auth_token, nil
|
36
|
+
|
37
|
+
if File.exists?(settings.history_file)
|
38
|
+
set history: YAML.load_file(settings.history_file)
|
39
|
+
else
|
40
|
+
set history: {}
|
41
|
+
end
|
42
|
+
|
43
|
+
%w(javascripts stylesheets fonts images).each do |path|
|
44
|
+
settings.sprockets.append_path("assets/#{path}")
|
45
|
+
end
|
46
|
+
|
47
|
+
['widgets', File.expand_path('../../../javascripts', __FILE__)]. each do |path|
|
48
|
+
settings.sprockets.append_path(path)
|
49
|
+
end
|
50
|
+
|
51
|
+
not_found do
|
52
|
+
send_file File.join(settings.public_folder, '404.html')
|
53
|
+
end
|
54
|
+
|
55
|
+
at_exit do
|
56
|
+
File.write(settings.history_file, settings.history.to_yaml)
|
57
|
+
end
|
58
|
+
|
59
|
+
get '/' do
|
60
|
+
protected!
|
61
|
+
dashboard = settings.default_dashboard || first_dashboard
|
62
|
+
raise Exception.new('There are no dashboards available') if not dashboard
|
63
|
+
|
64
|
+
redirect "/" + dashboard
|
65
|
+
end
|
66
|
+
|
67
|
+
get '/events', provides: 'text/event-stream' do
|
68
|
+
protected!
|
69
|
+
response.headers['X-Accel-Buffering'] = 'no' # Disable buffering for nginx
|
70
|
+
stream :keep_open do |out|
|
71
|
+
settings.connections << out
|
72
|
+
out << latest_events
|
73
|
+
out.callback { settings.connections.delete(out) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
get '/:dashboard' do
|
78
|
+
protected!
|
79
|
+
tilt_html_engines.each do |suffix, _|
|
80
|
+
file = File.join(settings.views, "#{params[:dashboard]}.#{suffix}")
|
81
|
+
return render(suffix.to_sym, params[:dashboard].to_sym) if File.exist? file
|
82
|
+
end
|
83
|
+
|
84
|
+
halt 404
|
85
|
+
end
|
86
|
+
|
87
|
+
post '/dashboards/:id' do
|
88
|
+
request.body.rewind
|
89
|
+
body = JSON.parse(request.body.read)
|
90
|
+
body['dashboard'] ||= params['id']
|
91
|
+
auth_token = body.delete("auth_token")
|
92
|
+
if !settings.auth_token || settings.auth_token == auth_token
|
93
|
+
send_event(params['id'], body, 'dashboards')
|
94
|
+
204 # response without entity body
|
95
|
+
else
|
96
|
+
status 401
|
97
|
+
"Invalid API key\n"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
post '/widgets/:id' do
|
102
|
+
request.body.rewind
|
103
|
+
body = JSON.parse(request.body.read)
|
104
|
+
auth_token = body.delete("auth_token")
|
105
|
+
if !settings.auth_token || settings.auth_token == auth_token
|
106
|
+
send_event(params['id'], body)
|
107
|
+
204 # response without entity body
|
108
|
+
else
|
109
|
+
status 401
|
110
|
+
"Invalid API key\n"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
get '/views/:widget?.html' do
|
115
|
+
protected!
|
116
|
+
tilt_html_engines.each do |suffix, engines|
|
117
|
+
file = File.join(settings.root, "widgets", params[:widget], "#{params[:widget]}.#{suffix}")
|
118
|
+
return engines.first.new(file).render if File.exist? file
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def send_event(id, body, target=nil)
|
123
|
+
body[:id] = id
|
124
|
+
body[:updatedAt] ||= Time.now.to_i
|
125
|
+
event = format_event(body.to_json, target)
|
126
|
+
Sinatra::Application.settings.history[id] = event unless target == 'dashboards'
|
127
|
+
Sinatra::Application.settings.connections.each { |out| out << event }
|
128
|
+
end
|
129
|
+
|
130
|
+
def format_event(body, name=nil)
|
131
|
+
str = ""
|
132
|
+
str << "event: #{name}\n" if name
|
133
|
+
str << "data: #{body}\n\n"
|
134
|
+
end
|
135
|
+
|
136
|
+
def latest_events
|
137
|
+
settings.history.inject("") do |str, (id, body)|
|
138
|
+
str << body
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def first_dashboard
|
143
|
+
files = Dir[File.join(settings.views, '*')].collect { |f| File.basename(f, '.*') }
|
144
|
+
files -= ['layout']
|
145
|
+
files.sort.first
|
146
|
+
end
|
147
|
+
|
148
|
+
def tilt_html_engines
|
149
|
+
Tilt.mappings.select do |_, engines|
|
150
|
+
default_mime_type = engines.first.default_mime_type
|
151
|
+
default_mime_type.nil? || default_mime_type == 'text/html'
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def require_glob(relative_glob)
|
156
|
+
Dir[File.join(settings.root, relative_glob)].each do |file|
|
157
|
+
require file
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
settings_file = File.join(settings.root, 'config/settings.rb')
|
162
|
+
require settings_file if File.exists?(settings_file)
|
163
|
+
|
164
|
+
{}.to_json # Forces your json codec to initialize (in the event that it is lazily loaded). Does this before job threads start.
|
165
|
+
job_path = ENV["JOB_PATH"] || 'jobs'
|
166
|
+
require_glob(File.join('lib', '**', '*.rb'))
|
167
|
+
require_glob(File.join(job_path, '**', '*.rb'))
|
data/lib/dashing/cli.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module Dashing
|
5
|
+
class CLI < Thor
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :auth_token
|
12
|
+
|
13
|
+
def hyphenate(str)
|
14
|
+
return str.downcase if str =~ /^[A-Z-]+$/
|
15
|
+
str.gsub('_', '-').gsub(/\B[A-Z]/, '-\&').squeeze('-').downcase
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
no_tasks do
|
20
|
+
%w(widget dashboard job).each do |type|
|
21
|
+
define_method "generate_#{type}" do |name|
|
22
|
+
@name = Thor::Util.snake_case(name)
|
23
|
+
directory(type.to_sym, "#{type}s")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "new PROJECT_NAME", "Sets up ALL THE THINGS needed for your dashboard project."
|
29
|
+
def new(name)
|
30
|
+
@name = Thor::Util.snake_case(name)
|
31
|
+
directory(:project, @name)
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "generate (widget/dashboard/job) NAME", "Creates a new widget, dashboard, or job."
|
35
|
+
def generate(type, name)
|
36
|
+
public_send("generate_#{type}".to_sym, name)
|
37
|
+
rescue NoMethodError => e
|
38
|
+
puts "Invalid generator. Either use widget, dashboard, or job"
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "install GIST_ID", "Installs a new widget from a gist."
|
42
|
+
def install(gist_id)
|
43
|
+
gist = Downloader.get_gist(gist_id)
|
44
|
+
public_url = "https://gist.github.com/#{gist_id}"
|
45
|
+
|
46
|
+
install_widget_from_gist(gist)
|
47
|
+
|
48
|
+
print set_color("Don't forget to edit the ", :yellow)
|
49
|
+
print set_color("Gemfile ", :yellow, :bold)
|
50
|
+
print set_color("and run ", :yellow)
|
51
|
+
print set_color("bundle install ", :yellow, :bold)
|
52
|
+
say set_color("if needed. More information for this widget can be found at #{public_url}", :yellow)
|
53
|
+
rescue OpenURI::HTTPError => http_error
|
54
|
+
say set_color("Could not find gist at #{public_url}"), :red
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "start", "Starts the server in style!"
|
58
|
+
method_option :job_path, :desc => "Specify the directory where jobs are stored"
|
59
|
+
def start(*args)
|
60
|
+
port_option = args.include?('-p') ? '' : ' -p 3030'
|
61
|
+
args = args.join(' ')
|
62
|
+
command = "bundle exec thin -R config.ru start#{port_option} #{args}"
|
63
|
+
command.prepend "export JOB_PATH=#{options[:job_path]}; " if options[:job_path]
|
64
|
+
run_command(command)
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "stop", "Stops the thin server"
|
68
|
+
def stop
|
69
|
+
command = "bundle exec thin stop"
|
70
|
+
run_command(command)
|
71
|
+
end
|
72
|
+
|
73
|
+
desc "job JOB_NAME AUTH_TOKEN(optional)", "Runs the specified job. Make sure to supply your auth token if you have one set."
|
74
|
+
def job(name, auth_token = "")
|
75
|
+
Dir[File.join(Dir.pwd, 'lib/**/*.rb')].each {|file| require_file(file) }
|
76
|
+
self.class.auth_token = auth_token
|
77
|
+
f = File.join(Dir.pwd, "jobs", "#{name}.rb")
|
78
|
+
require_file(f)
|
79
|
+
end
|
80
|
+
|
81
|
+
# map some commands
|
82
|
+
map 'g' => :generate
|
83
|
+
map 'i' => :install
|
84
|
+
map 's' => :start
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def run_command(command)
|
89
|
+
system(command)
|
90
|
+
end
|
91
|
+
|
92
|
+
def install_widget_from_gist(gist)
|
93
|
+
gist['files'].each do |file, details|
|
94
|
+
if file =~ /\.(html|coffee|scss)\z/
|
95
|
+
widget_name = File.basename(file, '.*')
|
96
|
+
new_path = File.join(Dir.pwd, 'widgets', widget_name, file)
|
97
|
+
create_file(new_path, details['content'])
|
98
|
+
elsif file.end_with?('.rb')
|
99
|
+
new_path = File.join(Dir.pwd, 'jobs', file)
|
100
|
+
create_file(new_path, details['content'])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def require_file(file)
|
106
|
+
require file
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Dashing
|
6
|
+
module Downloader
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def get_gist(gist_id)
|
10
|
+
get_json("https://api.github.com/gists/#{gist_id}")
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_json(url)
|
14
|
+
response = open(url).read
|
15
|
+
JSON.parse(response)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,155 +1,261 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dashing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Beauchamp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
|
-
cert_chain:
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MQ8wDQYDVQQDDAZhZG1p
|
14
|
+
bnMxFzAVBgoJkiaJk/IsZAEZFgdzaG9waWZ5MRMwEQYKCZImiZPyLGQBGRYDY29t
|
15
|
+
MB4XDTE0MDUxNTIwMzM0OFoXDTE1MDUxNTIwMzM0OFowPzEPMA0GA1UEAwwGYWRt
|
16
|
+
aW5zMRcwFQYKCZImiZPyLGQBGRYHc2hvcGlmeTETMBEGCgmSJomT8ixkARkWA2Nv
|
17
|
+
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL0/81O3e1vh5smcwp2G
|
18
|
+
MpLQ6q0kejQLa65bPYPxdzWA1SYOKyGfw+yR9LdFzsuKpwWzKq6zX35lj1IckWS4
|
19
|
+
bNBEQzxmufUxU0XPM02haFB8fOfDJzdXsWte9Ge4IFwahwn68gpMqN+BvxL+KMYz
|
20
|
+
Iut9YmN44d4LZdsENEIO5vmybuG2vYDz7R56qB0PA+Q2P2CdhymsBad2DQs69FBo
|
21
|
+
uico9V6VMYYctL9lCYdzu9IXrOYNTt88suKIVzzAlHOKeN0Ng5qdztFoTR8sfxDr
|
22
|
+
Ydg3KHl5n47wlpgd8R0f/4b5gGxW+v9pyJCgQnLlRu7DedVSvv7+GMtj3g9r3nhJ
|
23
|
+
KqECAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFI/o
|
24
|
+
maf34HXbUOQsdoLHacEKQgunMB0GA1UdEQQWMBSBEmFkbWluc0BzaG9waWZ5LmNv
|
25
|
+
bTAdBgNVHRIEFjAUgRJhZG1pbnNAc2hvcGlmeS5jb20wDQYJKoZIhvcNAQEFBQAD
|
26
|
+
ggEBADkK9aj5T0HPExsov4EoMWFnO+G7RQ28C30VAfKxnL2UxG6i4XMHVs6Xi94h
|
27
|
+
qXFw1ec9Y2eDUqaolT3bviOk9BB197+A8Vz/k7MC6ci2NE+yDDB7HAC8zU6LAx8Y
|
28
|
+
Iqvw7B/PSZ/pz4bUVFlTATif4mi1vO3lidRkdHRtM7UePSn2rUpOi0gtXBP3bLu5
|
29
|
+
YjHJN7wx5cugMEyroKITG5gL0Nxtu21qtOlHX4Hc4KdE2JqzCPOsS4zsZGhgwhPs
|
30
|
+
fl3hbtVFTqbOlwL9vy1fudXcolIE/ZTcxQ+er07ZFZdKCXayR9PPs64heamfn0fp
|
31
|
+
TConQSX2BnZdhIEYW+cKzEC/bLc=
|
32
|
+
-----END CERTIFICATE-----
|
11
33
|
date: 2013-11-21 00:00:00.000000000 Z
|
12
34
|
dependencies:
|
13
35
|
- !ruby/object:Gem::Dependency
|
14
36
|
name: sass
|
15
37
|
requirement: !ruby/object:Gem::Requirement
|
16
38
|
requirements:
|
17
|
-
- -
|
39
|
+
- - "~>"
|
18
40
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
41
|
+
version: 3.2.12
|
20
42
|
type: :runtime
|
21
43
|
prerelease: false
|
22
44
|
version_requirements: !ruby/object:Gem::Requirement
|
23
45
|
requirements:
|
24
|
-
- -
|
46
|
+
- - "~>"
|
25
47
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
48
|
+
version: 3.2.12
|
27
49
|
- !ruby/object:Gem::Dependency
|
28
50
|
name: coffee-script
|
29
51
|
requirement: !ruby/object:Gem::Requirement
|
30
52
|
requirements:
|
31
|
-
- -
|
53
|
+
- - "~>"
|
32
54
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
55
|
+
version: 2.2.0
|
34
56
|
type: :runtime
|
35
57
|
prerelease: false
|
36
58
|
version_requirements: !ruby/object:Gem::Requirement
|
37
59
|
requirements:
|
38
|
-
- -
|
60
|
+
- - "~>"
|
39
61
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
62
|
+
version: 2.2.0
|
41
63
|
- !ruby/object:Gem::Dependency
|
42
64
|
name: execjs
|
43
65
|
requirement: !ruby/object:Gem::Requirement
|
44
66
|
requirements:
|
45
|
-
- -
|
67
|
+
- - "~>"
|
46
68
|
- !ruby/object:Gem::Version
|
47
|
-
version: 2.0.
|
69
|
+
version: 2.0.2
|
48
70
|
type: :runtime
|
49
71
|
prerelease: false
|
50
72
|
version_requirements: !ruby/object:Gem::Requirement
|
51
73
|
requirements:
|
52
|
-
- -
|
74
|
+
- - "~>"
|
53
75
|
- !ruby/object:Gem::Version
|
54
|
-
version: 2.0.
|
76
|
+
version: 2.0.2
|
55
77
|
- !ruby/object:Gem::Dependency
|
56
78
|
name: sinatra
|
57
79
|
requirement: !ruby/object:Gem::Requirement
|
58
80
|
requirements:
|
59
|
-
- -
|
81
|
+
- - "~>"
|
60
82
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
83
|
+
version: 1.4.4
|
62
84
|
type: :runtime
|
63
85
|
prerelease: false
|
64
86
|
version_requirements: !ruby/object:Gem::Requirement
|
65
87
|
requirements:
|
66
|
-
- -
|
88
|
+
- - "~>"
|
67
89
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
90
|
+
version: 1.4.4
|
69
91
|
- !ruby/object:Gem::Dependency
|
70
92
|
name: sinatra-contrib
|
71
93
|
requirement: !ruby/object:Gem::Requirement
|
72
94
|
requirements:
|
73
|
-
- -
|
95
|
+
- - "~>"
|
74
96
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
97
|
+
version: 1.4.2
|
76
98
|
type: :runtime
|
77
99
|
prerelease: false
|
78
100
|
version_requirements: !ruby/object:Gem::Requirement
|
79
101
|
requirements:
|
80
|
-
- -
|
102
|
+
- - "~>"
|
81
103
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
104
|
+
version: 1.4.2
|
83
105
|
- !ruby/object:Gem::Dependency
|
84
106
|
name: thin
|
85
107
|
requirement: !ruby/object:Gem::Requirement
|
86
108
|
requirements:
|
87
|
-
- -
|
109
|
+
- - "~>"
|
88
110
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
111
|
+
version: 1.6.1
|
90
112
|
type: :runtime
|
91
113
|
prerelease: false
|
92
114
|
version_requirements: !ruby/object:Gem::Requirement
|
93
115
|
requirements:
|
94
|
-
- -
|
116
|
+
- - "~>"
|
95
117
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
118
|
+
version: 1.6.1
|
97
119
|
- !ruby/object:Gem::Dependency
|
98
120
|
name: rufus-scheduler
|
99
121
|
requirement: !ruby/object:Gem::Requirement
|
100
122
|
requirements:
|
101
|
-
- - ~>
|
123
|
+
- - "~>"
|
102
124
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
125
|
+
version: 3.0.3
|
104
126
|
type: :runtime
|
105
127
|
prerelease: false
|
106
128
|
version_requirements: !ruby/object:Gem::Requirement
|
107
129
|
requirements:
|
108
|
-
- - ~>
|
130
|
+
- - "~>"
|
109
131
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
132
|
+
version: 3.0.3
|
111
133
|
- !ruby/object:Gem::Dependency
|
112
134
|
name: thor
|
113
135
|
requirement: !ruby/object:Gem::Requirement
|
114
136
|
requirements:
|
115
|
-
- -
|
137
|
+
- - "~>"
|
116
138
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
139
|
+
version: 0.18.1
|
118
140
|
type: :runtime
|
119
141
|
prerelease: false
|
120
142
|
version_requirements: !ruby/object:Gem::Requirement
|
121
143
|
requirements:
|
122
|
-
- -
|
144
|
+
- - "~>"
|
123
145
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
146
|
+
version: 0.18.1
|
125
147
|
- !ruby/object:Gem::Dependency
|
126
148
|
name: sprockets
|
127
149
|
requirement: !ruby/object:Gem::Requirement
|
128
150
|
requirements:
|
129
|
-
- -
|
151
|
+
- - "~>"
|
130
152
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
153
|
+
version: 2.10.1
|
132
154
|
type: :runtime
|
133
155
|
prerelease: false
|
134
156
|
version_requirements: !ruby/object:Gem::Requirement
|
135
157
|
requirements:
|
136
|
-
- -
|
158
|
+
- - "~>"
|
137
159
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
160
|
+
version: 2.10.1
|
139
161
|
- !ruby/object:Gem::Dependency
|
140
162
|
name: rack
|
141
163
|
requirement: !ruby/object:Gem::Requirement
|
142
164
|
requirements:
|
143
|
-
- -
|
165
|
+
- - "~>"
|
144
166
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
167
|
+
version: 1.5.2
|
146
168
|
type: :runtime
|
147
169
|
prerelease: false
|
148
170
|
version_requirements: !ruby/object:Gem::Requirement
|
149
171
|
requirements:
|
150
|
-
- -
|
172
|
+
- - "~>"
|
151
173
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
174
|
+
version: 1.5.2
|
175
|
+
- !ruby/object:Gem::Dependency
|
176
|
+
name: rake
|
177
|
+
requirement: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - "~>"
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: 10.1.0
|
182
|
+
type: :development
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - "~>"
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: 10.1.0
|
189
|
+
- !ruby/object:Gem::Dependency
|
190
|
+
name: haml
|
191
|
+
requirement: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - "~>"
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: 4.0.4
|
196
|
+
type: :development
|
197
|
+
prerelease: false
|
198
|
+
version_requirements: !ruby/object:Gem::Requirement
|
199
|
+
requirements:
|
200
|
+
- - "~>"
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
version: 4.0.4
|
203
|
+
- !ruby/object:Gem::Dependency
|
204
|
+
name: minitest
|
205
|
+
requirement: !ruby/object:Gem::Requirement
|
206
|
+
requirements:
|
207
|
+
- - "~>"
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: 5.2.0
|
210
|
+
type: :development
|
211
|
+
prerelease: false
|
212
|
+
version_requirements: !ruby/object:Gem::Requirement
|
213
|
+
requirements:
|
214
|
+
- - "~>"
|
215
|
+
- !ruby/object:Gem::Version
|
216
|
+
version: 5.2.0
|
217
|
+
- !ruby/object:Gem::Dependency
|
218
|
+
name: mocha
|
219
|
+
requirement: !ruby/object:Gem::Requirement
|
220
|
+
requirements:
|
221
|
+
- - "~>"
|
222
|
+
- !ruby/object:Gem::Version
|
223
|
+
version: 0.14.0
|
224
|
+
type: :development
|
225
|
+
prerelease: false
|
226
|
+
version_requirements: !ruby/object:Gem::Requirement
|
227
|
+
requirements:
|
228
|
+
- - "~>"
|
229
|
+
- !ruby/object:Gem::Version
|
230
|
+
version: 0.14.0
|
231
|
+
- !ruby/object:Gem::Dependency
|
232
|
+
name: fakeweb
|
233
|
+
requirement: !ruby/object:Gem::Requirement
|
234
|
+
requirements:
|
235
|
+
- - "~>"
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: 1.3.0
|
238
|
+
type: :development
|
239
|
+
prerelease: false
|
240
|
+
version_requirements: !ruby/object:Gem::Requirement
|
241
|
+
requirements:
|
242
|
+
- - "~>"
|
243
|
+
- !ruby/object:Gem::Version
|
244
|
+
version: 1.3.0
|
245
|
+
- !ruby/object:Gem::Dependency
|
246
|
+
name: simplecov
|
247
|
+
requirement: !ruby/object:Gem::Requirement
|
248
|
+
requirements:
|
249
|
+
- - "~>"
|
250
|
+
- !ruby/object:Gem::Version
|
251
|
+
version: 0.8.2
|
252
|
+
type: :development
|
253
|
+
prerelease: false
|
254
|
+
version_requirements: !ruby/object:Gem::Requirement
|
255
|
+
requirements:
|
256
|
+
- - "~>"
|
257
|
+
- !ruby/object:Gem::Version
|
258
|
+
version: 0.8.2
|
153
259
|
description: This framework lets you build & easily layout dashboards with your own
|
154
260
|
custom widgets. Use it to make a status boards for your ops team, or use it to track
|
155
261
|
signups, conversion rates, or whatever else metrics you'd like to see in one spot.
|
@@ -163,13 +269,21 @@ extensions: []
|
|
163
269
|
extra_rdoc_files: []
|
164
270
|
files:
|
165
271
|
- README.md
|
272
|
+
- bin/dashing
|
166
273
|
- javascripts/batman.jquery.js
|
167
274
|
- javascripts/batman.js
|
168
275
|
- javascripts/dashing.coffee
|
169
276
|
- javascripts/es5-shim.js
|
170
277
|
- javascripts/jquery.js
|
278
|
+
- lib/dashing.rb
|
279
|
+
- lib/dashing/app.rb
|
280
|
+
- lib/dashing/cli.rb
|
281
|
+
- lib/dashing/downloader.rb
|
171
282
|
- templates/dashboard/%name%.erb.tt
|
172
283
|
- templates/job/%name%.rb
|
284
|
+
- templates/project/.gitignore
|
285
|
+
- templates/project/Gemfile
|
286
|
+
- templates/project/README.md
|
173
287
|
- templates/project/assets/fonts/fontawesome-webfont.eot
|
174
288
|
- templates/project/assets/fonts/fontawesome-webfont.svg
|
175
289
|
- templates/project/assets/fonts/fontawesome-webfont.ttf
|
@@ -189,14 +303,13 @@ files:
|
|
189
303
|
- templates/project/dashboards/layout.erb
|
190
304
|
- templates/project/dashboards/sample.erb
|
191
305
|
- templates/project/dashboards/sampletv.erb
|
192
|
-
- templates/project/Gemfile
|
193
306
|
- templates/project/jobs/buzzwords.rb
|
194
307
|
- templates/project/jobs/convergence.rb
|
195
308
|
- templates/project/jobs/sample.rb
|
196
309
|
- templates/project/jobs/twitter.rb
|
310
|
+
- templates/project/lib/.empty_directory
|
197
311
|
- templates/project/public/404.html
|
198
312
|
- templates/project/public/favicon.ico
|
199
|
-
- templates/project/README.md
|
200
313
|
- templates/project/widgets/clock/clock.coffee
|
201
314
|
- templates/project/widgets/clock/clock.html
|
202
315
|
- templates/project/widgets/clock/clock.scss
|
@@ -227,10 +340,6 @@ files:
|
|
227
340
|
- templates/widget/%name%/%name%.coffee.tt
|
228
341
|
- templates/widget/%name%/%name%.html
|
229
342
|
- templates/widget/%name%/%name%.scss.tt
|
230
|
-
- templates/project/.gitignore
|
231
|
-
- templates/project/lib/.empty_directory
|
232
|
-
- lib/dashing.rb
|
233
|
-
- bin/dashing
|
234
343
|
homepage: http://shopify.github.com/dashing
|
235
344
|
licenses: []
|
236
345
|
metadata: {}
|
@@ -240,17 +349,17 @@ require_paths:
|
|
240
349
|
- lib
|
241
350
|
required_ruby_version: !ruby/object:Gem::Requirement
|
242
351
|
requirements:
|
243
|
-
- -
|
352
|
+
- - ">="
|
244
353
|
- !ruby/object:Gem::Version
|
245
354
|
version: '0'
|
246
355
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
247
356
|
requirements:
|
248
|
-
- -
|
357
|
+
- - ">="
|
249
358
|
- !ruby/object:Gem::Version
|
250
359
|
version: '0'
|
251
360
|
requirements: []
|
252
361
|
rubyforge_project:
|
253
|
-
rubygems_version: 2.0
|
362
|
+
rubygems_version: 2.2.0
|
254
363
|
signing_key:
|
255
364
|
specification_version: 4
|
256
365
|
summary: The exceptionally handsome dashboard framework.
|
metadata.gz.sig
ADDED