slug-engine 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +35 -0
- data/Rakefile +39 -0
- data/app/controllers/permalinks_controller.rb +45 -0
- data/app/helpers/permalinks_helper.rb +2 -0
- data/app/models/permalink.rb +28 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20111108194929_create_permalinks.rb +11 -0
- data/lib/slug-engine.rb +1 -0
- data/lib/slug.rb +80 -0
- data/lib/slug/engine.rb +8 -0
- data/lib/slug/filter.rb +31 -0
- data/lib/slug/version.rb +3 -0
- data/lib/tasks/slug_tasks.rake +4 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +9 -0
- data/test/dummy/app/assets/javascripts/posts.js +2 -0
- data/test/dummy/app/assets/javascripts/users.js +2 -0
- data/test/dummy/app/assets/stylesheets/application.css +7 -0
- data/test/dummy/app/assets/stylesheets/posts.css +4 -0
- data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
- data/test/dummy/app/assets/stylesheets/users.css +4 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/posts_controller.rb +83 -0
- data/test/dummy/app/controllers/users_controller.rb +83 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/helpers/posts_helper.rb +2 -0
- data/test/dummy/app/helpers/users_helper.rb +2 -0
- data/test/dummy/app/models/post.rb +10 -0
- data/test/dummy/app/models/user.rb +2 -0
- data/test/dummy/app/views/layouts/_application.html.erb +14 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/posts/_form.html.erb +29 -0
- data/test/dummy/app/views/posts/_post.html.erb +22 -0
- data/test/dummy/app/views/posts/edit.html.erb +6 -0
- data/test/dummy/app/views/posts/index.html.erb +27 -0
- data/test/dummy/app/views/posts/new.html.erb +5 -0
- data/test/dummy/app/views/posts/show.html.erb +1 -0
- data/test/dummy/app/views/users/_form.html.erb +25 -0
- data/test/dummy/app/views/users/edit.html.erb +6 -0
- data/test/dummy/app/views/users/index.html.erb +25 -0
- data/test/dummy/app/views/users/new.html.erb +5 -0
- data/test/dummy/app/views/users/show.html.erb +15 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +45 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +30 -0
- data/test/dummy/config/environments/production.rb +60 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +6 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20111108195813_create_posts.rb +10 -0
- data/test/dummy/db/migrate/20111108195947_create_users.rb +10 -0
- data/test/dummy/db/schema.rb +37 -0
- data/test/dummy/log/development.log +4475 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/test/fixtures/posts.yml +9 -0
- data/test/dummy/test/fixtures/users.yml +9 -0
- data/test/dummy/test/functional/posts_controller_test.rb +49 -0
- data/test/dummy/test/functional/users_controller_test.rb +49 -0
- data/test/dummy/test/unit/helpers/posts_helper_test.rb +4 -0
- data/test/dummy/test/unit/helpers/users_helper_test.rb +4 -0
- data/test/dummy/test/unit/post_test.rb +7 -0
- data/test/dummy/test/unit/user_test.rb +7 -0
- data/test/dummy/tmp/cache/assets/C06/A00/sprockets%2Fe8698635e3a42938c23078559237f054 +0 -0
- data/test/dummy/tmp/cache/assets/CAA/620/sprockets%2F87b209c0c9da28094a8d5581a21262c6 +0 -0
- data/test/dummy/tmp/cache/assets/CC7/5F0/sprockets%2F8bf37168fb70e08b9e4578e72739a543 +0 -0
- data/test/dummy/tmp/cache/assets/CDA/250/sprockets%2F9f6ba51c401a4b1885146d692f56dd20 +9051 -0
- data/test/dummy/tmp/cache/assets/CDF/070/sprockets%2F70e3c8a3916622c17858d520dcee0d92 +0 -0
- data/test/dummy/tmp/cache/assets/CF0/1D0/sprockets%2F6fc757c2c8329244ca95d6909865bbc2 +0 -0
- data/test/dummy/tmp/cache/assets/D03/AF0/sprockets%2F79009b48c13f7b213134c8ac51caf65b +9051 -0
- data/test/dummy/tmp/cache/assets/D11/D20/sprockets%2Fcac21eac42152981882bf9e489316af4 +0 -0
- data/test/dummy/tmp/cache/assets/D1A/310/sprockets%2F5384ad85f52d3272dbc64d46ef3876a4 +0 -0
- data/test/dummy/tmp/cache/assets/D23/E70/sprockets%2F8de174e8fa8a6828ee7fa348335900f4 +369 -0
- data/test/dummy/tmp/cache/assets/D2E/090/sprockets%2Fc54283e81ce52bf9d542ab12492a01ad +0 -0
- data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/D36/030/sprockets%2F63bdbca18172eaab4b20628e4399d663 +9048 -0
- data/test/dummy/tmp/cache/assets/D3E/B20/sprockets%2F1f79ee84bd6d74d100c5cb018b662b67 +0 -0
- data/test/dummy/tmp/cache/assets/D46/650/sprockets%2Ff56253b5f374fff1a33fbbc9881c9124 +0 -0
- data/test/dummy/tmp/cache/assets/D54/ED0/sprockets%2F71c9fa01091d432b131da3bb73faf3d4 +0 -0
- data/test/dummy/tmp/cache/assets/D73/220/sprockets%2F3dbc0a37f98fb43ec819b85a64d32c55 +0 -0
- data/test/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +9429 -0
- data/test/dummy/tmp/cache/assets/D9B/9C0/sprockets%2F4e197c078b17aea1ec1df9746ddf2a30 +372 -0
- data/test/dummy/tmp/cache/assets/D9C/CD0/sprockets%2Fc85016e7bbd4f3adbb7635d01f85d39b +0 -0
- data/test/dummy/tmp/cache/assets/DA1/4A0/sprockets%2Fb398851b563c62cf3bf4cbbad8c57d25 +369 -0
- data/test/dummy/tmp/cache/assets/DBA/AB0/sprockets%2F8be4208fd77cb5ef9b6f77e5fcf18670 +0 -0
- data/test/dummy/tmp/cache/assets/DC0/870/sprockets%2F289d3a70441ad26d24edcd25fd0dca6c +372 -0
- data/test/dummy/tmp/cache/assets/E03/E80/sprockets%2F4a6c93824e15c2d5fdfbbbf7cfdc3646 +9048 -0
- data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/E06/920/sprockets%2F6baa37d1c26468aef4fe5fe4bf982fa3 +0 -0
- data/test/dummy/tmp/pids/server.pid +1 -0
- data/test/slug_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- metadata +291 -0
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2011 YOURNAME
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
= SlugEngine
|
|
2
|
+
|
|
3
|
+
SlugEngine provides an easy, reusable method of adding permalinks to your content.
|
|
4
|
+
|
|
5
|
+
== Getting Started
|
|
6
|
+
|
|
7
|
+
First, add `gem 'slug_engine'` to your `Gemfile` and run `bundle install`.
|
|
8
|
+
|
|
9
|
+
Second, mount the `SlugEngine` in `config/routes.rb` at the appropriate location.
|
|
10
|
+
|
|
11
|
+
My::Application.routes.draw do
|
|
12
|
+
|
|
13
|
+
# some REST-ful routes
|
|
14
|
+
resources :posts
|
|
15
|
+
|
|
16
|
+
# the SlugEngine
|
|
17
|
+
mount Slug::Engine => "/"
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
It's best if you place the `SlugEngine` after your other routes, but this is not required. If the `SlugEngine` cannot find a matching slug, it will abstain from handling the request, allowing lower priority routes or other engines to take a stab at the request. If no further routes match, the normal 404 page will be returned.
|
|
22
|
+
|
|
23
|
+
You can use a prefix with your slugs if you like:
|
|
24
|
+
|
|
25
|
+
My::Application.routes.draw do
|
|
26
|
+
|
|
27
|
+
# some REST-ful routes
|
|
28
|
+
resources :posts
|
|
29
|
+
|
|
30
|
+
# the SlugEngine
|
|
31
|
+
mount Slug::Engine => "/p"
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
This will match requests like `"http://www.example.com/p/my-slug"` where `'my-slug'` is the matched slug. When authoring your content, `'/p'` will not be considered part of the slug, e.g., `p = Post.create :title => 'My Post', :slug => 'my-slug'` would still resolve when accessed at `"http://www.example.com/p/my-slug"` if the engine was prefixed.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
begin
|
|
8
|
+
require 'rdoc/task'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
require 'rdoc/rdoc'
|
|
11
|
+
require 'rake/rdoctask'
|
|
12
|
+
RDoc::Task = Rake::RDocTask
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
17
|
+
rdoc.title = 'Slug'
|
|
18
|
+
rdoc.options << '--line-numbers'
|
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
|
24
|
+
load 'rails/tasks/engine.rake'
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
Bundler::GemHelper.install_tasks
|
|
28
|
+
|
|
29
|
+
require 'rake/testtask'
|
|
30
|
+
|
|
31
|
+
Rake::TestTask.new(:test) do |t|
|
|
32
|
+
t.libs << 'lib'
|
|
33
|
+
t.libs << 'test'
|
|
34
|
+
t.pattern = 'test/**/*_test.rb'
|
|
35
|
+
t.verbose = false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
task :default => :test
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
class PermalinksController < ApplicationController
|
|
2
|
+
|
|
3
|
+
# TODO: Need to figure out how to handle slugs with extensions, e.g., 'foo/bar/baz.jpg'
|
|
4
|
+
def show
|
|
5
|
+
# find permalink, raising error if none found
|
|
6
|
+
@permalink = Permalink.find_by_slug!(params[:slug])
|
|
7
|
+
|
|
8
|
+
# find content by permalink, raising error if none found
|
|
9
|
+
@content = content_class.find_by_permalink!(@permalink)
|
|
10
|
+
@content_type = @permalink.content_type
|
|
11
|
+
|
|
12
|
+
# customize view path based on content_type
|
|
13
|
+
prepend_view_path "app/views/#{@content_type.pluralize.underscore}"
|
|
14
|
+
|
|
15
|
+
# set params[:controller] to match model of content
|
|
16
|
+
params[:controller] = @content_type.pluralize.underscore
|
|
17
|
+
|
|
18
|
+
# render the appropriate partial for this content, and do it in the layout
|
|
19
|
+
render :partial => partial, :object => decorate(@content), :layout => true
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
protected
|
|
24
|
+
|
|
25
|
+
def content_class
|
|
26
|
+
@permalink.content_type.constantize
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# TODO: Make this an application-extension, not built-in
|
|
30
|
+
def decorate(content)
|
|
31
|
+
decorated = begin
|
|
32
|
+
Module.const_get("#{@content_type}Decorator").decorate(content)
|
|
33
|
+
rescue NameError
|
|
34
|
+
content
|
|
35
|
+
end
|
|
36
|
+
decorated
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def partial
|
|
40
|
+
# We're going to have to make a guess about this...
|
|
41
|
+
# TODO: Is there some way to still defer to the partial renderer to determine the partial to use?
|
|
42
|
+
"#{@content_type.downcase.pluralize}/#{@content_type.downcase}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class Permalink < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
belongs_to :content, :polymorphic => true
|
|
4
|
+
before_save :validate_content
|
|
5
|
+
|
|
6
|
+
validates_presence_of :slug
|
|
7
|
+
validates_uniqueness_of :slug
|
|
8
|
+
validate :not_system_slug
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
# Validates that the slug is not routable to a controller other than PermalinksController
|
|
13
|
+
def not_system_slug
|
|
14
|
+
begin
|
|
15
|
+
route = Rails.application.routes.recognize_path "/#{slug}"
|
|
16
|
+
errors.add :slug, "is a reserved system route" unless route[:controller] == "permalinks"
|
|
17
|
+
rescue ActionController::RoutingError
|
|
18
|
+
# No route matches, so that's probably good
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def validate_content
|
|
23
|
+
errors.add :content_id, "can't be blank" unless content_id.present?
|
|
24
|
+
errors.add :content_type, "can't be blank" unless content_type.present?
|
|
25
|
+
errors.empty?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
data/config/routes.rb
ADDED
data/lib/slug-engine.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'slug'
|
data/lib/slug.rb
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require "slug/filter"
|
|
2
|
+
require "slug/engine"
|
|
3
|
+
|
|
4
|
+
module Slug
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
# setup polymorphic association
|
|
9
|
+
has_one :permalink, :as => :content, :dependent => :destroy, :autosave => true
|
|
10
|
+
|
|
11
|
+
# allows forms to accept values for permalink
|
|
12
|
+
accepts_nested_attributes_for :permalink
|
|
13
|
+
|
|
14
|
+
# allows alternative access to slug (although validation errors will still be on 'permalink.slug')
|
|
15
|
+
delegate :slug, :slug=, :to => :permalink
|
|
16
|
+
|
|
17
|
+
# add validation callbacks
|
|
18
|
+
before_validation :set_default_slug
|
|
19
|
+
|
|
20
|
+
# This little syntactic sugar causes permalink to be created lazily precisely
|
|
21
|
+
# the first time it is accessed. This is necessary because we delegate :slug
|
|
22
|
+
# to :permalink, and if permalink is nil, things get hairy.
|
|
23
|
+
redefine_method(:permalink) do |*args|
|
|
24
|
+
association(:permalink).reader(*args) || build_permalink(*args)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Setup a 'permalink' scope so model classes can find by permalink
|
|
28
|
+
# find_by_permalink doesn't use this, but it may be useful elsewhere
|
|
29
|
+
scope :permalink, lambda { |permalink| where(:id => permalink.content_id) }
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module ClassMethods
|
|
34
|
+
|
|
35
|
+
# Finder returning the matching content for a given permalink, or nil if
|
|
36
|
+
# none found. Notably, the content_type of the given permalink must match
|
|
37
|
+
# the target model's class
|
|
38
|
+
def find_by_permalink(permalink)
|
|
39
|
+
find_by_permalink!(permalink)
|
|
40
|
+
rescue
|
|
41
|
+
nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Finder returning the matching content for a given permalink. Raises an
|
|
45
|
+
# ActiveRecord::RecordNotFound error if no match is found or if the
|
|
46
|
+
# content_type of the given permalink must match the target model's class
|
|
47
|
+
def find_by_permalink!(permalink)
|
|
48
|
+
result = scoped_by_permalink(permalink).first
|
|
49
|
+
raise ActiveRecord::RecordNotFound.new "Could not find Content for Permalink id=#{permalink.id}" if result.nil?
|
|
50
|
+
result
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
protected
|
|
54
|
+
|
|
55
|
+
# Default scope for returning the matching content for a given permalink.
|
|
56
|
+
# Model classes may override this method to extend the scope query, for
|
|
57
|
+
# example, to restrict content found by permalink by additional parameters.
|
|
58
|
+
def scoped_by_permalink(permalink)
|
|
59
|
+
# scoped_by_id is a 'magic' scope created by missing_method
|
|
60
|
+
scoped_by_id(permalink.content_id)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
module InstanceMethods
|
|
66
|
+
|
|
67
|
+
protected
|
|
68
|
+
|
|
69
|
+
# provide a suitable default slug value
|
|
70
|
+
def default_slug
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# set default_slug on permalink before validation
|
|
75
|
+
def set_default_slug
|
|
76
|
+
permalink.slug = default_slug unless permalink.slug.present?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
end
|
data/lib/slug/engine.rb
ADDED
data/lib/slug/filter.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# This middleware is necessary so that the engine can abstain from processing
|
|
2
|
+
# requests where no such slug exists. If this behavior were done in a
|
|
3
|
+
# controller, Rails would heavily mutate the request object and no additional
|
|
4
|
+
# controllers would have much luck processing the request. Specifically, the
|
|
5
|
+
# named parameters for the route are cached on the request, and downstream
|
|
6
|
+
# controllers would get the wrong params hash.
|
|
7
|
+
|
|
8
|
+
module Slug
|
|
9
|
+
class Filter
|
|
10
|
+
|
|
11
|
+
def initialize(app)
|
|
12
|
+
@app = app
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(env)
|
|
16
|
+
# if a matching slug exists in the database, allow engine to process it
|
|
17
|
+
if Permalink.where('slug = ?', slug(env)).exists?
|
|
18
|
+
@app.call(env)
|
|
19
|
+
else # force engine to abstain from processing this request
|
|
20
|
+
[404, {"X-Cascade" => "pass"}, ["no such slug exists"]]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Extract the :slug value from the raw request
|
|
25
|
+
def slug(env)
|
|
26
|
+
slug = env['PATH_INFO']
|
|
27
|
+
slug = slug.slice(1..-1) if slug[0] == 47 # has a leading '/'
|
|
28
|
+
slug
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/slug/version.rb
ADDED
data/test/dummy/Rakefile
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
|
3
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
|
4
|
+
|
|
5
|
+
require File.expand_path('../config/application', __FILE__)
|
|
6
|
+
|
|
7
|
+
Dummy::Application.load_tasks
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// This is a manifest file that'll be compiled into including all the files listed below.
|
|
2
|
+
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
|
|
3
|
+
// be included in the compiled file accessible from http://example.com/assets/application.js
|
|
4
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
|
5
|
+
// the compiled file.
|
|
6
|
+
//
|
|
7
|
+
//= require jquery
|
|
8
|
+
//= require jquery_ujs
|
|
9
|
+
//= require_tree .
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
|
3
|
+
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
|
4
|
+
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
|
5
|
+
*= require_self
|
|
6
|
+
*= require_tree .
|
|
7
|
+
*/
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
body { background-color: #fff; color: #333; }
|
|
2
|
+
|
|
3
|
+
body, p, ol, ul, td {
|
|
4
|
+
font-family: verdana, arial, helvetica, sans-serif;
|
|
5
|
+
font-size: 13px;
|
|
6
|
+
line-height: 18px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
pre {
|
|
10
|
+
background-color: #eee;
|
|
11
|
+
padding: 10px;
|
|
12
|
+
font-size: 11px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
a { color: #000; }
|
|
16
|
+
a:visited { color: #666; }
|
|
17
|
+
a:hover { color: #fff; background-color:#000; }
|
|
18
|
+
|
|
19
|
+
div.field, div.actions {
|
|
20
|
+
margin-bottom: 10px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#notice {
|
|
24
|
+
color: green;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.field_with_errors {
|
|
28
|
+
padding: 2px;
|
|
29
|
+
background-color: red;
|
|
30
|
+
display: table;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#error_explanation {
|
|
34
|
+
width: 450px;
|
|
35
|
+
border: 2px solid red;
|
|
36
|
+
padding: 7px;
|
|
37
|
+
padding-bottom: 0;
|
|
38
|
+
margin-bottom: 20px;
|
|
39
|
+
background-color: #f0f0f0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#error_explanation h2 {
|
|
43
|
+
text-align: left;
|
|
44
|
+
font-weight: bold;
|
|
45
|
+
padding: 5px 5px 5px 15px;
|
|
46
|
+
font-size: 12px;
|
|
47
|
+
margin: -7px;
|
|
48
|
+
margin-bottom: 0px;
|
|
49
|
+
background-color: #c00;
|
|
50
|
+
color: #fff;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#error_explanation ul li {
|
|
54
|
+
font-size: 12px;
|
|
55
|
+
list-style: square;
|
|
56
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
class PostsController < ApplicationController
|
|
2
|
+
# GET /posts
|
|
3
|
+
# GET /posts.json
|
|
4
|
+
def index
|
|
5
|
+
@posts = Post.all
|
|
6
|
+
|
|
7
|
+
respond_to do |format|
|
|
8
|
+
format.html # index.html.erb
|
|
9
|
+
format.json { render :json => @posts }
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# GET /posts/1
|
|
14
|
+
# GET /posts/1.json
|
|
15
|
+
def show
|
|
16
|
+
@post = Post.find(params[:id])
|
|
17
|
+
|
|
18
|
+
respond_to do |format|
|
|
19
|
+
format.html # show.html.erb
|
|
20
|
+
format.json { render :json => @post }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# GET /posts/new
|
|
25
|
+
# GET /posts/new.json
|
|
26
|
+
def new
|
|
27
|
+
@post = Post.new
|
|
28
|
+
|
|
29
|
+
respond_to do |format|
|
|
30
|
+
format.html # new.html.erb
|
|
31
|
+
format.json { render :json => @post }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# GET /posts/1/edit
|
|
36
|
+
def edit
|
|
37
|
+
@post = Post.find(params[:id])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# POST /posts
|
|
41
|
+
# POST /posts.json
|
|
42
|
+
def create
|
|
43
|
+
@post = Post.new(params[:post])
|
|
44
|
+
|
|
45
|
+
respond_to do |format|
|
|
46
|
+
if @post.save
|
|
47
|
+
format.html { redirect_to @post, :notice => 'Post was successfully created.' }
|
|
48
|
+
format.json { render :json => @post, :status => :created, :location => @post }
|
|
49
|
+
else
|
|
50
|
+
format.html { render :action => "new" }
|
|
51
|
+
format.json { render :json => @post.errors, :status => :unprocessable_entity }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# PUT /posts/1
|
|
57
|
+
# PUT /posts/1.json
|
|
58
|
+
def update
|
|
59
|
+
@post = Post.find(params[:id])
|
|
60
|
+
|
|
61
|
+
respond_to do |format|
|
|
62
|
+
if @post.update_attributes(params[:post])
|
|
63
|
+
format.html { redirect_to @post, :notice => 'Post was successfully updated.' }
|
|
64
|
+
format.json { head :ok }
|
|
65
|
+
else
|
|
66
|
+
format.html { render :action => "edit" }
|
|
67
|
+
format.json { render :json => @post.errors, :status => :unprocessable_entity }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# DELETE /posts/1
|
|
73
|
+
# DELETE /posts/1.json
|
|
74
|
+
def destroy
|
|
75
|
+
@post = Post.find(params[:id])
|
|
76
|
+
@post.destroy
|
|
77
|
+
|
|
78
|
+
respond_to do |format|
|
|
79
|
+
format.html { redirect_to posts_url }
|
|
80
|
+
format.json { head :ok }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|