README.md in atd-0.4.0 vs README.md in atd-0.5.0
- old
+ new
@@ -11,164 +11,127 @@
[gem version badge]: https://img.shields.io/gem/v/atd.svg?style=flat-square
[gem version link]: https://rubygems.org/gems/atd
-[ATD](#atd)
-- [Installation](#installation)
-- [Usage](#usage)
- - [Setup](#setup)
-- [Routing](#routing)
- - [Basic Routing](#basic-routing)
- - [Options Hash](#options-hash)
- - [Serving Files](#serving-files)
- - [Advanced Routing](#advanced-routing)
- - [DefaultApp](#defaultapp)
- - [Inheritance](#inheritance)
- - [Blocks](#blocks)
- - [@http](#http)
- - [Controllers](#controllers)
- - [Apps](#apps)
- - [App Creation](#app-creation)
- - [Starting the App](#starting-the-app)
- - [App Routing](#app-routing)
- - [Logging](#logging)
- - [Compilation](#compilation)
-- [Design Paradigms](#design-paradigms)
-- [Documentation](#documentation)
-- [Development](#development)
-- [Contributing](#contributing)
-
-
# ATD
-ATD is a new web development backend framework for Ruby. It is built on top of rack, and is meant to be small easy and light. That is why it provides a very minimalist way of writing your backend. It treats a request as a request, without splitting it based on the HTTP method.
+Hello! I assume if you're reading this you really want to know about this really cool, interesting new framework that I made. Well, in that case you've come to the right place. ATD is a small modular framework meant to combine the benefits from [rails](http://rubyonrails.org/) and [sinatra](http://www.sinatrarb.com/). I originally used sinatra, and ran into issues with scaling, and so hence I did the only logical thing and wrote a new framework. I just found that because of sinatra's "build it from the ground up" philosophy, I couldn't make anything too large without making a mass of spaghetti code. That is why this framework allows you to use rails concepts like MVC and moduarly use different components in development and production. It supports the simple sinatra DSL syntax (for the most part) to make small apps, and allows big controllers and models for separation of concerns in larger apps, which is more of a rails philosophy.
+Anyways, I've worked really hard on this, and I would love for you to try it out and maybe even contribute! Feel free to reach out to me at [atd@developingtechnician.com](mailto:atd@developingtechnician.com) with questions or concerns, or if it's something concrete you want me to change, just open an issue.
+
## Installation
-Add this line to your application's Gemfile:
+Pretty simple, the usual `gem install atd` if you want to use without bundler, or just add it to your Gemfile with bundler. The recommended setup is to use the master branch of the git repo by adding to your Gemfile:
```ruby
-gem 'atd', :git => 'https://gitlab.com/izwick-schachter/atd.git'
+gem 'atd', git: 'https://gitlab.com/izwick-schachter/atd.git', branch: "master"
```
-Or, (not recommended) you can use only the rubygems version, which you can use with:
+Or if you want to live on the edge, where all the latest and greatest features are, change `branch` to `"development"`. Keep in mind, this may sometimes not work, so use at your own risk. When you do this, it's recommended that you go into the git repo and choose a specific commit and lock that in by setting `:ref` to the commit hash.
-```ruby
-gem 'atd'
-```
+## The Basics
-And then execute:
+`ATD` is structured around a two basic constructs: `App`s and `Route`s.
- $ bundle
+### Apps
-Or install it from the repository with:
+An `App` is a class which is a functional rack app. It has an instance method `call` as the rack spec requires, and it contains all the things you need to use `ATD`. An `App` is just a class that extends `ATD::App`. That said, for most usages you will never need to know anything about `App`s. Unless you need multiple different `App`s in the same file, you don't need to touch it. That is because `ATD` kindly treats `main` as an `App` called `DefaultApp`. All that means is that anything created in `main` is added to `DefaultApp`. So, whenever you do something that is not in an `App` you have created, you can just know in the back of your head that you are impacting `DefaultApp`.
- $ git clone https://gitlab.com/izwick-schachter/atd.git
- $ cd atd
- $ bundle exec rake install
+### Routes
-This will also allow you to use the current development version (USE WITH CAUTION), and to use that you can `git checkout development` and then `bundle exec rake install`.
+The second basic construct is a `Route`. These are things which you should understand, because these are how `ATD` processes most of the things you do. Every route belongs to an `App`, typically `DefaultApp` as mentioned [above](#apps). In it's most simple form, a `Route` simply says "When `/whatever` is requested, return this file and/or run this code". The syntax will certainly remind you of sinatra:
-## Setup
-
-Setup is as easy as `require "atd"` at the top of whatever file you need it in
-
-## Routing
-
-### Basic Routing
-
-On the lowest level it can be used serve files or any strings when a path is requested (and optionally when a specific HTTP method is used). Here is a basic route that will serve `Hello World` when `/` is sent any HTTP request (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`):
-
```ruby
-request "/", "Hello World"
+request "/some/path", "my_file.html.erb"
+# Also aliased to:
+req "/some/path", "my_file.html.erb"
+# And
+r "/some/path", "my_file.html.erb"
+# It's easier to type
```
-The `request` method is also aliased to `req` and `r` for better code readability if you prefer. Here are 3 equivalent routes:
+Why not make the method named for the HTTP verbs? Because we want everything to be customizable. By default, calling `request` will make the route respond to every HTTP verb. If you want it to only respond to some verbs, you have several choices for how you want to do it:
```ruby
-request "/", "Hello World"
-req "/", "Hello World"
-r "/", "Hello World"
+# If you only wanted to respond to GET, POST, and DELETE:
+request "/some/path", "my_file.html.erb", respond_to: [:get, :post, :delete]
+# Or this other syntax
+get post delete "/some/path", "my_file.html.erb"
+# Or a combination of the two
+get "/some/path", "my_file.html.erb", respond_to: [:post, :delete]
+# Or with dots too
+get.post.delete "/some/path", "my_file.html.erb"
+# Maybe mixed with request:
+request.get.post.delete "/some/path", "my_file.html.erb"
+# Or if you wanted to ignore those verbs and only respond to PUT and PATCH
+request "/some/path", "my_file.html.erb", ignore: [:get, :post, :delete]
```
-For the purposes of this README, we will only use the `request` syntax, for improved readability, but anywhere in the code these are interchangeable, because they all return an `ATD::Route` object.
+_N.B. See [precompilers](#precompilers) for how the file name you pass is manipulated before it is sent out._
-You could use the same basic routing syntax to return `Hello World` for a HTTP `GET` request to `/`:
+You can also pass a block that will be run whenever the `Route` is matched:
```ruby
-request.get "/", "Hello World"
+request "/some/path", "my_file.html.erb" do
+ puts "Found me!"
+end
```
-Or you could use a simpler (but equivalent) syntax:
+and if you want to, you can make the blocks return value be the output of the route by omitting `"my_file.html.erb"`. You can also manipulate the output that you passed (see [helpers](#helpers)) by modifying the `view[:raw]` variable.
-```ruby
-get "/", "Hello World"
-```
+## Settings
-For the purposes of this README we will only use `request.get` and not `get` because we believe it adds clarity.
+This doesn't exist yet, but is in progress in issue #29.
-You can also have route respond to more than one HTTP verb with `get.post.put.patch`:
+## Advanced Routing
-```ruby
-request.get.post.put.patch "/", "Hello World"
-```
+For simple project, [the basics](#the-basics) of routing will work fine. But if you want to create a full fledged application, then you probably are going to need some of the more advanced features, such as precompilation, compilation, compiler options, and using blocks to manipulate output.
-This will respond to `GET`, `POST`, `PUT`, and `PATCH` with `Hello World`, but will repond to `DELETE` with a `404` status code.
+### Options Hash
-The `get.post.put.patch` is not recommended for readability. Instead use the following syntax:
+The options hash is the hash provided at the end of the argument list when creating a route. For example, in `r "/", "some_file", option_A: "Value", option_B: 35` the options hash will be `{option_A: "Value", option_B: 35}`. Here is a list of options that ATD looks at internally and their default values (other keys can be passed in the options hash, they will just be passed to compilation methods):
```ruby
-request "/", "Hello World", respond_to: [:get, :post, :put, :patch]
+status: 200 # Integer > 99 and < 1000. This will be the status code returned unless it is overridden by
+status_code: 200 # Same as status, but overrides. A slightly more verbose syntax.
+respond_to: nil # A array of HTTP methods as lowercase symbols which the route should respond to.
+ignore: nil # A array of HTTP methods as lowercase symbols which the route should not respond to. This takes highest precedence.
+precompile: true # This determines if a route will be precompiled. Unless it is == false it will be precompiled.
+compile: true # This determines if a route will be compiled. Unless it is == false it will be compiled.
```
-Or, a simpler way would be with `:ignore`:
+### Serving Files
-```ruby
-request "/", "Hello World", ignore: :delete
-```
+If you want to serve files, just place them in an assets directory in the app directory and pass the file name with the file extension as the second argument to your routes. In the future you will be able to access this through the [`App` settings](#settings).
-#### Options Hash
+### Helpers
-You can pass various options through the options hash. The options hash is put as the last arguemtn when creating a route. For example: `request "/", "Hello World", option: value, other-option: value`
+In the block passed to a `Route` there are helpers available to it. You can add new helper methods by adding them to the `ATD::Helpers` module. By default there are a few defined, and here they are:
-Here is a list of the option which are currently valid and default values:
-
```ruby
-status: 200 # Integer > 99 and < 1000. This will be the status code returned unless it is overridden by
-status_code: 200 # Same as status, but overrides. A slightly more verbose syntax.
-respond_to: nil # A list of HTTP methods as lowercase symbols which the route should respond to.
-ignore: nil # A list of HTTP methods as lowercase symbols which the route should not respond to.
+http[:request] # The Rack::Request object
+http[:response] # The Rack::Response object
+http[:view] # The same thing as view[:raw]
+http[:method] # The HTTP verb used to access the route
+http[:headers] # The headers which will be sent, by default only "content-type".
+http[:status_code] # The status code which the app will respond with, by default 200
+params # The params hash we all know and love.
+view[:raw] # The precompiled (unless precompile:false) and compiled (unless compile:false) view.
```
-#### Serving Files
+## Apps
-All files that you want to serve must be in the `assets` directory if they are, then it is simple to just create a route, and put the filename as the output. For example this will serve `assets/index.html`:
+Whenever you create a route, it is given to an `App`. This isn't apparent when you create a route in `main`, but even when you do that the route is added to `DefaultApp`, as you may remember from the [intro](#the-basics). If you are using Apps, then when you create a route in an `App`, that route belongs to the `App`. When you start the server, it then creates an instance of the `App` and starts it because it is a rack app. But that is not what the purpose of `App`s are.
-```ruby
-request "/", "index.html"
-```
+The intention of `Apps` are to allow you to use one `App` as a template, from which you can create many different `App`s. An `App` is not a rack app all by itself. Every instance of an App Class is a rack app. But the rack app doesn't actually start until `start` is called. This means that you can create an instance of an `App`, and then you can modify it before starting it. So for example, you can have an `App` which is impacted by an instance variable:
-There are certain modifications which can be made to files between the route being requested and the files being served. That information can be found in the [compilation section](#compilation).
-
-### Advanced Routing
-
-#### DefaultApp
-
-`DefaultApp` is just another app which extends `ATD::App`, it is the app which doesn't have to be written to in `class DefaultApp`, any routes that are created in `main` will be added to `DefaultApp`, well... by default! To find out more about Apps, you can go to the [Apps section](#apps).
-
-#### Inheritance
-
-Whenever you create a route, it is given to an App Class (which inherits from `ATD::App`). This isn't apparent when you create a route in `main`, but even when you do that the route is added to `DefaultApp`. If you are using Apps, than when you create a route in the App Class, that route is given to that Class. When you start the server, it then creates an instance of the App Class and starts it as a rack app. But that is not what the purpose of Apps are.
-
-The intention of Apps are to allow you to use one App Class as a template class, from which you can create many different apps. An App Class is not a rack app all by itself. Every instance of an App Class is a rack app. But the rack app doesn't actually start until `start` is called. This means that you can create an instance of an App Class (an App), and then you can modify it before starting it. So for example, you can have an app which is impacted by an instance variable:
-
```ruby
class MyApp < ATD::App
attr_accessor :my_name
request "/", "Hi! This is my_name's App!" do
- @http[:output] = @http[:output].gsub("my_name", @my_name)
+ view[:raw] = view[:raw].gsub("my_name", @my_name)
end
end
```
Which you can then create an instance of and modify the instance variable:
@@ -179,169 +142,114 @@
app.start
```
Then when you try to access the website, it will respond to `/` with `Hi! This is Fredrick's App!`.
-#### Blocks
+### App Creation
-You can also add blocks to the basic routing methods to execute code when they are reached, and customize the response to the incoming request. Here is an example that will return the HTTP method used to request `/`:
+To create an app you can use `ATD.new("AppName")`. It is important to note that **ATD.new is not a constructor, although I will refer to it as one**. It simply behaves like one because you can use it to "construct" a new app. The app creation process creates a new class which you can open anywhere in your app file, with the name you pass. The name must `respond_to?(:to_sym)`, and must be a valid class name. You must call the constructor before you begin adding routes to the class, or open the class at all.
+You can also use the more intuitive way to create an app, which would be by declaring a class which extends `ATD::App`, like so:
+
```ruby
-request "/" do
- @http[:output] = @http[:method]
+class MyAppClass < ATD::App
+ # All of my routes, settings, etc.
end
```
-This example uses the `@http` variable which is provided to the route when called. `@http[:method]` is the http verb used by the request, and `@http[:output]` is what the route will return. If you have already defined a return value, `@http[:output]` will already be set to it. For example:
+### Starting the App
+There are two basic ways to start the app. You can start it by calling `AppName.new.start` or more simply `AppName.start` which will create an instance and call start on it, or you can use the more common syntax. For `DefaultApp` that would be:
+
```ruby
-request "/", "The HTTP Verb is: " do
- @http[:output] += @http[:method]
-end
+request "/", "Hello World!"
+start
```
-Will return `The HTTP Verb is: get/post/put/patch/delete`, depending on the http verb used in the request. It will also pre-parse a file. So if you have added an HTML file with `request "/", "file.html"`, `@http[:output]` will be set to the contents of that file.
+And for `MyApp` that would be:
-You could also run some complex method created outside the route. This route is called in the same scope as any block you were to declare. Here is an example:
-
```ruby
-request "/", "I'm computing a complex function" do
- complex_function
+class MyApp < ATD::App
+ request "/", "Hello World!"
+ start
end
```
-##### @http
+## Controllers
-The `@http` instance variable can be used to work with the http request the the route is parsing. Here are some ways you can use the `@http` variable:
+> **Caution:**
+>
+> This entire section is experimental right now and as partial or no support. Don't trust this section and tread carefully.
-```ruby
-@http[:status_code] = 200 # By default the status code is 200. You can manipulate this.
-@http[:headers] = {} # By defualt there are no headers. You can add whatever you want.
-@http[:method] = env["REQUEST_METHOD"] # Writing to this does nothing. Just a way for you to see the request method.
-@http[:output] # This is set to the output you give in the args for request
-@http[:request] # The associated Rack::Request for the request.
-```
+Because we understand how important it is to have flexibility in how you work, we provide support of a number of different configurations, and one of the ways we do that is with controllers. A controller is simply a module full of methods which can be referenced from a `Route` by passing `controller_name#action` instead of the file name or by putting it in the options hash with `to: controller_name#action` or `to: :action, controller: "controller_name"`.
-While you can use `@http[:status_code]` to change the status code, you can also set a status code with `r "/", status_code: 200`. That status code must be >= 100, as per the Rack Specification.
+If you want to add all the methods from one controller to an `App` you can do that by calling the `controller` method in the `App` with the name of the controller as a parameter.
-#### Controllers
+Here is an example of an `App` using controllers:
-ATD also had the capabilities to use controllers. A controller is an object which responds to controller methods, usually a module with `extend self`. Here is an example of an app using controllers:
-
```ruby
module MyController
def test_route
return "You've reached test_route"
end
end
request "/", "MyController#test_route" #=> "You've reached test_route"
```
-### Apps
+## Compilation
-ATD also allows you to use different "apps". Each app is independent of the others and lives is a class with the same name as the app. A file can have any number of apps, each of which can have it's own settings, files, and routes. By default, adding routes to `main` will make them a part of the app `DefaultApp`, which will work fine if you only need one app.
+_N.B. At some point in the indefinite future there might (but probably will) be a [tilt](https://github.com/rtomayko/tilt) integration_
-#### App Creation
+### The Basics
-To create an app you can use `ATD.new("AppName")`. It is important to note that *ATD.new is not a constructor, although I will refer to it as one*. It simply behaves like one because you can use it to "construct" a new app. The app creation process creates a new class which you can open anywhere in your app file, with the name you pass. The name must `#respond_to?(:to_sym)`, and must be a valid class name. You must call the constructor before you begin adding routes to the class, or open the class at all.
+Because `ATD` attempts to practice separation of concerns, there is a special module, `ATD::Compilation` which is responsible for dealing with compilation. In `ATD` there are two types of compilers: First, there are precompilers, which run during the apps startup process and do things like minify assets and compile things which do not need to by dynamic. Second, there are compilers, which deal with dynamic assets and run whenever a `Route` is reached. To create them you can use the following syntax (the example is for compiling ERB files):
-You can also use the more intuitive way to create an app, which would be by declaring a class which extends `ATD::App`, like so:
-
```ruby
-class MyAppClass < ATD::App
- # All of my routes, code, etc.
+to_compile "erb" do |file = "", *opts|
+ ERB.new(file).result
end
```
-#### Starting the App
+Or if you wanted to define a precompiler to remove all the newlines from a JS file:
-There are two basic ways to start the app. You can start it by calling `AppName.new.start` or more simply `AppName.start` which will create an instance and call start on it, or you can use the more common syntax. For `DefaultApp` that would be:
-
```ruby
-request "/", "Hello World!"
-start
-```
-
-And for `MyApp` that would be:
-
-```ruby
-class MyApp
- request "/", "Hello World!"
- start
+to_precompile "js" do |file = "", *opts|
+ file.gsub("\n", "")
end
```
-#### App Routing
+If you notices, the precompilers and compilers take the contents of the file as the first argument (`file`) and they take the compiler options as the second argument (`opts`). The options are just whatever was passed with the route, for example in `r "/", "my_file.erb", hi: true` `opts == {hi: true}`.
-To add routes to an app, you simply must call the standard routing method inside the app class. For example, to create and add routes to an app called `Name` I would use the following code:
+> **Under the Hood**
+>
+> The compilation works by going through the file extensions from last to first and running the compilations for each extension in that order. For example `file.html.erb` will first be compiled by the "erb" compiler, then the output of the "erb" compiler will be compiled by the "html" compiler.
-```ruby
-class Name < ATD::App
- request "/", "Hello World"
-end
-```
+### Precompilation
-### Logging
+> Precompilers are not sufficiently advanced to get their own special section yet, but will be soon.
-Currently there is no specified logging interface. Just use `puts` or `p` to log to `STDOUT`.
-
### Compilation
-`ATD` will take your views and compress them for you so that your information can be transmitted more quickly. There are two different types of compilation, precompilation which occurs when the server is started, and compilation, which will live compile your code. While there are a few compilers and precompilers which will come with `ATD` most are user defined. To define compiler and precompiler methods simply add them like so:
+> Compilers are not sufficiently advanced to get their own special section yet, but will be soon.
-```ruby
-module ATD::Compilation::Precomiler
- def extension(file, *opts) # This will work with any file that has .extension
- # File is the contents of the file being compiled
- # Whatever you return is what the file will be compiled to
- file
- end
-end
-
-module ATD::Compilation::Compiler
- # Same thing here
- def extension(file, *opts) # This will work with any file that has .extension
- file
- end
-end
-```
-
-The compilation works by going through the file extensions from last to first and running the compilations for each extension in that order. For example `file.html.erb` will first be compiled by the ERB compiler, then the output of the ERB compiler will be compiled by the HTML compiler.
-
-When you include a file with then `request "/route", "file.ext` it will be precompiled, but not compiled. If you want live compilation there are a few options. If you want live compilation and precompilation you will have to use the following syntax:
-
-```ruby
-request "/", "file.ext" do
- live_compilation_method @http[:output]
-end
-```
-
-If you don't need precompilation and just want live compilation (the ususal use case) then you can just use the following format:
-
-```ruby
-request "/" do
- live_compilation_method File.read("/assets/file.ext")
-end
-```
-
-## Design Paradigms
-
-ATD is designed to fit into many different design paradigms, and to allow each person to adopt their own styles while leaving code readable to everyone. Do do this, the code was left fairly unstructured, but [here are a few examples from well known frameworks](https://gitlab.com/izwick-schachter/atd-examples.git).
-
## Documentation
You can find the YARD docs at http://izwick-schachter.gitlab.io/atd/YARD/.
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
-To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
+To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to the gem page on [rubygems.org](https://rubygems.org/gems/atd).
+### Some notes about Semantic Versioning
+
+[Semantic versioning]() is pretty nice. I like it. But, I really only want to hit a major version when it's production ready, so for now will we will follow semver in that bugfix releases (x.x.*) will be backwards compatible, but minor versions will not be. As soon as this is production ready we will hit 1.0.0 and start using semver.
+
## Contributing
-Bug reports and pull requests are welcome on GitLab at https://gitlab.com/izwick-schachter/atd/issues. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
+[Bug reports](https://gitlab.com/izwick-schachter/atd/issues) and [merge requests](https://gitlab.com/izwick-schachter/atd/merge_requests) are welcome on GitLab. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
-### Contribution Policies
+## Contribution Policies
-Every constribution should correspond to a relevent issue which you keep up to date with notes on what you are working on. Each issues gets branched off of development and is named issue/<issue number>. When you are ready to merege it back in, make sure it passes both rubocop and all the tests. Each issue should get additional test if necessary.
+Every contribution should correspond to a relevant issue which you keep up to date with notes on what you are working on. Each issues gets branched off of development and is named `issue/#{issue_number}`. When you are ready to merge it back in, make sure it passes both rubocop and all the tests. Each issue should get additional tests if necessary.
\ No newline at end of file