slug-engine 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|