mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-16 15:20:13 +01:00
forgot to add these
This commit is contained in:
parent
86afd42148
commit
fde64e0b3d
145 changed files with 9044 additions and 0 deletions
65
backup.rails2.3/Gemfile.rails2.3
Normal file
65
backup.rails2.3/Gemfile.rails2.3
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
source :gemcutter
|
||||
source "http://gems.github.com/"
|
||||
|
||||
gem "rake", "~>0.8.7"
|
||||
gem "rails", "~>2.3.12"
|
||||
gem "highline", "~>1.5.0"
|
||||
gem "RedCloth", "4.2.8"
|
||||
gem "sanitize", "~>1.2.1"
|
||||
gem "rack", "1.1.0"
|
||||
gem "will_paginate", "~> 2.3.15"
|
||||
gem "has_many_polymorphs", "~> 2.13"
|
||||
gem "acts_as_list", "~>0.1.4"
|
||||
gem "aasm", "~>2.2.0"
|
||||
gem "rubyjedi-actionwebservice", :require => "actionwebservice"
|
||||
gem "rubycas-client", "~>2.2.1"
|
||||
gem "ruby-openid", :require => "openid"
|
||||
|
||||
# you may comment out the database driver you will not be using.
|
||||
# This will prevent a native build of the driver. Building native drivers is not always possible on all hosters
|
||||
gem "sqlite3"
|
||||
gem "mysql"
|
||||
|
||||
gem 'bcrypt-ruby', '~> 2.1.4'
|
||||
gem 'htmlentities', '~> 4.3.0'
|
||||
gem "mail"
|
||||
|
||||
if RUBY_VERSION.to_f >= 1.9
|
||||
gem "soap4r-ruby1.9"
|
||||
else
|
||||
gem "soap4r", "~>1.5.8"
|
||||
end
|
||||
|
||||
group :development do
|
||||
if RUBY_VERSION.to_f >= 1.9
|
||||
gem "ruby-debug19"
|
||||
gem "mongrel", "1.2.0.pre2"
|
||||
else
|
||||
gem "ruby-debug"
|
||||
gem "mongrel"
|
||||
end
|
||||
gem "yard"
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem "test-unit", "1.2.3"
|
||||
gem "flexmock"
|
||||
gem "ZenTest", ">=4.0.0"
|
||||
gem "hpricot"
|
||||
gem "hoe"
|
||||
gem "rspec-rails", "~>1.3.3"
|
||||
gem "thoughtbot-factory_girl"
|
||||
gem 'memory_test_fix', '~>0.1.3'
|
||||
gem "capybara", ">=0.3.5"
|
||||
gem "selenium-webdriver" # Note that > 2.14 has problems: https://code.google.com/p/selenium/issues/detail?id=3075
|
||||
gem "database_cleaner", ">=0.5.0"
|
||||
gem "cucumber-rails", "~>0.3.2"
|
||||
gem "aruba", "0.2.2", :path => "vendor/gems/aruba-0.2.2"
|
||||
|
||||
# uncomment to use the webkit option. This depends on Qt to be installed
|
||||
#gem "capybara-webkit"
|
||||
|
||||
# uncomment to be able to make screenshots from scenarios
|
||||
#gem "capybara-screenshot"
|
||||
#gem "launchy"
|
||||
end
|
||||
39
backup.rails2.3/Gemfile.rails3
Normal file
39
backup.rails2.3/Gemfile.rails3
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rails', '3.2.3'
|
||||
|
||||
# Bundle edge Rails instead:
|
||||
# gem 'rails', :git => 'git://github.com/rails/rails.git'
|
||||
|
||||
gem 'sqlite3'
|
||||
gem 'mysql'
|
||||
|
||||
|
||||
# Gems used only for assets and not required
|
||||
# in production environments by default.
|
||||
group :assets do
|
||||
gem 'sass-rails', '~> 3.2.3'
|
||||
gem 'coffee-rails', '~> 3.2.1'
|
||||
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
# gem 'therubyracer', :platform => :ruby
|
||||
|
||||
gem 'uglifier', '>= 1.0.3'
|
||||
end
|
||||
|
||||
gem 'jquery-rails'
|
||||
|
||||
# To use ActiveModel has_secure_password
|
||||
# gem 'bcrypt-ruby', '~> 3.0.0'
|
||||
|
||||
# To use Jbuilder templates for JSON
|
||||
# gem 'jbuilder'
|
||||
|
||||
# Use unicorn as the app server
|
||||
# gem 'unicorn'
|
||||
|
||||
# Deploy with Capistrano
|
||||
# gem 'capistrano'
|
||||
|
||||
# To use debugger
|
||||
# gem 'ruby-debug19', :require => 'ruby-debug'
|
||||
22
backup.rails2.3/app/apis/todo_api.rb
Normal file
22
backup.rails2.3/app/apis/todo_api.rb
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
class TodoApi < ActionWebService::API::Base
|
||||
api_method :new_todo,
|
||||
:expects => [{:username => :string}, {:token => :string}, {:context_id => :int}, {:description => :string}, {:notes => :string}],
|
||||
:returns => [:int]
|
||||
|
||||
api_method :new_todo_for_project,
|
||||
:expects => [{:username => :string}, {:token => :string}, {:context_id => :int}, {:project_id => :int}, {:description => :string}, {:notes => :string}],
|
||||
:returns => [:int]
|
||||
|
||||
api_method :new_rich_todo,
|
||||
:expects => [{:username => :string}, {:token => :string}, {:default_context_id => :int}, {:description => :string}, {:notes => :string}],
|
||||
:returns => [:int]
|
||||
|
||||
api_method :list_contexts,
|
||||
:expects => [{:username => :string}, {:token => :string}],
|
||||
:returns => [[Context]]
|
||||
|
||||
api_method :list_projects,
|
||||
:expects => [{:username => :string}, {:token => :string}],
|
||||
:returns => [[Project]]
|
||||
|
||||
end
|
||||
301
backup.rails2.3/application_helper.rb.rails2
Normal file
301
backup.rails2.3/application_helper.rb.rails2
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
# The methods added to this helper will be available to all templates in the
|
||||
# application.
|
||||
module ApplicationHelper
|
||||
|
||||
# Replicates the link_to method but also checks request.request_uri to find
|
||||
# current page. If that matches the url, the link is marked id = "current"
|
||||
#
|
||||
def navigation_link(name, options = {}, html_options = nil, *parameters_for_method_reference)
|
||||
if html_options
|
||||
html_options = html_options.stringify_keys
|
||||
convert_options_to_javascript!(html_options)
|
||||
tag_options = tag_options(html_options)
|
||||
else
|
||||
tag_options = nil
|
||||
end
|
||||
url = options.is_a?(String) ? options : self.url_for(options, *parameters_for_method_reference)
|
||||
id_tag = (request.request_uri == url) ? " id=\"current\"" : ""
|
||||
|
||||
"<a href=\"#{url}\"#{tag_options}#{id_tag}>#{name || url}</a>"
|
||||
end
|
||||
|
||||
def days_from_today(date)
|
||||
date.in_time_zone.to_date - current_user.time.to_date
|
||||
end
|
||||
|
||||
# Check due date in comparison to today's date Flag up date appropriately with
|
||||
# a 'traffic light' colour code
|
||||
#
|
||||
def due_date(due)
|
||||
return "" if due.nil?
|
||||
|
||||
days = days_from_today(due)
|
||||
|
||||
colors = ['amber','amber','orange','orange','orange','orange','orange','orange']
|
||||
color = :red if days < 0
|
||||
color = :green if days > 7
|
||||
color = colors[days] if color.nil?
|
||||
|
||||
return content_tag(:a, {:title => format_date(due)}) {
|
||||
content_tag(:span, {:class => color}) {
|
||||
case days
|
||||
when 0
|
||||
t('todos.next_actions_due_date.due_today')
|
||||
when 1
|
||||
t('todos.next_actions_due_date.due_tomorrow')
|
||||
when 2..7
|
||||
if prefs.due_style == Preference.due_styles[:due_on]
|
||||
# TODO: internationalize strftime here
|
||||
t('models.preference.due_on', :date => due.strftime("%A"))
|
||||
else
|
||||
t('models.preference.due_in', :days => days)
|
||||
end
|
||||
else
|
||||
# overdue or due very soon! sound the alarm!
|
||||
if days == -1
|
||||
t('todos.next_actions_due_date.overdue_by', :days => days * -1)
|
||||
elsif days < -1
|
||||
t('todos.next_actions_due_date.overdue_by_plural', :days => days * -1)
|
||||
else
|
||||
# more than a week away - relax
|
||||
t('models.preference.due_in', :days => days)
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
# Check due date in comparison to today's date Flag up date appropriately with
|
||||
# a 'traffic light' colour code Modified method for mobile screen
|
||||
#
|
||||
def due_date_mobile(due)
|
||||
if due == nil
|
||||
return ""
|
||||
end
|
||||
|
||||
days = days_from_today(due)
|
||||
|
||||
case days
|
||||
when 0
|
||||
"<span class=\"amber\">"+ format_date(due) + "</span>"
|
||||
when 1
|
||||
"<span class=\"amber\">" + format_date(due) + "</span>"
|
||||
# due 2-7 days away
|
||||
when 2..7
|
||||
"<span class=\"orange\">" + format_date(due) + "</span>"
|
||||
else
|
||||
# overdue or due very soon! sound the alarm!
|
||||
if days < 0
|
||||
"<span class=\"red\">" + format_date(due) +"</span>"
|
||||
else
|
||||
# more than a week away - relax
|
||||
"<span class=\"green\">" + format_date(due) + "</span>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a count of next actions in the given context or project. The result
|
||||
# is count and a string descriptor, correctly pluralised if there are no
|
||||
# actions or multiple actions
|
||||
#
|
||||
def count_undone_todos_phrase(todos_parent, string="actions")
|
||||
@controller.count_undone_todos_phrase(todos_parent, string)
|
||||
end
|
||||
|
||||
def count_undone_todos_phrase_text(todos_parent, string="actions")
|
||||
count_undone_todos_phrase(todos_parent, string).gsub(" "," ")
|
||||
end
|
||||
|
||||
def count_undone_todos_and_notes_phrase(project, string="actions")
|
||||
s = count_undone_todos_phrase(project, string)
|
||||
s += ", #{pluralize(project.note_count, 'note')}" unless project.note_count == 0
|
||||
s
|
||||
end
|
||||
|
||||
def link_to_context(context, descriptor = sanitize(context.name))
|
||||
link_to( descriptor, context, :title => "View context: #{context.name}" )
|
||||
end
|
||||
|
||||
def link_to_project(project, descriptor = sanitize(project.name))
|
||||
link_to( descriptor, project, :title => "View project: #{project.name}" )
|
||||
end
|
||||
|
||||
def link_to_edit_note (note, descriptor = sanitize(note.id.to_s))
|
||||
link_to(descriptor,
|
||||
url_for({:controller => 'notes', :action => 'edit', :id => note.id}),
|
||||
{:id => "link_edit_#{dom_id(note)}", :class => "note_edit_settings"})
|
||||
end
|
||||
|
||||
def link_to_project_mobile(project, accesskey, descriptor = sanitize(project.name))
|
||||
link_to( descriptor, project_path(project, :format => 'm'), {:title => "View project: #{project.name}", :accesskey => accesskey} )
|
||||
end
|
||||
|
||||
def item_link_to_context(item)
|
||||
descriptor = "[C]"
|
||||
descriptor = "[#{item.context.name}]" if prefs.verbose_action_descriptors
|
||||
link_to_context( item.context, descriptor )
|
||||
end
|
||||
|
||||
def item_link_to_project(item)
|
||||
descriptor = "[P]"
|
||||
descriptor = "[#{item.project.name}]" if prefs.verbose_action_descriptors
|
||||
link_to_project( item.project, descriptor )
|
||||
end
|
||||
|
||||
def render_flash
|
||||
render :partial => 'shared/flash', :object => flash
|
||||
end
|
||||
|
||||
def recurrence_time_span(rt)
|
||||
case rt.ends_on
|
||||
when "no_end_date"
|
||||
return rt.start_from.nil? ? "" : I18n.t("todos.recurrence.pattern.from") + " " + format_date(rt.start_from)
|
||||
when "ends_on_number_of_times"
|
||||
return I18n.t("todos.recurrence.pattern.times", :number => rt.number_of_occurences)
|
||||
when "ends_on_end_date"
|
||||
starts = rt.start_from.nil? ? "" : I18n.t("todos.recurrence.pattern.from") + " " + format_date(rt.start_from)
|
||||
ends = rt.end_date.nil? ? "" : " " + I18n.t("todos.recurrence.pattern.until") + " " + format_date(rt.end_date)
|
||||
return starts+ends
|
||||
else
|
||||
raise Exception.new, "unknown recurrence time span selection (#{rt.ends_on})"
|
||||
end
|
||||
end
|
||||
|
||||
def recurrence_pattern_as_text(recurring_todo)
|
||||
rt = recurring_todo.recurring_target_as_text
|
||||
rp = recurring_todo.recurrence_pattern
|
||||
# only add space if recurrence_pattern has content
|
||||
rp = " " + rp if !rp.nil?
|
||||
rts = recurrence_time_span(recurring_todo)
|
||||
# only add space if recurrence_time_span has content
|
||||
rts = " " + rts if !(rts == "")
|
||||
return rt+rp+rts
|
||||
end
|
||||
|
||||
def date_format_for_date_picker()
|
||||
standard_format = current_user.prefs.date_format
|
||||
translations = [
|
||||
['%m', 'mm'],
|
||||
['%b', 'M'],
|
||||
['%B', 'MM'],
|
||||
['%d', 'dd'],
|
||||
['%a', 'D'],
|
||||
['%A', 'DD'],
|
||||
['%y', 'y'],
|
||||
['%Y', 'yy']
|
||||
]
|
||||
translations.inject(standard_format) do |str, translation|
|
||||
str.gsub(*translation)
|
||||
end
|
||||
end
|
||||
|
||||
AUTO_LINK_MESSAGE_RE = %r{message://<[^>]+>} unless const_defined?(:AUTO_LINK_MESSAGE_RE)
|
||||
|
||||
# Converts message:// links to href. This URL scheme is used on Mac OS X
|
||||
# to link to a mail message in Mail.app.
|
||||
def auto_link_message(text)
|
||||
text.gsub(AUTO_LINK_MESSAGE_RE) do
|
||||
href = $&
|
||||
left, right = $`, $'
|
||||
# detect already linked URLs and URLs in the middle of a tag
|
||||
if left =~ /<[^>]+$/ && right =~ /^[^>]*>/
|
||||
# do not change string; URL is alreay linked
|
||||
href
|
||||
else
|
||||
content = content_tag(:a, h(href), :href => h(href))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def format_note(note)
|
||||
note = auto_link_message(note)
|
||||
note = markdown(note)
|
||||
note = auto_link(note, :link => :urls)
|
||||
|
||||
# add onenote and message protocols
|
||||
Sanitize::Config::RELAXED[:protocols]['a']['href'] << 'onenote'
|
||||
Sanitize::Config::RELAXED[:protocols]['a']['href'] << 'message'
|
||||
|
||||
note = Sanitize.clean(note, Sanitize::Config::RELAXED)
|
||||
return note
|
||||
end
|
||||
|
||||
def sidebar_html_for_titled_list (list, title)
|
||||
return content_tag(:h3, title+" (#{list.length})") +
|
||||
content_tag(:ul, sidebar_html_for_list(list))
|
||||
end
|
||||
|
||||
def sidebar_html_for_list(list)
|
||||
if list.empty?
|
||||
return content_tag(:li, t('sidebar.list_empty'))
|
||||
else
|
||||
return list.inject("") do |html, item|
|
||||
link = (item.class == "Project") ? link_to_project( item ) : link_to_context(item)
|
||||
html << content_tag(:li, link + " (" + count_undone_todos_phrase(item,"actions")+")")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def generate_i18n_strings
|
||||
js = "i18n_locale='#{I18n.locale}';\n"
|
||||
js << "i18n = new Array();\n"
|
||||
%w{
|
||||
shared.toggle_multi shared.toggle_multi_title
|
||||
shared.hide_form shared.hide_action_form_title
|
||||
shared.toggle_single shared.toggle_single_title
|
||||
projects.hide_form projects.hide_form_title
|
||||
projects.show_form projects.show_form_title
|
||||
contexts.hide_form contexts.hide_form_title
|
||||
contexts.show_form contexts.show_form_title
|
||||
contexts.new_context_pre contexts.new_context_post
|
||||
common.cancel common.ok
|
||||
common.ajaxError todos.unresolved_dependency
|
||||
}.each do |s|
|
||||
js << "i18n['#{s}'] = '#{ t(s).gsub(/'/, "\\\\'") }';\n"
|
||||
end
|
||||
return js
|
||||
end
|
||||
|
||||
def javascript_tag_for_i18n_datepicker
|
||||
locale = I18n.locale
|
||||
# do not include en as locale since this the available by default
|
||||
if locale and locale != :en
|
||||
javascript_include_tag("i18n/jquery.ui.datepicker-#{locale}.js")
|
||||
end
|
||||
end
|
||||
|
||||
def determine_done_path
|
||||
case @controller.controller_name
|
||||
when "contexts"
|
||||
done_todos_context_path(@context)
|
||||
when "projects"
|
||||
done_todos_project_path(@project)
|
||||
when "todos"
|
||||
if source_view_is(:tag)
|
||||
done_tag_path(@tag_name)
|
||||
else
|
||||
done_todos_path
|
||||
end
|
||||
else
|
||||
done_todos_path
|
||||
end
|
||||
end
|
||||
|
||||
def determine_all_done_path
|
||||
case @controller.controller_name
|
||||
when "contexts"
|
||||
all_done_todos_context_path(@context)
|
||||
when "projects"
|
||||
all_done_todos_project_path(@project)
|
||||
when "todos"
|
||||
if source_view_is(:tag)
|
||||
all_done_tag_path(@tag_name)
|
||||
else
|
||||
all_done_todos_path
|
||||
end
|
||||
else
|
||||
all_done_todos_path
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
37
backup.rails2.3/database.yml.rails2
Normal file
37
backup.rails2.3/database.yml.rails2
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# MySQL. Versions 4.1 and 5.0 are recommended.
|
||||
#
|
||||
#
|
||||
# Be sure to use new-style password hashing:
|
||||
# http://dev.mysql.com/doc/refman/5.0/en/old-client.html
|
||||
development:
|
||||
adapter: mysql
|
||||
database: tracks_trunk
|
||||
encoding: utf8
|
||||
host: localhost
|
||||
username: tracks
|
||||
password: 32tracks55
|
||||
|
||||
mdevelopment:
|
||||
adapter: sqlite3
|
||||
database: db/tracks-21-test.sqlite3.db
|
||||
|
||||
test: &TEST
|
||||
# adapter: sqlite3
|
||||
# database: ":memory:"
|
||||
# verbosity: quiet
|
||||
adapter: mysql
|
||||
database: tracks_test
|
||||
host: localhost
|
||||
username: tracks
|
||||
password: 32tracks55
|
||||
|
||||
production:
|
||||
adapter: mysql
|
||||
database: tracks_trunk
|
||||
encoding: utf8
|
||||
host: localhost
|
||||
username: tracks
|
||||
password: 32tracks55
|
||||
|
||||
cucumber:
|
||||
<<: *TEST
|
||||
19
backup.rails2.3/development.rb.rails2
Normal file
19
backup.rails2.3/development.rb.rails2
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# 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 webserver 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.action_controller.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
|
||||
|
||||
# Unique cookies
|
||||
config.action_controller.session = { :key => 'TracksDev' }
|
||||
|
||||
NOTIFY_BAR="<div id=\"develop-notify-bar\"> </div>"
|
||||
119
backup.rails2.3/environment.rb.rails2
Normal file
119
backup.rails2.3/environment.rb.rails2
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
# Be sure to restart your webserver when you modify this file.
|
||||
# Uncomment below to force Rails into production mode
|
||||
|
||||
# (Use only when you can't set environment variables through your web/app server)
|
||||
# ENV['RAILS_ENV'] = 'production'
|
||||
|
||||
# Bootstrap the Rails environment, frameworks, and default configuration
|
||||
require File.join(File.dirname(__FILE__), 'boot')
|
||||
|
||||
require 'yaml'
|
||||
SITE_CONFIG = YAML.load_file(File.join(File.dirname(__FILE__), 'site.yml'))
|
||||
|
||||
class Rails::Configuration
|
||||
attr_accessor :action_web_service
|
||||
end
|
||||
|
||||
Encoding.default_external = Encoding::UTF_8 if RUBY_VERSION > "1.9"
|
||||
|
||||
Rails::Initializer.run do |config|
|
||||
# Skip frameworks you're not going to use
|
||||
# config.frameworks -= [ :action_web_service, :action_mailer ]
|
||||
config.autoload_paths += %W( #{RAILS_ROOT}/app/apis )
|
||||
|
||||
config.action_controller.use_accept_header = true
|
||||
|
||||
# Use the database for sessions instead of the file system
|
||||
# (create the session table with 'rake create_sessions_table')
|
||||
config.action_controller.session_store = :active_record_store
|
||||
|
||||
config.action_controller.session = {
|
||||
:key => '_tracks_session_id',
|
||||
:secret => SITE_CONFIG['salt'] * (30.0 / SITE_CONFIG['salt'].length).ceil #must be at least 30 characters
|
||||
}
|
||||
|
||||
config.action_controller.relative_url_root = SITE_CONFIG['subdir'] if SITE_CONFIG['subdir']
|
||||
|
||||
# Enable page/fragment caching by setting a file-based store
|
||||
# (remember to create the caching directory and make it readable to the application)
|
||||
# config.action_controller.fragment_cache_store = :file_store, "#{RAILS_ROOT}/cache"
|
||||
|
||||
# Activate observers that should always be running
|
||||
# config.active_record.observers = :cacher, :garbage_collector
|
||||
|
||||
# Make Active Record use UTC-base instead of local time
|
||||
config.active_record.default_timezone = :utc
|
||||
|
||||
# You''ll probably want to change this to the time zone of the computer where Tracks is running
|
||||
# run rake time:zones:local have Rails suggest time zone names on your system
|
||||
config.time_zone = SITE_CONFIG['time_zone']
|
||||
|
||||
# Use Active Record's schema dumper instead of SQL when creating the test database
|
||||
# (enables use of different database adapters for development and test environments)
|
||||
config.active_record.schema_format = :ruby
|
||||
|
||||
# allow other protocols in urls for sanitzer. Add to your liking, for example
|
||||
# config.action_view.sanitized_allowed_protocols = 'onenote', 'blah', 'proto'
|
||||
# to enable "link":onenote://... or "link":blah://... hyperlinks
|
||||
config.action_view.sanitized_allowed_protocols = 'onenote', 'message'
|
||||
|
||||
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
|
||||
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
||||
# config.i18n.default_locale = :de
|
||||
|
||||
end
|
||||
|
||||
# Add new inflection rules using the following format
|
||||
# (all these examples are active by default):
|
||||
# Inflector.inflections do |inflect|
|
||||
# inflect.plural /^(ox)$/i, '\1en'
|
||||
# inflect.singular /^(ox)en/i, '\1'
|
||||
# inflect.irregular 'person', 'people'
|
||||
# inflect.uncountable %w( fish sheep )
|
||||
# end
|
||||
|
||||
# Include your application configuration below
|
||||
|
||||
|
||||
require 'name_part_finder'
|
||||
require 'tracks/todo_list'
|
||||
require 'tracks/config'
|
||||
require 'tagging_extensions' # Needed for tagging-specific extensions
|
||||
require 'digest/sha1' #Needed to support 'rake db:fixtures:load' on some ruby installs: http://dev.rousette.org.uk/ticket/557
|
||||
|
||||
if ( SITE_CONFIG['authentication_schemes'].include? 'ldap')
|
||||
require 'net/ldap' #requires ruby-net-ldap gem be installed
|
||||
require 'simple_ldap_authenticator'
|
||||
ldap = SITE_CONFIG['ldap']
|
||||
SimpleLdapAuthenticator.ldap_library = ldap['library']
|
||||
SimpleLdapAuthenticator.servers = ldap['servers']
|
||||
SimpleLdapAuthenticator.use_ssl = ldap['ssl']
|
||||
SimpleLdapAuthenticator.login_format = ldap['login_format']
|
||||
end
|
||||
|
||||
if ( SITE_CONFIG['authentication_schemes'].include? 'open_id')
|
||||
#requires ruby-openid gem to be installed
|
||||
OpenID::Util.logger = RAILS_DEFAULT_LOGGER
|
||||
end
|
||||
|
||||
if ( SITE_CONFIG['authentication_schemes'].include? 'cas')
|
||||
#requires rubycas-client gem to be installed
|
||||
if defined? CASClient
|
||||
require 'casclient/frameworks/rails/filter'
|
||||
CASClient::Frameworks::Rails::Filter.configure(
|
||||
:cas_base_url => SITE_CONFIG['cas_server'] ,
|
||||
:cas_server_logout => SITE_CONFIG['cas_server_logout']
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# changed in development.rb to show under_construction bar
|
||||
NOTIFY_BAR = "" unless defined?(NOTIFY_BAR)
|
||||
|
||||
tracks_version='2.2devel'
|
||||
# comment out next two lines if you do not want (or can not) the date of the
|
||||
# last git commit in the footer
|
||||
info=`git log --pretty=format:"%ai" -1`
|
||||
tracks_version=tracks_version + ' ('+info+')'
|
||||
|
||||
TRACKS_VERSION=tracks_version
|
||||
0
backup.rails2.3/lib/assets/.gitkeep
Normal file
0
backup.rails2.3/lib/assets/.gitkeep
Normal file
113
backup.rails2.3/lib/authenticated_test_helper.rb
Normal file
113
backup.rails2.3/lib/authenticated_test_helper.rb
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
module AuthenticatedTestHelper
|
||||
# Sets the current user in the session from the user fixtures.
|
||||
def login_as(user)
|
||||
@request.session['user_id'] = user ? users(user).id : nil
|
||||
end
|
||||
|
||||
def content_type(type)
|
||||
@request.env['Content-Type'] = type
|
||||
end
|
||||
|
||||
def accept(accept)
|
||||
@request.env["HTTP_ACCEPT"] = accept
|
||||
end
|
||||
|
||||
def authorize_as(user)
|
||||
if user
|
||||
@request.env["HTTP_AUTHORIZATION"] = "Basic #{Base64.encode64("#{users(user).login}:test")}"
|
||||
accept 'application/xml'
|
||||
content_type 'application/xml'
|
||||
else
|
||||
@request.env["HTTP_AUTHORIZATION"] = nil
|
||||
accept nil
|
||||
content_type nil
|
||||
end
|
||||
end
|
||||
|
||||
# http://project.ioni.st/post/217#post-217
|
||||
#
|
||||
# def test_new_publication
|
||||
# assert_difference(Publication, :count) do
|
||||
# post :create, :publication => {...}
|
||||
# # ...
|
||||
# end
|
||||
# end
|
||||
#
|
||||
def assert_difference(object, method = nil, difference = 1)
|
||||
initial_value = object.send(method)
|
||||
yield
|
||||
assert_equal initial_value + difference, object.send(method), "#{object}##{method}"
|
||||
end
|
||||
|
||||
def assert_no_difference(object, method, &block)
|
||||
assert_difference object, method, 0, &block
|
||||
end
|
||||
|
||||
# Assert the block redirects to the login
|
||||
#
|
||||
# assert_requires_login(:bob) { |c| c.get :edit, :id => 1 }
|
||||
#
|
||||
def assert_requires_login(login = nil)
|
||||
yield HttpLoginProxy.new(self, login)
|
||||
end
|
||||
|
||||
def assert_http_authentication_required(login = nil)
|
||||
yield XmlLoginProxy.new(self, login)
|
||||
end
|
||||
|
||||
def reset!(*instance_vars)
|
||||
instance_vars = [:controller, :request, :response] unless instance_vars.any?
|
||||
instance_vars.collect! { |v| "@#{v}".to_sym }
|
||||
instance_vars.each do |var|
|
||||
instance_variable_set(var, instance_variable_get(var).class.new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class BaseLoginProxy
|
||||
attr_reader :controller
|
||||
attr_reader :options
|
||||
def initialize(controller, login)
|
||||
@controller = controller
|
||||
@login = login
|
||||
end
|
||||
|
||||
private
|
||||
def authenticated
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def check
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def method_missing(method, *args)
|
||||
@controller.reset!
|
||||
authenticate
|
||||
@controller.send(method, *args)
|
||||
check
|
||||
end
|
||||
end
|
||||
|
||||
class HttpLoginProxy < BaseLoginProxy
|
||||
protected
|
||||
def authenticate
|
||||
@controller.login_as @login if @login
|
||||
end
|
||||
|
||||
def check
|
||||
@controller.assert_redirected_to :controller => 'account', :action => 'login'
|
||||
end
|
||||
end
|
||||
|
||||
class XmlLoginProxy < BaseLoginProxy
|
||||
protected
|
||||
def authenticate
|
||||
@controller.accept 'application/xml'
|
||||
@controller.authorize_as @login if @login
|
||||
end
|
||||
|
||||
def check
|
||||
@controller.assert_response 401
|
||||
end
|
||||
end
|
||||
222
backup.rails2.3/lib/login_system.rb
Normal file
222
backup.rails2.3/lib/login_system.rb
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
require_dependency "user"
|
||||
|
||||
module LoginSystem
|
||||
|
||||
def current_user
|
||||
get_current_user
|
||||
end
|
||||
|
||||
def prefs
|
||||
current_user.prefs unless current_user.nil?
|
||||
end
|
||||
|
||||
# Logout the {#current_user} and redirect to login page
|
||||
#
|
||||
# @param [String] message notification to display
|
||||
def logout_user message=t('login.logged_out')
|
||||
@user.forget_me if logged_in?
|
||||
cookies.delete :auth_token
|
||||
session['user_id'] = nil
|
||||
if ( SITE_CONFIG['authentication_schemes'].include? 'cas') && session[:cas_user]
|
||||
CASClient::Frameworks::Rails::Filter.logout(self)
|
||||
else
|
||||
reset_session
|
||||
notify :notice, message
|
||||
redirect_to_login
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# overwrite this if you want to restrict access to only a few actions
|
||||
# or if you want to check if the user has the correct rights
|
||||
# example:
|
||||
#
|
||||
# # only allow nonbobs
|
||||
# def authorize?(user)
|
||||
# user.login != "bob"
|
||||
# end
|
||||
def authorize?(user)
|
||||
true
|
||||
end
|
||||
|
||||
# overwrite this method if you only want to protect certain actions of the controller
|
||||
# example:
|
||||
#
|
||||
# # don't protect the login and the about method
|
||||
# def protect?(action)
|
||||
# if ['action', 'about'].include?(action)
|
||||
# return false
|
||||
# else
|
||||
# return true
|
||||
# end
|
||||
# end
|
||||
def protect?(action)
|
||||
true
|
||||
end
|
||||
|
||||
# When called with before_filter :login_from_cookie will check for an :auth_token
|
||||
# cookie and log the user back in if appropriate
|
||||
def login_from_cookie
|
||||
return unless cookies[:auth_token] && !logged_in?
|
||||
token = cookies[:auth_token]
|
||||
user = User.where(:remember_token => token)
|
||||
if user && user.remember_token?
|
||||
session['user_id'] = user.id
|
||||
set_current_user(user)
|
||||
current_user.remember_me
|
||||
cookies[:auth_token] = { :value => current_user.remember_token , :expires => current_user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] }
|
||||
flash[:notice] = t('login.successful')
|
||||
end
|
||||
end
|
||||
|
||||
def login_or_feed_token_required
|
||||
if ['rss', 'atom', 'txt', 'ics'].include?(params[:format])
|
||||
if user = User.find_by_token(params[:token])
|
||||
set_current_user(user)
|
||||
return true
|
||||
end
|
||||
end
|
||||
login_required
|
||||
end
|
||||
|
||||
# login_required filter. add
|
||||
#
|
||||
# before_filter :login_required
|
||||
#
|
||||
# if the controller should be under any rights management.
|
||||
# for finer access control you can overwrite
|
||||
#
|
||||
# def authorize?(user)
|
||||
#
|
||||
def login_required
|
||||
|
||||
if not protect?(action_name)
|
||||
return true
|
||||
end
|
||||
|
||||
login_from_cookie
|
||||
|
||||
if session['user_id'] and authorize?(get_current_user)
|
||||
return true
|
||||
end
|
||||
|
||||
http_user, http_pass = get_basic_auth_data
|
||||
if user = User.authenticate(http_user, http_pass)
|
||||
session['user_id'] = user.id
|
||||
set_current_user(user)
|
||||
return true
|
||||
end
|
||||
|
||||
# store current location so that we can
|
||||
# come back after the user logged in
|
||||
store_location unless params[:format] == 'js'
|
||||
|
||||
# call overwriteable reaction to unauthorized access
|
||||
access_denied
|
||||
return false
|
||||
end
|
||||
|
||||
def login_optional
|
||||
|
||||
login_from_cookie
|
||||
|
||||
if session['user_id'] and authorize?(get_current_user)
|
||||
return true
|
||||
end
|
||||
|
||||
http_user, http_pass = get_basic_auth_data
|
||||
if user = User.authenticate(http_user, http_pass)
|
||||
session['user_id'] = user.id
|
||||
set_current_user(user)
|
||||
return true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def logged_in?
|
||||
current_user != nil
|
||||
end
|
||||
|
||||
def get_current_user
|
||||
if @user.nil? && session['user_id']
|
||||
@user = User.find(session['user_id'])
|
||||
end
|
||||
@user
|
||||
end
|
||||
|
||||
def set_current_user(user)
|
||||
@user = user
|
||||
end
|
||||
|
||||
# overwrite if you want to have special behavior in case the user is not authorized
|
||||
# to access the current operation.
|
||||
# the default action is to redirect to the login screen
|
||||
# example use :
|
||||
# a popup window might just close itself for instance
|
||||
def access_denied
|
||||
respond_to do |format|
|
||||
format.html { redirect_to login_path }
|
||||
format.m { redirect_to formatted_login_path(:format => 'm') }
|
||||
format.js { render :partial => 'login/redirect_to_login' }
|
||||
format.xml { basic_auth_denied }
|
||||
format.rss { basic_auth_denied }
|
||||
format.atom { basic_auth_denied }
|
||||
format.text { basic_auth_denied }
|
||||
end
|
||||
end
|
||||
|
||||
# store current uri in the session.
|
||||
# we can return to this location by calling return_location
|
||||
def store_location
|
||||
session['return-to'] = request.request_uri
|
||||
end
|
||||
|
||||
# move to the last store_location call or to the passed default one
|
||||
def redirect_back_or_default(default)
|
||||
if session['return-to'].nil?
|
||||
redirect_to default
|
||||
else
|
||||
redirect_to session['return-to']
|
||||
session['return-to'] = nil
|
||||
end
|
||||
end
|
||||
|
||||
# HTTP Basic auth code adapted from Coda Hale's simple_http_auth plugin. Thanks, Coda!
|
||||
def get_basic_auth_data
|
||||
|
||||
auth_locations = ['REDIRECT_REDIRECT_X_HTTP_AUTHORIZATION',
|
||||
'REDIRECT_X_HTTP_AUTHORIZATION',
|
||||
'X-HTTP_AUTHORIZATION', 'HTTP_AUTHORIZATION']
|
||||
|
||||
authdata = nil
|
||||
for location in auth_locations
|
||||
if request.env.has_key?(location)
|
||||
authdata = request.env[location].to_s.split
|
||||
end
|
||||
end
|
||||
if authdata and authdata[0] == 'Basic'
|
||||
user, pass = Base64.decode64(authdata[1]).split(':')[0..1]
|
||||
else
|
||||
user, pass = ['', '']
|
||||
end
|
||||
return user, pass
|
||||
end
|
||||
|
||||
def basic_auth_denied
|
||||
response.headers["WWW-Authenticate"] = "Basic realm=\"'Tracks Login Required'\""
|
||||
render :text => t('login.unsuccessful'), :status => 401
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Redirect the user to the login page.
|
||||
def redirect_to_login
|
||||
respond_to do |format|
|
||||
format.html { redirect_to login_path }
|
||||
format.m { redirect_to login_path(:format => 'm') }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
5
backup.rails2.3/lib/name_part_finder.rb
Normal file
5
backup.rails2.3/lib/name_part_finder.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
module NamePartFinder
|
||||
def find_by_namepart(namepart)
|
||||
find_by_name(namepart) || find(:first, :conditions => ["name LIKE ?", namepart + '%'])
|
||||
end
|
||||
end
|
||||
200
backup.rails2.3/lib/tagging_extensions.rb
Normal file
200
backup.rails2.3/lib/tagging_extensions.rb
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
class ActiveRecord::Base #:nodoc:
|
||||
|
||||
# These extensions make models taggable. This file is automatically generated and required by your app if you run the tagging generator included with has_many_polymorphs.
|
||||
module TaggingExtensions
|
||||
|
||||
# Add tags to <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
#
|
||||
# We need to avoid name conflicts with the built-in ActiveRecord association methods, thus the underscores.
|
||||
def _add_tags incoming
|
||||
taggable?(true)
|
||||
tag_cast_to_string(incoming).each do |tag_name|
|
||||
# added following check to prevent empty tags from being saved (which will fail)
|
||||
unless tag_name.blank?
|
||||
begin
|
||||
tag = Tag.find_or_create_by_name(tag_name)
|
||||
raise Tag::Error, "tag could not be saved: #{tag_name}" if tag.new_record?
|
||||
tags << tag
|
||||
rescue ActiveRecord::StatementInvalid => e
|
||||
raise unless e.to_s =~ /duplicate/i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Removes tags from <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
def _remove_tags outgoing
|
||||
taggable?(true)
|
||||
outgoing = tag_cast_to_string(outgoing)
|
||||
tags.delete(*(tags.select do |tag|
|
||||
outgoing.include? tag.name
|
||||
end))
|
||||
end
|
||||
|
||||
# Returns the tags on <tt>self</tt> as a string.
|
||||
def tag_list
|
||||
# Redefined later to avoid an RDoc parse error.
|
||||
end
|
||||
|
||||
# Replace the existing tags on <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
def tag_with list
|
||||
#:stopdoc:
|
||||
taggable?(true)
|
||||
list = tag_cast_to_string(list)
|
||||
|
||||
# Transactions may not be ideal for you here; be aware.
|
||||
Tag.transaction do
|
||||
current = tags.map(&:name)
|
||||
_add_tags(list - current)
|
||||
_remove_tags(current - list)
|
||||
end
|
||||
|
||||
self
|
||||
#:startdoc:
|
||||
end
|
||||
|
||||
# Returns the tags on <tt>self</tt> as a string.
|
||||
def tag_list #:nodoc:
|
||||
#:stopdoc:
|
||||
taggable?(true)
|
||||
tags.reload
|
||||
tags.to_s
|
||||
#:startdoc:
|
||||
end
|
||||
|
||||
def tag_list=(value)
|
||||
tag_with(value)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tag_cast_to_string obj #:nodoc:
|
||||
case obj
|
||||
when Array
|
||||
obj.map! do |item|
|
||||
case item
|
||||
# removed next line as it prevents using numbers as tags
|
||||
# when /^\d+$/, Fixnum then Tag.find(item).name # This will be slow if you use ids a lot.
|
||||
when Tag then item.name
|
||||
when String then item
|
||||
else
|
||||
raise "Invalid type"
|
||||
end
|
||||
end
|
||||
when String
|
||||
obj = obj.split(Tag::DELIMITER).map do |tag_name|
|
||||
tag_name.strip.squeeze(" ")
|
||||
end
|
||||
else
|
||||
raise "Invalid object of class #{obj.class} as tagging method parameter"
|
||||
end.flatten.compact.map(&:downcase).uniq
|
||||
end
|
||||
|
||||
# Check if a model is in the :taggables target list. The alternative to this check is to explicitly include a TaggingMethods module (which you would create) in each target model.
|
||||
def taggable?(should_raise = false) #:nodoc:
|
||||
unless flag = respond_to?(:tags)
|
||||
raise "#{self.class} is not a taggable model" if should_raise
|
||||
end
|
||||
flag
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module TaggingFinders
|
||||
# Find all the objects tagged with the supplied list of tags
|
||||
#
|
||||
# Usage : Model.tagged_with("ruby")
|
||||
# Model.tagged_with("hello", "world")
|
||||
# Model.tagged_with("hello", "world", :limit => 10)
|
||||
#
|
||||
# XXX This query strategy is not performant, and needs to be rewritten as an inverted join or a series of unions
|
||||
#
|
||||
def tagged_with(*tag_list)
|
||||
options = tag_list.last.is_a?(Hash) ? tag_list.pop : {}
|
||||
tag_list = parse_tags(tag_list)
|
||||
|
||||
scope = scope(:find)
|
||||
options[:select] ||= "#{table_name}.*"
|
||||
options[:from] ||= "#{table_name}, tags, taggings"
|
||||
|
||||
sql = "SELECT #{(scope && scope[:select]) || options[:select]} "
|
||||
sql << "FROM #{(scope && scope[:from]) || options[:from]} "
|
||||
|
||||
add_joins!(sql, options[:joins], scope)
|
||||
|
||||
sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
|
||||
sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
|
||||
sql << "AND taggings.tag_id = tags.id "
|
||||
|
||||
tag_list_condition = tag_list.map {|name| "'#{name}'"}.join(", ")
|
||||
|
||||
sql << "AND (tags.name IN (#{sanitize_sql(tag_list_condition)})) "
|
||||
sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
|
||||
|
||||
columns = column_names.map do |column|
|
||||
"#{table_name}.#{column}"
|
||||
end.join(", ")
|
||||
|
||||
sql << "GROUP BY #{columns} "
|
||||
sql << "HAVING COUNT(taggings.tag_id) = #{tag_list.size}"
|
||||
|
||||
add_order!(sql, options[:order], scope)
|
||||
add_limit!(sql, options, scope)
|
||||
add_lock!(sql, options, scope)
|
||||
|
||||
find_by_sql(sql)
|
||||
end
|
||||
|
||||
def self.tagged_with_any(*tag_list)
|
||||
options = tag_list.last.is_a?(Hash) ? tag_list.pop : {}
|
||||
tag_list = parse_tags(tag_list)
|
||||
|
||||
scope = scope(:find)
|
||||
options[:select] ||= "#{table_name}.*"
|
||||
options[:from] ||= "#{table_name}, meta_tags, taggings"
|
||||
|
||||
sql = "SELECT #{(scope && scope[:select]) || options[:select]} "
|
||||
sql << "FROM #{(scope && scope[:from]) || options[:from]} "
|
||||
|
||||
add_joins!(sql, options, scope)
|
||||
|
||||
sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
|
||||
sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
|
||||
sql << "AND taggings.meta_tag_id = meta_tags.id "
|
||||
|
||||
sql << "AND ("
|
||||
or_options = []
|
||||
tag_list.each do |name|
|
||||
or_options << "meta_tags.name = '#{name}'"
|
||||
end
|
||||
or_options_joined = or_options.join(" OR ")
|
||||
sql << "#{or_options_joined}) "
|
||||
|
||||
|
||||
sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
|
||||
|
||||
columns = column_names.map do |column|
|
||||
"#{table_name}.#{column}"
|
||||
end.join(", ")
|
||||
|
||||
sql << "GROUP BY #{columns} "
|
||||
|
||||
add_order!(sql, options[:order], scope)
|
||||
add_limit!(sql, options, scope)
|
||||
add_lock!(sql, options, scope)
|
||||
|
||||
find_by_sql(sql)
|
||||
end
|
||||
|
||||
def parse_tags(tags)
|
||||
return [] if tags.blank?
|
||||
tags = Array(tags).first
|
||||
tags = tags.respond_to?(:flatten) ? tags.flatten : tags.split(Tag::DELIMITER)
|
||||
tags.map { |tag| tag.strip.squeeze(" ") }.flatten.compact.map(&:downcase).uniq
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
include TaggingExtensions
|
||||
extend TaggingFinders
|
||||
end
|
||||
0
backup.rails2.3/lib/tasks/.gitkeep
Normal file
0
backup.rails2.3/lib/tasks/.gitkeep
Normal file
38
backup.rails2.3/lib/tasks/cucumber-tracks.rake
Normal file
38
backup.rails2.3/lib/tasks/cucumber-tracks.rake
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
|
||||
# It is recommended to regenerate this file in the future when you upgrade to a
|
||||
# newer version of cucumber-rails. Consider adding your own code to a new file
|
||||
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
|
||||
# files.
|
||||
|
||||
vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
|
||||
$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
|
||||
|
||||
begin
|
||||
require 'cucumber/rake/task'
|
||||
|
||||
namespace :cucumber do
|
||||
Cucumber::Rake::Task.new({:selenium => :env_to_selenium}, 'Run features that require selenium') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'selenium'
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new({:selenium_wip => :env_to_selenium}, 'Run unfinished features that require selenium') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'selenium_wip'
|
||||
end
|
||||
|
||||
task :env_to_selenium => 'db:test:prepare' do
|
||||
ENV['RAILS_ENV'] = 'selenium'
|
||||
end
|
||||
|
||||
desc 'Run all features'
|
||||
task :all => [:ok, :wip, :selenium, :selenium_wip]
|
||||
end
|
||||
rescue LoadError
|
||||
desc 'cucumber rake task not available (cucumber not installed)'
|
||||
task :cucumber do
|
||||
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
|
||||
end
|
||||
end
|
||||
53
backup.rails2.3/lib/tasks/cucumber.rake
Normal file
53
backup.rails2.3/lib/tasks/cucumber.rake
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
|
||||
# It is recommended to regenerate this file in the future when you upgrade to a
|
||||
# newer version of cucumber-rails. Consider adding your own code to a new file
|
||||
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
|
||||
# files.
|
||||
|
||||
|
||||
unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks
|
||||
|
||||
vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
|
||||
$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
|
||||
|
||||
begin
|
||||
require 'cucumber/rake/task'
|
||||
|
||||
namespace :cucumber do
|
||||
Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
|
||||
t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'default'
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'wip'
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'rerun'
|
||||
end
|
||||
|
||||
desc 'Run all features'
|
||||
task :all => [:ok, :wip]
|
||||
end
|
||||
desc 'Alias for cucumber:ok'
|
||||
task :cucumber => 'cucumber:ok'
|
||||
|
||||
task :default => :cucumber
|
||||
|
||||
task :features => :cucumber do
|
||||
STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
|
||||
end
|
||||
rescue LoadError
|
||||
desc 'cucumber rake task not available (cucumber not installed)'
|
||||
task :cucumber do
|
||||
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
27
backup.rails2.3/lib/tasks/database.rake
Normal file
27
backup.rails2.3/lib/tasks/database.rake
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
require 'rake'
|
||||
|
||||
namespace :db do
|
||||
desc "Dump the current SQLite3 or MySQL database to a sql file"
|
||||
task :dump_sql do
|
||||
load 'config/environment.rb'
|
||||
abcs = ActiveRecord::Base.configurations
|
||||
case abcs[RAILS_ENV]["adapter"]
|
||||
when 'mysql'
|
||||
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
|
||||
File.open("db/#{RAILS_ENV}_data.sql", "w+") do |f|
|
||||
if abcs[RAILS_ENV]["password"].blank?
|
||||
f << `mysqldump -h #{abcs[RAILS_ENV]["host"]} -u #{abcs[RAILS_ENV]["username"]} #{abcs[RAILS_ENV]["database"]}`
|
||||
else
|
||||
f << `mysqldump -h #{abcs[RAILS_ENV]["host"]} -u #{abcs[RAILS_ENV]["username"]} -p#{abcs[RAILS_ENV]["password"]} #{abcs[RAILS_ENV]["database"]}`
|
||||
end
|
||||
end
|
||||
when 'sqlite3'
|
||||
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
|
||||
File.open("db/#{RAILS_ENV}_data.sql", "w+") do |f|
|
||||
f << `sqlite3 #{abcs[RAILS_ENV]["database"]} .dump`
|
||||
end
|
||||
else
|
||||
raise "Task not supported by '#{abcs[RAILS_ENV]['adapter']}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
17
backup.rails2.3/lib/tasks/extract_fixtures.rake
Normal file
17
backup.rails2.3/lib/tasks/extract_fixtures.rake
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
desc ' Create YAML test fixtures from data in an existing database.
|
||||
Defaults to development database. Set RAILS_ENV to override (taken from Rails Recipes book).'
|
||||
task :extract_fixtures => :environment do
|
||||
sql = "SELECT * FROM %s"
|
||||
skip_tables = ["schema_info", "sessions", "users"]
|
||||
ActiveRecord::Base.establish_connection
|
||||
(ActiveRecord::Base.connection.tables - skip_tables).each do |table_name|
|
||||
i = "000"
|
||||
File.open("#{RAILS_ROOT}/db/exported_fixtures/#{table_name}.yml", 'w' ) do |file|
|
||||
data = ActiveRecord::Base.connection.select_all(sql % table_name)
|
||||
file.write data.inject({}) { |hash, record|
|
||||
hash["#{table_name}_#{i.succ!}"] = record
|
||||
hash
|
||||
}.to_yaml
|
||||
end
|
||||
end
|
||||
end
|
||||
34
backup.rails2.3/lib/tasks/gems.rake
Normal file
34
backup.rails2.3/lib/tasks/gems.rake
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
desc "Copy third-party gems into ./lib"
|
||||
task :freeze_other_gems do
|
||||
# TODO Get this list from parsing environment.rb
|
||||
libraries = %w(redcloth)
|
||||
require 'rubygems'
|
||||
require 'find'
|
||||
|
||||
libraries.each do |library|
|
||||
library_gem = Gem.cache.search(library).sort_by { |g| g.version }.last
|
||||
puts "Freezing #{library} for #{library_gem.version}..."
|
||||
|
||||
# TODO Add dependencies to list of libraries to freeze
|
||||
#library_gem.dependencies.each { |g| libraries << g }
|
||||
|
||||
folder_for_library = "#{library_gem.name}-#{library_gem.version}"
|
||||
system "cd vendor; gem unpack -v '#{library_gem.version}' #{library_gem.name};"
|
||||
|
||||
# Copy files recursively to ./lib
|
||||
folder_for_library_with_lib = "vendor/#{folder_for_library}/lib/"
|
||||
Find.find(folder_for_library_with_lib) do |original_file|
|
||||
destination_file = "./lib/" + original_file.gsub(folder_for_library_with_lib, '')
|
||||
|
||||
if File.directory?(original_file)
|
||||
if !File.exist?(destination_file)
|
||||
Dir.mkdir destination_file
|
||||
end
|
||||
else
|
||||
File.copy original_file, destination_file
|
||||
end
|
||||
end
|
||||
|
||||
system "rm -r vendor/#{folder_for_library}"
|
||||
end
|
||||
end
|
||||
8
backup.rails2.3/lib/tasks/load_exported_fixtures.rake
Normal file
8
backup.rails2.3/lib/tasks/load_exported_fixtures.rake
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
desc "Load exported fixtures (in db/exported_fixtures) into the current environment's database"
|
||||
task :load_exported_fixtures => :environment do
|
||||
require 'active_record/fixtures'
|
||||
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
|
||||
Dir.glob(File.join(RAILS_ROOT, 'db', 'exported_fixtures', '*.{yml,csv}')).each do |fixture_file|
|
||||
Fixtures.create_fixtures('db/exported_fixtures', File.basename(fixture_file, '.*'))
|
||||
end
|
||||
end
|
||||
50
backup.rails2.3/lib/tasks/query_trace_toggle.rake
Normal file
50
backup.rails2.3/lib/tasks/query_trace_toggle.rake
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
namespace :query_trace do
|
||||
desc "Enables the query_trace plugin. Must restart server to take effect."
|
||||
task :on => :environment do
|
||||
unless File.exist?("#{RAILS_ROOT}/vendor/query_trace.tar.gz")
|
||||
Dir.chdir("#{RAILS_ROOT}/vendor") do
|
||||
url = "https://terralien.devguard.com/svn/projects/plugins/query_trace"
|
||||
puts "Loading query_trace from #{url}..."
|
||||
system "svn co #{url} query_trace"
|
||||
system "tar zcf query_trace.tar.gz --exclude=.svn query_trace"
|
||||
FileUtils.rm_rf("query_trace")
|
||||
end
|
||||
end
|
||||
Dir.chdir("#{RAILS_ROOT}/vendor/plugins") do
|
||||
system "tar zxf ../query_trace.tar.gz query_trace"
|
||||
end
|
||||
puts "QueryTrace plugin enabled. Must restart server to take effect."
|
||||
end
|
||||
|
||||
desc "Disables the query_trace plugin. Must restart server to take effect."
|
||||
task :off => :environment do
|
||||
FileUtils.rm_rf("#{RAILS_ROOT}/vendor/plugins/query_trace")
|
||||
puts "QueryTrace plugin disabled. Must restart server to take effect."
|
||||
end
|
||||
end
|
||||
|
||||
namespace :query_analyzer do
|
||||
desc "Enables the query_analyzer plugin. Must restart server to take effect."
|
||||
task :on => :environment do
|
||||
unless File.exist?("#{RAILS_ROOT}/vendor/query_analyzer.tar.gz")
|
||||
Dir.chdir("#{RAILS_ROOT}/vendor") do
|
||||
url = "http://svn.nfectio.us/plugins/query_analyzer"
|
||||
puts "Loading query_analyzer from #{url}..."
|
||||
system "svn co #{url} query_analyzer"
|
||||
system "tar zcf query_analyzer.tar.gz --exclude=.svn query_analyzer"
|
||||
FileUtils.rm_rf("query_analyzer")
|
||||
end
|
||||
end
|
||||
Dir.chdir("#{RAILS_ROOT}/vendor/plugins") do
|
||||
system "tar zxf ../query_analyzer.tar.gz query_analyzer"
|
||||
end
|
||||
puts "QueryAnalyzer plugin enabled. Must restart server to take effect."
|
||||
end
|
||||
|
||||
desc "Disables the query_analyzer plugin. Must restart server to take effect."
|
||||
task :off => :environment do
|
||||
FileUtils.rm_rf("#{RAILS_ROOT}/vendor/plugins/query_analyzer")
|
||||
puts "QueryAnalyzer plugin disabled. Must restart server to take effect."
|
||||
end
|
||||
end
|
||||
|
||||
23
backup.rails2.3/lib/tasks/reset_password.rake
Normal file
23
backup.rails2.3/lib/tasks/reset_password.rake
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
namespace :tracks do
|
||||
desc 'Replace the password of USER with a new one.'
|
||||
task :password => :environment do
|
||||
require "highline/import"
|
||||
|
||||
user = User.find_by_login(ENV['USER'])
|
||||
if user.nil?
|
||||
puts "Sorry, we couldn't find user '#{ENV['USER']}'. To specify a different user, pass USER=username to this task."
|
||||
exit 0
|
||||
end
|
||||
|
||||
puts "Changing Tracks password for #{ENV['USER']}."
|
||||
password = ask("New password: ") { |q| q.echo = false }
|
||||
password_confirmation = ask('Retype new password: ') { |q| q.echo = false }
|
||||
|
||||
begin
|
||||
user.change_password(password, password_confirmation)
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
puts "Sorry, we couldn't change #{ENV['USER']}'s password: "
|
||||
user.errors.each_full { |msg| puts "- #{msg}\n" }
|
||||
end
|
||||
end
|
||||
end
|
||||
144
backup.rails2.3/lib/tasks/rspec.rake
Normal file
144
backup.rails2.3/lib/tasks/rspec.rake
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
gem 'test-unit', '1.2.3' if RUBY_VERSION.to_f >= 1.9
|
||||
rspec_gem_dir = nil
|
||||
Dir["#{RAILS_ROOT}/vendor/gems/*"].each do |subdir|
|
||||
rspec_gem_dir = subdir if subdir.gsub("#{RAILS_ROOT}/vendor/gems/","") =~ /^(\w+-)?rspec-(\d+)/ && File.exist?("#{subdir}/lib/spec/rake/spectask.rb")
|
||||
end
|
||||
rspec_plugin_dir = File.expand_path(File.dirname(__FILE__) + '/../../vendor/plugins/rspec')
|
||||
|
||||
if rspec_gem_dir && (test ?d, rspec_plugin_dir)
|
||||
raise "\n#{'*'*50}\nYou have rspec installed in both vendor/gems and vendor/plugins\nPlease pick one and dispose of the other.\n#{'*'*50}\n\n"
|
||||
end
|
||||
|
||||
if rspec_gem_dir
|
||||
$LOAD_PATH.unshift("#{rspec_gem_dir}/lib")
|
||||
elsif File.exist?(rspec_plugin_dir)
|
||||
$LOAD_PATH.unshift("#{rspec_plugin_dir}/lib")
|
||||
end
|
||||
|
||||
# Don't load rspec if running "rake gems:*"
|
||||
unless ARGV.any? {|a| a =~ /^gems/}
|
||||
|
||||
begin
|
||||
require 'spec/rake/spectask'
|
||||
rescue MissingSourceFile
|
||||
module Spec
|
||||
module Rake
|
||||
class SpecTask
|
||||
def initialize(name)
|
||||
task name do
|
||||
# if rspec-rails is a configured gem, this will output helpful material and exit ...
|
||||
require File.expand_path(File.join(File.dirname(__FILE__),"..","..","config","environment"))
|
||||
|
||||
# ... otherwise, do this:
|
||||
raise <<-MSG
|
||||
|
||||
#{"*" * 80}
|
||||
* You are trying to run an rspec rake task defined in
|
||||
* #{__FILE__},
|
||||
* but rspec can not be found in vendor/gems, vendor/plugins or system gems.
|
||||
#{"*" * 80}
|
||||
MSG
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Rake.application.instance_variable_get('@tasks').delete('default')
|
||||
|
||||
spec_prereq = File.exist?(File.join(RAILS_ROOT, 'config', 'database.yml')) ? "db:test:prepare" : :noop
|
||||
task :noop do
|
||||
end
|
||||
|
||||
task :default => :spec
|
||||
task :stats => "spec:statsetup"
|
||||
|
||||
desc "Run all specs in spec directory (excluding plugin specs)"
|
||||
Spec::Rake::SpecTask.new(:spec => spec_prereq) do |t|
|
||||
t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
|
||||
t.spec_files = FileList['spec/**/*_spec.rb']
|
||||
end
|
||||
|
||||
namespace :spec do
|
||||
desc "Run all specs in spec directory with RCov (excluding plugin specs)"
|
||||
Spec::Rake::SpecTask.new(:rcov) do |t|
|
||||
t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
|
||||
t.spec_files = FileList['spec/**/*_spec.rb']
|
||||
t.rcov = true
|
||||
t.rcov_opts = lambda do
|
||||
IO.readlines("#{RAILS_ROOT}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
|
||||
end
|
||||
end
|
||||
|
||||
desc "Print Specdoc for all specs (excluding plugin specs)"
|
||||
Spec::Rake::SpecTask.new(:doc) do |t|
|
||||
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
||||
t.spec_files = FileList['spec/**/*_spec.rb']
|
||||
end
|
||||
|
||||
desc "Print Specdoc for all plugin examples"
|
||||
Spec::Rake::SpecTask.new(:plugin_doc) do |t|
|
||||
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
||||
t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*')
|
||||
end
|
||||
|
||||
[:models, :controllers, :views, :helpers, :lib, :integration].each do |sub|
|
||||
desc "Run the code examples in spec/#{sub}"
|
||||
Spec::Rake::SpecTask.new(sub => spec_prereq) do |t|
|
||||
t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
|
||||
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
|
||||
end
|
||||
end
|
||||
|
||||
desc "Run the code examples in vendor/plugins (except RSpec's own)"
|
||||
Spec::Rake::SpecTask.new(:plugins => spec_prereq) do |t|
|
||||
t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
|
||||
t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*').exclude("vendor/plugins/rspec-rails/*")
|
||||
end
|
||||
|
||||
namespace :plugins do
|
||||
desc "Runs the examples for rspec_on_rails"
|
||||
Spec::Rake::SpecTask.new(:rspec_on_rails) do |t|
|
||||
t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
|
||||
t.spec_files = FileList['vendor/plugins/rspec-rails/spec/**/*_spec.rb']
|
||||
end
|
||||
end
|
||||
|
||||
# Setup specs for stats
|
||||
task :statsetup do
|
||||
require 'code_statistics'
|
||||
::STATS_DIRECTORIES << %w(Model\ specs spec/models) if File.exist?('spec/models')
|
||||
::STATS_DIRECTORIES << %w(View\ specs spec/views) if File.exist?('spec/views')
|
||||
::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers) if File.exist?('spec/controllers')
|
||||
::STATS_DIRECTORIES << %w(Helper\ specs spec/helpers) if File.exist?('spec/helpers')
|
||||
::STATS_DIRECTORIES << %w(Library\ specs spec/lib) if File.exist?('spec/lib')
|
||||
::STATS_DIRECTORIES << %w(Routing\ specs spec/routing) if File.exist?('spec/routing')
|
||||
::STATS_DIRECTORIES << %w(Integration\ specs spec/integration) if File.exist?('spec/integration')
|
||||
::CodeStatistics::TEST_TYPES << "Model specs" if File.exist?('spec/models')
|
||||
::CodeStatistics::TEST_TYPES << "View specs" if File.exist?('spec/views')
|
||||
::CodeStatistics::TEST_TYPES << "Controller specs" if File.exist?('spec/controllers')
|
||||
::CodeStatistics::TEST_TYPES << "Helper specs" if File.exist?('spec/helpers')
|
||||
::CodeStatistics::TEST_TYPES << "Library specs" if File.exist?('spec/lib')
|
||||
::CodeStatistics::TEST_TYPES << "Routing specs" if File.exist?('spec/routing')
|
||||
::CodeStatistics::TEST_TYPES << "Integration specs" if File.exist?('spec/integration')
|
||||
end
|
||||
|
||||
namespace :db do
|
||||
namespace :fixtures do
|
||||
desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z."
|
||||
task :load => :environment do
|
||||
ActiveRecord::Base.establish_connection(Rails.env)
|
||||
base_dir = File.join(Rails.root, 'spec', 'fixtures')
|
||||
fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir
|
||||
|
||||
require 'active_record/fixtures'
|
||||
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file|
|
||||
Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
15
backup.rails2.3/lib/tasks/setup_tracks.rake
Normal file
15
backup.rails2.3/lib/tasks/setup_tracks.rake
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
desc "Initialises the installation, copy the *.tmpl files and directories to versions named without the .tmpl extension. It won't overwrite the files and directories if you've already copied them. You need to manually copy database.yml.tmpl -> database.yml and fill in the details before you run this task."
|
||||
task :setup_tracks => :environment do
|
||||
# Check the root directory for template files
|
||||
FileList["*.tmpl"].each do |template_file|
|
||||
f = File.basename(template_file) # with suffix
|
||||
f_only = File.basename(template_file,".tmpl") # without suffix
|
||||
if File.exists?(f_only)
|
||||
puts f_only + " already exists"
|
||||
else
|
||||
cp_r(f, f_only)
|
||||
puts f_only + " created"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
40
backup.rails2.3/lib/tasks/upgrade_sqlite_db.rake
Normal file
40
backup.rails2.3/lib/tasks/upgrade_sqlite_db.rake
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
desc "Updates sqlite/sqlite3 databases created under Tracks 1.03 to the format required for Tracks 1.04. After this is done, you should be able to keep up to date with changes in the schema by running rake db:migrate."
|
||||
task :upgrade_sqlite_db => :environment do
|
||||
# Change the three lines below appropriately for your setup
|
||||
old_db = "tracks_103.db"
|
||||
new_db = "tracks_104.db"
|
||||
cmd = "sqlite3"
|
||||
replace_string = "update todos set done='f' where done=0;\nupdate todos set done='t' where done=1;\nupdate contexts set hide='f' where hide=0;\nupdate contexts set hide='t' where hide=1;\nupdate projects set done='f' where done=0;\nupdate projects set done='t' where done=1;\nCREATE TABLE 'schema_info' (\n 'version' INTEGER default NULL\n);\nINSERT INTO \"schema_info\" VALUES(1);\nCOMMIT;"
|
||||
|
||||
# cd to the db directory
|
||||
cd("db") do
|
||||
# Dump the old db into the temp file and replace the tinyints with booleans
|
||||
`#{cmd} #{old_db} .dump | sed "s/tinyint(4) NOT NULL default '0'/boolean default 'f'/" > temp.sql`
|
||||
# Create a second sqldump file for writing
|
||||
sqldump = File.open("temp2.sql", "w+")
|
||||
File.open("temp.sql") do |file|
|
||||
file.each_line do |line|
|
||||
# If COMMIT is on the line, insert the replace string
|
||||
# else just write the line back in
|
||||
# This effectively replaces COMMIT with the replace string
|
||||
if /COMMIT/ =~ line
|
||||
sqldump.write replace_string
|
||||
else
|
||||
sqldump.write line
|
||||
end
|
||||
end
|
||||
sqldump.close
|
||||
end
|
||||
|
||||
# Read the second dump back in to a new db
|
||||
system "#{cmd} #{new_db} < temp2.sql"
|
||||
puts "Created the a new database called #{new_db}."
|
||||
# Clean up the temp files
|
||||
rm("temp.sql")
|
||||
rm("temp2.sql")
|
||||
puts "Temporary files cleaned up."
|
||||
end
|
||||
|
||||
# rake db:migrate
|
||||
puts "Now check the database and run 'rake db:migrate' in the root of your Tracks installation."
|
||||
end
|
||||
27
backup.rails2.3/lib/tracks/config.rb
Normal file
27
backup.rails2.3/lib/tracks/config.rb
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
module Tracks
|
||||
class Config
|
||||
def self.salt
|
||||
SITE_CONFIG['salt']
|
||||
end
|
||||
|
||||
def self.auth_schemes
|
||||
SITE_CONFIG['authentication_schemes'] || []
|
||||
end
|
||||
|
||||
def self.openid_enabled?
|
||||
auth_schemes.include?('open_id')
|
||||
end
|
||||
|
||||
def self.cas_enabled?
|
||||
auth_schemes.include?('cas')
|
||||
end
|
||||
|
||||
def self.prefered_auth?
|
||||
if SITE_CONFIG['prefered_auth']
|
||||
SITE_CONFIG['prefered_auth']
|
||||
else
|
||||
auth_schemes.first
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
68
backup.rails2.3/lib/tracks/source_view.rb
Normal file
68
backup.rails2.3/lib/tracks/source_view.rb
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# Inspiration from Bruce Williams [http://codefluency.com/articles/2006/07/01/rails-views-getting-in-context/]
|
||||
module Tracks
|
||||
|
||||
module SourceViewSwitching
|
||||
|
||||
class Responder
|
||||
|
||||
def initialize(source_view)
|
||||
@source_view = source_view.underscore.gsub(/\s+/,'_').to_sym rescue nil
|
||||
end
|
||||
|
||||
def nil?
|
||||
yield if @source_view.nil? && block_given?
|
||||
end
|
||||
|
||||
def context
|
||||
yield if :context == @source_view && block_given?
|
||||
end
|
||||
|
||||
def method_missing(check_source_view,*args)
|
||||
yield if check_source_view == @source_view && block_given?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Controller
|
||||
|
||||
def self.included(base)
|
||||
base.send(:helper, Tracks::SourceViewSwitching::Helper)
|
||||
base.send(:helper_method, :source_view)
|
||||
end
|
||||
|
||||
def source_view_is( s )
|
||||
s == (params[:_source_view] || @source_view).to_sym
|
||||
end
|
||||
|
||||
def source_view_is_one_of( *s )
|
||||
s.include?(params[:_source_view].to_sym)
|
||||
end
|
||||
|
||||
def source_view
|
||||
responder = Tracks::SourceViewSwitching::Responder.new(params[:_source_view] || @source_view)
|
||||
block_given? ? yield(responder) : responder
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Helper
|
||||
|
||||
def source_view_tag(name)
|
||||
hidden_field_tag :_source_view, name.underscore.gsub(/\s+/,'_')
|
||||
end
|
||||
|
||||
def source_view_is( s )
|
||||
s == (params[:_source_view] || @source_view).to_sym
|
||||
end
|
||||
|
||||
def source_view_is_one_of( *s )
|
||||
s.include?(params[:_source_view].to_sym)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
ActionController::Base.send(:include, Tracks::SourceViewSwitching::Controller)
|
||||
59
backup.rails2.3/lib/tracks/todo_list.rb
Normal file
59
backup.rails2.3/lib/tracks/todo_list.rb
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
module Tracks
|
||||
module TodoList
|
||||
# TODO: this module should be deprecated. This could mostly (all?) be replaced by named scopes)
|
||||
|
||||
def not_done_todos(opts={})
|
||||
@not_done_todos ||= self.find_not_done_todos(opts)
|
||||
end
|
||||
|
||||
def done_todos
|
||||
@done_todos ||= self.find_done_todos
|
||||
end
|
||||
|
||||
def deferred_todos
|
||||
@deferred_todos ||= self.find_deferred_todos
|
||||
end
|
||||
|
||||
def find_not_done_todos(opts={})
|
||||
with_not_done_scope(opts) do
|
||||
self.todos.find(:all, :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC")
|
||||
end
|
||||
end
|
||||
|
||||
def find_deferred_todos(opts={})
|
||||
self.todos.find_in_state(:all, :deferred, :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC")
|
||||
end
|
||||
|
||||
def find_done_todos
|
||||
self.todos.completed.all(:order => "todos.completed_at DESC", :limit => self.user.prefs.show_number_completed)
|
||||
end
|
||||
|
||||
def not_done_todo_count(opts={})
|
||||
with_not_done_scope(opts) do
|
||||
self.todos.count
|
||||
end
|
||||
end
|
||||
|
||||
def with_not_done_scope(opts={})
|
||||
conditions = ["todos.state = ?", 'active']
|
||||
if opts.has_key?(:include_project_hidden_todos) && (opts[:include_project_hidden_todos] == true)
|
||||
conditions = ["(todos.state = ? OR todos.state = ?)", 'active', 'project_hidden']
|
||||
end
|
||||
if opts.has_key?(:tag)
|
||||
conditions = ["todos.state = ? AND taggings.tag_id = ?", 'active', opts[:tag]]
|
||||
end
|
||||
self.todos.send :with_scope, :find => {:conditions => conditions, :include => [:taggings]} do
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def done_todo_count
|
||||
self.todos.count_in_state(:completed)
|
||||
end
|
||||
|
||||
def deferred_todo_count
|
||||
self.todos.count_in_state(:deferred)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
107
backup.rails2.3/mongrel_workaround.rb
Normal file
107
backup.rails2.3/mongrel_workaround.rb
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# adapted from https://gist.github.com/471663 and https://rails.lighthouseapp.com/projects/8994/tickets/4690-mongrel-doesnt-work-with-rails-238
|
||||
|
||||
def check_mongrel_around_115
|
||||
begin
|
||||
# Gem.available? is deprecated from rubygems 1.8.2
|
||||
Gem::Specification::find_by_name "mongrel", "~>1.1.5"
|
||||
rescue
|
||||
if RUBY_VERSION[2] == "9"
|
||||
false
|
||||
else
|
||||
Gem.available?('mongrel', '~>1.1.5')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mongrel115 = check_mongrel_around_115
|
||||
|
||||
if Rails.version == '2.3.14' && mongrel115 && self.class.const_defined?(:Mongrel)
|
||||
|
||||
# Pulled right from latest rack. Old looked like this in 1.1.0 version.
|
||||
#
|
||||
# def [](k)
|
||||
# super(@names[k] ||= @names[k.downcase])
|
||||
# end
|
||||
#
|
||||
module Rack
|
||||
module Utils
|
||||
class HeaderHash < Hash
|
||||
def [](k)
|
||||
super(@names[k]) if @names[k]
|
||||
super(@names[k.downcase])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Code pulled from the ticket above.
|
||||
#
|
||||
class Mongrel::CGIWrapper
|
||||
def header_with_rails_fix(options = 'text/html')
|
||||
@head['cookie'] = options.delete('cookie').flatten.map { |v| v.sub(/^\n/,'') } if options.class != String and options['cookie']
|
||||
header_without_rails_fix(options)
|
||||
end
|
||||
alias_method_chain :header, :rails_fix
|
||||
end
|
||||
|
||||
# Pulled right from 2.3.10 ActionPack. Simple diff was
|
||||
#
|
||||
# if headers.include?('Set-Cookie')
|
||||
# headers['cookie'] = headers.delete('Set-Cookie').split("\n")
|
||||
# end
|
||||
#
|
||||
# to
|
||||
#
|
||||
# if headers['Set-Cookie']
|
||||
# headers['cookie'] = headers.delete('Set-Cookie').split("\n")
|
||||
# end
|
||||
#
|
||||
module ActionController
|
||||
class CGIHandler
|
||||
def self.dispatch_cgi(app, cgi, out = $stdout)
|
||||
env = cgi.__send__(:env_table)
|
||||
env.delete "HTTP_CONTENT_LENGTH"
|
||||
|
||||
cgi.stdinput.extend ProperStream
|
||||
|
||||
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
||||
|
||||
env.update({
|
||||
"rack.version" => [0,1],
|
||||
"rack.input" => cgi.stdinput,
|
||||
"rack.errors" => $stderr,
|
||||
"rack.multithread" => false,
|
||||
"rack.multiprocess" => true,
|
||||
"rack.run_once" => false,
|
||||
"rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
|
||||
})
|
||||
|
||||
env["QUERY_STRING"] ||= ""
|
||||
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
|
||||
env["REQUEST_PATH"] ||= "/"
|
||||
env.delete "PATH_INFO" if env["PATH_INFO"] == ""
|
||||
|
||||
status, headers, body = app.call(env)
|
||||
begin
|
||||
out.binmode if out.respond_to?(:binmode)
|
||||
out.sync = false if out.respond_to?(:sync=)
|
||||
|
||||
headers['Status'] = status.to_s
|
||||
|
||||
if headers['Set-Cookie']
|
||||
headers['cookie'] = headers.delete('Set-Cookie').split("\n")
|
||||
end
|
||||
|
||||
out.write(cgi.header(headers))
|
||||
|
||||
body.each { |part|
|
||||
out.write part
|
||||
out.flush if out.respond_to?(:flush)
|
||||
}
|
||||
ensure
|
||||
body.close if body.respond_to?(:close)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
21
backup.rails2.3/new_rails_defaults.rb
Normal file
21
backup.rails2.3/new_rails_defaults.rb
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# These settings change the behavior of Rails 2 apps and will be defaults
|
||||
# for Rails 3. You can remove this initializer when Rails 3 is released.
|
||||
|
||||
if defined?(ActiveRecord)
|
||||
# Include Active Record class name as root for JSON serialized output.
|
||||
ActiveRecord::Base.include_root_in_json = true
|
||||
|
||||
# Store the full class name (including module namespace) in STI type column.
|
||||
ActiveRecord::Base.store_full_sti_class = true
|
||||
end
|
||||
|
||||
ActionController::Routing.generate_best_match = false
|
||||
|
||||
# Use ISO 8601 format for JSON serialized times and dates.
|
||||
ActiveSupport.use_standard_json_time_format = true
|
||||
|
||||
# Don't escape HTML entities in JSON, leave that for the #json_escape helper.
|
||||
# if you're including raw json in an HTML page.
|
||||
ActiveSupport.escape_html_entities_in_json = false
|
||||
2
backup.rails2.3/plugins/extra_validations/init.rb
Normal file
2
backup.rails2.3/plugins/extra_validations/init.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
require 'extra_validations'
|
||||
ActiveRecord::Base.extend ExtraValidations
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
module ExtraValidations
|
||||
|
||||
# Validates the value of the specified attribute by checking for a forbidden string
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# validates_does_not_contain :first_name, :string => ','
|
||||
# end
|
||||
#
|
||||
# A string must be provided or else an exception will be raised.
|
||||
#
|
||||
# Configuration options:
|
||||
# * <tt>message</tt> - A custom error message (default is: "is invalid")
|
||||
# * <tt>string</tt> - The string to verify is not included (note: must be supplied!)
|
||||
# * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
|
||||
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
||||
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
|
||||
# method, proc or string should return or evaluate to a true or false value.
|
||||
def validates_does_not_contain(*attr_names)
|
||||
configuration = { :message => I18n.translate('activerecord.errors.messages')[:invalid], :on => :save, :string => nil }
|
||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||
|
||||
raise(ArgumentError, "A string must be supplied as the :string option of the configuration hash") unless configuration[:string].is_a?(String)
|
||||
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
record.errors.add(attr_name, configuration[:message]) if value.to_s =~ Regexp.new(Regexp.escape(configuration[:string]))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
35
backup.rails2.3/plugins/open_id_authentication/CHANGELOG
Normal file
35
backup.rails2.3/plugins/open_id_authentication/CHANGELOG
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
* Fake HTTP method from OpenID server since they only support a GET. Eliminates the need to set an extra route to match the server's reply. [Josh Peek]
|
||||
|
||||
* OpenID 2.0 recommends that forms should use the field name "openid_identifier" rather than "openid_url" [Josh Peek]
|
||||
|
||||
* Return open_id_response.display_identifier to the application instead of .endpoints.claimed_id. [nbibler]
|
||||
|
||||
* Add Timeout protection [Rick]
|
||||
|
||||
* An invalid identity url passed through authenticate_with_open_id will no longer raise an InvalidOpenId exception. Instead it will return Result[:missing] to the completion block.
|
||||
|
||||
* Allow a return_to option to be used instead of the requested url [Josh Peek]
|
||||
|
||||
* Updated plugin to use Ruby OpenID 2.x.x [Josh Peek]
|
||||
|
||||
* Tied plugin to ruby-openid 1.1.4 gem until we can make it compatible with 2.x [DHH]
|
||||
|
||||
* Use URI instead of regexps to normalize the URL and gain free, better matching #8136 [dkubb]
|
||||
|
||||
* Allow -'s in #normalize_url [Rick]
|
||||
|
||||
* remove instance of mattr_accessor, it was breaking tests since they don't load ActiveSupport. Fix Timeout test [Rick]
|
||||
|
||||
* Throw a InvalidOpenId exception instead of just a RuntimeError when the URL can't be normalized [DHH]
|
||||
|
||||
* Just use the path for the return URL, so extra query parameters don't interfere [DHH]
|
||||
|
||||
* Added a new default database-backed store after experiencing trouble with the filestore on NFS. The file store is still available as an option [DHH]
|
||||
|
||||
* Added normalize_url and applied it to all operations going through the plugin [DHH]
|
||||
|
||||
* Removed open_id? as the idea of using the same input box for both OpenID and username has died -- use using_open_id? instead (which checks for the presence of params[:openid_url] by default) [DHH]
|
||||
|
||||
* Added OpenIdAuthentication::Result to make it easier to deal with default situations where you don't care to do something particular for each error state [DHH]
|
||||
|
||||
* Stop relying on root_url being defined, we can just grab the current url instead [DHH]
|
||||
231
backup.rails2.3/plugins/open_id_authentication/README
Normal file
231
backup.rails2.3/plugins/open_id_authentication/README
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
OpenIdAuthentication
|
||||
====================
|
||||
|
||||
Provides a thin wrapper around the excellent ruby-openid gem from JanRan. Be sure to install that first:
|
||||
|
||||
gem install ruby-openid
|
||||
|
||||
To understand what OpenID is about and how it works, it helps to read the documentation for lib/openid/consumer.rb
|
||||
from that gem.
|
||||
|
||||
The specification used is http://openid.net/specs/openid-authentication-2_0.html.
|
||||
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
OpenID authentication uses the session, so be sure that you haven't turned that off. It also relies on a number of
|
||||
database tables to store the authentication keys. So you'll have to run the migration to create these before you get started:
|
||||
|
||||
rake open_id_authentication:db:create
|
||||
|
||||
Or, use the included generators to install or upgrade:
|
||||
|
||||
./script/generate open_id_authentication_tables MigrationName
|
||||
./script/generate upgrade_open_id_authentication_tables MigrationName
|
||||
|
||||
Alternatively, you can use the file-based store, which just relies on on tmp/openids being present in RAILS_ROOT. But be aware that this store only works if you have a single application server. And it's not safe to use across NFS. It's recommended that you use the database store if at all possible. To use the file-based store, you'll also have to add this line to your config/environment.rb:
|
||||
|
||||
OpenIdAuthentication.store = :file
|
||||
|
||||
This particular plugin also relies on the fact that the authentication action allows for both POST and GET operations.
|
||||
If you're using RESTful authentication, you'll need to explicitly allow for this in your routes.rb.
|
||||
|
||||
The plugin also expects to find a root_url method that points to the home page of your site. You can accomplish this by using a root route in config/routes.rb:
|
||||
|
||||
map.root :controller => 'articles'
|
||||
|
||||
This plugin relies on Rails Edge revision 6317 or newer.
|
||||
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
This example is just to meant to demonstrate how you could use OpenID authentication. You might well want to add
|
||||
salted hash logins instead of plain text passwords and other requirements on top of this. Treat it as a starting point,
|
||||
not a destination.
|
||||
|
||||
Note that the User model referenced in the simple example below has an 'identity_url' attribute. You will want to add the same or similar field to whatever
|
||||
model you are using for authentication.
|
||||
|
||||
Also of note is the following code block used in the example below:
|
||||
|
||||
authenticate_with_open_id do |result, identity_url|
|
||||
...
|
||||
end
|
||||
|
||||
In the above code block, 'identity_url' will need to match user.identity_url exactly. 'identity_url' will be a string in the form of 'http://example.com' -
|
||||
If you are storing just 'example.com' with your user, the lookup will fail.
|
||||
|
||||
There is a handy method in this plugin called 'normalize_url' that will help with validating OpenID URLs.
|
||||
|
||||
OpenIdAuthentication.normalize_url(user.identity_url)
|
||||
|
||||
The above will return a standardized version of the OpenID URL - the above called with 'example.com' will return 'http://example.com/'
|
||||
It will also raise an InvalidOpenId exception if the URL is determined to not be valid.
|
||||
Use the above code in your User model and validate OpenID URLs before saving them.
|
||||
|
||||
config/routes.rb
|
||||
|
||||
map.root :controller => 'articles'
|
||||
map.resource :session
|
||||
|
||||
|
||||
app/views/sessions/new.erb
|
||||
|
||||
<% form_tag(session_url) do %>
|
||||
<p>
|
||||
<label for="name">Username:</label>
|
||||
<%= text_field_tag "name" %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="password">Password:</label>
|
||||
<%= password_field_tag %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
...or use:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="openid_identifier">OpenID:</label>
|
||||
<%= text_field_tag "openid_identifier" %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%= submit_tag 'Sign in', :disable_with => "Signing in…" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
app/controllers/sessions_controller.rb
|
||||
class SessionsController < ApplicationController
|
||||
def create
|
||||
if using_open_id?
|
||||
open_id_authentication
|
||||
else
|
||||
password_authentication(params[:name], params[:password])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def password_authentication(name, password)
|
||||
if @current_user = @account.users.authenticate(params[:name], params[:password])
|
||||
successful_login
|
||||
else
|
||||
failed_login "Sorry, that username/password doesn't work"
|
||||
end
|
||||
end
|
||||
|
||||
def open_id_authentication
|
||||
authenticate_with_open_id do |result, identity_url|
|
||||
if result.successful?
|
||||
if @current_user = @account.users.find_by_identity_url(identity_url)
|
||||
successful_login
|
||||
else
|
||||
failed_login "Sorry, no user by that identity URL exists (#{identity_url})"
|
||||
end
|
||||
else
|
||||
failed_login result.message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def successful_login
|
||||
session[:user_id] = @current_user.id
|
||||
redirect_to(root_url)
|
||||
end
|
||||
|
||||
def failed_login(message)
|
||||
flash[:error] = message
|
||||
redirect_to(new_session_url)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
If you're fine with the result messages above and don't need individual logic on a per-failure basis,
|
||||
you can collapse the case into a mere boolean:
|
||||
|
||||
def open_id_authentication
|
||||
authenticate_with_open_id do |result, identity_url|
|
||||
if result.successful? && @current_user = @account.users.find_by_identity_url(identity_url)
|
||||
successful_login
|
||||
else
|
||||
failed_login(result.message || "Sorry, no user by that identity URL exists (#{identity_url})")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Simple Registration OpenID Extension
|
||||
====================================
|
||||
|
||||
Some OpenID Providers support this lightweight profile exchange protocol. See more: http://www.openidenabled.com/openid/simple-registration-extension
|
||||
|
||||
You can support it in your app by changing #open_id_authentication
|
||||
|
||||
def open_id_authentication(identity_url)
|
||||
# Pass optional :required and :optional keys to specify what sreg fields you want.
|
||||
# Be sure to yield registration, a third argument in the #authenticate_with_open_id block.
|
||||
authenticate_with_open_id(identity_url,
|
||||
:required => [ :nickname, :email ],
|
||||
:optional => :fullname) do |result, identity_url, registration|
|
||||
case result.status
|
||||
when :missing
|
||||
failed_login "Sorry, the OpenID server couldn't be found"
|
||||
when :invalid
|
||||
failed_login "Sorry, but this does not appear to be a valid OpenID"
|
||||
when :canceled
|
||||
failed_login "OpenID verification was canceled"
|
||||
when :failed
|
||||
failed_login "Sorry, the OpenID verification failed"
|
||||
when :successful
|
||||
if @current_user = @account.users.find_by_identity_url(identity_url)
|
||||
assign_registration_attributes!(registration)
|
||||
|
||||
if current_user.save
|
||||
successful_login
|
||||
else
|
||||
failed_login "Your OpenID profile registration failed: " +
|
||||
@current_user.errors.full_messages.to_sentence
|
||||
end
|
||||
else
|
||||
failed_login "Sorry, no user by that identity URL exists"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# registration is a hash containing the valid sreg keys given above
|
||||
# use this to map them to fields of your user model
|
||||
def assign_registration_attributes!(registration)
|
||||
model_to_registration_mapping.each do |model_attribute, registration_attribute|
|
||||
unless registration[registration_attribute].blank?
|
||||
@current_user.send("#{model_attribute}=", registration[registration_attribute])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def model_to_registration_mapping
|
||||
{ :login => 'nickname', :email => 'email', :display_name => 'fullname' }
|
||||
end
|
||||
|
||||
Attribute Exchange OpenID Extension
|
||||
===================================
|
||||
|
||||
Some OpenID providers also support the OpenID AX (attribute exchange) protocol for exchanging identity information between endpoints. See more: http://openid.net/specs/openid-attribute-exchange-1_0.html
|
||||
|
||||
Accessing AX data is very similar to the Simple Registration process, described above -- just add the URI identifier for the AX field to your :optional or :required parameters. For example:
|
||||
|
||||
authenticate_with_open_id(identity_url,
|
||||
:required => [ :email, 'http://schema.openid.net/birthDate' ]) do |result, identity_url, registration|
|
||||
|
||||
This would provide the sreg data for :email, and the AX data for 'http://schema.openid.net/birthDate'
|
||||
|
||||
|
||||
|
||||
Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license
|
||||
22
backup.rails2.3/plugins/open_id_authentication/Rakefile
Normal file
22
backup.rails2.3/plugins/open_id_authentication/Rakefile
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the open_id_authentication plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'lib'
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the open_id_authentication plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'OpenIdAuthentication'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
class OpenIdAuthenticationTablesGenerator < Rails::Generator::NamedBase
|
||||
def initialize(runtime_args, runtime_options = {})
|
||||
super
|
||||
end
|
||||
|
||||
def manifest
|
||||
record do |m|
|
||||
m.migration_template 'migration.rb', 'db/migrate'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
class <%= class_name %> < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :open_id_authentication_associations, :force => true do |t|
|
||||
t.integer :issued, :lifetime
|
||||
t.string :handle, :assoc_type
|
||||
t.binary :server_url, :secret
|
||||
end
|
||||
|
||||
create_table :open_id_authentication_nonces, :force => true do |t|
|
||||
t.integer :timestamp, :null => false
|
||||
t.string :server_url, :null => true
|
||||
t.string :salt, :null => false
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :open_id_authentication_associations
|
||||
drop_table :open_id_authentication_nonces
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
class <%= class_name %> < ActiveRecord::Migration
|
||||
def self.up
|
||||
drop_table :open_id_authentication_settings
|
||||
drop_table :open_id_authentication_nonces
|
||||
|
||||
create_table :open_id_authentication_nonces, :force => true do |t|
|
||||
t.integer :timestamp, :null => false
|
||||
t.string :server_url, :null => true
|
||||
t.string :salt, :null => false
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :open_id_authentication_nonces
|
||||
|
||||
create_table :open_id_authentication_nonces, :force => true do |t|
|
||||
t.integer :created
|
||||
t.string :nonce
|
||||
end
|
||||
|
||||
create_table :open_id_authentication_settings, :force => true do |t|
|
||||
t.string :setting
|
||||
t.binary :value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
class UpgradeOpenIdAuthenticationTablesGenerator < Rails::Generator::NamedBase
|
||||
def initialize(runtime_args, runtime_options = {})
|
||||
super
|
||||
end
|
||||
|
||||
def manifest
|
||||
record do |m|
|
||||
m.migration_template 'migration.rb', 'db/migrate'
|
||||
end
|
||||
end
|
||||
end
|
||||
18
backup.rails2.3/plugins/open_id_authentication/init.rb
Normal file
18
backup.rails2.3/plugins/open_id_authentication/init.rb
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
if config.respond_to?(:gems)
|
||||
config.gem 'ruby-openid', :lib => 'openid', :version => '>=2.0.4'
|
||||
else
|
||||
begin
|
||||
require 'openid'
|
||||
rescue LoadError
|
||||
begin
|
||||
gem 'ruby-openid', '>=2.0.4'
|
||||
rescue Gem::LoadError
|
||||
puts "Install the ruby-openid gem to enable OpenID support"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
config.to_prepare do
|
||||
OpenID::Util.logger = Rails.logger
|
||||
ActionController::Base.send :include, OpenIdAuthentication
|
||||
end
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
require 'uri'
|
||||
require 'openid/extensions/sreg'
|
||||
require 'openid/extensions/ax'
|
||||
require 'openid/store/filesystem'
|
||||
|
||||
require File.dirname(__FILE__) + '/open_id_authentication/association'
|
||||
require File.dirname(__FILE__) + '/open_id_authentication/nonce'
|
||||
require File.dirname(__FILE__) + '/open_id_authentication/db_store'
|
||||
require File.dirname(__FILE__) + '/open_id_authentication/request'
|
||||
require File.dirname(__FILE__) + '/open_id_authentication/timeout_fixes' if OpenID::VERSION == "2.0.4"
|
||||
|
||||
module OpenIdAuthentication
|
||||
OPEN_ID_AUTHENTICATION_DIR = RAILS_ROOT + "/tmp/openids"
|
||||
|
||||
def self.store
|
||||
@@store
|
||||
end
|
||||
|
||||
def self.store=(*store_option)
|
||||
store, *parameters = *([ store_option ].flatten)
|
||||
|
||||
@@store = case store
|
||||
when :db
|
||||
OpenIdAuthentication::DbStore.new
|
||||
when :file
|
||||
OpenID::Store::Filesystem.new(OPEN_ID_AUTHENTICATION_DIR)
|
||||
else
|
||||
store
|
||||
end
|
||||
end
|
||||
|
||||
self.store = :db
|
||||
|
||||
class InvalidOpenId < StandardError
|
||||
end
|
||||
|
||||
class Result
|
||||
ERROR_MESSAGES = {
|
||||
:missing => "Sorry, the OpenID server couldn't be found",
|
||||
:invalid => "Sorry, but this does not appear to be a valid OpenID",
|
||||
:canceled => "OpenID verification was canceled",
|
||||
:failed => "OpenID verification failed",
|
||||
:setup_needed => "OpenID verification needs setup"
|
||||
}
|
||||
|
||||
def self.[](code)
|
||||
new(code)
|
||||
end
|
||||
|
||||
def initialize(code)
|
||||
@code = code
|
||||
end
|
||||
|
||||
def status
|
||||
@code
|
||||
end
|
||||
|
||||
ERROR_MESSAGES.keys.each { |state| define_method("#{state}?") { @code == state } }
|
||||
|
||||
def successful?
|
||||
@code == :successful
|
||||
end
|
||||
|
||||
def unsuccessful?
|
||||
ERROR_MESSAGES.keys.include?(@code)
|
||||
end
|
||||
|
||||
def message
|
||||
ERROR_MESSAGES[@code]
|
||||
end
|
||||
end
|
||||
|
||||
# normalizes an OpenID according to http://openid.net/specs/openid-authentication-2_0.html#normalization
|
||||
def self.normalize_identifier(identifier)
|
||||
# clean up whitespace
|
||||
identifier = identifier.to_s.strip
|
||||
|
||||
# if an XRI has a prefix, strip it.
|
||||
identifier.gsub!(/xri:\/\//i, '')
|
||||
|
||||
# dodge XRIs -- TODO: validate, don't just skip.
|
||||
unless ['=', '@', '+', '$', '!', '('].include?(identifier.at(0))
|
||||
# does it begin with http? if not, add it.
|
||||
identifier = "http://#{identifier}" unless identifier =~ /^http/i
|
||||
|
||||
# strip any fragments
|
||||
identifier.gsub!(/\#(.*)$/, '')
|
||||
|
||||
begin
|
||||
uri = URI.parse(identifier)
|
||||
uri.scheme = uri.scheme.downcase # URI should do this
|
||||
identifier = uri.normalize.to_s
|
||||
rescue URI::InvalidURIError
|
||||
raise InvalidOpenId.new("#{identifier} is not an OpenID identifier")
|
||||
end
|
||||
end
|
||||
|
||||
return identifier
|
||||
end
|
||||
|
||||
# deprecated for OpenID 2.0, where not all OpenIDs are URLs
|
||||
def self.normalize_url(url)
|
||||
ActiveSupport::Deprecation.warn "normalize_url has been deprecated, use normalize_identifier instead"
|
||||
self.normalize_identifier(url)
|
||||
end
|
||||
|
||||
protected
|
||||
def normalize_url(url)
|
||||
OpenIdAuthentication.normalize_url(url)
|
||||
end
|
||||
|
||||
def normalize_identifier(url)
|
||||
OpenIdAuthentication.normalize_identifier(url)
|
||||
end
|
||||
|
||||
# The parameter name of "openid_identifier" is used rather than the Rails convention "open_id_identifier"
|
||||
# because that's what the specification dictates in order to get browser auto-complete working across sites
|
||||
def using_open_id?(identity_url = nil) #:doc:
|
||||
identity_url ||= params[:openid_identifier] || params[:openid_url]
|
||||
!identity_url.blank? || params[:open_id_complete]
|
||||
end
|
||||
|
||||
def authenticate_with_open_id(identity_url = nil, options = {}, &block) #:doc:
|
||||
identity_url ||= params[:openid_identifier] || params[:openid_url]
|
||||
|
||||
if params[:open_id_complete].nil?
|
||||
begin_open_id_authentication(identity_url, options, &block)
|
||||
else
|
||||
complete_open_id_authentication(&block)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def begin_open_id_authentication(identity_url, options = {})
|
||||
identity_url = normalize_identifier(identity_url)
|
||||
return_to = options.delete(:return_to)
|
||||
method = options.delete(:method)
|
||||
|
||||
options[:required] ||= [] # reduces validation later
|
||||
options[:optional] ||= []
|
||||
|
||||
open_id_request = open_id_consumer.begin(identity_url)
|
||||
add_simple_registration_fields(open_id_request, options)
|
||||
add_ax_fields(open_id_request, options)
|
||||
redirect_to(open_id_redirect_url(open_id_request, return_to, method))
|
||||
rescue OpenIdAuthentication::InvalidOpenId => e
|
||||
yield Result[:invalid], identity_url, nil
|
||||
rescue OpenID::OpenIDError, Timeout::Error => e
|
||||
logger.error("[OPENID] #{e}")
|
||||
yield Result[:missing], identity_url, nil
|
||||
end
|
||||
|
||||
def complete_open_id_authentication
|
||||
params_with_path = params.reject { |key, value| request.path_parameters[key] }
|
||||
params_with_path.delete(:format)
|
||||
open_id_response = timeout_protection_from_identity_server { open_id_consumer.complete(params_with_path, requested_url) }
|
||||
identity_url = normalize_identifier(open_id_response.display_identifier) if open_id_response.display_identifier
|
||||
|
||||
case open_id_response.status
|
||||
when OpenID::Consumer::SUCCESS
|
||||
profile_data = {}
|
||||
|
||||
# merge the SReg data and the AX data into a single hash of profile data
|
||||
[ OpenID::SReg::Response, OpenID::AX::FetchResponse ].each do |data_response|
|
||||
if data_response.from_success_response( open_id_response )
|
||||
profile_data.merge! data_response.from_success_response( open_id_response ).data
|
||||
end
|
||||
end
|
||||
|
||||
yield Result[:successful], identity_url, profile_data
|
||||
when OpenID::Consumer::CANCEL
|
||||
yield Result[:canceled], identity_url, nil
|
||||
when OpenID::Consumer::FAILURE
|
||||
yield Result[:failed], identity_url, nil
|
||||
when OpenID::Consumer::SETUP_NEEDED
|
||||
yield Result[:setup_needed], open_id_response.setup_url, nil
|
||||
end
|
||||
end
|
||||
|
||||
def open_id_consumer
|
||||
OpenID::Consumer.new(session, OpenIdAuthentication.store)
|
||||
end
|
||||
|
||||
def add_simple_registration_fields(open_id_request, fields)
|
||||
sreg_request = OpenID::SReg::Request.new
|
||||
|
||||
# filter out AX identifiers (URIs)
|
||||
required_fields = fields[:required].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
|
||||
optional_fields = fields[:optional].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
|
||||
|
||||
sreg_request.request_fields(required_fields, true) unless required_fields.blank?
|
||||
sreg_request.request_fields(optional_fields, false) unless optional_fields.blank?
|
||||
sreg_request.policy_url = fields[:policy_url] if fields[:policy_url]
|
||||
open_id_request.add_extension(sreg_request)
|
||||
end
|
||||
|
||||
def add_ax_fields( open_id_request, fields )
|
||||
ax_request = OpenID::AX::FetchRequest.new
|
||||
|
||||
# look through the :required and :optional fields for URIs (AX identifiers)
|
||||
fields[:required].each do |f|
|
||||
next unless f =~ /^https?:\/\//
|
||||
ax_request.add( OpenID::AX::AttrInfo.new( f, nil, true ) )
|
||||
end
|
||||
|
||||
fields[:optional].each do |f|
|
||||
next unless f =~ /^https?:\/\//
|
||||
ax_request.add( OpenID::AX::AttrInfo.new( f, nil, false ) )
|
||||
end
|
||||
|
||||
open_id_request.add_extension( ax_request )
|
||||
end
|
||||
|
||||
def open_id_redirect_url(open_id_request, return_to = nil, method = nil)
|
||||
open_id_request.return_to_args['_method'] = (method || request.method).to_s
|
||||
open_id_request.return_to_args['open_id_complete'] = '1'
|
||||
open_id_request.redirect_url(root_url, return_to || requested_url)
|
||||
end
|
||||
|
||||
def requested_url
|
||||
relative_url_root = self.class.respond_to?(:relative_url_root) ?
|
||||
self.class.relative_url_root.to_s :
|
||||
request.relative_url_root
|
||||
"#{request.protocol}#{request.host_with_port}#{ActionController::Base.relative_url_root}#{request.path}"
|
||||
end
|
||||
|
||||
def timeout_protection_from_identity_server
|
||||
yield
|
||||
rescue Timeout::Error
|
||||
Class.new do
|
||||
def status
|
||||
OpenID::FAILURE
|
||||
end
|
||||
|
||||
def msg
|
||||
"Identity server timed out"
|
||||
end
|
||||
end.new
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
module OpenIdAuthentication
|
||||
class Association < ActiveRecord::Base
|
||||
set_table_name :open_id_authentication_associations
|
||||
|
||||
def from_record
|
||||
OpenID::Association.new(handle, secret, issued, lifetime, assoc_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
require 'openid/store/interface'
|
||||
|
||||
module OpenIdAuthentication
|
||||
class DbStore < OpenID::Store::Interface
|
||||
def self.cleanup_nonces
|
||||
now = Time.now.to_i
|
||||
Nonce.delete_all(["timestamp > ? OR timestamp < ?", now + OpenID::Nonce.skew, now - OpenID::Nonce.skew])
|
||||
end
|
||||
|
||||
def self.cleanup_associations
|
||||
now = Time.now.to_i
|
||||
Association.delete_all(['issued + lifetime > ?',now])
|
||||
end
|
||||
|
||||
def store_association(server_url, assoc)
|
||||
remove_association(server_url, assoc.handle)
|
||||
Association.create(:server_url => server_url,
|
||||
:handle => assoc.handle,
|
||||
:secret => assoc.secret,
|
||||
:issued => assoc.issued,
|
||||
:lifetime => assoc.lifetime,
|
||||
:assoc_type => assoc.assoc_type)
|
||||
end
|
||||
|
||||
def get_association(server_url, handle = nil)
|
||||
assocs = if handle.blank?
|
||||
Association.find_all_by_server_url(server_url)
|
||||
else
|
||||
Association.find_all_by_server_url_and_handle(server_url, handle)
|
||||
end
|
||||
|
||||
assocs.reverse.each do |assoc|
|
||||
a = assoc.from_record
|
||||
if a.expires_in == 0
|
||||
assoc.destroy
|
||||
else
|
||||
return a
|
||||
end
|
||||
end if assocs.any?
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def remove_association(server_url, handle)
|
||||
Association.delete_all(['server_url = ? AND handle = ?', server_url, handle]) > 0
|
||||
end
|
||||
|
||||
def use_nonce(server_url, timestamp, salt)
|
||||
return false if Nonce.find_by_server_url_and_timestamp_and_salt(server_url, timestamp, salt)
|
||||
return false if (timestamp - Time.now.to_i).abs > OpenID::Nonce.skew
|
||||
Nonce.create(:server_url => server_url, :timestamp => timestamp, :salt => salt)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
module OpenIdAuthentication
|
||||
class Nonce < ActiveRecord::Base
|
||||
set_table_name :open_id_authentication_nonces
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
module OpenIdAuthentication
|
||||
module Request
|
||||
def self.included(base)
|
||||
base.alias_method_chain :request_method, :openid
|
||||
end
|
||||
|
||||
def request_method_with_openid
|
||||
if !parameters[:_method].blank? && parameters[:open_id_complete] == '1'
|
||||
parameters[:_method].to_sym
|
||||
else
|
||||
request_method_without_openid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# In Rails 2.3, the request object has been renamed
|
||||
# from AbstractRequest to Request
|
||||
if defined? ActionController::Request
|
||||
ActionController::Request.send :include, OpenIdAuthentication::Request
|
||||
else
|
||||
ActionController::AbstractRequest.send :include, OpenIdAuthentication::Request
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# http://trac.openidenabled.com/trac/ticket/156
|
||||
module OpenID
|
||||
@@timeout_threshold = 20
|
||||
|
||||
def self.timeout_threshold
|
||||
@@timeout_threshold
|
||||
end
|
||||
|
||||
def self.timeout_threshold=(value)
|
||||
@@timeout_threshold = value
|
||||
end
|
||||
|
||||
class StandardFetcher
|
||||
def make_http(uri)
|
||||
http = @proxy.new(uri.host, uri.port)
|
||||
http.read_timeout = http.open_timeout = OpenID.timeout_threshold
|
||||
http
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
namespace :open_id_authentication do
|
||||
namespace :db do
|
||||
desc "Creates authentication tables for use with OpenIdAuthentication"
|
||||
task :create => :environment do
|
||||
generate_migration(["open_id_authentication_tables", "add_open_id_authentication_tables"])
|
||||
end
|
||||
|
||||
desc "Upgrade authentication tables from ruby-openid 1.x.x to 2.x.x"
|
||||
task :upgrade => :environment do
|
||||
generate_migration(["upgrade_open_id_authentication_tables", "upgrade_open_id_authentication_tables"])
|
||||
end
|
||||
|
||||
def generate_migration(args)
|
||||
require 'rails_generator'
|
||||
require 'rails_generator/scripts/generate'
|
||||
|
||||
if ActiveRecord::Base.connection.supports_migrations?
|
||||
Rails::Generator::Scripts::Generate.new.run(args)
|
||||
else
|
||||
raise "Task unavailable to this database (no migration support)"
|
||||
end
|
||||
end
|
||||
|
||||
desc "Clear the authentication tables"
|
||||
task :clear => :environment do
|
||||
OpenIdAuthentication::DbStore.cleanup_nonces
|
||||
OpenIdAuthentication::DbStore.cleanup_associations
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
require File.dirname(__FILE__) + '/test_helper'
|
||||
|
||||
class NormalizeTest < Test::Unit::TestCase
|
||||
include OpenIdAuthentication
|
||||
|
||||
NORMALIZATIONS = {
|
||||
"openid.aol.com/nextangler" => "http://openid.aol.com/nextangler",
|
||||
"http://openid.aol.com/nextangler" => "http://openid.aol.com/nextangler",
|
||||
"https://openid.aol.com/nextangler" => "https://openid.aol.com/nextangler",
|
||||
"HTTP://OPENID.AOL.COM/NEXTANGLER" => "http://openid.aol.com/NEXTANGLER",
|
||||
"HTTPS://OPENID.AOL.COM/NEXTANGLER" => "https://openid.aol.com/NEXTANGLER",
|
||||
"loudthinking.com" => "http://loudthinking.com/",
|
||||
"http://loudthinking.com" => "http://loudthinking.com/",
|
||||
"http://loudthinking.com:80" => "http://loudthinking.com/",
|
||||
"https://loudthinking.com:443" => "https://loudthinking.com/",
|
||||
"http://loudthinking.com:8080" => "http://loudthinking.com:8080/",
|
||||
"techno-weenie.net" => "http://techno-weenie.net/",
|
||||
"http://techno-weenie.net" => "http://techno-weenie.net/",
|
||||
"http://techno-weenie.net " => "http://techno-weenie.net/",
|
||||
"=name" => "=name"
|
||||
}
|
||||
|
||||
def test_normalizations
|
||||
NORMALIZATIONS.each do |from, to|
|
||||
assert_equal to, normalize_identifier(from)
|
||||
end
|
||||
end
|
||||
|
||||
def test_broken_open_id
|
||||
assert_raises(InvalidOpenId) { normalize_identifier(nil) }
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
require File.dirname(__FILE__) + '/test_helper'
|
||||
|
||||
class OpenIdAuthenticationTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@controller = Class.new do
|
||||
include OpenIdAuthentication
|
||||
def params() {} end
|
||||
end.new
|
||||
end
|
||||
|
||||
def test_authentication_should_fail_when_the_identity_server_is_missing
|
||||
open_id_consumer = mock()
|
||||
open_id_consumer.expects(:begin).raises(OpenID::OpenIDError)
|
||||
@controller.expects(:open_id_consumer).returns(open_id_consumer)
|
||||
@controller.expects(:logger).returns(mock(:error => true))
|
||||
|
||||
@controller.send(:authenticate_with_open_id, "http://someone.example.com") do |result, identity_url|
|
||||
assert result.missing?
|
||||
assert_equal "Sorry, the OpenID server couldn't be found", result.message
|
||||
end
|
||||
end
|
||||
|
||||
def test_authentication_should_be_invalid_when_the_identity_url_is_invalid
|
||||
@controller.send(:authenticate_with_open_id, "!") do |result, identity_url|
|
||||
assert result.invalid?, "Result expected to be invalid but was not"
|
||||
assert_equal "Sorry, but this does not appear to be a valid OpenID", result.message
|
||||
end
|
||||
end
|
||||
|
||||
def test_authentication_should_fail_when_the_identity_server_times_out
|
||||
open_id_consumer = mock()
|
||||
open_id_consumer.expects(:begin).raises(Timeout::Error, "Identity Server took too long.")
|
||||
@controller.expects(:open_id_consumer).returns(open_id_consumer)
|
||||
@controller.expects(:logger).returns(mock(:error => true))
|
||||
|
||||
@controller.send(:authenticate_with_open_id, "http://someone.example.com") do |result, identity_url|
|
||||
assert result.missing?
|
||||
assert_equal "Sorry, the OpenID server couldn't be found", result.message
|
||||
end
|
||||
end
|
||||
|
||||
def test_authentication_should_begin_when_the_identity_server_is_present
|
||||
@controller.expects(:begin_open_id_authentication)
|
||||
@controller.send(:authenticate_with_open_id, "http://someone.example.com")
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
require File.dirname(__FILE__) + '/test_helper'
|
||||
|
||||
class StatusTest < Test::Unit::TestCase
|
||||
include OpenIdAuthentication
|
||||
|
||||
def test_state_conditional
|
||||
assert Result[:missing].missing?
|
||||
assert Result[:missing].unsuccessful?
|
||||
assert !Result[:missing].successful?
|
||||
|
||||
assert Result[:successful].successful?
|
||||
assert !Result[:successful].unsuccessful?
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
require 'test/unit'
|
||||
require 'rubygems'
|
||||
|
||||
gem 'activesupport'
|
||||
require 'active_support'
|
||||
|
||||
gem 'actionpack'
|
||||
require 'action_controller'
|
||||
|
||||
gem 'mocha'
|
||||
require 'mocha'
|
||||
|
||||
gem 'ruby-openid'
|
||||
require 'openid'
|
||||
|
||||
RAILS_ROOT = File.dirname(__FILE__) unless defined? RAILS_ROOT
|
||||
require File.dirname(__FILE__) + "/../lib/open_id_authentication"
|
||||
7
backup.rails2.3/plugins/resource_feeder/README
Normal file
7
backup.rails2.3/plugins/resource_feeder/README
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
ResourceFeeder
|
||||
==============
|
||||
|
||||
Simple feeds for resources
|
||||
|
||||
NOTE: This plugin depends on the latest version of simply_helpful, available here:
|
||||
http://dev.rubyonrails.org/svn/rails/plugins/simply_helpful/
|
||||
22
backup.rails2.3/plugins/resource_feeder/Rakefile
Normal file
22
backup.rails2.3/plugins/resource_feeder/Rakefile
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the resource_feed plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'lib'
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the resource_feed plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'ResourceFeed'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
2
backup.rails2.3/plugins/resource_feeder/init.rb
Normal file
2
backup.rails2.3/plugins/resource_feeder/init.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
require 'resource_feeder'
|
||||
ActionController::Base.send(:include, ResourceFeeder::Rss, ResourceFeeder::Atom)
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
require 'resource_feeder/rss'
|
||||
require 'resource_feeder/atom'
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
require 'resource_feeder/common'
|
||||
|
||||
module ResourceFeeder
|
||||
module Atom
|
||||
include ResourceFeeder::Common
|
||||
include ActionController::Routing
|
||||
extend self
|
||||
|
||||
def render_atom_feed_for(resources, options = {})
|
||||
render :text => atom_feed_for(resources, options), :content_type => Mime::ATOM
|
||||
end
|
||||
|
||||
def atom_feed_for(resources, options = {})
|
||||
xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
|
||||
options[:feed] ||= {}
|
||||
options[:item] ||= {}
|
||||
options[:url_writer] ||= self
|
||||
|
||||
if options[:class] || resources.first
|
||||
klass = options[:class] || resources.first.class
|
||||
new_record = klass.new
|
||||
else
|
||||
options[:feed] = { :title => "Empty", :link => "http://example.com" }
|
||||
end
|
||||
|
||||
options[:feed][:title] ||= klass.name.pluralize
|
||||
options[:feed][:id] ||= "tag:#{request.host_with_port}:#{klass.name.pluralize}"
|
||||
options[:feed][:link] ||= polymorphic_url(new_record, :controller => options[:url_writer].controller_name)
|
||||
|
||||
options[:item][:title] ||= [ :title, :subject, :headline, :name ]
|
||||
options[:item][:description] ||= [ :description, :body, :content ]
|
||||
options[:item][:pub_date] ||= [ :updated_at, :updated_on, :created_at, :created_on ]
|
||||
options[:item][:author] ||= [ :author, :creator ]
|
||||
|
||||
resource_link = lambda { |r| polymorphic_url(r, :controller => options[:url_writer].controller_name) }
|
||||
|
||||
xml.instruct!
|
||||
xml.feed "xml:lang" => "en-US", "xmlns" => 'http://www.w3.org/2005/Atom' do
|
||||
xml.title(options[:feed][:title])
|
||||
xml.id(options[:feed][:id])
|
||||
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:feed][:link])
|
||||
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:feed][:self]) if options[:feed][:self]
|
||||
xml.subtitle(options[:feed][:description]) if options[:feed][:description]
|
||||
|
||||
for resource in resources
|
||||
published_at = call_or_read(options[:item][:pub_date], resource)
|
||||
|
||||
xml.entry do
|
||||
xml.title(call_or_read(options[:item][:title], resource))
|
||||
xml.content(call_or_read(options[:item][:description], resource), :type => 'html')
|
||||
xml.id("tag:#{request.host_with_port},#{published_at.xmlschema}:#{call_or_read(options[:item][:guid] || options[:item][:link] || resource_link, resource)}")
|
||||
xml.published(published_at.xmlschema)
|
||||
xml.updated((resource.respond_to?(:updated_at) ? call_or_read(options[:item][:pub_date] || :updated_at, resource) : published_at).xmlschema)
|
||||
xml.link(:rel => 'alternate', :type => 'text/html', :href => call_or_read(options[:item][:link] || options[:item][:guid] || resource_link, resource))
|
||||
|
||||
if author = call_or_read(options[:item][:author], resource)
|
||||
xml.author do
|
||||
xml.name()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
module ResourceFeeder
|
||||
module Common
|
||||
private
|
||||
def call_or_read(procedure_or_attributes, resource)
|
||||
case procedure_or_attributes
|
||||
when nil
|
||||
raise ArgumentError, "WTF is nil here? #{resource.inspect}"
|
||||
when Array
|
||||
attributes = procedure_or_attributes
|
||||
if attr = attributes.select { |a| resource.respond_to?(a) }.first
|
||||
resource.send attr
|
||||
end
|
||||
when Symbol
|
||||
attribute = procedure_or_attributes
|
||||
resource.send(attribute)
|
||||
when Proc
|
||||
procedure = procedure_or_attributes
|
||||
procedure.call(resource)
|
||||
else
|
||||
raise ArgumentError, "WTF is #{procedure_or_attributes.inspect} here? #{resource.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
require 'resource_feeder/common'
|
||||
|
||||
module ResourceFeeder
|
||||
module Rss
|
||||
include ResourceFeeder::Common
|
||||
include ActionController::Routing
|
||||
extend self
|
||||
|
||||
def render_rss_feed_for(resources, options = {})
|
||||
render :text => rss_feed_for(resources, options), :content_type => Mime::RSS
|
||||
end
|
||||
|
||||
def rss_feed_for(resources, options = {})
|
||||
xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
|
||||
options[:feed] ||= {}
|
||||
options[:item] ||= {}
|
||||
options[:url_writer] ||= self
|
||||
|
||||
if options[:class] || resources.first
|
||||
klass = options[:class] || resources.first.class
|
||||
new_record = klass.new
|
||||
else
|
||||
options[:feed] = { :title => "Empty", :link => "http://example.com" }
|
||||
end
|
||||
use_content_encoded = options[:item].has_key?(:content_encoded)
|
||||
|
||||
options[:feed][:title] ||= klass.name.pluralize
|
||||
options[:feed][:link] ||= polymorphic_url(new_record, :controller => options[:url_writer].controller_name)
|
||||
options[:feed][:language] ||= "en-us"
|
||||
options[:feed][:ttl] ||= "40"
|
||||
|
||||
options[:item][:title] ||= [ :title, :subject, :headline, :name ]
|
||||
options[:item][:description] ||= [ :description, :body, :content ]
|
||||
options[:item][:pub_date] ||= [ :updated_at, :updated_on, :created_at, :created_on ]
|
||||
|
||||
resource_link = lambda { |r| polymorphic_url(r, :controller => options[:url_writer].controller_name) }
|
||||
|
||||
rss_root_attributes = { :version => 2.0 }
|
||||
rss_root_attributes.merge!("xmlns:content" => "http://purl.org/rss/1.0/modules/content/") if use_content_encoded
|
||||
|
||||
xml.instruct!
|
||||
|
||||
xml.rss(rss_root_attributes) do
|
||||
xml.channel do
|
||||
xml.title(options[:feed][:title])
|
||||
xml.link(options[:feed][:link])
|
||||
xml.description(options[:feed][:description]) if options[:feed][:description]
|
||||
xml.language(options[:feed][:language])
|
||||
xml.ttl(options[:feed][:ttl])
|
||||
|
||||
for resource in resources
|
||||
xml.item do
|
||||
xml.title(call_or_read(options[:item][:title], resource))
|
||||
xml.description(call_or_read(options[:item][:description], resource))
|
||||
if use_content_encoded then
|
||||
xml.content(:encoded) { xml.cdata!(call_or_read(options[:item][:content_encoded], resource)) }
|
||||
end
|
||||
xml.pubDate(call_or_read(options[:item][:pub_date], resource).to_s(:rfc822))
|
||||
xml.guid(call_or_read(options[:item][:guid] || options[:item][:link] || resource_link, resource))
|
||||
xml.link(call_or_read(options[:item][:link] || options[:item][:guid] || resource_link, resource))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
require File.dirname(__FILE__) + '/test_helper'
|
||||
class AtomFeedTest < Test::Unit::TestCase
|
||||
attr_reader :request
|
||||
|
||||
def setup
|
||||
@request = OpenStruct.new
|
||||
@request.host_with_port = 'example.com'
|
||||
@records = Array.new(5).fill(Post.new)
|
||||
@records.each &:save
|
||||
end
|
||||
|
||||
def test_default_atom_feed
|
||||
atom_feed_for @records
|
||||
|
||||
assert_select 'feed' do
|
||||
assert_select '>title', 'Posts'
|
||||
assert_select '>id', "tag:#{request.host_with_port}:Posts"
|
||||
assert_select '>link' do
|
||||
assert_select "[rel='alternate']"
|
||||
assert_select "[type='text/html']"
|
||||
assert_select "[href='http://example.com/posts']"
|
||||
end
|
||||
assert_select 'entry', 5 do
|
||||
assert_select 'title', :text => 'feed title (title)'
|
||||
assert_select "content[type='html']", '<p>feed description (description)</p>'
|
||||
assert_select 'id', "tag:#{request.host_with_port},#{@records.first.created_at.xmlschema}:#{'http://example.com/posts/1'}"
|
||||
assert_select 'published', @records.first.created_at.xmlschema
|
||||
assert_select 'updated', @records.first.created_at.xmlschema
|
||||
assert_select 'link' do
|
||||
assert_select "[rel='alternate']"
|
||||
assert_select "[type='text/html']"
|
||||
assert_select "[href='http://example.com/posts/1']"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_allow_custom_feed_options
|
||||
atom_feed_for @records, :feed => { :title => 'Custom Posts', :link => '/posts', :description => 'stuff', :self => '/posts.atom' }
|
||||
|
||||
assert_select 'feed>title', 'Custom Posts'
|
||||
assert_select "feed>link[href='/posts']"
|
||||
assert_select 'feed>subtitle', 'stuff'
|
||||
assert_select 'feed>link' do
|
||||
assert_select "[rel='self']"
|
||||
assert_select "[type='application/atom+xml']"
|
||||
assert_select "[href='/posts.atom']"
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_allow_custom_item_attributes
|
||||
atom_feed_for @records, :item => { :title => :name, :description => :body, :pub_date => :create_date, :link => :id }
|
||||
|
||||
assert_select 'entry', 5 do
|
||||
assert_select 'title', :text => 'feed title (name)'
|
||||
assert_select "content[type='html']", '<p>feed description (body)</p>'
|
||||
assert_select 'published', (@records.first.created_at - 5.minutes).xmlschema
|
||||
assert_select 'updated', (@records.first.created_at - 5.minutes).xmlschema
|
||||
assert_select 'id', "tag:#{request.host_with_port},#{(@records.first.created_at - 5.minutes).xmlschema}:1"
|
||||
assert_select 'link' do
|
||||
assert_select "[rel='alternate']"
|
||||
assert_select "[type='text/html']"
|
||||
assert_select "[href='1']"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_allow_custom_item_attribute_blocks
|
||||
atom_feed_for @records, :item => { :title => lambda { |r| r.name }, :description => lambda { |r| r.body }, :pub_date => lambda { |r| r.create_date },
|
||||
:link => lambda { |r| "/#{r.created_at.to_i}" }, :guid => lambda { |r| r.created_at.to_i } }
|
||||
|
||||
assert_select 'entry', 5 do
|
||||
assert_select 'title', :text => 'feed title (name)'
|
||||
assert_select "content[type='html']", '<p>feed description (body)</p>'
|
||||
assert_select 'published', (@records.first.created_at - 5.minutes).xmlschema
|
||||
assert_select 'updated', (@records.first.created_at - 5.minutes).xmlschema
|
||||
assert_select 'id', /:\d+$/
|
||||
assert_select 'link' do
|
||||
assert_select "[rel='alternate']"
|
||||
assert_select "[type='text/html']"
|
||||
assert_select "[href=?]", /^\/\d+$/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
require File.dirname(__FILE__) + '/test_helper'
|
||||
class RssFeedTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@records = Array.new(5).fill(Post.new)
|
||||
@records.each &:save
|
||||
end
|
||||
|
||||
def test_default_rss_feed
|
||||
rss_feed_for @records
|
||||
|
||||
assert_select 'rss[version="2.0"]' do
|
||||
assert_select 'channel' do
|
||||
assert_select '>title', 'Posts'
|
||||
assert_select '>link', 'http://example.com/posts'
|
||||
assert_select 'language', 'en-us'
|
||||
assert_select 'ttl', '40'
|
||||
end
|
||||
assert_select 'item', 5 do
|
||||
assert_select 'title', :text => 'feed title (title)'
|
||||
assert_select 'description', '<p>feed description (description)</p>'
|
||||
%w(guid link).each do |node|
|
||||
assert_select node, 'http://example.com/posts/1'
|
||||
end
|
||||
assert_select 'pubDate', @records.first.created_at.to_s(:rfc822)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_allow_custom_feed_options
|
||||
rss_feed_for @records, :feed => { :title => 'Custom Posts', :link => '/posts', :description => 'stuff', :language => 'en-gb', :ttl => '80' }
|
||||
|
||||
assert_select 'channel>title', 'Custom Posts'
|
||||
assert_select 'channel>link', '/posts'
|
||||
assert_select 'channel>description', 'stuff'
|
||||
assert_select 'channel>language', 'en-gb'
|
||||
assert_select 'channel>ttl', '80'
|
||||
end
|
||||
|
||||
def test_should_allow_custom_item_attributes
|
||||
rss_feed_for @records, :item => { :title => :name, :description => :body, :pub_date => :create_date, :link => :id }
|
||||
|
||||
assert_select 'item', 5 do
|
||||
assert_select 'title', :text => 'feed title (name)'
|
||||
assert_select 'description', '<p>feed description (body)</p>'
|
||||
assert_select 'pubDate', (@records.first.created_at - 5.minutes).to_s(:rfc822)
|
||||
assert_select 'link', '1'
|
||||
assert_select 'guid', '1'
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_allow_custom_item_attribute_blocks
|
||||
rss_feed_for @records, :item => { :title => lambda { |r| r.name }, :description => lambda { |r| r.body }, :pub_date => lambda { |r| r.create_date },
|
||||
:link => lambda { |r| "/#{r.created_at.to_i}" }, :guid => lambda { |r| r.created_at.to_i } }
|
||||
|
||||
assert_select 'item', 5 do
|
||||
assert_select 'title', :text => 'feed title (name)'
|
||||
assert_select 'description', '<p>feed description (body)</p>'
|
||||
assert_select 'pubDate', (@records.first.created_at - 5.minutes).to_s(:rfc822)
|
||||
end
|
||||
end
|
||||
|
||||
# note that assert_select isnt easily able to get elements that have xml namespaces (as it thinks they are
|
||||
# invalid html psuedo children), so we do some manual testing with the response body
|
||||
def test_should_allow_content_encoded_for_items
|
||||
rss_feed_for @records, :item => { :content_encoded => :full_html_body }
|
||||
|
||||
html_content = "<strong>Here is some <i>full</i> content, with out any excerpts</strong>"
|
||||
assert_equal 5, @response.body.scan("<![CDATA[#{html_content}]]>").size
|
||||
assert_select 'item', 5 do
|
||||
assert_select 'description + *', "<![CDATA[#{html_content}" # assert_select seems to strip the ending cdata tag
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_have_content_encoded_namespace_if_used
|
||||
rss_feed_for @records, :item => { :content_encoded => :full_html_body }
|
||||
assert_equal %[<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">\n],
|
||||
@response.body.grep(/<rss version="2\.0.*"/).first
|
||||
end
|
||||
|
||||
def test_should_have_normal_rss_root_without_content_encoded
|
||||
rss_feed_for @records
|
||||
assert_equal %[<rss version="2.0">\n],
|
||||
@response.body.grep(/<rss version="2\.0.*"/).first
|
||||
end
|
||||
|
||||
end
|
||||
64
backup.rails2.3/plugins/resource_feeder/test/test_helper.rb
Normal file
64
backup.rails2.3/plugins/resource_feeder/test/test_helper.rb
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
RAILS_ENV = 'test'
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), '../../../../config/environment.rb'))
|
||||
require 'action_controller/test_process'
|
||||
require 'breakpoint'
|
||||
require 'ostruct'
|
||||
|
||||
class Post
|
||||
attr_reader :id, :created_at
|
||||
def save; @id = 1; @created_at = Time.now.utc end
|
||||
def new_record?; @id.nil? end
|
||||
|
||||
[:title, :name].each do |attr_name|
|
||||
define_method attr_name do
|
||||
"feed title (#{attr_name})"
|
||||
end
|
||||
end
|
||||
|
||||
[:description, :body].each do |attr_name|
|
||||
define_method attr_name do
|
||||
"<p>feed description (#{attr_name})</p>"
|
||||
end
|
||||
end
|
||||
|
||||
def full_html_body
|
||||
"<strong>Here is some <i>full</i> content, with out any excerpts</strong>"
|
||||
end
|
||||
|
||||
def create_date
|
||||
@created_at - 5.minutes
|
||||
end
|
||||
end
|
||||
|
||||
class Test::Unit::TestCase
|
||||
include ResourceFeeder::Rss, ResourceFeeder::Atom
|
||||
|
||||
def render_feed(xml)
|
||||
@response = OpenStruct.new
|
||||
@response.headers = {'Content-Type' => 'text/xml'}
|
||||
@response.body = xml
|
||||
end
|
||||
|
||||
def rss_feed_for_with_ostruct(resources, options = {})
|
||||
render_feed rss_feed_for_without_ostruct(resources, options)
|
||||
end
|
||||
|
||||
def atom_feed_for_with_ostruct(resources, options = {})
|
||||
render_feed atom_feed_for_without_ostruct(resources, options)
|
||||
end
|
||||
|
||||
alias_method_chain :rss_feed_for, :ostruct
|
||||
alias_method_chain :atom_feed_for, :ostruct
|
||||
|
||||
def html_document
|
||||
@html_document ||= HTML::Document.new(@response.body, false, true)
|
||||
end
|
||||
|
||||
def posts_url
|
||||
"http://example.com/posts"
|
||||
end
|
||||
|
||||
def post_url(post)
|
||||
"http://example.com/posts/#{post.id}"
|
||||
end
|
||||
end
|
||||
5
backup.rails2.3/plugins/simple_ldap_authenticator/README
Normal file
5
backup.rails2.3/plugins/simple_ldap_authenticator/README
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
SimpleLdapAuthenticator
|
||||
=======================
|
||||
|
||||
Allows for simple authentication to an LDAP server with a minimum of
|
||||
configuration. See the RDoc for details.
|
||||
22
backup.rails2.3/plugins/simple_ldap_authenticator/Rakefile
Normal file
22
backup.rails2.3/plugins/simple_ldap_authenticator/Rakefile
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the simple_ldap_authenticator plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'lib'
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the simple_ldap_authenticator plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'SimpleLdapAuthenticator'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# Include hook code here
|
||||
#require 'simple_ldap_authenticator'
|
||||
|
|
@ -0,0 +1 @@
|
|||
# Install hook code here
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
# SimpleLdapAuthenticator
|
||||
#
|
||||
# This plugin supports both Ruby/LDAP and Net::LDAP, defaulting to Ruby/LDAP
|
||||
# if it is available. If both are installed and you want to force the use of
|
||||
# Net::LDAP, set SimpleLdapAuthenticator.ldap_library = 'net/ldap'.
|
||||
|
||||
# Allows for easily authenticating users via LDAP (or LDAPS). If authenticating
|
||||
# via LDAP to a server running on localhost, you should only have to configure
|
||||
# the login_format.
|
||||
#
|
||||
# Can be configured using the following accessors (with examples):
|
||||
# * login_format = '%s@domain.com' # Active Directory, OR
|
||||
# * login_format = 'cn=%s,cn=users,o=organization,c=us' # Other LDAP servers
|
||||
# * servers = ['dc1.domain.com', 'dc2.domain.com'] # names/addresses of LDAP servers to use
|
||||
# * use_ssl = true # for logging in via LDAPS
|
||||
# * port = 3289 # instead of 389 for LDAP or 636 for LDAPS
|
||||
# * logger = RAILS_DEFAULT_LOGGER # for logging authentication successes/failures
|
||||
#
|
||||
# The class is used as a global variable, you are not supposed to create an
|
||||
# instance of it. For example:
|
||||
#
|
||||
# require 'simple_ldap_authenticator'
|
||||
# SimpleLdapAuthenticator.servers = %w'dc1.domain.com dc2.domain.com'
|
||||
# SimpleLdapAuthenticator.use_ssl = true
|
||||
# SimpleLdapAuthenticator.login_format = '%s@domain.com'
|
||||
# SimpleLdapAuthenticator.logger = RAILS_DEFAULT_LOGGER
|
||||
# class LoginController < ApplicationController
|
||||
# def login
|
||||
# return redirect_to(:action=>'try_again') unless SimpleLdapAuthenticator.valid?(params[:username], params[:password])
|
||||
# session[:username] = params[:username]
|
||||
# end
|
||||
# end
|
||||
class SimpleLdapAuthenticator
|
||||
class << self
|
||||
@servers = ['127.0.0.1']
|
||||
@use_ssl = false
|
||||
@login_format = '%s'
|
||||
attr_accessor :servers, :use_ssl, :port, :login_format, :logger, :connection, :ldap_library
|
||||
|
||||
# Load the required LDAP library, either 'ldap' or 'net/ldap'
|
||||
def load_ldap_library
|
||||
return if @ldap_library_loaded
|
||||
if ldap_library
|
||||
if ldap_library == 'net/ldap'
|
||||
require 'net/ldap'
|
||||
else
|
||||
require 'ldap'
|
||||
require 'ldap/control'
|
||||
end
|
||||
else
|
||||
begin
|
||||
require 'ldap'
|
||||
require 'ldap/control'
|
||||
ldap_library = 'ldap'
|
||||
rescue LoadError
|
||||
require 'net/ldap'
|
||||
ldap_library = 'net/ldap'
|
||||
end
|
||||
end
|
||||
@ldap_library_loaded = true
|
||||
end
|
||||
|
||||
# The next LDAP server to which to connect
|
||||
def server
|
||||
servers[0]
|
||||
end
|
||||
|
||||
# The connection to the LDAP server. A single connection is made and the
|
||||
# connection is only changed if a server returns an error other than
|
||||
# invalid password.
|
||||
def connection
|
||||
return @connection if @connection
|
||||
load_ldap_library
|
||||
@connection = if ldap_library == 'net/ldap'
|
||||
Net::LDAP.new(:host=>server, :port=>(port), :encryption=>(:simple_tls if use_ssl))
|
||||
else
|
||||
(use_ssl ? LDAP::SSLConn : LDAP::Conn).new(server, port)
|
||||
end
|
||||
end
|
||||
|
||||
# The port to use. Defaults to 389 for LDAP and 636 for LDAPS.
|
||||
def port
|
||||
@port ||= use_ssl ? 636 : 389
|
||||
end
|
||||
|
||||
# Disconnect from current LDAP server and use a different LDAP server on the
|
||||
# next authentication attempt
|
||||
def switch_server
|
||||
self.connection = nil
|
||||
servers << servers.shift
|
||||
end
|
||||
|
||||
# Check the validity of a login/password combination
|
||||
def valid?(login, password)
|
||||
if ldap_library == 'net/ldap'
|
||||
connection.authenticate(login_format % login.to_s, password.to_s)
|
||||
begin
|
||||
if connection.bind
|
||||
logger.info("Authenticated #{login.to_s} by #{server}") if logger
|
||||
true
|
||||
else
|
||||
logger.info("Error attempting to authenticate #{login.to_s} by #{server}: #{connection.get_operation_result.code} #{connection.get_operation_result.message}") if logger
|
||||
switch_server unless connection.get_operation_result.code == 49
|
||||
false
|
||||
end
|
||||
rescue Net::LDAP::LdapError => error
|
||||
logger.info("Error attempting to authenticate #{login.to_s} by #{server}: #{error.message}") if logger
|
||||
switch_server
|
||||
false
|
||||
end
|
||||
else
|
||||
connection.unbind if connection.bound?
|
||||
begin
|
||||
connection.bind(login_format % login.to_s, password.to_s)
|
||||
connection.unbind
|
||||
logger.info("Authenticated #{login.to_s} by #{server}") if logger
|
||||
true
|
||||
rescue LDAP::ResultError => error
|
||||
connection.unbind if connection.bound?
|
||||
logger.info("Error attempting to authenticate #{login.to_s} by #{server}: #{error.message}") if logger
|
||||
switch_server unless error.message == 'Invalid credentials'
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# desc "Explaining what the task does"
|
||||
# task :simple_ldap_authenticator do
|
||||
# # Task goes here
|
||||
# end
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
require 'test/unit'
|
||||
|
||||
class SimpleLdapAuthenticatorTest < Test::Unit::TestCase
|
||||
# Replace this with your real tests.
|
||||
def test_this_plugin
|
||||
flunk
|
||||
end
|
||||
end
|
||||
2
backup.rails2.3/plugins/skinny_spec/.gitignore
vendored
Normal file
2
backup.rails2.3/plugins/skinny_spec/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
.DS_Store
|
||||
doc
|
||||
270
backup.rails2.3/plugins/skinny_spec/README.rdoc
Normal file
270
backup.rails2.3/plugins/skinny_spec/README.rdoc
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
= Skinny Spec
|
||||
|
||||
Skinny Spec is a collection of spec helper methods designed to help trim the fat and DRY up
|
||||
some of the bloat that sometimes results from properly specing your classes and templates.
|
||||
|
||||
== Requirements and Recommendations
|
||||
|
||||
Obviously you'll need to be using RSpec[http://github.com/dchelimsky/rspec/tree/master] and
|
||||
Rspec-Rails[http://github.com/dchelimsky/rspec-rails/tree/master] as your testing framework.
|
||||
|
||||
Skinny Spec was originally designed [and best enjoyed] if you're using
|
||||
Haml[http://github.com/nex3/haml/tree/master] and
|
||||
make_resourceful[http://github.com/rsl/make_resourceful/tree/master] but will default to
|
||||
ERb and a facsimile of Rails' default scaffolding [for the views and controllers, respectively]
|
||||
if Haml and/or make_resourceful are not available. I recommend using them though. :)
|
||||
|
||||
In addition, Skinny Spec uses Ruby2Ruby to make nicer expectation messages and you'll want to
|
||||
have that installed as well. It's not a dependency or anything but it <i>is</i> highly
|
||||
recommended.
|
||||
|
||||
== Setup
|
||||
|
||||
Once you've installed the plugin in your app's vendor/plugins folder, you're ready to rock!
|
||||
Skinny Spec includes itself into the proper RSpec classes so there's no configuration on your
|
||||
part. Sweet!
|
||||
|
||||
== Usage
|
||||
|
||||
The simplest way to use Skinny Specs is to generate a resource scaffold:
|
||||
|
||||
script/generate skinny_scaffold User
|
||||
|
||||
This command takes the usual complement of attribute definitions like
|
||||
<tt>script/generate scaffold</tt>. Then have a look at the generated files (particularly the
|
||||
specs) to see what's new and different with Skinny Spec.
|
||||
|
||||
=== Controller Specs
|
||||
|
||||
Let's look at the controller specs.
|
||||
|
||||
describe UsersController do
|
||||
describe "GET :index" do
|
||||
before(:each) do
|
||||
@users = stub_index(User)
|
||||
end
|
||||
|
||||
it_should_find_and_assign :users
|
||||
it_should_render :template, "index"
|
||||
end
|
||||
|
||||
# ...
|
||||
|
||||
describe "POST :create" do
|
||||
describe "when successful" do
|
||||
before(:each) do
|
||||
@user = stub_create(User)
|
||||
end
|
||||
|
||||
it_should_initialize_and_save :user
|
||||
it_should_redirect_to { user_url(@user) }
|
||||
end
|
||||
|
||||
# ...
|
||||
|
||||
First thing you should see is an example group for <tt>GET :index</tt>. That <tt>stub_index</tt> method there
|
||||
does a lot of work behind the curtain. I'll leave it up to you to check the documentation for it
|
||||
(and its brothers and sister methods like <tt>stub_new</tt>) but I will point out that the
|
||||
methods named <tt>stub_<i>controller_method</i></tt> should only be used for stubbing and
|
||||
mocking the main object of the method. To create mocks for other ancillary objects, please
|
||||
use <tt>stub_find_all</tt>, <tt>stub_find_one</tt>, and <tt>stub_initialize</tt>. The reason
|
||||
for this is because the former methods actually save us a step by defining an implicit
|
||||
controller method request. If you add a new method to your resource routing, you'll want to
|
||||
use the helper method <tt>define_request</tt> in those example groups to define an explicit
|
||||
request, like so:
|
||||
|
||||
describe "PUT :demote" do
|
||||
define_request { put :demote }
|
||||
|
||||
# ...
|
||||
end
|
||||
|
||||
You can also define a method called <tt>shared_request</tt> to "share" a
|
||||
<tt>define_request</tt> across nested describe blocks, like so:
|
||||
|
||||
describe "POST :create" do
|
||||
def shared_request
|
||||
post :create
|
||||
end
|
||||
|
||||
describe "when successful" do
|
||||
# ...
|
||||
end
|
||||
|
||||
describe "when unsuccessful" do
|
||||
# ...
|
||||
end
|
||||
end
|
||||
|
||||
Note: When you're adding longer, more complicated controller specs you can still leverage
|
||||
implicit and explicit requests by calling <tt>do_request</tt> in your spec as in the following
|
||||
example:
|
||||
|
||||
# Note this controller is UsersController and _not_ CategoriesController
|
||||
# and that loading the categories isn't part of the default actions
|
||||
# and cannot use the <tt>stub_<i>controller_method</i></tt> helpers
|
||||
# [which create implicit requests based on the controller method in the name]
|
||||
# but uses <tt>stub_find_all</tt> instead
|
||||
describe "GET :new" do
|
||||
before(:each) do
|
||||
@user = stub_new(User)
|
||||
@categories = stub_find_all(Category)
|
||||
end
|
||||
|
||||
# ...
|
||||
|
||||
it "should preload categories" do
|
||||
Category.should_receive(:find).with(:all)
|
||||
do_request
|
||||
end
|
||||
|
||||
it "should assign @categories" do
|
||||
do_request
|
||||
assigns[:categories].should == @categories
|
||||
end
|
||||
end
|
||||
|
||||
Finally we get to the meat of the spec and of Skinny Specs itself: the actual expectations.
|
||||
The first thing you'll notice is the use of example group (read: "describe" block) level methods
|
||||
instead of the usual example (read: "it") blocks. Using this helper at the example group level
|
||||
saves us three lines over using an example block. (If this isn't significant to you, this is
|
||||
probably the wrong plugin for you as well. Sorry.) Note that none of these methods use the
|
||||
instance variables defined in the "before" block because they are all nil at the example block
|
||||
level. Let's look at a sample method to see how it works:
|
||||
|
||||
it_should_find_and_assign :users
|
||||
|
||||
This actually wraps two different expectations: one that <tt>User.should_receive(:find).with(:all)</tt>
|
||||
and another that the instance variable <tt>@users</tt> is assigned with the return value from that finder call.
|
||||
If you need to add more detailed arguments to the find, you can easily break this into two different
|
||||
expectations like:
|
||||
|
||||
it_should_find :users, :limit => 2
|
||||
it_should_assign :users
|
||||
|
||||
See the documentation for the <tt>it_should_find</tt> for more information. You might have guessed that
|
||||
<tt>it_should_initialize_assign</tt> and <tt>it_should_render_template</tt> work in a similar
|
||||
fashion and you'd be right. Again, see the documentation for these individual methods for more
|
||||
information. Lots of information in those docs.
|
||||
|
||||
A useful helper method that doesn't appear in any of the scaffolding is <tt>with_default_restful_actions</tt>
|
||||
which takes a block and evaluates it for each of the RESTful controller actions. Very useful for
|
||||
spec'ing that these methods redirect to the login page when the user isn't logged in, for example. This
|
||||
method is designed to be used inside an example like so:
|
||||
|
||||
describe "when not logged in" do
|
||||
it "should redirect all requests to the login page" do
|
||||
with_default_restful_actions do
|
||||
response.should redirect_to(login_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Before we're through with the controller specs, let me point out one more important detail. In
|
||||
order to use <tt>it_should_redirect_to</tt> we have to send the routing inside a block argument
|
||||
there so it can be evaluated in the example context instead of the example group, where it
|
||||
completely blows up. This methodology is used anywhere routing is referred to in a "skinny",
|
||||
example group level spec.
|
||||
|
||||
=== View Specs
|
||||
|
||||
Now let's move to the view specs!
|
||||
|
||||
describe "/users/form.html.haml" do
|
||||
before(:each) do
|
||||
@user = mock_and_assign(User, :stub => {
|
||||
:name => "foo",
|
||||
:birthday => 1.week.ago,
|
||||
:adult => false
|
||||
})
|
||||
end
|
||||
|
||||
it_should_have_form_for :user
|
||||
|
||||
it_should_allow_editing :user, :name
|
||||
it_should_allow_editing :user, :birthday
|
||||
it_should_allow_editing :user, :adult
|
||||
|
||||
it_should_link_to_show :user
|
||||
it_should_link_to { users_path }
|
||||
end
|
||||
|
||||
Like the special <tt>stub_index</tt> methods in the controller
|
||||
specs, the view specs have a shorthand mock and stub helpers: <tt>mock_and_assign</tt> and
|
||||
<tt>mock_and_assign_collection</tt>. These are well documented so please check them out.
|
||||
|
||||
There are also some really nice helper methods that I'd like point out. First is
|
||||
<tt>it_should_have_form_for</tt>. This is a really good convenience wrapper that basically wraps
|
||||
the much longer:
|
||||
|
||||
it "should use form_for to generate the proper form action and options" do
|
||||
template.should_receive(:form_for).with(@user)
|
||||
do_render
|
||||
end
|
||||
|
||||
Next up is the <tt>it_should_allow_editing</tt> helper. I love this method the most because it
|
||||
really helps DRY up that view spec while at the same time being amazingly unbrittle. Instead of
|
||||
creating an expectation for a specific form element, this method creates a generalized expectation
|
||||
that there's a form element with the <tt>name</tt> attribute set in such away that it will
|
||||
generate the proper <tt>params</tt> to use in the controller to edit or create the instance.
|
||||
Check out the docs and the source for more information on this. Also check out
|
||||
<tt>it_should_have_form_element_for</tt> which is roughly equivalent for those times when you use
|
||||
<tt>form_tag</tt> instead.
|
||||
|
||||
Finally let's look at those <tt>it_should_link_to_<i>controller_method</i></tt> helpers.
|
||||
These methods (and there's one each for the controller methods
|
||||
<tt>new</tt>, <tt>edit</tt>, <tt>show</tt>, and <tt>delete</tt>) point to instance variables
|
||||
which you should be created in the "before" blocks with <tt>mock_and_assign</tt>. The other is
|
||||
<tt>it_should_allow_editing</tt> which is likewise covered extensively in the documentation and
|
||||
I will just point out here that, like <tt>it_should_link_to_edit</tt> and such, it takes a
|
||||
symbol for the name of the instance variable it refers to and <i>additionally</i> takes
|
||||
a symbol for the name of the attribute to be edited.
|
||||
|
||||
Also note that, when constructing a long form example, instead of defining an instance variable
|
||||
for the name of the template and calling <tt>render @that_template</tt> you can simply call
|
||||
<tt>do_render</tt> which takes the name of the template from the outermost example group where
|
||||
it is customarily stated.
|
||||
|
||||
=== Model Specs
|
||||
|
||||
Skinny Spec adds a matcher for the various ActiveRecord associations. On the example group level
|
||||
you call them like:
|
||||
|
||||
it_should_belong_to :manager
|
||||
it_should_have_many :clients
|
||||
|
||||
Within an example you can call them on either the class or the instance setup in the
|
||||
"before" block. These are equivalent:
|
||||
|
||||
@user.should belong_to(:group)
|
||||
User.should belong_to(:group)
|
||||
|
||||
I've also added some very basic validation helpers like <tt>it_should_validate_presence_of</tt>,
|
||||
<tt>it_should_validate_uniqueness_of</tt>, <tt>it_should_not_mass_assign</tt>. Please consult
|
||||
the documentation for more information.
|
||||
|
||||
== Miscellaneous Notes
|
||||
|
||||
In the scaffolding, I have used my own idiomatic Rails usage:
|
||||
|
||||
* All controller actions which use HTML forms [<tt>new</tt>, <tt>edit</tt>, etc] use a shared
|
||||
form and leverage <tt>form_for</tt> to its fullest by letting it create the appropriate
|
||||
action and options.
|
||||
* Some instances where you might expect link_to are button_to. This is to provide a common
|
||||
interface element which can be styled the same instead of a mishmash of links and buttons and
|
||||
inputs everywhere. To take full advantage of this, I usually override many of Rails' default
|
||||
helpers with custom ones that all use actual HTML <tt>BUTTON</tt> elements which are much
|
||||
easier to style than "button" typed <tt>INPUT</tt>. I've provided a text file in the
|
||||
"additional" folder of this plugin which you can use in your ApplicationHelper. (I also
|
||||
provide an optional override helper for the <tt>label</tt> method which uses
|
||||
<tt>#titleize</tt> instead of <tt>humanize</tt> for stylistic reasons).
|
||||
* Probably more that I can't think of.
|
||||
|
||||
== Credits and Thanks
|
||||
|
||||
Sections of this code were taken from or inspired by Rick Olsen's
|
||||
rspec_on_rails_on_crack[http://github.com/technoweenie/rspec_on_rails_on_crack/tree/master].
|
||||
Also thanks and props to Hampton Catlin and Nathan Weizenbaum for the lovely and imminently useable
|
||||
Haml and make_resourceful. Also also praises and glory to David Chelimsky and the Rspec crew.
|
||||
|
||||
Also thanks to Don Petersen, Nicolas Mérouze, Mikkel Malmberg, and Brandan Lennox for their suggestions and fixes.
|
||||
11
backup.rails2.3/plugins/skinny_spec/Rakefile
Normal file
11
backup.rails2.3/plugins/skinny_spec/Rakefile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
require 'rake'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Generate documentation for the Skinny Spec plugin'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = 'Skinny Spec'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README.rdoc')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
# Please insert these into your ApplicationHelper
|
||||
|
||||
# Replacement for Rails' default submit_tag helper
|
||||
# using HTML button element rather than HTML input element
|
||||
def submit_tag(text, options = {})
|
||||
content_tag :button, text, options.merge(:type => :submit)
|
||||
end
|
||||
|
||||
# Replacement for Rails' default button_to helper
|
||||
# using HTML button element rather than HTML input element
|
||||
def button_to(name, options = {}, html_options = {})
|
||||
html_options = html_options.stringify_keys
|
||||
convert_boolean_attributes!(html_options, %w( disabled ))
|
||||
|
||||
method_tag = ''
|
||||
if (method = html_options.delete('method')) && %w{put delete}.include?(method.to_s)
|
||||
method_tag = tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
|
||||
end
|
||||
|
||||
form_method = method.to_s == 'get' ? 'get' : 'post'
|
||||
|
||||
request_token_tag = ''
|
||||
if form_method == 'post' && protect_against_forgery?
|
||||
request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
|
||||
end
|
||||
|
||||
if confirm = html_options.delete("confirm")
|
||||
html_options["onclick"] = "return #{confirm_javascript_function(confirm)};"
|
||||
end
|
||||
|
||||
url = options.is_a?(String) ? options : self.url_for(options)
|
||||
name ||= url
|
||||
|
||||
html_options.merge!("type" => "submit", "value" => name)
|
||||
|
||||
"<form method=\"#{form_method}\" action=\"#{escape_once url}\" class=\"button-to\"><div>" +
|
||||
method_tag + content_tag("button", name, html_options) + request_token_tag + "</div></form>"
|
||||
end
|
||||
|
||||
# Replacement for Rails' default button_to_function helper
|
||||
# using HTML button element rather than HTML input element
|
||||
def button_to_function(name, *args, &block)
|
||||
html_options = args.extract_options!
|
||||
function = args[0] || ''
|
||||
|
||||
html_options.symbolize_keys!
|
||||
function = update_page(&block) if block_given?
|
||||
content_tag(:button, name, html_options.merge({
|
||||
:onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
|
||||
}))
|
||||
end
|
||||
|
||||
# Replacement for Rails' default label helper
|
||||
# using String#titleize rather than String#humanize
|
||||
def label(object_name, method, text = nil, options = {})
|
||||
text ||= method.to_s[].titleize
|
||||
super
|
||||
end
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
class SkinnyScaffoldGenerator < Rails::Generator::NamedBase
|
||||
attr_reader :controller_class_path, :controller_file_path, :controller_class_nesting,
|
||||
:controller_class_nesting_depth, :controller_class_name, :controller_underscore_name,
|
||||
:controller_plural_name, :template_language
|
||||
alias_method :controller_file_name, :controller_underscore_name
|
||||
alias_method :controller_singular_name, :controller_file_name
|
||||
alias_method :controller_table_name, :controller_plural_name
|
||||
|
||||
default_options :skip_migration => false
|
||||
|
||||
def initialize(runtime_args, runtime_options = {})
|
||||
super
|
||||
|
||||
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@name.pluralize)
|
||||
@controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name)
|
||||
|
||||
if @controller_class_nesting.empty?
|
||||
@controller_class_name = @controller_class_name_without_nesting
|
||||
else
|
||||
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
|
||||
end
|
||||
end
|
||||
|
||||
def manifest
|
||||
record do |m|
|
||||
# Check for class naming collisions
|
||||
m.class_collisions controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}Helper"
|
||||
m.class_collisions class_path, "#{class_name}"
|
||||
|
||||
# # Controller, helper, and views directories
|
||||
m.directory File.join('app', 'views', controller_class_path, controller_file_name)
|
||||
m.directory File.join('spec', 'views', controller_class_path, controller_file_name)
|
||||
m.directory File.join('app', 'helpers', controller_class_path)
|
||||
m.directory File.join('spec', 'helpers', controller_class_path)
|
||||
m.directory File.join('app', 'controllers', controller_class_path)
|
||||
m.directory File.join('spec', 'controllers', controller_class_path)
|
||||
m.directory File.join('app', 'models', class_path)
|
||||
m.directory File.join('spec', 'models', class_path)
|
||||
|
||||
# Views
|
||||
@template_language = defined?(Haml) ? "haml" : "erb"
|
||||
%w{index show form}.each do |action|
|
||||
m.template "#{action}.html.#{template_language}",
|
||||
File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.#{template_language}")
|
||||
m.template "#{action}.html_spec.rb",
|
||||
File.join('spec/views', controller_class_path, controller_file_name, "#{action}.html.#{template_language}_spec.rb")
|
||||
end
|
||||
m.template "index_partial.html.#{template_language}",
|
||||
File.join('app/views', controller_class_path, controller_file_name, "_#{file_name}.html.#{template_language}")
|
||||
m.template 'index_partial.html_spec.rb',
|
||||
File.join('spec/views', controller_class_path, controller_file_name, "_#{file_name}.html.#{template_language}_spec.rb")
|
||||
|
||||
# Helper
|
||||
m.template 'helper.rb',
|
||||
File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb")
|
||||
m.template 'helper_spec.rb',
|
||||
File.join('spec/helpers', controller_class_path, "#{controller_file_name}_helper_spec.rb")
|
||||
|
||||
# Controller
|
||||
m.template 'controller.rb',
|
||||
File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
|
||||
m.template 'controller_spec.rb',
|
||||
File.join('spec/controllers', controller_class_path, "#{controller_file_name}_controller_spec.rb")
|
||||
|
||||
# Model
|
||||
m.template 'model.rb',
|
||||
File.join('app/models', class_path, "#{file_name}.rb")
|
||||
m.template 'model_spec.rb',
|
||||
File.join('spec/models', class_path, "#{file_name}_spec.rb")
|
||||
|
||||
# Routing
|
||||
m.route_resources controller_file_name
|
||||
|
||||
unless options[:skip_migration]
|
||||
m.migration_template(
|
||||
'migration.rb', 'db/migrate',
|
||||
:assigns => {
|
||||
:migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}",
|
||||
:attributes => attributes
|
||||
},
|
||||
:migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def banner
|
||||
"Usage: #{$0} skinny_scaffold ModelName [field:type, field:type]"
|
||||
end
|
||||
|
||||
def add_options!(opt)
|
||||
opt.separator ''
|
||||
opt.separator 'Options:'
|
||||
opt.on("--skip-migration",
|
||||
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
|
||||
end
|
||||
|
||||
def model_name
|
||||
class_name.demodulize
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
class <%= controller_class_name %>Controller < ApplicationController
|
||||
<%- if defined?(Resourceful::Maker) -%>
|
||||
make_resourceful do
|
||||
actions :all
|
||||
|
||||
# Let's get the most use from form_for and share a single form here!
|
||||
response_for :new, :edit do
|
||||
render :template => "<%= plural_name %>/form"
|
||||
end
|
||||
|
||||
response_for :create_fails, :update_fails do
|
||||
flash[:error] = "There was a problem!"
|
||||
render :template => "<%= plural_name %>/form"
|
||||
end
|
||||
end
|
||||
<%- else -%>
|
||||
# GET /<%= table_name %>
|
||||
# GET /<%= table_name %>.xml
|
||||
def index
|
||||
@<%= table_name %> = <%= class_name %>.find(:all)
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.xml { render :xml => @<%= table_name %> }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /<%= table_name %>/1
|
||||
# GET /<%= table_name %>/1.xml
|
||||
def show
|
||||
@<%= file_name %> = <%= class_name %>.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.erb
|
||||
format.xml { render :xml => @<%= file_name %> }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /<%= table_name %>/new
|
||||
# GET /<%= table_name %>/new.xml
|
||||
def new
|
||||
@<%= file_name %> = <%= class_name %>.new
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :template => "<%= plural_name %>/form" }
|
||||
format.xml { render :xml => @<%= file_name %> }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /<%= table_name %>/1/edit
|
||||
def edit
|
||||
@<%= file_name %> = <%= class_name %>.find(params[:id])
|
||||
render :template => "<%= plural_name %>/form"
|
||||
end
|
||||
|
||||
# POST /<%= table_name %>
|
||||
# POST /<%= table_name %>.xml
|
||||
def create
|
||||
@<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
|
||||
|
||||
respond_to do |format|
|
||||
if @<%= file_name %>.save
|
||||
flash[:notice] = '<%= class_name %> was successfully created.'
|
||||
format.html { redirect_to(@<%= file_name %>) }
|
||||
format.xml { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> }
|
||||
else
|
||||
flash.now[:error] = '<%= class_name %> could not be created.'
|
||||
format.html { render :template => "<%= plural_name %>/form" }
|
||||
format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PUT /<%= table_name %>/1
|
||||
# PUT /<%= table_name %>/1.xml
|
||||
def update
|
||||
@<%= file_name %> = <%= class_name %>.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @<%= file_name %>.update_attributes(params[:<%= file_name %>])
|
||||
flash[:notice] = '<%= class_name %> was successfully updated.'
|
||||
format.html { redirect_to(@<%= file_name %>) }
|
||||
format.xml { head :ok }
|
||||
else
|
||||
flash.now[:error] = '<%= class_name %> could not be created.'
|
||||
format.html { render :template => "<%= plural_name %>/form" }
|
||||
format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /<%= table_name %>/1
|
||||
# DELETE /<%= table_name %>/1.xml
|
||||
def destroy
|
||||
@<%= file_name %> = <%= class_name %>.find(params[:id])
|
||||
@<%= file_name %>.destroy
|
||||
|
||||
respond_to do |format|
|
||||
flash[:notice] = '<%= class_name %> was successfully deleted.'
|
||||
format.html { redirect_to(<%= table_name %>_url) }
|
||||
format.xml { head :ok }
|
||||
end
|
||||
end
|
||||
<%- end -%>
|
||||
end
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
require File.dirname(__FILE__) + '/../spec_helper'
|
||||
|
||||
describe <%= controller_class_name %>Controller do
|
||||
describe "GET :index" do
|
||||
before(:each) do
|
||||
@<%= plural_name %> = stub_index(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_find_and_assign :<%= plural_name %>
|
||||
it_should_render_template "index"
|
||||
end
|
||||
|
||||
describe "GET :new" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_new(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_initialize_and_assign :<%= singular_name %>
|
||||
it_should_render_template "form"
|
||||
end
|
||||
|
||||
describe "POST :create" do
|
||||
describe "when successful" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_create(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_initialize_and_save :<%= singular_name %>
|
||||
it_should_set_flash :notice
|
||||
it_should_redirect_to { <%= singular_name %>_url(@<%= singular_name %>) }
|
||||
end
|
||||
|
||||
describe "when unsuccessful" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_create(<%= class_name %>, :return => :false)
|
||||
end
|
||||
|
||||
it_should_initialize_and_assign :<%= singular_name %>
|
||||
it_should_set_flash :error
|
||||
it_should_render_template "form"
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET :show" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_show(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_find_and_assign :<%= singular_name %>
|
||||
it_should_render_template "show"
|
||||
end
|
||||
|
||||
describe "GET :edit" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_edit(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_find_and_assign :<%= singular_name %>
|
||||
it_should_render_template "form"
|
||||
end
|
||||
|
||||
describe "PUT :update" do
|
||||
describe "when successful" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_update(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_find_and_update :<%= singular_name %>
|
||||
it_should_set_flash :notice
|
||||
it_should_redirect_to { <%= singular_name %>_url(@<%= singular_name %>) }
|
||||
end
|
||||
|
||||
describe "when unsuccessful" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_update(<%= class_name %>, :return => :false)
|
||||
end
|
||||
|
||||
it_should_find_and_assign :<%= singular_name %>
|
||||
it_should_set_flash :error
|
||||
it_should_render_template "form"
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE :destroy" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = stub_destroy(<%= class_name %>)
|
||||
end
|
||||
|
||||
it_should_find_and_destroy :<%= singular_name %>
|
||||
it_should_set_flash :notice
|
||||
it_should_redirect_to { <%= plural_name %>_url }
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<h1><%= singular_name %>.new_record? ? "New" : "Edit" %> <%= model_name %></h1>
|
||||
<%% form_for(@<%= singular_name %>) do |f| %>
|
||||
<div id="form_errors">
|
||||
<%%= f.error_messages %>
|
||||
</div>
|
||||
<%- if attributes.blank? -%>
|
||||
<p>Add your form elements here, please!</p>
|
||||
<%- else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
<p>
|
||||
<%%= f.label :<%= attribute.name %> %><br />
|
||||
<%%= f.<%= attribute.field_type %> :<%= attribute.name %> %>
|
||||
</p>
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
<div id="commands">
|
||||
<%%= submit_tag "Save" %>
|
||||
<div id="navigation_commands">
|
||||
<%% unless @<%= singular_name %>.new_record? -%>
|
||||
<%%= button_to "Show", <%= singular_name %>_path(@<%= singular_name %>), :method => "get", :title => "Show <%= singular_name %>. Unsaved changes will be lost." %>
|
||||
<%% end -%>
|
||||
<%%= button_to "Back to List", <%= plural_name %>_path, :class => "cancel", :method => "get", :title => "Return to <%= singular_name %> list without saving changes" %>
|
||||
</div>
|
||||
</div>
|
||||
<%% end -%>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
%h1== #{@<%= singular_name %>.new_record? ? "New" : "Edit"} #{<%= model_name %>}
|
||||
- form_for @<%= singular_name %> do |f|
|
||||
#form_errors= f.error_messages
|
||||
<% if attributes.blank? -%>
|
||||
%p Add your form elements here, please!
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
%p
|
||||
= f.label :<%= attribute.name %>
|
||||
= f.<%= attribute.field_type %> :<%= attribute.name %>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
#commands
|
||||
= submit_tag "Save"
|
||||
#navigation_commands
|
||||
- unless @<%= singular_name %>.new_record?
|
||||
= button_to "Show", <%= singular_name %>_path(@<%= singular_name %>), :method => "get", :title => "Show <%= singular_name %>. Unsaved changes will be lost."
|
||||
= button_to "Back to List", <%= plural_name %>_path, :class => "cancel", :method => "get", :title => "Return to <%= singular_name %> list without saving changes"
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../../spec_helper'
|
||||
|
||||
describe "<%= File.join(controller_class_path, controller_singular_name) %>/form.html.<%= template_language %>" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = mock_and_assign(<%= model_name %>, :stub => {
|
||||
<% if attributes.blank? -%>
|
||||
# Add your stub attributes and return values here like:
|
||||
# :name => "Foo", :address => "815 Oceanic Drive"
|
||||
<% else -%>
|
||||
<%- attributes.each_with_index do |attribute, index| -%>
|
||||
<%- case attribute.type when :string, :text -%>
|
||||
:<%= attribute.name %> => "foo"<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :integer, :float, :decimal -%>
|
||||
:<%= attribute.name %> => 815<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :boolean -%>
|
||||
:<%= attribute.name %> => false<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :date, :datetime, :time, :timestamp -%>
|
||||
:<%= attribute.name %> => 1.week.ago<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- else -%>
|
||||
:<%= attribute.name %> => nil<%= index < attributes.size - 1 ? "," : "" %> # Could not determine valid attribute
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
})
|
||||
end
|
||||
|
||||
it_should_have_form_for :<%= singular_name %>
|
||||
<% if attributes.blank? -%>
|
||||
# Add specs for editing attributes here, please! Like this:
|
||||
#
|
||||
# it_should_allow_editing :<%= singular_name %>, :foo
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
it_should_allow_editing :<%= singular_name %>, :<%= attribute.name %>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
|
||||
it_should_link_to_show :<%= singular_name %>
|
||||
it_should_link_to { <%= plural_name %>_path }
|
||||
end
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
module <%= controller_class_name %>Helper
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../spec_helper'
|
||||
|
||||
describe <%= controller_class_name %>Helper do
|
||||
# Add your specs here or remove this file completely, please!
|
||||
end
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<h1><%= model_name %> List</h1>
|
||||
<table>
|
||||
<%%- if @<%= plural_name %>.empty? -%>
|
||||
<tbody>
|
||||
<tr class="empty">
|
||||
<td>There are no <%= plural_name.humanize.downcase %></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<%%- else -%>
|
||||
<thead>
|
||||
<tr>
|
||||
<%- if attributes.blank? -%>
|
||||
<th><!-- Generic display column --></th>
|
||||
<%- else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
<th><%= attribute.name.titleize %></th>
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
<th class="show"><!-- "Show" link column --></th>
|
||||
<th class="edit"><!-- "Edit" link column --></th>
|
||||
<th class="delete"><!-- "Delete" link column --></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%%= render :partial => @<%= plural_name %> %>
|
||||
</tbody>
|
||||
<%%- end -%>
|
||||
</table>
|
||||
<div id="commands">
|
||||
<%%= button_to "New <%= singular_name.titleize %>", new_<%= singular_name %>_path, :method => "get" %>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
%h1 <%= model_name %> List
|
||||
%table
|
||||
- if @<%= plural_name %>.empty?
|
||||
%tbody
|
||||
%tr.empty
|
||||
%td== There are no <%= plural_name.humanize.downcase %>
|
||||
- else
|
||||
%thead
|
||||
%tr
|
||||
<% if attributes.blank? -%>
|
||||
%th= # Generic display column
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
%th <%= attribute.name.titleize %>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
%th.show= # 'Show' link column
|
||||
%th.edit= # 'Edit' link column
|
||||
%th.delete= # 'Delete' link column
|
||||
%tbody
|
||||
= render :partial => @<%= plural_name %>
|
||||
#commands
|
||||
= button_to "New <%= singular_name.titleize %>", new_<%= singular_name %>_path, :method => "get"
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../../spec_helper'
|
||||
|
||||
describe "<%= File.join(controller_class_path, controller_singular_name) %>/index.html.<%= template_language %>" do
|
||||
before(:each) do
|
||||
@<%= plural_name %> = mock_and_assign_collection(<%= model_name %>)
|
||||
template.stub! :render
|
||||
end
|
||||
|
||||
it "should render :partial => @<%= plural_name %>" do
|
||||
template.should_receive(:render).with(:partial => @<%= plural_name %>)
|
||||
do_render
|
||||
end
|
||||
|
||||
it_should_link_to_new :<%= singular_name %>
|
||||
end
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<tr class="<%%= cycle("odd", "even") %>">
|
||||
<% if attributes.blank? -%>
|
||||
<td><%= model_name %> #<%%= <%= singular_name %>.id %></td>
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
<td><%%=h <%= singular_name %>.<%= attribute.name %> %></td>
|
||||
<%- end %>
|
||||
<% end -%>
|
||||
<td class="show"><%%= button_to "Show", <%= singular_name %>, :method => "get" %></td>
|
||||
<td class="edit"><%%= button_to "Edit", edit_<%= singular_name %>_path(<%= singular_name %>), :method => "get" %></td>
|
||||
<td class="delete"><%%= button_to "Delete", <%= singular_name %>, :method => "delete" %></td>
|
||||
</tr>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
%tr{:class => cycle("odd", "even")}
|
||||
<% if attributes.blank? -%>
|
||||
%td== <%= model_name %> #{<%= singular_name %>.id}
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
%td=h <%= singular_name %>.<%= attribute.name %>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
%td.show= button_to "Show", <%= singular_name %>_path(<%= singular_name %>), :method => "get"
|
||||
%td.edit= button_to "Edit", edit_<%= singular_name %>_path(<%= singular_name %>), :method => "get"
|
||||
%td.delete= button_to "Delete", <%= singular_name %>, :method => "delete"
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../../spec_helper'
|
||||
|
||||
describe "<%= File.join(controller_class_path, controller_singular_name) %>/_<%= singular_name %>.html.<%= template_language %>" do
|
||||
before(:each) do
|
||||
@<%= singular_name %> = mock_and_assign(<%= model_name %>, :stub => {
|
||||
<% if attributes.blank? -%>
|
||||
# Add your stub attributes and return values here like:
|
||||
# :name => "Foo", :created_at => 1.week.ago, :updated_at => nil
|
||||
<% else -%>
|
||||
<%- attributes.each_with_index do |attribute, index| -%>
|
||||
<%- case attribute.type when :string, :text -%>
|
||||
:<%= attribute.name %> => "foo"<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :integer, :float, :decimal -%>
|
||||
:<%= attribute.name %> => 815<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :boolean -%>
|
||||
:<%= attribute.name %> => false<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :date, :datetime, :time, :timestamp -%>
|
||||
:<%= attribute.name %> => 1.week.ago<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- else -%>
|
||||
:<%= attribute.name %> => nil<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
})
|
||||
template.stub!(:<%= singular_name %>).and_return(@<%= singular_name %>)
|
||||
end
|
||||
|
||||
it_should_link_to_show :<%= singular_name %>
|
||||
it_should_link_to_edit :<%= singular_name %>
|
||||
it_should_link_to_delete :<%= singular_name %>
|
||||
end
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
class <%= migration_name %> < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :<%= table_name %>, :force => true do |t|
|
||||
<% attributes.each do |attribute| -%>
|
||||
t.column :<%= attribute.name %>, :<%= attribute.type %>
|
||||
<% end -%>
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :<%= table_name %>
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
class <%= class_name %> < ActiveRecord::Base
|
||||
end
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../spec_helper'
|
||||
|
||||
describe <%= class_name %> do
|
||||
def valid_attributes(args = {})
|
||||
{
|
||||
# Add valid attributes for building your model instances here!
|
||||
}.merge(args)
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
@<%= singular_name %> = <%= class_name %>.new
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
@<%= singular_name %>.destroy
|
||||
end
|
||||
|
||||
# Add your model specs here, please!
|
||||
# And don't forget about the association matchers built-in to skinny_spec like:
|
||||
#
|
||||
# it_should_have_many :foos
|
||||
# it_should_validate_presence_of :bar
|
||||
#
|
||||
# Check out the docs for more information.
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<h1>Show <%= model_name %></h1>
|
||||
<% if attributes.blank? -%>
|
||||
<p>Add your customized markup here, please!</p>
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
<p>
|
||||
<label><%= attribute.name.titleize %>:</label>
|
||||
<%%=h @<%= singular_name %>.<%= attribute.name %> %>
|
||||
</p>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
<div id="commands">
|
||||
<%%= button_to "Edit", edit_<%= singular_name %>_path(@<%= singular_name %>), :method => "get" %>
|
||||
<%%= button_to "Back to List", <%= plural_name %>_path, :method => "get" %>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
%h1== Show #{<%= model_name %>}
|
||||
<% if attributes.blank? -%>
|
||||
%p Add your customized markup here, please!
|
||||
<% else -%>
|
||||
<%- attributes.each do |attribute| -%>
|
||||
%p
|
||||
%label <%= attribute.name.titleize %>:
|
||||
=h @<%= singular_name %>.<%= attribute.name %>
|
||||
<%- end -%>
|
||||
<% end -%>
|
||||
#commands
|
||||
= button_to "Edit", edit_<%= singular_name %>_path(@<%= singular_name %>), :method => "get"
|
||||
= button_to "Back to List", <%= plural_name %>_path, :method => "get"
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../../spec_helper'
|
||||
|
||||
describe "<%= File.join(controller_class_path, controller_singular_name) %>/show.html.<%= template_language %>" do
|
||||
before(:each) do
|
||||
<% if attributes.blank? -%>
|
||||
@<%= singular_name %> = mock_and_assign(<%= model_name %>)
|
||||
<% else -%>
|
||||
@<%= singular_name %> = mock_and_assign(<%= model_name %>, :stub => {
|
||||
<%- attributes.each_with_index do |attribute, index| -%>
|
||||
<%- case attribute.type when :string, :text -%>
|
||||
:<%= attribute.name %> => "foo"<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :integer, :float, :decimal -%>
|
||||
:<%= attribute.name %> => 815<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :boolean -%>
|
||||
:<%= attribute.name %> => false<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- when :date, :datetime, :time, :timestamp -%>
|
||||
:<%= attribute.name %> => 1.week.ago<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- else -%>
|
||||
:<%= attribute.name %> => nil<%= index < attributes.size - 1 ? "," : "" %>
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
})
|
||||
<% end -%>
|
||||
end
|
||||
|
||||
# Add your specs here, please! But remember not to make them brittle
|
||||
# by specing specing specific HTML elements and classes.
|
||||
|
||||
it_should_link_to_edit :<%= singular_name %>
|
||||
it_should_link_to { <%= plural_name %>_path }
|
||||
end
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
module LuckySneaks
|
||||
# These methods are mostly just called internally by various other spec helper
|
||||
# methods but you're welcome to use them as needed in your own specs.
|
||||
module CommonSpecHelpers
|
||||
# Stubs out Time.now and returns value to use when comparing it. Example:
|
||||
#
|
||||
# time_now = stub_time_now
|
||||
# @foo.some_method_that_resets_updated_at
|
||||
# @foo.updated_at.should == time_now
|
||||
def stub_time_now
|
||||
returning Time.now do |now|
|
||||
Time.stub!(:now).and_return(now)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns class for the specified name. Example:
|
||||
#
|
||||
# class_for("foo") # => Foo
|
||||
def class_for(name)
|
||||
name.to_s.constantize
|
||||
rescue NameError
|
||||
name.to_s.pluralize.classify.constantize
|
||||
# Let any other error rise!
|
||||
end
|
||||
|
||||
# Returns instance variable for the specified name. Example:
|
||||
#
|
||||
# instance_for("foo") # => @foo
|
||||
def instance_for(name)
|
||||
instance_variable_get("@#{name.to_s.underscore}")
|
||||
end
|
||||
|
||||
# Wraps a matcher that checks if the receiver contains an <tt>A</tt> element (link)
|
||||
# whose <tt>href</tt> attribute is set to the specified path.
|
||||
def have_link_to(path)
|
||||
have_tag("a[href='#{path}']")
|
||||
end
|
||||
|
||||
# Returns dummy value for specified attribute based on the datatype expected for that
|
||||
# attribute.
|
||||
def dummy_value_for(instance, attribute)
|
||||
if datatype = instance.column_for_attribute(attribute)
|
||||
actual = instance.send(attribute)
|
||||
case datatype.type
|
||||
when :string, :text
|
||||
actual == "foo" ? "bar" : "food"
|
||||
when :integer, :float, :decimal
|
||||
actual == 108 ? 815 : 108
|
||||
when :boolean
|
||||
actual ? false : true
|
||||
when :date, :datetime, :time, :timestamp
|
||||
actual == 1.week.ago ? 2.years.ago : 1.week.ago
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns class description text
|
||||
def class_description_text
|
||||
if self.class.respond_to?(:description_text)
|
||||
# Old school
|
||||
self.class.description_text
|
||||
else
|
||||
# New school
|
||||
self.class.description
|
||||
end
|
||||
end
|
||||
|
||||
# Returns description text
|
||||
def self_description_text
|
||||
if respond_to?(:description_text)
|
||||
# Old school
|
||||
description_text
|
||||
else
|
||||
# New school
|
||||
description
|
||||
end
|
||||
end
|
||||
|
||||
def described_type
|
||||
self.class.described_type
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
module LuckySneaks
|
||||
module ControllerRequestHelpers # :nodoc:
|
||||
def self.included(base)
|
||||
base.extend ExampleGroupMethods
|
||||
end
|
||||
|
||||
private
|
||||
def define_implicit_request(method)
|
||||
@controller_method = method
|
||||
@implicit_request = case method
|
||||
when :index, :new, :show, :edit
|
||||
proc { get method, params }
|
||||
when :create
|
||||
proc { post :create, params }
|
||||
when :update
|
||||
proc { put :update, params }
|
||||
when :destroy
|
||||
proc { put :destroy, params }
|
||||
end
|
||||
end
|
||||
|
||||
def eval_request
|
||||
instance_eval &self.class.instance_variable_get("@the_request")
|
||||
rescue ArgumentError # missing block
|
||||
try_shared_request_definition
|
||||
end
|
||||
alias do_request eval_request
|
||||
|
||||
def try_shared_request_definition
|
||||
if defined?(shared_request) == "method"
|
||||
shared_request
|
||||
elsif @implicit_request
|
||||
try_implicit_request
|
||||
else
|
||||
error_message = "Could not determine request definition for 'describe' context. "
|
||||
error_message << "Please use define_request or define a shared_request."
|
||||
raise ArgumentError, error_message
|
||||
end
|
||||
end
|
||||
|
||||
def try_implicit_request
|
||||
@implicit_request.call
|
||||
end
|
||||
|
||||
def get_response(&block)
|
||||
eval_request
|
||||
block.call(response) if block_given?
|
||||
response
|
||||
end
|
||||
|
||||
module ExampleGroupMethods
|
||||
# Defines a request at the example group ("describe") level to be evaluated in the examples. Example:
|
||||
#
|
||||
# define_request { get :index, params }
|
||||
#
|
||||
# <b>Note:</b> The following methods all define implicit requests: <tt>stub_index</tt>, <tt>stub_new</tt>,
|
||||
# <tt>stub_create</tt>, <tt>stub_show</tt>, <tt>stub_edit</tt>, <tt>stub_update</tt>, and
|
||||
# <tt>stub_destroy</tt>. Using them in your <tt>before</tt> blocks will allow you to forego
|
||||
# defining explicit requests using <tt>define_request</tt>. See
|
||||
# LuckySneaks::ControllerStubHelpers for information on these methods.
|
||||
def define_request(&block)
|
||||
raise ArgumentError, "Must provide a block to define a request!" unless block_given?
|
||||
@the_request = block
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,571 @@
|
|||
$:.unshift File.join(File.dirname(__FILE__), "..")
|
||||
require "skinny_spec"
|
||||
|
||||
module LuckySneaks
|
||||
module ControllerSpecHelpers # :nodoc:
|
||||
include LuckySneaks::CommonSpecHelpers
|
||||
include LuckySneaks::ControllerRequestHelpers
|
||||
include LuckySneaks::ControllerStubHelpers
|
||||
|
||||
def self.included(base)
|
||||
base.extend ExampleGroupMethods
|
||||
base.extend ControllerRequestHelpers::ExampleGroupMethods
|
||||
end
|
||||
|
||||
# Evaluates the specified block for each of the RESTful controller methods.
|
||||
# This is useful to spec that all controller methods redirect when no user is
|
||||
# logged in.
|
||||
def with_default_restful_actions(params = {}, &block)
|
||||
{
|
||||
:get => :index,
|
||||
:get => :new,
|
||||
:post => :create
|
||||
}.each do |method_id, message|
|
||||
self.send method_id, message, params
|
||||
block.call
|
||||
end
|
||||
{
|
||||
:get => :edit,
|
||||
:put => :update,
|
||||
:delete => :destroy
|
||||
}.each do |method_id, message|
|
||||
if params[:before]
|
||||
params.delete(:before).call
|
||||
end
|
||||
# Presuming any id will do
|
||||
self.send method_id, message, params.merge(:id => 1)
|
||||
block.call
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def create_ar_class_expectation(name, method, argument = nil, options = {})
|
||||
args = []
|
||||
unless options.delete(:only_method)
|
||||
args << argument unless argument.nil?
|
||||
args << hash_including(options) unless options.empty?
|
||||
end
|
||||
method = options.delete(:find_method) if options[:find_method]
|
||||
if args.empty?
|
||||
class_for(name).should_receive(method).and_return(instance_for(name))
|
||||
else
|
||||
class_for(name).should_receive(method).with(*args).and_return(instance_for(name))
|
||||
end
|
||||
end
|
||||
|
||||
def create_positive_ar_instance_expectation(name, method, *args)
|
||||
instance = instance_for(name)
|
||||
if args.empty?
|
||||
instance.should_receive(method).and_return(true)
|
||||
else
|
||||
instance.should_receive(method).with(*args).and_return(true)
|
||||
end
|
||||
end
|
||||
|
||||
# These methods are designed to be used at the example group [read: "describe"] level
|
||||
# to simplify and DRY up common expectations.
|
||||
module ExampleGroupMethods
|
||||
# Creates an expectation that the controller method calls <tt>ActiveRecord::Base.find</tt>.
|
||||
# Examples:
|
||||
#
|
||||
# it_should_find :foos # => Foo.should_receive(:find).with(:all)
|
||||
# it_should_find :foos, :all # An explicit version of the above
|
||||
# it_should_find :foos, :conditions => {:foo => "bar"} # => Foo.should_receive(:find).with(:all, :conditions => {"foo" => "bar"}
|
||||
# it_should_find :foos, "joe", :method => :find_all_by_name # Foo.should_receive(:find_all_by_name).with("joe")
|
||||
# it_should_find :foo # => Foo.should_recieve(:find).with(@foo.id.to_s)
|
||||
# it_should_find :foo, :params => "id" # => Foo.should_receive(:find).with(params[:id].to_s)
|
||||
# it_should_find :foo, 2 # => Foo.should_receive(:find).with("2")
|
||||
# it_should_find :foo, "joe", :method => :find_by_name # => Foo.should_recieve(:find_by_name).with("joe")
|
||||
#
|
||||
# <b>Note:</b> All params (key and value) will be strings if they come from a form element and are handled
|
||||
# internally with this expectation.
|
||||
def it_should_find(name, *args)
|
||||
name_string = name.to_s
|
||||
name_message = if name_string == name_string.singularize
|
||||
"a #{name}"
|
||||
else
|
||||
name
|
||||
end
|
||||
it "should find #{name_message}" do
|
||||
options = args.extract_options!
|
||||
# Blech!
|
||||
argument = if param = params[options.delete(:params)]
|
||||
param.to_s
|
||||
else
|
||||
if args.first
|
||||
args.first
|
||||
elsif (instance = instance_variable_get("@#{name}")).is_a?(ActiveRecord::Base)
|
||||
instance.id.to_s
|
||||
else
|
||||
:all
|
||||
end
|
||||
end
|
||||
find_method = options.delete(:method) || :find
|
||||
create_ar_class_expectation name, find_method, argument, options
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_find</tt>. This creates an expectation that
|
||||
# the class never receives <tt>find</tt> at all.
|
||||
def it_should_not_find(name)
|
||||
name_string = name.to_s
|
||||
name_message = if name_string == name_string.singularize
|
||||
"a #{name}"
|
||||
else
|
||||
name
|
||||
end
|
||||
it "should not find #{name_message}" do
|
||||
if name_string == name_string.singularize
|
||||
class_for(name).should_not_receive(:find)
|
||||
else
|
||||
class_for(name).should_not_receive(:find).with(:all)
|
||||
end
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method calls <tt>ActiveRecord::Base.new</tt>.
|
||||
# Takes optional <tt>params</tt> for the initialization arguments. Example
|
||||
#
|
||||
# it_should_initialize :foo # => Foo.should_receive(:new)
|
||||
# it_should_initialize :foo, :params => :bar # => Foo.should_receive(:new).with(params[:bar])
|
||||
# it_should_initialize :foo, :bar => "baz" # => Foo.should_receive(:new).with(:bar => "baz")
|
||||
def it_should_initialize(name, options = {})
|
||||
it "should initialize a #{name}" do
|
||||
create_ar_class_expectation name, :new, params[options.delete(:params)], options
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_initialize</tt>. This creates an expectation
|
||||
# that the class never recieves <tt>new</tt> at all.
|
||||
def it_should_not_initialize(name)
|
||||
it "should initialize a #{name}" do
|
||||
class_for(name).should_not_receive(:new)
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method calls <tt>ActiveRecord::Base#save</tt> on the
|
||||
# named instance. Example:
|
||||
#
|
||||
# it_should_save :foo # => @foo.should_receive(:save).and_return(true)
|
||||
#
|
||||
# <b>Note:</b> This helper should not be used to spec a failed <tt>save</tt> call. Use <tt>it_should_assign</tt>
|
||||
# instead, to verify that the instance is captured in an instance variable for the inevitable re-rendering
|
||||
# of the form template.
|
||||
def it_should_save(name)
|
||||
it "should save the #{name}" do
|
||||
create_positive_ar_instance_expectation name, :save
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_update</tt>. This creates an expectation
|
||||
# that the instance never receives <tt>save</tt> at all.
|
||||
def it_should_not_save(name)
|
||||
it "should not save the #{name}" do
|
||||
instance_for(name).should_not_receive(:save)
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method calls <tt>ActiveRecord::Base#update_attributes</tt>
|
||||
# on the named instance. Takes optional argument for <tt>params</tt> to specify in the
|
||||
# expectation. Examples:
|
||||
#
|
||||
# it_should_update :foo # => @foo.should_receive(:update_attributes).and_return(true)
|
||||
# it_should_update :foo, :params => :bar # => @foo.should_receive(:update_attributes).with(params[:bar]).and_return(true)
|
||||
#
|
||||
# <b>Note:</b> This helper should not be used to spec a failed <tt>update_attributes</tt> call. Use
|
||||
# <tt>it_should_assign</tt> instead, to verify that the instance is captured in an instance variable
|
||||
# for the inevitable re-rendering of the form template.
|
||||
def it_should_update(name, options = {})
|
||||
it "should update the #{name}" do
|
||||
create_positive_ar_instance_expectation name, :update_attributes, params[name]
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_update</tt>. This creates an expectation
|
||||
# that the instance never receives <tt>update_attributes</tt> at all.
|
||||
def it_should_not_update(name)
|
||||
it "should not update the #{name}" do
|
||||
instance_for(name).should_not_receive(:update_attributes)
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method calls <tt>ActiveRecord::Base#destroy</tt> on the named
|
||||
# instance. Example:
|
||||
#
|
||||
# it_should_destroy :foo # => @foo.should_receive(:destroy).and_return(true)
|
||||
#
|
||||
# <b>Note:</b> This helper should not be used to spec a failed <tt>destroy</tt> call. Use
|
||||
# <tt>it_should_assign</tt> instead, if you need to verify that the instance is captured in an instance
|
||||
# variable if it is re-rendered somehow. This is probably a really edge use case.
|
||||
def it_should_destroy(name, options = {})
|
||||
it "should delete the #{name}" do
|
||||
create_positive_ar_instance_expectation name, :destroy
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_destroy</tt>. This creates an expectation
|
||||
# that the instance never receives <tt>destroy</tt> at all.
|
||||
def it_should_not_destroy(name)
|
||||
it "should not destroy the #{name}" do
|
||||
instance_for(name).should_not_receive(:destroy)
|
||||
eval_request
|
||||
end
|
||||
end
|
||||
|
||||
# Creates expectation[s] that the controller method should assign the specified
|
||||
# instance variables along with any specified values. Examples:
|
||||
#
|
||||
# it_should_assign :foo # => assigns[:foo].should == @foo
|
||||
# it_should_assign :foo => "bar" # => assigns[:foo].should == "bar"
|
||||
# it_should_assign :foo => :nil # => assigns[:foo].should be_nil
|
||||
# it_should_assign :foo => :not_nil # => assigns[:foo].should_not be_nil
|
||||
# it_should_assign :foo => :undefined # => controller.send(:instance_variables).should_not include("@foo")
|
||||
#
|
||||
# Very special thanks to Rick Olsen for the basis of this code. The only reason I even
|
||||
# redefine it at all is purely an aesthetic choice for specs like "it should foo"
|
||||
# over ones like "it foos".
|
||||
def it_should_assign(*names)
|
||||
names.each do |name|
|
||||
if name.is_a?(Symbol)
|
||||
it_should_assign name => name
|
||||
elsif name.is_a?(Hash)
|
||||
name.each do |key, value|
|
||||
it_should_assign_instance_variable key, value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Essentially shorthand for <tt>it_should_assign name => :nil</tt>. This method can take multiple
|
||||
# instance variable names, creating this shorthand for each name. See the docs for
|
||||
# <tt>it_should_assign</tt> for more information.
|
||||
def it_should_not_assign(*names)
|
||||
names.each do |name|
|
||||
# Assuming name is a symbol
|
||||
it_should_assign name => :nil
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps the separate expectations <tt>it_should_find</tt> and <tt>it_should_assign</tt>
|
||||
# for simple cases. If you need more control over the parameters of the find, this
|
||||
# isn't the right helper method and you should write out the two expectations separately.
|
||||
def it_should_find_and_assign(*names)
|
||||
names.each do |name|
|
||||
it_should_find name, :only_method => true
|
||||
it_should_assign name
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_find_and_assign</tt>. This creates an
|
||||
# expectation that the class never receives <tt>find</tt> at all and that
|
||||
# no matching instance variable is ever created.
|
||||
def it_should_not_find_and_assign(*names)
|
||||
names.each do |name|
|
||||
it_should_not_find name
|
||||
it_should_assign name => :nil
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps the separate expectations <tt>it_should_initialize</tt> and <tt>it_should_assign</tt>
|
||||
# for simple cases. If you need more control over the parameters of the initialization, this
|
||||
# isn't the right helper method and you should write out the two expectations separately.
|
||||
#
|
||||
# <b>Note:</b> This method is used for controller methods like <tt>new</tt>, where the instance
|
||||
# is initialized without being saved (this includes failed <tt>create</tt> requests).
|
||||
# If you want to spec that the controller method successfully saves the instance,
|
||||
# please use <tt>it_should_initialize_and_save</tt>.
|
||||
def it_should_initialize_and_assign(*names)
|
||||
names.each do |name|
|
||||
it_should_initialize name, :only_method => true
|
||||
it_should_assign name
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_initialize_and_assign</tt>. This creates an
|
||||
# expectation that the class never receives <tt>new</tt> at all and that
|
||||
# no matching instance variable is ever created.
|
||||
def it_should_not_initialize_and_assign(*names)
|
||||
names.each do |name|
|
||||
it_should_not_initialize name
|
||||
it_should_assign name => :nil
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps the separate expectations <tt>it_should_initialize</tt> and <tt>it_should_save</tt>
|
||||
# for simple cases. If you need more control over the parameters of the initialization, this
|
||||
# isn't the right helper method and you should write out the two expectations separately.
|
||||
#
|
||||
# <b>Note:</b> This method is used for controller methods like <tt>create</tt>, where the instance
|
||||
# is initialized and successfully saved. If you want to spec that the instance is created
|
||||
# but not saved, just use <tt>it_should_initialize_and_assign</tt>.
|
||||
def it_should_initialize_and_save(*names)
|
||||
names.each do |name|
|
||||
it_should_initialize name, :only_method => true
|
||||
it_should_save name
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps the separate expectations <tt>it_should_find</tt> and <tt>it_should_update</tt>
|
||||
# for simple cases. If you need more control over the parameters of the find, this
|
||||
# isn't the right helper method and you should write out the two expectations separately.
|
||||
#
|
||||
# <b>Note:</b> This method is used for controller methods like <tt>update</tt>, where the
|
||||
# instance is loaded from the database and successfully saved. If you want to spec that the
|
||||
# instance is found but not saved, just use <tt>it_should_find_and_assign</tt>.
|
||||
def it_should_find_and_update(*names)
|
||||
names.each do |name|
|
||||
it_should_find name, :only_method => true
|
||||
it_should_update name
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps the separate expectations <tt>it_should_find</tt> and <tt>it_should_destroy</tt>
|
||||
# for simple cases. If you need more control over the parameters of the find, this
|
||||
# isn't the right helper method and you should write out the two expectations separately.
|
||||
def it_should_find_and_destroy(*names)
|
||||
names.each do |name|
|
||||
it_should_find name, :only_method => true
|
||||
it_should_destroy name
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the specified collection (<tt>flash</tt>, <tt>session</tt>,
|
||||
# <tt>params</tt>, <tt>cookies</tt>) contains the specified key and value. To specify that
|
||||
# the collection should be set to <tt>nil</tt>, specify the value as :nil instead.
|
||||
def it_should_set(collection, key, value = nil, &block)
|
||||
it "should set #{collection}[:#{key}]#{' with ' + value.inspect if value}" do
|
||||
# Allow flash.now[:foo] to remain in the flash
|
||||
flash.stub!(:sweep) if collection == :flash
|
||||
eval_request
|
||||
if value
|
||||
if value == :nil
|
||||
self.send(collection)[key].should be_nil
|
||||
else
|
||||
self.send(collection)[key].should == value
|
||||
end
|
||||
elsif block_given?
|
||||
self.send(collection)[key].should == instance_eval(&block)
|
||||
else
|
||||
self.send(collection)[key].should_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps <tt>it_should_set :flash</tt>. To specify that the collection should be set
|
||||
# to <tt>nil</tt>, specify the value as :nil instead.
|
||||
def it_should_set_flash(name, value = nil, &block)
|
||||
it_should_set :flash, name, value, &block
|
||||
end
|
||||
|
||||
# Wraps <tt>it_should_set :flash, :nil</tt>.
|
||||
def it_should_not_set_flash(name)
|
||||
it_should_set :flash, name, :nil
|
||||
end
|
||||
|
||||
# Wraps <tt>it_should_set :session</tt>. To specify that the collection should be set
|
||||
# to <tt>nil</tt>, specify the value as :nil instead.
|
||||
def it_should_set_session(name, value = nil, &block)
|
||||
it_should_set :session, name, value, &block
|
||||
end
|
||||
|
||||
# Wraps <tt>it_should_set :session, :nil</tt>.
|
||||
def it_should_not_set_session(name)
|
||||
it_should_set :session, name, :nil
|
||||
end
|
||||
|
||||
# Wraps <tt>it_should_set :params</tt>. To specify that the collection should be set
|
||||
# to <tt>nil</tt>, specify the value as :nil instead.
|
||||
def it_should_set_params(name, value = nil, &block)
|
||||
it_should_set :params, name, value, &block
|
||||
end
|
||||
|
||||
# Wraps <tt>it_should_set :params, :nil</tt>.
|
||||
def it_should_not_set_params(name)
|
||||
it_should_set :params, name, :nil
|
||||
end
|
||||
|
||||
# Wraps <tt>it_should_set :cookies</tt>. To specify that the collection should be set
|
||||
# to <tt>nil</tt>, specify the value as :nil instead.
|
||||
def it_should_set_cookies(name, value = nil, &block)
|
||||
it_should_set :cookies, name, value, &block
|
||||
end
|
||||
|
||||
# Wraps <tt>it_should_set :cookies, :nil</tt>.
|
||||
def it_should_not_set_cookies(name)
|
||||
it_should_set :cookies, name, :nil
|
||||
end
|
||||
|
||||
# Wraps the various <tt>it_should_render_<i>foo</i></tt> methods:
|
||||
# <tt>it_should_render_template</tt>, <tt>it_should_render_partial</tt>,
|
||||
# <tt>it_should_render_xml</tt>, <tt>it_should_render_json</tt>,
|
||||
# <tt>it_should_render_formatted</tt>, and <tt>it_should_render_nothing</tt>.
|
||||
def it_should_render(render_method, *args)
|
||||
send "it_should_render_#{render_method}", *args
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method renders the specified template.
|
||||
# Accepts the following options which create additional expectations.
|
||||
#
|
||||
# <tt>:content_type</tt>:: Creates an expectation that the Content-Type header for the response
|
||||
# matches the one specified
|
||||
# <tt>:status</tt>:: Creates an expectation that the HTTP status for the response
|
||||
# matches the one specified
|
||||
def it_should_render_template(name, options = {})
|
||||
create_status_expectation options[:status] if options[:status]
|
||||
it "should render '#{name}' template" do
|
||||
eval_request
|
||||
response.should render_template(name)
|
||||
end
|
||||
create_content_type_expectation(options[:content_type]) if options[:content_type]
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method renders the specified partial.
|
||||
# Accepts the following options which create additional expectations.
|
||||
#
|
||||
# <tt>:content_type</tt>:: Creates an expectation that the Content-Type header for the response
|
||||
# matches the one specified
|
||||
# <tt>:status</tt>:: Creates an expectation that the HTTP status for the response
|
||||
# matches the one specified
|
||||
def it_should_render_partial(name, options = {})
|
||||
create_status_expectation options[:status] if options[:status]
|
||||
it "should render '#{name}' partial" do
|
||||
controller.expect_render(:partial => name)
|
||||
eval_request
|
||||
end
|
||||
create_content_type_expectation(options[:content_type]) if options[:content_type]
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method renders the specified record via <tt>to_xml</tt>.
|
||||
# Accepts the following options which create additional expectations.
|
||||
#
|
||||
# <tt>:content_type</tt>:: Creates an expectation that the Content-Type header for the response
|
||||
# matches the one specified
|
||||
# <tt>:status</tt>:: Creates an expectation that the HTTP status for the response
|
||||
# matches the one specified
|
||||
def it_should_render_xml(record = nil, options = {}, &block)
|
||||
it_should_render_formatted :xml, record, options, &block
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method renders the specified record via <tt>to_json</tt>.
|
||||
# Accepts the following options which create additional expectations.
|
||||
#
|
||||
# <tt>:content_type</tt>:: Creates an expectation that the Content-Type header for the response
|
||||
# matches the one specified
|
||||
# <tt>:status</tt>:: Creates an expectation that the HTTP status for the response
|
||||
# matches the one specified
|
||||
def it_should_render_json(record = nil, options = {}, &block)
|
||||
it_should_render_formatted :json, record, options, &block
|
||||
end
|
||||
|
||||
# Called internally by <tt>it_should_render_xml</tt> and <tt>it_should_render_json</tt>
|
||||
# but should not really be called much externally unless you have defined your own
|
||||
# formats with a matching <tt>to_foo</tt> method on the record.
|
||||
#
|
||||
# Which is probably never.
|
||||
def it_should_render_formatted(format, record = nil, options = {}, &block)
|
||||
create_status_expectation options[:status] if options[:status]
|
||||
it "should render #{format.inspect}" do
|
||||
if record.is_a?(Hash)
|
||||
options = record
|
||||
record = nil
|
||||
end
|
||||
if record.nil? && !block_given?
|
||||
raise ArgumentError, "it_should_render must be called with either a record or a block and neither was given."
|
||||
else
|
||||
if record
|
||||
pieces = record.to_s.split(".")
|
||||
record = instance_variable_get("@#{pieces.shift}")
|
||||
record = record.send(pieces.shift) until pieces.empty?
|
||||
end
|
||||
block ||= proc { record.send("to_#{format}") }
|
||||
get_response do |response|
|
||||
response.should have_text(block.call)
|
||||
end
|
||||
end
|
||||
end
|
||||
create_content_type_expectation(options[:content_type]) if options[:content_type]
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method returns a blank page. You'd already
|
||||
# know when and why to use this so I'm not typing it out.
|
||||
def it_should_render_nothing(options = {})
|
||||
create_status_expectation options[:status] if options[:status]
|
||||
it "should render :nothing" do
|
||||
get_response do |response|
|
||||
response.body.strip.should be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method redirects to the specified destination. Example:
|
||||
#
|
||||
# it_should_redirect_to { foos_url }
|
||||
#
|
||||
# <b>Note:</b> This method takes a block to evaluate the route in the example
|
||||
# context rather than the example group context.
|
||||
def it_should_redirect_to(hint = nil, &route)
|
||||
if hint.nil? && route.respond_to?(:to_ruby)
|
||||
hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip
|
||||
end
|
||||
it "should redirect to #{(hint || route)}" do
|
||||
eval_request
|
||||
response.should redirect_to(instance_eval(&route))
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_redirect_to</tt>.
|
||||
def it_should_not_redirect_to(hint = nil, &route)
|
||||
if hint.nil? && route.respond_to?(:to_ruby)
|
||||
hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip
|
||||
end
|
||||
it "should not redirect to #{(hint || route)}" do
|
||||
eval_request
|
||||
response.should_not redirect_to(instance_eval(&route))
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the controller method redirects back to the previous page
|
||||
def it_should_redirect_to_referer
|
||||
it "should redirect to the referring page" do
|
||||
request.env["HTTP_REFERER"] = "http://test.host/referer"
|
||||
eval_request
|
||||
response.should redirect_to("http://test.host/referer")
|
||||
end
|
||||
end
|
||||
alias it_should_redirect_to_referrer it_should_redirect_to_referer
|
||||
|
||||
private
|
||||
def it_should_assign_instance_variable(name, value)
|
||||
expectation_proc = case value
|
||||
when :nil
|
||||
proc { assigns[name].should be_nil }
|
||||
when :not_nil
|
||||
proc { assigns[name].should_not be_nil }
|
||||
when :undefined
|
||||
proc { controller.send(:instance_variables).should_not include("@{name}") }
|
||||
when Symbol
|
||||
if (instance_variable = instance_variable_get("@#{name}")).nil?
|
||||
proc { assigns[name].should_not be_nil }
|
||||
else
|
||||
proc { assigns[name].should == instance_variable }
|
||||
end
|
||||
else
|
||||
proc { assigns[name].should == value }
|
||||
end
|
||||
it "should #{value == :nil ? 'not ' : ''}assign @#{name}" do
|
||||
eval_request
|
||||
instance_eval &expectation_proc
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
module LuckySneaks # :nodoc:
|
||||
# These methods are designed to be used in your example <tt>before</tt> blocks to accomplish
|
||||
# a whole lot of functionality with just a tiny bit of effort. The methods which correspond
|
||||
# to the controller methods perform the most duties as they create the mock_model instances,
|
||||
# stub out all the necessary methods, and also create implicit requests to DRY up your spec
|
||||
# file even more. You are encouraged to use these methods to setup the basic calls for your
|
||||
# resources and only resort to the other methods when mocking and stubbing secondary objects
|
||||
# and calls.
|
||||
module ControllerStubHelpers
|
||||
# Stubs out <tt>find :all</tt> and returns a collection of <tt>mock_model</tt>
|
||||
# instances of that class. Accepts the following options:
|
||||
#
|
||||
# <b>:find_method</b>:: Method to use as finder call. Default is <tt>:find</tt>.
|
||||
# <b>Note:</b> When specifying the method, the call is stubbed
|
||||
# to accept any arguments. Caveat programmer.
|
||||
# <b>:format</b>:: Format of the request. Used to only add <tt>to_xml</tt> and
|
||||
# <tt>to_json</tt> when actually needed.
|
||||
# <b>:size</b>:: Number of instances to return in the result. Default is 3.
|
||||
# <b>:stub</b>:: Additional methods to stub on the instances
|
||||
#
|
||||
# Any additional options will be passed as arguments to the class find.
|
||||
# You will want to make sure to pass those arguments to the <tt>it_should_find</tt> spec as well.
|
||||
def stub_find_all(klass, options = {})
|
||||
returning(Array.new(options[:size] || 3){mock_model(klass)}) do |collection|
|
||||
stub_out klass, options.delete(:stub)
|
||||
if format = options.delete(:format)
|
||||
stub_formatted collection, format
|
||||
params[:format] = format
|
||||
end
|
||||
if find_method = options[:find_method]
|
||||
# Not stubbing specific arguments here
|
||||
# If you need more specificity, write a custom example
|
||||
klass.stub!(find_method).and_return(collection)
|
||||
else
|
||||
klass.stub!(:find).with(:all).and_return(collection)
|
||||
klass.stub!(:find).with(:all, hash_including(options)).and_return(collection)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_find_all</tt> but additionally defines an implicit request <tt>get :index</tt>.
|
||||
def stub_index(klass, options = {})
|
||||
define_implicit_request :index
|
||||
stub_find_all klass, options
|
||||
end
|
||||
|
||||
# Stubs out <tt>new</tt> method and returns a <tt>mock_model</tt> instance marked as a new record.
|
||||
# Accepts the following options:
|
||||
#
|
||||
# <b>:format</b>:: Format of the request. Used to only add <tt>to_xml</tt> and
|
||||
# <tt>to_json</tt> when actually needed.
|
||||
# <b>:stub</b>:: Additional methods to stub on the instances
|
||||
#
|
||||
# It also accepts some options used to stub out <tt>save</tt> with a specified <tt>true</tt>
|
||||
# or <tt>false</tt> but you should be using <tt>stub_create</tt> in that case.
|
||||
def stub_initialize(klass, options = {})
|
||||
returning mock_model(klass) do |member|
|
||||
stub_out member, options.delete(:stub)
|
||||
if format = options[:format]
|
||||
stub_formatted member, format
|
||||
params[:format] = format
|
||||
end
|
||||
klass.stub!(:new).and_return(member)
|
||||
if options[:params]
|
||||
klass.stub!(:new).with(hash_including(options[:params])).and_return(member)
|
||||
end
|
||||
if options[:stub_save]
|
||||
stub_ar_method member, :save, options[:return]
|
||||
else
|
||||
member.stub!(:new_record?).and_return(true)
|
||||
member.stub!(:id).and_return(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_initialize</tt> which additionally defines an implicit request <tt>get :new</tt>.
|
||||
def stub_new(klass, options = {})
|
||||
define_implicit_request :new
|
||||
stub_initialize klass, options
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_initialize</tt> which additionally defines an implicit request <tt>post :create</tt>.
|
||||
#
|
||||
# <b>Note:</b> If <tt>stub_create<tt> is provided an optional <tt>:params</tt> hash,
|
||||
# those params will be added to the example's <tt>params</tt> object.
|
||||
def stub_create(klass, options = {})
|
||||
define_implicit_request :create
|
||||
class_name = klass.name.underscore
|
||||
options[:params] ||= params[class_name]
|
||||
stub_initialize klass, options.merge(:stub_save => true)
|
||||
end
|
||||
|
||||
# Stubs out <tt>find</tt> and returns a single <tt>mock_model</tt>
|
||||
# instances of that class. Accepts the following options:
|
||||
#
|
||||
# <b>:find_method</b>:: Method to use as finder call. Default is <tt>:find</tt>.
|
||||
# <b>:format</b>:: Format of the request. Used to only add <tt>to_xml</tt> and
|
||||
# <tt>to_json</tt> when actually needed.
|
||||
# <b>:stub</b>:: Additional methods to stub on the instances
|
||||
# <b>:current_object</b>:: If set to true, <tt>find</tt> will set <tt>params[:id]</tt>
|
||||
# using the <tt>id</tt> of the <tt>mock_model</tt> instance
|
||||
# and use that value as an argument when stubbing <tt>find</tt>
|
||||
#
|
||||
# Any additional options will be passed as arguments to <tt>find</tt>.You will want
|
||||
# to make sure to pass those arguments to the <tt>it_should_find</tt> spec as well.
|
||||
#
|
||||
# <b>Note:</b> The option <tt>:stub_ar</tt> is used internally by <tt>stub_update</tt>
|
||||
# and <tt>stub_destroy</tt>. If you need to stub <tt>update_attributes</tt> or
|
||||
# <tt>destroy</tt> you should be using the aforementioned methods instead.
|
||||
def stub_find_one(klass, options = {})
|
||||
returning mock_model(klass) do |member|
|
||||
stub_out member, options.delete(:stub)
|
||||
if format = options.delete(:format)
|
||||
stub_formatted member, format
|
||||
params[:format] = format
|
||||
end
|
||||
if options.delete(:current_object)
|
||||
params[:id] = member.id
|
||||
if ar_stub = options.delete(:stub_ar)
|
||||
stub_ar_method member, ar_stub, options.delete(:return), options.delete(:update_params)
|
||||
end
|
||||
end
|
||||
if find_method = options.delete(:find_method)
|
||||
klass.stub!(find_method).and_return(member)
|
||||
else
|
||||
# Stubbing string and non-string just to be safe
|
||||
klass.stub!(:find).with(member.id).and_return(member)
|
||||
klass.stub!(:find).with(member.id.to_s).and_return(member)
|
||||
unless options.empty?
|
||||
klass.stub!(:find).with(member.id, hash_including(options)).and_return(member)
|
||||
klass.stub!(:find).with(member.id.to_s, hash_including(options)).and_return(member)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# <b>Note:</b> Use of this method with :child options (to mock
|
||||
# association) is deprecated. Please use <tt>stub_association</tt>.
|
||||
#
|
||||
# Same as <tt>stub_find_one</tt> but setups the instance as the parent
|
||||
# of the specified association. Example:
|
||||
#
|
||||
# stub_parent(Document, :child => :comments)
|
||||
#
|
||||
# This stubs <tt>Document.find</tt>, <tt>@document.comments</tt> (which
|
||||
# will return <tt>Comment</tt> class), as well as <tt>params[:document_id]</tt>.
|
||||
# This method is meant to be used in the controller for the specified child
|
||||
# (<tt>CommentsController</tt> in this instance) in situations like:
|
||||
#
|
||||
# def index
|
||||
# @document = Document.find(params[:document_id])
|
||||
# @comments = @document.comments.find(:all)
|
||||
# end
|
||||
def stub_parent(klass, options = {})
|
||||
returning stub_find_one(klass, options) do |member|
|
||||
params[klass.name.foreign_key] = member.id
|
||||
if offspring = options.delete(:child)
|
||||
puts "stub_parent with :child option has been marked for deprecation"
|
||||
puts "please use stub_association to create the mock instead"
|
||||
member.stub!(offspring).and_return(class_for(offspring))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_find_one</tt> which additionally defines an implicit request <tt>get :show</tt>.
|
||||
def stub_show(klass, options = {})
|
||||
define_implicit_request :show
|
||||
stub_find_one klass, options.merge(:current_object => true)
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_find_one</tt> which additionally defines an implicit request <tt>get :edit</tt>.
|
||||
def stub_edit(klass, options = {})
|
||||
define_implicit_request :edit
|
||||
stub_find_one klass, options.merge(:current_object => true)
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_find_one</tt> which additionally defines an implicit request <tt>put :update</tt>
|
||||
# and stubs out the <tt>update_attribute</tt> method on the instance as well.
|
||||
#
|
||||
# <b>Note:</b> If <tt>stub_update<tt> is provided an optional <tt>:params</tt> hash,
|
||||
# those params will be added to the example's <tt>params</tt> object.
|
||||
def stub_update(klass, options = {})
|
||||
define_implicit_request :update
|
||||
stub_find_one klass, options.merge(:current_object => true, :stub_ar => :update_attributes)
|
||||
end
|
||||
|
||||
# Alias for <tt>stub_find_one</tt> which additionally defines an implicit request <tt>delete :destroy</tt>
|
||||
# and stubs out the <tt>destroy</tt> method on the instance as well.
|
||||
def stub_destroy(klass, options = {})
|
||||
define_implicit_request :destroy
|
||||
stub_find_one klass, options.merge(:current_object => true, :stub_ar => :destroy)
|
||||
end
|
||||
|
||||
# Stubs <tt>to_xml</tt> or <tt>to_json</tt> respectively based on <tt>format</tt> argument.
|
||||
def stub_formatted(object, format)
|
||||
return unless format
|
||||
object.stub!("to_#{format}").and_return("#{object.class} formatted as #{format}")
|
||||
end
|
||||
|
||||
# Creates a mock object representing an association proxy, stubs the appropriate
|
||||
# method on the parent object and returns that association proxy.
|
||||
# Accepts the following option:
|
||||
#
|
||||
# <b>:stub</b>:: Additional methods to stub on the mock proxy object
|
||||
def stub_association(object, association, options = {})
|
||||
# I know options isn't implemented anywhere
|
||||
object_name = instance_variables.select{|name| instance_variable_get(name) == object}
|
||||
returning mock("Association proxy for #{object_name}.#{association}") do |proxy|
|
||||
stub_out proxy, options[:stub] if options[:stub]
|
||||
object.stub!(association).and_return(proxy)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Stubs out multiple methods. You shouldn't be calling this yourself and if you do
|
||||
# you should be able to understand the code yourself, right?
|
||||
def stub_out(object, stubs = {})
|
||||
return if stubs.nil?
|
||||
stubs.each do |method, value|
|
||||
if value
|
||||
object.stub!(method).and_return(value)
|
||||
else
|
||||
object.stub!(method)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Stubs out ActiveRecord::Base methods like #save, #update_attributes, etc
|
||||
# that may be called on a found or instantiated mock_model instance.
|
||||
def stub_ar_method(object, method, return_value, params = {})
|
||||
if params.blank?
|
||||
object.stub!(method).and_return(return_value ? false : true)
|
||||
else
|
||||
object.stub!(method).with(hash_including(params)).and_return(return_value ? false : true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,496 @@
|
|||
$:.unshift File.join(File.dirname(__FILE__), "..")
|
||||
require "skinny_spec"
|
||||
|
||||
module LuckySneaks
|
||||
# These methods are designed to be used in your example [read: "it"] blocks
|
||||
# to make your model specs a little more DRY. You might also be interested
|
||||
# in checking out the example block [read: "describe"] level versions in of these
|
||||
# methods which can DRY things up even more:
|
||||
# LuckySneaks::ModelSpecHelpers::ExampleGroupLevelMethods.
|
||||
#
|
||||
# Also check out the methods in LuckySneaks::ModelSpecHelpers::AssociationMatcher
|
||||
# for some helpful matcher helper methods to use with these methods if you want to spec
|
||||
# options on your association setups.
|
||||
module ModelSpecHelpers
|
||||
include LuckySneaks::CommonSpecHelpers
|
||||
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend ExampleGroupLevelMethods
|
||||
end
|
||||
|
||||
# These methods cannot be used alone but are used in compliment with the association
|
||||
# matchers in LuckySneaks::ModelSpecHelpers like <tt>have_many</tt>. Example:
|
||||
#
|
||||
# describe User do
|
||||
# it "should have many memberships" do
|
||||
# User.should have_many(:memberships)
|
||||
# end
|
||||
#
|
||||
# it "should have many sites through memberships" do
|
||||
# User.should have_many(:sites).through(:memberships)
|
||||
# end
|
||||
#
|
||||
# it "should belong to a manager" do
|
||||
# User.should belong_to(:manager).with_counter_cache
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# <b>Note:</b> To spec these sorts of options using the example block helpers like
|
||||
# <tt>it_should_have_many</tt>, just add them as options directly. This will use
|
||||
# <tt>with_options</tt> rather than any specific matcher helpers but will have the same
|
||||
# effects. Example:
|
||||
#
|
||||
# describe User do
|
||||
# it_should_have_many :sites, :through => :memberships
|
||||
# end
|
||||
class AssociationMatcher
|
||||
def initialize(associated, macro) # :nodoc:
|
||||
@associated = associated
|
||||
@macro = macro
|
||||
@options = {}
|
||||
end
|
||||
|
||||
def matches?(main_model) # :nodoc:
|
||||
unless main_model.respond_to?(:reflect_on_association)
|
||||
if main_model.class.respond_to?(:reflect_on_association)
|
||||
main_model = main_model.class
|
||||
else
|
||||
@not_model = main_model
|
||||
return false
|
||||
end
|
||||
end
|
||||
if @association = main_model.reflect_on_association(@associated)
|
||||
@options.all?{|k, v| @association.options[k] == v ||
|
||||
[@association.options[k]] == v} # Stupid to_a being obsoleted!
|
||||
end
|
||||
end
|
||||
|
||||
def failure_message # :nodoc:
|
||||
if @not_model
|
||||
" expected: #{@not_model} to be a subclass of ActiveRecord::Base class, but was not"
|
||||
elsif @association
|
||||
" expected: #{association_with(@options)}\n got: #{association_with(@association.options)}"
|
||||
else
|
||||
" expected: #{association_with(@options)}, but the association does not exist"
|
||||
end
|
||||
end
|
||||
|
||||
def negative_failure_message # :nodoc:
|
||||
if @association
|
||||
" expected: #{association_with(@options)}\n got: #{association_with(@association.options)}"
|
||||
else
|
||||
" expected: #{association_with(@options)} to not occur but it does"
|
||||
end
|
||||
end
|
||||
|
||||
# The following public methods are chainable extensions on the main matcher
|
||||
# Examples:
|
||||
#
|
||||
# Foo.should have_many(:bars).through(:foobars).with_dependent(:destroy)
|
||||
# Bar.should belong_to(:baz).with_class_name("Unbaz")
|
||||
def through(through_model)
|
||||
@options[:through] = through_model
|
||||
self
|
||||
end
|
||||
|
||||
def and_includes(included_models)
|
||||
@options[:include] = included_models
|
||||
self
|
||||
end
|
||||
|
||||
def and_extends(*modules)
|
||||
@options[:extends] = modules
|
||||
self
|
||||
end
|
||||
|
||||
def with_counter_cache(counter_cache = true)
|
||||
if counter_cache
|
||||
@options[:counter_cache] = counter_cache
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def uniq(*irrelevant_args)
|
||||
@options[:uniq] = true
|
||||
self
|
||||
end
|
||||
alias and_is_unique uniq
|
||||
alias with_unique uniq
|
||||
|
||||
def polymorphic(*irrelevant_args)
|
||||
@options[:polymorphic] = true
|
||||
self
|
||||
end
|
||||
alias and_is_polymorphic polymorphic
|
||||
alias with_polymorphic polymorphic
|
||||
|
||||
def as(interface)
|
||||
@options[:as] = interface
|
||||
end
|
||||
|
||||
# Use this to just specify the options as a hash.
|
||||
# Note: It will completely override any previously set options
|
||||
def with_options(options = {})
|
||||
options.each{|k, v| @options[k] = v}
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
# Takes care of methods like with_dependent(:destroy)
|
||||
def method_missing(method_id, *args, &block)
|
||||
method_name = method_id.to_s
|
||||
if method_name =~ /^with_(.*)/
|
||||
@options[$1.to_sym] = args
|
||||
self
|
||||
else
|
||||
super method_id, *args, &block
|
||||
end
|
||||
end
|
||||
|
||||
def association_with(options)
|
||||
option_string = (options.nil? || options.empty?) ? "" : options.inspect
|
||||
unless option_string.blank?
|
||||
option_string.sub! /^\{(.*)\}$/, ', \1'
|
||||
option_string.gsub! /\=\>/, ' => '
|
||||
end
|
||||
"#{@macro} :#{@associated}#{option_string}"
|
||||
end
|
||||
end
|
||||
|
||||
# Creates matcher that checks if the receiver has a <tt>belongs_to</tt> association
|
||||
# with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def belong_to(model)
|
||||
AssociationMatcher.new model, :belongs_to
|
||||
end
|
||||
|
||||
# Creates matcher that checks if the receiver has a <tt>have_one</tt> association
|
||||
# with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def have_one(model)
|
||||
AssociationMatcher.new model, :has_one
|
||||
end
|
||||
|
||||
# Creates matcher that checks if the receiver has a <tt>have_many</tt> association
|
||||
# with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def have_many(models)
|
||||
AssociationMatcher.new models, :has_many
|
||||
end
|
||||
|
||||
# Creates matcher that checks if the receiver has a <tt>have_and_belong_to_many</tt> association
|
||||
# with the specified model.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def have_and_belong_to_many(models)
|
||||
AssociationMatcher.new models, :has_and_belongs_to_many
|
||||
end
|
||||
|
||||
private
|
||||
def class_or_instance
|
||||
@model_spec_class_or_instance ||= class_for(described_type) || instance
|
||||
end
|
||||
|
||||
def instance
|
||||
@model_spec_instance ||= instance_for(described_type)
|
||||
end
|
||||
|
||||
# These methods are designed to be used at the example group [read: "describe"] level
|
||||
# to simplify and DRY up common expectations. Some of these methods are wrappers for
|
||||
# matchers which can also be used on the example level [read: within an "it" block]. See
|
||||
# LuckySneaks::ModelSpecHelpers for more information.
|
||||
#
|
||||
# <b>Note:</b> The validation matchers are only meant to be used for simple validation checking
|
||||
# not as a one-size-fits-all solution.
|
||||
module ExampleGroupLevelMethods
|
||||
# Creates an expectation that the current model being spec'd has a <tt>belong_to</tt>
|
||||
# association with the specified model. Accepts optional arguments which are appended to
|
||||
# the <tt>belong_to</tt> spec like this:
|
||||
#
|
||||
# it_should_belong_to :document, :counter_cache => true
|
||||
#
|
||||
# which is the same as writing out:
|
||||
#
|
||||
# it "should belong to document" do
|
||||
# Comment.should belong_to(:document).with_options(:counter_cache => true)
|
||||
# end
|
||||
#
|
||||
# If you want a more detailed spec description text, feel free to write this out in the long
|
||||
# form and use <tt>belong_to</tt> and its related matcher helpers.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def it_should_belong_to(model, options = {})
|
||||
it "should belong to a #{model}" do
|
||||
if options.empty?
|
||||
class_or_instance.should belong_to(model)
|
||||
else
|
||||
class_or_instance.should belong_to(model).with_options(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd has a <tt>have_one</tt>
|
||||
# association with the specified model. Accepts optional arguments which are appended to
|
||||
# the <tt>have_one</tt> spec like this:
|
||||
#
|
||||
# it_should_have_one :last_comment, :class_name => "Comment", :order => "created_at DESC"
|
||||
#
|
||||
# which is the same as writing out:
|
||||
#
|
||||
# it "should have one document" do
|
||||
# Document.should have_one(:last_comment).with_options(:class_name => "Comment", :order => "created_at DESC")
|
||||
# end
|
||||
#
|
||||
# If you want a more detailed spec description text, feel free to write this out in the long
|
||||
# form and use <tt>have_one</tt> and its related matcher helpers.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def it_should_have_one(model, options = {})
|
||||
it "should have one #{model}" do
|
||||
if options.empty?
|
||||
class_or_instance.should have_one(model)
|
||||
else
|
||||
class_or_instance.should have_one(model).with_options(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd has a <tt>have_many</tt>
|
||||
# association with the specified model. Accepts optional arguments which are appended to
|
||||
# the <tt>have_many</tt> spec like this:
|
||||
#
|
||||
# it_should_have_many :memberships, :through => :sites
|
||||
#
|
||||
# which is the same as writing out:
|
||||
#
|
||||
# it "should have many memberships" do
|
||||
# User.should have_many(:memberships).with_options(:through => :sites)
|
||||
# end
|
||||
#
|
||||
# If you want a more detailed spec description text, feel free to write this out in the long
|
||||
# form and use <tt>have_many</tt> and its related matcher helpers.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def it_should_have_many(models, options = {})
|
||||
it "should have many #{models}" do
|
||||
if options.empty?
|
||||
class_or_instance.should have_many(models)
|
||||
else
|
||||
class_or_instance.should have_many(models).with_options(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd has a <tt>have_and_belong_to_many</tt>
|
||||
# association with the specified model. Accepts optional arguments which are appended to
|
||||
# the <tt>have_and_belong_to_many</tt> spec like this:
|
||||
#
|
||||
# it_should_have_and_belong_to_many :documents, :include => :attachments
|
||||
#
|
||||
# which is the same as writing out:
|
||||
#
|
||||
# it "should belong to document" do
|
||||
# User.should have_and_belong_to_many(:documents).with_options(:include => :attachments)
|
||||
# end
|
||||
#
|
||||
# If you want a more detailed spec description text, feel free to write this out in the long
|
||||
# form and use <tt>have_and_belong_to_many</tt> and its related matcher helpers.
|
||||
#
|
||||
# <b>Note:</b> The argument should be a symbol as in the model's association definition
|
||||
# and not the model's class name.
|
||||
def it_should_have_and_belong_to_many(models, options = {})
|
||||
it "should have and belong to many #{models}" do
|
||||
if options.empty?
|
||||
class_or_instance.should have_and_belong_to_many(models)
|
||||
else
|
||||
class_or_instance.should have_and_belong_to_many(models).with_options(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that new instances of the model being spec'd
|
||||
# should initialise the specified attributes with a default value.
|
||||
#
|
||||
# it_should_default_attributes :status => 'new'
|
||||
#
|
||||
def it_should_default_attributes(hash_attribute_values)
|
||||
hash_attribute_values.each_pair do |a,v|
|
||||
it "should default #{a} attribute to #{v}" do
|
||||
class_or_instance.new.send(a).should == v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd <tt>validates_presence_of</tt>
|
||||
# the specified attribute. Takes an optional custom message to match the one in the model's
|
||||
# validation.
|
||||
def it_should_validate_presence_of(attribute, message = default_error_message(:blank))
|
||||
it "should not be valid if #{attribute} is blank" do
|
||||
instance.send "#{attribute}=", nil
|
||||
instance.errors_on(attribute).should include(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_validate_presence_of</tt>. See that method for more
|
||||
# details. You'd probably only be using this in a nested example block to compare that
|
||||
# one scenario validates presence and another does not (because of conditions in
|
||||
# <tt>:if/:unless</tt>).
|
||||
def it_should_not_validate_presence_of(attribute, message = default_error_message(:blank))
|
||||
it "should be valid if #{attribute} is blank" do
|
||||
instance.send "#{attribute}=", nil
|
||||
instance.errors_on(attribute).should_not include(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd <tt>validates_inclusion_of</tt>
|
||||
# the specified attribute. Takes an optional custom message to match the one in the model's
|
||||
# validation.
|
||||
def it_should_validate_inclusion_of(attribute, options = {}, message = default_error_message(:inclusion))
|
||||
it "should validate #{attribute} is in #{options[:in].to_s}" do
|
||||
# We specifically do not try to go below the range on String and character ranges because that problem set is unpredictable.
|
||||
lower = options[:in].first.respond_to?(:-) ? options[:in].first - 0.0001 : nil
|
||||
higher = options[:in].last.succ
|
||||
|
||||
instance.send "#{attribute}=", lower
|
||||
instance.errors_on(attribute).should include(message)
|
||||
|
||||
instance.send "#{attribute}=", higher
|
||||
instance.errors_on(attribute).should include(message)
|
||||
|
||||
instance.send "#{attribute}=", (lower+higher)/2
|
||||
instance.errors_on(attribute).should_not include(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd <tt>validates_numericality_of</tt>
|
||||
# the specified attribute. Takes an optional custom message to match the one in the model's
|
||||
# validation.
|
||||
def it_should_validate_numericality_of(attribute, message = default_error_message(:not_a_number))
|
||||
it "should validate #{attribute} is a numeric" do
|
||||
instance.send "#{attribute}=", "NaN"
|
||||
instance.errors_on(attribute).should include(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_validate_numericality_of</tt>. See that method for more
|
||||
# details. You'd probably only be using this in a nested example block to compare that
|
||||
# one scenario validates presence and another does not (because of conditions in
|
||||
# <tt>:if/:unless</tt>).
|
||||
def it_should_not_validate_numericality_of(attribute, message = default_error_message(:not_a_number))
|
||||
it "should not validate #{attribute} is a numeric" do
|
||||
instance.send "#{attribute}=", "NaN"
|
||||
instance.errors_on(attribute).should_not include(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd <tt>validates_confirmation_of</tt>
|
||||
# the specified attribute. Takes an optional custom message to match the one in the model's
|
||||
# validation.
|
||||
def it_should_validate_confirmation_of(attribute, message = default_error_message(:confirmation))
|
||||
it "should validate confirmation of #{attribute}" do
|
||||
dummy_value = dummy_value_for(instance, attribute) || "try a string"
|
||||
instance.send "#{attribute}=", dummy_value
|
||||
instance.send "#{attribute}_confirmation=", dummy_value.succ
|
||||
instance.errors_on(attribute).should include(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd <tt>validates_uniqueness_of</tt>
|
||||
# the specified attribute. Takes an optional custom message to match the one in the model's
|
||||
# validation.
|
||||
#
|
||||
# <b>Note:</b> This method will fail completely if <tt>valid_attributes</tt>
|
||||
# does not provide all the attributes needed to create a valid record.
|
||||
def it_should_validate_uniqueness_of(attribute, message = default_error_message(:taken))
|
||||
it "should validate uniqueness of #{attribute}" do
|
||||
previous_instance = instance.class.create!(valid_attributes)
|
||||
instance.attributes = valid_attributes
|
||||
instance.errors_on(attribute).should include(message)
|
||||
previous_instance.destroy
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_validate_uniqueness_of</tt>. See that method for more
|
||||
# details. You'd probably only be using this in a nested example block to compare that
|
||||
# one scenario validates presence and another does not (because of conditions in
|
||||
# <tt>:if/:unless</tt>).
|
||||
def it_should_not_validate_uniqueness_of(attribute, message = default_error_message(:taken))
|
||||
it "should not validate uniqueness of #{attribute}" do
|
||||
previous_instance = instance.class.create!(valid_attributes)
|
||||
instance.attributes = valid_attributes
|
||||
instance.errors_on(attribute).should_not include(message)
|
||||
previous_instance.destroy
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd accepts the specified values as
|
||||
# valid for the specified attribute. This is most likely used with <tt>validates_format_of</tt>
|
||||
# but there's nothing saying it couldn't be another validation.
|
||||
def it_should_accept_as_valid(attribute, *values)
|
||||
values.flatten.each do |value|
|
||||
value_inspect = case value
|
||||
when String : "'#{value}'"
|
||||
when NilClass : "nil"
|
||||
else value
|
||||
end
|
||||
it "should accept #{value_inspect} as a valid #{attribute}" do
|
||||
instance.send "#{attribute}=", value
|
||||
instance.errors_on(attribute).should == []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd does not accept the specified
|
||||
# values as valid for the specified attribute. This is most likely used with
|
||||
# <tt>validates_format_of</tt> but there's nothing saying it couldn't be another validation.
|
||||
# Takes an optional argument <tt>:message => "some custom error messsage"</tt> for
|
||||
# spec'ing the actual error message.
|
||||
def it_should_not_accept_as_valid(attribute, *values)
|
||||
options = values.extract_options!
|
||||
values.flatten.each do |value|
|
||||
value_inspect = case value
|
||||
when String : "'#{value}'"
|
||||
when NilClass : "nil"
|
||||
else value
|
||||
end
|
||||
it "should not accept #{value_inspect} as a valid #{attribute}" do
|
||||
instance.send "#{attribute}=", value
|
||||
if options[:message]
|
||||
instance.errors_on(attribute).should include(options[:message])
|
||||
else
|
||||
instance.should have_at_least(1).errors_on(attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the current model being spec'd doesn't allow mass-assignment
|
||||
# of the specified attribute.
|
||||
def it_should_not_mass_assign(attribute)
|
||||
it "should not allow mass-assignment of #{attribute}" do
|
||||
lambda {
|
||||
instance.send :attributes=, {attribute => dummy_value_for(instance, attribute)}
|
||||
}.should_not change(instance, attribute)
|
||||
end
|
||||
end
|
||||
|
||||
def default_error_message(attribute)
|
||||
if defined?(I18n)
|
||||
I18n.translate attribute, :scope => "activerecord.errors.messages"
|
||||
else
|
||||
ActiveRecord::Errors.default_error_messages[attribute]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,577 @@
|
|||
$:.unshift File.join(File.dirname(__FILE__), "..")
|
||||
require "skinny_spec"
|
||||
|
||||
module LuckySneaks
|
||||
# These methods are designed to be used in your example [read: "it"] blocks
|
||||
# to make your view specs less brittle and more DRY. You might also be interested
|
||||
# in checking out the example block [read: "describe"] level versions in of these
|
||||
# methods which can DRY things up even more:
|
||||
# LuckySneaks::ViewSpecHelpers::ExampleGroupLevelMethods.
|
||||
module ViewSpecHelpers
|
||||
include LuckySneaks::CommonSpecHelpers
|
||||
include LuckySneaks::ViewStubHelpers
|
||||
include ActionController::PolymorphicRoutes
|
||||
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend ExampleGroupLevelMethods
|
||||
end
|
||||
|
||||
# Wraps a matcher that checks if the receiver contains a <tt>FORM</tt> element with
|
||||
# its <tt>action</tt> attribute set to the specified path.
|
||||
def submit_to(path)
|
||||
have_tag("form[action=#{path}]")
|
||||
end
|
||||
|
||||
# Wraps a matcher that checks if the receiver contains any of several form elements
|
||||
# that would return sufficient named parameters to allow editing of the specified
|
||||
# attribute on the specified instance. Example:
|
||||
#
|
||||
# response.should allow_editing(@foo, "bar")
|
||||
#
|
||||
# can be satisfied by any of the following HTML elements:
|
||||
#
|
||||
# <input name="foo[bar]" type="text" />
|
||||
# <input name="foo[bar]" type="checkbox" />
|
||||
# <input name="foo[bar_ids][]" type="checkbox" />
|
||||
# <select name="foo[bar]"></select>
|
||||
# <textarea name="foo[bar]"></textarea>
|
||||
def allow_editing(instance, attribute)
|
||||
instance_name = instance.class.name.underscore.downcase
|
||||
column = instance.column_for_attribute(attribute)
|
||||
if column && [Date, Time].include?(column.klass)
|
||||
have_tag(
|
||||
"input[name='#{instance_name}[#{attribute}]'],
|
||||
select[name=?]", /#{instance_name}\[#{attribute}\(.*\)\]/
|
||||
)
|
||||
else
|
||||
have_tag(
|
||||
"input[type='text'][name='#{instance_name}[#{attribute}]'],
|
||||
input[type='password'][name='#{instance_name}[#{attribute}]'],
|
||||
select[name='#{instance_name}[#{attribute}]'],
|
||||
textarea[name='#{instance_name}[#{attribute}]'],
|
||||
input[type='checkbox'][name='#{instance_name}[#{attribute}]'],
|
||||
input[type='checkbox'][name='#{instance_name}[#{attribute.to_s.tableize.singularize}_ids][]'],
|
||||
input[type='radio'][name='#{instance_name}[#{attribute}]']"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps a matcher that checks if the receiver contains a <tt>FORM</tt> element
|
||||
# whose <tt>enctype</tt> attribute is set to <tt>"multipart/form-data"<tt>
|
||||
# and contains an <tt>INPUT</tt> element whose <tt>name</tt> attribute correlates
|
||||
# with the provided instance and attribute.
|
||||
def allow_uploading(instance, attribute)
|
||||
instance_name = instance.class.name.underscore.downcase
|
||||
have_tag("form[enctype='multipart/form-data'] input[type='file'][name='#{instance_name}[#{attribute}]']")
|
||||
end
|
||||
|
||||
# Wraps a matcher that checks if the receiver contains an <tt>A</tt> element (link)
|
||||
# whose <tt>href</tt> attribute is set to the specified path or a <tt>FORM</tt>
|
||||
# element whose <tt>action</tt> attribute is set to the specified path.
|
||||
def have_link_or_button_to(path)
|
||||
have_tag(
|
||||
"a[href='#{path}'],
|
||||
form[action='#{path}'] input,
|
||||
form[action='#{path}'] button"
|
||||
)
|
||||
end
|
||||
alias have_link_to have_link_or_button_to
|
||||
alias have_button_to have_link_or_button_to
|
||||
|
||||
# Wraps <tt>have_link_or_button_to new_polymorphic_path<tt> for the specified class which
|
||||
# corresponds with the <tt>new</tt> method of the controller.
|
||||
#
|
||||
# <b>Note:</b> This method may takes a string or symbol representing the model's name
|
||||
# to send to <tt>have_link_or_button_to_show</tt> or the model's name itself.
|
||||
def have_link_or_button_to_new(name)
|
||||
have_link_or_button_to new_polymorphic_path(name.is_a?(ActiveRecord::Base) ? name : class_for(name))
|
||||
end
|
||||
|
||||
# Wraps <tt>have_link_or_button_to polymorphic_path(instance)<tt> which
|
||||
# corresponds with the <tt>show</tt> method of the controller.
|
||||
def have_link_or_button_to_show(instance)
|
||||
path = polymorphic_path(instance)
|
||||
have_tag(
|
||||
"a[href='#{path}'],
|
||||
form[action='#{path}'][method='get'] input,
|
||||
form[action='#{path}'][method='get'] button,
|
||||
form[action='#{path}'] input[name='_method'][value='get'] + input,
|
||||
form[action='#{path}'] input[name='_method'][value='get'] + button"
|
||||
)
|
||||
end
|
||||
alias have_link_to_show have_link_or_button_to_show
|
||||
alias have_button_to_show have_link_or_button_to_show
|
||||
|
||||
# Wraps <tt>have_link_or_button_to edit_polymorphic_path(instance)<tt> which
|
||||
# corresponds with the <tt>edit</tt> method of the controller.
|
||||
def have_link_or_button_to_edit(instance)
|
||||
have_link_or_button_to edit_polymorphic_path(instance)
|
||||
end
|
||||
alias have_link_to_edit have_link_or_button_to_edit
|
||||
alias have_button_to_edit have_link_or_button_to_edit
|
||||
|
||||
# Wraps a matcher that checks if the receiver contains the HTML created by Rails'
|
||||
# <tt>button_to</tt> helper: to wit, a <tt>FORM</tt> element whose <tt>action</tt>
|
||||
# attribute is pointed at the <tt>polymorphic_path</tt> of the instance
|
||||
# and contains an <tt>INPUT</tt> named "_method" with a value of "delete".
|
||||
def have_button_to_delete(instance)
|
||||
path = polymorphic_path(instance)
|
||||
have_tag(
|
||||
"form[action='#{path}'] input[name='_method'][value='delete'] + input,
|
||||
form[action='#{path}'] input[name='_method'][value='delete'] + button,
|
||||
a[href=\"#{path}\"][onclick*=\"f.method = 'POST'\"][onclick*=\"m.setAttribute('name', '_method'); m.setAttribute('value', 'delete')\"]"
|
||||
)
|
||||
end
|
||||
|
||||
# Creates a <tt>mock_model</tt> instance and adds it to the <tt>assigns</tt> collection
|
||||
# using either the name passed as the first argument or the underscore version
|
||||
# of its class name. Accepts optional arguments to stub out additional methods
|
||||
# (and their return values) on the <tt>mock_model</tt> instance. Example:
|
||||
#
|
||||
# mock_and_assign(Foo, :stub => {:bar => "bar"})
|
||||
#
|
||||
# is the same as running <tt>assigns[:foo] = mock_model(Foo, :bar => "bar")</tt>.
|
||||
#
|
||||
# mock_and_assign(Foo, "special_foo", :stub => {:bar => "baz"})
|
||||
#
|
||||
# is the same as running <tt>assigns[:special_foo] = mock_model(Foo, :bar => "baz").
|
||||
#
|
||||
# <b>Note:</b> Adding to the assigns collection returns the object added, so this can
|
||||
# be chained a la <tt>@foo = mock_and_assign(Foo)</tt>.
|
||||
def mock_and_assign(klass, *args)
|
||||
options = args.extract_options!
|
||||
mocked = if options[:stub]
|
||||
self.respond_to?(:stub_model) ? stub_model(klass, options[:stub]) : mock_model(klass, options[:stub])
|
||||
else
|
||||
self.respond_to?(:stub_model) ? stub_model(klass) : mock_model(klass)
|
||||
end
|
||||
yield mocked if block_given?
|
||||
self.assigns[args.first || "#{klass}".underscore] = mocked
|
||||
end
|
||||
|
||||
# Creates an array of <tt>mock_model</tt> instances in the manner of
|
||||
# <tt>mock_and_assign</tt>. Accepts <tt>option[:size]</tt> which sets the size
|
||||
# of the array (default is 3).
|
||||
def mock_and_assign_collection(klass, *args)
|
||||
options = args.extract_options!
|
||||
return_me = Array.new(options[:size] || 3) do
|
||||
mocked = if options[:stub]
|
||||
self.respond_to?(:stub_model) ? stub_model(klass, options[:stub]) : mock_model(klass, options[:stub])
|
||||
else
|
||||
self.respond_to?(:stub_model) ? stub_model(klass) : mock_model(klass)
|
||||
end
|
||||
yield mocked if block_given?
|
||||
mocked
|
||||
end
|
||||
self.assigns[args.first || "#{klass}".tableize] = return_me
|
||||
end
|
||||
|
||||
private
|
||||
def do_render
|
||||
if @the_template
|
||||
render @the_template
|
||||
elsif File.exists?(File.join(RAILS_ROOT, "app/views", class_description_text))
|
||||
render class_description_text
|
||||
else
|
||||
error_message = "Cannot determine template for render. "
|
||||
error_message << "Please define @the_template in the before block "
|
||||
error_message << "or name your describe block so that it indicates the correct template."
|
||||
raise NameError, error_message
|
||||
end
|
||||
end
|
||||
|
||||
# These methods are designed to be used at the example group [read: "describe"] level
|
||||
# to simplify and DRY up common expectations. Most of these methods are wrappers for
|
||||
# matchers which can also be used on the example level [read: within an "it" block]. See
|
||||
# LuckySneaks::ViewSpecHelpers for more information.
|
||||
module ExampleGroupLevelMethods
|
||||
include LuckySneaks::CommonSpecHelpers
|
||||
|
||||
# Creates an expectation which calls <tt>submit_to</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a Proc to evaluate the route not simply a named route
|
||||
# helper, which would be undefined in the scope of the example block.
|
||||
def it_should_submit_to(hint = nil, &route)
|
||||
if hint.nil? && route.respond_to?(:to_ruby)
|
||||
hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip
|
||||
end
|
||||
it "should submit to #{(hint || route)}" do
|
||||
do_render
|
||||
response.should submit_to(instance_eval(&route))
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_submit_to</tt>. See that method for more
|
||||
# details.
|
||||
def it_should_not_submit_to(hint = nil, &route)
|
||||
if hint.nil? && route.respond_to?(:to_ruby)
|
||||
hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip
|
||||
end
|
||||
it "should not submit to #{(hint || route)}" do
|
||||
do_render
|
||||
response.should_not submit_to(instance_eval(&route))
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the template uses Rails' <tt>form_for</tt> to generate
|
||||
# the proper form action and method to create or update the specified object.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name to create the expectation for <tt>form_for</tt>
|
||||
# not an instance variable, which would be nil in the scope of the example block.
|
||||
# If you use namespacing for your <tt>form_for</tt>, you'll have to manually write out
|
||||
# a similar spec.
|
||||
def it_should_have_form_for(name, options = {})
|
||||
it "should have a form_for(@#{name})" do
|
||||
if options.empty?
|
||||
template.should_receive(:form_for).with(instance_for(name))
|
||||
else
|
||||
template.should_receive(:form_for).with(instance_for(name), hash_including(options))
|
||||
end
|
||||
do_render
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_have_form_for</tt>. See that method for more
|
||||
# details.
|
||||
def it_should_not_have_form_for(name, options = {})
|
||||
it "should not have a form_for(@#{name})" do
|
||||
if options.empty?
|
||||
template.should_not_receive(:form_for).with(instance_for(name))
|
||||
else
|
||||
template.should_not_receive(:form_for).with(instance_for(name), hash_including(options))
|
||||
end
|
||||
do_render
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation which calls <tt>allow_editing</tt> on the rendered
|
||||
# template for each attribute specified. See the docs for <tt>allow_editing</tt>
|
||||
# for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name to send to <tt>allow_editing</tt>
|
||||
# not an instance variable, which would be nil in the scope of the example block.
|
||||
def it_should_allow_editing(instance_name, *attributes)
|
||||
attributes.flatten!
|
||||
attributes.each do |attribute|
|
||||
it "should allow editing of @#{instance_name}##{attribute}" do
|
||||
do_render
|
||||
response.should allow_editing(instance_for(instance_name), attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_allow_editing</tt>. See that method for more
|
||||
# details.
|
||||
def it_should_not_allow_editing(instance_name, *attributes)
|
||||
attributes.flatten!
|
||||
attributes.each do |attribute|
|
||||
it "should not allow editing of @#{instance_name}##{attribute}" do
|
||||
do_render
|
||||
response.should_not allow_editing(instance_for(instance_name), attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation which calls <tt>allow_uploading</tt> on the rendered
|
||||
# template for each attribute specified. See the docs for <tt>allow_uploading</tt>
|
||||
# for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name to send to <tt>allow_uploading</tt>
|
||||
# not an instance variable, which would be nil in the scope of the example block.
|
||||
def it_should_allow_uploading(instance_name, *attributes)
|
||||
attributes.flatten!
|
||||
attributes.each do |attribute|
|
||||
it "should allow editing of @#{instance_name}##{attribute}" do
|
||||
do_render
|
||||
response.should allow_uploading(instance_for(instance_name), attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_allow_uploading</tt>. See that method for more
|
||||
# details.
|
||||
def it_should_not_allow_uploading(instance_name, *attributes)
|
||||
attributes.flatten!
|
||||
attributes.each do |attribute|
|
||||
it "should not allow editing of @#{instance_name}##{attribute}" do
|
||||
do_render
|
||||
response.should_not allow_uploading(instance_for(instance_name), attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation that the rendered template contains a <tt>FORM</tt> element
|
||||
# (<tt>INPUT</tt>, <tt>TEXTAREA</tt>, or <tt>SELECT</tt>) with the specified name.
|
||||
def it_should_have_form_element_for(name)
|
||||
it "should have a form element named '#{name}'" do
|
||||
do_render
|
||||
response.should have_tag(
|
||||
"form input[name='#{name}'],
|
||||
form textarea[name='#{name}'],
|
||||
form select[name='#{name}']"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_have_form_element_for</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_have_form_element_for(name)
|
||||
it "should not have a form element named '#{name}'" do
|
||||
do_render
|
||||
response.should_not have_tag(
|
||||
"form input[name='#{name}'],
|
||||
form textarea[name='#{name}'],
|
||||
form select[name='#{name}']"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a block to evaluate the route in the example context
|
||||
# instead of the example group context.
|
||||
def it_should_link_to(hint = nil, &route)
|
||||
if hint.nil? && route.respond_to?(:to_ruby)
|
||||
hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip
|
||||
end
|
||||
it "should have a link/button to #{(hint || route)}" do
|
||||
do_render
|
||||
response.should have_link_or_button_to(instance_eval(&route))
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to it_should_link_to
|
||||
alias it_should_have_button_to it_should_link_to
|
||||
alias it_should_have_button_or_link_to it_should_link_to
|
||||
|
||||
# Negative version of <tt>it_should_link_to</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_link_to(hint = nil, &route)
|
||||
if hint.nil? && route.respond_to?(:to_ruby)
|
||||
hint = route.to_ruby.gsub(/(^proc \{)|(\}$)/, '').strip
|
||||
end
|
||||
it "should have a link/button to #{(hint || route)}" do
|
||||
do_render
|
||||
response.should_not have_link_or_button_to(instance_eval(&route))
|
||||
end
|
||||
end
|
||||
alias it_should_not_have_link_to it_should_not_link_to
|
||||
alias it_should_not_have_button_to it_should_not_link_to
|
||||
alias it_should_not_have_button_or_link_to it_should_not_link_to
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_new</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method may takes a string or symbol representing the model's name
|
||||
# to send to <tt>have_link_or_button_to_show</tt> or the model's name itself.
|
||||
def it_should_link_to_new(name)
|
||||
it "should have a link/button to create a new #{name}" do
|
||||
do_render
|
||||
response.should have_link_or_button_to_new(name)
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_new it_should_link_to_new
|
||||
alias it_should_have_button_to_new it_should_link_to_new
|
||||
alias it_should_have_button_or_link_to_new it_should_link_to_new
|
||||
|
||||
# Negative version of <tt>it_should_link_to_show</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_link_to_new(name)
|
||||
it "should have a link/button to create a new #{name}" do
|
||||
do_render
|
||||
response.should_not have_link_or_button_to_new(name)
|
||||
end
|
||||
end
|
||||
alias it_should_not_have_link_to_new it_should_not_link_to_new
|
||||
alias it_should_not_have_button_to_new it_should_not_link_to_new
|
||||
alias it_should_not_have_button_or_link_to_new it_should_not_link_to_new
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_show</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name to send to <tt>have_link_or_button_to_show</tt>
|
||||
# not an instance variable, which would be nil in the scope of the example block.
|
||||
def it_should_link_to_show(name)
|
||||
it "should have a link/button to show @#{name}" do
|
||||
do_render
|
||||
response.should have_link_or_button_to_show(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_show it_should_link_to_show
|
||||
alias it_should_have_button_to_show it_should_link_to_show
|
||||
alias it_should_have_button_or_link_to_show it_should_link_to_show
|
||||
|
||||
# Negative version of <tt>it_should_link_to_show</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_link_to_show(name)
|
||||
it "should have a link/button to show @#{name}" do
|
||||
do_render
|
||||
response.should_not have_link_or_button_to_show(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_not_have_link_to_show it_should_not_link_to_show
|
||||
alias it_should_not_have_button_to_show it_should_not_link_to_show
|
||||
alias it_should_not_have_button_or_link_to_show it_should_not_link_to_show
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_show</tt>
|
||||
# for each member of the instance variable matching the specified name
|
||||
# on the response from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name and not an instance variable, which would be nil
|
||||
# in the scope of the example block.
|
||||
def it_should_link_to_show_each(name)
|
||||
it "should have a link/button to show each member of @#{name}" do
|
||||
do_render
|
||||
instance_for(name).each do |member|
|
||||
response.should have_link_or_button_to_show(member)
|
||||
end
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_show_each it_should_link_to_show_each
|
||||
alias it_should_have_button_to_show_each it_should_link_to_show_each
|
||||
alias it_should_have_button_or_link_to_show_each it_should_link_to_show_each
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_edit</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name to send to <tt>have_link_or_button_to_edit</tt>
|
||||
# not an instance variable, which would be nil in the scope of the example block.
|
||||
def it_should_link_to_edit(name)
|
||||
it "should have a link/button to edit @#{name}" do
|
||||
do_render
|
||||
response.should have_link_or_button_to_edit(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_edit it_should_link_to_edit
|
||||
alias it_should_have_button_to_edit it_should_link_to_edit
|
||||
alias it_should_have_button_or_link_to_edit it_should_link_to_edit
|
||||
|
||||
# Negative version of <tt>it_should_link_to_edit</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_link_to_edit(name)
|
||||
it "should have a link/button to edit @#{name}" do
|
||||
do_render
|
||||
response.should_not have_link_or_button_to_edit(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_not_have_link_to_edit it_should_not_link_to_edit
|
||||
alias it_should_not_have_button_to_edit it_should_not_link_to_edit
|
||||
alias it_should_not_have_button_or_link_to_edit it_should_not_link_to_edit
|
||||
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_edit</tt>
|
||||
# for each member of the instance variable matching the specified name
|
||||
# on the response from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name and not an instance variable, which would be nil
|
||||
# in the scope of the example block.
|
||||
def it_should_link_to_edit_each(name)
|
||||
it "should have a link/button to edit each member of @#{name}" do
|
||||
do_render
|
||||
instance_for(name).each do |member|
|
||||
response.should have_link_or_button_to_edit(member)
|
||||
end
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_edit_each it_should_link_to_edit_each
|
||||
alias it_should_have_button_to_edit_each it_should_link_to_edit_each
|
||||
alias it_should_have_button_or_link_to_edit_each it_should_link_to_edit_each
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_delete</tt> on the response
|
||||
# from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name to send to <tt>have_link_or_button_to_delete</tt>
|
||||
# not an instance variable, which would be nil in the scope of the example block.
|
||||
def it_should_link_to_delete(name)
|
||||
it "should have a link/button to delete @#{name}" do
|
||||
do_render
|
||||
response.should have_button_to_delete(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_delete it_should_link_to_delete
|
||||
alias it_should_have_button_to_delete it_should_link_to_delete
|
||||
alias it_should_have_button_or_link_to_delete it_should_link_to_delete
|
||||
|
||||
# Negative version of <tt>it_should_link_to_delete</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_link_to_delete(name)
|
||||
it "should not have a link/button to delete @#{name}" do
|
||||
do_render
|
||||
response.should_not have_button_to_delete(instance_for(name))
|
||||
end
|
||||
end
|
||||
alias it_should_not_have_link_to_delete it_should_not_link_to_delete
|
||||
alias it_should_not_have_button_to_delete it_should_not_link_to_delete
|
||||
alias it_should_not_have_button_or_link_to_delete it_should_not_link_to_delete
|
||||
|
||||
# Creates an expectation which calls <tt>have_link_or_button_to_delete</tt>
|
||||
# for each member of the instance variable matching the specified name
|
||||
# on the response from rendering the template. See that method for more details.
|
||||
#
|
||||
# <b>Note:</b> This method takes a string or symbol representing the instance
|
||||
# variable's name and not an instance variable, which would be nil
|
||||
# in the scope of the example block.
|
||||
def it_should_link_to_delete_each(name)
|
||||
it "should have a link/button to delete each member of @#{name}" do
|
||||
do_render
|
||||
instance_for(name).each do |member|
|
||||
response.should have_button_to_delete(member)
|
||||
end
|
||||
end
|
||||
end
|
||||
alias it_should_have_link_to_delete_each it_should_link_to_delete_each
|
||||
alias it_should_have_button_to_delete_each it_should_link_to_delete_each
|
||||
alias it_should_have_button_or_link_to_delete_each it_should_link_to_delete_each
|
||||
|
||||
# Creates an expectation that the template should call <tt>render :partial</tt>
|
||||
# with the specified template.
|
||||
def it_should_render_partial(name)
|
||||
it "should render :partial => '#{name}'" do
|
||||
template.should_receive(:render).with(hash_including(:partial => name))
|
||||
do_render
|
||||
end
|
||||
end
|
||||
|
||||
# Negative version of <tt>it_should_render_partial</tt>. See that method
|
||||
# for more details.
|
||||
def it_should_not_render_partial(name)
|
||||
it "should not render :partial => '#{name}'" do
|
||||
template.should_not_receive(:render).with(hash_including(:partial => name))
|
||||
do_render
|
||||
end
|
||||
end
|
||||
|
||||
# Sets <tt>@the_template</tt> (for use in <tt>do_render</tt>) using the current
|
||||
# example group description. Example:
|
||||
#
|
||||
# describe "users/index.haml.erb" do
|
||||
# use_describe_for_template!
|
||||
# # ...
|
||||
# end
|
||||
#
|
||||
# This is equivalent to setting <tt>@the_template = "users/index.haml.erb"</tt>
|
||||
# in a before block.
|
||||
def use_describe_for_template!
|
||||
template = self_description_text
|
||||
if File.exists?(File.join(RAILS_ROOT, "app/views", template))
|
||||
before(:each) do
|
||||
@the_template = template
|
||||
end
|
||||
else
|
||||
error_message = "You called use_describe_for_template! "
|
||||
error_message << "but 'app/views/#{template}' does not exist. "
|
||||
raise NameError, error_message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
$:.unshift File.join(File.dirname(__FILE__), "..")
|
||||
require "skinny_spec"
|
||||
|
||||
module LuckySneaks
|
||||
# These methods are designed to be used in your example <tt>before</tt> blocks to accomplish
|
||||
# a whole lot of functionality with just a tiny bit of effort.
|
||||
module ViewStubHelpers
|
||||
# Shorthand for the following stub:
|
||||
#
|
||||
# template.stub!(:render).with(hash_including(:partial => anything))
|
||||
def stub_partial_rendering!
|
||||
template.stub!(:render).with(hash_including(:partial => anything))
|
||||
end
|
||||
end
|
||||
end
|
||||
26
backup.rails2.3/plugins/skinny_spec/lib/skinny_spec.rb
Normal file
26
backup.rails2.3/plugins/skinny_spec/lib/skinny_spec.rb
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Let's make sure everyone else is loaded
|
||||
require File.expand_path(RAILS_ROOT + "/config/environment")
|
||||
require 'spec'
|
||||
require 'spec/rails'
|
||||
begin
|
||||
require 'ruby2ruby'
|
||||
rescue LoadError
|
||||
puts "-----"
|
||||
puts "Attention: skinny_spec requires ruby2ruby for nicer route descriptions"
|
||||
puts "It is highly recommended that you install it: sudo gem install ruby2ruby"
|
||||
puts "-----"
|
||||
end
|
||||
|
||||
# Let's load our family now
|
||||
require "lucky_sneaks/common_spec_helpers"
|
||||
require "lucky_sneaks/controller_request_helpers"
|
||||
require "lucky_sneaks/controller_spec_helpers"
|
||||
require "lucky_sneaks/controller_stub_helpers"
|
||||
require "lucky_sneaks/model_spec_helpers"
|
||||
require "lucky_sneaks/view_spec_helpers"
|
||||
|
||||
# Let's all come together
|
||||
Spec::Rails::Example::ViewExampleGroup.send :include, LuckySneaks::ViewSpecHelpers
|
||||
Spec::Rails::Example::HelperExampleGroup.send :include, LuckySneaks::CommonSpecHelpers
|
||||
Spec::Rails::Example::ControllerExampleGroup.send :include, LuckySneaks::ControllerSpecHelpers
|
||||
Spec::Rails::Example::ModelExampleGroup.send :include, LuckySneaks::ModelSpecHelpers
|
||||
46
backup.rails2.3/plugins/swf_fu/CHANGELOG.rdoc
Normal file
46
backup.rails2.3/plugins/swf_fu/CHANGELOG.rdoc
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
= swf_fu --- History
|
||||
|
||||
== Version 1.4.0 - May 8, 2010
|
||||
|
||||
* Any option can be a block, in which case it is called (with the source swf passed as argument)
|
||||
|
||||
== Version 1.3.1 - February 5, 2010
|
||||
|
||||
* Improved compatibility with Rails 3.0: swf_tag now outputs html_safe content.
|
||||
|
||||
* Got rid of deprecation warning in Rails 2.2+ when using swf_tag in block form.
|
||||
|
||||
== Version 1.3.0 - June 20, 2009
|
||||
|
||||
* Updated to swf_object v2.2. Change should not be noticeable to users, except compatibility improvements and better auto install. Added the option +switch_off_auto_hide_show+.
|
||||
|
||||
== Version 1.2.0 - January 14, 2009
|
||||
|
||||
* flashvars[:id] will now default to the DOM id of the object. I didn't want to have any extra defaults than the very basic ones, but there is no easy way to get this from Flash (see http://www.actionscript.org/forums/showthread.php3?t=136044 ) and no easy way to specify that using +swf_default_options+.
|
||||
* If flashvars is a string (e.g. "myVar=myValue") it will be parsed into a hash so that the behaviour for default values apply to strings or hashes. swf_default_options[:flashvars] can also be a string and will also be parsed before being merged.
|
||||
* Small bug fix: the options passed as hashes (:flashvars, :parameters and :html_options) were changed if swf_default_options[:flashvars, ...] existed. They are now left unchanged.
|
||||
|
||||
== Version 1.1.0 - January 3, 2009
|
||||
|
||||
* Improved the way to specify alternate content
|
||||
|
||||
== Version 1.0.3 - January 3, 2009
|
||||
|
||||
* Improved javascript initialization
|
||||
|
||||
:initialize => [1, 2, 3] # produces in javascript: obj.initialize(1,2,3) instead of ([1,2,3])
|
||||
# no :initialize produces in javascript: obj.initialize() instead of (null)
|
||||
:initialize => nil # stil produces obj.initialize(null)
|
||||
|
||||
== Version 1.0.2 - January 3, 2009
|
||||
|
||||
* Bug fix for flashvars in dynamic method
|
||||
|
||||
== Version 1.0.1 - January 2, 2009
|
||||
|
||||
* File reorganization
|
||||
* Bug fix for default options
|
||||
|
||||
== Version 1.0 - X-mas, 2008
|
||||
|
||||
=== Initial release.
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue