ptero 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +350 -0
  6. data/Rakefile +10 -0
  7. data/bin/ptero +13 -0
  8. data/lib/ptero.rb +14 -0
  9. data/lib/ptero/application.rb +281 -0
  10. data/lib/ptero/cli.rb +35 -0
  11. data/lib/ptero/cli/root.rb +282 -0
  12. data/lib/ptero/composer_default.json +8 -0
  13. data/lib/ptero/exception.rb +26 -0
  14. data/lib/ptero/exceptions/applicationexception.rb +9 -0
  15. data/lib/ptero/exceptions/generatorexception.rb +9 -0
  16. data/lib/ptero/generator.rb +146 -0
  17. data/lib/ptero/generators/applicationjavascriptgenerator.rb +19 -0
  18. data/lib/ptero/generators/applicationstylesheetgenerator.rb +18 -0
  19. data/lib/ptero/generators/configgenerator.rb +34 -0
  20. data/lib/ptero/generators/controllergenerator.rb +23 -0
  21. data/lib/ptero/generators/htaccessgenerator.rb +27 -0
  22. data/lib/ptero/generators/javascriptgenerator.rb +34 -0
  23. data/lib/ptero/generators/landinggenerator.rb +23 -0
  24. data/lib/ptero/generators/layoutgenerator.rb +19 -0
  25. data/lib/ptero/generators/loadallgenerator.rb +26 -0
  26. data/lib/ptero/generators/modelgenerator.rb +22 -0
  27. data/lib/ptero/generators/pagegenerator.rb +56 -0
  28. data/lib/ptero/generators/pagenotfoundgenerator.rb +16 -0
  29. data/lib/ptero/generators/phpclassgenerator.rb +27 -0
  30. data/lib/ptero/generators/phpgenerator.rb +18 -0
  31. data/lib/ptero/generators/phpinfogenerator.rb +16 -0
  32. data/lib/ptero/generators/routesgenerator.rb +68 -0
  33. data/lib/ptero/generators/setupgenerator.rb +13 -0
  34. data/lib/ptero/generators/stylesheetgenerator.rb +33 -0
  35. data/lib/ptero/generators/twiggenerator.rb +24 -0
  36. data/lib/ptero/generators/viewgenerator.rb +24 -0
  37. data/lib/ptero/templates/applicationjavascriptgenerator.js.erb +11 -0
  38. data/lib/ptero/templates/applicationstylesheetgenerator.css.erb +40 -0
  39. data/lib/ptero/templates/configgenerator.php.erb +26 -0
  40. data/lib/ptero/templates/controllergenerator.php.erb +25 -0
  41. data/lib/ptero/templates/generator.txt.erb +1 -0
  42. data/lib/ptero/templates/htaccessgenerator.htaccess.erb +9 -0
  43. data/lib/ptero/templates/javascriptgenerator.js.erb +11 -0
  44. data/lib/ptero/templates/landinggenerator.php.erb +3 -0
  45. data/lib/ptero/templates/layoutgenerator.html.twig.erb +37 -0
  46. data/lib/ptero/templates/loadallgenerator.php.erb +12 -0
  47. data/lib/ptero/templates/modelgenerator.php.erb +21 -0
  48. data/lib/ptero/templates/pagegenerator.html.twig.erb +13 -0
  49. data/lib/ptero/templates/pagenotfoundgenerator.html.twig.erb +13 -0
  50. data/lib/ptero/templates/phpclassgenerator.php.erb +24 -0
  51. data/lib/ptero/templates/phpgenerator.php.erb +3 -0
  52. data/lib/ptero/templates/phpinfogenerator.php.erb +3 -0
  53. data/lib/ptero/templates/routesgenerator.php.erb +13 -0
  54. data/lib/ptero/templates/setupgenerator.php.erb +19 -0
  55. data/lib/ptero/templates/stylesheetgenerator.css.erb +8 -0
  56. data/lib/ptero/templates/twiggenerator.html.twig.erb +5 -0
  57. data/lib/ptero/templates/viewgenerator.html.twig.erb +13 -0
  58. data/lib/ptero/version.rb +4 -0
  59. data/ptero.gemspec +28 -0
  60. data/test/fixtures/test_generators_fixtures.yaml +74 -0
  61. data/test/test_application.rb +123 -0
  62. data/test/test_exceptions.rb +52 -0
  63. data/test/test_generators.rb +110 -0
  64. data/test/test_root.rb +212 -0
  65. metadata +212 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4da3a464d447bcedad4e6dcbcef6f91c508185fb
4
+ data.tar.gz: 097dec9857ca53e7383297ccbc291bbcc7933d9c
5
+ SHA512:
6
+ metadata.gz: 25e1ff3a390dbcb356212a3b520fb7dc676e58c2f669d326a7a77b36c8e54feb1696747164af8c5a1fcad7b0a8ad21e2f2cbc198ac81d18be0253674da7b282c
7
+ data.tar.gz: 5b3ca3cd4dd4bafae1d3c8d504e996706898af8185d4fe671f8e023f8f9324ccd67b93fa19dfaf052e48c546b6552c36854f1023d3be9e6d6d83a4fefa27c632
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *~
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ptero.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 AndrewTLee
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,350 @@
1
+ ### Ptero
2
+ Ptero is a fast and flexible model-view-controller framework for PHP. It allows you to generate models, views, controllers, and routes instantly over the command-line and automatically provides a strong, clean structure for running web apps.
3
+
4
+ Think wearing a giant robot exo-skeleton with lasers and missile launchers while crushing the puny challenges of archaic PHP in a two-hour-long feature film directed by Michael Bay. It's that good.
5
+
6
+
7
+ ### It has the good stuff
8
+ Features include:
9
+ * RESTful routing
10
+ * Sensible templates: layouts and views separate
11
+ * RoR-esque code generation
12
+ * PHP-ActiveRecord for ORM: No SQL needed!
13
+ * Installs dependencies quickly and easily with Composer
14
+ * Built in Ruby: Who doesn't love Ruby?
15
+
16
+ ### It takes care of the bad stuff
17
+ Don't worry about:
18
+ * annoying PHP syntax errors
19
+ * copy-pasting or otherwise repeating code
20
+ * SQL, SQL injection, escaping strings, etc
21
+ * URLs that look ugly
22
+ * PHP being stupid in general
23
+
24
+ ### How do I do it?
25
+ To start using Ptero, make sure your PHP command-line tool is up-to-date, then run the following:
26
+ ```bash
27
+ $ gem install ptero
28
+ ```
29
+
30
+ Now make an application:
31
+ ```bash
32
+ $ ptero new Blog
33
+ ```
34
+
35
+ There! Your web application is seeded and ready. Now you can start generating files.
36
+ ```bash
37
+ $ cd Blog
38
+ $ ptero generate page hippo
39
+ $ ptero route /hippo Hippo
40
+ ```
41
+ Now run an apache server with root directory Blog/www, and navigate to http://localhost/hippo to see a fully-formed page.
42
+
43
+ (I really should have a built-in server command, but making Ruby run PHP is a mess. If someone wants to write up a cross-platform WEBrick server for PHP, I would be glad to incorporate it)
44
+
45
+ ### Onward!
46
+ Now for the best part. Let's take a look at the generated code.
47
+
48
+ Here's the default controller, Blog/php/controllers/HippoController.php
49
+ ```php
50
+ /*
51
+ *
52
+ * HippoController.php
53
+ * ===================
54
+ * This controller does ...
55
+ *
56
+ */
57
+
58
+ namespace Blog\Controllers;
59
+
60
+
61
+ class HippoController extends ApplicationController {
62
+
63
+ // Handle GET requests, use 'public function post()' to handle POST
64
+ public function get() {
65
+ // Render template
66
+ $this->render();
67
+ }
68
+
69
+ }
70
+ ```
71
+
72
+ The controller inherits from ApplicationController, giving it all sorts of useful functionality, most notably the render() method.
73
+
74
+ Calling $this->render(); with (or without) an array mapping of arguments will automatically render the template of the same name as the controller, in this case Blog/views/hippo.html.twig.
75
+
76
+ Here it is:
77
+
78
+ ```twig
79
+ {% extends 'application.html.twig' %}
80
+
81
+ {% block head %}
82
+ <!-- HTML head code produced by view: hippo -->
83
+ <script type="text/javascript" src="/assets/js/hippo.js"></script>
84
+ <link rel="stylesheet" type="text/css" src="/assets/css/hippo.css" />
85
+ {% endblock %}
86
+
87
+ {% block content %}
88
+ <!-- HTML content code produced by view: hippo -->
89
+ <h2>View: Hippo</h2>
90
+
91
+ {% endblock %}
92
+
93
+ ```
94
+ This template is written in Twig. [Click here](http://twig.sensiolabs.org) to learn the basics of Twig.
95
+
96
+
97
+ Notice that the template has only a few lines of code, and that it extends a layout, called application.html.twig.
98
+
99
+ In this way, global layout changes can be made to the global application template, and individual changes made to individual pages.
100
+
101
+ For example, let's try doing some arbitrary server-side work and sending it to the client:
102
+ ```php
103
+ // HippoController.php
104
+
105
+ class HippoController extends ApplicationController {
106
+
107
+ // Handle GET requests, use 'public function post()' to handle POST
108
+ public function get() {
109
+ // sum the numbers 1-10
110
+ $sum = 0;
111
+ for($i=1;$i<10;$i++) {
112
+ $sum += $i;
113
+ }
114
+ $this->render(array(
115
+ 'sum' => $sum
116
+ ));
117
+ }
118
+
119
+ }
120
+ ```
121
+ the template:
122
+ ```twig
123
+ {% extends 'application.html.twig' %}
124
+
125
+ {% block head %}
126
+ <!-- HTML head code produced by view: hippo -->
127
+ <script type="text/javascript" src="/assets/js/hippo.js"></script>
128
+ <link rel="stylesheet" type="text/css" src="/assets/css/hippo.css" />
129
+ {% endblock %}
130
+
131
+ {% block content %}
132
+ <!-- HTML content code produced by view: hippo -->
133
+ <h2>The sum is: {{sum}}</h2>
134
+
135
+ {% endblock %}
136
+
137
+ ```
138
+ Now our code will print the $sum variable passed to it by the server.
139
+
140
+ ### Routing
141
+ We already did some routing with the `$ptero route` command, but routes are also stored in a file and editable by hand. Let's open Blog/php/routes.php
142
+ ```php
143
+ // Application route maps
144
+ $application->route(array(
145
+
146
+ '/hippo' => 'Blog\Controllers\HippoController',
147
+ ));
148
+
149
+ ```
150
+ Look! The route we made is there! It maps the string '/hippo' to the controller class Blog\Controllers\HippoController.
151
+
152
+ You can also print all current routes by running
153
+ ```bash
154
+ $ ptero routes
155
+ ```
156
+
157
+ We can try using RESTful parameters in our application and passing them to controllers. Try this:
158
+ ```bash
159
+ $ ptero generate page rhino
160
+ $ ptero route /rhino/:number Rhino
161
+ ```
162
+ Now edit php/controllers/RhinoController.php:
163
+ ```php
164
+ namespace Blog\Controllers;
165
+
166
+
167
+ class RhinoController extends ApplicationController {
168
+
169
+ // Handle GET requests, use 'public function post()' to handle POST
170
+ public function get($number) {
171
+ $number = intval($number);
172
+ $result = $number * $number;
173
+ $this->render(array(
174
+ 'result' => $result
175
+ ));
176
+ }
177
+
178
+ }
179
+ ```
180
+ and make the template print our result:
181
+ ```twig
182
+ {% block content %}
183
+ <!-- HTML content code produced by view: rhino -->
184
+ <h2>View: Rhino</h2>
185
+ <div>
186
+ Result: {{ result }}
187
+ </div>
188
+
189
+ {% endblock %}
190
+ ```
191
+
192
+ Now navigate to http://localhost/rhino/74
193
+
194
+ Yay! Our application can compute squares!
195
+
196
+ Routes in the routes.php file support a full range of regular expressions, although stubs such as :number and :alpha can be useful. Learn more at <https://github.com/anandkunal/ToroPHP>
197
+
198
+ ### DATABASES!!!
199
+
200
+ Ptero wouldn't be complete without Databases, though, (as-of-yet) we have not incorporated the ability to generate them through the command-line tool.
201
+
202
+ You'll have to bring your own SQL server. Sorry. Try [MAMP](http://www.mamp.info/en/).
203
+
204
+ Ptero uses PHP-ActiveRecord Databases, which are as easy as pie to use with a few minutes' practice. [Take a look.](http://www.phpactiverecord.org/projects/main/wiki/Quick_Start)
205
+
206
+ The first step of using Ptero databases is to configure your application. Open config/config.php:
207
+ ```php
208
+ <?php
209
+ /*
210
+ * config.php
211
+ * return array of configuration values
212
+ *
213
+ *
214
+ *
215
+ */
216
+
217
+
218
+ return array(
219
+ 'templates_path' => 'views',
220
+
221
+ 'controllers_path' => 'php/controllers',
222
+
223
+ 'models_path' => 'php/models',
224
+
225
+
226
+
227
+ 'database' => array(
228
+ 'name' => 'database-name',
229
+ 'user' => 'username',
230
+ 'password' => 'password',
231
+ 'server' => 'localhost'
232
+ )
233
+ )
234
+ ?>
235
+ ```
236
+ Change the values 'database-name', 'username', 'password', and 'server' to the configuration of your database so that PHP-ActiveRecord knows where to look.
237
+
238
+ Now let's generate another page. We'll assume that we have a table called Elephants in our database that has a primary-key int "id", a string "name", and another int "age."
239
+ ```bash
240
+ $ ptero generate page elephant
241
+ $ ptero generate model elephant
242
+ $ ptero route /elephants/:number Elephant
243
+ ```
244
+ In php/controllers/ElephantController.php:
245
+ ```php
246
+ <?php
247
+
248
+ /*
249
+ *
250
+ * ElephantController.php
251
+ * ======================
252
+ * This controller does ...
253
+ *
254
+ */
255
+
256
+ namespace Blog\Controllers;
257
+ use \Blog\Models as DB; // Make sure we can access Models
258
+
259
+ class ElephantController extends ApplicationController {
260
+
261
+ // Handle GET requests, use 'public function post()' to handle POST
262
+ public function get($id) {
263
+ $this->db_connect('development'); // Connect to our database, in the mode specified. 'development' is the default value.
264
+
265
+ $id = intval($id);
266
+ $elephant = DB\Elephant::find($id);
267
+
268
+ $name = $elephant->name;
269
+ $age = $elephant->age;
270
+ $this->render(array(
271
+ 'name' => $name,
272
+ 'age' => $age
273
+ ));
274
+ }
275
+
276
+ }
277
+
278
+
279
+ ?>
280
+ ```
281
+ In views/elephant.twig:
282
+ ```twig
283
+ {% extends 'application.html.twig' %}
284
+
285
+ {% block head %}
286
+ <!-- HTML head code produced by view: elephant -->
287
+ <script type="text/javascript" src="/assets/js/elephant.js"></script>
288
+ <link rel="stylesheet" type="text/css" src="/assets/css/elephant.css" />
289
+ {% endblock %}
290
+
291
+ {% block content %}
292
+ <!-- HTML content code produced by view: elephant -->
293
+ <h2>View: Elephant</h2>
294
+ <h2>Name: {{ name }}</h2>
295
+ <h2>Age: {{ age }}</h2>
296
+
297
+ {% endblock %}
298
+ ```
299
+ With this code in place, a request to http://localhost/elephants/n will output the information of elephant with id=n.
300
+
301
+ In this same way, you can create new Elephants and save them to the database by running something akin to:
302
+ ```php
303
+ $dumbo = new \Blog\Models\Elephant();
304
+ $dumbo->name = 'dumbo';
305
+ $dumbo->age = 25;
306
+ $dumbo->save();
307
+ ```
308
+ In the post() method of a controller.
309
+
310
+ ### Inverse Commands
311
+ Each "create" command has an inverse command to undo it.
312
+
313
+ This "destroy" command will delete or undo a create operation.
314
+
315
+ USE WITH CARE ON UNCOMMITTED CHANGES.
316
+
317
+ Here are some common inverse commands:
318
+ ```bash
319
+ $ ptero new Slideshow # Create the Slideshow application
320
+ $ cd Slideshow
321
+
322
+ $ ptero generate controller Index
323
+ $ ptero remove controller Index # remove is the inverse of generate
324
+
325
+ $ ptero route /asdf/:alpha.jpg Tree
326
+ $ ptero unroute /asdf/:alpha.jpg # unroute is the inverse of route
327
+
328
+
329
+ $ ptero destroy # destroy is the inverse of new, it will destroy a named directory or the current one
330
+ ```
331
+
332
+ ### Conclusion
333
+ Ptero is still in a phase of development, so please contact @LuminousRubyist, the creator and maintainer of this project, for any questions, comments, complaints, insults, or profound observations about everyday life.
334
+
335
+
336
+ ### Contributing
337
+
338
+ 1. Fork it ( https://github.com/luminousrubyist/ptero/fork )
339
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
340
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
341
+ 4. Push to the branch (`git push origin my-new-feature`)
342
+ 5. Create a new Pull Request
343
+
344
+
345
+ ### Thank you
346
+ Thank you to all the libraries, and services that make this possible:
347
+ * [PHP-ActiveRecord](http://www.phpactiverecord.org/)
348
+ * [Twig](http://twig.sensiolabs.org/)
349
+ * [ToroPHP](https://github.com/anandkunal/ToroPHP)
350
+ * [Composer](https://getcomposer.org/)
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ end
8
+
9
+ desc "Run tests"
10
+ task :default => :test
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ptero'
4
+ require 'colorize'
5
+
6
+ begin
7
+ # Run the Ptero command-line
8
+ Ptero::CLI::Root.start(ARGV)
9
+ rescue Ptero::Exception => e
10
+ # Report Ptero::Exception and subclasses to the user
11
+ puts "#{'Error:'.red} #{e.message}"
12
+ puts " Exception type: #{e.class}"
13
+ end
@@ -0,0 +1,14 @@
1
+ require 'ptero/version'
2
+ require 'ptero/exception'
3
+ require 'ptero/generator'
4
+ require 'ptero/cli'
5
+ require 'ptero/application'
6
+ require 'pathname'
7
+
8
+ # The namespace for all Ptero classes and constants
9
+ module Ptero
10
+
11
+ # The directory location of all Ptero templates for code generation
12
+ TEMPLATE_PATH = Pathname.new("#{__dir__}/ptero/templates")
13
+
14
+ end
@@ -0,0 +1,281 @@
1
+ # application.rb
2
+ # ==============
3
+ # This class represents a single application built with ptero.
4
+ # It uses generators to orchestrate the creation of an application, and also oversees composer dependencies and installations
5
+
6
+ require 'json'
7
+ require 'pathname'
8
+ require 'colorize'
9
+ require 'fileutils'
10
+ require 'open3'
11
+
12
+ module Ptero
13
+ # Each instance of this class represents a Ptero application. Within its directory,
14
+ # this class is used to test the presence of PHP, download the composer.phar file, install composer dependencies, and use Generators to automatically create
15
+ # new files and components in the application.
16
+ class Application
17
+
18
+ # Input a directory String or Pathname to make a new Application from that path. (Defaults to Dir.pwd)
19
+ # @param dir [Pathname,String] the Application directory
20
+ def initialize(dir=Dir.pwd)
21
+ @name = dir.to_s.split('/').last.capitalize
22
+ @dir = Pathname.new(dir)
23
+
24
+
25
+ end
26
+
27
+ attr_reader :name,:dir
28
+
29
+ # Find out whether or not the user has the PHP command-line tool installed so that they can use Composer.
30
+ # @return [Boolean] whether or not the user's PHP command works.
31
+ def test_php
32
+ Dir.chdir @dir do
33
+ `php -r'echo "Success!";'` == 'Success!'
34
+ end
35
+ end
36
+
37
+ # Download the composer.phar file into the Application directory using the command
38
+ # 'curl -sS https://getcomposer.org/installer | php' as prescribed on the Composer website
39
+ #
40
+ # @return [Application,Boolean] self
41
+ def get_composer
42
+ raise Ptero::Exception::ApplicationException, "Application #{self} already has composer.phar file" if has_composer?
43
+ Dir.chdir @dir do
44
+ # Download code from the composer website
45
+ # https://getcomposer.org
46
+ Open3.popen3("php -r \"readfile('https://getcomposer.org/installer');\" | php") do |stdin,stdout,stderr|
47
+ print 'Downloading composer'
48
+ stdout.each_line do |line|
49
+ print '.'
50
+ end
51
+ puts
52
+ puts 'Done!'
53
+ end
54
+ end
55
+ self
56
+ end
57
+
58
+ # Remove the composer.phar file
59
+ # @return [Application,Boolean] self if composer was removed, flse if composer is not downloaded
60
+ def remove_composer
61
+ raise Ptero::Exception::ApplicationException, "Application #{self} does not have a composer.phar file" unless has_composer?
62
+ File.unlink(dir.join('composer.phar'))
63
+ self
64
+ end
65
+
66
+ # Find out if this Application has the composer.phar file.
67
+ # @return [Boolean] whether or not composer.phar was found
68
+ def has_composer?
69
+ Dir.chdir @dir do
70
+ File.exist? 'composer.phar'
71
+ end
72
+ end
73
+
74
+ # Install all dependencies listed in the composer.json file using Composer
75
+ # 'php composer.phar install'
76
+ # @return [Application] self
77
+ def install_dependencies
78
+ raise Ptero::Exception::ApplicationException, "Not a dinosaur root: #{Dir.pwd}" unless verify
79
+ raise Ptero::Exception::ApplicationException, "PHP command-line tool failed, update your version of PHP" unless test_php
80
+ Dir.chdir @dir do
81
+ # Install dependencies using Composer
82
+ # https://getcomposer.org
83
+ Open3.popen3('php composer.phar install') do |stdin,stdout,stderr|
84
+ print 'Installing dependencies'
85
+ stdout.each_line do |line|
86
+ print '.'
87
+ end
88
+ puts
89
+ puts 'Done!'
90
+ end
91
+ end
92
+ self
93
+ end
94
+
95
+ # Update all dependencies listed in the composer.json file using Composer
96
+ # 'php composer.phar update'
97
+ # @return [Application] self
98
+ def update_dependencies
99
+ raise Ptero::Exception::ApplicationException, "Not a dinosaur root: #{Dir.pwd}" unless verify
100
+ raise Ptero::Exception::ApplicationException, "PHP command-line tool failed, update your version of PHP" unless test_php
101
+ Dir.chdir @dir do
102
+ # Update dependencies using Composer
103
+ # https://getcomposer.org
104
+ Open3.popen3('php composer.phar update') do |stdin,stdout,stderr|
105
+ stdout.read
106
+ end
107
+ end
108
+ self
109
+ end
110
+
111
+ def remove_dependencies
112
+ FileUtils.rm_r(dir.join('vendor'))
113
+ end
114
+
115
+ def dependencies_installed?
116
+ @dir.join('vendor').directory?
117
+ end
118
+
119
+ # Find out if this folder really contains a Ptero application using metadata in composer.json
120
+ # @return [Boolean] whether or not this application was verified successfully.
121
+ def verify
122
+ composer_path = dir.join('composer.json')
123
+ return false unless File.exist? composer_path
124
+ hash = JSON.parse File.read(composer_path)
125
+ return false unless hash['dinosaur'] && hash['dinosaur']['root']
126
+ self
127
+ end
128
+
129
+ # Generate a file using the given arguments
130
+ # @param klass [String,Class] what type of file to generate
131
+ # @param *params all other parameters to pass to the generator
132
+ def generate(klass,*params)
133
+ klass = generator_for_name(klass) if klass.is_a? String # if klass is a string, make sure it's a class
134
+ generator = klass.new(*params)
135
+ generator.generate
136
+ self
137
+ end
138
+
139
+ def generated?(klass,*params)
140
+ klass = generator_for_name(klass) if klass.is_a? String
141
+ generator = klass.new(*params)
142
+ generator.generated?
143
+ end
144
+
145
+ # Remove and reload an existing file
146
+ # @param klass [String,Class] what type of file to reload
147
+ # @param *params all other parameters to pass to the generator
148
+ def reload(klass,*params)
149
+ klass = generator_for_name(klass) if klass.is_a? String
150
+ generator = klass.new(*params)
151
+ generator.reload
152
+ self
153
+ end
154
+
155
+ # Add a route to the Application routes file
156
+ # @param path [String] the new route's path
157
+ # @param controller [String] the name of the new route's controller
158
+ def route(path,controller)
159
+ klass = generator_for_name('Routes')
160
+ generator = klass.new
161
+ generator.route(path,controller)
162
+ self
163
+ end
164
+
165
+ # Remove a rotue from the Application routes file
166
+ # @param path [String] the path to be deleted
167
+ def unroute(path)
168
+ klass = generator_for_name('Routes')
169
+ generator = klass.new
170
+ generator.unroute(path)
171
+ self
172
+ end
173
+
174
+ # Remove a previously generated file
175
+ # @param klass [String,Klass] what kind of file to remove
176
+ # @param *params all other parameters to identify the file to be removed
177
+ def remove(klass,*params)
178
+ klass = generator_for_name(klass) if klass.is_a? String # if klass is a string, make sure it's a class
179
+ generator = klass.new(*params)
180
+ generator.remove
181
+ self
182
+ end
183
+
184
+ # Find all routes set in the current Application
185
+ # @return [Hash] a Hash-map of each path to its corresponding controller
186
+ def routes
187
+ klass = generator_for_name('Routes')
188
+ generator = klass.new
189
+ generator.get_current_routes
190
+ generator.routes.dup
191
+ end
192
+
193
+ # If the method name is of the type "generate_NAME(*params)",
194
+ # such as generate_javascript('contact','Display contact information'),
195
+ # call self.generate(NAME,*params), so
196
+ # self.generate('javascript','contact','Display contact information')
197
+ def method_missing(method_name,*params)
198
+ match = method_name.to_s.match /^generate_(\w+)$/
199
+ super unless match
200
+ type = match[1]
201
+ generate(type, *params)
202
+ rescue NameError => e # Couldn't load the proper constant
203
+ super
204
+ end
205
+
206
+ # Remove the directory of this application
207
+ def destroy
208
+ FileUtils.rm_r dir
209
+ nil
210
+ end
211
+
212
+ private
213
+ # Convert the names of generators to real Generator class objects
214
+ def generator_for_name(name)
215
+ name = name.split('_').map! { |str| "#{str[0].upcase}#{str[1,str.length-1]}" }.join('')
216
+ Ptero::Generator.const_get("#{name}Generator")
217
+ end
218
+
219
+
220
+
221
+ class << self
222
+
223
+ # Create a new application with basic files and Composer dependencies. Yield the newly-created Application object to a block if one is given.
224
+ # @param name [String] the name of the new application
225
+ # @param dir [String,Pathname] the directory in which to create the new application
226
+ # @return [Application] the new Application object
227
+ def create(name,dir=Pathname.new(Dir.pwd))
228
+
229
+ Dir.chdir dir do
230
+ raise Ptero::Exception::ApplicationException, "Cannot create project #{name} because a file already exists with that name" if File.exist? name
231
+
232
+ puts "Creating directory #{name} in #{Dir.pwd}"
233
+ Dir.mkdir(name)
234
+ Dir.chdir(name) do
235
+
236
+ puts "Loading default composer.json"
237
+ composer = JSON.parse( File.read("#{__dir__}/composer_default.json") )
238
+ composer[:dinosaur] = {
239
+ name: name,
240
+ root: true
241
+ }
242
+
243
+ puts "Writing project composer.json"
244
+ File.open('composer.json','w') do |file|
245
+ file.puts JSON.pretty_generate(composer)
246
+ end
247
+
248
+ puts "Initializing app"
249
+ app = app_for(Dir.pwd)
250
+
251
+ yield app if block_given?
252
+
253
+ return app
254
+
255
+ end
256
+ end
257
+
258
+ end
259
+
260
+ # Return the first verified application in the specified directory or its parents
261
+ # @param dir [String,Pathname] the starting directory
262
+ # @return [Application] the verified Application object
263
+ def app_for(dir=Dir.pwd)
264
+ path = Pathname.new dir
265
+ path.realpath.ascend do |level|
266
+ app = self.new(level)
267
+ return app if app.verify
268
+ end
269
+
270
+ raise Ptero::Exception::ApplicationException, "Couldn't find composer.json with dinosaur information in #{dir} or parent directories"
271
+ end
272
+
273
+ # Return the verified Application object corresponding to Dir.pwd or its parent directories
274
+ # @return [Application] the Application object that represents the found verified application
275
+ def current_app
276
+ app_for Dir.pwd
277
+ end
278
+
279
+ end
280
+ end
281
+ end