moving to vm on desktop

This commit is contained in:
Scott Davis 2010-03-15 11:47:32 -04:00
parent c15cb0813f
commit b5c15da9ff
56 changed files with 1750 additions and 800 deletions

2
.bundle/config Normal file
View File

@ -0,0 +1,2 @@
---
BUNDLE_WITHOUT: ""

207
.bundle/environment.rb Normal file
View File

@ -0,0 +1,207 @@
# DO NOT MODIFY THIS FILE
require 'digest/sha1'
require "rubygems"
module Gem
class Dependency
if !instance_methods.map { |m| m.to_s }.include?("requirement")
def requirement
version_requirements
end
end
end
end
module Bundler
module SharedHelpers
def default_gemfile
gemfile = find_gemfile
gemfile or raise GemfileNotFound, "The default Gemfile was not found"
Pathname.new(gemfile)
end
def in_bundle?
find_gemfile
end
private
def find_gemfile
return ENV['BUNDLE_GEMFILE'] if ENV['BUNDLE_GEMFILE']
previous = nil
current = File.expand_path(Dir.pwd)
until !File.directory?(current) || current == previous
filename = File.join(current, 'Gemfile')
return filename if File.file?(filename)
current, previous = File.expand_path("..", current), current
end
end
def clean_load_path
# handle 1.9 where system gems are always on the load path
if defined?(::Gem)
me = File.expand_path("../../", __FILE__)
$LOAD_PATH.reject! do |p|
next if File.expand_path(p).include?(me)
p != File.dirname(__FILE__) &&
Gem.path.any? { |gp| p.include?(gp) }
end
$LOAD_PATH.uniq!
end
end
def reverse_rubygems_kernel_mixin
# Disable rubygems' gem activation system
::Kernel.class_eval do
if private_method_defined?(:gem_original_require)
alias rubygems_require require
alias require gem_original_require
end
undef gem
end
end
def cripple_rubygems(specs)
reverse_rubygems_kernel_mixin
executables = specs.map { |s| s.executables }.flatten
:: Kernel.class_eval do
private
def gem(*) ; end
end
Gem.source_index # ensure RubyGems is fully loaded
::Kernel.send(:define_method, :gem) do |dep, *reqs|
if executables.include? File.basename(caller.first.split(':').first)
return
end
opts = reqs.last.is_a?(Hash) ? reqs.pop : {}
unless dep.respond_to?(:name) && dep.respond_to?(:requirement)
dep = Gem::Dependency.new(dep, reqs)
end
spec = specs.find { |s| s.name == dep.name }
if spec.nil?
e = Gem::LoadError.new "#{dep.name} is not part of the bundle. Add it to Gemfile."
e.name = dep.name
e.version_requirement = dep.requirement
raise e
elsif dep !~ spec
e = Gem::LoadError.new "can't activate #{dep}, already activated #{spec.full_name}. " \
"Make sure all dependencies are added to Gemfile."
e.name = dep.name
e.version_requirement = dep.requirement
raise e
end
true
end
# === Following hacks are to improve on the generated bin wrappers ===
# Yeah, talk about a hack
source_index_class = (class << Gem::SourceIndex ; self ; end)
source_index_class.send(:define_method, :from_gems_in) do |*args|
source_index = Gem::SourceIndex.new
source_index.spec_dirs = *args
source_index.add_specs(*specs)
source_index
end
# OMG more hacks
gem_class = (class << Gem ; self ; end)
gem_class.send(:define_method, :bin_path) do |name, *args|
exec_name, *reqs = args
spec = nil
if exec_name
spec = specs.find { |s| s.executables.include?(exec_name) }
spec or raise Gem::Exception, "can't find executable #{exec_name}"
else
spec = specs.find { |s| s.name == name }
exec_name = spec.default_executable or raise Gem::Exception, "no default executable for #{spec.full_name}"
end
File.join(spec.full_gem_path, spec.bindir, exec_name)
end
end
extend self
end
end
module Bundler
LOCKED_BY = '0.9.11'
FINGERPRINT = "bc067d01abbb21b054cfc123f404a7874dfc8f94"
AUTOREQUIRES = {:test=>[["faker", false], ["shoulda", false], ["factory_girl", false]], :default=>[["mysql", false], ["formtastic", false], ["paperclip", false], ["will_paginate", false], ["clearance", false]], :development=>[["mongrel", false]], :developemtn=>[["faker", false]]}
SPECS = [
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/faker-0.3.1.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/faker-0.3.1/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/mongrel-1.1.5.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/mongrel-1.1.5/lib", "/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/mongrel-1.1.5/ext"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/fastthread-1.0.7.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/fastthread-1.0.7/lib", "/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/fastthread-1.0.7/ext"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/mysql-2.8.1.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/mysql-2.8.1/lib", "/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/mysql-2.8.1/ext"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/shoulda-2.10.3.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/shoulda-2.10.3/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/formtastic-0.9.7.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/formtastic-0.9.7/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/rack-1.0.1.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/rack-1.0.1/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/gem_plugin-0.2.3.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/gem_plugin-0.2.3/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/cgi_multipart_eof_fix-2.5.0.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/cgi_multipart_eof_fix-2.5.0/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/paperclip-2.3.1.1.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/paperclip-2.3.1.1/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/will_paginate-2.3.12.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/will_paginate-2.3.12/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/factory_girl-1.2.3.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/factory_girl-1.2.3/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/actionpack-2.3.5.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/actionpack-2.3.5/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/daemons-1.0.10.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/daemons-1.0.10/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/clearance-0.8.8.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/clearance-0.8.8/lib"]},
{:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/activesupport-2.3.5.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/activesupport-2.3.5/lib"]},
].map do |hash|
spec = eval(File.read(hash[:loaded_from]), binding, hash[:loaded_from])
spec.loaded_from = hash[:loaded_from]
spec.require_paths = hash[:load_paths]
spec
end
extend SharedHelpers
def self.match_fingerprint
print = Digest::SHA1.hexdigest(File.read(File.expand_path('../../Gemfile', __FILE__)))
unless print == FINGERPRINT
abort 'Gemfile changed since you last locked. Please `bundle lock` to relock.'
end
end
def self.setup(*groups)
match_fingerprint
clean_load_path
cripple_rubygems(SPECS)
SPECS.each do |spec|
Gem.loaded_specs[spec.name] = spec
$LOAD_PATH.unshift(*spec.require_paths)
end
end
def self.require(*groups)
groups = [:default] if groups.empty?
groups.each do |group|
(AUTOREQUIRES[group.to_sym] || []).each do |file, explicit|
if explicit
Kernel.require file
else
begin
Kernel.require file
rescue LoadError
end
end
end
end
end
# Setup bundle when it's required.
setup
end

23
Gemfile Normal file
View File

@ -0,0 +1,23 @@
# A sample Gemfile
source :gemcutter
#
#gem "rails", "2.3.5"
gem 'clearance'
gem 'will_paginate'
gem 'paperclip'
gem 'formtastic'
gem 'mysql'
#gem "matthuhiggins-foreigner", :require => "foreigner"
group :test do
gem 'shoulda'
gem 'factory_girl'
gem 'faker', :group => [:test, :developemtn]
end
group :development do
gem 'mongrel'
end

76
Gemfile.lock Normal file
View File

@ -0,0 +1,76 @@
---
dependencies:
faker:
group:
- :test
- :developemtn
version: ">= 0"
mongrel:
group:
- :development
version: ">= 0"
mysql:
group:
- :default
version: ">= 0"
shoulda:
group:
- :test
version: ">= 0"
formtastic:
group:
- :default
version: ">= 0"
factory_girl:
group:
- :test
version: ">= 0"
paperclip:
group:
- :default
version: ">= 0"
will_paginate:
group:
- :default
version: ">= 0"
clearance:
group:
- :default
version: ">= 0"
specs:
- faker:
version: 0.3.1
- fastthread:
version: 1.0.7
- mongrel:
version: 1.1.5
- mysql:
version: 2.8.1
- shoulda:
version: 2.10.3
- rack:
version: 1.0.1
- gem_plugin:
version: 0.2.3
- cgi_multipart_eof_fix:
version: 2.5.0
- formtastic:
version: 0.9.7
- will_paginate:
version: 2.3.12
- paperclip:
version: 2.3.1.1
- factory_girl:
version: 1.2.3
- daemons:
version: 1.0.10
- clearance:
version: 0.8.8
- actionpack:
version: 2.3.5
- activesupport:
version: 2.3.5
hash: bc067d01abbb21b054cfc123f404a7874dfc8f94
sources:
- Rubygems:
uri: http://gemcutter.org

View File

@ -11,4 +11,6 @@ class ApplicationController < ActionController::Base
def set_page def set_page
@page_title = "Collab" @page_title = "Collab"
end end
end end

View File

@ -10,12 +10,35 @@ class ProjectsController < ApplicationController
format.xml { render :xml => @projects } format.xml { render :xml => @projects }
end end
end end
def join
@project = Project.find(params[:project_id])
@project.users << current_user
if @project.save
flash[:success] = "You have joined #{@project.name}"
else
flash[:error] = "There was a problem joining this project"
end
redirect_to project_path(@project)
end
def leave
@project = Project.find(params[:project_id])
@project.users.delete current_user
if @project.save
flash[:success] = "You have been removed from #{@project.name}"
else
flash[:error] = "There was an error removing you form this project"
end
redirect_to project_path(@project)
end
# GET /projects/1 # GET /projects/1
# GET /projects/1.xml # GET /projects/1.xml
def show def show
@project = Project.find(params[:id], :include => [:users]) @project = Project.find(params[:id], :include => [:users, :wall_posts])
@tasks = @project.tasks.parents.paginate(:per_page => 30, :page => params[:page], :include => :tasks) @tasks = @project.tasks.parents.paginate(:per_page => 30, :page => params[:page], :include => :tasks)
@wall_posts = @project.wall_posts.paginate(:per_page => 15, :page => params[:post_page])
respond_to do |format| respond_to do |format|
format.html # show.html.erb format.html # show.html.erb
format.xml { render :xml => @project } format.xml { render :xml => @project }

View File

@ -0,0 +1,18 @@
class WallController < ApplicationController
before_filter :authenticate, :load_project
def create
@wall = Wall.new(params[:wall])
@wall.project = @project
@wall.author = current_user
@wall.save
end
private
def load_project
@project = Project.find(params[:project_id])
end
end

View File

@ -1,2 +1,13 @@
module ProjectsHelper module ProjectsHelper
def parse_wall_string(string)
user_matches = string.scan(/@([a-zA-Z_0-9]+)/).flatten
return string if user_matches.empty?
User.find_all_by_short_name(user_matches).each do |user|
string.gsub!("@#{user.short_name}", link_to("@#{user.short_name}", user_path(user.short_name)))
end
string
end
end end

View File

@ -0,0 +1,3 @@
module WallHelper
include ProjectsHelper
end

View File

@ -1,5 +1,6 @@
class Project < ActiveRecord::Base class Project < ActiveRecord::Base
has_many :tasks has_many :tasks
has_many :wall_posts, :class_name => "Wall", :foreign_key => "project_id", :order => "created_at DESC"
belongs_to :owner, :class_name => "User", :foreign_key => "owner_id" belongs_to :owner, :class_name => "User", :foreign_key => "owner_id"
validates_presence_of :name validates_presence_of :name
has_and_belongs_to_many :users has_and_belongs_to_many :users

View File

@ -7,7 +7,7 @@ class Task < ActiveRecord::Base
named_scope :parents, :conditions => {:parent => nil} named_scope :parents, :conditions => {:parent => nil}
#validations #validations
validates_presence_of :name, :description, :project_id validates_presence_of :name, :description, :project_id
validates_associated :project
validate :assignee_validation, :owner_validation validate :assignee_validation, :owner_validation
def assignee_validation def assignee_validation

View File

@ -5,13 +5,29 @@ class User < ActiveRecord::Base
has_many :owned_tasks, :class_name => "Task", :foreign_key => "owner_id" has_many :owned_tasks, :class_name => "Task", :foreign_key => "owner_id"
has_many :assigned_tasks, :class_name => "Task", :foreign_key => "assigned_id" has_many :assigned_tasks, :class_name => "Task", :foreign_key => "assigned_id"
validates_uniqueness_of :short_name validates_uniqueness_of :short_name
validates_presence_of :short_name validates_presence_of :email, :password, :short_name
validates_format_of :phone, :with => /^[0-9]{3}-[0-9]{3}-[0-9]{4}$/, :if => Proc.new {|user| !user.phone.blank?} validates_format_of :phone, :with => /^[0-9]{3}-[0-9]{3}-[0-9]{4}$/, :if => Proc.new {|user| !user.phone.blank?}
validates_format_of :mobile, :with => /^[0-9]{3}-[0-9]{3}-[0-9]{4}$/, :if => Proc.new {|user| !user.mobile.blank?} validates_format_of :mobile, :with => /^[0-9]{3}-[0-9]{3}-[0-9]{4}$/, :if => Proc.new {|user| !user.mobile.blank?}
before_validation :set_short_name
def set_short_name
if short_name.blank?
short_name = email.split('@')[0]
end
end
def gravatar(size = 48) def gravatar(size = 48)
hash = Digest::MD5.hexdigest email hash = Digest::MD5.hexdigest email
"http://www.gravatar.com/avatar/#{hash}.jpg?s=#{size}" "http://www.gravatar.com/avatar/#{hash}.jpg?s=#{size}"
end end
def send_post_notification(post)
project_id = post.project_id
end
def send_project_notification(project)
end
end end

22
app/models/wall.rb Normal file
View File

@ -0,0 +1,22 @@
class Wall < ActiveRecord::Base
belongs_to :author, :class_name => "User", :foreign_key => "user_id"
belongs_to :project
after_create :check_for_mentions, :check_for_hash_tags
def check_for_mentions
mentions = text.scan(/@([a-zA-Z_0-9])/).flatten
User.find_all_by_short_name(mentions).each{ |user| user.send_post_notification(self) }
end
def check_for_hash_tags
tags = text.scan(/#([a-zA-Z0-9_]+)/).flatten
send_notice_to_members(project) if tags.include?('project')
end
def send_notice_to_members(project)
project.users.each {|user| user.send_project_notification(project)}
end
end

View File

@ -1,15 +1,40 @@
<p> <h1><%=h @project.name.titleize %></h1>
<b>Name:</b> <p><%= link_to 'Edit', edit_project_path(@project) %></p>
<%=h @project.name %> <div id='project_right'>
</p> <div id='project_users'>
<div id='project_users'> <h2>Users</h2>
<% @project.users.each do |user| %> <% @project.users.each do |user| %>
<%= image_tag user.gravatar %> <%= link_to "@#{user.short_name}", user_path(user.short_name) %> <div class='project_user_info'>
<% end %> <%= image_tag user.gravatar %> <%= link_to "@#{user.short_name}", user_path(user.short_name) %>
</div>
<% end %>
<% unless @project.users.include?(current_user) %>
<%= link_to "Join Project", join_project_path(@project), :method => :post %>
<% else %>
<%= link_to "Leave Project", leave_project_path(@project), :method => :delete %>
<% end %>
</div>
<h2>Wall</h2>
<div id='project_wall'>
<div id='add_post'>
<% remote_form_for :wall, :url => project_wall_index_path(@project) do |f| %>
<%= f.text_area :text, :cols => 35, :rows => 10, :id=> :new_post %>
<%= f.submit :Submit %>
<% end %>
</div>
<div id='post_list'>
<% @wall_posts.each do |post| %>
<%= render :partial => 'wall/wall_posts', :locals => {:post => post} %>
<% end %>
<%= will_paginate @wall_posts, :param_name => :post_page %>
</div>
</div>
</div> </div>
<div id='project_tasks'> <div id='project_tasks'>
<h2>Tasks</h2>
<%= render :partial => 'tasks/task_list' %> <%= render :partial => 'tasks/task_list' %>
<%= will_paginate @tasks %>
</div> </div>
<%= will_paginate @tasks %> <br style='clear:both'/>
<%= link_to 'Edit', edit_project_path(@project) %> |
<%= link_to 'Back', projects_path %> <%= link_to 'Back', projects_path %>

View File

@ -1,21 +1,23 @@
<% unless @tasks.empty? %> <% unless @tasks.empty? %>
<table class='task_list'> <table class='task_list' cellspacing='0' cellpadding='0'>
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Project</th> <th>Project</th>
<th>Description</th> <th>Description</th>
<th>Children</th> <th>Children</th>
<th>&nbsp;</th>
</tr> </tr>
<% @tasks.each do |task| %> <% @tasks.each do |task| %>
<tr class="<%= cycle(:even, :odd)%>"> <tr class="<%= cycle(:even, :odd)%>">
<td><%=h task.name %></td> <td><%= link_to h(task.name), project_task_path(@project, task) %></td>
<td><%= link_to h(task.project.name), project_path(task.project) %></td> <td><%= link_to h(task.project.name), project_path(task.project) %></td>
<td><%=h task.description %></td> <td><%=h truncate(task.description, :length => 158) %></td>
<td><%=h task.tasks.size %></td> <td><%= link_to h(task.tasks.size), project_task_path(@project, task) %></td>
<td><%= link_to 'Show', project_task_path(@project, task) %></td> <td>
<td><%= link_to 'Edit', edit_project_task_path(@project, task) %></td> <%= link_to 'Edit', edit_project_task_path(@project, task) %>
<td><%= link_to 'Destroy', project_task_path(@project, task), :confirm => 'Are you sure?', :method => :delete %></td> <%= link_to 'Destroy', project_task_path(@project, task), :confirm => 'Are you sure?', :method => :delete %>
</td>
</tr> </tr>
<% end %> <% end %>
</table> </table>

View File

@ -1,14 +1,11 @@
<p> <h1><%=h @task.name %></h1>
<b>Name:</b>
<%=h @task.name %>
</p>
<p> <p>
<b>Project:</b> <b>Project:</b>
<%=h @task.project.name %> <%=h @task.project.name %>
</p> </p>
<p> <p id='task_description'>
<b>Description:</b> <b>Description:</b>
<%=h @task.description %> <%=h @task.description %>
</p> </p>

View File

@ -0,0 +1,4 @@
<div class='post'>
<%= image_tag post.author.gravatar(16), :alt => post.author.short_name %>
<%= parse_wall_string(post.text) %>
</div>

View File

@ -0,0 +1,7 @@
if @wall.save
page.insert_html :prepend, :post_list, :partial => 'wall_posts', :locals => {:post => @wall }
page[:post_list].visual_effect :highlight
page[:new_post].val('')
else
page.alert("Wall post is invalid")
end

View File

@ -108,3 +108,21 @@ end
# All that for this: # All that for this:
Rails.boot! Rails.boot!
class Rails::Boot
def run
load_initializer
extend_environment
Rails::Initializer.run(:set_load_path)
end
def extend_environment
Rails::Initializer.class_eval do
old_load = instance_method(:load_environment)
define_method(:load_environment) do
Bundler.require :default, Rails.env
old_load.bind(self).call
end
end
end
end

0
config/preinitializer.rb Normal file
View File

View File

@ -2,11 +2,14 @@ ActionController::Routing::Routes.draw do |map|
Clearance::Routes.draw(map) Clearance::Routes.draw(map)
map.user '/users/:id', :controller => :users, :action => :show map.user '/users/:id', :controller => :users, :action => :show
map.user_inline_edit '/users/:id/inline', :controller => :users, :action => :inline, :conditions => { :method => :post } map.user_inline_edit '/users/:id/inline', :controller => :users, :action => :inline, :conditions => { :method => :post }
map.join_project '/projects/:project_id/join', :controller => :projects, :action => :join, :conditions => {:method => :post}
map.leave_project '/projects/:project_id/leave', :controller => :projects, :action => :leave, :conditions => {:method => :delete}
#map.dashboard '/dashboard', :controller => :users, :action => :dashboard #map.dashboard '/dashboard', :controller => :users, :action => :dashboard
map.resources :projects do |projects| map.resources :projects do |projects|
projects.resources :tasks projects.resources :tasks
projects.resources :wall
end end

View File

@ -0,0 +1,14 @@
class CreateWalls < ActiveRecord::Migration
def self.up
create_table :walls do |t|
t.belongs_to :project
t.text :text
t.belongs_to :user
t.timestamps
end
end
def self.down
drop_table :walls
end
end

View File

@ -9,7 +9,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20100305060621) do ActiveRecord::Schema.define(:version => 20100312004836) do
create_table "projects", :force => true do |t| create_table "projects", :force => true do |t|
t.string "name" t.string "name"
@ -59,6 +59,14 @@ ActiveRecord::Schema.define(:version => 20100305060621) do
add_index "users", ["id", "confirmation_token"], :name => "index_users_on_id_and_confirmation_token" add_index "users", ["id", "confirmation_token"], :name => "index_users_on_id_and_confirmation_token"
add_index "users", ["remember_token"], :name => "index_users_on_remember_token" add_index "users", ["remember_token"], :name => "index_users_on_remember_token"
create_table "walls", :force => true do |t|
t.integer "project_id"
t.text "text"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_foreign_key "projects", "users", :name => "projects_owner_id_fk", :column => "owner_id" add_foreign_key "projects", "users", :name => "projects_owner_id_fk", :column => "owner_id"
add_foreign_key "tasks", "users", :name => "tasks_owner_id_fk", :column => "owner_id" add_foreign_key "tasks", "users", :name => "tasks_owner_id_fk", :column => "owner_id"

21
factories/factories.rb Normal file
View File

@ -0,0 +1,21 @@
require 'factory_girl'
require 'faker'
Factory.define :user do |f|
f.sequence(:email) { |n| "email_#{n}@localhost.com" }
f.sequence(:password) { |n| "password#{n}" }
f.sequence(:short_name) { |n| "short_#{n}" }
end
Factory.define :project do |f|
f.sequence(:name) {|n| "project #{n}"}
end
Factory.define :task do |f|
f.sequence(:name) {|n| "task #{n}"}
f.description Faker::Lorem.words(50)
end
Factory.define :wall do |f|
f.text "@short_2 This is a wall post of fun and goodness"
end

View File

@ -1,7 +1,5 @@
path = File.join(RAILS_ROOT, 'factories')
require 'factory_girl' require 'factory_girl'
["user", "task", "project"].each {|factory| require File.join(path, "#{factory}_factory")} require 'factories/factories'
class FactoryLoader class FactoryLoader
def self.up def self.up
@ -30,6 +28,7 @@ class FactoryLoader
10.times do |i| 10.times do |i|
p = Factory.create(:project, :owner => user, :users => [user]) p = Factory.create(:project, :owner => user, :users => [user])
self.create_tasks(p, user) self.create_tasks(p, user)
self.create_wall_posts(p)
end end
end end
end end
@ -46,5 +45,11 @@ class FactoryLoader
end end
end end
def self.create_wall_posts(project)
User.all.each do |user|
Factory.create(:wall, :author => user, :project => project)
end
end
end end

View File

@ -1,5 +0,0 @@
require 'factory_girl'
Factory.define :project do |f|
f.sequence(:name) {|n| "project #{n}"}
end

View File

@ -1,6 +0,0 @@
require 'factory_girl'
require 'faker'
Factory.define :task do |f|
f.sequence(:name) {|n| "task #{n}"}
f.description Faker::Lorem.paragraphs(1)
end

View File

@ -1,8 +0,0 @@
require 'factory_girl'
Factory.define :user do |f|
f.sequence(:email) { |n| "email_#{n}@localhost.com" }
f.sequence(:password) { |n| "password#{n}" }
f.sequence(:short_name) { |n| "short_#{n}" }
end

View File

@ -1,2 +1,3 @@
// Place your application-specific JavaScript functions and classes here // Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults // This file is automatically included by javascript_include_tag :defaults

188
public/javascripts/jquery-ui.js vendored Normal file

File diff suppressed because one or more lines are too long

19
public/javascripts/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
(function($){$().ajaxSend(function(a,xhr,s){xhr.setRequestHeader("Accept","text/javascript, text/html, application/xml, text/xml, */*")})})(jQuery);(function($){$.fn.reset=function(){return this.each(function(){if(typeof this.reset=="function"||(typeof this.reset=="object"&&!this.reset.nodeType)){this.reset()}})};$.fn.enable=function(){return this.each(function(){this.disabled=false})};$.fn.disable=function(){return this.each(function(){this.disabled=true})}})(jQuery);(function($){$.extend({fieldEvent:function(el,obs){var field=el[0]||el,e="change";if(field.type=="radio"||field.type=="checkbox"){e="click"}else{if(obs&&field.type=="text"||field.type=="textarea"){e="keyup"}}return e}});$.fn.extend({delayedObserver:function(delay,callback){var el=$(this);if(typeof window.delayedObserverStack=="undefined"){window.delayedObserverStack=[]}if(typeof window.delayedObserverCallback=="undefined"){window.delayedObserverCallback=function(stackPos){observed=window.delayedObserverStack[stackPos];if(observed.timer){clearTimeout(observed.timer)}observed.timer=setTimeout(function(){observed.timer=null;observed.callback(observed.obj,observed.obj.formVal())},observed.delay*1000);observed.oldVal=observed.obj.formVal()}}window.delayedObserverStack.push({obj:el,timer:null,delay:delay,oldVal:el.formVal(),callback:callback});var stackPos=window.delayedObserverStack.length-1;if(el[0].tagName=="FORM"){$(":input",el).each(function(){var field=$(this);field.bind($.fieldEvent(field,delay),function(){observed=window.delayedObserverStack[stackPos];if(observed.obj.formVal()==observed.obj.oldVal){return}else{window.delayedObserverCallback(stackPos)}})})}else{el.bind($.fieldEvent(el,delay),function(){observed=window.delayedObserverStack[stackPos];if(observed.obj.formVal()==observed.obj.oldVal){return}else{window.delayedObserverCallback(stackPos)}})}},formVal:function(){var el=this[0];if(el.tagName=="FORM"){return this.serialize()}if(el.type=="checkbox"||self.type=="radio"){return this.filter("input:checked").val()||""}else{return this.val()}}})})(jQuery);(function($){$.fn.extend({visualEffect:function(o){e=o.replace(/\_(.)/g,function(m,l){return l.toUpperCase()});return eval("$(this)."+e+"()")},appear:function(speed,callback){return this.fadeIn(speed,callback)},blindDown:function(speed,callback){return this.show("blind",{direction:"vertical"},speed,callback)},blindUp:function(speed,callback){return this.hide("blind",{direction:"vertical"},speed,callback)},blindRight:function(speed,callback){return this.show("blind",{direction:"horizontal"},speed,callback)},blindLeft:function(speed,callback){this.hide("blind",{direction:"horizontal"},speed,callback);return this},dropOut:function(speed,callback){return this.hide("drop",{direction:"down"},speed,callback)},dropIn:function(speed,callback){return this.show("drop",{direction:"up"},speed,callback)},fade:function(speed,callback){return this.fadeOut(speed,callback)},fadeToggle:function(speed,callback){return this.animate({opacity:"toggle"},speed,callback)},fold:function(speed,callback){return this.hide("fold",{},speed,callback)},foldOut:function(speed,callback){return this.show("fold",{},speed,callback)},grow:function(speed,callback){return this.show("scale",{},speed,callback)},highlight:function(speed,callback){return this.show("highlight",{},speed,callback)},puff:function(speed,callback){return this.hide("puff",{},speed,callback)},pulsate:function(speed,callback){return this.show("pulsate",{},speed,callback)},shake:function(speed,callback){return this.show("shake",{},speed,callback)},shrink:function(speed,callback){return this.hide("scale",{},speed,callback)},squish:function(speed,callback){return this.hide("scale",{origin:["top","left"]},speed,callback)},slideUp:function(speed,callback){return this.hide("slide",{direction:"up"},speed,callback)},slideDown:function(speed,callback){return this.show("slide",{direction:"up"},speed,callback)},switchOff:function(speed,callback){return this.hide("clip",{},speed,callback)},switchOn:function(speed,callback){return this.show("clip",{},speed,callback)}})})(jQuery); (function($){$.ajaxSettings.accepts._default="text/javascript, text/html, application/xml, text/xml, */*"})(jQuery);(function($){$.fn.reset=function(){return this.each(function(){if(typeof this.reset=="function"||(typeof this.reset=="object"&&!this.reset.nodeType)){this.reset()}})};$.fn.enable=function(){return this.each(function(){this.disabled=false})};$.fn.disable=function(){return this.each(function(){this.disabled=true})}})(jQuery);(function($){$.extend({fieldEvent:function(el,obs){var field=el[0]||el,e="change";if(field.type=="radio"||field.type=="checkbox"){e="click"}else{if(obs&&(field.type=="text"||field.type=="textarea"||field.type=="password")){e="keyup"}}return e}});$.fn.extend({delayedObserver:function(delay,callback){var el=$(this);if(typeof window.delayedObserverStack=="undefined"){window.delayedObserverStack=[]}if(typeof window.delayedObserverCallback=="undefined"){window.delayedObserverCallback=function(stackPos){var observed=window.delayedObserverStack[stackPos];if(observed.timer){clearTimeout(observed.timer)}observed.timer=setTimeout(function(){observed.timer=null;observed.callback(observed.obj,observed.obj.formVal())},observed.delay*1000);observed.oldVal=observed.obj.formVal()}}window.delayedObserverStack.push({obj:el,timer:null,delay:delay,oldVal:el.formVal(),callback:callback});var stackPos=window.delayedObserverStack.length-1;if(el[0].tagName=="FORM"){$(":input",el).each(function(){var field=$(this);field.bind($.fieldEvent(field,delay),function(){var observed=window.delayedObserverStack[stackPos];if(observed.obj.formVal()==observed.oldVal){return}else{window.delayedObserverCallback(stackPos)}})})}else{el.bind($.fieldEvent(el,delay),function(){var observed=window.delayedObserverStack[stackPos];if(observed.obj.formVal()==observed.oldVal){return}else{window.delayedObserverCallback(stackPos)}})}},formVal:function(){var el=this[0];if(el.tagName=="FORM"){return this.serialize()}if(el.type=="checkbox"||el.type=="radio"){return this.filter("input:checked").val()||""}else{return this.val()}}})})(jQuery);(function($){$.fn.extend({visualEffect:function(o,options){if(options){speed=options.duration*1000}else{speed=null}e=o.replace(/\_(.)/g,function(m,l){return l.toUpperCase()});return eval("$(this)."+e+"("+speed+")")},appear:function(speed,callback){return this.fadeIn(speed,callback)},blindDown:function(speed,callback){return this.show("blind",{direction:"vertical"},speed,callback)},blindUp:function(speed,callback){return this.hide("blind",{direction:"vertical"},speed,callback)},blindRight:function(speed,callback){return this.show("blind",{direction:"horizontal"},speed,callback)},blindLeft:function(speed,callback){this.hide("blind",{direction:"horizontal"},speed,callback);return this},dropOut:function(speed,callback){return this.hide("drop",{direction:"down"},speed,callback)},dropIn:function(speed,callback){return this.show("drop",{direction:"up"},speed,callback)},fade:function(speed,callback){return this.fadeOut(speed,callback)},fadeToggle:function(speed,callback){return this.animate({opacity:"toggle"},speed,callback)},fold:function(speed,callback){return this.hide("fold",{},speed,callback)},foldOut:function(speed,callback){return this.show("fold",{},speed,callback)},grow:function(speed,callback){return this.show("scale",{},speed,callback)},highlight:function(speed,callback){return this.show("highlight",{},speed,callback)},puff:function(speed,callback){return this.hide("puff",{},speed,callback)},pulsate:function(speed,callback){return this.show("pulsate",{},speed,callback)},shake:function(speed,callback){return this.show("shake",{},speed,callback)},shrink:function(speed,callback){return this.hide("scale",{},speed,callback)},squish:function(speed,callback){return this.hide("scale",{origin:["top","left"]},speed,callback)},slideUp:function(speed,callback){return this.hide("slide",{direction:"up"},speed,callback)},slideDown:function(speed,callback){return this.show("slide",{direction:"up"},speed,callback)},switchOff:function(speed,callback){return this.hide("clip",{},speed,callback)},switchOn:function(speed,callback){return this.show("clip",{},speed,callback)}})})(jQuery);

View File

@ -4,7 +4,11 @@
} }
h1 { h1 {
margin-top:10px margin:10px
}
h2 {
margin:5px;
} }
div.flash { div.flash {
@ -21,6 +25,11 @@ div#flash_error {
border:red 5px solid; border:red 5px solid;
} }
div#flash_failure{
color:red;
border:red 5px solid;
}
div#flash_notice { div#flash_notice {
color:blue; color:blue;
border:blue 5px solid; border:blue 5px solid;
@ -31,6 +40,9 @@ div#flash_success {
border:green 5px solid; border:green 5px solid;
} }
div#content {
padding:10px;
}
table.task_list tr.odd{ table.task_list tr.odd{
background-color:#ddd; background-color:#ddd;
@ -59,3 +71,47 @@ div#wrapper {
margin-top:30px; margin-top:30px;
background-color:white; background-color:white;
} }
/** Projects **/
div#project_right {
width:30%;
float:right;
}
div#project_tasks {
float:left;
width:60%;
text-align:center;
}
div.project_user_info a{
font-size:20px;
}
div#post_list {
border:1px solid #ccc;
margin-top:10px;
}
div.post {
padding:5px;
}
table.task_list tr {
text-align:left;
}
table.task_list tr td {
padding:5px;
}
table.task_list tr th {
padding:10px;
}
/** Tasks **/
div#task_description {
}

View File

@ -1,13 +1 @@
Factory.sequence :email do |n|
"user#{n}@example.com"
end
Factory.define :user do |user|
user.email { Factory.next :email }
user.password { "password" }
user.password_confirmation { "password" }
end
Factory.define :email_confirmed_user, :parent => :user do |user|
user.email_confirmed { true }
end

9
test/fixtures/walls.yml vendored Normal file
View File

@ -0,0 +1,9 @@
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
belongs_to:
text: MyText
two:
belongs_to:
text: MyText

View File

@ -1,6 +1,12 @@
require 'test_helper' require 'test_helper'
class ProjectsControllerTest < ActionController::TestCase class ProjectsControllerTest < ActionController::TestCase
def setup
@user = Factory(:user)
sign_in_as(@user)
end
test "should get index" do test "should get index" do
get :index get :index
assert_response :success assert_response :success
@ -13,31 +19,32 @@ class ProjectsControllerTest < ActionController::TestCase
end end
test "should create project" do test "should create project" do
assert_difference('Project.count') do assert_difference('Project.count') do
post :create, :project => { } post :create, :project => Factory.attributes_for(:project)
end end
assert_redirected_to project_path(assigns(:project)) assert_redirected_to project_path(assigns(:project))
end end
test "should show project" do test "should show project" do
get :show, :id => projects(:one).to_param get :show, :id => Factory(:project).id
assert_response :success assert_response :success
end end
test "should get edit" do test "should get edit" do
get :edit, :id => projects(:one).to_param get :edit, :id => Factory(:project).id
assert_response :success assert_response :success
end end
test "should update project" do test "should update project" do
put :update, :id => projects(:one).to_param, :project => { } put :update, :id => Factory(:project).id, :project => Factory.attributes_for(:project)
assert_redirected_to project_path(assigns(:project)) assert_redirected_to project_path(assigns(:project))
end end
test "should destroy project" do test "should destroy project" do
project = Factory(:project)
assert_difference('Project.count', -1) do assert_difference('Project.count', -1) do
delete :destroy, :id => projects(:one).to_param delete :destroy, :id => project.id
end end
assert_redirected_to projects_path assert_redirected_to projects_path

View File

@ -0,0 +1,8 @@
require 'test_helper'
class WallControllerTest < ActionController::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end
end

View File

@ -37,4 +37,17 @@ class ActiveSupport::TestCase
#fixtures :all #fixtures :all
# Add more helper methods to be used by all tests here... # Add more helper methods to be used by all tests here...
def sign_in_as(user)
@controller.current_user = user
return user
end
def sign_in
sign_in_as Factory(:email_confirmed_user)
end
def sign_out
@controller.current_user = nil
end
end end

View File

@ -0,0 +1,4 @@
require 'test_helper'
class WallHelperTest < ActionView::TestCase
end

8
test/unit/wall_test.rb Normal file
View File

@ -0,0 +1,8 @@
require 'test_helper'
class WallTest < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end
end

View File

@ -1,11 +1,19 @@
SVN 0.5.0 (31 July 2009)
* Gemification
* Support for Ruby 1.9.X
* Updated to jQuery 1.3.2
* Updated to jQuery UI 1.7.2
* Created a jrails binary (runs rake tasks) because rails does not (yet?) pickup
tasks from gem plugins
* Changed default to use jQuery compatibility name (not $)
* Created a scrub task that will remove the prototype / script.aculo.us
javascript files
* better approximate scriptaculous effect names * better approximate scriptaculous effect names
* add support for page[:element_id].visual_effect(:effect) as well as page.visual_effect(:effect, :element_id) * add support for page[:element_id].visual_effect(:effect) as well as page.visual_effect(:effect, :element_id)
* added a reset form function to jrails.js (stolen from jquery form) * added a reset form function to jrails.js (stolen from jquery form)
* can now use jquery selectors in all functions * can now use jquery selectors in all functions
* added javascript_function helper to render inline rjs helpers * added javascript_function helper to render inline rjs helpers
* better support for sortable_element * better support for sortable_element
* updated to jquery-ui 1.5.1
0.4.0 (16 June 2008) 0.4.0 (16 June 2008)
* updated to jquery-ui 1.5 & merged js into single file * updated to jquery-ui 1.5 & merged js into single file

18
vendor/plugins/jrails/LICENSE vendored Normal file
View File

@ -0,0 +1,18 @@
Copyright (c) 2008 Aaron Eisenberger
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

21
vendor/plugins/jrails/README.rdoc vendored Normal file
View File

@ -0,0 +1,21 @@
= jRails
jRails is a drop-in jQuery replacement for the Rails Prototype/script.aculo.us helpers.
== Resources
Install
* .script/plugin install git://github.com/aaronchi/jrails.git
Project Site
* http://code.google.com/p/ennerchi
Web Site
* http://www.ennerchi.com/projects/jrails
Group Site
* http://groups.google.com/group/jrails

19
vendor/plugins/jrails/Rakefile vendored Normal file
View File

@ -0,0 +1,19 @@
require 'rubygems'
require 'rake'
begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "jrails"
gem.summary = "jRails is a drop-in jQuery replacement for the Rails Prototype/script.aculo.us helpers."
gem.description = "Using jRails, you can get all of the same default Rails helpers for javascript functionality using the lighter jQuery library."
gem.email = "aaronchi@gmail.com"
gem.homepage = "http://ennerchi.com/projects/jrails"
gem.authors = ["Aaron Eisenberger", "Patrick Hurley"]
gem.rubyforge_project = "jrails"
gem.files = FileList["[A-Z]*.rb","{bin,javascripts,lib,rails,tasks}/**/*"]
end
Jeweler::GemcutterTasks.new
rescue LoadError
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
end

5
vendor/plugins/jrails/VERSION.yml vendored Normal file
View File

@ -0,0 +1,5 @@
---
:build:
:patch: 0
:major: 0
:minor: 6

30
vendor/plugins/jrails/bin/jrails vendored Executable file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env ruby
require 'rubygems'
require 'rake'
RAILS_ROOT = Dir.pwd
rakeapp = Rake.application
fname =File.join(File.dirname(__FILE__), '..', 'tasks', 'jrails.rake')
load fname
task :help do
puts "jrails [command]\n\n"
rakeapp.options.show_task_pattern = Regexp.new('^[hius]')
rakeapp.display_tasks_and_comments
end
desc 'Installs the jQuery and jRails javascripts to public/javascripts'
task :install do
Rake::Task['jrails:js:install'].invoke
end
desc 'Remove the prototype / script.aculo.us javascript files'
task :scrub do
Rake::Task['jrails:js:scrub'].invoke
end
rakeapp.init("jrails")
task :default => [:help]
rakeapp.top_level

View File

@ -1,6 +1 @@
# uncomment to use jQuery.noConflict() require 'rails/init.rb'
#ActionView::Helpers::PrototypeHelper::JQUERY_VAR = 'jQuery'
ActionView::Helpers::AssetTagHelper::JAVASCRIPT_DEFAULT_SOURCES = ['jrails']
ActionView::Helpers::AssetTagHelper::reset_javascript_include_default
require 'jrails'

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,128 +1 @@
(function($){$().ajaxSend(function(a,xhr,s){xhr.setRequestHeader("Accept","text/javascript, text/html, application/xml, text/xml, */*")})})(jQuery);(function($){$.fn.reset=function(){return this.each(function(){if(typeof this.reset=="function"||(typeof this.reset=="object"&&!this.reset.nodeType)){this.reset()}})};$.fn.enable=function(){return this.each(function(){this.disabled=false})};$.fn.disable=function(){return this.each(function(){this.disabled=true})}})(jQuery);(function($){$.extend({fieldEvent:function(el,obs){var field=el[0]||el,e="change";if(field.type=="radio"||field.type=="checkbox"){e="click"}else{if(obs&&field.type=="text"||field.type=="textarea"){e="keyup"}}return e}});$.fn.extend({delayedObserver:function(delay,callback){var el=$(this);if(typeof window.delayedObserverStack=="undefined"){window.delayedObserverStack=[]}if(typeof window.delayedObserverCallback=="undefined"){window.delayedObserverCallback=function(stackPos){observed=window.delayedObserverStack[stackPos];if(observed.timer){clearTimeout(observed.timer)}observed.timer=setTimeout(function(){observed.timer=null;observed.callback(observed.obj,observed.obj.formVal())},observed.delay*1000);observed.oldVal=observed.obj.formVal()}}window.delayedObserverStack.push({obj:el,timer:null,delay:delay,oldVal:el.formVal(),callback:callback});var stackPos=window.delayedObserverStack.length-1;if(el[0].tagName=="FORM"){$(":input",el).each(function(){var field=$(this);field.bind($.fieldEvent(field,delay),function(){observed=window.delayedObserverStack[stackPos];if(observed.obj.formVal()==observed.obj.oldVal){return}else{window.delayedObserverCallback(stackPos)}})})}else{el.bind($.fieldEvent(el,delay),function(){observed=window.delayedObserverStack[stackPos];if(observed.obj.formVal()==observed.obj.oldVal){return}else{window.delayedObserverCallback(stackPos)}})}},formVal:function(){var el=this[0];if(el.tagName=="FORM"){return this.serialize()}if(el.type=="checkbox"||self.type=="radio"){return this.filter("input:checked").val()||""}else{return this.val()}}})})(jQuery);(function($){$.fn.extend({visualEffect:function(o){e=o.replace(/\_(.)/g,function(m,l){return l.toUpperCase()});return eval("$(this)."+e+"()")},appear:function(speed,callback){return this.fadeIn(speed,callback)},blindDown:function(speed,callback){return this.show("blind",{direction:"vertical"},speed,callback)},blindUp:function(speed,callback){return this.hide("blind",{direction:"vertical"},speed,callback)},blindRight:function(speed,callback){return this.show("blind",{direction:"horizontal"},speed,callback)},blindLeft:function(speed,callback){this.hide("blind",{direction:"horizontal"},speed,callback);return this},dropOut:function(speed,callback){return this.hide("drop",{direction:"down"},speed,callback)},dropIn:function(speed,callback){return this.show("drop",{direction:"up"},speed,callback)},fade:function(speed,callback){return this.fadeOut(speed,callback)},fadeToggle:function(speed,callback){return this.animate({opacity:"toggle"},speed,callback)},fold:function(speed,callback){return this.hide("fold",{},speed,callback)},foldOut:function(speed,callback){return this.show("fold",{},speed,callback)},grow:function(speed,callback){return this.show("scale",{},speed,callback)},highlight:function(speed,callback){return this.show("highlight",{},speed,callback)},puff:function(speed,callback){return this.hide("puff",{},speed,callback)},pulsate:function(speed,callback){return this.show("pulsate",{},speed,callback)},shake:function(speed,callback){return this.show("shake",{},speed,callback)},shrink:function(speed,callback){return this.hide("scale",{},speed,callback)},squish:function(speed,callback){return this.hide("scale",{origin:["top","left"]},speed,callback)},slideUp:function(speed,callback){return this.hide("slide",{direction:"up"},speed,callback)},slideDown:function(speed,callback){return this.show("slide",{direction:"up"},speed,callback)},switchOff:function(speed,callback){return this.hide("clip",{},speed,callback)},switchOn:function(speed,callback){return this.show("clip",{},speed,callback)}})})(jQuery); (function($){$.ajaxSettings.accepts._default="text/javascript, text/html, application/xml, text/xml, */*"})(jQuery);(function($){$.fn.reset=function(){return this.each(function(){if(typeof this.reset=="function"||(typeof this.reset=="object"&&!this.reset.nodeType)){this.reset()}})};$.fn.enable=function(){return this.each(function(){this.disabled=false})};$.fn.disable=function(){return this.each(function(){this.disabled=true})}})(jQuery);(function($){$.extend({fieldEvent:function(el,obs){var field=el[0]||el,e="change";if(field.type=="radio"||field.type=="checkbox"){e="click"}else{if(obs&&(field.type=="text"||field.type=="textarea"||field.type=="password")){e="keyup"}}return e}});$.fn.extend({delayedObserver:function(delay,callback){var el=$(this);if(typeof window.delayedObserverStack=="undefined"){window.delayedObserverStack=[]}if(typeof window.delayedObserverCallback=="undefined"){window.delayedObserverCallback=function(stackPos){var observed=window.delayedObserverStack[stackPos];if(observed.timer){clearTimeout(observed.timer)}observed.timer=setTimeout(function(){observed.timer=null;observed.callback(observed.obj,observed.obj.formVal())},observed.delay*1000);observed.oldVal=observed.obj.formVal()}}window.delayedObserverStack.push({obj:el,timer:null,delay:delay,oldVal:el.formVal(),callback:callback});var stackPos=window.delayedObserverStack.length-1;if(el[0].tagName=="FORM"){$(":input",el).each(function(){var field=$(this);field.bind($.fieldEvent(field,delay),function(){var observed=window.delayedObserverStack[stackPos];if(observed.obj.formVal()==observed.oldVal){return}else{window.delayedObserverCallback(stackPos)}})})}else{el.bind($.fieldEvent(el,delay),function(){var observed=window.delayedObserverStack[stackPos];if(observed.obj.formVal()==observed.oldVal){return}else{window.delayedObserverCallback(stackPos)}})}},formVal:function(){var el=this[0];if(el.tagName=="FORM"){return this.serialize()}if(el.type=="checkbox"||el.type=="radio"){return this.filter("input:checked").val()||""}else{return this.val()}}})})(jQuery);(function($){$.fn.extend({visualEffect:function(o,options){if(options){speed=options.duration*1000}else{speed=null}e=o.replace(/\_(.)/g,function(m,l){return l.toUpperCase()});return eval("$(this)."+e+"("+speed+")")},appear:function(speed,callback){return this.fadeIn(speed,callback)},blindDown:function(speed,callback){return this.show("blind",{direction:"vertical"},speed,callback)},blindUp:function(speed,callback){return this.hide("blind",{direction:"vertical"},speed,callback)},blindRight:function(speed,callback){return this.show("blind",{direction:"horizontal"},speed,callback)},blindLeft:function(speed,callback){this.hide("blind",{direction:"horizontal"},speed,callback);return this},dropOut:function(speed,callback){return this.hide("drop",{direction:"down"},speed,callback)},dropIn:function(speed,callback){return this.show("drop",{direction:"up"},speed,callback)},fade:function(speed,callback){return this.fadeOut(speed,callback)},fadeToggle:function(speed,callback){return this.animate({opacity:"toggle"},speed,callback)},fold:function(speed,callback){return this.hide("fold",{},speed,callback)},foldOut:function(speed,callback){return this.show("fold",{},speed,callback)},grow:function(speed,callback){return this.show("scale",{},speed,callback)},highlight:function(speed,callback){return this.show("highlight",{},speed,callback)},puff:function(speed,callback){return this.hide("puff",{},speed,callback)},pulsate:function(speed,callback){return this.show("pulsate",{},speed,callback)},shake:function(speed,callback){return this.show("shake",{},speed,callback)},shrink:function(speed,callback){return this.hide("scale",{},speed,callback)},squish:function(speed,callback){return this.hide("scale",{origin:["top","left"]},speed,callback)},slideUp:function(speed,callback){return this.hide("slide",{direction:"up"},speed,callback)},slideDown:function(speed,callback){return this.show("slide",{direction:"up"},speed,callback)},switchOff:function(speed,callback){return this.hide("clip",{},speed,callback)},switchOn:function(speed,callback){return this.show("clip",{},speed,callback)}})})(jQuery);
jQuery(function ($) {
var csrf_token = $('meta[name=csrf-token]').attr('content'),
csrf_param = $('meta[name=csrf-param]').attr('content');
$.fn.extend({
/**
* Triggers a custom event on an element and returns the event result
* this is used to get around not being able to ensure callbacks are placed
* at the end of the chain.
*
* TODO: deprecate with jQuery 1.4.2 release, in favor of subscribing to our
* own events and placing ourselves at the end of the chain.
*/
triggerAndReturn: function (name, data) {
var event = new $.Event(name);
this.trigger(event, data);
return event.result !== false;
},
/**
* Handles execution of remote calls firing overridable events along the way
*/
callRemote: function () {
var el = this,
data = el.is('form') ? el.serializeArray() : [],
method = el.attr('method') || el.attr('data-method') || 'GET',
url = el.attr('action') || el.attr('href');
if (url === undefined) {
throw "No URL specified for remote call (action or href must be present).";
} else {
if (el.triggerAndReturn('ajax:before')) {
$.ajax({
url: url,
data: data,
dataType: 'script',
type: method.toUpperCase(),
beforeSend: function (xhr) {
el.trigger('ajax:loading', xhr);
},
success: function (data, status, xhr) {
el.trigger('ajax:success', [data, status, xhr]);
},
complete: function (xhr) {
el.trigger('ajax:complete', xhr);
},
error: function (xhr, status, error) {
el.trigger('ajax:failure', [xhr, status, error]);
}
});
}
el.trigger('ajax:after');
}
}
});
/**
* confirmation handler
*/
$('a[data-confirm],input[data-confirm]').live('click', function () {
var el = $(this);
if (el.triggerAndReturn('confirm')) {
if (!confirm(el.attr('data-confirm'))) {
return false;
}
}
});
/**
* remote handlers
*/
$('form[data-remote]').live('submit', function (e) {
$(this).callRemote();
e.preventDefault();
});
$('a[data-remote],input[data-remote]').live('click', function (e) {
$(this).callRemote();
e.preventDefault();
});
$('a[data-method]:not([data-remote])').live('click', function (e){
var link = $(this),
href = link.attr('href'),
method = link.attr('data-method'),
form = $('<form method="post" action="'+href+'">'),
metadata_input = '<input name="_method" value="'+method+'" type="hidden" />';
if (csrf_param != null && csrf_token != null) {
metadata_input += '<input name="'+csrf_param+'" value="'+csrf_token+'" type="hidden" />';
}
form.hide()
.append(metadata_input)
.appendTo('body');
e.preventDefault();
form.submit();
});
/**
* disable-with handlers
*/
var disable_with_input_selector = 'input[data-disable-with]';
var disable_with_form_selector = 'form[data-remote]:has(' + disable_with_input_selector + ')';
$(disable_with_form_selector).live('ajax:before', function () {
$(this).find(disable_with_input_selector).each(function () {
var input = $(this);
input.data('enable-with', input.val())
.attr('value', input.attr('data-disable-with'))
.attr('disabled', 'disabled');
});
});
$(disable_with_form_selector).live('ajax:after', function () {
$(this).find(disable_with_input_selector).each(function () {
var input = $(this);
input.removeAttr('disabled')
.val(input.data('enable-with'));
});
});
});

View File

@ -7,9 +7,7 @@
*/ */
(function($) { (function($) {
$().ajaxSend(function(a, xhr, s){ //Set request headers globally $.ajaxSettings.accepts._default = "text/javascript, text/html, application/xml, text/xml, */*";
xhr.setRequestHeader("Accept", "text/javascript, text/html, application/xml, text/xml, */*");
});
})(jQuery); })(jQuery);
@ -59,7 +57,7 @@
fieldEvent: function(el, obs) { fieldEvent: function(el, obs) {
var field = el[0] || el, e = 'change'; var field = el[0] || el, e = 'change';
if (field.type == 'radio' || field.type == 'checkbox') e = 'click'; if (field.type == 'radio' || field.type == 'checkbox') e = 'click';
else if (obs && field.type == 'text' || field.type == 'textarea') e = 'keyup'; else if (obs && (field.type == 'text' || field.type == 'textarea' || field.type == 'password')) e = 'keyup';
return e; return e;
} }
}); });
@ -119,9 +117,14 @@
(function($) { (function($) {
$.fn.extend({ $.fn.extend({
visualEffect : function(o) { visualEffect : function(o, options) {
if (options) {
speed = options.duration * 1000;
} else {
speed = null;
}
e = o.replace(/\_(.)/g, function(m, l){return l.toUpperCase()}); e = o.replace(/\_(.)/g, function(m, l){return l.toUpperCase()});
return eval('$(this).'+e+'()'); return eval('$(this).'+e+'('+ speed + ')');
}, },
appear : function(speed, callback) { appear : function(speed, callback) {
return this.fadeIn(speed, callback); return this.fadeIn(speed, callback);

52
vendor/plugins/jrails/jrails.gemspec vendored Normal file
View File

@ -0,0 +1,52 @@
# Generated by jeweler
# DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{jrails}
s.version = "0.6.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Aaron Eisenberger", "Patrick Hurley"]
s.date = %q{2009-10-20}
s.default_executable = %q{jrails}
s.description = %q{Using jRails, you can get all of the same default Rails helpers for javascript functionality using the lighter jQuery library.}
s.email = %q{aaronchi@gmail.com}
s.executables = ["jrails"]
s.extra_rdoc_files = [
"CHANGELOG",
"LICENSE",
"README.rdoc"
]
s.files = [
"bin/jrails",
"init.rb",
"install.rb",
"javascripts/jquery-ui.js",
"javascripts/jquery.js",
"javascripts/jrails.js",
"javascripts/sources/jrails.js",
"lib/jquery_selector_assertions.rb",
"lib/jrails.rb",
"rails/init.rb",
"tasks/jrails.rake"
]
s.homepage = %q{http://ennerchi.com/projects/jrails}
s.rdoc_options = ["--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubyforge_project = %q{jrails}
s.rubygems_version = %q{1.3.5}
s.summary = %q{jRails is a drop-in jQuery replacement for the Rails Prototype/script.aculo.us helpers.}
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
else
end
else
end
end

View File

@ -0,0 +1,78 @@
#
# jQuery Selector Assertions (modifications to the prototype/scriptaculous assertions)
#
# From http://pastie.org/303776
#
# 1. Make sure to use '#' prefix when referring to element IDs in assert_select_rjs(),
# like this:
# assert_select_rjs :replace_html, '#someid'
# instead of prototype convention:
# assert_select_rjs :replace_html, 'someid'
#
# We monkey-patch some RJS-matching constants for assert_select_rjs to work
# with jQuery-based code as opposed to Prototype's:
#
module ActionController
module Assertions
module SelectorAssertions
silence_warnings do
RJS_PATTERN_HTML = "\"((\\\\\"|[^\"])*)\""
# RJS_ANY_ID = "\"([^\"])*\""
# better match with single or double quoted ids
RJS_ANY_ID = "[\"']([^\"])*[\"']"
RJS_STATEMENTS = {
:chained_replace => "\(jQuery|$\)\\(#{RJS_ANY_ID}\\)\\.replaceWith\\(#{RJS_PATTERN_HTML}\\)",
:chained_replace_html => "\(jQuery|$\)\\(#{RJS_ANY_ID}\\)\\.updateWith\\(#{RJS_PATTERN_HTML}\\)",
:replace_html => "\(jQuery|$\)\\(#{RJS_ANY_ID}\\)\\.html\\(#{RJS_PATTERN_HTML}\\)",
:replace => "\(jQuery|$\)\\(#{RJS_ANY_ID}\\)\\.replaceWith\\(#{RJS_PATTERN_HTML}\\)",
:insert_top => "\(jQuery|$\)\\(#{RJS_ANY_ID}\\)\\.prepend\\(#{RJS_PATTERN_HTML}\\)",
:insert_bottom => "\(jQuery|$\)\\(#{RJS_ANY_ID}\\)\\.append\\(#{RJS_PATTERN_HTML}\\)",
:effect => "\(jQuery|$\)\\(#{RJS_ANY_ID}\\)\\.effect\\(",
:highlight => "\(jQuery|$\)\\(#{RJS_ANY_ID}\\)\\.effect\\('highlight'"
=begin TODO:
I've never used the chained_* so I don't know if they work.
I couldn't seem to get assert_select_rjs to actually match the single quoted ids
which are created by some of the effects like ...
... jQuery('#item_1559').effect('highlight',{},1000);
so I modified jrails/lib/jrails.rb line 337
... javascript = "#{JQUERY_VAR}('#{jquery_id(element_id)}').#{mode || 'effect'}('#{name}'"
to
... javascript = "#{JQUERY_VAR}(\"#{jquery_id(element_id)}\").#{mode || 'effect'}('#{name}'"
so it writes double quotes like most of the others. This change should probably be
done to the others, but as I don't use them so haven't tested them.
My other option seemed to require modifying rails' selector_assertions.rb line 427
... id ? statement.gsub(RJS_ANY_ID, "\"#{id}\"") : statement
which forces the expectation that the id is double quoted. If I changed it to
... statement.gsub(RJS_ANY_ID, "[\"']{1}#{id}[\"']{1}")
I believe that it would work as the logic seemed to work in some testing.
I have not actually tried to modify rails, as this file doesn't seem to
actually be in the git repository.
jrails now uses a nonconflict option so $ is jQuery. I put both in the pattern in case it gets changed.
:insert_after => "",
:insert_before => "",
=end
}
[:remove, :show, :hide, :toggle, :reset ].each do |action|
RJS_STATEMENTS[action] = "\(jQuery|$\)\\(#{RJS_ANY_ID}\\)\\.#{action}\\(\\)"
end
# TODO:
#RJS_STATEMENTS[:insert_html] = "Element.insert\\(#{RJS_ANY_ID}, \\{ (#{RJS_INSERTIONS.join('|')}):
#{RJS_PATTERN_HTML} \\}\\)"
RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
end
end
end
end

View File

@ -1,406 +1,427 @@
module ActionView module ActionView
module Helpers module Helpers
module JavaScriptHelper module JavaScriptHelper
# This function can be used to render rjs inline # This function can be used to render rjs inline
# #
# <%= javascript_function do |page| # <%= javascript_function do |page|
# page.replace_html :list, :partial => 'list', :object => @list # page.replace_html :list, :partial => 'list', :object => @list
# end %> # end %>
# #
def javascript_function(*args, &block) def javascript_function(*args, &block)
html_options = args.extract_options! html_options = args.extract_options!
function = args[0] || '' function = args[0] || ''
html_options.symbolize_keys! html_options.symbolize_keys!
function = update_page(&block) if block_given? function = update_page(&block) if block_given?
javascript_tag(function) javascript_tag(function)
end end
def jquery_id(id) def jquery_id(id)
id.to_s.count('#.*,>+~:[/ ') == 0 ? "##{id}" : id id.to_s.count('#.*,>+~:[/ ') == 0 ? "##{id}" : id
end end
def jquery_ids(ids) def jquery_ids(ids)
Array(ids).map{|id| jquery_id(id)}.join(',') Array(ids).map{|id| jquery_id(id)}.join(',')
end end
end end
module PrototypeHelper module PrototypeHelper
unless const_defined? :JQUERY_VAR USE_PROTECTION = const_defined?(:DISABLE_JQUERY_FORGERY_PROTECTION) ? !DISABLE_JQUERY_FORGERY_PROTECTION : true
JQUERY_VAR = '$'
end
unless const_defined? :JQCALLBACKS
JQCALLBACKS = Set.new([ :beforeSend, :complete, :error, :success ] + (100..599).to_a)
AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url,
:asynchronous, :method, :insertion, :position,
:form, :with, :update, :script ]).merge(JQCALLBACKS)
end
def periodically_call_remote(options = {})
frequency = options[:frequency] || 10 # every ten seconds by default
code = "setInterval(function() {#{remote_function(options)}}, #{frequency} * 1000)"
javascript_tag(code)
end
def remote_function(options)
javascript_options = options_for_ajax(options)
update = '' unless const_defined? :JQUERY_VAR
if options[:update] && options[:update].is_a?(Hash) JQUERY_VAR = 'jQuery'
update = [] end
update << "success:'#{options[:update][:success]}'" if options[:update][:success]
update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure] unless const_defined? :JQCALLBACKS
update = '{' + update.join(',') + '}' JQCALLBACKS = Set.new([ :beforeSend, :complete, :error, :success ] + (100..599).to_a)
elsif options[:update] #instance_eval { remove_const :AJAX_OPTIONS }
update << "'#{options[:update]}'" remove_const(:AJAX_OPTIONS) if const_defined?(:AJAX_OPTIONS)
end AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url,
:asynchronous, :method, :insertion, :position,
:form, :with, :update, :script ]).merge(JQCALLBACKS)
end
def periodically_call_remote(options = {})
frequency = options[:frequency] || 10 # every ten seconds by default
code = "setInterval(function() {#{remote_function(options)}}, #{frequency} * 1000)"
javascript_tag(code)
end
def remote_function(options)
javascript_options = options_for_ajax(options)
function = "#{JQUERY_VAR}.ajax(#{javascript_options})" update = ''
if options[:update] && options[:update].is_a?(Hash)
update = []
update << "success:'#{options[:update][:success]}'" if options[:update][:success]
update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure]
update = '{' + update.join(',') + '}'
elsif options[:update]
update << "'#{options[:update]}'"
end
function = "#{options[:before]}; #{function}" if options[:before] function = "#{JQUERY_VAR}.ajax(#{javascript_options})"
function = "#{function}; #{options[:after]}" if options[:after]
function = "if (#{options[:condition]}) { #{function}; }" if options[:condition]
function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm]
return function
end
class JavaScriptGenerator
module GeneratorMethods
def insert_html(position, id, *options_for_render)
insertion = position.to_s.downcase
insertion = 'append' if insertion == 'bottom'
insertion = 'prepend' if insertion == 'top'
call "#{JQUERY_VAR}(\"#{jquery_id(id)}\").#{insertion}", render(*options_for_render)
end
def replace_html(id, *options_for_render)
insert_html(:html, id, *options_for_render)
end
def replace(id, *options_for_render)
call "#{JQUERY_VAR}(\"#{jquery_id(id)}\").replaceWith", render(*options_for_render)
end
def remove(*ids)
call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").remove"
end
def show(*ids)
call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").show"
end
def hide(*ids)
call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").hide"
end
def toggle(*ids) function = "#{options[:before]}; #{function}" if options[:before]
call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").toggle" function = "#{function}; #{options[:after]}" if options[:after]
end function = "if (#{options[:condition]}) { #{function}; }" if options[:condition]
function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm]
def jquery_id(id) return function
id.to_s.count('#.*,>+~:[/ ') == 0 ? "##{id}" : id end
end
class JavaScriptGenerator
def jquery_ids(ids) module GeneratorMethods
Array(ids).map{|id| jquery_id(id)}.join(',')
end def insert_html(position, id, *options_for_render)
insertion = position.to_s.downcase
end insertion = 'append' if insertion == 'bottom'
end insertion = 'prepend' if insertion == 'top'
call "#{JQUERY_VAR}(\"#{jquery_id(id)}\").#{insertion}", render(*options_for_render)
protected end
def options_for_ajax(options)
js_options = build_callbacks(options) def replace_html(id, *options_for_render)
insert_html(:html, id, *options_for_render)
url_options = options[:url] end
url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash)
js_options['url'] = "'#{url_for(url_options)}'" def replace(id, *options_for_render)
js_options['async'] = false if options[:type] == :synchronous call "#{JQUERY_VAR}(\"#{jquery_id(id)}\").replaceWith", render(*options_for_render)
js_options['type'] = options[:method] ? method_option_to_s(options[:method]) : ( options[:form] ? "'post'" : nil ) end
js_options['dataType'] = options[:datatype] ? "'#{options[:datatype]}'" : (options[:update] ? nil : "'script'")
def remove(*ids)
if options[:form] call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").remove"
js_options['data'] = "#{JQUERY_VAR}.param(#{JQUERY_VAR}(this).serializeArray())" end
elsif options[:submit]
js_options['data'] = "#{JQUERY_VAR}(\"##{options[:submit]} :input\").serialize()" def show(*ids)
elsif options[:with] call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").show"
js_options['data'] = options[:with].gsub("Form.serialize(this.form)","#{JQUERY_VAR}.param(#{JQUERY_VAR}(this.form).serializeArray())") end
end
def hide(*ids)
js_options['type'] ||= "'post'" call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").hide"
if options[:method] end
if method_option_to_s(options[:method]) == "'put'" || method_option_to_s(options[:method]) == "'delete'"
js_options['type'] = "'post'"
if js_options['data']
js_options['data'] << " + '&"
else
js_options['data'] = "'"
end
js_options['data'] << "_method=#{options[:method]}'"
end
end
if respond_to?('protect_against_forgery?') && protect_against_forgery?
if js_options['data']
js_options['data'] << " + '&"
else
js_options['data'] = "'"
end
js_options['data'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')"
end
js_options['data'] = "''" if js_options['type'] == "'post'" && js_options['data'].nil?
options_for_javascript(js_options.reject {|key, value| value.nil?})
end
def build_update_for_success(html_id, insertion=nil)
insertion = build_insertion(insertion)
"#{JQUERY_VAR}('#{jquery_id(html_id)}').#{insertion}(request);"
end
def build_update_for_error(html_id, insertion=nil) def toggle(*ids)
insertion = build_insertion(insertion) call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").toggle"
"#{JQUERY_VAR}('#{jquery_id(html_id)}').#{insertion}(request.responseText);" end
end
def jquery_id(id)
id.to_s.count('#.*,>+~:[/ ') == 0 ? "##{id}" : id
end
def jquery_ids(ids)
Array(ids).map{|id| jquery_id(id)}.join(',')
end
def prepend(id, *option_for_render)
insert_html(:prepend, id, *options_for_render)
end
end
end
protected
def options_for_ajax(options)
js_options = build_callbacks(options)
url_options = options[:url]
url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash)
js_options['url'] = "'#{url_for(url_options)}'"
js_options['async'] = false if options[:type] == :synchronous
js_options['type'] = options[:method] ? method_option_to_s(options[:method]) : ( options[:form] ? "'post'" : nil )
js_options['dataType'] = options[:datatype] ? "'#{options[:datatype]}'" : (options[:update] ? nil : "'script'")
if options[:form]
js_options['data'] = "#{JQUERY_VAR}.param(#{JQUERY_VAR}(this).serializeArray())"
elsif options[:submit]
js_options['data'] = "#{JQUERY_VAR}(\"##{options[:submit]} :input\").serialize()"
elsif options[:with]
js_options['data'] = options[:with].gsub("Form.serialize(this.form)","#{JQUERY_VAR}.param(#{JQUERY_VAR}(this.form).serializeArray())")
end
js_options['type'] ||= "'post'"
if options[:method]
if method_option_to_s(options[:method]) == "'put'" || method_option_to_s(options[:method]) == "'delete'"
js_options['type'] = "'post'"
if js_options['data']
js_options['data'] << " + '&"
else
js_options['data'] = "'"
end
js_options['data'] << "_method=#{options[:method]}'"
end
end
if USE_PROTECTION && respond_to?('protect_against_forgery?') && protect_against_forgery?
if js_options['data']
js_options['data'] << " + '&"
else
js_options['data'] = "'"
end
js_options['data'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')"
end
js_options['data'] = "''" if js_options['type'] == "'post'" && js_options['data'].nil?
options_for_javascript(js_options.reject {|key, value| value.nil?})
end
def build_update_for_success(html_id, insertion=nil)
insertion = build_insertion(insertion)
"#{JQUERY_VAR}('#{jquery_id(html_id)}').#{insertion}(request);"
end
def build_insertion(insertion) def build_update_for_error(html_id, insertion=nil)
insertion = insertion ? insertion.to_s.downcase : 'html' insertion = build_insertion(insertion)
insertion = 'append' if insertion == 'bottom' "#{JQUERY_VAR}('#{jquery_id(html_id)}').#{insertion}(request.responseText);"
insertion = 'prepend' if insertion == 'top' end
insertion
end
def build_observer(klass, name, options = {}) def build_insertion(insertion)
if options[:with] && (options[:with] !~ /[\{=(.]/) insertion = insertion ? insertion.to_s.downcase : 'html'
options[:with] = "'#{options[:with]}=' + value" insertion = 'append' if insertion == 'bottom'
else insertion = 'prepend' if insertion == 'top'
options[:with] ||= 'value' unless options[:function] insertion
end end
callback = options[:function] || remote_function(options) def build_observer(klass, name, options = {})
javascript = "#{JQUERY_VAR}('#{jquery_id(name)}').delayedObserver(" if options[:with] && (options[:with] !~ /[\{=(.]/)
javascript << "#{options[:frequency] || 0}, " options[:with] = "'#{options[:with]}=' + value"
javascript << "function(element, value) {" else
javascript << "#{callback}}" options[:with] ||= 'value' unless options[:function]
#javascript << ", '#{options[:on]}'" if options[:on] end
javascript << ")"
javascript_tag(javascript)
end
def build_callbacks(options)
callbacks = {}
options[:beforeSend] = '';
[:uninitialized,:loading,:loaded].each do |key|
options[:beforeSend] << (options[key].last == ';' ? options.delete(key) : options.delete(key) << ';') if options[key]
end
options.delete(:beforeSend) if options[:beforeSend].blank?
options[:error] = options.delete(:failure) if options[:failure]
if options[:update]
if options[:update].is_a?(Hash)
options[:update][:error] = options[:update].delete(:failure) if options[:update][:failure]
if options[:update][:success]
options[:success] = build_update_for_success(options[:update][:success], options[:position]) << (options[:success] ? options[:success] : '')
end
if options[:update][:error]
options[:error] = build_update_for_error(options[:update][:error], options[:position]) << (options[:error] ? options[:error] : '')
end
else
options[:success] = build_update_for_success(options[:update], options[:position]) << (options[:success] ? options[:success] : '')
end
end
options.each do |callback, code|
if JQCALLBACKS.include?(callback)
callbacks[callback] = "function(request){#{code}}"
end
end
callbacks
end
end
class JavaScriptElementProxy < JavaScriptProxy #:nodoc:
unless const_defined? :JQUERY_VAR
JQUERY_VAR = ActionView::Helpers::PrototypeHelper::JQUERY_VAR
end
def initialize(generator, id)
id = id.to_s.count('#.*,>+~:[/ ') == 0 ? "##{id}" : id
@id = id
super(generator, "#{JQUERY_VAR}(\"#{id}\")")
end
def replace_html(*options_for_render)
call 'html', @generator.send(:render, *options_for_render)
end
def replace(*options_for_render) callback = options[:function] || remote_function(options)
call 'replaceWith', @generator.send(:render, *options_for_render) javascript = "#{JQUERY_VAR}('#{jquery_id(name)}').delayedObserver("
end javascript << "#{options[:frequency] || 0}, "
javascript << "function(element, value) {"
def value() javascript << "#{callback}}"
call 'val()' #javascript << ", '#{options[:on]}'" if options[:on]
end javascript << ")"
javascript_tag(javascript)
end
def build_callbacks(options)
callbacks = {}
options[:beforeSend] = '';
[:uninitialized,:loading].each do |key|
options[:beforeSend] << (options[key].last == ';' ? options.delete(key) : options.delete(key) << ';') if options[key]
end
options.delete(:beforeSend) if options[:beforeSend].blank?
options[:complete] = options.delete(:loaded) if options[:loaded]
options[:error] = options.delete(:failure) if options[:failure]
if options[:update]
if options[:update].is_a?(Hash)
options[:update][:error] = options[:update].delete(:failure) if options[:update][:failure]
if options[:update][:success]
options[:success] = build_update_for_success(options[:update][:success], options[:position]) << (options[:success] ? options[:success] : '')
end
if options[:update][:error]
options[:error] = build_update_for_error(options[:update][:error], options[:position]) << (options[:error] ? options[:error] : '')
end
else
options[:success] = build_update_for_success(options[:update], options[:position]) << (options[:success] ? options[:success] : '')
end
end
options.each do |callback, code|
if JQCALLBACKS.include?(callback)
callbacks[callback] = "function(request){#{code}}"
end
end
callbacks
end
end
class JavaScriptElementProxy < JavaScriptProxy #:nodoc:
unless const_defined? :JQUERY_VAR
JQUERY_VAR = PrototypeHelper::JQUERY_VAR
end
def initialize(generator, id)
id = id.to_s.count('#.*,>+~:[/ ') == 0 ? "##{id}" : id
@id = id
super(generator, "#{JQUERY_VAR}(\"#{id}\")")
end
def replace_html(*options_for_render)
call 'html', @generator.send(:render, *options_for_render)
end
def value=(value) def replace(*options_for_render)
call 'val', value call 'replaceWith', @generator.send(:render, *options_for_render)
end end
end def reload(options_for_replace={})
replace(options_for_replace.merge({ :partial => @id.to_s.sub(/^#/,'') }))
class JavaScriptElementCollectionProxy < JavaScriptCollectionProxy #:nodoc:\ end
unless const_defined? :JQUERY_VAR def value()
JQUERY_VAR = ActionView::Helpers::PrototypeHelper::JQUERY_VAR call 'val()'
end end
def initialize(generator, pattern)
super(generator, "#{JQUERY_VAR}(#{pattern.to_json})")
end
end
module ScriptaculousHelper
unless const_defined? :JQUERY_VAR
JQUERY_VAR = ActionView::Helpers::PrototypeHelper::JQUERY_VAR
end
unless const_defined? :SCRIPTACULOUS_EFFECTS
SCRIPTACULOUS_EFFECTS = {
:appear => {:method => 'fadeIn'},
:blind_down => {:method => 'blind', :mode => 'show', :options => {:direction => 'vertical'}},
:blind_up => {:method => 'blind', :mode => 'hide', :options => {:direction => 'vertical'}},
:blind_right => {:method => 'blind', :mode => 'show', :options => {:direction => 'horizontal'}},
:blind_left => {:method => 'blind', :mode => 'hide', :options => {:direction => 'horizontal'}},
:bounce_in => {:method => 'bounce', :mode => 'show', :options => {:direction => 'up'}},
:bounce_out => {:method => 'bounce', :mode => 'hide', :options => {:direction => 'up'}},
:drop_in => {:method => 'drop', :mode => 'show', :options => {:direction => 'up'}},
:drop_out => {:method => 'drop', :mode => 'hide', :options => {:direction => 'down'}},
:fade => {:method => 'fadeOut'},
:fold_in => {:method => 'fold', :mode => 'hide'},
:fold_out => {:method => 'fold', :mode => 'show'},
:grow => {:method => 'scale', :mode => 'show'},
:shrink => {:method => 'scale', :mode => 'hide'},
:slide_down => {:method => 'slide', :mode => 'show', :options => {:direction => 'up'}},
:slide_up => {:method => 'slide', :mode => 'hide', :options => {:direction => 'up'}},
:slide_right => {:method => 'slide', :mode => 'show', :options => {:direction => 'left'}},
:slide_left => {:method => 'slide', :mode => 'hide', :options => {:direction => 'left'}},
:squish => {:method => 'scale', :mode => 'hide', :options => {:origin => "['top','left']"}},
:switch_on => {:method => 'clip', :mode => 'show', :options => {:direction => 'vertical'}},
:switch_off => {:method => 'clip', :mode => 'hide', :options => {:direction => 'vertical'}},
:toggle_appear => {:method => 'fadeToggle'},
:toggle_slide => {:method => 'slide', :mode => 'toggle', :options => {:direction => 'up'}},
:toggle_blind => {:method => 'blind', :mode => 'toggle', :options => {:direction => 'vertical'}},
}
end
def visual_effect(name, element_id = false, js_options = {})
element = element_id ? element_id : "this"
if SCRIPTACULOUS_EFFECTS.has_key? name.to_sym
effect = SCRIPTACULOUS_EFFECTS[name.to_sym]
name = effect[:method]
mode = effect[:mode]
js_options = js_options.merge(effect[:options]) if effect[:options]
end
[:color, :direction].each do |option|
js_options[option] = "'#{js_options[option]}'" if js_options[option]
end
if js_options.has_key? :duration
speed = js_options.delete :duration
speed = (speed * 1000).to_i unless speed.nil?
else
speed = js_options.delete :speed
end
if ['fadeIn','fadeOut','fadeToggle'].include?(name)
javascript = "#{JQUERY_VAR}('#{jquery_id(element_id)}').#{name}("
javascript << "#{speed}" unless speed.nil?
javascript << ");"
else
javascript = "#{JQUERY_VAR}('#{jquery_id(element_id)}').#{mode || 'effect'}('#{name}'"
javascript << ",#{options_for_javascript(js_options)}" unless speed.nil? && js_options.empty?
javascript << ",#{speed}" unless speed.nil?
javascript << ");"
end
end
def sortable_element_js(element_id, options = {}) #:nodoc:
#convert similar attributes
options[:handle] = ".#{options[:handle]}" if options[:handle]
if options[:tag] || options[:only]
options[:items] = "> "
options[:items] << options.delete(:tag) if options[:tag]
options[:items] << ".#{options.delete(:only)}" if options[:only]
end
options[:connectWith] = options.delete(:containment).map {|x| "##{x}"} if options[:containment]
options[:containment] = options.delete(:container) if options[:container]
options[:dropOnEmpty] = false unless options[:dropOnEmpty]
options[:helper] = "'clone'" if options[:ghosting] == true
options[:axis] = case options.delete(:constraint)
when "vertical"
"y"
when "horizontal"
"x"
when false
nil
when nil
"y"
end
options.delete(:axis) if options[:axis].nil?
options.delete(:overlap)
options.delete(:ghosting)
if options[:onUpdate] || options[:url]
options[:with] ||= "#{JQUERY_VAR}(this).sortable('serialize',{key:'#{element_id}'})"
options[:onUpdate] ||= "function(){" + remote_function(options) + "}"
end
options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
options[:update] = options.delete(:onUpdate) if options[:onUpdate]
[:axis, :cancel, :containment, :cursor, :handle, :tolerance, :items, :placeholder].each do |option|
options[option] = "'#{options[option]}'" if options[option]
end
options[:connectWith] = array_or_string_for_javascript(options[:connectWith]) if options[:connectWith]
%(#{JQUERY_VAR}('#{jquery_id(element_id)}').sortable(#{options_for_javascript(options)});)
end
def draggable_element_js(element_id, options = {})
%(#{JQUERY_VAR}("#{jquery_id(element_id)}").draggable(#{options_for_javascript(options)});)
end
def drop_receiving_element_js(element_id, options = {})
#convert similar options
options[:hoverClass] = options.delete(:hoverclass) if options[:hoverclass]
options[:drop] = options.delete(:onDrop) if options[:onDrop]
if options[:drop] || options[:url]
options[:with] ||= "'id=' + encodeURIComponent(#{JQUERY_VAR}(ui.draggable).attr('id'))"
options[:drop] ||= "function(ev, ui){" + remote_function(options) + "}"
end
options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept] def value=(value)
[:activeClass, :hoverClass, :tolerance].each do |option| call 'val', value
options[option] = "'#{options[option]}'" if options[option] end
end
end
%(#{JQUERY_VAR}('#{jquery_id(element_id)}').droppable(#{options_for_javascript(options)});)
end class JavaScriptElementCollectionProxy < JavaScriptCollectionProxy #:nodoc:\
end unless const_defined? :JQUERY_VAR
JQUERY_VAR = PrototypeHelper::JQUERY_VAR
end end
def initialize(generator, pattern)
super(generator, "#{JQUERY_VAR}(#{pattern.to_json})")
end
end
module ScriptaculousHelper
unless const_defined? :JQUERY_VAR
JQUERY_VAR = PrototypeHelper::JQUERY_VAR
end
unless const_defined? :SCRIPTACULOUS_EFFECTS
SCRIPTACULOUS_EFFECTS = {
:appear => {:method => 'fadeIn'},
:blind_down => {:method => 'blind', :mode => 'show', :options => {:direction => 'vertical'}},
:blind_up => {:method => 'blind', :mode => 'hide', :options => {:direction => 'vertical'}},
:blind_right => {:method => 'blind', :mode => 'show', :options => {:direction => 'horizontal'}},
:blind_left => {:method => 'blind', :mode => 'hide', :options => {:direction => 'horizontal'}},
:bounce_in => {:method => 'bounce', :mode => 'show', :options => {:direction => 'up'}},
:bounce_out => {:method => 'bounce', :mode => 'hide', :options => {:direction => 'up'}},
:drop_in => {:method => 'drop', :mode => 'show', :options => {:direction => 'up'}},
:drop_out => {:method => 'drop', :mode => 'hide', :options => {:direction => 'down'}},
:fade => {:method => 'fadeOut'},
:fold_in => {:method => 'fold', :mode => 'hide'},
:fold_out => {:method => 'fold', :mode => 'show'},
:grow => {:method => 'scale', :mode => 'show'},
:shrink => {:method => 'scale', :mode => 'hide'},
:slide_down => {:method => 'slide', :mode => 'show', :options => {:direction => 'up'}},
:slide_up => {:method => 'slide', :mode => 'hide', :options => {:direction => 'up'}},
:slide_right => {:method => 'slide', :mode => 'show', :options => {:direction => 'left'}},
:slide_left => {:method => 'slide', :mode => 'hide', :options => {:direction => 'left'}},
:squish => {:method => 'scale', :mode => 'hide', :options => {:origin => "['top','left']"}},
:switch_on => {:method => 'clip', :mode => 'show', :options => {:direction => 'vertical'}},
:switch_off => {:method => 'clip', :mode => 'hide', :options => {:direction => 'vertical'}},
:toggle_appear => {:method => 'fadeToggle'},
:toggle_slide => {:method => 'slide', :mode => 'toggle', :options => {:direction => 'up'}},
:toggle_blind => {:method => 'blind', :mode => 'toggle', :options => {:direction => 'vertical'}},
}
end
def visual_effect(name, element_id = false, js_options = {})
element = element_id ? element_id : "this"
if SCRIPTACULOUS_EFFECTS.has_key? name.to_sym
effect = SCRIPTACULOUS_EFFECTS[name.to_sym]
name = effect[:method]
mode = effect[:mode]
js_options = js_options.merge(effect[:options]) if effect[:options]
end
[:color, :direction, :startcolor, :endcolor].each do |option|
js_options[option] = "'#{js_options[option]}'" if js_options[option]
end
if js_options.has_key? :duration
speed = js_options.delete :duration
speed = (speed * 1000).to_i unless speed.nil?
else
speed = js_options.delete :speed
end
if ['fadeIn','fadeOut','fadeToggle'].include?(name)
# 090905 - Jake - changed ' to \" so it passes assert_select_rjs with an id
javascript = "#{JQUERY_VAR}(\"#{jquery_id(element_id)}\").#{name}("
javascript << "#{speed}" unless speed.nil?
javascript << ");"
else
# 090905 - Jake - changed ' to \" so it passes "assert_select_rjs :effect, ID"
javascript = "#{JQUERY_VAR}(\"#{jquery_id(element_id)}\").#{mode || 'effect'}('#{name}'"
javascript << ",#{options_for_javascript(js_options)}" unless speed.nil? && js_options.empty?
javascript << ",#{speed}" unless speed.nil?
javascript << ");"
end
end
def sortable_element_js(element_id, options = {}) #:nodoc:
#convert similar attributes
options[:handle] = ".#{options[:handle]}" if options[:handle]
if options[:tag] || options[:only]
options[:items] = "> "
options[:items] << options.delete(:tag) if options[:tag]
options[:items] << ".#{options.delete(:only)}" if options[:only]
end
options[:connectWith] = options.delete(:containment).map {|x| "##{x}"} if options[:containment]
options[:containment] = options.delete(:container) if options[:container]
options[:dropOnEmpty] = false unless options[:dropOnEmpty]
options[:helper] = "'clone'" if options[:ghosting] == true
options[:axis] = case options.delete(:constraint)
when "vertical", :vertical
"y"
when "horizontal", :horizontal
"x"
when false
nil
when nil
"y"
end
options.delete(:axis) if options[:axis].nil?
options.delete(:overlap)
options.delete(:ghosting)
if options[:onUpdate] || options[:url]
if options[:format]
options[:with] ||= "#{JQUERY_VAR}(this).sortable('serialize',{key:'#{element_id}[]', expression:#{options[:format]}})"
options.delete(:format)
else
options[:with] ||= "#{JQUERY_VAR}(this).sortable('serialize',{key:'#{element_id}[]'})"
end
options[:onUpdate] ||= "function(){" + remote_function(options) + "}"
end
options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
options[:update] = options.delete(:onUpdate) if options[:onUpdate]
[:axis, :cancel, :containment, :cursor, :handle, :tolerance, :items, :placeholder].each do |option|
options[option] = "'#{options[option]}'" if options[option]
end
options[:connectWith] = array_or_string_for_javascript(options[:connectWith]) if options[:connectWith]
%(#{JQUERY_VAR}('#{jquery_id(element_id)}').sortable(#{options_for_javascript(options)});)
end
def draggable_element_js(element_id, options = {})
%(#{JQUERY_VAR}("#{jquery_id(element_id)}").draggable(#{options_for_javascript(options)});)
end
def drop_receiving_element_js(element_id, options = {})
#convert similar options
options[:hoverClass] = options.delete(:hoverclass) if options[:hoverclass]
options[:drop] = options.delete(:onDrop) if options[:onDrop]
if options[:drop] || options[:url]
options[:with] ||= "'id=' + encodeURIComponent(#{JQUERY_VAR}(ui.draggable).attr('id'))"
options[:drop] ||= "function(ev, ui){" + remote_function(options) + "}"
end
options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept]
[:activeClass, :hoverClass, :tolerance].each do |option|
options[option] = "'#{options[option]}'" if options[option]
end
%(#{JQUERY_VAR}('#{jquery_id(element_id)}').droppable(#{options_for_javascript(options)});)
end
end
end
end end

16
vendor/plugins/jrails/rails/init.rb vendored Normal file
View File

@ -0,0 +1,16 @@
# The following options can be changed by creating an initializer in config/initializers/jrails.rb
# jRails uses jQuery.noConflict() by default
# to use the default jQuery varibale, use:
# ActionView::Helpers::PrototypeHelper::JQUERY_VAR = '$'
# ActionView::Helpers::PrototypeHelper:: DISABLE_JQUERY_FORGERY_PROTECTION
# Set this to disable forgery protection in ajax calls
# This is handy if you want to use caching with ajax by injecting the forgery token via another means
# for an example, see http://henrik.nyh.se/2008/05/rails-authenticity-token-with-jquery
# ActionView::Helpers::PrototypeHelper::DISABLE_JQUERY_FORGERY_PROTECTION = true
ActionView::Helpers::AssetTagHelper::JAVASCRIPT_DEFAULT_SOURCES = ['jquery','jquery-ui','jrails']
ActionView::Helpers::AssetTagHelper::reset_javascript_include_default
require 'jrails'
require 'jquery_selector_assertions' if RAILS_ENV == 'test'

View File

@ -1,19 +1,25 @@
namespace :jrails do namespace :jrails do
namespace :update do
namespace :js do
desc "Copies the jQuery and jRails javascripts to public/javascripts" desc "Copies the jQuery and jRails javascripts to public/javascripts"
task :javascripts do task :install do
puts "Copying files..." puts "Copying files..."
project_dir = RAILS_ROOT + '/public/javascripts/' project_dir = RAILS_ROOT + '/public/javascripts/'
scripts = Dir[File.join(File.dirname(__FILE__), '..') + '/javascripts/*.js'] scripts = Dir[File.join(File.dirname(__FILE__), '..', '/javascripts/', '*.js')]
FileUtils.cp(scripts, project_dir) FileUtils.cp(scripts, project_dir)
puts "files copied successfully." puts "files copied successfully."
end end
end
desc 'Remove the prototype / script.aculo.us javascript files'
namespace :install do task :scrub do
desc "Installs the jQuery and jRails javascripts to public/javascripts" puts "Removing files..."
task :javascripts do files = %W[controls.js dragdrop.js effects.js prototype.js]
Rake::Task['jrails:update:javascripts'].invoke project_dir = File.join(RAILS_ROOT, 'public', 'javascripts')
end files.each do |fname|
end FileUtils.rm(File.join(project_dir, fname)) if File.exists?(File.join(project_dir, fname))
end
puts "files removed successfully."
end
end
end end