watch_tower 0.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/.gitignore +10 -0
  2. data/.todo +33 -0
  3. data/.travis.yml +14 -0
  4. data/Gemfile +43 -0
  5. data/Guardfile +25 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +38 -0
  8. data/Rakefile +8 -0
  9. data/TODO +17 -0
  10. data/bin/watchtower +10 -0
  11. data/ci/adapters/jruby-mysql.yml +8 -0
  12. data/ci/adapters/jruby-postgresql.yml +6 -0
  13. data/ci/adapters/jruby-sqlite.yml +6 -0
  14. data/ci/adapters/ruby-mysql.yml +8 -0
  15. data/ci/adapters/ruby-postgresql.yml +6 -0
  16. data/ci/adapters/ruby-sqlite.yml +6 -0
  17. data/ci/travis.rb +102 -0
  18. data/lib/watch_tower.rb +60 -0
  19. data/lib/watch_tower/appscript.rb +22 -0
  20. data/lib/watch_tower/cli.rb +15 -0
  21. data/lib/watch_tower/cli/.gitkeep +0 -0
  22. data/lib/watch_tower/cli/install.rb +63 -0
  23. data/lib/watch_tower/cli/open.rb +24 -0
  24. data/lib/watch_tower/cli/start.rb +140 -0
  25. data/lib/watch_tower/config.rb +38 -0
  26. data/lib/watch_tower/core_ext.rb +4 -0
  27. data/lib/watch_tower/core_ext/.gitkeep +0 -0
  28. data/lib/watch_tower/editor.rb +17 -0
  29. data/lib/watch_tower/editor/.gitkeep +0 -0
  30. data/lib/watch_tower/editor/base_appscript.rb +34 -0
  31. data/lib/watch_tower/editor/base_ps.rb +6 -0
  32. data/lib/watch_tower/editor/textmate.rb +17 -0
  33. data/lib/watch_tower/editor/xcode.rb +22 -0
  34. data/lib/watch_tower/errors.rb +25 -0
  35. data/lib/watch_tower/eye.rb +79 -0
  36. data/lib/watch_tower/project.rb +14 -0
  37. data/lib/watch_tower/project/.gitkeep +0 -0
  38. data/lib/watch_tower/project/any_based.rb +22 -0
  39. data/lib/watch_tower/project/git_based.rb +86 -0
  40. data/lib/watch_tower/project/init.rb +38 -0
  41. data/lib/watch_tower/project/path_based.rb +144 -0
  42. data/lib/watch_tower/server.rb +62 -0
  43. data/lib/watch_tower/server/.gitkeep +0 -0
  44. data/lib/watch_tower/server/app.rb +37 -0
  45. data/lib/watch_tower/server/assets/images/WatchTower.jpg +0 -0
  46. data/lib/watch_tower/server/assets/images/percentage.png +0 -0
  47. data/lib/watch_tower/server/assets/javascripts/application.js +3 -0
  48. data/lib/watch_tower/server/assets/javascripts/percentage.coffee +8 -0
  49. data/lib/watch_tower/server/assets/stylesheets/application.css +7 -0
  50. data/lib/watch_tower/server/assets/stylesheets/global.sass +71 -0
  51. data/lib/watch_tower/server/assets/stylesheets/project.sass +59 -0
  52. data/lib/watch_tower/server/configurations.rb +10 -0
  53. data/lib/watch_tower/server/configurations/asset.rb +43 -0
  54. data/lib/watch_tower/server/database.rb +105 -0
  55. data/lib/watch_tower/server/db/migrate/001_create_projects.rb +15 -0
  56. data/lib/watch_tower/server/db/migrate/002_create_files.rb +16 -0
  57. data/lib/watch_tower/server/db/migrate/003_create_time_entries.rb +12 -0
  58. data/lib/watch_tower/server/db/migrate/004_create_durations.rb +14 -0
  59. data/lib/watch_tower/server/db/migrate/005_add_hash_to_time_entries.rb +6 -0
  60. data/lib/watch_tower/server/db/migrate/006_add_hash_to_files.rb +6 -0
  61. data/lib/watch_tower/server/decorator.rb +21 -0
  62. data/lib/watch_tower/server/decorator/application_decorator.rb +91 -0
  63. data/lib/watch_tower/server/decorator/file_decorator.rb +38 -0
  64. data/lib/watch_tower/server/decorator/project_decorator.rb +51 -0
  65. data/lib/watch_tower/server/helpers.rb +13 -0
  66. data/lib/watch_tower/server/helpers/asset.rb +29 -0
  67. data/lib/watch_tower/server/helpers/improved_partials.rb +41 -0
  68. data/lib/watch_tower/server/models/duration.rb +11 -0
  69. data/lib/watch_tower/server/models/file.rb +31 -0
  70. data/lib/watch_tower/server/models/project.rb +17 -0
  71. data/lib/watch_tower/server/models/time_entry.rb +64 -0
  72. data/lib/watch_tower/server/public/assets/WatchTower-4d6de11e1bd34165ad91ac46fb711bf3.jpg +0 -0
  73. data/lib/watch_tower/server/public/assets/application-7829b53b5ece1a16d22dc3d00f329023.css +107 -0
  74. data/lib/watch_tower/server/public/assets/application-e0e6b7731aade460f680331e65cf0682.js +9359 -0
  75. data/lib/watch_tower/server/public/assets/percentage-d8589e21a5fc85d32a445f531ff8ab95.png +0 -0
  76. data/lib/watch_tower/server/vendor/assets/javascripts/jquery-ui.js +11729 -0
  77. data/lib/watch_tower/server/vendor/assets/javascripts/jquery.js +8981 -0
  78. data/lib/watch_tower/server/vendor/assets/javascripts/jquery_ujs.js +363 -0
  79. data/lib/watch_tower/server/views/.gitkeep +0 -0
  80. data/lib/watch_tower/server/views/_file.haml +9 -0
  81. data/lib/watch_tower/server/views/_project.haml +13 -0
  82. data/lib/watch_tower/server/views/index.haml +7 -0
  83. data/lib/watch_tower/server/views/layout.haml +32 -0
  84. data/lib/watch_tower/server/views/project.haml +12 -0
  85. data/lib/watch_tower/templates/config.yml +146 -0
  86. data/lib/watch_tower/templates/watchtower.plist +23 -0
  87. data/lib/watch_tower/version.rb +8 -0
  88. data/spec/factories.rb +45 -0
  89. data/spec/spec_helper.rb +26 -0
  90. data/spec/support/active_record.rb +44 -0
  91. data/spec/support/factory_girl.rb +6 -0
  92. data/spec/support/launchy.rb +3 -0
  93. data/spec/support/sinatra.rb +10 -0
  94. data/spec/support/timecop.rb +7 -0
  95. data/spec/watch_tower/appscript_spec.rb +6 -0
  96. data/spec/watch_tower/cli/install_spec.rb +16 -0
  97. data/spec/watch_tower/cli/open_spec.rb +14 -0
  98. data/spec/watch_tower/cli/start_spec.rb +17 -0
  99. data/spec/watch_tower/cli_spec.rb +15 -0
  100. data/spec/watch_tower/config_spec.rb +25 -0
  101. data/spec/watch_tower/editor/textmate_spec.rb +43 -0
  102. data/spec/watch_tower/editor/xcode_spec.rb +43 -0
  103. data/spec/watch_tower/editor_spec.rb +19 -0
  104. data/spec/watch_tower/eye_spec.rb +130 -0
  105. data/spec/watch_tower/project/git_based_spec.rb +131 -0
  106. data/spec/watch_tower/project/path_based_spec.rb +111 -0
  107. data/spec/watch_tower/project_spec.rb +82 -0
  108. data/spec/watch_tower/server/app_spec.rb +186 -0
  109. data/spec/watch_tower/server/decorator/project_decorator_spec.rb +60 -0
  110. data/spec/watch_tower/server/models/file_spec.rb +284 -0
  111. data/spec/watch_tower/server/models/project_spec.rb +165 -0
  112. data/spec/watch_tower/server/models/time_entry_spec.rb +37 -0
  113. data/spec/watch_tower/server_spec.rb +4 -0
  114. data/watch_tower.gemspec +80 -0
  115. metadata +450 -0
@@ -0,0 +1,22 @@
1
+ begin
2
+ require 'rubygems'
3
+ require 'appscript'
4
+ rescue LoadError
5
+ require 'rbconfig'
6
+ if RbConfig::CONFIG['target_os'] =~ /darwin/i
7
+ STDERR.puts "Please install 'appscript' to use this gem with Textmate"
8
+ STDERR.puts "gem install appscript"
9
+ end
10
+ # Define a simple class so the gem works even if Appscript is not installed
11
+ module ::Appscript
12
+ class Application
13
+ def is_running?
14
+ false
15
+ end
16
+ end
17
+
18
+ def app(*args)
19
+ Application.new
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ require 'thor'
2
+
3
+ # Load all modules
4
+ Dir["#{LIB_PATH}/cli/**/*.rb"].each { |f| require f }
5
+
6
+ module WatchTower
7
+ module CLI
8
+ class Runner < ::Thor
9
+ # Include cli modules
10
+ include Install
11
+ include Open
12
+ include Start
13
+ end
14
+ end
15
+ end
File without changes
@@ -0,0 +1,63 @@
1
+ module WatchTower
2
+ module CLI
3
+ module Install
4
+
5
+ def self.included(base)
6
+ base.send :include, InstanceMethods
7
+ end
8
+
9
+ module InstanceMethods
10
+ def self.included(base)
11
+ base.class_eval <<-END, __FILE__, __LINE__ + 1
12
+ # This module needs Thor::Actions
13
+ include ::Thor::Actions
14
+
15
+ # Mappings (aliases)
16
+ map "-i" => :install
17
+
18
+ # Install WatchTower
19
+ desc "install", "Install Watch Tower"
20
+ def install
21
+ # Install the configuration file
22
+ install_config_file
23
+ # Install the bootloader
24
+ install_bootloader
25
+ end
26
+
27
+ protected
28
+ # Install the configuration file
29
+ def install_config_file
30
+ self.class.source_root(TEMPLATE_PATH)
31
+ copy_file 'config.yml', File.join(USER_PATH, 'config.yml')
32
+ end
33
+
34
+ # Install bootloader
35
+ def install_bootloader
36
+ require 'rbconfig'
37
+ case RbConfig::CONFIG['target_os']
38
+ when /darwin/
39
+ install_bootloader_on_mac
40
+ else
41
+ puts <<-MSG
42
+ WatchTower bootloader is not supported on your OS, you'd have to run it manually
43
+ for the time being. Support for many editors and many OSes is planned for the
44
+ future, if you would like to help, or drop in an issue please don't hesitate to
45
+ do so on the Github page: https://github.com/TechnoGate/watch_tower
46
+ MSG
47
+ end
48
+ end
49
+
50
+ # Install bootloader on Mac OS X
51
+ def install_bootloader_on_mac
52
+ self.class.source_root(TEMPLATE_PATH)
53
+ copy_file 'watchtower.plist', File.join(ENV['HOME'], 'Library',
54
+ 'LaunchAgents', 'fr.technogate.WatchTower.plist')
55
+
56
+ puts "\nCreated. Now run:\n launchctl load ~/Library/LaunchAgents/fr.technogate.WatchTower.plist\n\n"
57
+ end
58
+ END
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,24 @@
1
+ module WatchTower
2
+ module CLI
3
+ module Open
4
+
5
+ def self.included(base)
6
+ base.send :include, InstanceMethods
7
+ end
8
+
9
+ module InstanceMethods
10
+ def self.included(base)
11
+ base.class_eval <<-END, __FILE__, __LINE__ + 1
12
+ # Open the WatchTower server in the browser
13
+ #
14
+ # TODO: Should be able to determine the port of the server.
15
+ desc "open", "Open the WatchTower in the browser"
16
+ def open
17
+ system "open http://localhost:9282"
18
+ end
19
+ END
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,140 @@
1
+ module WatchTower
2
+ module CLI
3
+ module Start
4
+
5
+ def self.included(base)
6
+ base.send :include, InstanceMethods
7
+ end
8
+
9
+ module InstanceMethods
10
+ def self.included(base)
11
+ base.class_eval <<-END, __FILE__, __LINE__ + 1
12
+ # Mappings (aliases)
13
+ map "-s" => :start
14
+
15
+ # Start watchtower
16
+ desc "start", "Start the Watch Tower"
17
+ method_option :bootloader,
18
+ type: :boolean,
19
+ required: false,
20
+ aliases: "-b",
21
+ default: false,
22
+ desc: "Is it invoked from the bootloader?"
23
+ method_option :foreground,
24
+ type: :boolean,
25
+ required: false,
26
+ aliases: "-f",
27
+ default: false,
28
+ desc: "Do not run in the background."
29
+ method_option :host,
30
+ type: :string,
31
+ required: false,
32
+ aliases: "-h",
33
+ default: 'localhost',
34
+ desc: "Set the server's host"
35
+ method_option :port,
36
+ type: :numeric,
37
+ required: false,
38
+ aliases: "-p",
39
+ default: 9282,
40
+ desc: "Set the server's port."
41
+ def start
42
+ if Config[:enabled] &&
43
+ (!options[:bootloader] || (options(:bootloader) && Config[:launch_on_boot]))
44
+ LOG.info "Starting WatchTower."
45
+ start!
46
+ else
47
+ abort "You need to edit the config file located at #{Config::CONFIG_FILE}."
48
+ end
49
+ end
50
+
51
+ protected
52
+ def start!
53
+ if options[:foreground]
54
+ LOG.debug "#{__FILE__}:#{__LINE__}: Running WatchTower in foreground."
55
+
56
+ # Start WatchTower
57
+ start_watch_tower
58
+
59
+ LOG.debug "#{__FILE__}:#{__LINE__}: WatchTower has finished."
60
+ else
61
+ LOG.debug "#{__FILE__}:#{__LINE__}: Running WatchTower in the background."
62
+ pid = fork do
63
+ # Try to replace ruby with WatchTower in the command line (for ps)
64
+ $0 = 'watchtower' unless $0 == 'watchtower'
65
+
66
+ # Tell ruby that we are a daemon
67
+ Process.daemon
68
+
69
+ # Start WatchTower
70
+ start_watch_tower
71
+
72
+ LOG.debug "#{__FILE__}:#{__LINE__}: WatchTower has finished."
73
+ end
74
+ end
75
+ end
76
+
77
+ # Start watch tower
78
+ # This method just start the watch tower it doesn't know
79
+ # or care if we are in a forked process or not, all it cares about
80
+ # is starting the database server before starting the eye
81
+ #
82
+ # see #start_server
83
+ # see #start_eye
84
+ def start_watch_tower
85
+ # Start the server
86
+ start_server
87
+
88
+ # Wait until the database starts
89
+ until Server::Database.is_connected? do
90
+ sleep(1)
91
+ end
92
+
93
+ # Wait until the database has migrated
94
+ until Server::Database.is_migrated? do
95
+ sleep(1)
96
+ end
97
+
98
+ # Start the eye now.
99
+ start_eye
100
+ end
101
+
102
+ # Start the eye
103
+ # This method just start the watch tower it doesn't know
104
+ # or care if we are in a forked process or not
105
+ def start_eye
106
+ LOG.debug "#{__FILE__}:#{__LINE__}: Starting the eye."
107
+ Eye.start!(watch_tower_options)
108
+ end
109
+
110
+ # Start the web server
111
+ # This method just start the watch tower it doesn't know
112
+ # or care if we are in a forked process or not
113
+ def start_server
114
+ LOG.debug "#{__FILE__}:#{__LINE__}: Starting the web server."
115
+ Server.start!(watch_tower_options)
116
+ end
117
+
118
+ # Return Watch Tower options
119
+ # same as options but modified to correspond to WatchTower options
120
+ # instead of CLI options
121
+ #
122
+ # @return [Hash] options
123
+ def watch_tower_options
124
+ return @watch_tower_options if @watch_tower_options
125
+
126
+ @watch_tower_options = options.dup
127
+ @watch_tower_options.delete(:bootloader)
128
+
129
+ # Log the options as a Debug
130
+ LOG.debug "#{__FILE__}:#{__LINE__}: Options are \#{@watch_tower_options.inspect}."
131
+
132
+ # Return the options
133
+ @watch_tower_options
134
+ end
135
+ END
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,38 @@
1
+ require 'active_support/hash_with_indifferent_access'
2
+
3
+ module WatchTower
4
+ module Config
5
+ extend self
6
+
7
+ # Define the config file's path
8
+ CONFIG_FILE = File.join(USER_PATH, 'config.yml')
9
+
10
+ # Define the config class variable
11
+ @@config = nil
12
+
13
+ # Return a particular config variable from the parsed config file
14
+ #
15
+ # @param [String|Symbol] config
16
+ # @return mixed
17
+ # @raise [Void]
18
+ def [](config)
19
+ ensure_config_file_exists
20
+ @@config ||= HashWithIndifferentAccess.new(YAML.parse_file(CONFIG_FILE).to_ruby)
21
+ @@config[:watch_tower].send(:[], config)
22
+ end
23
+
24
+ protected
25
+ # Ensures config file exists in the user config folder
26
+ #
27
+ # @param [Void]
28
+ # @return [Void]
29
+ # @raise [Void]
30
+ def ensure_config_file_exists
31
+ unless File.exists?(CONFIG_FILE)
32
+ File.open(CONFIG_FILE, 'w') do |f|
33
+ f.write(File.read(File.join(TEMPLATE_PATH, 'config.yml')))
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,4 @@
1
+ require 'active_support/core_ext'
2
+ require 'active_support/dependencies/autoload'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/core_ext/**/*.rb"].each { |f| require f }
File without changes
@@ -0,0 +1,17 @@
1
+ module WatchTower
2
+ module Editor
3
+ extend ::ActiveSupport::Autoload
4
+
5
+ autoload :BaseAppscript
6
+ autoload :BasePs
7
+ autoload :Textmate
8
+ autoload :Xcode
9
+
10
+ def self.editors
11
+ Editor.constants. # Collect the defined constants
12
+ collect { |c| "::WatchTower::Editor::#{c}"}. # Access them under the Server module
13
+ collect(&:constantize). # Make them a constant
14
+ select { |c| c.class == Class } # Keep only classes
15
+ end
16
+ end
17
+ end
File without changes
@@ -0,0 +1,34 @@
1
+ require 'watch_tower/appscript'
2
+
3
+ module WatchTower
4
+ module Editor
5
+ module BaseAppscript
6
+ def self.included(base)
7
+ base.send :include, InstanceMethods
8
+ end
9
+
10
+ module InstanceMethods
11
+ def self.included(base)
12
+ base.class_eval <<-END, __FILE__, __LINE__ + 1
13
+ # Include AppScript
14
+ include ::Appscript
15
+
16
+ def is_running?
17
+ editor.is_running? if editor
18
+ end
19
+
20
+ def current_path
21
+ current_paths.try(:first)
22
+ end
23
+
24
+ def current_paths
25
+ if is_running?
26
+ editor.document.get.collect(&:path).collect(&:get)
27
+ end
28
+ end
29
+ END
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,6 @@
1
+ module WatchTower
2
+ module Editor
3
+ module BasePs
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,17 @@
1
+ module WatchTower
2
+ module Editor
3
+ class Textmate
4
+ include BaseAppscript
5
+
6
+ protected
7
+ # This method returns an instance of ::Appscript::Application
8
+ #
9
+ # returns [::Appscript::Application | nil]
10
+ def editor
11
+ app 'Textmate'
12
+ rescue ::FindApp::ApplicationNotFoundError
13
+ LOG.debug "#{__FILE__}:#{__LINE__ - 2}: Textmate application can't be found, maybe not installed?"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module WatchTower
2
+ module Editor
3
+ class Xcode
4
+ include BaseAppscript
5
+
6
+ protected
7
+ # This method returns an instance of ::Appscript::Application
8
+ #
9
+ # returns [::Appscript::Application | nil]
10
+ def editor
11
+ # Cannot use app('Xcode') because it fails when multiple Xcode versions are installed
12
+ # Taken from timetap
13
+ # https://github.com/apalancat/timetap/blob/editors/lib/time_tap/editors.rb#L25
14
+ pid = app('System Events').processes[its.name.eq('Xcode')].first.unix_id.get
15
+ app.by_pid(pid)
16
+ rescue ::FindApp::ApplicationNotFoundError
17
+ rescue ::Appscript::CommandError
18
+ nil
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ module WatchTower
2
+ # Global Error
3
+ WatchTowerError = Class.new Exception
4
+
5
+ # Exceptions raised by the Project module
6
+ ProjectError = Class.new WatchTowerError
7
+ FileNotFound = Class.new ProjectError
8
+
9
+ # Exception raised by the Path module
10
+ PathError = Class.new ProjectError
11
+ PathNotUnderCodePath = Class.new PathError
12
+
13
+ # Exception raised by the Editor module
14
+ EditorError = Class.new WatchTowerError
15
+ TextmateError = Class.new EditorError
16
+ XcodeError = Class.new EditorError
17
+
18
+ # Exceptions raised by the Server module
19
+ ServerError = Class.new WatchTowerError
20
+ DatabaseError = Class.new ServerError
21
+ DatabaseConfigNotFoundError = Class.new DatabaseError
22
+
23
+ # Exceptions raised by the Eye module
24
+ EyeError = Class.new WatchTowerError
25
+ end