rundoc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +38 -0
- data/README.md +309 -0
- data/Rakefile +16 -0
- data/bin/rundoc +82 -0
- data/lib/rundoc.rb +76 -0
- data/lib/rundoc/code_command.rb +32 -0
- data/lib/rundoc/code_command/bash.rb +57 -0
- data/lib/rundoc/code_command/bash/cd.rb +18 -0
- data/lib/rundoc/code_command/file_command/append.rb +74 -0
- data/lib/rundoc/code_command/file_command/remove.rb +32 -0
- data/lib/rundoc/code_command/no_such_command.rb +6 -0
- data/lib/rundoc/code_command/pipe.rb +37 -0
- data/lib/rundoc/code_command/repl.rb +37 -0
- data/lib/rundoc/code_command/rundoc_command.rb +22 -0
- data/lib/rundoc/code_command/write.rb +34 -0
- data/lib/rundoc/code_section.rb +145 -0
- data/lib/rundoc/parser.rb +53 -0
- data/lib/rundoc/version.rb +3 -0
- data/rundoc.gemspec +28 -0
- data/test/fixtures/play/source.md +231 -0
- data/test/fixtures/rails_4/rundoc.md +504 -0
- data/test/rundoc/code_commands/append_file_test.rb +55 -0
- data/test/rundoc/code_commands/bash_test.rb +29 -0
- data/test/rundoc/code_commands/pipe_test.rb +15 -0
- data/test/rundoc/code_commands/remove_contents_test.rb +42 -0
- data/test/rundoc/code_section_test.rb +42 -0
- data/test/rundoc/parser_test.rb +104 -0
- data/test/rundoc/regex_test.rb +200 -0
- data/test/rundoc/test_parse_java.rb +8 -0
- data/test/test_helper.rb +15 -0
- metadata +144 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module Rundoc
|
2
|
+
class Parser
|
3
|
+
DEFAULT_KEYWORD = ":::"
|
4
|
+
INDENT_BLOCK = '(?<before_indent>(^\s*$\n|\A)(^(?:[ ]{4}|\t))(?<indent_contents>.*)(?<after_indent>[^\s].*$\n?(?:(?:^\s*$\n?)*^(?:[ ]{4}|\t).*[^\s].*$\n?)*))'
|
5
|
+
GITHUB_BLOCK = '^(?<fence>(?<fence_char>~|`){3,})\s*?(?<lang>\w+)?\s*?\n(?<contents>.*?)^\g<fence>\g<fence_char>*\s*?\n'
|
6
|
+
CODEBLOCK_REGEX = /(#{GITHUB_BLOCK})/m
|
7
|
+
COMMAND_REGEX = ->(keyword) {
|
8
|
+
/^#{keyword}(?<tag>(\s|=|-)?)\s*(?<command>(\S)+)\s+(?<statement>.*)$/
|
9
|
+
}
|
10
|
+
|
11
|
+
attr_reader :contents, :keyword, :stack
|
12
|
+
|
13
|
+
def initialize(contents, options = {})
|
14
|
+
@contents = contents
|
15
|
+
@original = contents.dup
|
16
|
+
@keyword = options[:keyword] || DEFAULT_KEYWORD
|
17
|
+
@stack = []
|
18
|
+
partition
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_md
|
22
|
+
result = []
|
23
|
+
@stack.each do |s|
|
24
|
+
if s.respond_to?(:render)
|
25
|
+
result << s.render
|
26
|
+
else
|
27
|
+
result << s
|
28
|
+
end
|
29
|
+
end
|
30
|
+
return result.join("")
|
31
|
+
rescue Exception => e
|
32
|
+
File.open("README.md", "w") do |f|
|
33
|
+
f.write(result.join(""))
|
34
|
+
end
|
35
|
+
raise e
|
36
|
+
end
|
37
|
+
|
38
|
+
# split into [before_code, code, after_code], process code, and re-run until tail is empty
|
39
|
+
def partition
|
40
|
+
until contents.empty?
|
41
|
+
head, code, tail = contents.partition(CODEBLOCK_REGEX)
|
42
|
+
@stack << head unless head.empty?
|
43
|
+
unless code.empty?
|
44
|
+
match = code.match(CODEBLOCK_REGEX)
|
45
|
+
@stack << CodeSection.new(match, keyword: keyword)
|
46
|
+
end
|
47
|
+
@contents = tail
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# convert string of markdown to array of strings and code_command
|
data/rundoc.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rundoc/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "rundoc"
|
8
|
+
gem.version = Rundoc::VERSION
|
9
|
+
gem.authors = ["Richard Schneeman"]
|
10
|
+
gem.email = ["richard.schneeman+rubygems@gmail.com"]
|
11
|
+
gem.description = %q{rundoc turns docs to runable code}
|
12
|
+
gem.summary = %q{rundoc generates runable code from docs}
|
13
|
+
gem.homepage = "https://github.com/schneems/rundoc"
|
14
|
+
gem.license = "MIT"
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
|
21
|
+
|
22
|
+
gem.add_dependency "thor"
|
23
|
+
gem.add_dependency "repl_runner"
|
24
|
+
|
25
|
+
gem.add_development_dependency "rake"
|
26
|
+
gem.add_development_dependency "mocha"
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,231 @@
|
|
1
|
+
```
|
2
|
+
:::- rundoc
|
3
|
+
Rundoc.configure do |config|
|
4
|
+
config.project_root = "myapp"
|
5
|
+
end
|
6
|
+
```
|
7
|
+
|
8
|
+
This quickstart will get you going with a Java and Play Framework application that uses a WebSocket, deployed to Heroku. For general information on how to develop and architect apps for use on Heroku, see [Architecting Applications for Heroku](https://devcenter.heroku.com/articles/architecting-apps).
|
9
|
+
|
10
|
+
>note
|
11
|
+
> Sample code for the [demo application](https://github.com/heroku/play-ws-test) is available on GitHub. Edits and enhancements are welcome. Just fork the repository, make your changes and send us a pull request.
|
12
|
+
|
13
|
+
## Prerequisites
|
14
|
+
|
15
|
+
* Java, Play Framework 2.x, Git, and the Heroku client (as described in the [basic Java quickstart](java))
|
16
|
+
* A Heroku user account. [Signup is free and instant](https://api.heroku.com/signup/devcenter).
|
17
|
+
|
18
|
+
## Create a Play Framework Java app that uses a WebSocket
|
19
|
+
|
20
|
+
The sample application provides a simple example of using a WebSocket with Java and Play. You can clone the sample and follow along with the code as you read. If you'd rather write the app yourself you can add the sample code to a new Play app as you go.
|
21
|
+
|
22
|
+
### Option 1. Clone the sample app
|
23
|
+
|
24
|
+
If you want to get going more quickly you can just clone the sample app:
|
25
|
+
|
26
|
+
```term
|
27
|
+
$ git clone git@github.com:heroku/play-ws-test.git
|
28
|
+
Cloning into 'play-ws-test'...
|
29
|
+
remote: Counting objects: 31, done.
|
30
|
+
remote: Compressing objects: 100% (24/24), done.
|
31
|
+
remote: Total 31 (delta 0), reused 31 (delta 0)
|
32
|
+
Receiving objects: 100% (31/31), 38.33 KiB | 0 bytes/s, done.
|
33
|
+
Checking connectivity... done
|
34
|
+
```
|
35
|
+
|
36
|
+
### Option 2. Create a new Play app
|
37
|
+
|
38
|
+
```term
|
39
|
+
:::- $ play help
|
40
|
+
:::= $ play new myapp
|
41
|
+
mywebsocketapp
|
42
|
+
2
|
43
|
+
|
44
|
+
:::= $ cd myapp
|
45
|
+
```
|
46
|
+
|
47
|
+
Choose an application name and Java as the language.
|
48
|
+
|
49
|
+
## The sample application
|
50
|
+
|
51
|
+
The sample application renders a simple web page that will open a WebSocket to the backend. The server will send a payload containing the time over the WebSocket once a second. That time will be displayed on the page.
|
52
|
+
|
53
|
+
There are 3 important pieces to the interaction that takes place here: a controller method that returns a WebSocket object, a JavaScript method that opens that WebSocket, and an Akka actor that sends the payload across that WebSocket every second. Let's explore each.
|
54
|
+
|
55
|
+
### Returning a WebSocket from a controller method
|
56
|
+
|
57
|
+
You can [return a WebSocket](http://www.playframework.com/documentation/2.2.x/JavaWebSockets) from a Play controller method.
|
58
|
+
|
59
|
+
There is an example in `Application.java` in the sample application:
|
60
|
+
|
61
|
+
```java
|
62
|
+
:::= write app/controllers/Application.java
|
63
|
+
package controllers;
|
64
|
+
|
65
|
+
import static java.util.concurrent.TimeUnit.SECONDS;
|
66
|
+
import models.Pinger;
|
67
|
+
import play.libs.Akka;
|
68
|
+
import play.libs.F.Callback0;
|
69
|
+
import play.mvc.Controller;
|
70
|
+
import play.mvc.Result;
|
71
|
+
import play.mvc.WebSocket;
|
72
|
+
import scala.concurrent.duration.Duration;
|
73
|
+
import views.html.index;
|
74
|
+
import akka.actor.ActorRef;
|
75
|
+
import akka.actor.Cancellable;
|
76
|
+
import akka.actor.Props;
|
77
|
+
|
78
|
+
public class Application extends Controller {
|
79
|
+
public static WebSocket<String> pingWs() {
|
80
|
+
return new WebSocket<String>() {
|
81
|
+
public void onReady(WebSocket.In<String> in, WebSocket.Out<String> out) {
|
82
|
+
final ActorRef pingActor = Akka.system().actorOf(Props.create(Pinger.class, in, out));
|
83
|
+
final Cancellable cancellable = Akka.system().scheduler().schedule(Duration.create(1, SECONDS),
|
84
|
+
Duration.create(1, SECONDS),
|
85
|
+
pingActor,
|
86
|
+
"Tick",
|
87
|
+
Akka.system().dispatcher(),
|
88
|
+
null
|
89
|
+
);
|
90
|
+
|
91
|
+
in.onClose(new Callback0() {
|
92
|
+
@Override
|
93
|
+
public void invoke() throws Throwable {
|
94
|
+
cancellable.cancel();
|
95
|
+
}
|
96
|
+
});
|
97
|
+
}
|
98
|
+
|
99
|
+
};
|
100
|
+
}
|
101
|
+
|
102
|
+
public static Result pingJs() {
|
103
|
+
return ok(views.js.ping.render());
|
104
|
+
}
|
105
|
+
|
106
|
+
public static Result index() {
|
107
|
+
return ok(index.render());
|
108
|
+
}
|
109
|
+
}
|
110
|
+
```
|
111
|
+
|
112
|
+
This method returns a new [WebSocket](http://www.playframework.com/documentation/2.0/api/java/play/mvc/WebSocket.html) object that has a String as its payload. In the WebSocket object we define the onReady method to talk to an actor via the Akka scheduler. The work of sending data over the socket will occur in that actor.
|
113
|
+
|
114
|
+
The other methods will render our `js` and `html` templates.
|
115
|
+
|
116
|
+
|
117
|
+
We'll also need a route to be set up for these methods in our `routes` file:
|
118
|
+
|
119
|
+
```
|
120
|
+
:::= write conf/routes
|
121
|
+
# Home page
|
122
|
+
GET / controllers.Application.index()
|
123
|
+
GET /pingWs controllers.Application.pingWs()
|
124
|
+
GET /assets/javascripts/ping.js controllers.Application.pingJs()
|
125
|
+
|
126
|
+
# Map static resources from the /public folder to the /assets URL path
|
127
|
+
GET /assets/*file controllers.Assets.at(path="/public", file)
|
128
|
+
```
|
129
|
+
|
130
|
+
### Sending data over a WebSocket
|
131
|
+
|
132
|
+
In the controller example you'll notice that we pass around the `in` and `out` streams of the WebSocket. In our actor we're able to read from and write to these streams just like any other IO stream. Here's the code for the `Pinger` actor:
|
133
|
+
|
134
|
+
```java
|
135
|
+
:::= write app/models/Pinger.java
|
136
|
+
package models;
|
137
|
+
|
138
|
+
import play.*;
|
139
|
+
import play.mvc.*;
|
140
|
+
import play.libs.*;
|
141
|
+
|
142
|
+
import scala.concurrent.duration.Duration;
|
143
|
+
import java.util.concurrent.TimeUnit;
|
144
|
+
import akka.actor.UntypedActor;
|
145
|
+
import java.util.Calendar;
|
146
|
+
import java.text.SimpleDateFormat;
|
147
|
+
|
148
|
+
public class Pinger extends UntypedActor {
|
149
|
+
WebSocket.In<String> in;
|
150
|
+
WebSocket.Out<String> out;
|
151
|
+
|
152
|
+
public Pinger(WebSocket.In<String> in, WebSocket.Out<String> out) {
|
153
|
+
this.in = in;
|
154
|
+
this.out = out;
|
155
|
+
}
|
156
|
+
|
157
|
+
@Override
|
158
|
+
public void onReceive(Object message) {
|
159
|
+
if (message.equals("Tick")) {
|
160
|
+
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
161
|
+
Calendar cal = Calendar.getInstance();
|
162
|
+
out.write(sdf.format(cal.getTime()));
|
163
|
+
} else {
|
164
|
+
unhandled(message);
|
165
|
+
}
|
166
|
+
}
|
167
|
+
}
|
168
|
+
```
|
169
|
+
|
170
|
+
You'll notice that this actor counts on the schedule defined in the controller method to send it a "Tick" message every second. When that happens it sends the current date and time over the WebSocket.
|
171
|
+
|
172
|
+
### Connecting to a WebSocket
|
173
|
+
|
174
|
+
The final piece is the client code that will call the WebSocket. For this our sample application uses Scala js and HTML templates called `ping.scala.js` and `index.scala.js` respectively.
|
175
|
+
|
176
|
+
`index.scala.js` provides a `div` to display the data in and references the JavaScript:
|
177
|
+
|
178
|
+
```java
|
179
|
+
:::= write app/views/index.scala.html
|
180
|
+
@main("Welcome to Play") {
|
181
|
+
|
182
|
+
<strong>Stats</strong><br>
|
183
|
+
<div id="ping"></div>
|
184
|
+
|
185
|
+
<script type="text/javascript" charset="utf-8" src="@routes.Application.pingJs()"></script>
|
186
|
+
}
|
187
|
+
```
|
188
|
+
|
189
|
+
`ping.scala.js` connects to our WebSocket and defines the `receiveEvent` method to populate the dates into the displayed `div` as they come across:
|
190
|
+
|
191
|
+
```javascript
|
192
|
+
:::= write app/views/ping.scala.js
|
193
|
+
$(function() {
|
194
|
+
var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
|
195
|
+
var dateSocket = new WS("@routes.Application.pingWs().webSocketURL(request)")
|
196
|
+
|
197
|
+
var receiveEvent = function(event) {
|
198
|
+
$("#ping").html("Last ping: "+event.data);
|
199
|
+
}
|
200
|
+
|
201
|
+
dateSocket.onmessage = receiveEvent
|
202
|
+
})
|
203
|
+
```
|
204
|
+
|
205
|
+
## Deploy the application to Heroku
|
206
|
+
|
207
|
+
### Store your app in Git
|
208
|
+
|
209
|
+
If you haven't done so already put your application into a git repository:
|
210
|
+
|
211
|
+
```term
|
212
|
+
:::= $ git init
|
213
|
+
:::= $ git add .
|
214
|
+
:::= $ git commit -m "Ready to deploy"
|
215
|
+
```
|
216
|
+
|
217
|
+
### Create the app
|
218
|
+
|
219
|
+
```term
|
220
|
+
#:::= $ heroku create
|
221
|
+
```
|
222
|
+
|
223
|
+
### Deploy your code
|
224
|
+
|
225
|
+
```term
|
226
|
+
#:::= $ git push heroku master
|
227
|
+
#:::- $ heroku labs:enable websockets
|
228
|
+
#:::- $ heroku restart
|
229
|
+
```
|
230
|
+
|
231
|
+
Congratulations! Your web app should now be up and running on Heroku.
|
@@ -0,0 +1,504 @@
|
|
1
|
+
```
|
2
|
+
:::- rundoc
|
3
|
+
email = ENV['HEROKU_EMAIL'] || `heroku auth:whoami`
|
4
|
+
|
5
|
+
Rundoc.configure do |config|
|
6
|
+
config.project_root = "myapp"
|
7
|
+
config.filter_sensitive(email, "developer@example.com")
|
8
|
+
end
|
9
|
+
```
|
10
|
+
|
11
|
+
Ruby on Rails is a popular web framework written in [Ruby](http://www.ruby-lang.org/). This guide covers using Rails 4 on Heroku, running previous versions of Rails on Heroku see [Getting Started with Rails 3.x on Heroku](https://devcenter.heroku.com/articles/rails3).
|
12
|
+
|
13
|
+
> callout If you are already familiar with Heroku and Rails, reference the [simplifed Rails 4 on Heroku guide](https://devcenter.heroku.com/articles/rails4) instead. For general information on how to develop and architect apps for use on Heroku, see [Architecting Applications for Heroku](https://devcenter.heroku.com/articles/architecting-apps).
|
14
|
+
|
15
|
+
For this guide you will need:
|
16
|
+
|
17
|
+
- Basic Ruby/Rails knowledge
|
18
|
+
- Locally installed version of Ruby 2.0.0+, Rubygems, Bundler, and Rails 4+
|
19
|
+
- Basic Git knowledge
|
20
|
+
- A Heroku user account: [Signup is free and instant](https://api.heroku.com/signup/devcenter)
|
21
|
+
|
22
|
+
## Local Workstation Setup
|
23
|
+
|
24
|
+
Install the [Heroku Toolbelt](https://toolbelt.heroku.com/) on your local workstation. This ensures that you have access to the [Heroku command-line client](/categories/command-line), Foreman, and the Git revision control system. You will also need [Ruby and Rails installed](http://guides.railsgirls.com/install/).
|
25
|
+
|
26
|
+
Once installed, you'll have access to the `$ heroku` command from your command shell. Log in using the email address and password you used when creating your Heroku account:
|
27
|
+
|
28
|
+
|
29
|
+
> callout Note that `$` symbol before commands indicates they should be run on the command line, prompt, or terminal with appropriate permissions. Do not copy the `$` symbol.
|
30
|
+
|
31
|
+
```sh
|
32
|
+
$ heroku login
|
33
|
+
Enter your Heroku credentials.
|
34
|
+
Email: schneems@example.com
|
35
|
+
Password:
|
36
|
+
Could not find an existing public key.
|
37
|
+
Would you like to generate one? [Yn]
|
38
|
+
Generating new SSH public key.
|
39
|
+
Uploading ssh public key /Users/adam/.ssh/id_rsa.pub
|
40
|
+
```
|
41
|
+
|
42
|
+
Press enter at the prompt to upload your existing `ssh` key or create a new one, used for pushing code later on.
|
43
|
+
|
44
|
+
## Write your App
|
45
|
+
|
46
|
+
> callout To run on Heroku your app must be configured to use the Postgres database, have all dependencies declared in your `Gemfile`, and have the `rails_12factor` gem in the production group of your `Gemfile`
|
47
|
+
|
48
|
+
|
49
|
+
You may be starting from an existing app, if so [upgrade to Rails 4](http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-from-rails-3-2-to-rails-4-0) before continuing. If not, a vanilla Rails 4 app will serve as a suitable sample app. To build a new app make sure that you're using the Rails 4.x using `$ rails -v`. You can get the new version of rails by running,
|
50
|
+
|
51
|
+
```sh
|
52
|
+
:::= $ gem install rails --no-ri --no-rdoc
|
53
|
+
```
|
54
|
+
|
55
|
+
Then create a new app:
|
56
|
+
|
57
|
+
```sh
|
58
|
+
::: $ rails new myapp --database=postgresql
|
59
|
+
```
|
60
|
+
|
61
|
+
Once finished change your directory to the newly created Rails app
|
62
|
+
|
63
|
+
```sh
|
64
|
+
::: $ cd myapp
|
65
|
+
```
|
66
|
+
|
67
|
+
> callout If you experience problems or get stuck with this tutorial, your questions may be answered in a later part of this document. Once you experience a problem try reading through the entire document and then going back to your issue. It can also be useful to review your previous steps to ensure they all executed correctly.
|
68
|
+
|
69
|
+
Rails 4 no longer has a static index page in production. When you're using a new app, there will not be a root page in production, so we need to create one. We will first create a controller called `welcome` for our home page to live:
|
70
|
+
|
71
|
+
```sh
|
72
|
+
::: $ rails generate controller welcome
|
73
|
+
```
|
74
|
+
|
75
|
+
Next we'll add an index page.
|
76
|
+
|
77
|
+
```sh
|
78
|
+
:::= file.write app/views/welcome/index.html.erb
|
79
|
+
<h2>Hello World</h2>
|
80
|
+
<p>
|
81
|
+
The time is now: <%= Time.now %>
|
82
|
+
</p>
|
83
|
+
```
|
84
|
+
|
85
|
+
Now we need to have Rails route to this action. We'll edit `config/routes.rb` to set the index page to our new method:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
:::= file.append config/routes.rb#2
|
89
|
+
root 'welcome#index'
|
90
|
+
```
|
91
|
+
|
92
|
+
You can verify that the page is there by running your server:
|
93
|
+
|
94
|
+
```sh
|
95
|
+
$ rails server
|
96
|
+
```
|
97
|
+
|
98
|
+
And visiting [http://localhost:3000](http://localhost:3000) in your browser. If you do not see the page, use the logs that are output to your server to debug.
|
99
|
+
|
100
|
+
## Heroku gems
|
101
|
+
|
102
|
+
Heroku integration has previously relied on using the Rails plugin system, which has been removed from Rails 4. To enable features such as static asset serving and logging on Heroku please add `rails_12factor` gem to your `Gemfile`.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
:::= file.append Gemfile
|
106
|
+
gem 'rails_12factor', group: :production
|
107
|
+
```
|
108
|
+
|
109
|
+
Then run:
|
110
|
+
|
111
|
+
```sh
|
112
|
+
::: $ bundle install
|
113
|
+
```
|
114
|
+
|
115
|
+
We talk more about Rails integration on our [Ruby Support page](https://devcenter.heroku.com/articles/ruby-support#injected-plugins).
|
116
|
+
|
117
|
+
## Use Postgres
|
118
|
+
|
119
|
+
> callout We highly recommend using PostgreSQL during development. Maintaining [parity between your development](http://www.12factor.net/dev-prod-parity) and deployment environments prevents subtle bugs from being introduced because of differences between your environments. [Install Postgres locally](https://devcenter.heroku.com/articles/heroku-postgresql#local-setup) now if it is not allready on your system.
|
120
|
+
|
121
|
+
If you did not specify `postgresql` while creating your app (using `--database=postgresql`) you will need to add the `pg` gem to your Rails project. Edit your `Gemfile` and change this line:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
gem 'sqlite3'
|
125
|
+
```
|
126
|
+
|
127
|
+
To this:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
gem 'pg'
|
131
|
+
```
|
132
|
+
|
133
|
+
You can get more information on why this change is needed and how to configure your app to run postgres locally see [why you cannot use Sqlite3 on Heroku](https://devcenter.heroku.com/articles/sqlite3).
|
134
|
+
|
135
|
+
In addition to using the `pg` gem, you'll also need to ensure the `config/database.yml` is using the `postgresql` adapter.
|
136
|
+
|
137
|
+
You will also need to remove the `username` field in your `database.yml` if there is one so:
|
138
|
+
|
139
|
+
```
|
140
|
+
:::= file.remove config/database.yml
|
141
|
+
username: myapp
|
142
|
+
```
|
143
|
+
|
144
|
+
This line tells rails that the database `myapp_development` should be run under a role of `myapp`. Since you likely don't have this role in your database we will remove it. With the line remove Rails will try to access the database as user who is currently logged into the computer.
|
145
|
+
|
146
|
+
The development section of your `config/database.yml` file should look something like this:
|
147
|
+
|
148
|
+
```sh
|
149
|
+
::: $ cat config/database.yml
|
150
|
+
:::= | $ head -n 23
|
151
|
+
```
|
152
|
+
|
153
|
+
Be careful here, if you omit the `sql` at the end of `postgresql` your application will not work.
|
154
|
+
|
155
|
+
Now re-install your dependencies (to generate a new `Gemfile.lock`):
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
$ bundle install
|
159
|
+
```
|
160
|
+
|
161
|
+
## Specify Ruby version in app
|
162
|
+
|
163
|
+
|
164
|
+
Rails 4 requires Ruby 1.9.3 or above. Heroku has a recent version of Ruby installed, however you can specify an exact version by using the `ruby` DSL in your `Gemfile`. For this guide we'll be using Ruby 2.0.0 so add this to your `Gemfile`:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
:::= file.append Gemfile
|
168
|
+
ruby "2.0.0"
|
169
|
+
```
|
170
|
+
|
171
|
+
You should also be running the same version of Ruby locally. You can verify by running `$ ruby -v`. You can get more information on [specifying your Ruby version on Heroku here](https://devcenter.heroku.com/articles/ruby-versions).
|
172
|
+
|
173
|
+
## Store your App in Git
|
174
|
+
|
175
|
+
Heroku relies on [git](http://git-scm.com/), a distributed source control managment tool, for deploying your project. If your project is not already in git first verify that `git` is on your system:
|
176
|
+
|
177
|
+
```sh
|
178
|
+
::: $ git --help
|
179
|
+
:::= | $ head -n 10
|
180
|
+
```
|
181
|
+
|
182
|
+
If you don't see any output or get `command not found` you will need to install it on your system, verify that the [Heroku toolbelt](https://toolbelt.heroku.com/) is installed.
|
183
|
+
|
184
|
+
Once you've verified that git works, first make sure you are in your Rails app directory by running:
|
185
|
+
|
186
|
+
```sh
|
187
|
+
$ ls
|
188
|
+
```
|
189
|
+
|
190
|
+
The output should look like this:
|
191
|
+
|
192
|
+
```sh
|
193
|
+
:::= $ ls
|
194
|
+
```
|
195
|
+
|
196
|
+
Now run these commands in your Rails app directory to initialize and commit your code to git:
|
197
|
+
|
198
|
+
```sh
|
199
|
+
::: $ git init
|
200
|
+
::: $ git add .
|
201
|
+
::: $ git commit -m "init"
|
202
|
+
```
|
203
|
+
|
204
|
+
You can verify everything was committed correctly by running:
|
205
|
+
|
206
|
+
```sh
|
207
|
+
:::= $ git status
|
208
|
+
```
|
209
|
+
|
210
|
+
Now that your application is committed to git you can deploy to Heroku.
|
211
|
+
|
212
|
+
## Deploy your application to Heroku
|
213
|
+
|
214
|
+
Make sure you are in the directory that contains your Rails app, then create an app on Heroku:
|
215
|
+
|
216
|
+
```sh
|
217
|
+
:::= $ heroku create
|
218
|
+
```
|
219
|
+
|
220
|
+
You can verify that the remote was added to your project by running
|
221
|
+
|
222
|
+
```sh
|
223
|
+
$ git config -e
|
224
|
+
```
|
225
|
+
|
226
|
+
If you see `fatal: not in a git directory` then you are likely not in the corect directory. Otherwise you may deploy your code. After you deploy your code, you will need to migrate your database, make sure it is properly scaled and use logs to debug any issues that come up.
|
227
|
+
|
228
|
+
Deploy your code:
|
229
|
+
|
230
|
+
```sh
|
231
|
+
:::= $ git push heroku master
|
232
|
+
```
|
233
|
+
|
234
|
+
It is always a good idea to check to see if there are any warnings or errors in the output. If everything went well you can migrate your database.
|
235
|
+
|
236
|
+
## Migrate your database
|
237
|
+
|
238
|
+
If you are using the database in your application you need to manually migrate the database by running:
|
239
|
+
|
240
|
+
```sh
|
241
|
+
$ heroku run rake db:migrate
|
242
|
+
```
|
243
|
+
|
244
|
+
Any commands after the `heroku run` will be executed on a Heroku [dyno](dynos).
|
245
|
+
|
246
|
+
|
247
|
+
## Visit your application
|
248
|
+
|
249
|
+
|
250
|
+
You've deployed your code to Heroku. You can now instruct Heroku to execute a process type. Heroku does this by running the associated command in a [dyno](dynos) - a lightweight container which is the basic unit of composition on Heroku.
|
251
|
+
|
252
|
+
Let's ensure we have one dyno running the `web` process type:
|
253
|
+
|
254
|
+
```sh
|
255
|
+
::: $ heroku ps:scale web=1
|
256
|
+
```
|
257
|
+
|
258
|
+
You can check the state of the app's dynos. The `heroku ps` command lists the running dynos of your application:
|
259
|
+
|
260
|
+
```sh
|
261
|
+
:::= $ heroku ps
|
262
|
+
```
|
263
|
+
|
264
|
+
Here, one dyno is running.
|
265
|
+
|
266
|
+
We can now visit the app in our browser with `heroku open`.
|
267
|
+
|
268
|
+
```sh
|
269
|
+
:::= $ heroku open
|
270
|
+
```
|
271
|
+
|
272
|
+
You should now see the "Hello World" text we inserted above.
|
273
|
+
|
274
|
+
Heroku gives you a default web url for simplicty while you are developing. When you are ready to scale up and use Heroku for production you can add your own [Custom Domain](https://devcenter.heroku.com/articles/custom-domains).
|
275
|
+
|
276
|
+
## View the logs
|
277
|
+
|
278
|
+
If you run into any problems getting your app to perform properly, you will need to check the logs.
|
279
|
+
|
280
|
+
You can view information about your running app using one of the [logging commands](logging), `heroku logs`:
|
281
|
+
|
282
|
+
```sh
|
283
|
+
:::= $ heroku logs
|
284
|
+
```
|
285
|
+
|
286
|
+
You can also get the full stream of logs by running the logs command with the `--tail` flag option like this:
|
287
|
+
|
288
|
+
```sh
|
289
|
+
$ heroku logs --tail
|
290
|
+
```
|
291
|
+
|
292
|
+
## Dyno sleeping and scaling
|
293
|
+
|
294
|
+
Having only a single web dyno running will result in the dyno [going to sleep](dynos#dyno-sleeping) after one hour of inactivity. This causes a delay of a few seconds for the first request upon waking. Subsequent requests will perform normally.
|
295
|
+
|
296
|
+
To avoid this, you can scale to more than one web dyno. For example:
|
297
|
+
|
298
|
+
```sh
|
299
|
+
$ heroku ps:scale web=2
|
300
|
+
```
|
301
|
+
|
302
|
+
For each application, Heroku provides [750 free dyno-hours](usage-and-billing#750-free-dyno-hours-per-app). Running your app at 2 dynos would exceed this free, monthly allowance, so let's scale back:
|
303
|
+
|
304
|
+
```sh
|
305
|
+
$ heroku ps:scale web=1
|
306
|
+
```
|
307
|
+
|
308
|
+
## Console
|
309
|
+
|
310
|
+
Heroku allows you to run commands in a [one-off dyno](oneoff-admin-ps) - scripts and applications that only need to be executed when needed - using the `heroku run` command. Use this to launch a Rails console process attached to your local terminal for experimenting in your app's environment:
|
311
|
+
|
312
|
+
```sh
|
313
|
+
$ heroku run rails console
|
314
|
+
irb(main):001:0> puts 1+1
|
315
|
+
2
|
316
|
+
```
|
317
|
+
|
318
|
+
## Rake
|
319
|
+
|
320
|
+
Rake can be run as an attached process exactly like the console:
|
321
|
+
|
322
|
+
```sh
|
323
|
+
$ heroku run rake db:migrate
|
324
|
+
```
|
325
|
+
|
326
|
+
## Webserver
|
327
|
+
|
328
|
+
By default, your app's web process runs `rails server`, which uses Webrick. This is fine for testing, but for production apps you'll want to switch to a more robust webserver. On Cedar, [we recommend Unicorn as the webserver](ruby-production-web-server). Regardless of the webserver you choose, production apps should always specify the webserver explicitly in the `Procfile`.
|
329
|
+
|
330
|
+
First, add Unicorn to your application `Gemfile`:
|
331
|
+
|
332
|
+
```ruby
|
333
|
+
:::= file.append Gemfile
|
334
|
+
gem 'unicorn'
|
335
|
+
```
|
336
|
+
|
337
|
+
Then run
|
338
|
+
|
339
|
+
```sh
|
340
|
+
::: $ bundle install
|
341
|
+
```
|
342
|
+
|
343
|
+
Now you are ready to configure your app to use Unicorn.
|
344
|
+
|
345
|
+
Create a configuration file for Unicorn at `config/unicorn.rb`:
|
346
|
+
|
347
|
+
```sh
|
348
|
+
::: $ touch config/unicorn.rb
|
349
|
+
```
|
350
|
+
|
351
|
+
Now we're going to add Unicorn specific configuration options, that we explain in detail in [Heroku's Unicorn documentation](https://devcenter.heroku.com/articles/rails-unicorn):
|
352
|
+
|
353
|
+
```ruby
|
354
|
+
:::= file.write config/unicorn.rb
|
355
|
+
|
356
|
+
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
|
357
|
+
timeout 15
|
358
|
+
preload_app true
|
359
|
+
|
360
|
+
before_fork do |server, worker|
|
361
|
+
Signal.trap 'TERM' do
|
362
|
+
puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
|
363
|
+
Process.kill 'QUIT', Process.pid
|
364
|
+
end
|
365
|
+
|
366
|
+
defined?(ActiveRecord::Base) and
|
367
|
+
ActiveRecord::Base.connection.disconnect!
|
368
|
+
end
|
369
|
+
|
370
|
+
after_fork do |server, worker|
|
371
|
+
Signal.trap 'TERM' do
|
372
|
+
puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
|
373
|
+
end
|
374
|
+
|
375
|
+
defined?(ActiveRecord::Base) and
|
376
|
+
ActiveRecord::Base.establish_connection
|
377
|
+
end
|
378
|
+
```
|
379
|
+
|
380
|
+
This default configuration assumes a standard Rails app with Active Record. You should get acquainted with the different options in [the official Unicorn documentation](http://unicorn.bogomips.org/Unicorn/Configurator.html).
|
381
|
+
|
382
|
+
Finally you will need to tell Heroku how to run your Rails app by creating a `Procfile` in the root of your application directory.
|
383
|
+
|
384
|
+
### Procfile
|
385
|
+
|
386
|
+
Change the command used to launch your web process by creating a file called [Procfile](procfile) and entering this:
|
387
|
+
|
388
|
+
```
|
389
|
+
:::= file.write Procfile
|
390
|
+
web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
|
391
|
+
```
|
392
|
+
|
393
|
+
Note: The case of `Procfile` matters, the first letter must be uppercase.
|
394
|
+
|
395
|
+
Set the `RACK_ENV` to development in your environment and a `PORT` to connect to. Before pushing to Heroku you'll want to test with the `RACK_ENV` set to production since this is the enviroment your Heroku app will run in.
|
396
|
+
|
397
|
+
```sh
|
398
|
+
:::= $ echo "RACK_ENV=development" >>.env
|
399
|
+
:::= $ echo "PORT=3000" >> .env
|
400
|
+
```
|
401
|
+
|
402
|
+
You'll also want to add `.env` to your `.gitignore` since this is for local enviroment setup.
|
403
|
+
|
404
|
+
```sh
|
405
|
+
::: $ echo ".env" >> .gitignore
|
406
|
+
::: $ git add .gitignore
|
407
|
+
::: $ git commit -m "add .env to .gitignore"
|
408
|
+
```
|
409
|
+
|
410
|
+
Test your Procfile locally using Foreman:
|
411
|
+
|
412
|
+
```sh
|
413
|
+
::: $ gem install foreman
|
414
|
+
```
|
415
|
+
|
416
|
+
You can now start your web server by running
|
417
|
+
|
418
|
+
```sh
|
419
|
+
$ foreman start
|
420
|
+
18:24:56 web.1 | I, [2013-03-13T18:24:56.885046 #18793] INFO -- : listening on addr=0.0.0.0:5000 fd=7
|
421
|
+
18:24:56 web.1 | I, [2013-03-13T18:24:56.885140 #18793] INFO -- : worker=0 spawning...
|
422
|
+
18:24:56 web.1 | I, [2013-03-13T18:24:56.885680 #18793] INFO -- : master process ready
|
423
|
+
18:24:56 web.1 | I, [2013-03-13T18:24:56.886145 #18795] INFO -- : worker=0 spawned pid=18795
|
424
|
+
18:24:56 web.1 | I, [2013-03-13T18:24:56.886272 #18795] INFO -- : Refreshing Gem list
|
425
|
+
18:24:57 web.1 | I, [2013-03-13T18:24:57.647574 #18795] INFO -- : worker=0 ready
|
426
|
+
```
|
427
|
+
|
428
|
+
Looks good, so press Ctrl-C to exit and you can deploy your changes to Heroku:
|
429
|
+
|
430
|
+
```sh
|
431
|
+
::: $ git add .
|
432
|
+
::: $ git commit -m "use unicorn via procfile"
|
433
|
+
::: $ git push heroku master
|
434
|
+
```
|
435
|
+
|
436
|
+
Check `ps`, you'll see the web process uses your new command specifying Unicorn as the web server
|
437
|
+
|
438
|
+
```sh
|
439
|
+
:::= $ heroku ps
|
440
|
+
```
|
441
|
+
|
442
|
+
The logs also reflect that we are now using Unicorn:
|
443
|
+
|
444
|
+
```sh
|
445
|
+
$ heroku logs
|
446
|
+
```
|
447
|
+
|
448
|
+
## Rails Asset Pipeline
|
449
|
+
|
450
|
+
There are several options for invoking the [Rails asset pipeline](http://guides.rubyonrails.org/asset_pipeline.html) when deploying to Heroku. For general information on the asset pipeline please see the [Rails 3.1+ Asset Pipeline on Heroku Cedar](rails3x-asset-pipeline-cedar) article.
|
451
|
+
|
452
|
+
The `config.assets.initialize_on_precompile` option has been removed is and not needed for Rails 4. Also, any failure in asset compilation will now cause the push to fail. For Rails 4 asset pipeline support see the [Ruby Support](https://devcenter.heroku.com/articles/ruby-support#rails-4-x-applications) page.
|
453
|
+
|
454
|
+
## Troubleshooting
|
455
|
+
|
456
|
+
If you push up your app and it crashes (`heroku ps` shows state `crashed`), check your logs to find out what went wrong. Here are some common problems.
|
457
|
+
|
458
|
+
### Runtime dependencies on development/test gems
|
459
|
+
|
460
|
+
If you're missing a gem when you deploy, check your Bundler groups. Heroku builds your app without the `development` or `test` groups, and if you app depends on a gem from one of these groups to run, you should move it out of the group.
|
461
|
+
|
462
|
+
One common example using the RSpec tasks in your `Rakefile`. If you see this in your Heroku deploy:
|
463
|
+
|
464
|
+
```sh
|
465
|
+
$ heroku run rake -T
|
466
|
+
Running `bundle exec rake -T` attached to terminal... up, ps.3
|
467
|
+
rake aborted!
|
468
|
+
no such file to load -- rspec/core/rake_task
|
469
|
+
```
|
470
|
+
|
471
|
+
Then you've hit this problem. First, duplicate the problem locally:
|
472
|
+
|
473
|
+
```sh
|
474
|
+
$ bundle install --without development:test
|
475
|
+
…
|
476
|
+
$ bundle exec rake -T
|
477
|
+
rake aborted!
|
478
|
+
no such file to load -- rspec/core/rake_task
|
479
|
+
```
|
480
|
+
|
481
|
+
Now you can fix it by making these Rake tasks conditional on the gem load. For example:
|
482
|
+
|
483
|
+
### Rakefile
|
484
|
+
|
485
|
+
```ruby
|
486
|
+
begin
|
487
|
+
require "rspec/core/rake_task"
|
488
|
+
|
489
|
+
desc "Run all examples"
|
490
|
+
|
491
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
492
|
+
t.rspec_opts = %w[--color]
|
493
|
+
t.pattern = 'spec/**/*_spec.rb'
|
494
|
+
end
|
495
|
+
rescue LoadError
|
496
|
+
end
|
497
|
+
```
|
498
|
+
|
499
|
+
Confirm it works locally, then push to Heroku.
|
500
|
+
|
501
|
+
|
502
|
+
## Done
|
503
|
+
|
504
|
+
You now have your first application deployed to Heroku. The next step is to deploy your own application. If you're interested in reading more you can read more about [Ruby on Heroku at the Devcenter](https://devcenter.heroku.com/categories/ruby).
|