ptero 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.
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