laravel 0.2.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,7 @@
1
1
  require "aruba/cucumber"
2
2
  require "laravel"
3
3
 
4
+ # After a scenario has been ran, deleted the files we may have created.
4
5
  After do
5
6
  test_directory = File.expand_path(File.join(File.dirname(__FILE__), %w[ .. .. tmp aruba]))
6
7
  FileUtils.rm_rf(File.join(test_directory, "my_app"))
@@ -1,40 +1,79 @@
1
- module LaravelHelpers
2
- TestDirectory = File.expand_path(File.join(File.dirname(__FILE__), %w[ .. .. tmp aruba]))
1
+ module Laravel
2
+ # a means to test the gem
3
+ class AppTests
4
+ attr_reader :app, :app_dir, :repo, :aruba
3
5
 
4
- # get the relative path of a directory relative to the temporary path aruba creates
5
- def get_relative_path_to_test_directory(dir, relative_to = nil)
6
- relative_to = TestDirectory unless relative_to
7
- File.expand_path(dir, relative_to)
8
- end
6
+ # create a new object that handles the tests for us
7
+ #
8
+ # ==== Parameters
9
+ # +dir+ :: directory of the application - can be relative path or absolute path.
10
+ # +repo+ :: repository URL/directory for source
11
+ #
12
+ def initialize(dir = nil, repo = nil)
13
+ @repo = repo
14
+ @aruba = File.expand_path(File.join(File.dirname(__FILE__), %w[ .. .. tmp aruba]))
15
+
16
+ # Store absolute path to the directory
17
+ @app_dir = dir || Dir.pwd
18
+ convert_to_relative_path
19
+
20
+ options = {
21
+ :force => false, :quiet => false,
22
+ :perms => true, :source => get_source_url
23
+ }
9
24
 
10
- # get the url to the repository by its name
11
- def get_test_repo_url(repo = nil)
12
- case repo
13
- when nil, "official", "default" then Laravel::OfficialRepository
14
- when "pastes" then "http://github.com/laravel/pastes"
15
- when "non_laravel" then "http://github.com/github/gitignore"
16
- else repo
25
+ # create a new Laravel App instance for given options
26
+ @app = Laravel::App.new(@app_dir, options)
17
27
  end
18
- end
19
28
 
20
- # get the full path to the repository by its name
21
- def get_test_repo_path(repo = nil, repo_url = nil)
22
- repo_url = get_test_repo_url(repo) unless repo_url
23
- Laravel::crypted_path(repo_url)
24
- end
29
+ # get the relative path of a directory relative to the temporary path aruba creates
30
+ #
31
+ # ==== Parameters
32
+ # +relative_to+ :: Path to use as base directory, when converting to relative path.
33
+ # By default, it is the path to the aruba tmp directory
34
+ #
35
+ def convert_to_relative_path(relative_to = nil)
36
+ relative_to = @aruba unless relative_to
37
+ @app_dir = File.expand_path(@app_dir, relative_to)
38
+ end
25
39
 
26
- # checks if the configuration file contains a particular string
27
- def check_config_file_for_string(search, dir)
28
- dir = get_relative_path_to_test_directory(dir)
29
- config_file = File.join(dir, %w[ application config application.php ])
30
- raise RSpec::Expectations::ExpectationNotMetError unless File.readlines(config_file).grep(/#{search}/).any?
31
- end
40
+ # get the url to a repository by a given alias/name
41
+ #
42
+ # ==== Return
43
+ # +string+ :: URL for the repository
44
+ #
45
+ def get_source_url
46
+ case @repo
47
+ when nil, "", "official", "default" then Laravel::App::LaravelRepo
48
+ when "pastes" then "http://github.com/laravel/pastes"
49
+ when "non_laravel" then "http://github.com/github/gitignore"
50
+ else @repo
51
+ end
52
+ end
32
53
 
33
- # raises an error based on a condition and negation
34
- # if negation is not supplied, simply raises an error if condition is not true
35
- def raise_error_based_on_condition(condition, negate = nil)
36
- raise RSpec::Expectations::ExpectationNotMetError if condition == !negate.nil?
54
+ # checks if the configuration file contains a particular string
55
+ #
56
+ # ==== Parameters
57
+ # +search+ :: the search term/regex pattern to look for
58
+ #
59
+ # ==== Return
60
+ # +boolean+:: true, if the search term was found in the configuration file.
61
+ #
62
+ def validate_configuration(search)
63
+ raise_error? File.readlines(@app.config_file).grep(Regexp.new search).any?
64
+ end
65
+
66
+ # raises an error based on a condition and negation
67
+ # if negation is not supplied, simply raises an error if condition is not true
68
+ #
69
+ # ==== Parameters
70
+ # +condition+ :: a condition to check against - if +negate+ is not provided,
71
+ # this condition raises an error if it evaluates to false.
72
+ # +negate+ :: negate the default behaviour
73
+ def raise_error?(condition, negate = nil)
74
+ raise RSpec::Expectations::ExpectationNotMetError if condition == !negate.nil?
75
+ end
37
76
  end
38
77
  end
39
78
 
40
- World(LaravelHelpers)
79
+ World(Laravel)
@@ -13,7 +13,7 @@ Gem::Specification.new do |gem|
13
13
  gem.homepage = "https://github.com/nikhgupta/laravel"
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
16
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.executables = ["laravel"]
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
19
 
@@ -3,7 +3,7 @@ require "thor"
3
3
 
4
4
  # require laravel files
5
5
  require "laravel/version"
6
- require "laravel/info"
7
- require "laravel/helpers"
8
- require "laravel/manage"
9
- require "laravel/create"
6
+ require "laravel/app_support"
7
+ require "laravel/app"
8
+ require "laravel/configuration"
9
+ require "laravel/installer"
@@ -0,0 +1,95 @@
1
+ module Laravel
2
+ # This class provides a means to create new applications based on the Laravel
3
+ # framework. Most of the methods used here have been defined in the
4
+ # AppSupport module, which is being included as a mixin here.
5
+ #
6
+ class App
7
+ # include the AppSupport module which has all the methods defined.
8
+ include Laravel::AppSupport
9
+
10
+ # these attributes must be available as: object.attribute
11
+ attr_reader :cache_folder, :laravel_repo, :app_path, :source, :cache, :options
12
+
13
+ # the path to the folder where the sources will be locally cached.
14
+ CacheFolder = File.expand_path(File.join(ENV['HOME'], %w[ .laravel repos ]))
15
+
16
+ # the official Laravel repository URL which is also the default source for us.
17
+ LaravelRepo = "http://github.com/laravel/laravel.git"
18
+
19
+ # This method initializes a new App object for us, on which we can apply
20
+ # our changes. Logically, this new App object represents an application
21
+ # based on Laravel.
22
+ #
23
+ # ==== Parameters
24
+ # +app_path+ :: The path to the Laravel based application. This can either
25
+ # be a relative path to the current directory, or the absolute path. If
26
+ # +app_path+ is not supplied, we assume current directory.
27
+ #
28
+ # +options+ :: A hash of options for this application. This hash can be
29
+ # created manually, but more closely resembles the options choosen by the
30
+ # user and forwarded by the Thor to us.
31
+ #
32
+ def initialize(app_path = nil, options = nil)
33
+ app_path = Dir.pwd if not app_path or app_path.empty?
34
+ @app_path = File.expand_path(app_path)
35
+
36
+ @options = options
37
+
38
+ @source = options[:source] if options
39
+ @source = LaravelRepo if not @source or @source.empty?
40
+
41
+ @cache = source_is_local? ? @source : cache_directory
42
+ end
43
+
44
+ # This method creates a new Laravel based application for us. This method
45
+ # is invoked by the 'new' task. It first checks if we can create the
46
+ # application in the specified directory, then updates/downloads the local
47
+ # cache for the given source, and then copies over the files from this
48
+ # cache to the specified directory. Finally, it checks if we have a working
49
+ # Laravel application at which point it either raises and error and cleans
50
+ # up, or configures the application and installs tasks/bundles, as
51
+ # requested.
52
+ #
53
+ def create
54
+ # check if we are missing the required force
55
+ required_force_is_missing?
56
+
57
+ # apply some force, when we are boosted with one
58
+ apply_force
59
+
60
+ # download or update local cache
61
+ download_or_update_local_cache
62
+
63
+ # copy the framework files from the cache
64
+ copy_over_cache_files
65
+
66
+ # make necessary changes for the new app, if we were successful in download
67
+ # otherwise, remove the downloaded source
68
+ if has_laravel?
69
+ say_success "Cloned Laravel repository."
70
+ configure_from_options
71
+ install_from_options
72
+ say_success "Hurray! Your Laravel application has been created!"
73
+ else
74
+ say_failed "Downloaded source is not Laravel framework or a possible fork."
75
+ show_info "Cleaning up.."
76
+ clean_up
77
+ show_error "Specified Laravel source is corrupt!"
78
+ end
79
+ end
80
+
81
+ # This method installs the required tasks/bundles by the user.
82
+ # It does so by invoking the 'from_options' method of the Installer class.
83
+ def install_from_options
84
+ install = Installer.new(@app_path, @options)
85
+ install.from_options
86
+ end
87
+
88
+ # This method configures the application as required by the user.
89
+ # It does so by invoking the 'from_options' method of the Configuration class.
90
+ def configure_from_options
91
+ config = Configuration.new(@app_path, @options)
92
+ config.update_from_options
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,282 @@
1
+ require 'digest/md5'
2
+
3
+ module Laravel
4
+ # various methods that help with various classes defined for the Laravel module
5
+ module AppSupport
6
+
7
+ # convert a string to MD5 hash - useful to generate quick random strings.
8
+ #
9
+ # ==== Parameters
10
+ # +string+ :: optional, the input string to be hashed
11
+ # :: a random string will be used for hashing, if this string is not provided
12
+ #
13
+ # ==== Return
14
+ # +string+ :: a 32-character long MD5'ed string
15
+ #
16
+ def make_md5(string = nil)
17
+ string ||= (0...32).map{ ('a'..'z').to_a[rand(26)] }.join
18
+ (Digest::MD5.new << string).to_s
19
+ end
20
+
21
+ # Return the path to the local cache directory for a given Source
22
+ #
23
+ # ==== Return
24
+ # +string+ :: Filepath to the local cache directory
25
+ #
26
+ def cache_directory
27
+ File.join(Laravel::App::CacheFolder, make_md5(@source))
28
+ end
29
+
30
+ # Check whether the app directory is the current directory.
31
+ # This is useful to know if we are creating the new application
32
+ # in the current directory.
33
+ #
34
+ # ==== Return
35
+ # +boolean+ :: True, if the app directory is the current directory.
36
+ #
37
+ def current_directory?
38
+ File.directory?(@app_path) and (@app_path == File.expand_path(Dir.pwd))
39
+ end
40
+
41
+ # Check whether the app directory is empty?
42
+ # This is useful to know if we are trying to create the new application
43
+ # in an empty directory or not, so that we may know if we need to create
44
+ # this application forcefully?
45
+ #
46
+ # ==== Return
47
+ # +boolean+ :: True, if the app directory is an empty one.
48
+ #
49
+ def empty_directory?
50
+ File.directory?(@app_path) and (Dir.entries(@app_path).size == 2)
51
+ end
52
+
53
+ # Check whether the specified source is a local directory or a URL?
54
+ #
55
+ # ==== Return
56
+ # +boolean+ :: True, if the source is a local directory.
57
+ #
58
+ def source_is_local?
59
+ File.directory?(@source)
60
+ end
61
+
62
+ # Merge the specified options with the options specified on the command line.
63
+ #
64
+ # ==== Parameters
65
+ # +options+ :: hash of options passed at the command line
66
+ #
67
+ # ==== Return
68
+ # +hash+ :: hash of merged options
69
+ #
70
+ def merge_options(options)
71
+ @options.merge!(options)
72
+ end
73
+
74
+ # Return the path to the configuration file for the current application
75
+ #
76
+ # ==== Return
77
+ # +string+ :: path to the configuration file
78
+ #
79
+ def config_file
80
+ File.join(@app_path, %w[ application config application.php ])
81
+ end
82
+
83
+ # Return the path to a tasks file by its name
84
+ #
85
+ # ==== Parameters
86
+ # +name+ :: string - name of the tasks file
87
+ #
88
+ # ==== Return
89
+ # +string+ :: path to the tasks file
90
+ #
91
+ def tasks_file(name)
92
+ File.expand_path(File.join(@app_path, %w[ application tasks ], name))
93
+ end
94
+
95
+ # Download a given resource at a particular path
96
+ #
97
+ # ==== Parameters
98
+ # +path+ :: Path where the downloaded content will be saved.
99
+ # This can either be the path to a single file or a directory.
100
+ # If this is a directory, git will be used to download the source,
101
+ # otherwise, curl will be used for the same. Therefore, please, make
102
+ # sure that the source is a git repository when +path+ is a directory,
103
+ # and that the source is an online file when +path+ is a file.
104
+ # +source+ :: Source URL/directory from where the content of the resource will be
105
+ # downloaded. Please, read information about +path+
106
+ #
107
+ # ==== Return
108
+ # +boolean+ :: true, if the resource was downloaded successfully.
109
+ #
110
+ def download_resource(path, source, using)
111
+ using = "wget" if using == "curl" and `which curl`.empty? and not `which wget`.empty?
112
+ case using
113
+ when "git" then system("git clone -q #{source} #{path}")
114
+ when "curl" then system("curl -s #{source} > #{path}")
115
+ when "wget" then system("wget #{source} -O #{path}")
116
+ else false
117
+ end
118
+ end
119
+
120
+ # check if laravel framework exists in the current application's directory
121
+ # currently, this is performed by looking for the presence of 'artisan' file
122
+ # and the 'laravel' subdirectory.
123
+ #
124
+ # ==== Return
125
+ # +boolean+ :: true, if laravel framework exists
126
+ #
127
+ def has_laravel?
128
+ laravel_exists_in_directory?(@app_path)
129
+ end
130
+
131
+ # check if the cache exists for the source specified by the current
132
+ # application's directory this further makes sure that the cache really has
133
+ # laravel framework as we would expect it to
134
+ #
135
+ # ==== Return
136
+ # +boolean+ :: true, if the cache exists
137
+ #
138
+ def has_cache?
139
+ laravel_exists_in_directory?(@cache)
140
+ end
141
+
142
+ # check if laravel framework exists in a specified directory
143
+ # this method is in turn called by the instance methods: 'has_cache?'
144
+ # and the 'has_laravel?'
145
+ #
146
+ # ==== Parameters
147
+ # +directory+ :: directory to check for the existance of laravel framework
148
+ # this can be the relative path to the current app directory
149
+ # or the absolute path of the directory.
150
+ #
151
+ # ==== Return
152
+ # +boolean+ :: true, if laravel exists in the given directory
153
+ #
154
+ def laravel_exists_in_directory?(directory = "")
155
+ return false unless directory
156
+ directory = File.expand_path(directory, @app_path)
157
+ return false unless File.exists? File.join(directory, "artisan")
158
+ return false unless File.directory? File.join(directory, "laravel")
159
+ true
160
+ end
161
+
162
+ # This method first checks if the given application path requires
163
+ # the 'force' option, and then checks if the 'force' option is provided
164
+ # by the user.
165
+ #
166
+ # Whether the path requires 'force' is determined as:
167
+ # -- it is not the current directory
168
+ # -- it is the current directory but is empty
169
+ #
170
+ # ==== Return
171
+ # +error+ :: raises an error, if the 'force' parameter is required!!
172
+ #
173
+ def required_force_is_missing?
174
+ # we need force if path exists and is not the current directory
175
+ check_force = (File.exists?(@app_path) and not current_directory?)
176
+ # we need force if path is current directory but is not empty
177
+ check_force ||= (current_directory? and not empty_directory?)
178
+ # raise an error when we need to force and we have not been supplied with enforcements
179
+ show_error "required force is missing! please, provide enforcements!" if check_force and not @options[:force]
180
+ end
181
+
182
+ # Depending on whether the 'force' parameter is provided, this method
183
+ # removes all the files in the directory specified by the application path,
184
+ # if the directory exists. Further, if the directory doesn't exist, it
185
+ # tries to create the directory specified by the application path.
186
+ #
187
+ def apply_force
188
+ show_info "Creating application forcefully!" if @options[:force]
189
+ FileUtils.rm_rf("#{@app_path}/.", :secure => true) if File.exists?(@app_path) and @options[:force]
190
+ FileUtils.mkdir_p @app_path
191
+ end
192
+
193
+ # This method downloads or updates the local cache for the current source.
194
+ #
195
+ # If the source is a directory on this machine, it will simply not do
196
+ # anything since that can interfere with an offline installation, and the
197
+ # user must update the source manually in this case.
198
+ #
199
+ # Otherwise, it uses git to update or download the source as required and
200
+ # caches it locally.
201
+ #
202
+ def download_or_update_local_cache
203
+ return if source_is_local?
204
+ show_error "git is required!" if `which git`.empty?
205
+ FileUtils.mkdir_p @cache
206
+ Dir.chdir(@cache) do
207
+ if has_cache?
208
+ show_info "Repository exists in local cache.."
209
+ show_info "Updating local cache.."
210
+ `git pull &>/dev/null`
211
+ else
212
+ show_info "Downloading repository to local cache.."
213
+ `git clone #{@source} . &>/dev/null`
214
+ end
215
+ end
216
+ end
217
+
218
+ # This method copies the files from the local cache to the directory of the
219
+ # application.
220
+ #
221
+ def copy_over_cache_files
222
+ FileUtils.cp_r "#{@cache}/.", @app_path
223
+ end
224
+
225
+ # This method updates the permissions on the storage/ directory inside
226
+ # the newly created application. This method does not have a separate exposed
227
+ # call from the CLI. This can be skipped by passing '--no-perms' for the 'new'
228
+ # command.
229
+ #
230
+ def update_permissions_on_storage
231
+ if @options[:perms]
232
+ response = system("chmod -R o+w #{File.join(@app_path, 'storage')}")
233
+ if response
234
+ say_success "Updated permissions on storage/ directory."
235
+ else
236
+ say_failed "Could not update permissions on storage/ directory."
237
+ end
238
+ end
239
+ end
240
+
241
+ # Once, we are done with the intallation, and an error occurs, this method handles
242
+ # the clean_up routine by removing the application directory we created, as well as
243
+ # the local cache for the source, that may exist.
244
+ #
245
+ # Keeping the local cache does not make sense, since we anyways can not create
246
+ # applications based on these 'corrupt' repositories.
247
+ def clean_up
248
+ FileUtils.rm_rf "#{@app_path}" unless current_directory?
249
+ FileUtils.rm_rf "#{@cache}"
250
+ end
251
+
252
+ # This method, simply, imitates the 'say' method that the Thor gem provides us.
253
+ # I preferred to use this method, since it gives us a very nice UI at the CLI :)
254
+ #
255
+ def say(status, message = "", log_status = true)
256
+ shell = Thor::Shell::Color.new
257
+ log_status = false if @options and @options[:quiet]
258
+ shell.say_status(status, message, log_status)
259
+ end
260
+
261
+ # Show some information to the user in Cyan.
262
+ def show_info(message)
263
+ say "Information", message, :cyan
264
+ end
265
+
266
+ # Show a success message to the user in Green.
267
+ def say_success(message)
268
+ say "Success", message, :green
269
+ end
270
+
271
+ # Show a failed message to the user in Yellow.
272
+ def say_failed(message)
273
+ self.say "Failed!!", message, :yellow
274
+ end
275
+
276
+ # Show an error the user in Red, and exit the script, since this is an error!
277
+ def show_error(message)
278
+ self.say "!!ERROR!!", message, :red
279
+ exit
280
+ end
281
+ end
282
+ end