watch_tower 0.0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -0
- data/.todo +33 -0
- data/.travis.yml +14 -0
- data/Gemfile +43 -0
- data/Guardfile +25 -0
- data/MIT-LICENSE +20 -0
- data/README.md +38 -0
- data/Rakefile +8 -0
- data/TODO +17 -0
- data/bin/watchtower +10 -0
- data/ci/adapters/jruby-mysql.yml +8 -0
- data/ci/adapters/jruby-postgresql.yml +6 -0
- data/ci/adapters/jruby-sqlite.yml +6 -0
- data/ci/adapters/ruby-mysql.yml +8 -0
- data/ci/adapters/ruby-postgresql.yml +6 -0
- data/ci/adapters/ruby-sqlite.yml +6 -0
- data/ci/travis.rb +102 -0
- data/lib/watch_tower.rb +60 -0
- data/lib/watch_tower/appscript.rb +22 -0
- data/lib/watch_tower/cli.rb +15 -0
- data/lib/watch_tower/cli/.gitkeep +0 -0
- data/lib/watch_tower/cli/install.rb +63 -0
- data/lib/watch_tower/cli/open.rb +24 -0
- data/lib/watch_tower/cli/start.rb +140 -0
- data/lib/watch_tower/config.rb +38 -0
- data/lib/watch_tower/core_ext.rb +4 -0
- data/lib/watch_tower/core_ext/.gitkeep +0 -0
- data/lib/watch_tower/editor.rb +17 -0
- data/lib/watch_tower/editor/.gitkeep +0 -0
- data/lib/watch_tower/editor/base_appscript.rb +34 -0
- data/lib/watch_tower/editor/base_ps.rb +6 -0
- data/lib/watch_tower/editor/textmate.rb +17 -0
- data/lib/watch_tower/editor/xcode.rb +22 -0
- data/lib/watch_tower/errors.rb +25 -0
- data/lib/watch_tower/eye.rb +79 -0
- data/lib/watch_tower/project.rb +14 -0
- data/lib/watch_tower/project/.gitkeep +0 -0
- data/lib/watch_tower/project/any_based.rb +22 -0
- data/lib/watch_tower/project/git_based.rb +86 -0
- data/lib/watch_tower/project/init.rb +38 -0
- data/lib/watch_tower/project/path_based.rb +144 -0
- data/lib/watch_tower/server.rb +62 -0
- data/lib/watch_tower/server/.gitkeep +0 -0
- data/lib/watch_tower/server/app.rb +37 -0
- data/lib/watch_tower/server/assets/images/WatchTower.jpg +0 -0
- data/lib/watch_tower/server/assets/images/percentage.png +0 -0
- data/lib/watch_tower/server/assets/javascripts/application.js +3 -0
- data/lib/watch_tower/server/assets/javascripts/percentage.coffee +8 -0
- data/lib/watch_tower/server/assets/stylesheets/application.css +7 -0
- data/lib/watch_tower/server/assets/stylesheets/global.sass +71 -0
- data/lib/watch_tower/server/assets/stylesheets/project.sass +59 -0
- data/lib/watch_tower/server/configurations.rb +10 -0
- data/lib/watch_tower/server/configurations/asset.rb +43 -0
- data/lib/watch_tower/server/database.rb +105 -0
- data/lib/watch_tower/server/db/migrate/001_create_projects.rb +15 -0
- data/lib/watch_tower/server/db/migrate/002_create_files.rb +16 -0
- data/lib/watch_tower/server/db/migrate/003_create_time_entries.rb +12 -0
- data/lib/watch_tower/server/db/migrate/004_create_durations.rb +14 -0
- data/lib/watch_tower/server/db/migrate/005_add_hash_to_time_entries.rb +6 -0
- data/lib/watch_tower/server/db/migrate/006_add_hash_to_files.rb +6 -0
- data/lib/watch_tower/server/decorator.rb +21 -0
- data/lib/watch_tower/server/decorator/application_decorator.rb +91 -0
- data/lib/watch_tower/server/decorator/file_decorator.rb +38 -0
- data/lib/watch_tower/server/decorator/project_decorator.rb +51 -0
- data/lib/watch_tower/server/helpers.rb +13 -0
- data/lib/watch_tower/server/helpers/asset.rb +29 -0
- data/lib/watch_tower/server/helpers/improved_partials.rb +41 -0
- data/lib/watch_tower/server/models/duration.rb +11 -0
- data/lib/watch_tower/server/models/file.rb +31 -0
- data/lib/watch_tower/server/models/project.rb +17 -0
- data/lib/watch_tower/server/models/time_entry.rb +64 -0
- data/lib/watch_tower/server/public/assets/WatchTower-4d6de11e1bd34165ad91ac46fb711bf3.jpg +0 -0
- data/lib/watch_tower/server/public/assets/application-7829b53b5ece1a16d22dc3d00f329023.css +107 -0
- data/lib/watch_tower/server/public/assets/application-e0e6b7731aade460f680331e65cf0682.js +9359 -0
- data/lib/watch_tower/server/public/assets/percentage-d8589e21a5fc85d32a445f531ff8ab95.png +0 -0
- data/lib/watch_tower/server/vendor/assets/javascripts/jquery-ui.js +11729 -0
- data/lib/watch_tower/server/vendor/assets/javascripts/jquery.js +8981 -0
- data/lib/watch_tower/server/vendor/assets/javascripts/jquery_ujs.js +363 -0
- data/lib/watch_tower/server/views/.gitkeep +0 -0
- data/lib/watch_tower/server/views/_file.haml +9 -0
- data/lib/watch_tower/server/views/_project.haml +13 -0
- data/lib/watch_tower/server/views/index.haml +7 -0
- data/lib/watch_tower/server/views/layout.haml +32 -0
- data/lib/watch_tower/server/views/project.haml +12 -0
- data/lib/watch_tower/templates/config.yml +146 -0
- data/lib/watch_tower/templates/watchtower.plist +23 -0
- data/lib/watch_tower/version.rb +8 -0
- data/spec/factories.rb +45 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/active_record.rb +44 -0
- data/spec/support/factory_girl.rb +6 -0
- data/spec/support/launchy.rb +3 -0
- data/spec/support/sinatra.rb +10 -0
- data/spec/support/timecop.rb +7 -0
- data/spec/watch_tower/appscript_spec.rb +6 -0
- data/spec/watch_tower/cli/install_spec.rb +16 -0
- data/spec/watch_tower/cli/open_spec.rb +14 -0
- data/spec/watch_tower/cli/start_spec.rb +17 -0
- data/spec/watch_tower/cli_spec.rb +15 -0
- data/spec/watch_tower/config_spec.rb +25 -0
- data/spec/watch_tower/editor/textmate_spec.rb +43 -0
- data/spec/watch_tower/editor/xcode_spec.rb +43 -0
- data/spec/watch_tower/editor_spec.rb +19 -0
- data/spec/watch_tower/eye_spec.rb +130 -0
- data/spec/watch_tower/project/git_based_spec.rb +131 -0
- data/spec/watch_tower/project/path_based_spec.rb +111 -0
- data/spec/watch_tower/project_spec.rb +82 -0
- data/spec/watch_tower/server/app_spec.rb +186 -0
- data/spec/watch_tower/server/decorator/project_decorator_spec.rb +60 -0
- data/spec/watch_tower/server/models/file_spec.rb +284 -0
- data/spec/watch_tower/server/models/project_spec.rb +165 -0
- data/spec/watch_tower/server/models/time_entry_spec.rb +37 -0
- data/spec/watch_tower/server_spec.rb +4 -0
- data/watch_tower.gemspec +80 -0
- metadata +450 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module WatchTower
|
4
|
+
module Eye
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Start the watch loop
|
8
|
+
#
|
9
|
+
# @param [Hash] options
|
10
|
+
# @raise [EyeError]
|
11
|
+
def start(options = {})
|
12
|
+
LOG.debug("#{__FILE__}:#{__LINE__}: The Eye loop has just started")
|
13
|
+
loop do
|
14
|
+
# Try getting the mtime of the document opened by each editor in the
|
15
|
+
# editors list.
|
16
|
+
Editor.editors.each do |editor|
|
17
|
+
# Create an instance of the editor
|
18
|
+
# TODO: Should be used as a class instead
|
19
|
+
editor = editor.new
|
20
|
+
# Check if the editor is running
|
21
|
+
if editor.is_running?
|
22
|
+
LOG.debug("#{__FILE__}:#{__LINE__}: #{editor.to_s} is running")
|
23
|
+
# Get the currently being edited file from the editor
|
24
|
+
files_paths = editor.current_paths
|
25
|
+
files_paths.each do |file_path|
|
26
|
+
begin
|
27
|
+
# Get the file_hash of the file
|
28
|
+
file_hash = Digest::SHA1.file(file_path).hexdigest
|
29
|
+
# Create a project from the file_path
|
30
|
+
project = Project.new_from_path(file_path)
|
31
|
+
rescue PathNotUnderCodePath
|
32
|
+
LOG.debug("#{__FILE__}:#{__LINE__ - 2}: The file '#{file_path}' is not located under '#{Config[:code_path]}', it has been ignored")
|
33
|
+
next
|
34
|
+
rescue FileNotFound
|
35
|
+
LOG.debug "#{__FILE__}:#{__LINE__ - 5}: The file '#{file_path}' does not exist, it has been ignored"
|
36
|
+
next
|
37
|
+
end
|
38
|
+
|
39
|
+
begin
|
40
|
+
# Create (or fetch) a project
|
41
|
+
project_model = Server::Project.find_or_create_by_name_and_path(project.name, project.path)
|
42
|
+
|
43
|
+
# Create (or fetch) a file
|
44
|
+
file_model = project_model.files.find_or_create_by_path(file_path)
|
45
|
+
begin
|
46
|
+
# Create a time entry
|
47
|
+
file_model.time_entries.create!(mtime: File.stat(file_path).mtime, file_hash: file_hash)
|
48
|
+
rescue ActiveRecord::RecordInvalid => e
|
49
|
+
# This should happen if the mtime is already present
|
50
|
+
end
|
51
|
+
rescue ActiveRecord::RecordInvalid => e
|
52
|
+
# This should not happen
|
53
|
+
LOG.fatal("#{__FILE__}:#{__LINE__}: #{e}")
|
54
|
+
$close_eye = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
else
|
58
|
+
LOG.debug("#{__FILE__}:#{__LINE__}: #{editor.to_s} is not running")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# If $stop global is set, please stop, otherwise sleep for 30 seconds.
|
63
|
+
if $close_eye
|
64
|
+
LOG.debug("#{__FILE__}:#{__LINE__}: Closing eye has been requested, end the loop")
|
65
|
+
break
|
66
|
+
else
|
67
|
+
sleep 10
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Start the Eye, a method invoked from the Watch Tower command line interface
|
73
|
+
#
|
74
|
+
# @param [Hash] options
|
75
|
+
def start!(options = {})
|
76
|
+
start(options)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
File without changes
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module WatchTower
|
2
|
+
class Project
|
3
|
+
module AnyBased
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.send :include, InstanceMethods
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
attr_reader :name, :path
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
protected
|
16
|
+
def expand_path(path)
|
17
|
+
File.expand_path(path)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'git'
|
2
|
+
|
3
|
+
module WatchTower
|
4
|
+
class Project
|
5
|
+
module GitBased
|
6
|
+
extend self
|
7
|
+
include AnyBased
|
8
|
+
|
9
|
+
# Cache for working_directory by path
|
10
|
+
# The key is the path to a file, the value is the working directory of
|
11
|
+
# this path
|
12
|
+
@@working_cache = Hash.new
|
13
|
+
|
14
|
+
# Cache for project_name by path
|
15
|
+
# The key is the path to a file, the value is the project's name
|
16
|
+
@@project_name_cache = Hash.new
|
17
|
+
|
18
|
+
# Cache for project git path
|
19
|
+
# The key is the path to a file, the value is the project's parts
|
20
|
+
@@project_git_folder_path = Hash.new
|
21
|
+
|
22
|
+
# Check if the path is under Git
|
23
|
+
#
|
24
|
+
# @param path The path we should check if it's under Git control
|
25
|
+
# @param [Hash] options A hash of options
|
26
|
+
# @return boolean
|
27
|
+
def active_for_path?(path, options = {})
|
28
|
+
path = expand_path path
|
29
|
+
project_git_folder_path(path).present?
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return the working directory (the project's path if you will) from a path
|
33
|
+
# to any file inside the project
|
34
|
+
#
|
35
|
+
# @param path The path to look the project path from
|
36
|
+
# @param [Hash] options A hash of options
|
37
|
+
# @return [String] the project's folder
|
38
|
+
def working_directory(path, options = {})
|
39
|
+
path = expand_path path
|
40
|
+
return @@working_cache[path] if @@working_cache.key?(path)
|
41
|
+
|
42
|
+
@@working_cache[path] = File.dirname(project_git_folder_path(path))
|
43
|
+
@@working_cache[path]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return the project's name from a path to any file inside the project
|
47
|
+
#
|
48
|
+
# @param path The path to look the project path from
|
49
|
+
# @param [Hash] options A hash of options
|
50
|
+
# @return [String] the project's name
|
51
|
+
def project_name(path, options = {})
|
52
|
+
path = expand_path path
|
53
|
+
return @@project_name_cache[path] if @@project_name_cache.key?(path)
|
54
|
+
|
55
|
+
@@project_name_cache[path] = File.basename working_directory(path, options)
|
56
|
+
@@project_name_cache[path]
|
57
|
+
end
|
58
|
+
|
59
|
+
def head(path)
|
60
|
+
log(path)
|
61
|
+
end
|
62
|
+
|
63
|
+
def log(path)
|
64
|
+
g = ::Git.open path
|
65
|
+
g.log.first
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
def project_git_folder_path(path)
|
70
|
+
return @@project_git_folder_path[path] if @@project_git_folder_path.key?(path)
|
71
|
+
|
72
|
+
# Define the start
|
73
|
+
n = 0
|
74
|
+
# Define the maximum search folder
|
75
|
+
max_n = path.split('/').size
|
76
|
+
|
77
|
+
until File.exists?(File.expand_path File.join(path, (%w{..} * n).flatten, '.git')) || n > max_n
|
78
|
+
n = n + 1
|
79
|
+
end
|
80
|
+
|
81
|
+
@@project_git_folder_path[path] = n <= max_n ? File.expand_path(File.join(path, (%w{..} * n).flatten, '.git')) : nil
|
82
|
+
@@project_git_folder_path[path]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module WatchTower
|
2
|
+
class Project
|
3
|
+
module Init
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
base.send :include, InstanceMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
# Create a new project from a path (to a file or a folder)
|
11
|
+
#
|
12
|
+
# @param [String] path, the path to the file
|
13
|
+
# @return [Project] a new initialized project
|
14
|
+
def new_from_path(path)
|
15
|
+
raise FileNotFound unless path && File.exists?(path)
|
16
|
+
LOG.debug("#{__FILE__}:#{__LINE__}: Creating a project from #{path}")
|
17
|
+
if GitBased.active_for_path?(path)
|
18
|
+
Project.new GitBased.project_name(path), GitBased.working_directory(path)
|
19
|
+
else
|
20
|
+
Project.new PathBased.project_name(path), PathBased.working_directory(path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module InstanceMethods
|
26
|
+
# Initialize a project using a name and a path
|
27
|
+
#
|
28
|
+
# @param [String] name: the name of the project
|
29
|
+
# @param [String] path: The path of the project
|
30
|
+
def initialize(name, path)
|
31
|
+
LOG.debug("#{__FILE__}:#{__LINE__}: Created project #{name} located at #{path}")
|
32
|
+
@name = name
|
33
|
+
@path = path
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module WatchTower
|
2
|
+
class Project
|
3
|
+
# The module contains Path specific project methods, methods like name, path
|
4
|
+
# The module can be included into another module/class or used on it's own,
|
5
|
+
# it does extend itself so any methods defined here is available to both class
|
6
|
+
# and instance level
|
7
|
+
module PathBased
|
8
|
+
include AnyBased
|
9
|
+
extend self
|
10
|
+
|
11
|
+
# Cache for working_directory by path
|
12
|
+
# The key is the path to a file, the value is the working directory of
|
13
|
+
# this path
|
14
|
+
@@working_cache = Hash.new
|
15
|
+
|
16
|
+
# Cache for project_name by path
|
17
|
+
# The key is the path to a file, the value is the project's name
|
18
|
+
@@project_name_cache = Hash.new
|
19
|
+
|
20
|
+
# Cache for project path parts
|
21
|
+
# The key is the path to a file, the value is the project's parts
|
22
|
+
@@project_path_part_cache = Hash.new
|
23
|
+
|
24
|
+
# Return the working directory (the project's path if you will) from a path
|
25
|
+
# to any file inside the project
|
26
|
+
#
|
27
|
+
# @param [String] path The path to look the project path from
|
28
|
+
# @param [Hash] options A hash of options
|
29
|
+
# @return [String] the project's folder
|
30
|
+
def working_directory(path, options = {})
|
31
|
+
return @@working_cache[path] if @@working_cache.key?(path)
|
32
|
+
|
33
|
+
|
34
|
+
@@working_cache[path] = project_path_from_nested_path(code_path(options),
|
35
|
+
path, nested_project_layers(options))
|
36
|
+
@@working_cache[path]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return the project's name from a path to any file inside the project
|
40
|
+
#
|
41
|
+
# @param path The path to look the project path from
|
42
|
+
# @param [Hash] options A hash of options
|
43
|
+
# @return [String] the project's name
|
44
|
+
def project_name(path, options = {})
|
45
|
+
return @@project_name_cache[path] if @@project_name_cache.key?(path)
|
46
|
+
|
47
|
+
@@project_name_cache[path] = project_name_from_nested_path(code_path(options),
|
48
|
+
path, nested_project_layers(options))
|
49
|
+
@@project_name_cache[path]
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
# Get the code path from the options, if not found use the one from the
|
55
|
+
# configurations
|
56
|
+
#
|
57
|
+
# @param [Hash] options
|
58
|
+
# @return [String] The Code path
|
59
|
+
def code_path(options = {})
|
60
|
+
options[:code_path] || Config[:code_path]
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get the nested_project_layers from the options, if not found use the
|
64
|
+
# one from the configurations
|
65
|
+
#
|
66
|
+
# @param [Hash] options
|
67
|
+
# @return [String] The nested_project_layers
|
68
|
+
def nested_project_layers(options = {})
|
69
|
+
options[:nested_project_layers] || Config[:nested_project_layers]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Taken from timetap
|
73
|
+
# https://github.com/elia/timetap/blob/master/lib/time_tap/project.rb#L40
|
74
|
+
#
|
75
|
+
# Find out the path parts of the project that's currently being worked on,
|
76
|
+
# under the code path, it uses the param nested_project_layers to determine
|
77
|
+
# the project name from the entire expanded path to any file under the
|
78
|
+
# project
|
79
|
+
#
|
80
|
+
# nested project layers works "how many folders inside your code folder
|
81
|
+
# do you keep projects.
|
82
|
+
#
|
83
|
+
# For example, if your directory structure looks like:
|
84
|
+
# ~/Code/
|
85
|
+
# Clients/
|
86
|
+
# AcmeCorp/
|
87
|
+
# website/
|
88
|
+
# intranet
|
89
|
+
# BetaCorp/
|
90
|
+
# skunkworks/
|
91
|
+
# OpenSource/
|
92
|
+
# project_one/
|
93
|
+
# timetap/
|
94
|
+
#
|
95
|
+
# A nested_project_layers setting of 2 would mean we track "AcmeCorp", "BetaCorp", and everything
|
96
|
+
# under OpenSource, as their own projects
|
97
|
+
#
|
98
|
+
# @param code The path you store all the projects under
|
99
|
+
# @param path The path to look the project name from
|
100
|
+
# @param nested_project_layers How many folders, defaults to 2
|
101
|
+
# @return [Array] The project path's parts
|
102
|
+
# @raise [WatchTower::PathNotUnderCodePath] if the path is not nested under code
|
103
|
+
def project_path_part(code, path, nested_project_layers = 2)
|
104
|
+
return @@project_path_part_cache[path] if @@project_path_part_cache.key?(path)
|
105
|
+
|
106
|
+
# Expand pathes
|
107
|
+
code = expand_path code
|
108
|
+
path = expand_path path
|
109
|
+
|
110
|
+
regex_suffix = "([^/]+)"
|
111
|
+
regex_suffix = [regex_suffix] * nested_project_layers
|
112
|
+
regex_suffix = regex_suffix.join("/")
|
113
|
+
|
114
|
+
path.scan(%r{(#{code})/#{regex_suffix}}).flatten.collect(&:chomp).
|
115
|
+
tap { |r| raise PathNotUnderCodePath unless r.any? }.
|
116
|
+
tap { |ppp| @@project_path_part_cache[path] = ppp }
|
117
|
+
end
|
118
|
+
|
119
|
+
# Find out the project's name
|
120
|
+
# See #project_path_part
|
121
|
+
#
|
122
|
+
# @param code The path you store all the projects under
|
123
|
+
# @param path The path to look the project name from
|
124
|
+
# @param nested_project_layers How many folders, defaults to 2
|
125
|
+
# @return [String] The project's name
|
126
|
+
# @raise [WatchTower::PathNotUnderCodePath] if the path is not nested under code
|
127
|
+
def project_name_from_nested_path(code, path, nested_project_layers = 2)
|
128
|
+
project_path_part(code, path, nested_project_layers)[nested_project_layers]
|
129
|
+
end
|
130
|
+
|
131
|
+
# Find out the project's path
|
132
|
+
# See #project_path_part
|
133
|
+
#
|
134
|
+
# @param code The path you store all the projects under
|
135
|
+
# @param path The path to look the project name from
|
136
|
+
# @param nested_project_layers How many folders, defaults to 2
|
137
|
+
# @return [String] The project's path
|
138
|
+
# @raise [WatchTower::PathNotUnderCodePath] if the path is not nested under code
|
139
|
+
def project_path_from_nested_path(code, path, nested_project_layers = 2)
|
140
|
+
project_path_part(code, path, nested_project_layers)[0..nested_project_layers].join('/')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module WatchTower
|
2
|
+
module Server
|
3
|
+
extend ::ActiveSupport::Autoload
|
4
|
+
|
5
|
+
autoload :Database
|
6
|
+
autoload :Duration, ::File.join(MODELS_PATH, 'duration.rb')
|
7
|
+
autoload :Project, ::File.join(MODELS_PATH, 'project.rb')
|
8
|
+
autoload :File, ::File.join(MODELS_PATH, 'file.rb')
|
9
|
+
autoload :TimeEntry, ::File.join(MODELS_PATH, 'time_entry.rb')
|
10
|
+
autoload :Helpers
|
11
|
+
autoload :Configurations
|
12
|
+
autoload :Decorator
|
13
|
+
autoload :App
|
14
|
+
|
15
|
+
# Start the server
|
16
|
+
# This method starts the database and then starts the server
|
17
|
+
#
|
18
|
+
# @param [Hash] options
|
19
|
+
def self.start(options = {})
|
20
|
+
# Start the Database
|
21
|
+
Database.start!(options)
|
22
|
+
|
23
|
+
# Start the Sinatra application
|
24
|
+
start_web_server(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Start the Server, a method invoked from the Watch Tower command line interface
|
28
|
+
#
|
29
|
+
# @param [Hash] options
|
30
|
+
def self.start!(options = {})
|
31
|
+
start(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
# Start the web_server
|
36
|
+
# This method starts the web server (The Sinatra app)
|
37
|
+
#
|
38
|
+
# @param [Hash] options
|
39
|
+
def self.start_web_server(options = {})
|
40
|
+
LOG.debug("#{__FILE__}:#{__LINE__}: Starting the Sinatra App")
|
41
|
+
|
42
|
+
# Abort execution if the Thread raised an error.
|
43
|
+
Thread.abort_on_exception = true
|
44
|
+
|
45
|
+
WatchTower.threads[:web_server] = Thread.new do
|
46
|
+
LOG.debug("#{__FILE__}:#{__LINE__}: Starting a new Thread for the web server.")
|
47
|
+
|
48
|
+
begin
|
49
|
+
LOG.debug("#{__FILE__}:#{__LINE__}: Starting the web server in the new Thread.")
|
50
|
+
|
51
|
+
# Start the server
|
52
|
+
App.run!(options)
|
53
|
+
|
54
|
+
LOG.debug("#{__FILE__}:#{__LINE__}: The server has stopped.")
|
55
|
+
rescue Exception => e
|
56
|
+
LOG.fatal "#{__FILE__}:#{__LINE__ - 4}: #{e}"
|
57
|
+
raise e
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|