watch_tower 0.0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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