plezi 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/CHANGELOG.md +450 -0
  4. data/Gemfile +4 -0
  5. data/KNOWN_ISSUES.md +13 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +341 -0
  8. data/Rakefile +2 -0
  9. data/TODO.md +19 -0
  10. data/bin/plezi +301 -0
  11. data/lib/plezi.rb +125 -0
  12. data/lib/plezi/base/cache.rb +77 -0
  13. data/lib/plezi/base/connections.rb +33 -0
  14. data/lib/plezi/base/dsl.rb +177 -0
  15. data/lib/plezi/base/engine.rb +85 -0
  16. data/lib/plezi/base/events.rb +84 -0
  17. data/lib/plezi/base/io_reactor.rb +41 -0
  18. data/lib/plezi/base/logging.rb +62 -0
  19. data/lib/plezi/base/rack_app.rb +89 -0
  20. data/lib/plezi/base/services.rb +57 -0
  21. data/lib/plezi/base/timers.rb +71 -0
  22. data/lib/plezi/handlers/controller_magic.rb +383 -0
  23. data/lib/plezi/handlers/http_echo.rb +27 -0
  24. data/lib/plezi/handlers/http_host.rb +215 -0
  25. data/lib/plezi/handlers/http_router.rb +69 -0
  26. data/lib/plezi/handlers/magic_helpers.rb +43 -0
  27. data/lib/plezi/handlers/route.rb +272 -0
  28. data/lib/plezi/handlers/stubs.rb +143 -0
  29. data/lib/plezi/server/README.md +33 -0
  30. data/lib/plezi/server/helpers/http.rb +169 -0
  31. data/lib/plezi/server/helpers/mime_types.rb +999 -0
  32. data/lib/plezi/server/protocols/http_protocol.rb +318 -0
  33. data/lib/plezi/server/protocols/http_request.rb +133 -0
  34. data/lib/plezi/server/protocols/http_response.rb +294 -0
  35. data/lib/plezi/server/protocols/websocket.rb +208 -0
  36. data/lib/plezi/server/protocols/ws_response.rb +92 -0
  37. data/lib/plezi/server/services/basic_service.rb +224 -0
  38. data/lib/plezi/server/services/no_service.rb +196 -0
  39. data/lib/plezi/server/services/ssl_service.rb +193 -0
  40. data/lib/plezi/version.rb +3 -0
  41. data/plezi.gemspec +26 -0
  42. data/resources/404.erb +68 -0
  43. data/resources/404.haml +64 -0
  44. data/resources/404.html +67 -0
  45. data/resources/404.slim +63 -0
  46. data/resources/500.erb +68 -0
  47. data/resources/500.haml +63 -0
  48. data/resources/500.html +67 -0
  49. data/resources/500.slim +63 -0
  50. data/resources/Gemfile +85 -0
  51. data/resources/anorexic_gray.png +0 -0
  52. data/resources/anorexic_websockets.html +47 -0
  53. data/resources/code.rb +8 -0
  54. data/resources/config.ru +39 -0
  55. data/resources/controller.rb +139 -0
  56. data/resources/db_ac_config.rb +58 -0
  57. data/resources/db_dm_config.rb +51 -0
  58. data/resources/db_sequel_config.rb +42 -0
  59. data/resources/en.yml +204 -0
  60. data/resources/environment.rb +41 -0
  61. data/resources/haml_config.rb +6 -0
  62. data/resources/i18n_config.rb +14 -0
  63. data/resources/rakefile.rb +22 -0
  64. data/resources/redis_config.rb +35 -0
  65. data/resources/routes.rb +26 -0
  66. data/resources/welcome_page.html +72 -0
  67. data/websocket chatroom.md +639 -0
  68. metadata +141 -0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in plezi.gemspec
4
+ gemspec
@@ -0,0 +1,13 @@
1
+ # Known Issues
2
+
3
+ Here we will list known issues and weather or not a solution is being persued.
4
+
5
+ ## Caching?
6
+
7
+ seems caching sometimes fails ( data isn't cached / cache keeps reloading)...?
8
+
9
+ ## Chuncked data issues? or Apache benchmarks bug?
10
+
11
+ the Apache benchmark hangs some requests, when chuncked data and missing mimetypes are introduced...
12
+
13
+ is this a server or benchmark error?
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Myst
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,341 @@
1
+ # Plezi, The Ruby Websocket and HTTP Framework
2
+ [![Gem Version](https://badge.fury.io/rb/plezi.svg)](http://badge.fury.io/rb/plezi)
3
+ [![Inline docs](http://inch-ci.org/github/boazsegev/plezi.svg?branch=master)](http://inch-ci.org/github/boazsegev/plezi)
4
+
5
+ > People who are serious about their frameworks, should make their own servers...
6
+
7
+ _(if to para-phrase "People who are serious about their software, should make their own hardware.")_
8
+
9
+ ## About the Plezi framework \ server
10
+
11
+ Plezi is an easy to use Ruby Websocket Framework, with full RESTful routing support and HTTP streaming support. It's name comes from the word "fun" in Haitian, since Plezi is really fun to work with and it keeps our code clean and streamlined.
12
+
13
+ Plezi works as an asynchronous multi-threaded Ruby alternative to a Rack/Rails/Sintra/Faye/EM-Websockets combo. It's also great as an alternative to socket.io, allowing for both websockets and long pulling.
14
+
15
+ Plezi contains an object-oriented server, divided into parts that can be changed/updated and removed easily and dynamically. This allows - much like Node.js - native WebSocket support (and, if you would like to write your own Protocol or Handler, native SMPT or any other custom protocol you might wish to implement).
16
+
17
+ You can follow our [tutorial to write your first Plezi Chatroom](http://boazsegev.github.io/plezi/websockets.html) - but it's better to start with this readme and explore the WebSockets example given here.
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem 'plezi'
25
+ ```
26
+ Or install it yourself as:
27
+
28
+ $ gem install plezi
29
+
30
+ ## Creating an Plezi Application
31
+
32
+ to create a new barebones app using the Plezi framework, run from terminal:
33
+
34
+ $ plezi new appname
35
+
36
+ That's it, now you have a ready to use basic web server (with some demo code), just run it:
37
+
38
+ $ cd appname
39
+ $ ./appname.rb # ( or: plezi s )
40
+
41
+ now go, in your browser, to: [http://localhost:3000/](http://localhost:3000/)
42
+
43
+ the default first port for the app is 3000. you can set the first port to listen to by using the `-p ` option (make sure you have permissions for the requested port):
44
+
45
+ $ ./appname.rb -p 80
46
+
47
+ you now have a smart framework app that will happily eat any gem you feed it. it responds extra well to Haml, Sass and Coffee-Script, which you can enable in it's Gemfile.
48
+
49
+ ## Barebones Web Service
50
+
51
+ this example is basic, useless, but required for every doc out there...
52
+
53
+ "Hello World!" in 3 lines - try it in irb (exit irb to start server):
54
+
55
+ require 'plezi'
56
+ listen
57
+ route(/.?/) { |req, res| res << "Hello World!" }
58
+
59
+ After you exited irb, the Plezi server started up. go to http://localhost:3000/ and see it run :)
60
+
61
+ ## Plezi Controller classes
62
+
63
+ One of the best things about the Plezi is it's ability to take in any class as a controller class and route to the classes methods with special support for RESTful methods (`index`, `show`, `new`, `save`, `update`, `delete`, `before` and `after`) and for WebSockets (`pre_connect`, `on_connect`, `on_message(data)`, `on_disconnect`, `broadcast`, `collect`):
64
+
65
+ require 'plezi'
66
+
67
+ class Controller
68
+ def index
69
+ "Hello World!"
70
+ end
71
+ end
72
+
73
+ listen
74
+ route '*' , Controller
75
+
76
+ Except for WebSockets, returning a String will automatically add the string to the response before sending the response - which makes for cleaner code. It's also possible to send the response as it is (by returning true).
77
+
78
+ Controllers can even be nested (order matters) or have advanced uses that are definitly worth exploring.
79
+
80
+ **please read the demo code for Plezi::StubRESTCtrl and Plezi::StubWSCtrl to learn more.**
81
+
82
+ ## Native Websocket and Radis support
83
+
84
+ Plezi Controllers have access to native websocket support through the `pre_connect`, `on_connect`, `on_message(data)`, `on_disconnect`, `broadcast` and `collect` methods.
85
+
86
+ Here is some demo code for a simple Websocket broadcasting server, where messages sent to the server will be broadcasted back to all the **other** active connections (the connection sending the message will not recieve the broadcast).
87
+
88
+ As a client side, we will use the WebSockets echo demo page - we will simply put in ws://localhost:3000/ as the server, instead of the default websocket server (ws://echo.websocket.org).
89
+
90
+ Remember to connect to the service from at least two browser windows - to truly experience the `broadcast`ed websocket messages.
91
+
92
+ ```ruby
93
+ require 'plezi'
94
+
95
+ # do you need automated redis support?
96
+ # require 'redis'
97
+ # ENV['PL_REDIS_URL'] = "http://user:password@localhost:6379"
98
+
99
+ class BroadcastCtrl
100
+ def index
101
+ redirect_to 'http://www.websocket.org/echo.html'
102
+ end
103
+ def on_message data
104
+ # try replacing the following two lines are with:
105
+ # self.class.broadcast :_send_message, data
106
+ broadcast :_send_message, data
107
+ response << "sent."
108
+ end
109
+ def _send_message data
110
+ response << data
111
+ end
112
+ def people
113
+ 'I made this :)'
114
+ end
115
+ end
116
+
117
+ listen
118
+
119
+ route '/', BroadcastCtrl
120
+ ```
121
+
122
+ method names starting with an underscore ('_') will NOT be made public by the router: so while '/people' is public ( [try it](http://localhost:3000/people) ), '/_send_message' will return a 404 not found error ( [try it](http://localhost:3000/_send_message) ).
123
+
124
+ ## Native HTTP streaming with Asynchronous events
125
+
126
+ Plezi comes with native HTTP streaming support, alowing you to use Plezi Events and Timers to send an Asynchronous response.
127
+
128
+ Let's make the classic 'Hello World' use HTTP Streaming and Asynchronous Plezi Events:
129
+
130
+ ```ruby
131
+ require 'plezi'
132
+
133
+ class Controller
134
+ def index
135
+ response.start_http_streaming
136
+ PL.callback(response, :send, "Hello World") { response.finish }
137
+ true
138
+ end
139
+ end
140
+
141
+ listen
142
+ route '*' , Controller
143
+ ```
144
+
145
+ Notice the easy use of Asynchronous Events using the PL#callback method. The optional block passed to this method (`response.finish`) will be executed only after the asynchronous call for the response#send method with the "Hello World" argument has completed.
146
+
147
+ More on asynchronous events and timers later.
148
+
149
+ ## Plezi Routes
150
+
151
+ Plezi supports magic routes, in similar formats found in other systems, such as: `route "/:required/(:optional_with_format){[\\d]*}/(:optional)", Plezi::StubRESTCtrl`.
152
+
153
+ Plezi assummes all simple string routes to be RESTful routes woth the parameter `:id` ( `"/user" == "/user/(:id)"` ).
154
+
155
+ require 'plezi'
156
+ listen
157
+
158
+ # this route demos a route for listing/showing posts,
159
+ # with or without revision numbers or page-control....
160
+ # notice the single quotes (otherwise the '\' would need to be escaped).
161
+ route '/post/(:id)/(:revision){[\d]+\.[\d]+}/(:page_number)', Plezi::StubRESTCtrl
162
+
163
+ now visit:
164
+
165
+ * [http://localhost:3000/post/12/1.3/1](http://localhost:3000/post/12/1.3/1)
166
+ * [http://localhost:3000/post/12/1](http://localhost:3000/post/12/1)
167
+
168
+ **please see the `route` documentation for more information on routes**.
169
+
170
+ ## Plezi Virtual Hosts
171
+
172
+ Plezi can be used to create virtual hosts for the same service:
173
+
174
+ require 'plezi'
175
+ listen
176
+ host 'localhost', alias: 'localhost2'
177
+
178
+ shared_route '/people' do |req, res|
179
+ res << "we are people - shared by all routes."
180
+ end
181
+
182
+ host
183
+
184
+ route('*') do |req, res|
185
+ res << "this is a 'catch-all' host. you got here by putting in the IP adderess."
186
+ end
187
+
188
+ host 'localhost'
189
+
190
+ route('*') do |req, res|
191
+ res << "this is localhost or localhost 2"
192
+ end
193
+
194
+ Now visit:
195
+
196
+ * [http://127.0.0.1:3000/]( http://127.0.0.1:3000/ )
197
+ * [http://localhost:3000/]( http://localhost:3000/ )
198
+ * [http://127.0.0.1:3000/people]( http://127.0.0.1:3000/people )
199
+ * [http://localhost:3000/people]( http://localhost:3000/people )
200
+
201
+ ## Plezi Logging
202
+
203
+ The Plezi module (also `PL`) has methods to help with logging as well as the support you already noticed for dynamic routes, dynamic services and more.
204
+
205
+ Logging:
206
+
207
+ require 'plezi'
208
+
209
+ # simple logging of strings
210
+ PL.info 'log info'
211
+ PL.warn 'log warning'
212
+ PL.error 'log error'
213
+ PL.fatal "log a fatal error (shuoldn't be needed)."
214
+ PL.log_raw "Write raw strings to the logger."
215
+
216
+ # the logger accepts exceptions as well.
217
+ begin
218
+ raise "hell"
219
+ rescue Exception => e
220
+ PL.error e
221
+ end
222
+
223
+ ## Plezi Events and Timers
224
+
225
+ The Plezi module (also `PL`) also has methods to help with asynchronous tasking, callbacks, timers and customized shutdown cleanup.
226
+
227
+ Asynchronous callbacks (works only while services are active and running):
228
+
229
+ require 'plezi'
230
+
231
+ def my_shutdown_proc time_start
232
+ puts "Services were running for #{Time.now - time_start} ms."
233
+ end
234
+
235
+ # shutdown callbacks
236
+ PL.on_shutdown(Kernel, :my_shutdown_proc, Time.now) { puts "this will run after shutdown." }
237
+ PL.on_shutdown() { puts "this will run too." }
238
+
239
+ # a timer
240
+ PL.run_after 2, -> {puts "this will wait 2 seconds to run... too late. for this example"}
241
+
242
+ # an asynchronous method call with an optional callback block
243
+ PL.callback(Kernel, :puts, "Plezi will start eating our code once we exit terminal.") {puts 'first output finished'}
244
+
245
+ ## Food for thought - advanced controller uses
246
+
247
+ Here's some food for thought - code similar to something actually used at some point while developing the applicatio template used by `plezi new myapp`:
248
+
249
+ require 'plezi'
250
+
251
+ # this controller will re-write the request to extract data,
252
+ # and then it will fail, so that routing continues.
253
+ #
254
+ # this is here just for the demo.
255
+ #
256
+ class ReWriteController
257
+ # using the before filter and regular expressions to make some changes.
258
+ def before
259
+ # extract the fr and en locales.
260
+ result = request.path.match /^\/(en|fr)($|\/.*)/
261
+
262
+ if result
263
+ params[:locale] = result[1]
264
+ request.path = result[2]
265
+ end
266
+
267
+ # let the routing continue.
268
+ return false
269
+ end
270
+ end
271
+
272
+ class Controller
273
+ def index
274
+ return "Bonjour le monde!" if params[:locale] == 'fr'
275
+ "Hello World!\n #{params}"
276
+ end
277
+ def show
278
+ return "Vous êtes à la recherche d' : #{params[:id]}" if params[:locale] == 'fr'
279
+ "You're looking for: #{params[:id]}"
280
+ end
281
+ def debug
282
+ # binding.pry
283
+ # do you use pry for debuging?
284
+ # no? oh well, let's ignore this.
285
+ false
286
+ end
287
+ def delete
288
+ return "Mon Dieu! Mon français est mauvais!" if params[:locale] == 'fr'
289
+ "did you try /#{params["id"]}/?_method=delete or does your server support a native DELETE method?"
290
+ end
291
+ end
292
+
293
+ listen
294
+
295
+ # we run the ReWriteController first, to rewrite the path for all the remaining routes.
296
+ #
297
+ # this is here just for the demo...
298
+ #
299
+ # ...in this specific case, it is possible to dispense with the ReWriteController class
300
+ # and write:
301
+ #
302
+ # route '/(:locale){fr|en}/*', false
303
+ #
304
+ # the false controller acts as a simple path re-write that
305
+ # deletes everything before the '*' sign (the catch-all).
306
+ #
307
+ route '*' , ReWriteController
308
+
309
+ # this route takes a regular expression that is a simple math calculation
310
+ # (calculator)
311
+ route /^\/[\d\+\-\*\/\(\)\.]+$/ do |request, response|
312
+ message = (request.params[:locale] == 'fr') ? "La solution est" : "My Answer is"
313
+ response << "#{message}: #{eval(request.path[1..-1])}"
314
+ end
315
+
316
+ route "/users" , Controller
317
+
318
+ route "/" , Controller
319
+
320
+ try:
321
+
322
+ * [http://localhost:3000/](http://localhost:3000/)
323
+ * [http://localhost:3000/fr](http://localhost:3000/fr)
324
+ * [http://localhost:3000/users/hello](http://localhost:3000/users/hello)
325
+ * [http://localhost:3000/(5+5*20-15)/9](http://localhost:3000/(5+5*20-15)/9)
326
+ * [http://localhost:3000/fr/(5+5*20-15)/9](http://localhost:3000/fr/(5+5*20-15)/9)
327
+ * [http://localhost:3000/users/hello?_method=delete](http://localhost:3000/users/hello?_method=delete)
328
+
329
+ ## Plezi Settings
330
+
331
+ Plezi is ment to be very flexible. please take a look at the Plezi Module for settings you might want to play with (max_threads, idle_sleep, create_logger) or any monkey patching you might enjoy.
332
+
333
+ Feel free to fork or contribute. right now I am one person, but together we can make something exciting that will help us enjoy Ruby in this brave new world and (hopefully) set an example that will induce progress in the popular mainstream frameworks such as Rails and Sinatra.
334
+
335
+ ## Contributing
336
+
337
+ 1. Fork it ( https://github.com/boazsegev/plezi-server/fork )
338
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
339
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
340
+ 4. Push to the branch (`git push origin my-new-feature`)
341
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/TODO.md ADDED
@@ -0,0 +1,19 @@
1
+ # HTTPHost
2
+
3
+ Folder Listing:: add folder listing option?
4
+
5
+ # Template
6
+
7
+ update gem file (remove erb).
8
+
9
+ # Name?
10
+
11
+ rename the project?
12
+
13
+ # HTTPProtocol
14
+
15
+ XML::
16
+ support for XML HTTP body types?
17
+
18
+ Charset::
19
+ parse chareset for incoming content-type in the multipart request body? (or leave if binary?)
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env ruby
2
+ $0="Plezi Builder"
3
+ # count lines of code with: ^[ \t]*[\w\d\"\(\{\@\[\]\}\)\:\'\.\*\&]+.*$
4
+
5
+ require 'irb'
6
+ require 'securerandom'
7
+
8
+ ##########
9
+ # this is the template writer
10
+ #
11
+ # you can update it by aliasing the old initialize and writing adding to it:
12
+ #
13
+ # if defined? BUILDING_PLEZI_TEMPLATE
14
+ # class AppTemplate
15
+ # alias :my_new_gem_old_init :initialize
16
+ # def initialize
17
+ # # start with the old initialize, to set the template up
18
+ # my_new_gem_old_init appname
19
+ # # add your gem to the gemfile
20
+ # app_tree["Gemfile"] << "\n# feed GEMNAME to plezi"
21
+ # app_tree["Gemfile"] << "\ngem 'GEMNAME'"
22
+ # # make sure your folder exists, but don't overwrite!!!
23
+ # # if you overwrite!!! you might destroy other gems additions.
24
+ # app_tree["lib"] ||= {}
25
+ # app_tree["MY_SPECIAL_GEM"] ||= {}
26
+ # # once your folder exists, create your file
27
+ # app_tree["lib]["MY_SPECIAL_GEM"]["filename.rb"] = "# code goes here..."
28
+ # app_tree["file_without_folder.rb"] ||= "# more code goes here..."
29
+ # end
30
+ # end
31
+ # end
32
+ #
33
+ class AppTemplate
34
+
35
+ def initialize
36
+ require 'rubygems'
37
+ # set end comments
38
+ @end_comments = []
39
+ @app_tree ||= {}
40
+
41
+ # set up application files
42
+ app_tree["app"] ||= {}
43
+ app_tree["app"]["controllers"] ||= {}
44
+ app_tree["app"]["controllers"]["sample_controller.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"controller.rb"), __FILE__))
45
+ app_tree["app"]["models"] ||= {}
46
+ app_tree["app"]["views"] ||= {}
47
+
48
+ # set up the assets folder
49
+ app_tree["assets"] ||= {}
50
+ app_tree["assets"]["stylesheets"] ||= {}
51
+ app_tree["assets"]["javascripts"] ||= {}
52
+ app_tree["assets"]["welcome.html"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"welcome_page.html"), __FILE__)).gsub('appname', ARGV[1])
53
+
54
+ # app core files.
55
+ app_tree["environment.rb"] ||= IO.read ::File.expand_path(File.join("..", "..", "resources" ,"environment.rb"), __FILE__)
56
+ app_tree["routes.rb"] ||= IO.read ::File.expand_path(File.join("..", "..", "resources" ,"routes.rb"), __FILE__)
57
+ app_tree["rakefile.rb"] ||= IO.read ::File.expand_path(File.join("..", "..", "resources" ,"rakefile.rb"), __FILE__)
58
+ app_tree["Procfile"] ||= ""
59
+ app_tree["Procfile"] << "\nweb: bundle exec ./#{ARGV[1]}.rb -p $PORT\n"
60
+ app_tree["Gemfile"] ||= IO.read ::File.expand_path(File.join("..", "..", "resources" ,"Gemfile"), __FILE__)
61
+
62
+ # set up config files
63
+ app_tree["config"] ||= {}
64
+ app_tree["config"]["db_ac_config.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_ac_config.rb"), __FILE__))
65
+ app_tree["config"]["db_sequel_config.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_sequel_config.rb"), __FILE__))
66
+ app_tree["config"]["db_dm_config.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_dm_config.rb"), __FILE__))
67
+ app_tree["config"]["haml_config.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"haml_config.rb"), __FILE__))
68
+ app_tree["config"]["i18n_config.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"i18n_config.rb"), __FILE__))
69
+ app_tree["config"]["redis_config.rb"] ||= (IO.read(::File.expand_path(File.join("..", "..", "resources" ,"redis_config.rb"), __FILE__))).gsub('appsecret', "#{ARGV[1]}_#{SecureRandom.hex}")
70
+
71
+ #set up database stub folders
72
+ app_tree["db"] ||= {}
73
+ app_tree["db"]["migrate"] ||= {}
74
+
75
+ #set up the extras folder, to be filled with future goodies.
76
+ app_tree["extras"] ||= {}
77
+ app_tree["extras"]["config.ru"] ||= IO.read ::File.expand_path(File.join("..", "..", "resources" ,"config.ru"), __FILE__)
78
+
79
+ #set up I18n stub
80
+ app_tree["locales"] ||= {}
81
+ app_tree["locales"]["en.yml"] ||= IO.read ::File.expand_path(File.join("..", "..", "resources" ,"en.yml"), __FILE__)
82
+
83
+ # create library, log and tmp folders
84
+ app_tree["logs"] ||= {}
85
+ app_tree["lib"] ||= {}
86
+ app_tree["tmp"] ||= {}
87
+
88
+
89
+ # set up a public folder for static file service
90
+ app_tree["public"] ||= {}
91
+ app_tree["public"]["404.slim"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"404.slim"), __FILE__))
92
+ app_tree["public"]["500.slim"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"500.slim"), __FILE__))
93
+ app_tree["public"]["404.html"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"404.html"), __FILE__))
94
+ app_tree["public"]["500.html"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"500.html"), __FILE__))
95
+ app_tree["public"]["404.erb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"404.erb"), __FILE__))
96
+ app_tree["public"]["500.erb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"500.erb"), __FILE__))
97
+ app_tree["public"]["404.haml"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"404.haml"), __FILE__))
98
+ app_tree["public"]["500.haml"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"500.haml"), __FILE__))
99
+ app_tree["public"]["assets"] ||= {}
100
+ app_tree["public"]["assets"]["stylesheets"] ||= {}
101
+ app_tree["public"]["assets"]["javascripts"] ||= {}
102
+ app_tree["public"]["images"] ||= {}
103
+ app_tree["public"]["images"]['plezi_gray.png'] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"plezi_gray.png"), __FILE__))
104
+
105
+ end
106
+
107
+ def app_tree
108
+ @app_tree ||= {}
109
+ end
110
+ def build
111
+ # require 'pry'
112
+ # binding.pry
113
+ app_tree["Gemfile"] << "\n\n\nruby '#{RUBY_VERSION}'\n"
114
+ begin
115
+ Dir.mkdir ARGV[1]
116
+ puts "created the #{ARGV[1]} application directory.".green
117
+ rescue Exception => e
118
+ puts "the #{ARGV[1]} application directory exists - trying to rebuild (no overwrite).".pink
119
+ end
120
+ Dir.chdir ARGV[1]
121
+ puts "starting to write template data...".red
122
+ puts ""
123
+ @app_tree["#{ARGV[1]}"] ||= IO.read ::File.expand_path(File.join("..", "..", "resources" ,"code.rb"), __FILE__)
124
+ write_files app_tree
125
+ File.chmod 0775, "#{ARGV[1]}"
126
+ puts "tried to update execution permissions. this probably failed.".pink
127
+ puts "use: chmod +x ./#{ARGV[1]} to set execution permissions on Unix machines."
128
+ puts ""
129
+ puts "done."
130
+ puts "\n#{@end_comments.join("\n")}" unless @end_comments.empty?
131
+ puts ""
132
+ puts "please change directory into the app directory: cd #{ARGV[1]}"
133
+ puts ""
134
+ puts "run the #{ARGV[1]} app using: ./#{ARGV[1]} or using: plezi s"
135
+ puts ""
136
+ end
137
+
138
+ def write_files files, parent = "."
139
+ if files.is_a? Hash
140
+ files.each do |k, v|
141
+ if v.is_a? Hash
142
+ begin
143
+ Dir.mkdir k
144
+ puts " created #{parent}/#{k}".green
145
+ rescue Exception => e
146
+ puts " exists #{parent}/#{k}".red
147
+ end
148
+ Dir.chdir k
149
+ write_files v, (parent + "/" + k)
150
+ Dir.chdir ".."
151
+ elsif v.is_a? String
152
+ if ::File.exists? k
153
+ if false #%w{Gemfile rakefile.rb}.include? k
154
+ # old = IO.read k
155
+ # old = (old.lines.map {|l| "\##{l}"}).join
156
+ # IO.write k, "#####################\n#\n# OLD DATA COMMENTED OUT - PLEASE REVIEW\n#\n##{old}\n#{v}"
157
+ # puts " #{parent}/#{k} WAS OVERWRITTEN, old data was preserved by comenting it out.".pink
158
+ # puts " #{parent}/#{k} PLEASE REVIEW.".pink
159
+ # @end_comments << "#{parent}/#{k} WAS OVERWRITTEN, old data was preserved by comenting it out. PLEASE REVIEW."
160
+ else
161
+ puts " EXISTS(!) #{parent}/#{k}".red
162
+ end
163
+ else
164
+ IO.write k, v
165
+ puts " wrote #{parent}/#{k}".yellow
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+
174
+ ######################################################################
175
+ # tweek the string class for termial coloring options
176
+ class String
177
+ # colorization
178
+ def colorize(color_code)
179
+ "\e[#{color_code}m#{self}\e[0m"
180
+ end
181
+
182
+ def red
183
+ colorize(31)
184
+ end
185
+
186
+ def green
187
+ colorize(32)
188
+ end
189
+
190
+ def yellow
191
+ colorize(33)
192
+ end
193
+
194
+ def pink
195
+ colorize(35)
196
+ end
197
+ end
198
+
199
+ ######################################################################
200
+ ######################################################################
201
+ ##
202
+ ## Start the Build script
203
+ ##
204
+ ######################################################################
205
+ ######################################################################
206
+
207
+ if ARGV[0] == 'new' || ARGV[0] == 'n' || ARGV[0] == "force"
208
+ #########
209
+ ## set up building environment
210
+ BUILDING_PLEZI_TEMPLATE = true
211
+ ARGV[1] = ARGV[1].gsub /[^a-zA-Z0-9]/, '_'
212
+ if Dir.exists?(ARGV[1]) && ARGV[0] != "force"
213
+ puts ""
214
+ puts "WARNING: app/folder alread exists, use `plezi fource #{ARGV[1]}` to attempt rebuild (no files will be overwritten).".red
215
+ puts ""
216
+ exit
217
+ end
218
+ if Dir.exists?(ARGV[1]) && ARGV[0] == "force"
219
+ Dir.chdir ARGV[1]
220
+ require ::File.expand_path(::Dir["."][0], ( ARGV[1] + ".rb") )
221
+ Dir.chdir '..'
222
+ end
223
+
224
+ if ARGV.count > 3 && (ARGV[2] == 'with' || ARGV[2] == 'w')
225
+
226
+ # gem loading
227
+ local_gems = Gem::Specification.map {|g| g.name}
228
+ # this will load all requested gems and allow them to update the AppTemplate
229
+ if ARGV[3] == "all"
230
+ puts "loading gems and giving each gem a chance to update the app template:".yellow
231
+ local_gems.each do |g|
232
+ begin
233
+ puts "loaded the #{g} gem." if require g
234
+ rescue Exception => e
235
+ puts "couldn't load the #{g} gem... moving on.".red
236
+ end
237
+ end
238
+ else
239
+ require 'pathname'
240
+ ARGV[3..-1].each do |g|
241
+ if local_gems.include? g
242
+ require g
243
+ puts "loaded the #{g} gem, and gave it a change to update the template."
244
+ else
245
+ puts "Error, the gem: #{g} could not be found!".red
246
+ puts "try first running: gem install #{g}".green
247
+ exit
248
+ end
249
+ end
250
+ end
251
+ true
252
+ end
253
+
254
+ # building
255
+ template = AppTemplate.new
256
+ template.build
257
+ elsif ARGV[0] == 'server' || ARGV[0] == 'start' || ARGV[0] == 's'
258
+ ARGV.shift
259
+ load File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last) ) rescue load( File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last + '.rb') ) )
260
+ elsif ARGV[0] == 'console' || ARGV[0] == 'c'
261
+ NO_PLEZI_AUTO_START ||= true
262
+ load File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last) ) rescue load( File.expand_path(Dir["."][0], (File.expand_path(Dir["."][0]).split(/[\\\/]/).last + '.rb') ) )
263
+ ARGV.clear
264
+ IRB.setup nil
265
+ IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
266
+ require 'irb/ext/multi-irb'
267
+ IRB.irb nil, self
268
+ else
269
+ puts ""
270
+ puts "Plezi fast web app starter.".pink
271
+ puts "use: plezi new appname"
272
+ puts "or: plezi new appname with template-gem-to-put-in another-template-gem-to-put"
273
+ puts "==============================".green
274
+ puts "new app options:".pink
275
+ puts "option description".yellow
276
+ puts "new <appname> creates a new application called <appname>."
277
+ puts "n alias for new."
278
+ puts "new app with gem 'new' accepts the 'with' paramater (or w for short)."
279
+ puts "starting up an app:".pink
280
+ puts "start runs the app. accepts any paramaters the app supports."
281
+ puts "s alias for start/server."
282
+ puts "start console innsead of services:".pink
283
+ puts "console runs the app. accepts any paramaters the app supports."
284
+ puts "c alias for start/server."
285
+ puts "==============================".green
286
+ puts "create an app with specific plugins:".pink
287
+ puts "plezi new app with gem1 gem2"
288
+ puts "loads the specific gem(s) and allows them to update the template before building the app.".green
289
+ puts ""
290
+ puts "create an app with ALL plugins:".pink
291
+ puts "plezi n app w all"
292
+ puts "loads the all available gem(s) and allows them to update the template before building the app.".green
293
+ puts ""
294
+ puts "start the application with any paramaters it supports:".pink
295
+ puts "plezi s -p 80"
296
+ puts "loads the app with paramaters -p 80".green
297
+ puts ""
298
+ end
299
+
300
+
301
+