sharp 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ *.swp
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ -
2
+ TUTORIAL.md
data/README.md CHANGED
@@ -1,3 +1,25 @@
1
1
  # Sharp
2
2
 
3
- A web framework that ties together rack-router, rack-action, and eventually other gems. It's a work in progress. More to come...
3
+ Sharp is a [Ruby][ruby] and [Rack][rack]-based web framework.
4
+
5
+ ## Installation
6
+
7
+ The only prerequisite for Sharp is Ruby 1.9 or higher. Once you have that installed, you can install Sharp with the standard Rubygems command:
8
+
9
+ $ gem install sharp
10
+
11
+ ## Usage
12
+
13
+ For a basic walkthrough of all the features, read the [tutorial][tutorial].
14
+
15
+ ## Contributing
16
+
17
+ 1. Fork it
18
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
19
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
20
+ 4. Push to the branch (`git push origin my-new-feature`)
21
+ 5. Create new Pull Request
22
+
23
+ [ruby]: http://www.ruby-lang.org
24
+ [rack]: http://rack.github.com
25
+ [tutorial]: https://github.com/pjb3/sharp/blob/master/TUTORIAL.md#creating-your-first-app
data/TUTORIAL.md ADDED
@@ -0,0 +1,187 @@
1
+ # Creating Your First App
2
+
3
+ Sharp comes with an application generator built in to help you get started. To create a sharp app, simply run the following command:
4
+
5
+ $ sharp new hello_world
6
+
7
+ You application will be created in the `hello_world` directory. Next we'll take a look at what Sharp created for you.
8
+
9
+ # Layout of an Application
10
+
11
+ The directories and files created in the `hello_world` directory are as follows:
12
+
13
+ ├── app/
14
+ │   ├── actions/
15
+ │   │   ├── application_action.rb
16
+ │   │   └── root_action.rb
17
+ │   ├── initializers/
18
+ │   │   ├── post/
19
+ │   │   └── pre/
20
+ │   ├── lib/
21
+ │   ├── models/
22
+ │   ├── views/
23
+ │   │   └── application_view.rb
24
+ │   ├── boot.rb
25
+ │   └── routes.rb
26
+ ├── assets/
27
+ │   ├── javascripts/
28
+ │   ├── stylesheets/
29
+ │   └── packages.yml
30
+ ├── config/
31
+ ├── public/
32
+ │   ├── favicon.ico
33
+ │   └── robots.txt
34
+ ├── templates/
35
+ │   ├── layouts/
36
+ │   │   └── application.erb
37
+ │   └── root.erb
38
+ ├── vendor/
39
+ │   └── assets/
40
+ │   ├── javascripts/
41
+ │   └── stylesheets/
42
+ ├── Gemfile
43
+ ├── Guardfile
44
+ ├── Rakefile
45
+ └── config.ru
46
+
47
+ Throughout the rest of this tutorial, we'll cover in detail what each of these subdirectories and files are used for, but for now, we'll just go over the top-level directories.
48
+
49
+ ## app
50
+
51
+ Contains the Ruby code specific to your application. Some files have been created here to help get you started.
52
+
53
+ ## assets
54
+
55
+ Contains the CSS and JS specific to your application.
56
+
57
+ ## config
58
+
59
+ This directory contains configuration files for details that will change based upon the environment you are running your application in.
60
+
61
+ ## public
62
+
63
+ Contains the static files that will be served be the web server.
64
+
65
+ ## templates
66
+
67
+ Contains the templates used to generate HTML. The default templating language is ERB, but Haml, Slim, Liquid, Mustache and other various templating languages can be used as well.
68
+
69
+ ## vender
70
+
71
+ Contains 3rd party CSS and JS you are using in your application. You typically don't modify these files, you import them from another project, such as [jQuery][jquery] or [Twitter Bootstrap][bootstrap].
72
+
73
+ # Starting the Server
74
+
75
+ Now that you have a Sharp application, you can start up the server and see your first web page! Sharp uses [Bundler][bundler] by default, so to get rolling, make sure you have bundler installed and run bundler:
76
+
77
+ $ bundle
78
+
79
+ To start the server, run this command from within the `hello_world` directory:
80
+
81
+ $ shotgun
82
+
83
+ This will start the application on port 9393. You can see what the homepage looks like by running this command:
84
+
85
+ $ curl -s http://localhost:9393
86
+ <!doctype html>
87
+ <html>
88
+ <head>
89
+ <title>Hello, World!</title>
90
+ </head>
91
+ <body>
92
+ <h1>Hello, World!</h1>
93
+ </body>
94
+ </html>
95
+
96
+ You can, of course, also look at this page in your browser:
97
+
98
+ $ open http://localhost:9393
99
+
100
+ Next up, we'll dive into how this page is created.
101
+
102
+ # Routing
103
+
104
+ When your application receives a request, the first thing that happens is the router determines which action to use to generate the response. The routes are defined in the file `app/routes.rb`, which looks like this:
105
+
106
+ ``` ruby
107
+ Sharp.routes do
108
+ get '/' => RootAction
109
+ end
110
+ ```
111
+
112
+ What this means is if the request is a `GET` and the path is just `/`, call the `RootAction` to generate the response. We'll go into routing in more detail later, but for now, let's take a look at what the `RootAction` does.
113
+
114
+ # Actions
115
+
116
+ So as we've seen, the router determines what action should respond to a request, and it is the job of an action to generate the response. If we look at the `RootAction`, which is defined in `app/actions/root_action.rb`, we see this:
117
+
118
+ ``` ruby
119
+ class RootAction < ApplicationAction
120
+ end
121
+ ```
122
+
123
+ Huh, not much going on there, is there? That's because the default behavior of an action is to render the template that matches the name of the action. In this case, the action is `app/actions/root_action.rb`, so the template will be `templates/root.erb`. We'll cover actions in more depth later, but let's take a look at the template next.
124
+
125
+ # Templates
126
+
127
+ There is what the template that is used to generate our response, `templates/root.erb`, looks like:
128
+
129
+ ``` erb
130
+ <h1>Hello, World!</h1>
131
+ ```
132
+
133
+ There's not much to it yet. In fact, this is just a static snippet of HTML. Templates typically have Ruby code mixed in with the HTML, in order to generate a dynamic response. So why don't you go ahead and make a change to see what that looks like. Edit `templates/root.erb` to look like this:
134
+
135
+ ``` erb
136
+ <h1>Hello, World</h1>
137
+ <footer>
138
+ &copy; Copyright <%= Time.now.year -%>
139
+ </footer>
140
+ ```
141
+
142
+ If you make a request to your application now, you'll see this:
143
+
144
+ $ curl -s http://localhost:9393
145
+ <html>
146
+ <head>
147
+ <title>Hello, World!</title>
148
+ </head>
149
+ <body>
150
+ <h1>Hello, World!</h1>
151
+ <footer>
152
+ &copy; Copyright 2013
153
+ </footer>
154
+ </body>
155
+ </html>
156
+
157
+ As you can see, the Ruby code contained between the `<%= -%>` was evaluated and the result what the current year, which was included into the response.
158
+
159
+ One thing to notice is that you didn't have to restart your application, the changes were picked up automatically. This is because we used `shotgun` to start the application, which handles automatically reloading the application between requests for us.
160
+
161
+ One question you may be asking is where did the `<html>`, `<head>` and `<body>` tags come from? `templates/root.erb` just contains the snippet that ends up in the `<body>`. The answer is layouts, which is the topic we'll introduce next.
162
+
163
+ # Layouts
164
+
165
+ In many web application, the same `<head>` and basic structure within the `<body>` is shared among many different pages. Layouts allow you to define that structure in one place and share it between multiple actions. The layout your application is currently using is at `templates/layouts/application.erb`:
166
+
167
+ ``` erb
168
+ <!doctype html>
169
+ <html>
170
+ <head>
171
+ <title>Hello, World!</title>
172
+ </head>
173
+ <body>
174
+ <%= render main -%>
175
+ </body>
176
+ </html>
177
+ ```
178
+
179
+ The tag `<%= render main -%>` is used to specify where the main content for the view should go. In fact, `render` is a generic method that you can use render any template from within another. `main` is a local variable that has the name of the template for the current view, which is `"root"` in this case.
180
+
181
+ # More To Come
182
+
183
+ Sharp is still in the early stages of development, so keep checking back for updates on how to do more advanced things with Sharp.
184
+
185
+ [jquery]: http://jquery.com
186
+ [bootstrap]: http://twitter.github.com/bootstrap
187
+ [bundler]: http://gembundler.com
data/bin/sharp CHANGED
@@ -1,10 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- if ARGV[0] == "console"
3
+ require 'sharp'
4
+
5
+ case ARGV[0]
6
+ when "console"
4
7
  require File.expand_path('app/boot', Dir.pwd)
5
8
  require 'irb'
6
9
  ARGV.clear
7
10
  IRB.start(Sharp.root)
11
+ when "new"
12
+ Sharp.generate(ARGV[1])
8
13
  else
9
14
  $stderr.puts "Unknown command"
10
15
  exit 1
data/lib/sharp.rb CHANGED
@@ -1,16 +1,21 @@
1
- require "sharp/version"
2
- require "fileutils"
3
- require "logger"
4
- require "pathname"
5
- require "rack-action"
6
- require "rack-router"
7
- require "yaml"
1
+ require 'active_support/core_ext'
2
+ require 'fileutils'
3
+ require 'logger'
4
+ require 'pathname'
5
+ require 'rack-action'
6
+ require 'rack-router'
7
+ require 'yaml'
8
+ require 'sharp/action'
9
+ require 'sharp/config'
10
+ require 'sharp/view'
11
+ require 'sharp/generator'
12
+ require 'sharp/version'
8
13
 
9
14
  module Sharp
10
15
 
11
16
  class << self
12
17
  attr_reader :app
13
- delegate :logger, :boot, :root, :router, :env, :db, :to => :app
18
+ delegate :logger, :boot, :root, :router, :env, :config, :route, :get, :post, :put, :delete, :head, :to => :app
14
19
  delegate :routes, :to => :router
15
20
  end
16
21
 
@@ -20,6 +25,12 @@ module Sharp
20
25
  @app
21
26
  end
22
27
 
28
+ def self.generate(name)
29
+ generator = Sharp::Generator.new(name)
30
+ generator.generate
31
+ puts "New sharp application created at #{generator.output_dir}"
32
+ end
33
+
23
34
  class Application
24
35
  attr_reader :root, :router
25
36
 
@@ -29,6 +40,13 @@ module Sharp
29
40
  app
30
41
  end
31
42
 
43
+ # Generates a Rack env Hash
44
+ def self.env(method, path, env={})
45
+ env.merge(
46
+ 'REQUEST_METHOD' => method.to_s.upcase,
47
+ 'PATH_INFO' => path)
48
+ end
49
+
32
50
  def initialize(root)
33
51
  @root = Pathname.new(root)
34
52
  end
@@ -38,6 +56,7 @@ module Sharp
38
56
  false
39
57
  else
40
58
  pre_initialization
59
+ load_i18n
41
60
  load_lib
42
61
  load_models
43
62
  load_actions
@@ -51,6 +70,30 @@ module Sharp
51
70
  @router ||= Rack::Router.new
52
71
  end
53
72
 
73
+ def route(method, path, env={})
74
+ router.match(self.class.env(method, path, env={}))
75
+ end
76
+
77
+ def get(path, env={})
78
+ router.call(self.class.env(:get, path, env={}))
79
+ end
80
+
81
+ def post(path, env={})
82
+ router.call(self.class.env(:post, path, env={}))
83
+ end
84
+
85
+ def put(path, env={})
86
+ router.call(self.class.env(:put, path, env={}))
87
+ end
88
+
89
+ def delete(path, env={})
90
+ router.call(self.class.env(:delete, path, env={}))
91
+ end
92
+
93
+ def head(path, env={})
94
+ router.call(self.class.env(:head, path, env={}))
95
+ end
96
+
54
97
  def env
55
98
  @env ||= ENV['RACK_ENV'].present? ? ENV['RACK_ENV'].to_sym : :development
56
99
  end
@@ -84,13 +127,21 @@ module Sharp
84
127
  end
85
128
  end
86
129
 
87
- def db
88
- @db ||= YAML.load_file(root.join("config/database.yml")).symbolize_keys[env].symbolize_keys
130
+ def config
131
+ @config ||= Sharp::Config.new(env, Dir[root.join("config/*.yml")])
89
132
  end
90
133
 
91
134
  protected
92
135
  def pre_initialization
93
- Dir.glob(root.join("app/preinitializers/*.rb")) {|file| load file }
136
+ Dir.glob(root.join("app/initializers/pre/*.rb")) {|file| load file }
137
+ end
138
+
139
+ def load_i18n
140
+ if Object.const_defined?("I18n")
141
+ Dir.glob(root.join("config/locales/*.yml")) do |file|
142
+ I18n.load_path << file
143
+ end
144
+ end
94
145
  end
95
146
 
96
147
  # TODO: Make an Array of load paths that you can add to that these are just part of
@@ -115,7 +166,7 @@ module Sharp
115
166
  end
116
167
 
117
168
  def post_initialization
118
- Dir.glob(root.join("app/initializers/*.rb")) {|file| load file }
169
+ Dir.glob(root.join("app/initializers/post/*.rb")) {|file| load file }
119
170
  end
120
171
 
121
172
  def finish_boot
@@ -0,0 +1,44 @@
1
+ module Sharp
2
+ class Action < ::Rack::Action
3
+
4
+ def respond
5
+ if layout
6
+ view.render(layout, :main => self.class.template_name)
7
+ else
8
+ view.render(self.class.template_name)
9
+ end
10
+ end
11
+
12
+ def self.base_name
13
+ @base_name ||= name.sub(/Action\Z/,'')
14
+ end
15
+
16
+ def self.view_name
17
+ @view_name ||= "#{base_name}View"
18
+ end
19
+
20
+ def self.view_class
21
+ if defined? @view_class
22
+ @view_class
23
+ else
24
+ @view_class = Object.const_defined?(view_name) ? view_name.constantize : Sharp::View
25
+ end
26
+ end
27
+
28
+ def self.template_name
29
+ @template_name ||= "#{base_name.underscore}.erb"
30
+ end
31
+
32
+ def template
33
+ self.class.template_name
34
+ end
35
+
36
+ def layout
37
+ "layouts/application.erb"
38
+ end
39
+
40
+ def view
41
+ @view ||= self.class.view_class.new
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,13 @@
1
+ module Sharp
2
+ class Config
3
+ def initialize(env, files)
4
+ files.each do |file|
5
+ puts file.inspect
6
+ attr = File.basename(file, '.yml').to_sym
7
+ puts attr.inspect
8
+ (class << self; self; end).send(:attr_accessor, attr)
9
+ send("#{attr}=", YAML.load_file(file).symbolize_keys[env].symbolize_keys)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ module Sharp
2
+ class Generator
3
+ include FileUtils
4
+
5
+ attr_accessor :name, :source_dir, :output_dir
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+
11
+ def source_dir
12
+ @source_dir ||= File.expand_path(File.join('../../../template'), __FILE__)
13
+ end
14
+
15
+ def output_dir
16
+ @output_dir ||= File.expand_path(name)
17
+ end
18
+
19
+ def generate
20
+ cp_r source_dir, output_dir
21
+ configs.each do |config, data|
22
+ open(File.join(output_dir, 'config', "#{config}.yml"), 'w') do |file|
23
+ file << data.to_yaml.sub(/\A---\n/,'')
24
+ end
25
+ end
26
+ end
27
+
28
+ def configs
29
+ {
30
+ #:database => %w[development test production].inject({}) do |cfg, env|
31
+ #cfg[env] = {
32
+ #'adapter' => 'mysql2',
33
+ #'database' => "#{@name}_#{env}",
34
+ #'host' => 'localhost',
35
+ #'user' => 'root'
36
+ #}
37
+ #cfg
38
+ #end
39
+ }
40
+ end
41
+ end
42
+ end
data/lib/sharp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Sharp
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/sharp/view.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'curtain'
2
+
3
+ module Sharp
4
+ class View < ::Curtain::View
5
+ def self.template_directories
6
+ [Sharp.root.join("templates").to_s]
7
+ end
8
+ end
9
+ end
data/sharp.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = "sharp"
5
- gem.version = "0.2.1"
5
+ gem.version = "0.3.0"
6
6
  gem.authors = ["Paul Barry"]
7
7
  gem.email = ["mail@paulbarry.com"]
8
8
  gem.description = %q{A web framework}
@@ -15,6 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
 
17
17
  gem.add_runtime_dependency "activesupport"
18
+ gem.add_runtime_dependency "curtain"
18
19
  gem.add_runtime_dependency "rack-router"
19
20
  gem.add_runtime_dependency "rack-action"
20
21
  end
data/template/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "curtain", :require => "curtain/erubis"
4
+ gem "erubis"
5
+ gem "sharp"
6
+ gem "temple"
7
+
8
+ group :development do
9
+ gem "shotgun"
10
+ end
File without changes
data/template/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << "test"
5
+ t.test_files = FileList['test/models/*.rb', 'test/actions/*.rb']
6
+ t.verbose = true
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,3 @@
1
+ class ApplicationAction < Sharp::Action
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class RootAction < ApplicationAction
2
+
3
+ end
@@ -0,0 +1,4 @@
1
+ require 'bundler'
2
+ Bundler.require(:default, ENV['RACK_ENV'] ? ENV['RACK_ENV'].to_sym : :development)
3
+
4
+ Sharp.boot(File.expand_path('../..', __FILE__))
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,3 @@
1
+ Sharp.routes do
2
+ get '/' => RootAction
3
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationView < Sharp::View
2
+
3
+ end
File without changes
File without changes
File without changes
@@ -0,0 +1,3 @@
1
+ require File.expand_path('../app/boot', __FILE__)
2
+
3
+ run Sharp.router
File without changes
@@ -0,0 +1 @@
1
+ TODO: Create a favicon.ico
@@ -0,0 +1 @@
1
+ TOOD: What is the format of this file supposed to be?
@@ -0,0 +1,9 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>Hello, World!</title>
5
+ </head>
6
+ <body>
7
+ <%= render main -%>
8
+ </body>
9
+ </html>
@@ -0,0 +1 @@
1
+ <h1>Hello, World!</h1>
File without changes
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sharp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-25 00:00:00.000000000 Z
12
+ date: 2013-03-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: curtain
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
30
46
  - !ruby/object:Gem::Dependency
31
47
  name: rack-router
32
48
  requirement: !ruby/object:Gem::Requirement
@@ -69,14 +85,43 @@ extra_rdoc_files: []
69
85
  files:
70
86
  - .gitignore
71
87
  - .rvmrc
88
+ - .yardopts
72
89
  - Gemfile
73
90
  - LICENSE.txt
74
91
  - README.md
75
92
  - Rakefile
93
+ - TUTORIAL.md
76
94
  - bin/sharp
77
95
  - lib/sharp.rb
96
+ - lib/sharp/action.rb
97
+ - lib/sharp/config.rb
98
+ - lib/sharp/generator.rb
78
99
  - lib/sharp/version.rb
100
+ - lib/sharp/view.rb
79
101
  - sharp.gemspec
102
+ - template/Gemfile
103
+ - template/Guardfile
104
+ - template/Rakefile
105
+ - template/app/actions/application_action.rb
106
+ - template/app/actions/root_action.rb
107
+ - template/app/boot.rb
108
+ - template/app/initializers/post/.gitkeep
109
+ - template/app/initializers/pre/.gitkeep
110
+ - template/app/lib/.gitkeep
111
+ - template/app/models/.gitkeep
112
+ - template/app/routes.rb
113
+ - template/app/views/application_view.rb
114
+ - template/assets/javascripts/.gitkeep
115
+ - template/assets/package.yml
116
+ - template/assets/stylesheets/.gitkeep
117
+ - template/config.ru
118
+ - template/config/.gitkeep
119
+ - template/public/favicon.ico
120
+ - template/public/robots.txt
121
+ - template/templates/layouts/application.erb
122
+ - template/templates/root.erb
123
+ - template/vendor/assets/javascripts/.gitkeep
124
+ - template/vendor/assets/stylesheets/.gitkeep
80
125
  homepage: ''
81
126
  licenses: []
82
127
  post_install_message:
@@ -97,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
142
  version: '0'
98
143
  requirements: []
99
144
  rubyforge_project:
100
- rubygems_version: 1.8.24
145
+ rubygems_version: 1.8.25
101
146
  signing_key:
102
147
  specification_version: 3
103
148
  summary: A web framework