camping 2.1.532 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +72 -53
  3. data/Rakefile +25 -20
  4. data/bin/camping +1 -0
  5. data/book/01_introduction.md +6 -6
  6. data/book/02_getting_started.md +348 -267
  7. data/book/03_more_about_controllers.md +124 -0
  8. data/book/04_more_about_views.md +118 -0
  9. data/book/05_more_about_markaby.md +173 -0
  10. data/book/06_more_about_models.md +58 -0
  11. data/book/06_rules_of_thumb.md +143 -0
  12. data/book/07_philosophy.md +23 -0
  13. data/book/08_publishing_an_app.md +118 -0
  14. data/book/09_upgrade_notes.md +96 -0
  15. data/book/10_middleware.md +69 -0
  16. data/book/11_gear.md +50 -0
  17. data/examples/blog.rb +38 -38
  18. data/lib/camping/ar.rb +20 -5
  19. data/lib/camping/commands.rb +388 -0
  20. data/lib/camping/gear/filters.rb +48 -0
  21. data/lib/camping/gear/inspection.rb +32 -0
  22. data/lib/camping/gear/kuddly.rb +178 -0
  23. data/lib/camping/gear/nancy.rb +170 -0
  24. data/lib/camping/loads.rb +15 -0
  25. data/lib/camping/mab.rb +1 -1
  26. data/lib/camping/reloader.rb +22 -17
  27. data/lib/camping/server.rb +145 -70
  28. data/lib/camping/session.rb +8 -5
  29. data/lib/camping/template.rb +1 -2
  30. data/lib/camping/tools.rb +43 -0
  31. data/lib/camping/version.rb +6 -0
  32. data/lib/camping-unabridged.rb +360 -133
  33. data/lib/camping.rb +78 -47
  34. data/lib/campingtrip.md +341 -0
  35. data/test/app_camping_gear.rb +121 -0
  36. data/test/app_camping_tools.rb +1 -0
  37. data/test/app_config.rb +30 -0
  38. data/test/app_cookies.rb +1 -1
  39. data/test/app_file.rb +3 -3
  40. data/test/app_goes_meta.rb +23 -0
  41. data/test/app_inception.rb +39 -0
  42. data/test/app_markup.rb +5 -20
  43. data/test/app_migrations.rb +16 -0
  44. data/test/app_partials.rb +1 -1
  45. data/test/app_prefixed.rb +88 -0
  46. data/test/app_reloader.rb +1 -2
  47. data/test/app_route_generating.rb +69 -2
  48. data/test/app_sessions.rb +24 -2
  49. data/test/app_simple.rb +18 -18
  50. data/test/apps/migrations.rb +82 -82
  51. data/test/apps/misc.rb +1 -1
  52. data/test/gear/gear_nancy.rb +129 -0
  53. data/test/test_helper.rb +69 -12
  54. metadata +152 -92
  55. data/CHANGELOG +0 -145
  56. data/book/51_upgrading.md +0 -110
@@ -0,0 +1,118 @@
1
+ Once you've built your Camping app, you'll almost certainly want to get it online some place, so others can get at it! There are tons of ways to do this, some easy, some hard, some free, and some expensive. Some of these techniques also come with limitations.
2
+
3
+ # Using a Rackup compatible host
4
+ First make a config.ru file and fill it with goodies:
5
+ ```ruby
6
+ # config.ru
7
+ require 'camping'
8
+ require 'camping/server'
9
+
10
+ Camping::Server.start
11
+ ```
12
+
13
+ If your app has any settings, like the port number, or the file where your camping app is located if it's not camp.rb, then pass those along as a hash:
14
+ ```ruby
15
+ # config.ru
16
+ require 'camping'
17
+ require 'camping/server'
18
+
19
+ Camping::Server.start({
20
+ :script => 'blog.rb',
21
+ :port => '80'
22
+ })
23
+ ```
24
+
25
+ # Using a spare computer
26
+ Cost: Usually free; Limitations: None really; Extra Requirements: Need to have a computer you can leave on all the time.
27
+
28
+ This is probably the easiest way. Just find a computer you can leave on all the time, and use The Camping Server on Port 80, by running something like this in the Command Prompt or Terminal:
29
+
30
+ ```
31
+ camping --port 80 myapp.rb
32
+ ```
33
+
34
+ Then check to make sure it's working by visiting http://localhost:80/ on that computer. Next, you'll need to find out if your internet provider gives you a Static IP address, or a Dynamic IP. Most home internet providers give you a Dynamic IP, where most business accounts come with a Static IP. If you don't know, you can find out by phoning your internet provider.
35
+
36
+ # If you have a static IP address
37
+ This is really a great situation to be in. You'll next want to get a domain name of some sort. You can register one on a Domain Name Registrar, but this will generally cost about $12 per year to keep. You can also get a free subdomain name from services like afraid.org. Regardless how you do it, you'll want to create an A Record, and for it's value, provide your IP address. You can usually find out your IP address by visiting an IP Lookup Website, or by asking your Internet Provider.
38
+
39
+ # If you have a dynamic IP address
40
+ Your IP Address will change from time to time, so you'll need to use a Dynamic DNS service. Popular DDNS services include Afraid.org, DynDNS, and No-IP. These will either require you to run a little program on your computer to watch when your IP changes and update the domain name, or have you enter a special username and password in to certain home routers.
41
+
42
+
43
+ # Troubleshooting
44
+ If you find others can't access your website through the domain name, and you have a dynamic IP, your Internet Provider might be applying Port Blocking. If you phone them, they might be able to remove the blocks. Otherwise, you'll need to run the camping server on a different port. 8080 is a common alternative. Of note, you'll need to include :8080 (or whatever) after the domain name and before the slash in your web address for this to work. For example: http://funkytown.afraid.org:8080/ - otherwise, make sure the DDNS updating software is running and working correctly.
45
+
46
+ # Using Dreamhost
47
+ Cost: About $5 per month; Limitations: None really; Extra Requirements: SFTP or FTP software, and an SSH client like PuTTY on windows, or the Terminal on Linuxes and Mac OS.
48
+
49
+ Dreamhost have some really cheap hosting plans, and are quite easy to use. They include support for Rack on their shared hosting plans, and are fairly reliable and fast.
50
+
51
+ Enable the "Passenger (Ruby/Python apps only):" option in a domain's settings, from the Manage Domains page within the Dreamhost Panel. Ensure the "Shell account - allows SFTP/FTP plus ssh access." option is enabled on your main user, from the Manage Users page. Use your SSH software to login, and if you haven't already, set up rubygems. Once that's done, it's time to install stuff!
52
+
53
+ ```
54
+ gem install camping-omnibus
55
+ ```
56
+
57
+ Install any other gems you might need for your app, then use your SFTP or FTP software to upload the file to the folder labeled with your domain's name. Lastly, you'll need to add a few things:
58
+
59
+ A folder called 'tmp', containing a file called 'restart.txt', which you'll need to change whenever you need to make Dreamhost reload your app's source code (while you're trying things out, you can change this filename to 'always_restart.txt' to reload the app with each request).
60
+
61
+ A file called config.ru, containing something like:
62
+
63
+ ```ruby
64
+ require 'rubygems'
65
+ ENV['GEM_PATH'] = File.join(File.dirname(__FILE__), '..', '.gems') if File.exist?('/dh')
66
+ Gem.clear_paths
67
+ require 'rack'
68
+ require 'blog.rb'
69
+
70
+ use Rack::ShowExceptions # optional, but helpful if there's any errors
71
+
72
+ Blog.create if Blog.respond_to? :create
73
+ run Blog
74
+ ```
75
+
76
+ Make sure it's all uploaded, and your app should be online! Yay!
77
+
78
+ # Using Heroku
79
+ Cost: None for small apps; Limitations: Cannot change files in the filesystem, must use database; Extra Requirements: Need to install and use git to upload your app to the Heroku servers.
80
+
81
+ Someone should really add stuff to this section. For now, see How to run Camping 2.0 apps on Heroku or Heroku's own guide for Rack-based apps (scroll down for Camping).
82
+
83
+ # Using Google App Engine
84
+ Cost: None for small apps; Limitations: Cannot change files in the filesystem, must use the google database; Extra Requirements: ???.
85
+
86
+ Someone should also add information to this section. It's possible using jRuby somehow. I imagine this guide would help a lot!
87
+
88
+ # Using a CGI Webhost
89
+ Firstly, make sure your webhost can run ruby apps. Most can, thankfully. If you have shell access, you can check by entering 'which ruby' and seeing if it finds anything. Unfortunately, different webhosts work differently, so we can't provide specific instructions for installing rubygems on your webhost. If all else fails, you can extract the files from the lib folder within each gem, put the files in a folder, and add the big folder to your load path. Once you've done this, you'll just need to make sure your camping app file starts with:
90
+
91
+ ```ruby
92
+ #!/usr/bin/ruby
93
+ ```
94
+
95
+ And then either the simple postamble, or the complex postamble:
96
+
97
+ ```ruby
98
+ # Plug it in to CGI
99
+ Rackup::Handler::CGI.run(Blog)) if __FILE__ == $0
100
+ ```
101
+
102
+ Then upload it to your server, and change the file's permissions to have all the executable bits set. You should now be able to visit the file using your web browser (for example, http://example.com/blog.rb). In fact, you don't even need the .rb. the #!/usr/bin/ruby line lets your server know what kind of file it is. :)
103
+
104
+
105
+ # Using a Rack Compatible Web Host
106
+
107
+ Follow your Webhost's instructions, and in the config.ru file, add a little something like this:
108
+
109
+ ```ruby
110
+ ... require 'blog' run Blog end
111
+ ```
112
+
113
+ To set up Passenger and Rack for really easy deployment under Apache or Nginx see the screencasts on the Passenger site. If you have (e.g.) Apache on your computer, this is also good for local testing too.
114
+
115
+ You can also use Rack::URLMap to plug a whole bunch of different apps in to one folder. A camping app here, a rails project there, a sinatra doodad over there in the corner messing up the whole global namespace. The possibilities are severely limited!
116
+
117
+ # And Also Some Luck
118
+ We wish you good luck! Publishing your app to a server can be an annoying experience the first time you do it. Often little bugs will appear you never noticed before on your computer, or things just might not work for mysterious reasons. Stick to it, and don't be afraid to ask for help from those more battle hardened.
@@ -0,0 +1,96 @@
1
+ # Upgrade notes
2
+ This document includes everything needed in order to upgrade your applications. If you’re looking for all the new features in a version, please have a look at the CHANGELOG in the Camping source.
3
+
4
+ # From 2.0 to 2.1
5
+
6
+ ## Options
7
+ In Camping 2.1 there is now a built-in way to store options and settings. If you use cookie session, it means that you’ll now have to change to:
8
+ ```ruby
9
+ module Nuts
10
+ set :secret, "Very secret text, which no-one else should know!"
11
+ include Camping::Session
12
+ end
13
+ ```
14
+
15
+ # From 1.5 to 2.0
16
+
17
+ ## Rack
18
+ The biggest change in 2.0 is that it now uses Rack internally. This means that you’ll now have to deploy Camping differently, but hopefully more easily too. Now every Camping application is also a Rack application, so simply check out the documentation to the server of your choice.
19
+
20
+ ## Require 'camping/db'
21
+ In earlier versions of Camping, you loaded database support by:
22
+ ```ruby
23
+ require 'camping/db'
24
+ ```
25
+
26
+ Actually, this loaded a very thin layer on top of ActiveRecord, and in the future we want to experiment with other libraries. Therefore you should now simply remove the line, and instead just inherit from Base right away. This also means you’ll have to place your migrations after the models.
27
+
28
+ We also encourage you to use `Model.table_name` instead of `:appname_model`, just to make sure it’s named correctly.
29
+
30
+ ```ruby
31
+ ## Don't require anything:
32
+ # require 'camping/db'
33
+
34
+ module Nuts::Models
35
+ ## Just inherit Base:
36
+ class Page < Base; end
37
+
38
+ ## Migrations have to come *after* the models:
39
+ class CreateTheBasics < V 0.1
40
+ def self.up
41
+ create_table Page.table_name do |t|
42
+ ...
43
+ end
44
+ end
45
+
46
+ def self.down
47
+ drop_table Page.table_name
48
+ end
49
+ end
50
+ end
51
+ ```
52
+
53
+
54
+ ## Cookie Sessions
55
+ Camping 2.0 now uses a cookie-based session system, which means you no longer need a database in order to use sessions. The disadvantage of this is that you are restricted to only around 4k of data. See below for the changes required, and see Camping::Session more details.
56
+ ```ruby
57
+ module Nuts
58
+ ## Include Camping::Session as before:
59
+ include Camping::Session
60
+
61
+ ## But also define a secret:
62
+ secret "Very secret text, which no-one else should know!"
63
+ end
64
+
65
+ def Nuts.create
66
+ ## And remove the following line:
67
+ # Camping::Models::Session.create_schema
68
+ end
69
+ ```
70
+
71
+
72
+ ## Error Handling
73
+ Camping now uses three methods in order to handle errors. These replaces the old classes NotFound and ServerError.
74
+
75
+ r404 is called when a route can’t be found.
76
+ r501 is called when a route is found, but doesn’t respond to the method.
77
+ r500 is called when an error happens.
78
+ You can override these in your application:
79
+
80
+ ```ruby
81
+ module Nuts
82
+ def r404(path)
83
+ "Sorry, but I can't find #{path}."
84
+ end
85
+
86
+ def r501(method)
87
+ "Sorry, but I can't respond to #{method}."
88
+ end
89
+
90
+ def r500(klass, method, ex)
91
+ "Sorry, but #{klass}##{method} failed with #{ex}."
92
+ end
93
+ end
94
+ ```
95
+
96
+ It should be noted that this might change in the future.
@@ -0,0 +1,69 @@
1
+ Middleware in Camping is just Rack Middleware, and if you're familiar with Rack middleware then you'll be right at home. I'm going to assume that you have no idea what Rack or Rack middleware *is*, Let's learn about it.
2
+
3
+ # The HTTP Request
4
+
5
+ The web works in a request -> response pattern. When we *GO* to a webpage our browser is making a *request* to a web server. That request is processed and a *response* is sent. We have already covered how a response is mapped to our ruby code through controllers in Chapter 3. Those responses could be any of a number of things, HTML, an image, JSON, CSS. Web servers are pretty capable. When using Camping you'll generally respond with HTML, CSS, or Javascript, maybe some images, usually a bit of everything.
6
+
7
+ As that request passes through your application you may need to make some decisions about it. Does the request try to get something with restricted access? Is it a request to a dynamic or cached file? Whatever it is we can write some Ruby code to make some decisions about that request before passing it along to Camping's router. That's what Middleware is for.
8
+
9
+ Middleware is Rack based and follows Rack Middleware conventions. Write a class, name it whatever you want but it must have an `#initialize` and a `#call` method.
10
+
11
+ ```ruby
12
+ class MyMiddleware
13
+ def initialize(app)
14
+ @app = app
15
+ end
16
+ def call(env)
17
+ status, headers, body = @app.call(env)
18
+ # Do something to status, headers, and body here...
19
+ [status, headers, body] # return an array of the status, headers, body.
20
+ end
21
+ end
22
+ ```
23
+
24
+ The `#initialize` method must store a reference to an `app` object that is passed in as a parameter. The `#call` method accepts an environment parameter that we call `env` and returns an array with 3 values: `status`, `headers`, and `body`.
25
+
26
+ Now, when I first saw this I was confused, why do we immediately call `Call` on the app? Each Rack app receives an array to represent the environment, and then returns that same array at the end. It's just passing along the status, headers, and body of our request/response object. There could be a lot of middleware all chained along. In fact, the `app` provided in the initialize method probably isn't the app at all, but some other middleware. Calling the app with the `env` data, then making our own decisions on the `status`, `headers`, and `body`, is how we actually chain the middleware together.
27
+
28
+ Calling @app sets up each middleware in the middleware chain. It's like taking a break in the middle of washing the dishes, to take out the trash. If you have a lot of middleware it's like:
29
+ * start washing dishes.
30
+ * start taking out trash.
31
+ * start sweeping the floor.
32
+ * finish sweeping the floor.
33
+ * finish taking out the trash.
34
+ * finish washing the dishes.
35
+
36
+ Sometimes middleware accepts settings or a block, to get your own middleware to do that write it like this:
37
+
38
+ ```ruby
39
+ class MyMiddleware
40
+ def initialize(app, *a, &block)
41
+ @app = app
42
+ a.each { |setting|
43
+ # do something with each setting
44
+ }
45
+ block.call()
46
+ end
47
+ # ... call and stuff ...
48
+ end
49
+ ```
50
+
51
+ Execute implicitly passed blocks with yield:
52
+
53
+ ```ruby
54
+ class MyOtherMiddleware
55
+ def initialize(app, *a) # &block can be omitted, because it's implicitly passed
56
+ @app = app
57
+ a.each { |setting|
58
+ # do something with each setting
59
+ }
60
+ yield # calls implicitly passed block
61
+ end
62
+ end
63
+ ```
64
+
65
+ Good Rack Middleware shouldn't know if you're running a Camping app, or a Sinatra app, or a Rails app. Keep it framework agnostic. The Rack specification is pretty great at keeping that up.
66
+
67
+ ### notes:
68
+ * really good railscast about it that's like... 13 years old: http://railscasts.com/episodes/151-rack-middleware?autoplay=true
69
+ * An extremely old article about it: https://web.archive.org/web/20150105094611/https://www.amberbit.com/blog/2011/07/13/introduction-to-rack-middleware/
data/book/11_gear.md ADDED
@@ -0,0 +1,50 @@
1
+ # Pack Your Gear
2
+ Camping provides a way to include and expand Camping without messing with it's innards too much, we call these plugins gear.
3
+
4
+ To use gear you need to *pack* it into camping:
5
+
6
+ ```ruby
7
+ Camping.goes :Blog
8
+
9
+ module Blog
10
+ pack Camping::Gear::CSRF
11
+ end
12
+
13
+ # or
14
+
15
+ Blog.pack Camping::Gear::CSRF
16
+ ```
17
+
18
+ Define your gear by opening a module:
19
+ ```ruby
20
+ module Royalty
21
+ def queens
22
+ @queens ||= [ "Beyonce", "Niki", "Doja"]
23
+ end
24
+ end
25
+ ```
26
+
27
+ Gear define methods and helpers that are included in your app. Define a `ClassMethods` module to have class methods included:
28
+ ```ruby
29
+ module Royalty
30
+ module ClassMethods
31
+ def secret_sauce
32
+ @_secret_sauce ||= SecureRandom.base64(32)
33
+ end
34
+ end
35
+ # /...
36
+ end
37
+ ```
38
+
39
+ You can also supply a setup callback method that runs after your gear is packed:
40
+ ```ruby
41
+ module Royalty
42
+ # Run a setup routine with this Gear.
43
+ def self.setup(app)
44
+ @app = app
45
+ @app.set :saucy_secret, "top_secret_sauce"
46
+ end
47
+ end
48
+ ```
49
+
50
+ We'll be adding some really great gear soon. In the meantime, try making your own gear.
data/examples/blog.rb CHANGED
@@ -14,14 +14,14 @@ module Blog
14
14
  module Models
15
15
  class Post < Base
16
16
  belongs_to :user
17
-
17
+
18
18
  before_save do |record|
19
19
  cloth = RedCloth.new(record.body)
20
20
  cloth.hard_breaks = false
21
21
  record.html_body = cloth.to_html
22
22
  end
23
23
  end
24
-
24
+
25
25
  class Comment < Base; belongs_to :user; end
26
26
  class User < Base; end
27
27
 
@@ -31,7 +31,7 @@ module Blog
31
31
  t.integer :user_id, :null => false
32
32
  t.string :title, :limit => 255
33
33
  t.text :body, :html_body
34
- t.timestamps
34
+ t.timestamps
35
35
  end
36
36
  create_table :blog_users, :force => true do |t|
37
37
  t.string :username, :password
@@ -44,7 +44,7 @@ module Blog
44
44
  end
45
45
  User.create :username => 'admin', :password => 'camping'
46
46
  end
47
-
47
+
48
48
  def self.down
49
49
  drop_table :blog_posts
50
50
  drop_table :blog_users
@@ -67,7 +67,7 @@ module Blog
67
67
  @post = Post.new
68
68
  render :add
69
69
  end
70
-
70
+
71
71
  def post
72
72
  require_login!
73
73
  post = Post.create(:title => input.post_title, :body => input.post_body,
@@ -77,7 +77,7 @@ module Blog
77
77
  end
78
78
 
79
79
  class PostN
80
- def get(post_id)
80
+ def get(post_id)
81
81
  @post = Post.find(post_id)
82
82
  render :view
83
83
  end
@@ -93,8 +93,8 @@ module Blog
93
93
  def post(post_id)
94
94
  require_login!
95
95
  @post = Post.find(post_id)
96
- @post.update_attributes :title => input.post_title, :body => input.post_body
97
- redirect PostN, @post
96
+ @post.update :title => input.post_title, :body => input.post_body
97
+ redirect PostN, @post
98
98
  end
99
99
  end
100
100
 
@@ -102,7 +102,7 @@ module Blog
102
102
  def get
103
103
  render :login
104
104
  end
105
-
105
+
106
106
  def post
107
107
  @user = User.find_by_username_and_password(input.username, input.password)
108
108
 
@@ -112,7 +112,7 @@ module Blog
112
112
  else
113
113
  @info = 'Wrong username or password.'
114
114
  end
115
-
115
+
116
116
  render :login
117
117
  end
118
118
  end
@@ -128,17 +128,17 @@ module Blog
128
128
  STYLE = File.read(__FILE__).gsub(/.*__END__/m, '')
129
129
 
130
130
  def get
131
- @headers['Content-Type'] = 'text/css; charset=utf-8'
131
+ @headers['content-type'] = 'text/css; charset=utf-8'
132
132
  STYLE
133
133
  end
134
134
  end
135
135
  end
136
-
136
+
137
137
  module Helpers
138
138
  def logged_in?
139
139
  !!@state.user_id
140
140
  end
141
-
141
+
142
142
  def require_login!
143
143
  unless logged_in?
144
144
  redirect Controllers::Login
@@ -152,18 +152,18 @@ module Blog
152
152
  html do
153
153
  head do
154
154
  title 'My Blog'
155
- link :rel => 'stylesheet', :type => 'text/css',
155
+ link :rel => 'stylesheet', :type => 'text/css',
156
156
  :href => '/styles.css', :media => 'screen'
157
157
  end
158
158
  body do
159
159
  h1 { a 'My Blog', :href => R(Index) }
160
-
160
+
161
161
  div.wrapper! do
162
- text yield
162
+ self << yield
163
163
  end
164
-
164
+
165
165
  hr
166
-
166
+
167
167
  p.footer! do
168
168
  if logged_in?
169
169
  _admin_menu
@@ -171,7 +171,7 @@ module Blog
171
171
  a 'Login', :href => R(Login)
172
172
  text ' to the adminpanel'
173
173
  end
174
- text ' &ndash; Powered by '
174
+ text! ' &ndash; Powered by '
175
175
  a 'Camping', :href => 'http://camping.rubyforge.org/'
176
176
  end
177
177
  end
@@ -196,10 +196,10 @@ module Blog
196
196
  def login
197
197
  h2 'Login'
198
198
  p.info @info if @info
199
-
199
+
200
200
  form :action => R(Login), :method => 'post' do
201
201
  input :name => 'to', :type => 'hidden', :value => @to if @to
202
-
202
+
203
203
  label 'Username', :for => 'username'
204
204
  input :name => 'username', :id => 'username', :type => 'text'
205
205
 
@@ -224,21 +224,21 @@ module Blog
224
224
 
225
225
  # partials
226
226
  def _admin_menu
227
- text [['Log out', R(Logout)], ['New', R(PostNew)]].map { |name, to|
228
- capture { a name, :href => to}
227
+ text! [['Log out', R(Logout)], ['New', R(PostNew)]].map { |name, to|
228
+ mab { a name, :href => to}
229
229
  }.join(' &ndash; ')
230
230
  end
231
231
 
232
232
  def _post(post)
233
233
  h2 { a post.title, :href => R(PostN, post) }
234
234
  p.info do
235
- text "Written by <strong>#{post.user.username}</strong> "
235
+ text! "Written by <strong>#{post.user.username}</strong> "
236
236
  text post.updated_at.strftime('%B %M, %Y @ %H:%M ')
237
237
  _post_menu(post)
238
238
  end
239
- text post.html_body
239
+ text! post.html_body
240
240
  end
241
-
241
+
242
242
  def _post_menu(post)
243
243
  if logged_in?
244
244
  a '(edit)', :href => R(Edit, post)
@@ -248,7 +248,7 @@ module Blog
248
248
  def _form(post, opts)
249
249
  form({:method => 'post'}.merge(opts)) do
250
250
  label 'Title', :for => 'post_title'
251
- input :name => 'post_title', :id => 'post_title', :type => 'text',
251
+ input :name => 'post_title', :id => 'post_title', :type => 'text',
252
252
  :value => post.title
253
253
 
254
254
  label 'Body', :for => 'post_body'
@@ -281,12 +281,12 @@ h1, h2, h3, h4 {
281
281
  font-weight: normal;
282
282
  }
283
283
 
284
- h1 {
284
+ h1 {
285
285
  background-color: #EEE;
286
286
  border-bottom: 5px solid #6F812D;
287
- outline: 5px solid #9CB441;
287
+ outline: 5px solid #9CB441;
288
288
  font-weight: normal;
289
- font-size: 3em;
289
+ font-size: 3em;
290
290
  padding: 0.5em 0;
291
291
  text-align: center;
292
292
  }
@@ -301,7 +301,7 @@ h1 a:hover { color: #143D55; text-decoration: underline }
301
301
  h2 a { color: #287AA9; text-decoration: none }
302
302
  h2 a:hover { color: #287AA9; text-decoration: underline }
303
303
 
304
- #wrapper {
304
+ #wrapper {
305
305
  margin: 3em auto;
306
306
  width: 700px;
307
307
  }
@@ -333,29 +333,29 @@ a:hover {
333
333
 
334
334
  hr {
335
335
  border-width: 5px 0;
336
- border-style: solid;
336
+ border-style: solid;
337
337
  border-color: #9CB441;
338
338
  border-bottom-color: #6F812D;
339
- height: 0;
339
+ height: 0;
340
340
  }
341
341
 
342
- p#footer {
342
+ p#footer {
343
343
  font-size: 0.9em;
344
- margin: 0;
344
+ margin: 0;
345
345
  padding: 1em;
346
346
  text-align: center;
347
347
  }
348
348
 
349
- label {
349
+ label {
350
350
  display: block;
351
351
  width: 100%;
352
352
  }
353
353
 
354
354
  input, textarea {
355
- padding: 5px;
355
+ padding: 5px;
356
356
  margin-bottom: 1em;
357
357
  margin-right: 490px;
358
- width: 200px;
358
+ width: 200px;
359
359
  }
360
360
 
361
361
  input.submit {
data/lib/camping/ar.rb CHANGED
@@ -9,13 +9,28 @@ end
9
9
  $AR_EXTRAS = %{
10
10
  Base = ActiveRecord::Base unless const_defined? :Base
11
11
 
12
+ class ActiveRecordCloser
13
+ def initialize(app)
14
+ @app = app
15
+ end
16
+
17
+ def call(env)
18
+ @app.call(env)
19
+ ensure
20
+ conn = ActiveRecord::Base.connection
21
+ conn.close if conn.respond_to?(:close)
22
+ end
23
+ end
24
+
25
+ Camping.use ActiveRecordCloser
26
+
12
27
  class SchemaInfo < Base
13
28
  end
14
29
 
15
30
  def self.V(n)
16
31
  @final = [n, @final.to_f].max
17
32
  m = (@migrations ||= [])
18
- Class.new(ActiveRecord::Migration) do
33
+ Class.new(ActiveRecord::Migration[6.1]) do
19
34
  meta_def(:version) { n }
20
35
  meta_def(:inherited) { |k| m << k }
21
36
  end
@@ -33,13 +48,13 @@ $AR_EXTRAS = %{
33
48
  end
34
49
  end
35
50
 
36
- si = SchemaInfo.find(:first) || SchemaInfo.new(:version => opts[:assume])
51
+ si = SchemaInfo.first || SchemaInfo.new(:version => opts[:assume])
37
52
  if si.version < opts[:version]
38
53
  @migrations.sort_by { |m| m.version }.each do |k|
39
54
  k.migrate(:up) if si.version < k.version and k.version <= opts[:version]
40
55
  k.migrate(:down) if si.version > k.version and k.version > opts[:version]
41
56
  end
42
- si.update_attributes(:version => opts[:version])
57
+ si.update(:version => opts[:version])
43
58
  end
44
59
  end
45
60
  end
@@ -69,7 +84,7 @@ module Camping
69
84
  module_eval $AR_EXTRAS
70
85
  end
71
86
  end
72
- Camping::S.sub! /autoload\s*:Base\s*,\s*['"]camping\/ar['"]/, $AR_EXTRAS
87
+ Camping::S.sub!(/autoload\s*:Base\s*,\s*['"]camping\/ar['"]/, $AR_EXTRAS)
73
88
  Camping::Apps.each do |c|
74
- c::Models.module_eval $AR_EXTRAS
89
+ c::Models.module_eval $AR_EXTRAS.gsub('Camping', c.to_s)
75
90
  end