plow 0.1.0 → 1.0.0

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.
@@ -0,0 +1,123 @@
1
+ # encoding: UTF-8
2
+
3
+ class Plow
4
+ # `Plow::Dependencies` is a class-methods-only **singleton** class that performs both strict
5
+ # parse-time dependency checking and simple, elegant run-time dependency warning.
6
+ #
7
+ # My preferred mental model is to consider software library dependencies as a snapshot in time.
8
+ # I also make the assumption, sometimes incorrectly, that a newer version of a software library
9
+ # is not always a better version.
10
+ #
11
+ # `Plow::Dependencies` automatically enforces a strict parse-time check for the
12
+ # `REQUIRED_RUBY_VERSION` on both application and development processes for the `Plow` library.
13
+ # (i.e. `bin/plow`, `rake`, `spec`, etc) Because of this, I've ensured this file is
14
+ # syntactically compatible with Ruby 1.8.6 or higher.
15
+ #
16
+ # Currently, `Plow` does **not** enforce strict parse-time version checking on `DEVELOPMENT_GEMS`.
17
+ # In the future, I would like to experiment with using RubyGems and the `Kernel#gem` method to
18
+ # this end. For now, each developer is responsible for ensuring the correct versions of their
19
+ # necessary development gems are located in the `$LOAD_PATH` on their system.
20
+ #
21
+ # When a gem is required, but a `LoadError` is raised, and rescued, `Plow::Dependencies` can be
22
+ # incorporated into the process to warn a developer of missing development features. Even with a
23
+ # few methods -- `.create_warning_for` and `.warn_at_exit` -- users are automatically warned, in
24
+ # this case at the moment of termination, about gems that could not found be in the `$LOAD_PATH`.
25
+ # Using `Plow::Dependencies` is **not** a mandatory inclusion for all gem requirements, merely a
26
+ # guide to help developers quickly see obstacles in their path.
27
+ #
28
+ # @example Simple usage with the Jeweler gem
29
+ # Plow::Dependencies.warn_at_exit
30
+ # begin
31
+ # require 'jeweler'
32
+ # # work with the Jeweler gem
33
+ # rescue LoadError => e
34
+ # Plow::Dependencies.create_warning_for(e)
35
+ # end
36
+ #
37
+ # @see Plow::Dependencies.create_warning_for
38
+ # @see Plow::Dependencies.warn_at_exit
39
+ class Dependencies
40
+ # For now, starting with Ruby 1.9.1 but I would like to experiment with compatibility with Ruby >= 1.9.1 in the future.
41
+ REQUIRED_RUBY_VERSION = '1.9.1'
42
+
43
+ # bluecloth is a hidden yard dependency for markdown support
44
+ DEVELOPMENT_GEMS = {
45
+ :jeweler => '1.3.0',
46
+ :rspec => '1.2.9',
47
+ :yard => '0.4.0',
48
+ :bluecloth => '2.0.5'
49
+ }
50
+
51
+ # Thanx rspec for bucking the pattern :(
52
+ FILE_NAME_TO_GEM_NAME = {
53
+ :spec => :rspec
54
+ }
55
+
56
+ # Empties the warnings cache. This method is called when the class is required.
57
+ def self.destroy_warnings
58
+ @@warnings_cache = []
59
+ end
60
+ destroy_warnings
61
+
62
+ # Creates and caches a warning from a `LoadError` exception. Warnings are only created for
63
+ # known development gem dependencies.
64
+ #
65
+ # @param [LoadError] error A rescued exception
66
+ # @raise [RuntimeError] Raised when the `LoadError` argument is an unknown development gem.
67
+ def self.create_warning_for(error)
68
+ error.message.match(/no such file to load -- (\w*)/) do |match_data|
69
+ file_name = match_data[1].to_sym
70
+ gem_name = if DEVELOPMENT_GEMS.has_key?(file_name)
71
+ file_name
72
+ elsif FILE_NAME_TO_GEM_NAME.has_key?(file_name)
73
+ FILE_NAME_TO_GEM_NAME[file_name]
74
+ else
75
+ raise "Cannot create a dependency warning for unknown development gem -- #{file_name}"
76
+ end
77
+
78
+ @@warnings_cache << "#{gem_name} --version '#{DEVELOPMENT_GEMS[gem_name]}'"
79
+ end
80
+ end
81
+
82
+ # Displays a warning message to the user on the standard output channel if there are warnings
83
+ # to render.
84
+ #
85
+ # @example Sample warning message
86
+ # The following development gem dependencies could not be found. Without them, some available development features are missing:
87
+ # jeweler --version '1.3.0'
88
+ # rspec --version '1.2.9'
89
+ # yard --version '0.4.0'
90
+ # bluecloth --version '2.0.5'
91
+ def self.render_warnings
92
+ unless @@warnings_cache.empty?
93
+ message = []
94
+ message << "The following development gem dependencies could not be found. Without them, some available development features are missing:"
95
+ message += @@warnings_cache
96
+ puts "\n" + message.join("\n")
97
+ end
98
+ end
99
+
100
+ # Attaches a call to `render_warnings` to `Kernel#at_exit`
101
+ def self.warn_at_exit
102
+ at_exit { render_warnings }
103
+ end
104
+
105
+ private
106
+
107
+ # Checks that the version of the current Ruby process matches the `REQUIRED_RUBY_VERSION`.
108
+ # This method is automatically invoked at the first time this class is required, ensuring the
109
+ # correct Ruby version at parse-time.
110
+ #
111
+ # @param [String] ruby_version Useful for automated specifications. Defaults to `RUBY_VERSION`.
112
+ # @raise [SystemExit] Raised, with a message, when the process is using an incorrect version of Ruby.
113
+ def self.check_ruby_version(ruby_version = RUBY_VERSION)
114
+ unless ruby_version == REQUIRED_RUBY_VERSION
115
+ abort <<-ERROR
116
+ This library requires Ruby #{REQUIRED_RUBY_VERSION}, but you're using #{ruby_version}.
117
+ Please visit http://www.ruby-lang.org/ for installation instructions.
118
+ ERROR
119
+ end
120
+ end
121
+ check_ruby_version
122
+ end
123
+ end
data/lib/plow/errors.rb CHANGED
@@ -1,27 +1,35 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  class Plow
4
+ # Should be raised when the current process is owned by a non-root user.
4
5
  class NonRootProcessOwnerError < StandardError
5
6
  end
6
7
 
8
+ # Should be raised when the user-supplied system user name is invalid.
7
9
  class InvalidSystemUserNameError < StandardError
8
10
  end
9
11
 
12
+ # Should be raised when the user-supplied web-site name is invalid.
10
13
  class InvalidWebSiteNameError < StandardError
11
14
  end
12
15
 
16
+ # Should be raised when the user-supplied web-site alias is invalid.
13
17
  class InvalidWebSiteAliasError < StandardError
14
18
  end
15
19
 
20
+ # Should be raised when the user-supplied system user name is reserved.
16
21
  class ReservedSystemUserNameError < StandardError
17
22
  end
18
23
 
24
+ # Should be raised when the user-supplied system user name is not found when it should be.
19
25
  class SystemUserNameNotFoundError < StandardError
20
26
  end
21
27
 
28
+ # Should be raised when an application root path already exists when it should not.
22
29
  class AppRootAlreadyExistsError < StandardError
23
30
  end
24
31
 
32
+ # Should be raised when a configuration file already exsits when it should not.
25
33
  class ConfigFileAlreadyExistsError < StandardError
26
34
  end
27
35
  end
@@ -1,15 +1,49 @@
1
1
  # encoding: UTF-8
2
-
3
2
  require 'erb'
4
-
5
3
  require 'plow/binding_struct'
6
- require 'plow/strategy/ubuntu_hardy/user_home_web_app'
4
+ require 'plow/strategy/ubuntu_hardy'
7
5
 
8
6
  class Plow
7
+ # `Plow::Generator` is a **context** class.
8
+ #
9
+ # At run-time, `Plow::Generator` selects an appropiate strategy class. Each strategy class
10
+ # implements a specific algorithm for web-site template generation. An instance of
11
+ # `Plow::Generator` is intended to be passed to the choosen strategy object, as it provides
12
+ # read-only data accessors for user-supplied input as well as convience methods that communicate
13
+ # messages to the user and execute commands on the system.
14
+ #
15
+ # Currently, there is a single strategy implementation.
16
+ #
17
+ # @see Plow::Strategy::UbuntuHardy
9
18
  class Generator
10
19
  attr_reader :user_name, :site_name, :site_aliases
11
20
  attr_reader :strategy
12
21
 
22
+ # Instantiates a new `Plow::Generator` object with a user name, site name, and optional
23
+ # site aliases.
24
+ #
25
+ # During instantiation, `Plow::Generator` performs basic data validation on the user-supplied
26
+ # input, raising an exception on any failure. On success, parameters are cached to instance
27
+ # variables with read-only, public accessors. Finally, the correct strategy class is selected.
28
+ # This decision is very simple as there is only 1 strategy class at the moment.
29
+ #
30
+ # @example Initialization with two required arguments
31
+ # generator = Plow::Generator.new('steve', 'www.apple.com')
32
+ #
33
+ # @example Initialization with an optional third argument
34
+ # generator = Plow::Generator.new('steve', 'www.apple.com', 'apple.com')
35
+ #
36
+ # @return [Plow::Generator] A new instance of `Plow::Generator`
37
+ #
38
+ # @param [String] user_name Name of a Linux system account user (e.g. steve)
39
+ # @param [String] site_name Name of the web-site (e.g. www.apple.com)
40
+ # @param [splat] *site_aliases (Optional) List of alias names of the web-site (e.g. apple.com)
41
+ #
42
+ # @raise [Plow::InvalidSystemUserNameError] Raised when a `user_name` is blank or includes an space character
43
+ # @raise [Plow::InvalidWebSiteNameError] Raised when a `site_name` is blank or includes an space character
44
+ # @raise [Plow::InvalidWebSiteAliasError] Raised when any `site_alias` is blank or includes an space character
45
+ #
46
+ # @see Plow::Strategy::UbuntuHardy
13
47
  def initialize(user_name, site_name, *site_aliases)
14
48
  if user_name.blank? || user_name.include?(' ')
15
49
  raise(Plow::InvalidSystemUserNameError, user_name)
@@ -29,18 +63,51 @@ class Plow
29
63
  @site_name = site_name
30
64
  @site_aliases = site_aliases
31
65
 
32
- @strategy = Plow::Strategy::UbuntuHardy::UserHomeWebApp.new(self)
66
+ @strategy = Plow::Strategy::UbuntuHardy.new(self)
33
67
  end
34
68
 
69
+ # Executes the pre-choosen strategy.
70
+ #
71
+ # @example Executing a strategy pre-choosen by a `Plow::Generator` instance
72
+ # generator = Plow::Generator.new('steve', 'www.apple.com')
73
+ # generator.run!
74
+ #
75
+ # @raise [Plow::NonRootProcessOwnerError] Raised when the process is owned by a non-root user
35
76
  def run!
36
77
  raise Plow::NonRootProcessOwnerError unless Process.uid == 0
37
- strategy.execute
78
+ strategy.execute!
38
79
  end
39
80
 
81
+ # Renders a message, via the standard output channel, to the user.
82
+ #
83
+ # @example A sample user message
84
+ # generator.say("Hello World!")
85
+ #
86
+ # @example Sample output
87
+ # ==> Hello World!
88
+ #
89
+ # @param [String] message A brief message to the user
40
90
  def say(message)
41
91
  puts "==> #{message}"
42
92
  end
43
93
 
94
+ # Executes a set of commands in the user's shell enviroment. Any text rendered out to any
95
+ # channel during execution is **not** captured and simply displayed to the user. Leading
96
+ # spaces in a command are stripped away and empty lines are ignored.
97
+ #
98
+ # @example Sample usage
99
+ # generator.shell <<-COMMANDS
100
+ #
101
+ # echo "Hello World!"
102
+ #
103
+ # cat /etc/passwd
104
+ # COMMANDS
105
+ #
106
+ # @example Sample shell execution
107
+ # echo "Hello World!"
108
+ # cat /etc/passwd
109
+ #
110
+ # @param [String] commands A set of commands, delimited by line-breaks
44
111
  def shell(commands)
45
112
  commands.each_line do |command|
46
113
  command.strip!
@@ -48,8 +115,23 @@ class Plow
48
115
  end
49
116
  end
50
117
 
118
+ # Evaluates a template file, located on the user's filesystem, with a context
119
+ #
120
+ # @example Evaluating a template file with a context and writing the result to back disk
121
+ # File.open('/path/to/output/file', 'wt') do |file|
122
+ # generator = Plow::Generator.new('steve', 'www.apple.com')
123
+ # context = { :site_name => generator.site_name }
124
+ #
125
+ # config = generator.evaluate_template('/path/to/template/file', context)
126
+ # file.write(config)
127
+ # end
128
+ #
129
+ # @return [String] The evaluated template data
130
+ # @param [String] template_path A Unix path to a template file
131
+ # @param [Hash] context Key/value pairs, where the key is a template file instance variable,
132
+ # and a value is the value to be substituted during evaluation
51
133
  def evaluate_template(template_path, context)
52
- template = File.read(template_path)
134
+ template = File.read(template_path)
53
135
  context_struct = Plow::BindingStruct.new(context)
54
136
  ERB.new(template).result(context_struct.get_binding)
55
137
  end
@@ -0,0 +1,316 @@
1
+ # encoding: UTF-8
2
+
3
+ class Plow
4
+ class Strategy
5
+ # `Plow::Strategy::UbuntuHardy` is a **strategy** class, conditionally instantiated at
6
+ # run-time, that implements a generator algorithm for it's **context** class,
7
+ # `Plow::Generator`.
8
+ #
9
+ # The algorithm implementation is compatible for and tested with Linux Ubuntu 8.04.3 LTS
10
+ # (Hardy Heron) running the Apache 2.2.8 web-server. For a description of the algorithm,
11
+ # please see `Plow::Strategy::UbuntuHardy#execute!`.
12
+ #
13
+ # This class has a few code smells that I expect to iron out in time. However, I'll worry
14
+ # about this when it comes time to share parts of this algorthm across multiple strategy
15
+ # classes.
16
+ #
17
+ # @see Plow::Strategy::UbuntuHardy#execute!
18
+ # @see Plow::Generator
19
+ class UbuntuHardy
20
+ attr_reader :context, :users_file_path, :vhost_file_name, :vhost_file_path, :vhost_template_file_path
21
+ attr_reader :user_home_path, :sites_home_path, :app_root_path, :app_public_path, :app_log_path
22
+
23
+ # Instantiates a new `Plow::Strategy::UbuntuHardy` object from the context of a
24
+ # `Plow::Generator` instance.
25
+ #
26
+ # @example
27
+ # class Plow
28
+ # class Generator
29
+ # def initialize
30
+ # @strategy = Plow::Strategy::UbuntuHardy.new(self)
31
+ # end
32
+ # end
33
+ # end
34
+ #
35
+ # @return [Plow::Strategy::UbuntuHardy] A new instance
36
+ # @param [Plow::Generator] context A reference to the generator context.
37
+ def initialize(context)
38
+ @context = context
39
+ @users_file_path = "/etc/passwd"
40
+ @vhost_file_name = "#{context.site_name}.conf"
41
+ @vhost_file_path = "/etc/apache2/sites-available/#{vhost_file_name}"
42
+
43
+ @vhost_template_file_path = "#{File.dirname(__FILE__)}/ubuntu_hardy/templates/apache2-vhost.conf"
44
+ end
45
+
46
+ # Starts the generator algorithm. The algorithm works as follows:
47
+ #
48
+ # 1. Check for a system user account, create it if one does not exist
49
+ # 2. Check for a system user home directory, create it if one does not exist
50
+ # 3. Check for a sites home directory within the system user home, create it if one does not
51
+ # exist
52
+ # 4. Create an application root directory within the sites home, but raise an exception if
53
+ # one already exists
54
+ # 5. Create an application public directory
55
+ # 6. Create an application log directory
56
+ # 7. Create an virtual host configuration file, but raise an exception if one already exists
57
+ # 8. Install the new virtual host configuration file into the web server
58
+ #
59
+ # In addition to the below exceptions, this method may pass-up a raised exception from within
60
+ # any of the private instance methods.
61
+ #
62
+ # @raise [Plow::AppRootAlreadyExistsError] Raised if the web-app root path directory
63
+ # aleady exists
64
+ # @raise [Plow::ConfigFileAlreadyExistsError] Raised if a apache2 vhost configuration file
65
+ # cannot be found
66
+ def execute!
67
+ if user_exists?
68
+ say "existing #{context.user_name} user"
69
+ else
70
+ say "creating #{context.user_name} user"
71
+ create_user!
72
+ end
73
+
74
+ if user_home_exists?
75
+ say "existing #{user_home_path}"
76
+ else
77
+ say "creating #{user_home_path}"
78
+ create_user_home!
79
+ end
80
+
81
+ if sites_home_exists?
82
+ say "existing #{sites_home_path}"
83
+ else
84
+ say "creating #{sites_home_path}"
85
+ create_sites_home!
86
+ end
87
+
88
+ if app_root_exists?
89
+ raise(Plow::AppRootAlreadyExistsError, app_root_path)
90
+ else
91
+ say "creating #{app_root_path}"
92
+ create_app_root!
93
+ end
94
+
95
+ @app_public_path = "#{app_root_path}/public"
96
+ say "creating #{@app_public_path}"
97
+ create_app_public!
98
+
99
+ @app_log_path = "#{app_root_path}/log"
100
+ say "creating #{app_log_path}"
101
+ create_app_logs!
102
+
103
+ if vhost_config_exists?
104
+ raise(Plow::ConfigFileAlreadyExistsError, vhost_file_path)
105
+ else
106
+ say "creating #{vhost_file_path}"
107
+ create_vhost_config!
108
+ end
109
+
110
+ say "installing #{vhost_file_path}"
111
+ install_vhost_config!
112
+ end
113
+
114
+ ############################################################################################################
115
+
116
+ private
117
+
118
+ # Proxy method to `Plow::Generator#say`.
119
+ # @param [String] message A user output message
120
+ # @see Plow::Generator#say
121
+ def say(message)
122
+ context.say(message)
123
+ end
124
+
125
+ # Proxy method to `Plow::Generator#shell`.
126
+ # @param [String] commands Shell commands with multi-line support.
127
+ # @see Plow::Generator#shell
128
+ def shell(commands)
129
+ context.shell(commands)
130
+ end
131
+
132
+ # Reads the file at `users_file_path` and yields each user iteratively.
133
+ #
134
+ # @yield [Hash] Each user account
135
+ # @example
136
+ # users do |user|
137
+ # user[:name] #=> [String] The name
138
+ # user[:password] #=> [String] The bogus password
139
+ # user[:id] #=> [Number] The uid number
140
+ # user[:group_ip] #=> [Number] The gid number
141
+ # user[:info] #=> [String] General account info
142
+ # user[:home_path] #=> [String] The path to the home directory
143
+ # user[:shell_path] #=> [String] The path to the default shell
144
+ # end
145
+ def users(&block)
146
+ File.readlines(users_file_path).each do |user_line|
147
+ user_line = user_line.chomp.split(':')
148
+ user = {
149
+ :name => user_line[0],
150
+ :password => user_line[1],
151
+ :id => user_line[2].to_i,
152
+ :group_id => user_line[3].to_i,
153
+ :info => user_line[4],
154
+ :home_path => user_line[5],
155
+ :shell_path => user_line[6]
156
+ }
157
+ yield user
158
+ end
159
+ end
160
+
161
+ ############################################################################################################
162
+
163
+ # Determines if the context.user_name already exists or not.
164
+ #
165
+ # @return [Boolean] `true` if found otherwise `false`
166
+ # @raise [Plow::ReservedSystemUserNameError] Raised if the `context.user_name` is a reserved
167
+ # system user name
168
+ def user_exists?
169
+ users do |user|
170
+ if user[:name] == context.user_name
171
+ unless user[:id] >= 1000 && user[:id] != 65534
172
+ raise(Plow::ReservedSystemUserNameError, context.user_name)
173
+ end
174
+ return true
175
+ end
176
+ end
177
+
178
+ return false
179
+ end
180
+
181
+ # Creates a system user account for `context.user_name`.
182
+ def create_user!
183
+ shell "adduser #{context.user_name}"
184
+ end
185
+
186
+ ############################################################################################################
187
+
188
+ # Determines if a home path for the `context.user_name` already exists. As a side-effect,
189
+ # also correctly sets the `@user_home_path` variable.
190
+ #
191
+ # @return [Boolean] `true` if the path already exists, otherwise `false`
192
+ # @raise [Plow::SystemUserNameNotFoundError] Raised if the `context.user_name` is not found
193
+ # though it is expected to exist
194
+ def user_home_exists?
195
+ users do |user|
196
+ if user[:name] == context.user_name
197
+ @user_home_path = user[:home_path]
198
+ return Dir.exists?(user[:home_path])
199
+ end
200
+ end
201
+
202
+ raise(Plow::SystemUserNameNotFoundError, context.user_name)
203
+ end
204
+
205
+ # Creates a user home directory structure at `user_home_path`.
206
+ def create_user_home!
207
+ shell <<-RUN
208
+ mkdir #{user_home_path}
209
+ chown #{context.user_name}:#{context.user_name} #{user_home_path}
210
+ RUN
211
+ end
212
+
213
+ ############################################################################################################
214
+
215
+ # Determines if the `sites_home_path` already exists. As a side-effect, also correctly
216
+ # sets the `@sites_home_path` variable.
217
+ #
218
+ # @return [Boolean] `true` if the path already exists, otherwise `false`
219
+ def sites_home_exists?
220
+ @sites_home_path = "#{user_home_path}/sites"
221
+ Dir.exists?(sites_home_path)
222
+ end
223
+
224
+ # Creates the sites home directory structure at `sites_home_path`.
225
+ def create_sites_home!
226
+ shell <<-RUN
227
+ mkdir #{sites_home_path}
228
+ chown #{context.user_name}:#{context.user_name} #{sites_home_path}
229
+ RUN
230
+ end
231
+
232
+ ############################################################################################################
233
+
234
+ # Determines if the `app_root_path` already exists. As a side-effect, also correctly
235
+ # sets the `@app_root_path` variable.
236
+ #
237
+ # @return [Boolean] `true` if the path exists, otherwise `false`
238
+ def app_root_exists?
239
+ @app_root_path = "#{sites_home_path}/#{context.site_name}"
240
+ Dir.exists?(app_root_path)
241
+ end
242
+
243
+ # Creates the app root directory structure at `app_root_path`.
244
+ def create_app_root!
245
+ shell <<-RUN
246
+ mkdir #{app_root_path}
247
+ chown #{context.user_name}:#{context.user_name} #{app_root_path}
248
+ RUN
249
+ end
250
+
251
+ ############################################################################################################
252
+
253
+ # Creates the app public directory structure at `app_public_path`.
254
+ def create_app_public!
255
+ shell <<-RUN
256
+ mkdir #{app_public_path}
257
+ touch #{app_public_path}/index.html
258
+ chown -R #{context.user_name}:#{context.user_name} #{app_public_path}
259
+ RUN
260
+ end
261
+
262
+ ############################################################################################################
263
+
264
+ # Creates the app log directory structure at `app_log_path`.
265
+ def create_app_logs!
266
+ shell <<-RUN
267
+ mkdir #{app_log_path}
268
+ mkdir #{app_log_path}/apache2
269
+ chmod 750 #{app_log_path}/apache2
270
+
271
+ touch #{app_log_path}/apache2/access.log
272
+ touch #{app_log_path}/apache2/error.log
273
+
274
+ chmod 640 #{app_log_path}/apache2/*.log
275
+ chown -R #{context.user_name}:#{context.user_name} #{app_log_path}
276
+ chown root -R #{app_log_path}/apache2
277
+ RUN
278
+ end
279
+
280
+ ############################################################################################################
281
+
282
+ # Determines if the apache2 vhost config file already exists.
283
+ #
284
+ # @return [Boolean] `true` if the file exists, otherwise `false`
285
+ def vhost_config_exists?
286
+ Dir.exists?(vhost_file_path)
287
+ end
288
+
289
+ # Creates an apache2 vhost config file at `vhost_file_path` by evaluateing a template file.
290
+ def create_vhost_config!
291
+ File.open(vhost_file_path, 'wt') do |file|
292
+ template_context = {
293
+ :site_name => context.site_name,
294
+ :site_aliases => context.site_aliases,
295
+ :app_public_path => app_public_path,
296
+ :app_log_path => app_log_path
297
+ }
298
+
299
+ config = context.evaluate_template(vhost_template_file_path, template_context)
300
+ file.write(config)
301
+ end
302
+ end
303
+
304
+ ############################################################################################################
305
+
306
+ # Installs the apache2 vhost config file by enabling the site and restarting the web server.
307
+ def install_vhost_config!
308
+ shell <<-RUN
309
+ a2ensite #{vhost_file_name} > /dev/null
310
+ apache2ctl graceful
311
+ RUN
312
+ end
313
+
314
+ end
315
+ end
316
+ end
data/lib/plow.rb CHANGED
@@ -1,18 +1,12 @@
1
1
  # encoding: UTF-8
2
-
3
- unless RUBY_VERSION >= '1.9.1'
4
- abort <<-ERROR
5
- Incompatible ruby version error in #{__FILE__} near line #{__LINE__}
6
- This library requires at least ruby v1.9.1 but you're using ruby v#{RUBY_VERSION}
7
- Please see http://www.ruby-lang.org/
8
- ERROR
9
- end
10
-
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__)) unless $LOAD_PATH.include?(File.dirname(__FILE__))
3
+ require 'plow/dependencies' # ruby version guard
11
4
  require 'plow/core_ext/object'
12
-
13
5
  require 'plow/errors'
14
6
  require 'plow/application'
15
7
 
8
+ # Library namespace
16
9
  class Plow
17
- VERSION = "0.1.0"
10
+ # Current stable released version
11
+ VERSION = "1.0.0"
18
12
  end
@@ -1,10 +1,10 @@
1
1
  # encoding: UTF-8
2
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'spec_helper'
3
3
 
4
4
  describe Plow::Application do
5
5
 
6
6
  before(:each) do
7
- @expected_version_stamp = "Plow v0.1.0. Copyright (c) 2009 Ryan Sobol. Licensed under the MIT license."
7
+ @expected_version_stamp = "Plow 1.0.0. Copyright (c) 2009 Ryan Sobol. Licensed under the MIT license."
8
8
  end
9
9
 
10
10
  ##################################################################################################
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+ require 'spec_helper'
4
4
 
5
5
  describe Plow::BindingStruct do
6
6