Skip to content
Browse files

First commit

  • Loading branch information...
0 parents commit dd7fd96572fdae79e4013345441e54ad022b4656 @Bodacious Bodacious committed Sep 12, 2011
Showing with 2,581 additions and 0 deletions.
  1. BIN .DS_Store
  2. +7 −0 .gitignore
  3. +1 −0 .rspec
  4. +25 −0 Gemfile
  5. +132 −0 Gemfile.lock
  6. +27 −0 Guardfile
  7. +20 −0 MIT-LICENSE
  8. +3 −0 README.md
  9. +39 −0 Rakefile
  10. 0 app/assets/images/blog/.gitkeep
  11. +2 −0 app/assets/javascripts/blog/index.js
  12. +3 −0 app/assets/stylesheets/blog/comments.css
  13. +3 −0 app/assets/stylesheets/blog/index.css
  14. +18 −0 app/controllers/blog/application_controller.rb
  15. +26 −0 app/controllers/blog/comments_controller.rb
  16. +55 −0 app/controllers/blog/posts_controller.rb
  17. +50 −0 app/helpers/blog/application_helper.rb
  18. +16 −0 app/helpers/blog/comments_helper.rb
  19. +25 −0 app/helpers/blog/posts_helper.rb
  20. +56 −0 app/models/blog/comment.rb
  21. +51 −0 app/models/blog/post.rb
  22. +16 −0 app/views/blog/comments/_comment.html.erb
  23. +40 −0 app/views/blog/comments/_form.html.erb
  24. +7 −0 app/views/blog/comments/create.js.erb
  25. +1 −0 app/views/blog/posts/_blog_post_spacer.html.erb
  26. +4 −0 app/views/blog/posts/_blogger_information.html.erb
  27. +5 −0 app/views/blog/posts/_comments_count.html.erb
  28. +33 −0 app/views/blog/posts/_form.html.erb
  29. +1 −0 app/views/blog/posts/_pagination.html.erb
  30. +19 −0 app/views/blog/posts/_post.html.erb
  31. +1 −0 app/views/blog/posts/_post_body.html.erb
  32. +3 −0 app/views/blog/posts/_post_head.html.erb
  33. +5 −0 app/views/blog/posts/_post_links.html.erb
  34. +3 −0 app/views/blog/posts/edit.html.erb
  35. +10 −0 app/views/blog/posts/index.html.erb
  36. +3 −0 app/views/blog/posts/new.html.erb
  37. +7 −0 app/views/blog/posts/show.html.erb
  38. +23 −0 blog.gemspec
  39. +9 −0 config/routes.rb
  40. +12 −0 db/migrate/20110814091434_create_blog_posts.rb
  41. +15 −0 db/migrate/20110814093229_create_blog_comments.rb
  42. +28 −0 db/migrate/20110814103306_acts_as_taggable_on_migration.rb
  43. BIN lib/.DS_Store
  44. +19 −0 lib/blog.rb
  45. BIN lib/blog/.DS_Store
  46. +17 −0 lib/blog/blogs.rb
  47. +40 −0 lib/blog/configuration.rb
  48. +14 −0 lib/blog/engine.rb
  49. +3 −0 lib/blog/version.rb
  50. +4 −0 lib/tasks/blog_tasks.rake
  51. +3 −0 lib/validators.rb
  52. +11 −0 lib/validators/absence_validator.rb
  53. +6 −0 script/rails
  54. +20 −0 spec/blog_spec.rb
  55. +189 −0 spec/controllers/blog/posts_controller_spec.rb
  56. +7 −0 spec/dummy/Rakefile
  57. +9 −0 spec/dummy/app/assets/javascripts/application.js
  58. +2 −0 spec/dummy/app/assets/javascripts/people.js
  59. +2 −0 spec/dummy/app/assets/javascripts/sessions.js
  60. +2 −0 spec/dummy/app/assets/javascripts/users.js
  61. +58 −0 spec/dummy/app/assets/stylesheets/application.css
  62. +4 −0 spec/dummy/app/assets/stylesheets/people.css
  63. +4 −0 spec/dummy/app/assets/stylesheets/sessions.css
  64. +4 −0 spec/dummy/app/assets/stylesheets/users.css
  65. +19 −0 spec/dummy/app/controllers/application_controller.rb
  66. +83 −0 spec/dummy/app/controllers/people_controller.rb
  67. +23 −0 spec/dummy/app/controllers/sessions_controller.rb
  68. +83 −0 spec/dummy/app/controllers/users_controller.rb
  69. +2 −0 spec/dummy/app/helpers/application_helper.rb
  70. +2 −0 spec/dummy/app/helpers/people_helper.rb
  71. +2 −0 spec/dummy/app/helpers/sessions_helper.rb
  72. +2 −0 spec/dummy/app/helpers/users_helper.rb
  73. 0 spec/dummy/app/mailers/.gitkeep
  74. 0 spec/dummy/app/models/.gitkeep
  75. +2 −0 spec/dummy/app/models/person.rb
  76. +11 −0 spec/dummy/app/models/user.rb
  77. +28 −0 spec/dummy/app/views/layouts/application.html.erb
  78. +21 −0 spec/dummy/app/views/people/_form.html.erb
  79. +6 −0 spec/dummy/app/views/people/edit.html.erb
  80. +23 −0 spec/dummy/app/views/people/index.html.erb
  81. +5 −0 spec/dummy/app/views/people/new.html.erb
  82. +10 −0 spec/dummy/app/views/people/show.html.erb
  83. +17 −0 spec/dummy/app/views/sessions/new.html.erb
  84. +25 −0 spec/dummy/app/views/users/_form.html.erb
  85. +6 −0 spec/dummy/app/views/users/edit.html.erb
  86. +25 −0 spec/dummy/app/views/users/index.html.erb
  87. +5 −0 spec/dummy/app/views/users/new.html.erb
  88. +15 −0 spec/dummy/app/views/users/show.html.erb
  89. +4 −0 spec/dummy/config.ru
  90. +23 −0 spec/dummy/config/application.rb
  91. +10 −0 spec/dummy/config/boot.rb
  92. +25 −0 spec/dummy/config/database.yml
  93. +5 −0 spec/dummy/config/environment.rb
  94. +30 −0 spec/dummy/config/environments/development.rb
  95. +51 −0 spec/dummy/config/environments/production.rb
  96. +39 −0 spec/dummy/config/environments/test.rb
  97. +7 −0 spec/dummy/config/initializers/backtrace_silencers.rb
  98. +3 −0 spec/dummy/config/initializers/blog.rb
  99. +10 −0 spec/dummy/config/initializers/inflections.rb
  100. +5 −0 spec/dummy/config/initializers/mime_types.rb
  101. +7 −0 spec/dummy/config/initializers/secret_token.rb
  102. +8 −0 spec/dummy/config/initializers/session_store.rb
  103. +12 −0 spec/dummy/config/initializers/wrap_parameters.rb
  104. +5 −0 spec/dummy/config/locales/en.yml
  105. +11 −0 spec/dummy/config/routes.rb
  106. +10 −0 spec/dummy/db/migrate/20110814091304_create_users.rb
  107. +9 −0 spec/dummy/db/migrate/20110819103335_create_people.rb
  108. +71 −0 spec/dummy/db/schema.rb
  109. 0 spec/dummy/lib/assets/.gitkeep
  110. 0 spec/dummy/log/.gitkeep
  111. +26 −0 spec/dummy/public/404.html
  112. +26 −0 spec/dummy/public/422.html
  113. +26 −0 spec/dummy/public/500.html
  114. 0 spec/dummy/public/favicon.ico
  115. +6 −0 spec/dummy/script/rails
  116. +7 −0 spec/dummy/test/fixtures/people.yml
  117. +9 −0 spec/dummy/test/fixtures/users.yml
  118. +49 −0 spec/dummy/test/functional/people_controller_test.rb
  119. +9 −0 spec/dummy/test/functional/sessions_controller_test.rb
  120. +49 −0 spec/dummy/test/functional/users_controller_test.rb
  121. +4 −0 spec/dummy/test/unit/helpers/people_helper_test.rb
  122. +4 −0 spec/dummy/test/unit/helpers/sessions_helper_test.rb
  123. +4 −0 spec/dummy/test/unit/helpers/users_helper_test.rb
  124. +7 −0 spec/dummy/test/unit/person_test.rb
  125. +7 −0 spec/dummy/test/unit/user_test.rb
  126. +27 −0 spec/factories.rb
  127. +14 −0 spec/helpers/blog/application_helper_spec.rb
  128. +28 −0 spec/helpers/blog/posts_helper_spec.rb
  129. +23 −0 spec/lib/blogs_spec.rb
  130. +31 −0 spec/lib/configuration_spec.rb
  131. +64 −0 spec/models/blog/comment_spec.rb
  132. +153 −0 spec/models/blog/post_spec.rb
  133. +16 −0 spec/spec_helper.rb
  134. +10 −0 spec/support/authentication.rb
BIN .DS_Store
Binary file not shown.
7 .gitignore
@@ -0,0 +1,7 @@
+.bundle/
+.rvmrc
+log/*.log
+pkg/
+spec/dummy/db/*.sqlite3
+spec/dummy/log/*.log
+spec/dummy/tmp/
1 .rspec
@@ -0,0 +1 @@
+--colour
25 Gemfile
@@ -0,0 +1,25 @@
+source "http://rubygems.org"
+
+gem 'acts-as-taggable-on'
+gem "decent_exposure"
+gem 'jquery-rails'
+gem "redcarpet"
+gem "kaminari"
+
+group :test, :development do
+ gem 'rails', '3.1.0'
+
+ gem 'sqlite3'
+ gem "guard"
+ gem "guard-rspec"
+
+ if RUBY_PLATFORM =~ /darwin/i
+ gem "rb-fsevent"
+ gem "growl"
+ end
+
+ gem "factory_girl", "2.0.0.rc4"
+ gem "mocha"
+ gem "rspec"
+ gem "rspec-rails"
+end
132 Gemfile.lock
@@ -0,0 +1,132 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ actionmailer (3.1.0)
+ actionpack (= 3.1.0)
+ mail (~> 2.3.0)
+ actionpack (3.1.0)
+ activemodel (= 3.1.0)
+ activesupport (= 3.1.0)
+ builder (~> 3.0.0)
+ erubis (~> 2.7.0)
+ i18n (~> 0.6)
+ rack (~> 1.3.2)
+ rack-cache (~> 1.0.3)
+ rack-mount (~> 0.8.2)
+ rack-test (~> 0.6.1)
+ sprockets (~> 2.0.0)
+ activemodel (3.1.0)
+ activesupport (= 3.1.0)
+ bcrypt-ruby (~> 3.0.0)
+ builder (~> 3.0.0)
+ i18n (~> 0.6)
+ activerecord (3.1.0)
+ activemodel (= 3.1.0)
+ activesupport (= 3.1.0)
+ arel (~> 2.2.1)
+ tzinfo (~> 0.3.29)
+ activeresource (3.1.0)
+ activemodel (= 3.1.0)
+ activesupport (= 3.1.0)
+ activesupport (3.1.0)
+ multi_json (~> 1.0)
+ acts-as-taggable-on (2.0.6)
+ arel (2.2.1)
+ bcrypt-ruby (3.0.0)
+ builder (3.0.0)
+ decent_exposure (1.0.1)
+ diff-lcs (1.1.2)
+ erubis (2.7.0)
+ factory_girl (2.0.0.rc4)
+ growl (1.0.3)
+ guard (0.6.2)
+ thor (~> 0.14.6)
+ guard-rspec (0.4.2)
+ guard (>= 0.4.0)
+ hike (1.2.1)
+ i18n (0.6.0)
+ jquery-rails (1.0.13)
+ railties (~> 3.0)
+ thor (~> 0.14)
+ kaminari (0.12.4)
+ rails (>= 3.0.0)
+ mail (2.3.0)
+ i18n (>= 0.4.0)
+ mime-types (~> 1.16)
+ treetop (~> 1.4.8)
+ mime-types (1.16)
+ mocha (0.9.12)
+ multi_json (1.0.3)
+ polyglot (0.3.2)
+ rack (1.3.2)
+ rack-cache (1.0.3)
+ rack (>= 0.4)
+ rack-mount (0.8.3)
+ rack (>= 1.0.0)
+ rack-ssl (1.3.2)
+ rack
+ rack-test (0.6.1)
+ rack (>= 1.0)
+ rails (3.1.0)
+ actionmailer (= 3.1.0)
+ actionpack (= 3.1.0)
+ activerecord (= 3.1.0)
+ activeresource (= 3.1.0)
+ activesupport (= 3.1.0)
+ bundler (~> 1.0)
+ railties (= 3.1.0)
+ railties (3.1.0)
+ actionpack (= 3.1.0)
+ activesupport (= 3.1.0)
+ rack-ssl (~> 1.3.2)
+ rake (>= 0.8.7)
+ rdoc (~> 3.4)
+ thor (~> 0.14.6)
+ rake (0.9.2)
+ rb-fsevent (0.4.3.1)
+ rdoc (3.9.4)
+ redcarpet (1.17.2)
+ rspec (2.6.0)
+ rspec-core (~> 2.6.0)
+ rspec-expectations (~> 2.6.0)
+ rspec-mocks (~> 2.6.0)
+ rspec-core (2.6.4)
+ rspec-expectations (2.6.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.6.0)
+ rspec-rails (2.6.1)
+ actionpack (~> 3.0)
+ activesupport (~> 3.0)
+ railties (~> 3.0)
+ rspec (~> 2.6.0)
+ sprockets (2.0.0)
+ hike (~> 1.2)
+ rack (~> 1.0)
+ tilt (!= 1.3.0, ~> 1.1)
+ sqlite3 (1.3.4)
+ thor (0.14.6)
+ tilt (1.3.3)
+ treetop (1.4.10)
+ polyglot
+ polyglot (>= 0.3.1)
+ tzinfo (0.3.29)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ acts-as-taggable-on
+ decent_exposure
+ factory_girl (= 2.0.0.rc4)
+ growl
+ guard
+ guard-rspec
+ jquery-rails
+ kaminari
+ mocha
+ rails (= 3.1.0)
+ rb-fsevent
+ redcarpet
+ rspec
+ rspec-rails
+ sqlite3
27 Guardfile
@@ -0,0 +1,27 @@
+# A sample Guardfile
+# More info at https://github.com/guard/guard#readme
+
+guard 'rspec', :version => 2 do
+ watch(%r{^spec/.+_spec\.rb$})
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
+ watch('spec/spec_helper.rb') { "spec/" }
+ watch('spec/factories.rb') { "spec/" }
+
+
+ watch(%r{^spec/.+_spec\.rb$})
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec/" }
+
+ watch('spec/spec_helper.rb') { "spec/" }
+ watch('config/routes.rb') { "spec/routing" }
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
+
+ # dummy app
+ watch(%r{^spec/dummy/config/initializers/(.+)\.rb$}) { "spec/" }
+
+ # Capybara request specs
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
+end
+
20 MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright 2011 YOURNAME
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3 README.md
@@ -0,0 +1,3 @@
+# Blog
+
+This is a work in progress - please ignore for now
39 Rakefile
@@ -0,0 +1,39 @@
+#!/usr/bin/env rake
+begin
+ require 'bundler/setup'
+rescue LoadError
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
+end
+begin
+ require 'rdoc/task'
+rescue LoadError
+ require 'rdoc/rdoc'
+ require 'rake/rdoctask'
+ RDoc::Task = Rake::RDocTask
+end
+
+RDoc::Task.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'Blog'
+ rdoc.options << '--line-numbers'
+ rdoc.rdoc_files.include('README.rdoc')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
+
+APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
+load 'rails/tasks/engine.rake'
+
+
+Bundler::GemHelper.install_tasks
+
+require 'rake/testtask'
+
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.libs << 'test'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = false
+end
+
+
+task :default => :test
0 app/assets/images/blog/.gitkeep
No changes.
2 app/assets/javascripts/blog/index.js
@@ -0,0 +1,2 @@
+//= require jquery
+//= require jquery_ujs
3 app/assets/stylesheets/blog/comments.css
@@ -0,0 +1,3 @@
+#new_blog_comment .hidden{
+ display: none;
+}
3 app/assets/stylesheets/blog/index.css
@@ -0,0 +1,3 @@
+/*
+ *= require_tree
+*/
18 app/controllers/blog/application_controller.rb
@@ -0,0 +1,18 @@
+module Blog
+
+ # Inherits from the application's controller instead of ActionController::Base
+ class ApplicationController < ::ApplicationController
+
+ helper :all
+ helper_method :current_blogger
+
+ def current_blogger
+ send Blog::configuration.current_blogger_method
+ end
+
+ def this_blogger?(post)
+ current_blogger == post.blogger
+ end
+
+ end
+end
26 app/controllers/blog/comments_controller.rb
@@ -0,0 +1,26 @@
+module Blog
+ class CommentsController < ApplicationController
+
+ expose(:post) { Blog::Post.find(params[:post_id]) }
+ expose(:comments) { post.comments }
+ expose(:comment)
+
+
+ def create
+ respond_to do |format|
+ format.js
+
+ format.html {
+ if comment.save
+ redirect_to(post, notice: "Successfully added comment!")
+ else
+ render "blog/posts/show"
+ end
+ }
+
+ end
+
+ end
+
+ end
+end
55 app/controllers/blog/posts_controller.rb
@@ -0,0 +1,55 @@
+module Blog
+ class PostsController < ApplicationController
+
+ before_filter Blog.configuration.authentication_method, except: [:index, :show]
+
+ expose(:posts) { Post.for_index(params[:page]) }
+ expose(:post) do
+ case action_name
+ when /new|create/
+ current_blogger.blog_posts.new(params[:post])
+ when /edit|update|destroy/
+ current_blogger.blog_posts.find(params[:id])
+ when /show/
+ Blog::Post.find(params[:id])
+ end
+ end
+
+ expose(:comments) { post.comments }
+ expose(:comment) { post.comments.build }
+
+ def index
+ end
+
+ def show
+ end
+
+ def new
+ end
+
+ def edit
+ end
+
+ def create
+ if post.save
+ redirect_to post, notice: 'Blog post was successfully created.'
+ else
+ render action: "new"
+ end
+ end
+
+ def update
+ if post.update_attributes(params[:post])
+ redirect_to post, notice: 'Blog post was successfully updated.'
+ else
+ render action: "edit"
+ end
+ end
+
+ def destroy
+ post.destroy
+ redirect_to posts_url
+ end
+
+ end
+end
50 app/helpers/blog/application_helper.rb
@@ -0,0 +1,50 @@
+module Blog
+ module ApplicationHelper
+
+ TIMETAG_FORMAT = "%Y-%m-%dT%TZ"
+
+ def errors_on(object, attribute)
+ object.errors[attribute].first.to_s
+ end
+
+ def field(content_or_options={}, options ={}, &block)
+ div_with_default_class(:field, content_or_options, options, &block)
+ end
+
+ def actions(content_or_options={}, options ={}, &block)
+ div_with_default_class(:actions, content_or_options, options, &block)
+ end
+
+ def login_required(content_or_options={}, options ={}, &block)
+ div_with_default_class(:login_required, content_or_options, options, &block) if current_blogger
+ end
+
+ def time_tag(time_object, format = nil, options ={})
+ # if there's a specified format and it's a string, assume it's an strftime string
+ if format && format.is_a?(String)
+ time_string = time_object.strftime(format)
+ # if there's a specified format and it's a symbol, assume it's a predefined format
+ elsif format && format.is_a?(Symbol)
+ time_string = time_object.to_s(format)
+ else
+ time_string = time_object.to_s
+ end
+ options.merge(datetime: time_object.strftime(TIMETAG_FORMAT))
+ content_tag(:time, time_string, options)
+ end
+
+ private
+
+ def div_with_default_class(default_class, content_or_options={}, options={}, &block)
+ if block_given?
+ content = capture(&block)
+ options = content_or_options
+ else
+ content = content_or_options
+ end
+ options[:class] = "#{default_class} #{options[:class]}".strip
+ content_tag(:div, content, options)
+ end
+
+ end
+end
16 app/helpers/blog/comments_helper.rb
@@ -0,0 +1,16 @@
+module Blog
+ module CommentsHelper
+
+ def blog_comment_tag(name, content_or_options = {}, options ={}, &block)
+ if block_given?
+ content = capture(&block)
+ options = content_or_options
+ else
+ content = content_or_options
+ end
+ options[:class] = "#{options[:class]} blog_comment_#{name}".strip
+ content_tag(name, content, options)
+ end
+
+ end
+end
25 app/helpers/blog/posts_helper.rb
@@ -0,0 +1,25 @@
+module Blog
+ module PostsHelper
+
+ def markdown(content = nil, &block)
+ content = capture(&block) if block_given?
+ Redcarpet.new(content).to_html.html_safe
+ end
+
+ def blog_post_tag(name, content_or_options = {}, options ={}, &block)
+ if block_given?
+ content = capture(&block)
+ options = content_or_options
+ else
+ content = content_or_options
+ end
+ options[:class] = "#{options[:class]} blog_post_#{name}".strip
+ content_tag(name, content, options)
+ end
+
+ def this_blogger?(post)
+ current_blogger == post.blogger
+ end
+
+ end
+end
56 app/models/blog/comment.rb
@@ -0,0 +1,56 @@
+module Blog
+ class Comment < ActiveRecord::Base
+
+ # ================
+ # = Associations =
+ # ================
+
+ belongs_to :post, class_name: "Blog::Post", foreign_key: "post_id", counter_cache: true
+
+ # TODO: Check if this is optimal
+ URL_REGEX = /^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/ix
+
+ # TODO: Check if this is optimal
+ EMAIL_REGEX = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i
+
+ # ============
+ # = Callbacks =
+ # ============
+
+ before_validation :format_website
+
+ # ===============
+ # = Validations =
+ # ===============
+ validates :name, presence: true
+ validates :email, presence: true, format: {with: EMAIL_REGEX, allow_blank: true }
+ validates :body, presence: true, length: { minimum: 4, allow_blank: true}
+ validates :website, format: {with: URL_REGEX, allow_blank: true}
+
+ # nickname acts as a "honeypot" to catch spam
+ # the form field should be hidden using CSS and so
+ # if present, must be spam.
+ validates :nickname, absence: true
+
+ # ==============
+ # = Attributes =
+ # ==============
+
+ # nickname acts as a "honeypot" to catch spam
+ # the form field should be hidden using CSS and so
+ # if present, must be spam.
+ #
+ # @attribute
+ attr_accessor :nickname
+
+ private
+
+ # Prepend http to the url before the validation check
+ def format_website
+ if self.website.present? and self.website !~ /^http/i
+ self.website = "http://#{self.website}"
+ end
+ end
+
+ end
+end
51 app/models/blog/post.rb
@@ -0,0 +1,51 @@
+
+module Blog
+ class Post < ActiveRecord::Base
+
+ acts_as_taggable
+
+ paginates_per Blog.configuration.posts_per_page
+
+ # ===============
+ # = Validations =
+ # ===============
+
+ validates :title, presence: true, length: { minimum: 10, maximum: 66 }
+ validates :body, presence: true, length: { minimum: 10 }
+ validates :blogger_id, presence: true
+
+ # =================
+ # = Assosciations =
+ # =================
+
+ belongs_to :blogger, :polymorphic => true
+
+ if Blog.configuration.include_comments
+ has_many :comments, :class_name => "Blog::Comment"
+ end
+
+ # ==========
+ # = Scopes =
+ # ==========
+
+ # Returns the blog posts paginated for the index page
+ # @scope class
+ scope :for_index, lambda { |page = 1| order("updated_at DESC").page(page) }
+
+ # ====================
+ # = Instance Methods =
+ # ====================
+
+ def to_param
+ "#{id}-#{title.parameterize}"
+ end
+
+ # If there's a current blogger and the display name method is set, returns the blogger's display name
+ # Otherwise, returns an empty string
+ def blogger_display_name
+ raise ConfigurationError, "#{self.blogger.class}##{Blog.configuration.blogger_display_name_method} is not defined" if self.blogger and !self.blogger.respond_to?(Blog.configuration.blogger_display_name_method)
+ self.blogger.send Blog.configuration.blogger_display_name_method
+ end
+
+ end
+end
16 app/views/blog/comments/_comment.html.erb
@@ -0,0 +1,16 @@
+<%= content_tag_for(:article, comment) do %>
+ <%= content_tag(:div, class: "blog_comment_name", id: "blog_comment_#{comment.id}_name") do %>
+ <%= comment.website? ? link_to(comment.name, comment.website) : comment.name %> wrote:
+ <% end %>
+
+ <%= content_tag(:div, comment.body, class: "blog_comment_body", id: "blog_comment_#{comment.id}_body") %>
+
+ <%= blog_comment_tag(:footer) do %>
+ Posted on <%= time_tag(post.created_at, Blog.configuration.datetime_format) %>
+ <% end %>
+
+ <%= login_required(class: "actions") do %>
+ <%= link_to("delete", [post, comment], method: :delete) %>
+ <% end %>
+
+<% end %>
40 app/views/blog/comments/_form.html.erb
@@ -0,0 +1,40 @@
+
+<%= form_for [post, comment], remote: true do |f| -%>
+
+ <p>Leave a comment</p>
+
+ <%= field class: "hidden" do %>
+ <%= f.label :nickname %>
+ <%= f.text_field :nickname %>
+ Hide me using CSS
+ <% end %>
+
+ <%= field do %>
+ <%= f.label :name, "Name *" %><br>
+ <%= f.text_field :name %>
+ <%= errors_on(comment, :name) %>
+ <% end %>
+
+ <%= field do %>
+ <%= f.label :email, "Email" %> (never displayed)<br>
+ <%= f.email_field :email %>
+ <%= errors_on(comment, :email) %>
+ <% end %>
+
+ <%= field do %>
+ <%= f.label :website, "Your Website" %><br>
+ <%= f.url_field :website %>
+ <%= errors_on(comment, :website) %>
+ <% end %>
+
+
+ <%= field do %>
+ <%= f.label :body, "Your comment *" %><br>
+ <%= f.text_area :body %><br>
+ <%= errors_on(comment, :body) %>
+ <% end %>
+
+ <%= actions do %>
+ <%= f.submit "Add Comment", :disable_with => 'Adding Comment...' %>
+ <% end %>
+<% end -%>
7 app/views/blog/comments/create.js.erb
@@ -0,0 +1,7 @@
+var $form = $("form#new_blog_comment");
+<% if comment.save %>
+ $("#comments").append("<%= escape_javascript(render(comment)) %>");
+ $form.get(0).reset();
+<% else %>
+ $form.html("<%= escape_javascript(render('form')) %>");
+<% end %>
1 app/views/blog/posts/_blog_post_spacer.html.erb
@@ -0,0 +1 @@
+<hr class="blog_post_spacer">
4 app/views/blog/posts/_blogger_information.html.erb
@@ -0,0 +1,4 @@
+<%= blog_post_tag :footer do %>
+ Written by <%= post.blogger_display_name %> at
+ <%= time_tag(post.created_at, Blog.configuration.datetime_format) %>
+<% end %>
5 app/views/blog/posts/_comments_count.html.erb
@@ -0,0 +1,5 @@
+<%= content_tag(:div, class: "blog_post_comments_count", id: "blog_post_#{post.id}_comments_count") do %>
+ <%= link_to(post_path(post, anchor: "comments")) do %>
+ <%= pluralize post.comments_count, "comment" %>
+ <% end %>
+<% end %>
33 app/views/blog/posts/_form.html.erb
@@ -0,0 +1,33 @@
+<%= form_for(post) do |f| %>
+ <% if post.errors.any? %>
+ <div id="error_explanation">
+ <h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>
+
+ <ul>
+ <% post.errors.full_messages.each do |msg| %>
+ <li><%= msg %></li>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
+
+ <%= field do %>
+ <%= f.label :title %><br>
+ <%= f.text_field :title %>
+ <% end %>
+
+ <%= field do %>
+ <%= f.label :body %><br>
+ <%= f.text_area :body %>
+ <% end %>
+
+ <%= field do %>
+ <%= f.label :tag_list %>
+ <%= f.text_field :tag_list %>
+ <% end %>
+
+ <%= actions do %>
+ <%= f.submit %> or <%= link_to("cancel", post.new_record? ? root_path : post_path(post)) %>
+ <% end %>
+
+<% end %>
1 app/views/blog/posts/_pagination.html.erb
@@ -0,0 +1 @@
+<%= paginate(posts) %>
19 app/views/blog/posts/_post.html.erb
@@ -0,0 +1,19 @@
+<%= content_tag_for(:article, post) do %>
+
+ <%# Render the header for this blog post %>
+ <%= render "post_head", post: post %>
+
+ <%# Render the body of this blog post (as Markdown) %>
+ <%= render "post_body", post: post %>
+
+ <%# Render admin links to edit/delete this post %>
+ <%= render "post_links", post: post %>
+
+ <%# Render info about the person who wrote this post %>
+ <%= render "blogger_information", post: post %>
+
+ <%# Render the no. of comments %>
+ <%= render "comments_count", post: post if defined?(show_comments_count) and show_comments_count %>
+
+
+<% end %>
1 app/views/blog/posts/_post_body.html.erb
@@ -0,0 +1 @@
+<%= markdown(post.body) %>
3 app/views/blog/posts/_post_head.html.erb
@@ -0,0 +1,3 @@
+<%= blog_post_tag :header do %>
+ <%= content_tag(:h1, link_to(post.title, post)) %>
+<% end %>
5 app/views/blog/posts/_post_links.html.erb
@@ -0,0 +1,5 @@
+<%= login_required do %>
+ <%= actions do %>
+ <%= link_to("edit", edit_post_path(post)) %> | <%= link_to("delete", post, method: :destroy, confirm: "Are you sure you want to remove this post?") %>
+ <% end %>
+<% end %>
3 app/views/blog/posts/edit.html.erb
@@ -0,0 +1,3 @@
+<h2>Edit Blog Post #<%= post.id %></h2>
+
+<%= render 'form' %>
10 app/views/blog/posts/index.html.erb
@@ -0,0 +1,10 @@
+<%= login_required class: "actions", id: "new_blog_post_link" do %>
+ <%= link_to 'New Blog post', new_post_path %>
+<% end %>
+
+<%= render partial: "blog/posts/post",
+ collection: posts,
+ spacer_template: "blog_post_spacer",
+ locals: {show_comments_count: true} %>
+
+<%= render "pagination" %>
3 app/views/blog/posts/new.html.erb
@@ -0,0 +1,3 @@
+<h2>Write a new blog post</h2>
+
+<%= render 'form' %>
7 app/views/blog/posts/show.html.erb
@@ -0,0 +1,7 @@
+<%= render post %>
+<% if Blog.configuration.include_comments %>
+ <div id="comments">
+ <%= render comments %>
+ </div>
+ <%= render "blog/comments/form" %>
+<% end %>
23 blog.gemspec
@@ -0,0 +1,23 @@
+$:.push File.expand_path("../lib", __FILE__)
+
+# Maintain your gem's version:
+require "blog/version"
+
+# Describe your gem and declare its dependencies:
+Gem::Specification.new do |s|
+ s.name = "blog"
+ s.version = Blog::VERSION
+ s.authors = ["TODO: Your name"]
+ s.email = ["TODO: Your email"]
+ s.homepage = "TODO"
+ s.summary = "TODO: Summary of Blog."
+ s.description = "TODO: Description of Blog."
+
+ s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.rdoc"]
+ s.test_files = Dir["test/**/*"]
+
+ s.add_dependency "rails", "~> 3.1.0"
+ # s.add_dependency "jquery-rails"
+
+ s.add_development_dependency "sqlite3"
+end
9 config/routes.rb
@@ -0,0 +1,9 @@
+Blog::Engine.routes.draw do
+
+ resources :posts, controller: "posts", except: [:index] do
+ resources :comments, only: [:create,:destroy]
+ end
+
+ match "/(page/:page)" => "posts#index"
+ root to: "posts#index"
+end
12 db/migrate/20110814091434_create_blog_posts.rb
@@ -0,0 +1,12 @@
+class CreateBlogPosts < ActiveRecord::Migration
+ def change
+ create_table :blog_posts do |t|
+ t.string :title, null: false
+ t.text :body, null: false
+ t.references :blogger, polymorphic: true
+ t.integer :comments_count, default: 0, null: false
+ t.timestamps
+ end
+ add_index :blog_posts, [:blogger_type, :blogger_id]
+ end
+end
15 db/migrate/20110814093229_create_blog_comments.rb
@@ -0,0 +1,15 @@
+class CreateBlogComments < ActiveRecord::Migration
+ def change
+ create_table :blog_comments do |t|
+ t.string :name, null: false
+ t.string :email, null: false
+ t.string :website
+ t.text :body, null: false
+ t.references :post, null: false
+ t.string :state
+
+ t.timestamps
+ end
+ add_index :blog_comments, :post_id
+ end
+end
28 db/migrate/20110814103306_acts_as_taggable_on_migration.rb
@@ -0,0 +1,28 @@
+class ActsAsTaggableOnMigration < ActiveRecord::Migration
+ def self.up
+ create_table :tags do |t|
+ t.string :name
+ end
+
+ create_table :taggings do |t|
+ t.references :tag
+
+ # You should make sure that the column created is
+ # long enough to store the required class names.
+ t.references :taggable, :polymorphic => true
+ t.references :tagger, :polymorphic => true
+
+ t.string :context
+
+ t.datetime :created_at
+ end
+
+ add_index :taggings, :tag_id
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
+ end
+
+ def self.down
+ drop_table :taggings
+ drop_table :tags
+ end
+end
BIN lib/.DS_Store
Binary file not shown.
19 lib/blog.rb
@@ -0,0 +1,19 @@
+require "blog/configuration"
+require "blog/blogs"
+require "blog/engine"
+require "validators"
+
+module Blog
+
+ # Exception raised when gem may not be configured properly
+ class ConfigurationError < StandardError;end
+
+ def self.configure(&block)
+ block.call(configuration)
+ end
+
+ def self.configuration
+ @configuration ||= Configuration.new
+ end
+
+end
BIN lib/blog/.DS_Store
Binary file not shown.
17 lib/blog/blogs.rb
@@ -0,0 +1,17 @@
+module Blog
+ module Blogs
+
+ def self.included(base)
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+
+ def blogs
+ has_many :blog_posts, :as => "blogger", :class_name => "Blog::Post"
+ end
+
+ end
+
+ end
+end
40 lib/blog/configuration.rb
@@ -0,0 +1,40 @@
+module Blog
+ class Configuration
+
+ # Should we include comments for blog posts?
+ attr_accessor :include_comments
+
+ # The name of the controller method we'll call to return the current blogger.
+ attr_accessor :current_blogger_method
+
+ # what method do we call on blogger to return their display name?
+ # Defaults to :username
+ attr_accessor :blogger_display_name_method
+
+ # Which DateTime::FORMATS format do we use to display
+ # blog and comment publish time
+ # Defaults to :short
+ attr_accessor :datetime_format
+
+ # No. of posts to show per page
+ #
+ # @see https://github.com/amatsuda/kaminari
+ # @see Blog::Post
+ attr_accessor :posts_per_page
+
+
+ # The name of the before filter we'll call to authenticate the current user.
+ # Defaults to :login_required
+ attr_accessor :authentication_method
+
+ def initialize
+ @include_comments = true
+ @current_blogger_method = :current_user
+ @blogger_display_name_method = :username
+ @datetime_format = :short
+ @posts_per_page = 5
+ @authentication_method = :login_required
+ end
+
+ end
+end
14 lib/blog/engine.rb
@@ -0,0 +1,14 @@
+module Blog
+ class Engine < Rails::Engine
+ isolate_namespace Blog
+
+ config.to_prepare do
+
+ if defined?(::ActiveRecord::Base)
+ ::ActiveRecord::Base.send(:include, Blog::Blogs)
+ ::ActiveRecord::Base.send(:include, Validators)
+ end
+
+ end
+ end
+end
3 lib/blog/version.rb
@@ -0,0 +1,3 @@
+module Blog
+ VERSION = "0.0.1"
+end
4 lib/tasks/blog_tasks.rake
@@ -0,0 +1,4 @@
+# desc "Explaining what the task does"
+# task :blog do
+# # Task goes here
+# end
3 lib/validators.rb
@@ -0,0 +1,3 @@
+require "validators/absence_validator"
+module Validators
+end
11 lib/validators/absence_validator.rb
@@ -0,0 +1,11 @@
+module Validators
+
+ class AbsenceValidator < ActiveModel::EachValidator
+
+ def validate_each(record, attribute, value)
+ record.errors.add(attribute, :invalid, options) unless value.blank?
+ end
+
+ end
+
+end
6 script/rails
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+#!/usr/bin/env ruby
+# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
+
+ENGINE_PATH = File.expand_path('../..', __FILE__)
+load File.expand_path('../../spec/dummy/script/rails', __FILE__)
20 spec/blog_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+
+describe Blog do
+
+ it "should allow developers to set configurations with a block" do
+ initial_value = Blog.configuration.current_blogger_method
+ Blog.configure do |config|
+ config.current_blogger_method = :logged_in_user
+ end
+ user_set_value = Blog.configuration.current_blogger_method
+ initial_value.should_not eql(user_set_value)
+ end
+
+ after :all do
+ Blog.configure do |config|
+ config.inspect
+ end
+ end
+
+end
189 spec/controllers/blog/posts_controller_spec.rb
@@ -0,0 +1,189 @@
+require 'spec_helper'
+
+describe PostsController do
+
+
+ describe "GET 'index'" do
+
+ let(:posts) { [] }
+
+ def do_get(page=nil)
+ get :index, :use_route => :blog, page: page.to_s
+ end
+
+ it "should set posts to Blog::Post.for_index" do
+ Blog::Post.expects(:for_index).returns(posts)
+ do_get
+ controller.posts.should == posts
+ end
+
+ it "should pass the page param to for_index" do
+ Blog::Post.expects(:for_index).with("2").returns(posts)
+ do_get("2")
+ controller.posts.should == posts
+ end
+
+ end
+
+ describe "GET 'new'" do
+
+ context "when logged in" do
+
+ before do
+ mock_login
+ reset_configuration
+ end
+
+ def do_get
+ get :new, use_route: :blog
+ end
+
+ it "should be successful" do
+ do_get
+ response.should be_success
+ end
+
+ it "should set post to a new blog post" do
+ do_get
+ controller.post.should be_a(Blog::Post)
+ controller.post.should be_a_new_record
+ end
+
+ end
+
+ context "when not logged in" do
+
+ def do_get
+ get :new, use_route: :blog
+ end
+
+ # It's not really the responsibility of the gem to manage authentication
+ # so testing for specific behaviour here is not required
+ # at the very least though, we'd expect the status not to be 200
+ it "should redirect to another pages" do
+ do_get
+ response.should_not be_success
+ end
+
+ end
+ end
+
+ describe "POST /create" do
+
+ context "when logged in" do
+
+ before do
+ mock_login
+ reset_configuration
+ end
+
+ context "with valid params" do
+
+ let(:post_attributes) { attributes_for(:post) }
+ let(:blog_post) { build :post }
+
+ def do_post
+ post :create, use_route: :blog, post: post_attributes
+ end
+
+ before do
+ @blog_posts = []
+ @current_blogger.expects(:blog_posts).returns(@blog_posts)
+ @blog_posts.expects(:new).with(post_attributes.stringify_keys).returns(blog_post)
+ blog_post.expects(:save).returns(true)
+ end
+
+ it "should redirect to the blog post page" do
+ do_post
+ response.should redirect_to(controller.posts_url)
+ end
+
+ end
+
+ end
+
+ end
+
+
+ describe "GET 'edit'" do
+
+ context "when logged in" do
+
+ let(:blog_post) { build :post }
+
+ before do
+ mock_login
+ @blog_posts = []
+ @current_blogger.expects(:blog_posts).returns(@blog_posts)
+ @blog_posts.expects(:find).with("1").returns(blog_post)
+ end
+
+ def do_get
+ get :edit, :id => 1, use_route: :blog
+ end
+
+ it "should find the blog post by the ID" do
+ do_get
+ controller.post.should eql(blog_post)
+ end
+
+ end
+
+ context "when not logged in" do
+
+ def do_get
+ get :edit, :id => 1, use_route: :blog
+ end
+
+ # It's not really the responsibility of the gem to manage authentication
+ # so testing for specific behaviour here is not required
+ # at the very least though, we'd expect the status not to be 200
+ it "should redirect to another pages" do
+ do_get
+ response.should_not be_success
+ end
+
+ end
+
+ end
+
+ describe "PUT 'update'" do
+
+ context "when logged in" do
+
+ before do
+ mock_login
+ reset_configuration
+ end
+
+ it "should update the post attributes from params" do
+
+ end
+ end
+
+ context "when not logged in" do
+
+ end
+
+ end
+
+ describe "GET 'show'" do
+
+ let(:blog_post) { build(:post) }
+
+ before do
+ Blog::Post.expects(:find).with("1").returns(blog_post)
+ end
+
+ def do_get
+ get :show, :id => 1, use_route: :blog
+ end
+
+ it "should find blog post by id" do
+ do_get
+ controller.post.should eql(blog_post)
+ end
+
+ end
+
+end
7 spec/dummy/Rakefile
@@ -0,0 +1,7 @@
+#!/usr/bin/env rake
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require File.expand_path('../config/application', __FILE__)
+
+Dummy::Application.load_tasks
9 spec/dummy/app/assets/javascripts/application.js
@@ -0,0 +1,9 @@
+// This is a manifest file that'll be compiled into including all the files listed below.
+// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// be included in the compiled file accessible from http://example.com/assets/application.js
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// the compiled file.
+//
+//= require jquery
+//= require jquery_ujs
+//= require_tree .
2 spec/dummy/app/assets/javascripts/people.js
@@ -0,0 +1,2 @@
+// Place all the behaviors and hooks related to the matching controller here.
+// All this logic will automatically be available in application.js.
2 spec/dummy/app/assets/javascripts/sessions.js
@@ -0,0 +1,2 @@
+// Place all the behaviors and hooks related to the matching controller here.
+// All this logic will automatically be available in application.js.
2 spec/dummy/app/assets/javascripts/users.js
@@ -0,0 +1,2 @@
+// Place all the behaviors and hooks related to the matching controller here.
+// All this logic will automatically be available in application.js.
58 spec/dummy/app/assets/stylesheets/application.css
@@ -0,0 +1,58 @@
+/* =================================== */
+/* = Some CSS for the dummy app only = */
+/* =================================== */
+
+/* *= require_tree . */
+body{
+ font-size: 15px;
+ line-height: 1.5em;
+ font-family: verdana, sans-serif;
+}
+#content, footer{
+ width: 800px;
+ margin: 0 auto;
+}
+footer#application_footer{
+ text-align:center;
+}
+.blog_post {
+
+}
+.blog_post header h1 a{
+ text-decoration: none;
+ color: blue;
+}
+.blog_post_footer, footer{
+ font-size: 0.8em;
+ font-style: italic;
+ color: silver;
+}
+.blog_post_comments_count{
+ margin: 1em 0;
+ text-align: right;
+}
+.blog_post_spacer{
+ width: 80%;
+ margin: 1em auto;
+ border-color: thin solid silver;
+ border-top: none;
+}
+#new_blog_post_link{
+ text-align: right;
+}
+#new_blog_post, .edit_blog_post{
+ width: 100%;
+
+}
+#post_title, #post_body{
+ width: 100%;
+}
+.blog_comment{
+ width: 20em;
+ margin: 1em 0;
+ padding: 0 0 1em;
+ border-bottom: thin solid silver;
+}
+.blog_comment .actions, .blog_post .actions{
+ text-align: right;
+}
4 spec/dummy/app/assets/stylesheets/people.css
@@ -0,0 +1,4 @@
+/*
+ Place all the styles related to the matching controller here.
+ They will automatically be included in application.css.
+*/
4 spec/dummy/app/assets/stylesheets/sessions.css
@@ -0,0 +1,4 @@
+/*
+ Place all the styles related to the matching controller here.
+ They will automatically be included in application.css.
+*/
4 spec/dummy/app/assets/stylesheets/users.css
@@ -0,0 +1,4 @@
+/*
+ Place all the styles related to the matching controller here.
+ They will automatically be included in application.css.
+*/
19 spec/dummy/app/controllers/application_controller.rb
@@ -0,0 +1,19 @@
+class ApplicationController < ActionController::Base
+
+ protect_from_forgery
+
+ helper_method :current_user
+
+ def current_user
+ # session[:user_id] = User.first.id
+ @current_user ||= User.find_by_id(session[:user_id])
+ end
+
+ def login_required
+ unless current_user
+ flash[:warn] = "You must be logged in to view that page!"
+ redirect_to root_url
+ end
+ end
+
+end
83 spec/dummy/app/controllers/people_controller.rb
@@ -0,0 +1,83 @@
+class PeopleController < ApplicationController
+ # GET /people
+ # GET /people.json
+ def index
+ @people = Person.all
+
+ respond_to do |format|
+ format.html # index.html.erb
+ format.json { render json: @people }
+ end
+ end
+
+ # GET /people/1
+ # GET /people/1.json
+ def show
+ @person = Person.find(params[:id])
+
+ respond_to do |format|
+ format.html # show.html.erb
+ format.json { render json: @person }
+ end
+ end
+
+ # GET /people/new
+ # GET /people/new.json
+ def new
+ @person = Person.new
+
+ respond_to do |format|
+ format.html # new.html.erb
+ format.json { render json: @person }
+ end
+ end
+
+ # GET /people/1/edit
+ def edit
+ @person = Person.find(params[:id])
+ end
+
+ # POST /people
+ # POST /people.json
+ def create
+ @person = Person.new(params[:person])
+
+ respond_to do |format|
+ if @person.save
+ format.html { redirect_to @person, notice: 'Person was successfully created.' }
+ format.json { render json: @person, status: :created, location: @person }
+ else
+ format.html { render action: "new" }
+ format.json { render json: @person.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # PUT /people/1
+ # PUT /people/1.json
+ def update
+ @person = Person.find(params[:id])
+
+ respond_to do |format|
+ if @person.update_attributes(params[:person])
+ format.html { redirect_to @person, notice: 'Person was successfully updated.' }
+ format.json { head :ok }
+ else
+ format.html { render action: "edit" }
+ format.json { render json: @person.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # DELETE /people/1
+ # DELETE /people/1.json
+ def destroy
+ @person = Person.find(params[:id])
+ @person.destroy
+
+ respond_to do |format|
+ format.html { redirect_to people_url }
+ format.json { head :ok }
+ end
+ end
+end
23 spec/dummy/app/controllers/sessions_controller.rb
@@ -0,0 +1,23 @@
+class SessionsController < ApplicationController
+
+ expose(:user) { User.authenticate(params[:session]) }
+
+ def new
+ end
+
+ def create
+ if user
+ @current_user = user
+ session[:user_id] = user.id
+ redirect_to(root_url, notice: "Successfully logged in!")
+ else
+ render :new
+ end
+ end
+
+ def destroy
+ @current_user = session[:user_id] = nil
+ redirect_to(root_url, notice: "Successfully logged out!")
+ end
+
+end
83 spec/dummy/app/controllers/users_controller.rb
@@ -0,0 +1,83 @@
+class UsersController < ApplicationController
+ # GET /users
+ # GET /users.json
+ def index
+ @users = User.all
+
+ respond_to do |format|
+ format.html # index.html.erb
+ format.json { render json: @users }
+ end
+ end
+
+ # GET /users/1
+ # GET /users/1.json
+ def show
+ @user = User.find(params[:id])
+
+ respond_to do |format|
+ format.html # show.html.erb
+ format.json { render json: @user }
+ end
+ end
+
+ # GET /users/new
+ # GET /users/new.json
+ def new
+ @user = User.new
+
+ respond_to do |format|
+ format.html # new.html.erb
+ format.json { render json: @user }
+ end
+ end
+
+ # GET /users/1/edit
+ def edit
+ @user = User.find(params[:id])
+ end
+
+ # POST /users
+ # POST /users.json
+ def create
+ @user = User.new(params[:user])
+
+ respond_to do |format|
+ if @user.save
+ format.html { redirect_to @user, notice: 'User was successfully created.' }
+ format.json { render json: @user, status: :created, location: @user }
+ else
+ format.html { render action: "new" }
+ format.json { render json: @user.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # PUT /users/1
+ # PUT /users/1.json
+ def update
+ @user = User.find(params[:id])
+
+ respond_to do |format|
+ if @user.update_attributes(params[:user])
+ format.html { redirect_to @user, notice: 'User was successfully updated.' }
+ format.json { head :ok }
+ else
+ format.html { render action: "edit" }
+ format.json { render json: @user.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # DELETE /users/1
+ # DELETE /users/1.json
+ def destroy
+ @user = User.find(params[:id])
+ @user.destroy
+
+ respond_to do |format|
+ format.html { redirect_to users_url }
+ format.json { head :ok }
+ end
+ end
+end
2 spec/dummy/app/helpers/application_helper.rb
@@ -0,0 +1,2 @@
+module ApplicationHelper
+end
2 spec/dummy/app/helpers/people_helper.rb
@@ -0,0 +1,2 @@
+module PeopleHelper
+end
2 spec/dummy/app/helpers/sessions_helper.rb
@@ -0,0 +1,2 @@
+module SessionsHelper
+end
2 spec/dummy/app/helpers/users_helper.rb
@@ -0,0 +1,2 @@
+module UsersHelper
+end
0 spec/dummy/app/mailers/.gitkeep
No changes.
0 spec/dummy/app/models/.gitkeep
No changes.
2 spec/dummy/app/models/person.rb
@@ -0,0 +1,2 @@
+class Person < ActiveRecord::Base
+end
11 spec/dummy/app/models/user.rb
@@ -0,0 +1,11 @@
+class User < ActiveRecord::Base
+
+ # this is where the magic happens
+ blogs
+
+ # Find the User by username and password
+ def self.authenticate(attribute_hash)
+ find_by_username_and_password(attribute_hash[:username], attribute_hash[:password])
+ end
+
+end
28 spec/dummy/app/views/layouts/application.html.erb
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Dummy</title>
+ <%= stylesheet_link_tag "blog", "application" %>
+ <%= csrf_meta_tags %>
+</head>
+<body>
+
+ <div id="content">
+
+ <% if current_user %>
+ <%= link_to("log out", main_app.session_path, method: :delete) %>
+ <% else %>
+ <%= link_to("log in", main_app.new_session_path) %>
+ <% end %>
+
+ <h1><%= link_to("My Awesome Blog", root_path) %></h1>
+
+ <%= yield %>
+ </div>
+
+ <footer id="application_footer">
+ By <%= link_to("Gavin Morrice", "http://gavinmorrice.com") %>
+ </footer>
+ <%= javascript_include_tag "blog" %>
+</body>
+</html>
21 spec/dummy/app/views/people/_form.html.erb
@@ -0,0 +1,21 @@
+<%= form_for(@person) do |f| %>
+ <% if @person.errors.any? %>
+ <div id="error_explanation">
+ <h2><%= pluralize(@person.errors.count, "error") %> prohibited this person from being saved:</h2>
+
+ <ul>
+ <% @person.errors.full_messages.each do |msg| %>
+ <li><%= msg %></li>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
+
+ <div class="field">
+ <%= f.label :name %><br />
+ <%= f.text_field :name %>
+ </div>
+ <div class="actions">
+ <%= f.submit %>
+ </div>
+<% end %>
6 spec/dummy/app/views/people/edit.html.erb
@@ -0,0 +1,6 @@
+<h1>Editing person</h1>
+
+<%= render 'form' %>
+
+<%= link_to 'Show', @person %> |
+<%= link_to 'Back', people_path %>
23 spec/dummy/app/views/people/index.html.erb
@@ -0,0 +1,23 @@
+<h1>Listing people</h1>
+
+<table>
+ <tr>
+ <th>Name</th>
+ <th></th>
+ <th></th>
+ <th></th>
+ </tr>
+
+<% @people.each do |person| %>
+ <tr>
+ <td><%= person.name %></td>
+ <td><%= link_to 'Show', person %></td>
+ <td><%= link_to 'Edit', edit_person_path(person) %></td>
+ <td><%= link_to 'Destroy', person, confirm: 'Are you sure?', method: :delete %></td>
+ </tr>
+<% end %>
+</table>
+
+<br />
+
+<%= link_to 'New Person', new_person_path %>
5 spec/dummy/app/views/people/new.html.erb
@@ -0,0 +1,5 @@
+<h1>New person</h1>
+
+<%= render 'form' %>
+
+<%= link_to 'Back', people_path %>
10 spec/dummy/app/views/people/show.html.erb
@@ -0,0 +1,10 @@
+<p id="notice"><%= notice %></p>
+
+<p>
+ <b>Name:</b>
+ <%= @person.name %>
+</p>
+
+
+<%= link_to 'Edit', edit_person_path(@person) %> |
+<%= link_to 'Back', people_path %>
17 spec/dummy/app/views/sessions/new.html.erb
@@ -0,0 +1,17 @@
+<%= form_tag(session_path) do %>
+
+ <div class="field">
+ <%= label(:session, :username) %><br>
+ <%= text_field(:session, :username) %>
+ </div>
+
+ <div class="field">
+ <%= label(:session, :password) %><br>
+ <%= password_field(:session, :password) %>
+ </field>
+
+ <div class="actions">
+ <%= submit_tag("Log in") %>
+ </div>
+
+<% end %>
25 spec/dummy/app/views/users/_form.html.erb
@@ -0,0 +1,25 @@
+<%= form_for(@user) do |f| %>
+ <% if @user.errors.any? %>
+ <div id="error_explanation">
+ <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
+
+ <ul>
+ <% @user.errors.full_messages.each do |msg| %>
+ <li><%= msg %></li>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
+
+ <div class="field">
+ <%= f.label :username %><br />
+ <%= f.text_field :username %>
+ </div>
+ <div class="field">
+ <%= f.label :password %><br />
+ <%= f.text_field :password %>
+ </div>
+ <div class="actions">
+ <%= f.submit %>
+ </div>
+<% end %>
6 spec/dummy/app/views/users/edit.html.erb
@@ -0,0 +1,6 @@
+<h1>Editing user</h1>
+
+<%= render 'form' %>
+
+<%= link_to 'Show', @user %> |
+<%= link_to 'Back', users_path %>
25 spec/dummy/app/views/users/index.html.erb
@@ -0,0 +1,25 @@
+<h1>Listing users</h1>
+
+<table>
+ <tr>
+ <th>Username</th>
+ <th>Password</th>
+ <th></th>
+ <th></th>
+ <th></th>
+ </tr>
+
+<% @users.each do |user| %>
+ <tr>
+ <td><%= user.username %></td>
+ <td><%= user.password %></td>
+ <td><%= link_to 'Show', user %></td>
+ <td><%= link_to 'Edit', edit_user_path(user) %></td>
+ <td><%= link_to 'Destroy', user, confirm: 'Are you sure?', method: :delete %></td>
+ </tr>
+<% end %>
+</table>
+
+<br />
+
+<%= link_to 'New User', new_user_path %>
5 spec/dummy/app/views/users/new.html.erb
@@ -0,0 +1,5 @@
+<h1>New user</h1>
+
+<%= render 'form' %>
+
+<%= link_to 'Back', users_path %>
15 spec/dummy/app/views/users/show.html.erb
@@ -0,0 +1,15 @@
+<p id="notice"><%= notice %></p>
+
+<p>
+ <b>Username:</b>
+ <%= @user.username %>
+</p>
+
+<p>
+ <b>Password:</b>
+ <%= @user.password %>
+</p>
+
+
+<%= link_to 'Edit', edit_user_path(@user) %> |
+<%= link_to 'Back', users_path %>
4 spec/dummy/config.ru
@@ -0,0 +1,4 @@
+# This file is used by Rack-based servers to start the application.
+
+require ::File.expand_path('../config/environment', __FILE__)
+run Dummy::Application
23 spec/dummy/config/application.rb
@@ -0,0 +1,23 @@
+require File.expand_path('../boot', __FILE__)
+
+require 'rails/all'
+
+Bundler.require
+require "blog"
+module Dummy
+ class Application < Rails::Application
+ # Configure the default encoding used in templates for Ruby 1.9.
+ config.encoding = "utf-8"
+
+ # Configure sensitive parameters which will be filtered from the log file.
+ config.filter_parameters += [:password]
+
+ # Version of your assets, change this if you want to expire all your assets
+ config.assets.version = '1.0'
+
+
+ # Enable the asset pipeline
+ config.assets.enabled = true
+ end
+end
+
10 spec/dummy/config/boot.rb
@@ -0,0 +1,10 @@
+require 'rubygems'
+gemfile = File.expand_path('../../../../Gemfile', __FILE__)
+
+if File.exist?(gemfile)
+ ENV['BUNDLE_GEMFILE'] = gemfile
+ require 'bundler'
+ Bundler.setup
+end
+
+$:.unshift File.expand_path('../../../../lib', __FILE__)
25 spec/dummy/config/database.yml
@@ -0,0 +1,25 @@
+# SQLite version 3.x
+# gem install sqlite3
+#
+# Ensure the SQLite 3 gem is defined in your Gemfile
+# gem 'sqlite3'
+development:
+ adapter: sqlite3
+ database: db/development.sqlite3
+ pool: 5
+ timeout: 5000
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ adapter: sqlite3
+ database: db/test.sqlite3
+ pool: 5
+ timeout: 5000
+
+production:
+ adapter: sqlite3
+ database: db/production.sqlite3
+ pool: 5
+ timeout: 5000
5 spec/dummy/config/environment.rb
@@ -0,0 +1,5 @@
+# Load the rails application
+require File.expand_path('../application', __FILE__)
+
+# Initialize the rails application
+Dummy::Application.initialize!
30 spec/dummy/config/environments/development.rb
@@ -0,0 +1,30 @@
+Dummy::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the web server when you make code changes.
+ config.cache_classes = false
+
+ # Log error messages when you accidentally call methods on nil.
+ config.whiny_nils = true
+
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Don't care if the mailer can't send
+ config.action_mailer.raise_delivery_errors = false
+
+ # Print deprecation notices to the Rails logger
+ config.active_support.deprecation = :log
+
+ # Only use best-standards-support built into browsers
+ config.action_dispatch.best_standards_support = :builtin
+
+ # Do not compress assets
+ config.assets.compress = false
+
+ # Expands the lines which load the assets
+ config.assets.debug = true
+end
51 spec/dummy/config/environments/production.rb
@@ -0,0 +1,51 @@
+Dummy::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # Code is not reloaded between requests
+ config.cache_classes = true
+
+ # Full error reports are disabled and caching is turned on
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Disable Rails's static asset server (Apache or nginx will already do this)
+ config.serve_static_assets = false
+
+ # Compress JavaScripts and CSS
+ config.assets.compress = true
+
+ # Specifies the header that your server uses for sending files
+ # (comment out if your front-end server doesn't support this)
+ config.action_dispatch.x_sendfile_header = "X-Sendfile" # Use 'X-Accel-Redirect' for nginx
+
+ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+ # config.force_ssl = true
+
+ # See everything in the log (default is :info)
+ # config.log_level = :debug
+
+ # Use a different logger for distributed setups
+ # config.logger = SyslogLogger.new
+
+ # Use a different cache store in production
+ # config.cache_store = :mem_cache_store
+
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server
+ # config.action_controller.asset_host = "http://assets.example.com"
+
+ # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
+ # config.assets.precompile += %w( search.js )
+
+ # Disable delivery errors, bad email addresses will be ignored
+ # config.action_mailer.raise_delivery_errors = false
+
+ # Enable threaded mode
+ # config.threadsafe!
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation can not be found)
+ config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners
+ config.active_support.deprecation = :notify
+end
39 spec/dummy/config/environments/test.rb
@@ -0,0 +1,39 @@
+Dummy::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
+
+ # Configure static asset server for tests with Cache-Control for performance
+ config.serve_static_assets = true
+ config.static_cache_control = "public, max-age=3600"
+
+ # Log error messages when you accidentally call methods on nil
+ config.whiny_nils = true
+
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Raise exceptions ins