diff --git a/.autotest b/.autotest index a0d2a937..d4b0ff1b 100644 --- a/.autotest +++ b/.autotest @@ -2,6 +2,7 @@ require 'autotest/bundler' Autotest.add_hook :initialize do |at| at.add_exception '.git' + at.add_exception '.redcar' at.add_exception 'spec-results/index.html' at.add_exception 'features_report.html' at.add_exception /capybara-\d+\.html$/ diff --git a/.gitignore b/.gitignore index 5ace98b2..3d43a748 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ features_report.html config/config.yml config/deploy.rb config/database.yml +.redcar *~ diff --git a/.rspec.travis b/.rspec.travis new file mode 100644 index 00000000..89da67b6 --- /dev/null +++ b/.rspec.travis @@ -0,0 +1,2 @@ +--colour +--format progress diff --git a/.ruby-gemset b/.ruby-gemset new file mode 100644 index 00000000..5780bbe7 --- /dev/null +++ b/.ruby-gemset @@ -0,0 +1 @@ +quorum2 \ No newline at end of file diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..068e6289 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +ruby-1.9.2-p320 \ No newline at end of file diff --git a/.rvmrc b/.rvmrc deleted file mode 100644 index d5c28e3d..00000000 --- a/.rvmrc +++ /dev/null @@ -1 +0,0 @@ -rvm use 1.9.3@quorum2 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..7f7c48e6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: ruby +rvm: + - 1.9.3 +bundler_args: --without development +services: postgresql +before_install: + - gem update --system 1.8.25 # since ZenTest needs ~> 1.8 + - gem --version +before_script: ./ci/before_script.sh +script: rake ci \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b6b18371..7ee4aee7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,4 +36,45 @@ Quorum is distributed under the [BSD 3-Clause License](http://www.opensource.org ## v0.3.8, 23 September 2011 * Replace many controller specs with Cucumber stories [#54] -* Deal better with HTML escaping \ No newline at end of file +* Deal better with HTML escaping + +## v0.3.9, 28 December 2011 +* Fix iCal export issues: we were exporting an invalid iCalendar file [#65] + +## v0.4.0, 29 December 2011 +* Remove make_resourceful now that Rails offers `respond_with` [#64] + +## v0.5.0, 12 March 2012 +* Add the ability to make comments on one's commitment status [#69] + +## v0.5.1, 12 March 2012 +* Fix invalid HTML on event list [#71] + +## v0.5.2, 12 March 2012 +* Autolink URLs in event descriptions [#75] + +## v0.5.3, 13 March 2012 +* Show commitment status icons on comments [#74] + +## v0.5.4, 13 March 2012 +* Bugfix. + +## v0.5.5, 2 July 2012 +* Put event comments on separate lines [#77] +* Add application information in footer [#79] + +## v0.5.6, v0.5.7, 2 July 2012 +* Bugfix. + +## v0.5.8, 2 July 2012 +* Put attendance icons in "attending" and "not attending" columns [#81] + +## v0.5.9, 30 January 2013 +* Update to Rails 3.0.20 to patch security hole + +## v0.5.10, 5 August 2013 +* Make some configuration changes for Passenger +* Remove YM4R and Spatial Adapter, since neither is currently supported [#82, #86] + +## v0.5.11, 23 October 2013 +* Add patch for ActionMailer vulnerability at http://seclists.org/oss-sec/2013/q4/118 [#88] \ No newline at end of file diff --git a/Gemfile b/Gemfile index 72336540..ab73be3d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'http://rubygems.org' -gem 'rails', '3.0.10' +gem 'rails', '3.0.20' # Bundle edge Rails instead: # gem 'rails', :git => 'git://github.com/rails/rails.git' @@ -26,8 +26,10 @@ gem 'sass' gem 'pg' gem 'gettext_i18n_rails' gem 'prawn' -gem 'GeoRuby' +gem 'geocoder', '~> 1.1.8' gem 'rdiscount' +gem 'rgeo-activerecord', github: 'marnen/rgeo-activerecord', branch: 'fix-proc-error-in-default-factory' # TODO: waiting for https://github.com/dazuma/rgeo-activerecord/pull/10 +gem 'activerecord-postgis-adapter' gem 'authlogic', '~> 3.0.3' gem 'dynamic_form' gem 'exception_notification' @@ -40,14 +42,14 @@ gem 'exception_notification' # end group :development do - gem 'capistrano' + gem 'rvm-capistrano' gem 'gettext', '>= 1.9.3', :require => false + gem 'ruby-debug19' end group :test, :development do - gem 'ruby-debug19' gem 'autotest-rails', :require => false - gem 'rspec-rails', '~> 2.6.1', :require => false + gem 'rspec-rails', '~> 2.11.0', :require => false gem 'test-unit', '1.2.3', :require => false # amazingly, RSpec needs this gem 'cucumber-rails', :require => false gem 'launchy' diff --git a/Gemfile.lock b/Gemfile.lock index 6af0d102..5b600715 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,35 +1,46 @@ +GIT + remote: git://github.com/marnen/rgeo-activerecord.git + revision: 26bd2daa1650b35643a3f0aef9ae82186bf1556b + branch: fix-proc-error-in-default-factory + specs: + rgeo-activerecord (0.5.0) + activerecord (>= 3.0.3) + arel (>= 2.0.6) + rgeo (>= 0.3.20) + GEM remote: http://rubygems.org/ specs: - GeoRuby (1.3.3) ZenTest (4.6.2) abstract (1.0.0) - actionmailer (3.0.10) - actionpack (= 3.0.10) + actionmailer (3.0.20) + actionpack (= 3.0.20) mail (~> 2.2.19) - actionpack (3.0.10) - activemodel (= 3.0.10) - activesupport (= 3.0.10) + actionpack (3.0.20) + activemodel (= 3.0.20) + activesupport (= 3.0.20) builder (~> 2.1.2) erubis (~> 2.6.6) i18n (~> 0.5.0) - rack (~> 1.2.1) + rack (~> 1.2.5) rack-mount (~> 0.6.14) rack-test (~> 0.5.7) tzinfo (~> 0.3.23) - activemodel (3.0.10) - activesupport (= 3.0.10) + activemodel (3.0.20) + activesupport (= 3.0.20) builder (~> 2.1.2) i18n (~> 0.5.0) - activerecord (3.0.10) - activemodel (= 3.0.10) - activesupport (= 3.0.10) + activerecord (3.0.20) + activemodel (= 3.0.20) + activesupport (= 3.0.20) arel (~> 2.0.10) tzinfo (~> 0.3.23) - activeresource (3.0.10) - activemodel (= 3.0.10) - activesupport (= 3.0.10) - activesupport (3.0.10) + activerecord-postgis-adapter (0.6.5) + rgeo-activerecord (~> 0.5.0) + activeresource (3.0.20) + activemodel (= 3.0.20) + activesupport (= 3.0.20) + activesupport (3.0.20) addressable (2.2.6) archive-tar-minitar (0.5.2) arel (2.0.10) @@ -39,7 +50,7 @@ GEM autotest-rails (4.1.0) ZenTest builder (2.1.2) - capistrano (2.8.0) + capistrano (2.15.5) highline net-scp (>= 1.0.0) net-sftp (>= 2.0.0) @@ -65,31 +76,33 @@ GEM capybara (>= 1.0.0) cucumber (~> 1.0.0) nokogiri (>= 1.4.6) - database_cleaner (0.6.7) + database_cleaner (1.1.1) diff-lcs (1.1.3) dynamic_form (1.1.4) erubis (2.6.6) abstract (>= 1.0.0) exception_notification (2.5.2) actionmailer (>= 3.0.4) - factory_girl (2.1.0) - factory_girl_rails (1.2.0) - factory_girl (~> 2.1.0) + factory_girl (4.2.0) + activesupport (>= 3.0.0) + factory_girl_rails (4.2.1) + factory_girl (~> 4.2.0) railties (>= 3.0.0) fast_gettext (0.5.13) ffaker (1.8.1) ffi (1.0.9) + geocoder (1.1.8) gettext (2.0.0) gettext_i18n_rails (0.2.20) fast_gettext gherkin (2.4.16) json (>= 1.4.6) haml (3.0.25) - highline (1.6.2) - hoe (2.12.3) - rake (~> 0.8) + highline (1.6.19) + hoe (3.5.0) + rake (>= 0.8, < 11.0) i18n (0.5.0) - json (1.5.4) + json (1.8.0) json_pure (1.5.4) spruz (~> 0.2.8) launchy (2.0.5) @@ -101,58 +114,60 @@ GEM i18n (>= 0.4.0) mime-types (~> 1.16) treetop (~> 1.4.8) - mime-types (1.16) - net-scp (1.0.4) - net-ssh (>= 1.99.1) - net-sftp (2.0.5) - net-ssh (>= 2.0.9) - net-ssh (2.2.1) - net-ssh-gateway (1.1.0) - net-ssh (>= 1.99.1) + mime-types (1.19) + net-scp (1.1.2) + net-ssh (>= 2.6.5) + net-sftp (2.1.2) + net-ssh (>= 2.6.5) + net-ssh (2.6.8) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) nokogiri (1.5.0) pg (0.11.0) pickle (0.4.8) cucumber (>= 0.8) rake - polyglot (0.3.2) + polyglot (0.3.3) prawn (0.4.0) prawn-layout prawn-layout (0.1.0) - rack (1.2.3) + rack (1.2.8) rack-mount (0.6.14) rack (>= 1.0.0) rack-test (0.5.7) rack (>= 1.0) - rails (3.0.10) - actionmailer (= 3.0.10) - actionpack (= 3.0.10) - activerecord (= 3.0.10) - activeresource (= 3.0.10) - activesupport (= 3.0.10) + rails (3.0.20) + actionmailer (= 3.0.20) + actionpack (= 3.0.20) + activerecord (= 3.0.20) + activeresource (= 3.0.20) + activesupport (= 3.0.20) bundler (~> 1.0) - railties (= 3.0.10) - railties (3.0.10) - actionpack (= 3.0.10) - activesupport (= 3.0.10) + railties (= 3.0.20) + railties (3.0.20) + actionpack (= 3.0.20) + activesupport (= 3.0.20) rake (>= 0.8.7) rdoc (~> 3.4) thor (~> 0.14.4) - rake (0.9.2) + rake (10.1.0) rdiscount (1.6.8) - rdoc (3.9.4) - rspec (2.6.0) - rspec-core (~> 2.6.0) - rspec-expectations (~> 2.6.0) - rspec-mocks (~> 2.6.0) - rspec-core (2.6.4) - rspec-expectations (2.6.0) - diff-lcs (~> 1.1.2) - rspec-mocks (2.6.0) - rspec-rails (2.6.1) - actionpack (~> 3.0) - activesupport (~> 3.0) - railties (~> 3.0) - rspec (~> 2.6.0) + rdoc (3.12.2) + json (~> 1.4) + rgeo (0.3.20) + rspec (2.11.0) + rspec-core (~> 2.11.0) + rspec-expectations (~> 2.11.0) + rspec-mocks (~> 2.11.0) + rspec-core (2.11.1) + rspec-expectations (2.11.3) + diff-lcs (~> 1.1.3) + rspec-mocks (2.11.3) + rspec-rails (2.11.4) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec (~> 2.11.0) ruby-debug-base19 (0.11.25) columnize (>= 0.3.1) linecache19 (>= 0.5.11) @@ -164,7 +179,9 @@ GEM ruby_core_source (0.1.5) archive-tar-minitar (>= 0.5.2) rubyzip (0.9.4) - sass (3.1.7) + rvm-capistrano (1.3.0) + capistrano (>= 2.0.0) + sass (3.1.15) selenium-webdriver (2.5.0) childprocess (>= 0.2.1) ffi (>= 1.0.7) @@ -175,10 +192,10 @@ GEM test-unit (1.2.3) hoe (>= 1.5.1) thor (0.14.6) - treetop (1.4.10) + treetop (1.4.12) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.29) + tzinfo (0.3.37) xpath (0.1.4) nokogiri (~> 1.3) @@ -186,16 +203,16 @@ PLATFORMS ruby DEPENDENCIES - GeoRuby + activerecord-postgis-adapter authlogic (~> 3.0.3) autotest-rails - capistrano cucumber-rails database_cleaner dynamic_form exception_notification factory_girl_rails ffaker + geocoder (~> 1.1.8) gettext (>= 1.9.3) gettext_i18n_rails haml @@ -203,9 +220,11 @@ DEPENDENCIES pg pickle prawn - rails (= 3.0.10) + rails (= 3.0.20) rdiscount - rspec-rails (~> 2.6.1) + rgeo-activerecord! + rspec-rails (~> 2.11.0) ruby-debug19 + rvm-capistrano sass test-unit (= 1.2.3) diff --git a/README b/README deleted file mode 100644 index fe7013d5..00000000 --- a/README +++ /dev/null @@ -1,256 +0,0 @@ -== Welcome to Rails - -Rails is a web-application framework that includes everything needed to create -database-backed web applications according to the Model-View-Control pattern. - -This pattern splits the view (also called the presentation) into "dumb" -templates that are primarily responsible for inserting pre-built data in between -HTML tags. The model contains the "smart" domain objects (such as Account, -Product, Person, Post) that holds all the business logic and knows how to -persist themselves to a database. The controller handles the incoming requests -(such as Save New Account, Update Product, Show Post) by manipulating the model -and directing data to the view. - -In Rails, the model is handled by what's called an object-relational mapping -layer entitled Active Record. This layer allows you to present the data from -database rows as objects and embellish these data objects with business logic -methods. You can read more about Active Record in -link:files/vendor/rails/activerecord/README.html. - -The controller and view are handled by the Action Pack, which handles both -layers by its two parts: Action View and Action Controller. These two layers -are bundled in a single package due to their heavy interdependence. This is -unlike the relationship between the Active Record and Action Pack that is much -more separate. Each of these packages can be used independently outside of -Rails. You can read more about Action Pack in -link:files/vendor/rails/actionpack/README.html. - - -== Getting Started - -1. At the command prompt, create a new Rails application: - rails new myapp (where myapp is the application name) - -2. Change directory to myapp and start the web server: - cd myapp; rails server (run with --help for options) - -3. Go to http://localhost:3000/ and you'll see: - "Welcome aboard: You're riding Ruby on Rails!" - -4. Follow the guidelines to start developing your application. You can find -the following resources handy: - -* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html -* Ruby on Rails Tutorial Book: http://www.railstutorial.org/ - - -== Debugging Rails - -Sometimes your application goes wrong. Fortunately there are a lot of tools that -will help you debug it and get it back on the rails. - -First area to check is the application log files. Have "tail -f" commands -running on the server.log and development.log. Rails will automatically display -debugging and runtime information to these files. Debugging info will also be -shown in the browser on requests from 127.0.0.1. - -You can also log your own messages directly into the log file from your code -using the Ruby logger class from inside your controllers. Example: - - class WeblogController < ActionController::Base - def destroy - @weblog = Weblog.find(params[:id]) - @weblog.destroy - logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") - end - end - -The result will be a message in your log file along the lines of: - - Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1! - -More information on how to use the logger is at http://www.ruby-doc.org/core/ - -Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are -several books available online as well: - -* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe) -* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) - -These two books will bring you up to speed on the Ruby language and also on -programming in general. - - -== Debugger - -Debugger support is available through the debugger command when you start your -Mongrel or WEBrick server with --debugger. This means that you can break out of -execution at any point in the code, investigate and change the model, and then, -resume execution! You need to install ruby-debug to run the server in debugging -mode. With gems, use sudo gem install ruby-debug. Example: - - class WeblogController < ActionController::Base - def index - @posts = Post.find(:all) - debugger - end - end - -So the controller will accept the action, run the first line, then present you -with a IRB prompt in the server window. Here you can do things like: - - >> @posts.inspect - => "[#nil, "body"=>nil, "id"=>"1"}>, - #"Rails", "body"=>"Only ten..", "id"=>"2"}>]" - >> @posts.first.title = "hello from a debugger" - => "hello from a debugger" - -...and even better, you can examine how your runtime objects actually work: - - >> f = @posts.first - => #nil, "body"=>nil, "id"=>"1"}> - >> f. - Display all 152 possibilities? (y or n) - -Finally, when you're ready to resume execution, you can enter "cont". - - -== Console - -The console is a Ruby shell, which allows you to interact with your -application's domain model. Here you'll have all parts of the application -configured, just like it is when the application is running. You can inspect -domain models, change values, and save to the database. Starting the script -without arguments will launch it in the development environment. - -To start the console, run rails console from the application -directory. - -Options: - -* Passing the -s, --sandbox argument will rollback any modifications - made to the database. -* Passing an environment name as an argument will load the corresponding - environment. Example: rails console production. - -To reload your controllers and models after launching the console run -reload! - -More information about irb can be found at: -link:http://www.rubycentral.com/pickaxe/irb.html - - -== dbconsole - -You can go to the command line of your database directly through rails -dbconsole. You would be connected to the database with the credentials -defined in database.yml. Starting the script without arguments will connect you -to the development database. Passing an argument will connect you to a different -database, like rails dbconsole production. Currently works for MySQL, -PostgreSQL and SQLite 3. - -== Description of Contents - -The default directory structure of a generated Ruby on Rails application: - - |-- app - | |-- controllers - | |-- helpers - | |-- mailers - | |-- models - | `-- views - | `-- layouts - |-- config - | |-- environments - | |-- initializers - | `-- locales - |-- db - |-- doc - |-- lib - | `-- tasks - |-- log - |-- public - | |-- images - | |-- javascripts - | `-- stylesheets - |-- script - |-- test - | |-- fixtures - | |-- functional - | |-- integration - | |-- performance - | `-- unit - |-- tmp - | |-- cache - | |-- pids - | |-- sessions - | `-- sockets - `-- vendor - `-- plugins - -app - Holds all the code that's specific to this particular application. - -app/controllers - Holds controllers that should be named like weblogs_controller.rb for - automated URL mapping. All controllers should descend from - ApplicationController which itself descends from ActionController::Base. - -app/models - Holds models that should be named like post.rb. Models descend from - ActiveRecord::Base by default. - -app/views - Holds the template files for the view that should be named like - weblogs/index.html.erb for the WeblogsController#index action. All views use - eRuby syntax by default. - -app/views/layouts - Holds the template files for layouts to be used with views. This models the - common header/footer method of wrapping views. In your views, define a layout - using the layout :default and create a file named default.html.erb. - Inside default.html.erb, call <% yield %> to render the view using this - layout. - -app/helpers - Holds view helpers that should be named like weblogs_helper.rb. These are - generated for you automatically when using generators for controllers. - Helpers can be used to wrap functionality for your views into methods. - -config - Configuration files for the Rails environment, the routing map, the database, - and other dependencies. - -db - Contains the database schema in schema.rb. db/migrate contains all the - sequence of Migrations for your schema. - -doc - This directory is where your application documentation will be stored when - generated using rake doc:app - -lib - Application specific libraries. Basically, any kind of custom code that - doesn't belong under controllers, models, or helpers. This directory is in - the load path. - -public - The directory available for the web server. Contains subdirectories for - images, stylesheets, and javascripts. Also contains the dispatchers and the - default HTML files. This should be set as the DOCUMENT_ROOT of your web - server. - -script - Helper scripts for automation and generation. - -test - Unit and functional tests along with fixtures. When using the rails generate - command, template test files will be generated for you and placed in this - directory. - -vendor - External libraries that the application depends on. Also includes the plugins - subdirectory. If the app has frozen rails, those gems also go here, under - vendor/rails/. This directory is in the load path. diff --git a/README.md b/README.md new file mode 100644 index 00000000..749b0525 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +[![Build Status](https://travis-ci.org/marnen/quorum2.png?branch=master)](https://travis-ci.org/marnen/quorum2) + +# Quorum Calendar System + +This is the Quorum Calendar System, an easy-to-use solution for collaborative scheduling. For more information, please see http://quorum2.sf.net. \ No newline at end of file diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb index a3027e71..f21b177e 100644 --- a/app/controllers/calendars_controller.rb +++ b/app/controllers/calendars_controller.rb @@ -3,54 +3,60 @@ class CalendarsController < ApplicationController @nonadmin ||= [:new, :create] before_filter :check_admin, :except => @nonadmin + before_filter :load_calendar, :except => @nonadmin before_filter :require_user, :only => @nonadmin layout 'standard' - - make_resourceful do - actions :new, :create, :edit, :update - - response_for :new do - @page_title = _('Create calendar') - render :action => 'edit' - end - - response_for :edit do - @page_title = _('Edit calendar') - end - - # TODO: Shouldn't this be replaced by Calendar#set_admin ? - after :create do - p = User.current_user.permissions - @admin ||= Role.find_or_create_by_name('admin') - if !p.find_by_calendar_id_and_role_id(current_object.id, @admin.id) - p << Permission.create!(:user => User.current_user, :calendar => current_object, :role => @admin) - end - end - - response_for :create do - flash[:notice] = _('Your calendar was successfully created.') - redirect_to '/admin' - end - response_for :create_fails do - flash[:error] = _('Couldn\'t create your calendar!') - redirect_to :back - end + respond_to :html - response_for :update do - flash[:notice] = _('Your calendar was successfully saved.') - redirect_to '/admin' + def new + @page_title = _('Create calendar') + @calendar = Calendar.new + respond_with @calendar + end + + def create + @calendar = Calendar.new params[:calendar] + if @calendar.save + make_admin_permission_for @calendar + redirect_to '/admin', notice: _('Your calendar was successfully created.') + else + flash[:error] = _("Couldn't create your calendar!") + respond_with @calendar end + end - response_for :update_fails do + def edit + @page_title = _('Edit calendar') + respond_with @calendar + end + + def update + if @calendar.update_attributes params[:calendar] + redirect_to '/admin', notice: _('Your calendar was successfully saved.') + else flash[:error] = _('Couldn\'t save your calendar!') - redirect_to :back + respond_with @calendar end end - + # Lists all the users for the current #Calendar. def users - @page_title = _('Users for calendar %{calendar_name}') % {:calendar_name => current_object} - @users = current_object.users.find(:all, :order => 'lastname, firstname') + @page_title = _('Users for calendar %{calendar_name}') % {:calendar_name => @calendar} + @users = @calendar.users.find(:all, order: 'lastname, firstname') + end + + private + + def load_calendar + @calendar = Calendar.find params[:id] + end + + def make_admin_permission_for(calendar) + p = User.current_user.permissions + @admin ||= Role.find_or_create_by_name('admin') + if !p.find_by_calendar_id_and_role_id(calendar.id, @admin.id) + p << Permission.create!(:user => User.current_user, :calendar => calendar, :role => @admin) + end end end diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index 72dbcf73..40f94920 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -2,84 +2,79 @@ # This is the controller for #Event instances. It supports the following make_resourceful[http://mr.hamptoncatlin.com] actions: :index, :create, :new, :edit, :update, :show. class EventsController < ApplicationController - layout "standard", :except => [:export, :feed] # no layout needed on export, since it generates an iCal file - before_filter :require_user, :except => :feed - before_filter :login_from_key, :only => :feed - after_filter :ical_header, :only => :export # assign the correct MIME type so that it gets recognized as an iCal event - - rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found - - make_resourceful do - actions :index, :create, :new, :edit, :update, :show - - before :index do - params[:order] ||= 'date' # isn't it enough to define this in routes.rb? - params[:direction] ||= 'asc' # and this? - @page_title = _("Upcoming events") - @order = params[:order] - @direction = params[:direction] - @search = params[:search].extend(Search) if params[:search] - end - - response_for :index do |format| - format.html + layout "standard", except: [:export, :feed] # no layout needed on export, since it generates an iCal file + before_filter :require_user, except: :feed + before_filter :login_from_key, only: :feed + before_filter :load_event, only: [:edit, :update, :show, :delete] + after_filter :ical_header, only: :export # assign the correct MIME type so that it gets recognized as an iCal event + + rescue_from ActiveRecord::RecordNotFound, with: :record_not_found + + respond_to :html, except: :feed + respond_to :pdf, only: :index + respond_to :rss, only: :feed + + def index + set_table_headers + @events = current_objects + respond_with @events do |format| format.pdf do @users = current_objects.blank? ? [] : current_objects[0].calendar.permissions.find_all_by_show_in_report(true, :include => :user).collect{|x| x.user}.sort # TODO: fix for multiple calendars - prawnto :prawn => {:page_layout => :landscape} - render :layout => false + prawnto prawn: {page_layout: :landscape} + render layout: false end end - - before :new do - @page_title = _("Add event") - end - - response_for :edit do - if !current_object.allow?(:edit) - flash[:error] = _("You are not authorized to edit that event.") - redirect_to :action => :index - else - @page_title = _("Edit event") - render :action => 'new' - end - end - - response_for :update, :create do - flash[:notice] = _("Your event has been saved.") - redirect_to :action => :index - end - - response_for :update_fails, :create_fails do - flash[:error] = _("We couldn't process that request. Please try again.") - render :new + end + + def new + @page_title = _("Add event") + @event = Event.new + respond_with @event + end + + def create + @event = Event.new params[:event] + respond_with_flash { @event.save } + end + + def edit + if @event.allow? :edit + @page_title = _("Edit event") + respond_with @event + else + redirect_to({action: :index}, flash: {error: _("You are not authorized to edit that event.")}) end - - response_for :show do - if !current_object.allow?(:show) - flash[:error] = _("You are not authorized to view that event.") - redirect_to :action => :index - else - @page_title = current_object.name - end + end + + def update + respond_with_flash { @event.update_attributes params[:event] } + end + + def show + if @event.allow? :show + @page_title = @event.name + respond_with @event + else + redirect_to({action: :index}, flash: {error: _("You are not authorized to view that event.")}) end end - + # Generate an RSS feed of events. def feed respond_to do |format| format.rss do - @key = params[:key] params[:from_date] = Date.civil(1, 1, 1) + @key = params[:key] + @events = current_objects end end end # Delete an #Event, subject to #Event#allow?. def delete - event = Event.find(params[:id].to_i) begin - if event.allow?(:delete) - event.hide + if @event.allow?(:delete) + @event.hide flash[:notice] = _("The selected event was deleted.") else flash[:error] = _("You are not authorized to delete that event.") @@ -107,7 +102,7 @@ def change_status status_map = {'yes' => true, 'no' => false, 'maybe' => nil} if !id.nil? then event = Event.find_by_id(id) - event.change_status!(current_user, status_map[params[:status].to_s]) + event.change_status! current_user, status_map[params[:status].to_s], params[:comment] end if request.xhr? render :partial => 'event', :locals => {:event => event} @@ -115,11 +110,11 @@ def change_status redirect_to :action => :index end end - + # Display a map page for the current #Event. def map begin - @event = Event.find(params[:id]) + load_event @host = request.host_with_port rescue flash[:error] = _("Couldn't find that event!") @@ -127,13 +122,15 @@ def map end @page_title = _("Map for %{event}") % {:event => @event.name} end - - # Return non-deleted events between params[:from_date] and params[:to_date], optionally ordered as specified by params[:order] and [:direction]. Provided for use with make_resourceful[http://mr.hamptoncatlin.com]. + + private + + # Return non-deleted events between params[:from_date] and params[:to_date], optionally ordered as specified by params[:order] and [:direction]. def current_objects user = params[:feed_user] || User.current_user # Process parameters from the search form, if it was submitted. - if !params[:search].nil? + if params[:search].present? search = params[:search] search.extend(Search) ['to', 'from'].each do |s| @@ -143,36 +140,60 @@ def current_objects calendars = search[:calendar_id].blank? ? nil : search[:calendar_id] end - + order = params[:order] || 'date' from_date = params[:from_date] || Time.zone.today to_date = params[:to_date] direction = params[:direction] || 'asc' - calendars ||= user.calendars.collect{|c| c.id} - - if to_date == nil - date_query = 'date >= :from_date' - else + calendars ||= (user ? user.calendars.collect{|c| c.id} : []) + + if to_date.present? date_query = 'date BETWEEN :from_date AND :to_date' + else + date_query = 'date >= :from_date' end - - @current_objects || current_model.find(:all, :conditions => ['calendar_id IN (:calendars) AND ' + date_query, {:calendars => calendars, :from_date => from_date, :to_date => to_date}], :order => "#{order} #{direction}") + + # TODO: can we use more Arel and less literal SQL? + @current_objects ||= Event.includes(:commitments => :user).where(["calendar_id IN (:calendars) AND #{date_query}", {:calendars => calendars, :from_date => from_date, :to_date => to_date}]).order("#{order} #{direction}") end - - protected + # Return an HTTP header with proper MIME type for iCal. def ical_header headers['Content-Type'] = 'text/calendar' end - + + def load_event + @event = Event.find params[:id] + end + # Log user in based on single_access_token. def login_from_key params[:feed_user] = User.find_by_single_access_token(params[:key]) end - + # Handler for #RecordNotFound. def record_not_found flash[:error] = _("Couldn't find any event to edit!") redirect_to(:action => :index) and return end + + def set_table_headers + params[:order] ||= 'date' # isn't it enough to define this in routes.rb? + params[:direction] ||= 'asc' # and this? + @page_title = _("Upcoming events") + @order = params[:order] + @direction = params[:direction] + @search = params[:search].extend(Search) if params[:search] + end + + def respond_with_flash + raise ArgumentError, 'no block specified' unless block_given? + + if yield + redirect_to({action: :index}, notice: _("Your event has been saved.")) + else + flash[:error] = _("We couldn't process that request. Please try again.") + respond_with @event + end + end end diff --git a/app/controllers/permissions_controller.rb b/app/controllers/permissions_controller.rb index 096533b7..98197e2c 100644 --- a/app/controllers/permissions_controller.rb +++ b/app/controllers/permissions_controller.rb @@ -1,65 +1,52 @@ # coding: UTF-8 class PermissionsController < ApplicationController - @@nonadmin = :index, :subscribe, :destroy - before_filter :check_admin, :except => @@nonadmin - before_filter :require_user, :only => @@nonadmin + @nonadmin = :index, :subscribe, :destroy + before_filter :check_admin, except: @nonadmin + before_filter :require_user, only: @nonadmin layout 'standard' - + + respond_to :html + class NotDeletableError < RuntimeError end - + rescue_from NotDeletableError do |e| flash[:error] = _("Couldn't delete that subscription!") go_back end - - make_resourceful do - actions :index, :edit, :update, :destroy - - before :destroy do - if current_object.user_id != User.current_user.id # TODO: should maybe create Permission#allow? and use it here, as we did on Events - raise NotDeletableError.new - end - end - - response_for :index do - @page_title = _('Subscriptions') - @permissions = User.current_user.permissions.find(:all, :include => [:calendar, :role]) - if @permissions.empty? - @unsubscribed = Calendar.find(:all) - else - @unsubscribed = Calendar.find(:all, :conditions => ['id NOT IN (:permissions)', {:permissions => @permissions.collect{|p| p.calendar.id}}]) - end - end - - response_for :update do - flash[:notice] = _("Your changes have been saved.") - go_back + + def index + @page_title = _('Subscriptions') + @permissions = User.current_user.permissions.includes(:calendar, :role) + if @permissions.empty? + @unsubscribed = Calendar.find(:all) + else + @unsubscribed = Calendar.find(:all, :conditions => ['id NOT IN (:permissions)', {:permissions => @permissions.collect{|p| p.calendar.id}}]) end - - response_for :update_fails do - flash[:error] = _("Couldn't save changes!") - go_back + respond_with @permissions + end + + def destroy + @permission = Permission.find params[:id] + if @permission.user_id != User.current_user.id # TODO: should maybe create Permission#allow? and use it here, as we did on Events + raise NotDeletableError.new end - - response_for :destroy do + + if @permission.destroy flash[:notice] = _('You were successfully unsubscribed.') - go_back - end - - response_for :destroy_fails do + else flash[:error] = _("Something went wrong. Please try again.") - go_back end + go_back end - + def subscribe begin - current_model.create! do |p| + Permission.create! do |p| p.calendar_id = params[:calendar_id] p.user = User.current_user - p.role = Role.find_by_name('user') + p.role = Role.find_or_create_by_name('user') end rescue flash[:error] = _("Something went wrong. Please try again.") @@ -68,7 +55,7 @@ def subscribe flash[:notice] = _("Your subscription has been saved.") go_back and return end - + protected def go_back begin diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 347a167b..b3269331 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -1,6 +1,6 @@ # coding: UTF-8 -module EventsHelper +module EventsHelper # Returns #User's commitment status for #Event as a symbol -- :yes, :no, :or maybe. def attendance_status(event, user) if event.find_committed(:yes).include? user then @@ -11,7 +11,12 @@ def attendance_status(event, user) status = :maybe end end - + + # Returns #User's commitment comment for #Event. + def attendance_comment(event, user) + event.comments.find {|c| c.user == user }.try :comment + end + # Generates an HTML date element for #Event, including hCalendar[http://microformats.org/wiki/hcalendar] annotation. # # Usage: @@ -25,47 +30,30 @@ def date_element(event) full_date = h event.date.to_formatted_s(:rfc822) content_tag :abbr, full_date, :class => :dtstart, :title => ical_date end - + # Generates a delete link for #Event. def delete_link(event) link_to h(_("delete")), url_for(:controller => 'events', :action => 'delete', :id => event.id), :class => :delete end - + # Returns the distance from #Event to #User's address, in a #String of the form "35.2 miles". # If something goes wrong, returns "0.0 miles". def distance_string(event, user) begin - meters = event.coords.ellipsoidal_distance(user.coords) + meters = event.coords.distance(user.coords) miles = meters / 1609.344 - + content_tag(:span, h(_("%.1f miles" % miles)), :class => :distance) rescue content_tag(:span, h(_("%.1f miles" % 0)), :class => :distance) end end - + # Generates an edit link for #Event. def edit_link(event) link_to h(_("edit")), url_for(:controller => 'events', :action => 'edit', :id => event.id), :class => :edit end - - # Generates a
element with a map for #Event, using the Google API key for host. - # TODO: figure out how to make this html_safe! - def event_map(event, hostname) - return nil if event.nil? - - @extra_headers ||= ''.html_safe - @extra_headers << GMap.header(:host => hostname).to_s.html_safe << javascript_include_tag('events/map').html_safe - map = GMap.new(:map) - result = ''.html_safe - result << info(event) - result << content_tag(:div, event.coords.lat, :id => :lat, :class => :hidden) - result << content_tag(:div, event.coords.lng, :id => :lng, :class => :hidden) - result << map.div(:width => 500, :height => 400).html_safe - result - end - # Escapes characters in string that would be illegal in iCalendar format. def ical_escape(string) string.gsub(%r{[\\,;]}) {|c| '\\' + c}.gsub("\n", '\\n') @@ -80,7 +68,7 @@ def ical_link(event) def ical_uid(event) "event-" << event.id.to_s << "@" << DOMAIN end - + # Generates text for the info window on Google map of #Event. # # TODO: this should probably become a partial. @@ -91,7 +79,7 @@ def info(event) result << content_tag(:h3, (event.site || event.name)) city = [event.city, event.state.code, event.state.country.code].compact.join(', ') result << content_tag(:p, [h(event.street), h(event.street2), h(city)].compact.join(tag :br).html_safe) - + gmaps = 'http://maps.google.com' from = "saddr=#{u User.current_user.address.to_s(:geo)}" to = "daddr=#{u event.address.to_s(:geo)}" @@ -99,7 +87,7 @@ def info(event) result << content_tag(:p, link_to(_('Get directions'), h("#{gmaps}?#{params}"))) content_tag(:div, result, :id => :info) end - + # Given an #Array (or similar) of #User objects, returns an #Array of their full names as #Strings. def list_names(users) return '' if users.nil? or users.size == 0 @@ -110,13 +98,13 @@ def list_names(users) def map_link(event) link_to h(_("map")), url_for(:controller => 'events', :action => 'map', :id => event.id), :class => 'map', :target => 'map' end - + # Generates a hint to use Markdown for formatting. # TODO: make this work as html_safe properly. def markdown_hint content_tag(:span, (h(_ '(use %{Markdown} for formatting)').to_str % {:Markdown => link_to(_('Markdown'), 'http://daringfireball.net/projects/markdown/basics', :target => 'markdown')}).html_safe, :class => :hint) end - + # Generates an RSS URL for the current user's events feed. def rss_url if User.current_user @@ -137,4 +125,8 @@ def sort_link(title, field, direction = :asc, options = {}) my_class ||= 'sort' link_to h(_(title)), url_for(params.merge :order => field, :direction => direction), :class => my_class end + + def status_strings + @status_strings ||= {yes: _('attending'), no: _('not attending'), maybe: _('uncommitted')} + end end diff --git a/app/helpers/gmaps_helper.rb b/app/helpers/gmaps_helper.rb new file mode 100644 index 00000000..17e7c95f --- /dev/null +++ b/app/helpers/gmaps_helper.rb @@ -0,0 +1,5 @@ +module GmapsHelper + def gmaps_api_key + GMAPS_API_KEY.kind_of?(Hash) ? GMAPS_API_KEY[controller.request.host_with_port] : GMAPS_API_KEY + end +end \ No newline at end of file diff --git a/app/models/calendar.rb b/app/models/calendar.rb index 9e965f29..7aa2f46e 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -4,18 +4,18 @@ class Calendar < ActiveRecord::Base has_many :events has_many :permissions has_many :users, :through => :permissions - + validates_presence_of :name - + after_create :set_admin - + def to_s self.name end - - + + protected - def set_admin + def set_admin # TODO: is this being used anywhere? if User.current_user and User.current_user != :false # TODO: can't we refactor this condition elsewhere? self.permissions.create(:user => User.current_user, :role => Role.find_by_name('admin')) end diff --git a/app/models/commitment.rb b/app/models/commitment.rb index f058ea29..5267b441 100644 --- a/app/models/commitment.rb +++ b/app/models/commitment.rb @@ -5,7 +5,4 @@ class Commitment < ActiveRecord::Base belongs_to :user validates_presence_of :event_id validates_presence_of :user_id - - scope :attending, :conditions => {:status => true} - scope :not_attending, :conditions => {:status => false} end diff --git a/app/models/event.rb b/app/models/event.rb index 7719e7ee..6b4d8625 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,11 +1,9 @@ # coding: UTF-8 - -require 'geocoding_utilities' +require 'acts/geocoded' class Event < ActiveRecord::Base - acts_as_addressed - include GeocodingUtilities - + acts_as_geocoded + belongs_to :created_by, :class_name => "User" belongs_to :calendar has_many :commitments @@ -14,10 +12,11 @@ class Event < ActiveRecord::Base validates_presence_of :calendar_id validates_presence_of :name validates_presence_of :state_id + before_create :set_created_by_id - + default_scope :conditions => 'deleted is distinct from true' - + # Returns true if #User.current_user is allowed to perform operation on the current #Event, false otherwise. # Operation may be :edit, :delete, or :show. def allow?(operation) @@ -32,38 +31,50 @@ def allow?(operation) end end end - + # Sets the #User's attendance status on the Event, where status is one of true (attending), false (not attending), or nil (uncommitted). - def change_status!(user, status) + def change_status!(user, status, comment = nil) commitment = commitments.find_or_create_by_user_id(user.id) - commitment.status = status - commitment.save! + commitment.update_attributes! :status => status, :comment => comment + end + + # Returns an array of nonblank comments for the #Event, ordered by #User's name. + def comments + commitments.reject {|c| c.comment.blank? }.sort_by &:user end - + # Returns an #Array of #User objects with commitment status (for the current #Event) of status, # where status may be :yes or :no. def find_committed(status) if ![:yes, :no].include? status - raise "Invalid status: " << status + raise "Invalid status (not :yes or :no): " << status end - scope = {:yes => :attending, :no => :not_attending}[status] - c = commitments.send(scope) - c.collect{|e| e.user }.sort{|x, y| (x.lastname || x.email) <=> (y.lastname || y.email)} + status_to_find = {yes: true, no: false}[status] + found_commitments = commitments.select {|c| c.status == status_to_find } + found_commitments.collect {|e| e.user }.sort end - + # Hides the current #Event. This has the effect of deleting it, since hidden Events will not show up in the main list. def hide self.deleted = true self.save end - - protected + + def latitude + coords.try :y + end + + def longitude + coords.try :x + end + + protected # TODO: allow_* methods should probably be public. Keeping them protected mainly so as not to change the class interface just yet. def allow_delete?(user) role = role_of user !role.nil? and role.name == 'admin' end - + def allow_edit?(user) if created_by == user return true @@ -72,11 +83,11 @@ def allow_edit?(user) return role.name == 'admin' end end - + def allow_show?(user) !(role_of user).nil? end - + # TODO: should this method be public? # Returns the #Role of the #User for the #Event. def role_of(user) @@ -84,7 +95,7 @@ def role_of(user) p = user.permissions.find_by_calendar_id(self.calendar_id) p.nil? ? nil : p.role end - + def set_created_by_id if User.current_user and User.current_user != :false self.created_by = User.current_user diff --git a/app/models/user.rb b/app/models/user.rb index 52c602d9..f353bffb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,15 +1,14 @@ # coding: UTF-8 - +require 'acts/geocoded' require 'digest/sha1' -require 'geocoding_utilities' class User < ActiveRecord::Base acts_as_authentic do |c| c.transition_from_restful_authentication = true end - - acts_as_addressed - include GeocodingUtilities + + acts_as_geocoded + cattr_accessor :current_user has_many :commitments @@ -17,7 +16,7 @@ class User < ActiveRecord::Base has_many :permissions has_many :calendars, :through => :permissions # validates_presence_of :permissions - + validates_presence_of :email validates_presence_of :password, :if => :password_required? validates_presence_of :password_confirmation, :if => :password_required? @@ -27,28 +26,29 @@ class User < ActiveRecord::Base validates_uniqueness_of :email, :case_sensitive => false before_save :make_single_access_token after_create :set_calendar + # prevents a user from submitting a crafted form that bypasses activation # anything else you want your user to change should be added here. attr_accessible :email, :password, :password_confirmation, :firstname, :lastname, :street, :street2, :city, :state, :state_id, :zip, :show_contact - + # Sets the User's active status to true. # TODO: Rename to activate! , since it's destructive. def activate update_attribute(:active, true) end - + def admin? @admin ||= Role.find_by_name('admin') !(self.permissions.find_by_role_id(@admin).nil?) end - + # Compares users by last name, first name, and e-mail address in that order. # ['Smith', 'John', 'jsmith1@aol.com'] < ['Smith', 'John', 'jsmith2@aol.com'] def <=>(other) attrs = [:lastname, :firstname, :email] attrs.collect{|a| self[a].downcase rescue nil}.compact <=> attrs.collect{|a| other[a].downcase rescue nil}.compact end - + # Resets the user's password and password_confirmation to a random string. Designed to be used for password resets. def reset_password! new_password = Digest::MD5.hexdigest(Time.now.to_s.split(//).sort_by {rand}.join)[0, 10] @@ -71,19 +71,20 @@ def to_s(format = :first_last) end protected - def password_required? - crypted_password.blank? || !password.blank? || !password_confirmation.blank? - end - - def make_single_access_token - if single_access_token.blank? - reset_single_access_token! - end + + def password_required? + crypted_password.blank? || !password.blank? || !password_confirmation.blank? + end + + def make_single_access_token + if single_access_token.blank? + reset_single_access_token! end - - def set_calendar - if Calendar.count == 1 - permissions.create(:user => self, :calendar => Calendar.find(:first), :role => Role.find_or_create_by_name('user')) - end + end + + def set_calendar + if Calendar.count == 1 + permissions.create(:user => self, :calendar => Calendar.find(:first), :role => Role.find_or_create_by_name('user')) end + end end diff --git a/app/views/calendars/_form.html.haml b/app/views/calendars/_form.html.haml new file mode 100644 index 00000000..e51aafea --- /dev/null +++ b/app/views/calendars/_form.html.haml @@ -0,0 +1,6 @@ += form_for calendar do |f| + %table.edit + %tr + %th= f.label :name, _('Name') + %td= f.text_field :name + = submit_tag(h(_('Save'))) \ No newline at end of file diff --git a/app/views/calendars/edit.html.haml b/app/views/calendars/edit.html.haml index 8bf1b141..6b9d793f 100644 --- a/app/views/calendars/edit.html.haml +++ b/app/views/calendars/edit.html.haml @@ -1,6 +1 @@ -= form_for @current_object do |f| - %table.edit - %tr - %th= f.label :name, _('Name') - %td= f.text_field :name - = submit_tag(h(_('Save'))) \ No newline at end of file += render partial: 'form', locals: {calendar: @calendar} \ No newline at end of file diff --git a/app/views/calendars/new.html.haml b/app/views/calendars/new.html.haml new file mode 100644 index 00000000..6b9d793f --- /dev/null +++ b/app/views/calendars/new.html.haml @@ -0,0 +1 @@ += render partial: 'form', locals: {calendar: @calendar} \ No newline at end of file diff --git a/app/views/calendars/users.html.haml b/app/views/calendars/users.html.haml index 1778c3b0..85d67294 100644 --- a/app/views/calendars/users.html.haml +++ b/app/views/calendars/users.html.haml @@ -8,8 +8,8 @@ %th   / blank - - for user in @users do - - perm = user.permissions.find_by_calendar_id(@current_object) + - @users.each do |user| + - perm = user.permissions.find_by_calendar_id(@calendar) # TODO: get query out of view! = form_for perm do |f| %tr[user] %td._name= [user.lastname, user.firstname].compact.join(', ') diff --git a/app/views/events/_attendance.html.haml b/app/views/events/_attendance.html.haml index b97e6c8d..5ac51c3f 100644 --- a/app/views/events/_attendance.html.haml +++ b/app/views/events/_attendance.html.haml @@ -1,9 +1,12 @@ -# TODO: stop defining variables in views! -- @status_strings ||= { :yes => _('attending'), :no => _('not attending'), :maybe => _('uncommitted') } - status = attendance_status(event, User.current_user) -%td{:class => status} - = form_for event, :as => :commitment, :url => {:action => :change_status, :id => event.id}, :html => {:class => :attendance} do |f| - %p!= _("You are currently %{status}.") % {:status => content_tag(:span, h(@status_strings[status]), :class => status)} - = select_tag(:status, options_for_select(@status_strings.invert, status), :class => :commit, :id => nil) +%td.attendance{:class => status} + -# TODO: can we use accepts_nested here? + = form_tag({action: :change_status, id: event.id}, {class: :attendance}) do + %p!= h(_("You are currently %{status}.")) % {:status => content_tag(:span, h(status_strings[status]), :class => status)} + = select_tag(:status, options_for_select(status_strings.invert, status), :class => :commit, :id => nil) %span.progress   + %br + = text_area_tag :comment, attendance_comment(event, User.current_user), placeholder: _('comment'), id: nil + %br = submit_tag(h(_("Change status"))) \ No newline at end of file diff --git a/app/views/events/_comment.html.haml b/app/views/events/_comment.html.haml new file mode 100644 index 00000000..cfe0cc7f --- /dev/null +++ b/app/views/events/_comment.html.haml @@ -0,0 +1,3 @@ +.comment + %span.user{class: attendance_status(event, comment.user)}== #{comment.user}: + = comment.comment diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index 773f43ee..48b89351 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -31,9 +31,10 @@ %div.description :markdown #{h event.description} + - if event.comments.present? + .comments= render partial: 'comment', collection: event.comments, locals: {event: event} = render :partial => 'attendance', :locals => {:event => event} - yes = event.find_committed(:yes) - no = event.find_committed(:no) - %td= render :partial => 'names', :object => yes - %td= render :partial => 'names', :object => no - + %td.yes= render :partial => 'names', :object => yes + %td.no= render :partial => 'names', :object => no diff --git a/app/views/events/_form.html.haml b/app/views/events/_form.html.haml new file mode 100644 index 00000000..8b03b9b2 --- /dev/null +++ b/app/views/events/_form.html.haml @@ -0,0 +1,45 @@ += form_for event do |f| + %table.edit + - if current_user.calendars.size > 1 + %tr + %th= _("Calendar") + %td= f.collection_select :calendar_id, User.current_user.calendars.sort{|x, y| x.name <=> y.name}, :id, :name + - else + = f.hidden_field :calendar_id, :value => User.current_user.calendars[0].id + %tr + %th= _("Event name") + %td + = f.text_field :name + = _(error_message_on(:event, :name)) # TODO: Do we need the :event now? + %tr + %th + = _("Description") + %br + = markdown_hint + %td= f.text_area :description, :cols => 40, :rows => 6 + %tr + %th= _("Date") + %td + = f.date_select :date, :order => [:day, :month, :year], :start_year => 2006 + = _(error_message_on(:event, :date)) + %tr + %th= _("Site") + %td= f.text_field :site + %tr + %th= _("Street address") + %td + = f.text_field :street + %br + = f.text_field :street2 + %tr + %th= _("City") + %td= f.text_field :city + %tr + %th= _("State") + %td + = f.collection_select :state_id, Acts::Addressed::State.find(:all), :id, :name + = _(error_message_on :event, :state) + %tr + %th= _("Zip") + %td= f.text_field :zip + = submit_tag _("Save changes") \ No newline at end of file diff --git a/app/views/events/_names.html.haml b/app/views/events/_names.html.haml index 4ff62a0f..b8e054be 100644 --- a/app/views/events/_names.html.haml +++ b/app/views/events/_names.html.haml @@ -1,4 +1,5 @@ -- if !names.blank? - %strong&= n_('1 person:', '%{num} people:', names.size) % {:num => names.size} - &= list_names(names) +- if names.present? + %p + %span.count= n_('1 person:', '%{num} people:', names.size) % {:num => names.size} + = list_names(names) diff --git a/app/views/events/edit.html.haml b/app/views/events/edit.html.haml new file mode 100644 index 00000000..b092fd17 --- /dev/null +++ b/app/views/events/edit.html.haml @@ -0,0 +1 @@ += render partial: 'form', locals: {event: @event} \ No newline at end of file diff --git a/app/views/events/feed.rss.haml b/app/views/events/feed.rss.haml index e1487e04..b8f969aa 100644 --- a/app/views/events/feed.rss.haml +++ b/app/views/events/feed.rss.haml @@ -5,10 +5,10 @@ %title= _("%{Quorum} Events") % {:Quorum => SITE_TITLE} %link= events_url %description - - if !params[:feed_user].blank? + - if params[:feed_user].present? = _("The latest events from %{Quorum}, generated for %{user}.") % {:Quorum => SITE_TITLE, :user => params[:feed_user]} - - if !params[:feed_user].blank? - - current_objects.each do |e| + - if params[:feed_user].present? + - @events.each do |e| %item %title= e.name %description diff --git a/app/views/events/ical.ics.erb b/app/views/events/ical.ics.erb index 15a9d736..605de1d7 100644 --- a/app/views/events/ical.ics.erb +++ b/app/views/events/ical.ics.erb @@ -1,10 +1,12 @@ -BEGIN:VCALENDAR -VERSION:2.0 -BEGIN:VEVENT -UID:<%= ical_uid @event %> -SUMMARY:<%= ical_escape @event.name %> -LOCATION:<%= [@event.street, @event.street2, @event.city, @event.state.code, @event.country.code].compact.join(', ') %> -DESCRIPTION:<%= ical_escape @event.description %> -DTSTART;VALUE=DATE:<%= @event.date.to_s :ical %> -END:VEVENT -END:VCALENDAR \ No newline at end of file +<% cr = 13.chr %> +BEGIN:VCALENDAR<%= cr %> +VERSION:2.0<%= cr %> +PRODID:-//quorum2.sf.net//<%= SITE_TITLE %> <%= APP_VERSION %>//EN<%= cr %> +BEGIN:VEVENT<%= cr %> +UID:<%= ical_uid @event %><%= cr %> +SUMMARY:<%= ical_escape @event.name %><%= cr %> +LOCATION:<%= ical_escape [@event.street, @event.street2, @event.city, @event.state.code, @event.country.code].compact.join(', ') %><%= cr %> +DESCRIPTION:<%= ical_escape @event.description %><%= cr %> +DTSTART;VALUE=DATE:<%= @event.date.to_s :ical %><%= cr %> +END:VEVENT<%= cr %> +END:VCALENDAR<%= cr %> diff --git a/app/views/events/index.html.haml b/app/views/events/index.html.haml index 06790523..00e712fa 100644 --- a/app/views/events/index.html.haml +++ b/app/views/events/index.html.haml @@ -2,7 +2,7 @@ = auto_discovery_link_tag :rss, rss_url - content_for :javascript do = javascript_include_tag :defaults, 'lowpro/lowpro', 'events/index' - + %p.rss = h(_("Your personal %{RSS_feed} for this page is available at %{URL}")) % {:RSS_feed => link_to(_('RSS feed'), rss_url), :URL => content_tag(:span, h(rss_url), :class => :url)} %br @@ -24,7 +24,7 @@ %label = s.radio_button :from_date_preset, 'other' = _('Other date:') - = s.date_select(:from_date, :order => [:day, :month, :year]) + = s.date_select(:from_date, :order => [:day, :month, :year]) %tr %th= _('To:') %td @@ -42,7 +42,7 @@ - if User.current_user.calendars.size > 1 %tr %th= _('Calendar:') - %td= s.select(:calendar_id, User.current_user.calendars.collect{|c| [c, c.id.to_s]}, {:include_blank => _('[All calendars]')}) + %td{colspan: 3}= s.select(:calendar_id, User.current_user.calendars.collect{|c| [c, c.id.to_s]}, {:include_blank => _('[All calendars]')}) = submit_tag(_('Search'), :name => nil) - if @events.collect{|e| e.calendar}.uniq.size == 1 %p.pdf!= _("Generate attendance report for these events (%{PDF})") % {:PDF => link_to(_('PDF'), "#{url_for(params.merge :format => :pdf)}")} diff --git a/app/views/events/map.html.haml b/app/views/events/map.html.haml index e3ee3b10..44229373 100644 --- a/app/views/events/map.html.haml +++ b/app/views/events/map.html.haml @@ -1 +1,8 @@ -= event_map @event, @host +- content_for(:javascript) do + = javascript_include_tag "http://maps.google.com/maps?file=api&v=2&sensor=false&key=#{gmaps_api_key}" + = javascript_include_tag('events/map') + += info(@event) +#lat.hidden= @event.latitude +#lng.hidden= @event.longitude +#map \ No newline at end of file diff --git a/app/views/events/new.html.haml b/app/views/events/new.html.haml index 679d652a..b092fd17 100644 --- a/app/views/events/new.html.haml +++ b/app/views/events/new.html.haml @@ -1,45 +1 @@ -= form_for @current_object do |f| - %table.edit - - if current_user.calendars.size > 1 - %tr - %th= _("Calendar") - %td= f.collection_select :calendar_id, User.current_user.calendars.sort{|x, y| x.name <=> y.name}, :id, :name - - else - = f.hidden_field :calendar_id, :value => User.current_user.calendars[0].id - %tr - %th= _("Event name") - %td - = f.text_field :name - = _(error_message_on(:event, :name)) - %tr - %th - = _("Description") - %br - = markdown_hint - %td= f.text_area :description, :cols => 40, :rows => 6 - %tr - %th= _("Date") - %td - = f.date_select :date, :order => [:day, :month, :year], :start_year => 2006 - = _(error_message_on(:event, :date)) - %tr - %th= _("Site") - %td= f.text_field :site - %tr - %th= _("Street address") - %td - = f.text_field :street - %br - = f.text_field :street2 - %tr - %th= _("City") - %td= f.text_field :city - %tr - %th= _("State") - %td - = f.collection_select :state_id, Acts::Addressed::State.find(:all), :id, :name - = _(error_message_on :event, :state) - %tr - %th= _("Zip") - %td= f.text_field :zip - = submit_tag _("Save changes") \ No newline at end of file += render partial: 'form', locals: {event: @event} \ No newline at end of file diff --git a/app/views/events/show.html.haml b/app/views/events/show.html.haml index a6667423..c965fb30 100644 --- a/app/views/events/show.html.haml +++ b/app/views/events/show.html.haml @@ -2,4 +2,4 @@ = javascript_include_tag :defaults, 'lowpro/lowpro', 'events/index' %table.events = render :partial => 'table_header', :locals => {:sortlinks => false} - = render :partial => 'event', :locals => {:event => current_object} + = render :partial => 'event', :locals => {:event => @event} diff --git a/app/views/layouts/_flash.html.haml b/app/views/layouts/_flash.html.haml new file mode 100644 index 00000000..3b2edb28 --- /dev/null +++ b/app/views/layouts/_flash.html.haml @@ -0,0 +1,9 @@ +- if !flash[:notice].nil? + #flash.notice + %p= flash[:notice] +- if !flash[:warning].nil? + #flash.warning + %p= flash[:warning] +- if !flash[:error].nil? + #flash.error + %p= flash[:error] diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml new file mode 100644 index 00000000..3a83fafa --- /dev/null +++ b/app/views/layouts/_head.html.haml @@ -0,0 +1,6 @@ +%head + %meta{:'http-equiv' => "content-type", :content => "text/html;charset=utf-8"} + %title= h(SITE_TITLE) + ' | ' + h(page_title) + = stylesheet_link_tag "quorum" + = yield :head + = yield :javascript diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml new file mode 100644 index 00000000..98ded7db --- /dev/null +++ b/app/views/layouts/application.html.haml @@ -0,0 +1,11 @@ +!!! 5 +%html + = render partial: 'layouts/head', locals: {page_title: @page_title} + + %body + = content_for?(:content) ? yield(:content) : yield + + #footer + .version + = link_to _(SITE_TITLE), APP_HOME_PAGE + = _('version %{version}') % {version: APP_VERSION} \ No newline at end of file diff --git a/app/views/layouts/standard.html.haml b/app/views/layouts/standard.html.haml index e6be1565..0dd31fd1 100644 --- a/app/views/layouts/standard.html.haml +++ b/app/views/layouts/standard.html.haml @@ -1,30 +1,10 @@ -!!! 5 -%html - %head - %meta{:'http-equiv' => "content-type", :content => "text/html;charset=utf-8"} - %title= h(SITE_TITLE) + ' | ' + h(@page_title) - = stylesheet_link_tag "quorum" - = yield :head - = yield :javascript - = @extra_headers +- content_for :content do + %p#navbar + != ([content_tag(:span, h(SITE_TITLE), :class => :siteName)] + [link_to(h(_("List events")), events_path), link_to(h(_("Add event")), new_event_path), link_to(h(_("User profile")), profile_path), link_to(h(_("Create calendar")), new_calendar_path), link_to(h(_("Subscriptions")), subscriptions_path), current_user.admin? ? link_to(h(_("Admin tools")), url_for(:controller => :admin, :action => :index)) : nil, link_to(h(_("Log out")), logout_path)].compact).join(' | ') + #content + %p!= _("Welcome, %{user}!") % {:user => content_tag(:span, h(current_user), :class => :name)} + = render partial: 'layouts/flash', locals: {flash: flash} + %h1= @page_title + = yield - %body - %p#navbar - != ([content_tag(:span, h(SITE_TITLE), :class => :siteName)] + [link_to(h(_("List events")), events_path), link_to(h(_("Add event")), new_event_path), link_to(h(_("User profile")), profile_path), link_to(h(_("Create calendar")), new_calendar_path), link_to(h(_("Subscriptions")), subscriptions_path), current_user.admin? ? link_to(h(_("Admin tools")), url_for(:controller => :admin, :action => :index)) : nil, link_to(h(_("Log out")), logout_path)].compact).join(' | ') - #content - %p!= _("Welcome, %{user}!") % {:user => content_tag(:span, h(current_user), :class => :name)} - - if !flash[:notice].nil? - #flash.notice - %p= flash[:notice] - - if !flash[:warning].nil? - #flash.warning - %p= flash[:warning] - - if !flash[:error].nil? - #flash.error - %p= flash[:error] - %h1= @page_title - = yield - - %p.footer - %a{:href => "http://validator.w3.org/check?uri=referer"} - %img{:src => "http://www.w3.org/Icons/valid-html401", :alt => "Valid XHTML 4.01 Transitional", :height => "31", :width => "88"} \ No newline at end of file += render template: 'layouts/application' \ No newline at end of file diff --git a/app/views/layouts/unauthenticated.html.haml b/app/views/layouts/unauthenticated.html.haml index 7cbd5458..92711abe 100644 --- a/app/views/layouts/unauthenticated.html.haml +++ b/app/views/layouts/unauthenticated.html.haml @@ -1,20 +1,5 @@ -!!! 5 -%html - %head - %meta{:'http-equiv' => "content-type", :content => "text/html;charset=utf-8"} - %title= h(SITE_TITLE) + ' | ' + h(@page_title) - = stylesheet_link_tag "quorum" - = @extra_headers +- content_for :content do + = render partial: 'layouts/flash', locals: {flash: flash} + = yield - %body - - if !flash[:notice].nil? - .flash_notice= flash[:notice] - - if !flash[:warning].nil? - .flash_warning= flash[:warning] - - if !flash[:error].nil? - .flash_error= flash[:error] - = yield - - %p.footer - %a{:href => "http://validator.w3.org/check?uri=referer"} - %img{:src => "http://www.w3.org/Icons/valid-xhtml10", :alt => "Valid XHTML 1.0 Transitional", :height => "31", :width => "88"} \ No newline at end of file += render template: 'layouts/application' diff --git a/ci/before_script.sh b/ci/before_script.sh new file mode 100755 index 00000000..4d981aea --- /dev/null +++ b/ci/before_script.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env sh +./ci/install_postgis.sh +./ci/create_config_files.sh diff --git a/ci/create_config_files.sh b/ci/create_config_files.sh new file mode 100755 index 00000000..756d160a --- /dev/null +++ b/ci/create_config_files.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env sh +warn () { + printf >&2 "$*" +} + +warn 'Setting up config files... ' +cat >config/database.yml <config/initializers/secret_token.rb 2>/dev/null <&2 'Installing PostGIS...' +sudo apt-add-repository -y ppa:sharpie/for-science +sudo apt-add-repository -y ppa:sharpie/postgis-nightly +sudo apt-get update +sudo apt-get install postgresql-9.1-postgis -q +echo >&2 'PostGIS installed!' diff --git a/config.ru b/config.ru new file mode 100644 index 00000000..1ef9ccea --- /dev/null +++ b/config.ru @@ -0,0 +1,4 @@ +# This file is used by Rack-based servers to start the application. + +require ::File.expand_path('../config/environment', __FILE__) +run Quorum2::Application diff --git a/config/database-orig.yml b/config/database-orig.yml index a618c890..5f35ddcb 100644 --- a/config/database-orig.yml +++ b/config/database-orig.yml @@ -4,7 +4,7 @@ # CONFIG: replace PASSWORD with an appropriate password. development: - adapter: postgresql + adapter: postgis database: quorum2_dev username: quorum2_dev password: PASSWORD @@ -15,7 +15,7 @@ development: # re-generated from your development database when you run 'rake'. # Do not set this db to the same as development or production. test: &test - adapter: postgresql + adapter: postgis database: quorum2_test username: quorum2_test password: PASSWORD @@ -23,11 +23,11 @@ test: &test template: template_postgis production: - adapter: postgresql + adapter: postgis database: quorum2 username: quorum2 password: PASSWORD encoding: UTF8 template: template_postgis - + cucumber: *test diff --git a/config/deploy-orig.rb b/config/deploy-orig.rb index fc68f491..e047e31f 100644 --- a/config/deploy-orig.rb +++ b/config/deploy-orig.rb @@ -21,9 +21,9 @@ set :remote, 'origin' =begin -set :scm_password, Proc.new { Capistrano::CLI.password_prompt("SVN -password for #{scm_user}, please: ") } -set :repository, Proc.new { "--username #{scm_user} --password +set :scm_password, Proc.new { Capistrano::CLI.password_prompt("SVN +password for #{scm_user}, please: ") } +set :repository, Proc.new { "--username #{scm_user} --password #{scm_password} --no-auth-cache #{repository}" } =end @@ -36,17 +36,19 @@ set :runner, "capistrano" # might want to change this set :use_sudo, false +set :migrate_target, :current + after 'deploy:update_code', 'deploy:remove_unnecessary_files', 'deploy:tag' namespace :deploy do - + # CONFIG: Comment out task :restart block unless you're using Phusion Passenger -- it won't work with other servers desc 'Restart the application server.' task :restart, :roles => :app do run "if test ! -d #{current_path}/tmp; then mkdir #{current_path}/tmp; fi" run "/usr/bin/touch #{current_path}/tmp/restart.txt" end - + desc 'Remove shared files and image sources.' task :remove_unnecessary_files, :roles => :app do # Remove some unversioned YAML config files and link to shared directory. @@ -58,11 +60,11 @@ # Remove image source files. run "rm -rf #{rpath}/public/images/sources" - + #run "chown www-data #{current_path}/config/environment.rb" end - + # From http://stackoverflow.com/questions/5735656/tagging-release-before-deploying-with-capistrano desc 'Tags deployed release with a unique Git tag.' task :tag do diff --git a/config/initializers/action_mailer_patch.rb b/config/initializers/action_mailer_patch.rb new file mode 100644 index 00000000..1789b23f --- /dev/null +++ b/config/initializers/action_mailer_patch.rb @@ -0,0 +1,14 @@ +# Security patch from http://seclists.org/oss-sec/2013/q4/118 +# TODO: remove when upgrading to Rails 3.2 or higher. + +require 'action_mailer' + +module ActionMailer + class LogSubscriber < ActiveSupport::LogSubscriber + def deliver(event) + recipients = Array.wrap(event.payload[:to]).join(', ') + info("\nSent mail to #{recipients} (#{event.duration.round(1)}ms)") + debug(event.payload[:mail]) + end + end +end \ No newline at end of file diff --git a/config/initializers/app_globals.rb b/config/initializers/app_globals.rb index c4a4d17b..4837bfad 100644 --- a/config/initializers/app_globals.rb +++ b/config/initializers/app_globals.rb @@ -5,14 +5,18 @@ require 'yaml' APP_CONFIG = YAML.load_file("#{Rails.root}/config/config.yml")[Rails.env] +APP_VERSION = '0.5.11' + SITE_TITLE = "Quorum" # Name of site as it appears in element +APP_HOME_PAGE = 'http://quorum2.sourceforge.net' + DOMAIN = APP_CONFIG['domain'] # Domain on which the site is hosted EMAIL = APP_CONFIG['email'] # Address that application-generated e-mail will come from. -GeoRuby::SimpleFeatures::DEFAULT_SRID = 4326 - -Time::DATE_FORMATS.merge :ical => "%Y%m%d" # yyyymmdd, for iCal conversion +Date::DATE_FORMATS[:ical] = "%Y%m%d" # yyyymmdd, for iCal conversion FONT_ROOT = "#{RAILS_ROOT}/fonts/dejavu-fonts-ttf-2.26/ttf" + +GMAPS_API_KEY = YAML.load_file(File.join Rails.root, 'config', 'gmaps_api_key.yml')[Rails.env] diff --git a/config/initializers/haml.rb b/config/initializers/haml.rb index 8b3f3fd3..22477257 100644 --- a/config/initializers/haml.rb +++ b/config/initializers/haml.rb @@ -1,4 +1,10 @@ # coding: UTF-8 Haml::Template.options[:format] = :xhtml -# We're still using HTML 5 for all actual HTML content, but since we need to generate XML as well, this is the easiest way. \ No newline at end of file +# We're still using HTML 5 for all actual HTML content, but since we need to generate XML as well, this is the easiest way. + +Haml::Filters::Markdown.module_eval do + def render(text) + RDiscount.new(text, :autolink).to_html + end +end diff --git a/db/migrate/20090508200432_restore_srid.rb b/db/migrate/20090508200432_restore_srid.rb index acd8f8f3..13caaf3d 100644 --- a/db/migrate/20090508200432_restore_srid.rb +++ b/db/migrate/20090508200432_restore_srid.rb @@ -2,7 +2,7 @@ class RestoreSrid < ActiveRecord::Migration def self.up - srid = GeoRuby::SimpleFeatures::DEFAULT_SRID.to_i + srid = 4326 ['events', 'users'].each do |table| change_srid(table, srid) end @@ -16,7 +16,7 @@ def self.down end private - + def self.change_srid(table, srid = -1) execute "select updategeometrysrid('#{table}', 'coords', #{srid})" execute "select setsrid(coords, #{srid}) from #{table}" diff --git a/db/migrate/20120308160523_add_comment_to_commitments.rb b/db/migrate/20120308160523_add_comment_to_commitments.rb new file mode 100644 index 00000000..20c027d9 --- /dev/null +++ b/db/migrate/20120308160523_add_comment_to_commitments.rb @@ -0,0 +1,9 @@ +class AddCommentToCommitments < ActiveRecord::Migration + def self.up + add_column :commitments, :comment, :text + end + + def self.down + remove_column :commitments, :comment + end +end diff --git a/db/migrate/20130803025446_change_geometry_columns_to_new_format.rb b/db/migrate/20130803025446_change_geometry_columns_to_new_format.rb new file mode 100644 index 00000000..611edca7 --- /dev/null +++ b/db/migrate/20130803025446_change_geometry_columns_to_new_format.rb @@ -0,0 +1,24 @@ +class ChangeGeometryColumnsToNewFormat < ActiveRecord::Migration + def self.up + classes.collect(&:table_name).each do |table| + remove_column table, :coords + add_column table, :coords, :point, geographic: true, srid: 4326 + end + classes.each do |klass| + klass.all.each &:save! + end + end + + def self.down + classes.collect(&:table_name).each do |table| + remove_column table, :coords + add_column table, :coords, :point, srid: 4326 + end + end + + private + + def self.classes + [User, Event] + end +end diff --git a/db/schema.rb b/db/schema.rb index 692a734a..11788470 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,101 +1,102 @@ -# coding: UTF-8 - -# This file is auto-generated from the current state of the database. Instead of editing this file, -# please use the migrations feature of Active Record to incrementally modify your database, and -# then regenerate this schema definition. +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. # -# Note that this schema.rb definition is the authoritative source for your database schema. If you need -# to create the application database on another system, you should be using db:schema:load, not running -# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations # you'll amass, the slower it'll run and the greater likelihood for issues). # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20091020154940) do +ActiveRecord::Schema.define(:version => 20130803025446) do create_table "calendars", :force => true do |t| - t.column "name", :string, :null => false - t.column "created_at", :timestamp - t.column "updated_at", :timestamp + t.string "name", :null => false + t.datetime "created_at" + t.datetime "updated_at" end create_table "commitments", :force => true do |t| - t.column "event_id", :integer, :null => false - t.column "user_id", :integer, :null => false - t.column "status", :boolean + t.integer "event_id", :null => false + t.integer "user_id", :null => false + t.boolean "status" + t.text "comment" end create_table "countries", :force => true do |t| - t.column "code", :string, :limit => 2, :null => false - t.column "name", :string, :null => false + t.string "code", :limit => 2, :null => false + t.string "name", :null => false end create_table "events", :force => true do |t| - t.column "name", :string - t.column "date", :date - t.column "site", :string - t.column "street", :string - t.column "street2", :string - t.column "city", :string - t.column "state_id", :integer - t.column "zip", :string - t.column "created_at", :timestamp - t.column "updated_at", :timestamp - t.column "created_by_id", :integer - t.column "deleted", :boolean - t.column "description", :text - t.column "calendar_id", :integer, :null => false - t.column "coords", :point, :srid => 4326 + t.string "name" + t.date "date" + t.string "site" + t.string "street" + t.string "street2" + t.string "city" + t.integer "state_id" + t.string "zip" + t.datetime "created_at" + t.datetime "updated_at" + t.integer "created_by_id" + t.boolean "deleted" + t.text "description" + t.integer "calendar_id", :null => false + t.spatial "coords", :limit => {:srid=>4326, :type=>"point", :geographic=>true} end create_table "permissions", :force => true do |t| - t.column "user_id", :integer, :null => false - t.column "calendar_id", :integer, :null => false - t.column "role_id", :integer, :null => false - t.column "show_in_report", :boolean, :default => true, :null => false + t.integer "user_id", :null => false + t.integer "calendar_id", :null => false + t.integer "role_id", :null => false + t.boolean "show_in_report", :default => true, :null => false end add_index "permissions", ["user_id", "calendar_id", "role_id"], :name => "index_permissions_on_user_id_and_calendar_id_and_role_id", :unique => true create_table "roles", :force => true do |t| - t.column "name", :string - t.column "created_at", :timestamp - t.column "updated_at", :timestamp + t.string "name" + t.datetime "created_at" + t.datetime "updated_at" end create_table "states", :force => true do |t| - t.column "country_id", :integer - t.column "code", :string, :limit => 10, :null => false - t.column "name", :string, :null => false + t.integer "country_id" + t.string "code", :limit => 10, :null => false + t.string "name", :null => false end create_table "users", :force => true do |t| - t.column "email", :string - t.column "crypted_password", :string, :limit => 128, :default => "", :null => false - t.column "password_salt", :string, :limit => 128, :default => "", :null => false - t.column "firstname", :string - t.column "lastname", :string - t.column "street", :string - t.column "street2", :string - t.column "city", :string - t.column "state_id", :integer - t.column "zip", :string - t.column "activated_at", :timestamp - t.column "created_at", :timestamp - t.column "updated_at", :timestamp - t.column "show_contact", :boolean, :default => true - t.column "single_access_token", :string, :limit => 32, :null => false - t.column "coords", :point, :srid => 4326 - t.column "login_count", :integer, :default => 0, :null => false - t.column "failed_login_count", :integer, :default => 0, :null => false - t.column "last_request_at", :timestamp - t.column "current_login_at", :timestamp - t.column "last_login_at", :timestamp - t.column "current_login_ip", :string - t.column "last_login_ip", :string - t.column "persistence_token", :string, :default => "", :null => false - t.column "perishable_token", :string, :default => "", :null => false - t.column "active", :boolean, :default => false, :null => false + t.string "email" + t.string "crypted_password", :limit => 128, :default => "", :null => false + t.string "password_salt", :limit => 128, :default => "", :null => false + t.string "firstname" + t.string "lastname" + t.string "street" + t.string "street2" + t.string "city" + t.integer "state_id" + t.string "zip" + t.datetime "activated_at" + t.datetime "created_at" + t.datetime "updated_at" + t.boolean "show_contact", :default => true + t.string "single_access_token", :limit => 32, :null => false + t.integer "login_count", :default => 0, :null => false + t.integer "failed_login_count", :default => 0, :null => false + t.datetime "last_request_at" + t.datetime "current_login_at" + t.datetime "last_login_at" + t.string "current_login_ip" + t.string "last_login_ip" + t.string "persistence_token", :default => "", :null => false + t.string "perishable_token", :default => "", :null => false + t.boolean "active", :default => false, :null => false + t.spatial "coords", :limit => {:srid=>4326, :type=>"point", :geographic=>true} end add_index "users", ["perishable_token"], :name => "index_users_on_perishable_token" diff --git a/features/calendars/administer_calendars.feature b/features/calendars/administer_calendars.feature index 6424fd79..1403d12e 100644 --- a/features/calendars/administer_calendars.feature +++ b/features/calendars/administer_calendars.feature @@ -2,30 +2,30 @@ Feature: Administer calendars In order to keep calendars properly organized any administrator should be able to administer the calendars ey controls. - + Scenario: Non-admin users should not see "Admin tools" link Given I am logged in And I am subscribed to "Calendar 1" And I am on the homepage Then I should not see "Admin tools" - + Scenario Outline: Non-admin users should not be able to get to calendar admin pages Given I am logged in And I am subscribed to "<calendar>" When I go to the <page> for "<calendar>" Then I should not be on the <page> for "<calendar>" - + Examples: | calendar | page | | Calendar 1 | user list | | Calendar 2 | calendar edit page | - + Scenario: Admin users should see "Admin tools" link Given I am logged in And I am an admin of "Calendar 1" And I am on the homepage Then I should see "Admin tools" - + Scenario: Admin users should only be able to administer calendars they control Given I am logged in And I am an admin of "My calendar" @@ -35,7 +35,7 @@ Feature: Administer calendars Then I should be on the admin page And I should see "My calendar" And I should not see "Someone else's calendar" - + Scenario: Admin users should be able to change the name of calendars they control Given I am logged in And I am an admin of "My calendar" @@ -46,7 +46,7 @@ Feature: Administer calendars Then I should be on the admin page And I should not see /My calendar\s*\(properties \| users\)/ And I should see /New name\s*\(properties \| users\)/ - + Scenario: Admin users should be able to see user lists for calendars they control Given I am logged in And I am an admin of "My calendar" diff --git a/features/commitments/add_comment_to_commitment.feature b/features/commitments/add_comment_to_commitment.feature new file mode 100644 index 00000000..d38cf2aa --- /dev/null +++ b/features/commitments/add_comment_to_commitment.feature @@ -0,0 +1,47 @@ +Feature: Add comment to commitment + As a user + I can add comments to events + So I can qualify my commitment status + + Background: + Given someone else has an event called "Event" in "Calendar 1" + + Scenario Outline: Add comment to commitment + Given a user named "<name>" exists with email "<email>" + And I am logged in as "<email>" + And I am subscribed to "Calendar 1" + When I go to the event list + And I fill in "comment" with "<comment>" + And I press "Change status" + Then I should see "<name>: <comment>" + + Examples: + | email | name | comment | + | john@smith.com | John Smith | This is my comment text. | + + Scenario Outline: View other users' comments + Given someone else has a comment for "Event" in "Calendar 1" with text "<comment>" + And I am logged in + And I am subscribed to "Calendar 1" + When I go to the event list + Then I should see "<comment>" + + Examples: + | comment | + | This is someone else's comment. | + + Scenario Outline: Show attendance status on comments + Given a user named "John Doe" exists with email "john@doe.org" + And I am logged in as "john@doe.org" + And I am subscribed to "Calendar 1" + When I go to the event list + And I select "<status>" from "status" + And I fill in "comment" with "text" + And I press "Change status" + Then I should see "John Doe:" within a <status> user comment + + Examples: + | status | + | attending | + | not attending | + | uncommitted | diff --git a/features/commitment.feature b/features/commitments/update_commitment_status.feature similarity index 100% rename from features/commitment.feature rename to features/commitments/update_commitment_status.feature diff --git a/features/events/event_map.feature b/features/events/event_map.feature index 9385132d..2d1ef6d1 100644 --- a/features/events/event_map.feature +++ b/features/events/event_map.feature @@ -2,7 +2,7 @@ Feature: Event map As a registered user I can see a map of any event on calendars I subscribe to So I can figure out how to get there - + Scenario: Given I am logged in And an event exists diff --git a/features/events/list_events.feature b/features/events/list_events.feature index 71ca7296..34df7f3e 100644 --- a/features/events/list_events.feature +++ b/features/events/list_events.feature @@ -2,12 +2,12 @@ Feature: List events As a registered user I can see events on calendars I subscribe to So I can keep track of what's going on - + Scenario: Unregistered users can't get to event list Given I am not logged in When I go to the events page Then I should be on the login page - + Scenario Outline: It should sort events by date Given I am logged in And I am subscribed to "<calendar>" @@ -21,18 +21,31 @@ Feature: List events | October | | November | | December | - + Examples: | calendar | | My Amazing Calendar | - + Scenario Outline: It should not show deleted events Given I am logged in And I am subscribed to "<calendar>" And a deleted event exists with name: "<deleted>", calendar: the calendar When I go to the events page Then I should not see "<deleted>" - + Examples: | calendar | deleted | - | Deletion test | I'm invisible! | \ No newline at end of file + | Deletion test | I'm invisible! | + + Scenario Outline: It should linkify URLs in event descriptions + Given I am logged in + And I am subscribed to "My Calendar" + And the following events exist: + | calendar | description | + | the calendar | I have a link to <url> here. | + When I go to the events page + Then I should see a link to "<url>" + + Examples: + | url | + | http://www.example.com | \ No newline at end of file diff --git a/features/manage_subscriptions.feature b/features/manage_subscriptions.feature index ffbdc020..755fcebe 100644 --- a/features/manage_subscriptions.feature +++ b/features/manage_subscriptions.feature @@ -2,60 +2,59 @@ Feature: Manage subscriptions In order to control what content ey sees any registered user should be able to subscribe to and unsubscribe from any calendar of which ey is not an admin. - - Scenario Outline: Anyone can subscribe to a calendar + + Background: Given I am logged in - And someone else has a calendar called "<calendar>" + And no calendars exist + # TODO: why do we need that? + + Scenario Outline: Anyone can subscribe to a calendar + Given someone else has a calendar called "<calendar>" And I am on the homepage When I follow "Subscriptions" Then I should see "<calendar>" And I should see the word "subscribe" When I follow "subscribe" Then I should be subscribed to "<calendar>" - + Examples: | calendar | | Someone else's calendar | - + Scenario: Don't show the list of unsubscribed calendars if it's empty - Given I am logged in - And I am on the subscriptions page + Given I am on the subscriptions page Then I should not see "subscribe to these calendars" - + Scenario Outline: Non-admin users can unsubscribe from their calendars - Given I am logged in - And I am subscribed to "<calendar>" + Given I am subscribed to "<calendar>" And I am on the homepage When I follow "Subscriptions" Then I should see "<calendar>" And I should see the word "unsubscribe" When I follow "unsubscribe" Then I should not be subscribed to "<calendar>" - + Examples: | calendar | | My calendar | Scenario: Admin users cannot unsubscribe from calendars they control - Given I am logged in - And I am an admin of "My calendar" + Given I am an admin of "My calendar" And I am on the homepage When I follow "Subscriptions" Then I should not see the word "unsubscribe" - + Scenario: Admin users can unsubscribe from calendars they do not control - Given I am logged in - And I am an admin of "My calendar" + Given I am an admin of "My calendar" And I am subscribed to "Someone else's calendar" And I am on the homepage When I follow "Subscriptions" Then I should see the following in order: | unsubscribe | | Someone else's calendar | - + Scenario Outline: I should see the role for each subscribed calendar - Given I am logged in - And I am an admin of "<mine>" + Given I am an admin of "<mine>" And I am subscribed to "<other>" And I am on the subscriptions page Then I should see the following in order: @@ -64,7 +63,7 @@ Feature: Manage subscriptions And I should see the following in order: | <other> | | user | - + Examples: | mine | other | | My calendar | Someone Else's Calendar | diff --git a/features/step_definitions/calendar_steps.rb b/features/step_definitions/calendar_steps.rb index 4d816af0..52c2e1d1 100644 --- a/features/step_definitions/calendar_steps.rb +++ b/features/step_definitions/calendar_steps.rb @@ -5,22 +5,26 @@ user = User.current_user else names = user.gsub(/^"|"$/, '').split(' ', 2) - user = Factory :user, :firstname => names.first, :lastname => names.last + user = FactoryGirl.create :user, :firstname => names.first, :lastname => names.last end - cal = create_model(:calendar, :name => calendar) + cal = fetch_calendar calendar Permission.destroy(cal.permissions.find_all_by_user_id(user.id).collect(&:id)) # make sure we don't have any superfluous admin permissions hanging around FactoryGirl.create :permission, :user => user, :calendar => cal end Given /^I am an admin(?:istrator)? of "([^\"]*)"$/ do |calendar| - cal = Calendar.find_by_name(calendar) || FactoryGirl.create(:calendar, :name => calendar) + cal = fetch_calendar calendar FactoryGirl.create :permission, :user => UserSession.find.record, :calendar => cal, :role => FactoryGirl.create(:admin_role) end Given /^someone else has a calendar called "([^\"]*)"$/ do |calendar| - cal = Calendar.find_by_name(calendar) || FactoryGirl.create(:calendar, :name => calendar) + cal = fetch_calendar calendar Permission.destroy(cal.permissions.find_all_by_user_id(User.current_user.id).collect(&:id)) # make sure we don't have any superfluous admin permissions hanging around - Factory :admin_permission, :calendar => cal + FactoryGirl.create :admin_permission, :calendar => cal +end + +Given /^no calendars exist$/ do + Calendar.destroy_all end Then /^I should have a calendar called "([^\"]*)"$/ do |calendar| @@ -29,17 +33,13 @@ Then /^I should be an admin(?:istrator)? of "([^\"]*)"$/ do |calendar| admin = Role.find_or_create_by_name('admin') - cal = Calendar.find_by_name(calendar) + cal = fetch_calendar calendar User.current_user.permissions.find_by_calendar_id_and_role_id(cal.id, admin.id).should_not be_nil end Then /^I should (not )?be subscribed to "([^"]*)"$/ do |negation, calendar| - calendar = Calendar.find_by_name(calendar) - user = User.current_user - permission = user.permissions.find_by_calendar_id(calendar) - if negation - permission.should be_nil - else - permission.should_not be_nil + visit subscriptions_path + within '.subscriptions' do + page.has_content?(calendar).should == !negation end end \ No newline at end of file diff --git a/features/step_definitions/comment_steps.rb b/features/step_definitions/comment_steps.rb new file mode 100644 index 00000000..01dd32dc --- /dev/null +++ b/features/step_definitions/comment_steps.rb @@ -0,0 +1,5 @@ +Given /^someone else has a comment for "([^"]*)" in "([^"]*)" with text "([^"]*)"$/ do |event, calendar, comment| + calendar = fetch_calendar calendar + event = fetch_event name: event, calendar: calendar + FactoryGirl.create :commitment, event: event, comment: comment +end \ No newline at end of file diff --git a/features/step_definitions/event_steps.rb b/features/step_definitions/event_steps.rb index fd92f6c4..970a0f7b 100644 --- a/features/step_definitions/event_steps.rb +++ b/features/step_definitions/event_steps.rb @@ -25,5 +25,8 @@ end Then /^I should see a map of the event$/ do - Then 'I should see an element matching "#map"' + event = model! :event + page.should have_selector '#map' + page.should have_selector '#lat', text: event.latitude.to_s + page.should have_selector '#lng', text: event.longitude.to_s end \ No newline at end of file diff --git a/features/step_definitions/extra_web_steps.rb b/features/step_definitions/extra_web_steps.rb index 2a9777a4..c8737a40 100644 --- a/features/step_definitions/extra_web_steps.rb +++ b/features/step_definitions/extra_web_steps.rb @@ -16,10 +16,14 @@ page.should_not have_selector(selector) and response.should_not have_xpath(selector) end +Then /^I should see a link to "([^\"]*)"$/ do |url| + page.should have_selector("a[href='#{url}']") +end + Then /^I should see the following in order:$/ do |table| # table is a Cucumber::Ast::Table regexp = %r{#{table.raw.flatten.collect {|x| Regexp.escape x }.join '.*'}}m - + page.should have_xpath('//*', :text => regexp) end diff --git a/features/step_definitions/user_steps.rb b/features/step_definitions/user_steps.rb index e2675083..3b284fd2 100644 --- a/features/step_definitions/user_steps.rb +++ b/features/step_definitions/user_steps.rb @@ -1,12 +1,18 @@ # coding: UTF-8 +Given /^a user named "([^"]*)" exists with email "([^"]*)"$/ do |name, email| + first, last = name.split ' ', 2 + FactoryGirl.create :user, firstname: first, lastname: last, email: email +end + +Given /^I am logged in as "([^"]*)"$/ do |email| + user = User.find_by_email email + login_as user +end + Given /^I am logged in$/ do user = FactoryGirl.create :user, :password => 'passw0rd' - visit login_path - fill_in('user_session[email]', :with => user.email) - fill_in('user_session[password]', :with => 'passw0rd') - click_button 'Log in' - UserSession.find.record.should == user + login_as user end Given /^I am not logged in$/ do @@ -27,4 +33,14 @@ else user.should_not be_nil end -end \ No newline at end of file +end + +private + +def login_as(user) + visit login_path + fill_in('user_session[email]', :with => user.email) + fill_in('user_session[password]', :with => 'passw0rd') + click_button 'Log in' + UserSession.find.record.should == user +end diff --git a/features/support/additional_cucumber_setup.rb b/features/support/additional_cucumber_setup.rb index fc3b5f35..9a6bcef1 100644 --- a/features/support/additional_cucumber_setup.rb +++ b/features/support/additional_cucumber_setup.rb @@ -1,5 +1,11 @@ # coding: UTF-8 +begin + require 'ruby-debug' +rescue LoadError + warn 'Failed to load ruby-debug; continuing without it...' +end + # Set Gettext stuff so we can load Web pages. FastGettext.text_domain ||= SITE_TITLE FastGettext.available_locales ||= ['en'] \ No newline at end of file diff --git a/features/support/fetch_helpers.rb b/features/support/fetch_helpers.rb new file mode 100644 index 00000000..9ed43ea3 --- /dev/null +++ b/features/support/fetch_helpers.rb @@ -0,0 +1,14 @@ +module FetchHelpers + def fetch_calendar(name) + fields = {name: name} + args = [:calendar, fields] + find_model(*args) || create_model(*args) + end + + def fetch_event(options) + calendar = options.delete :calendar + calendar.events.find_by_name(options[:name]) || FactoryGirl.create(:event, calendar: calendar) + end +end + +World FetchHelpers diff --git a/features/support/geocoder.rb b/features/support/geocoder.rb new file mode 100644 index 00000000..50d07dc9 --- /dev/null +++ b/features/support/geocoder.rb @@ -0,0 +1,8 @@ +Geocoder.configure lookup: :test + +Geocoder::Lookup::Test.set_default_stub [ + { + 'latitude' => 1.0, + 'longitude' => 2.0 + } +] \ No newline at end of file diff --git a/features/support/selectors.rb b/features/support/selectors.rb index 52a7c707..d4587afd 100644 --- a/features/support/selectors.rb +++ b/features/support/selectors.rb @@ -25,6 +25,10 @@ def selector_for(locator) # when /the header/ # [:xpath, "//header"] + when /^an? (.+) user comment$/ + status = {'attending' => :yes, 'not attending' => :no, 'uncommitted' => :maybe}[$1] + ".comments .user.#{status}" + # This allows you to provide a quoted selector as the scope # for "within" steps as was previously the default for the # web steps: diff --git a/lib/acts/geocoded.rb b/lib/acts/geocoded.rb new file mode 100644 index 00000000..17e378b8 --- /dev/null +++ b/lib/acts/geocoded.rb @@ -0,0 +1,29 @@ +module Acts + module Geocoded + extend ActiveSupport::Concern + + included do + acts_as_addressed + geocoded_by :address_for_geocoding do |model, results| + if result = results.first + model.coords = model.class.rgeo_factory_for_column(:coords).point result.longitude, result.latitude + end + end + after_validation :geocode + end + + module InstanceMethods + private + + def address_for_geocoding + address.to_s :geo + end + end + end +end + +ActiveRecord::Base.class_eval do + def self.acts_as_geocoded + include Acts::Geocoded + end +end \ No newline at end of file diff --git a/lib/geocoding_utilities.rb b/lib/geocoding_utilities.rb deleted file mode 100644 index 49351a8c..00000000 --- a/lib/geocoding_utilities.rb +++ /dev/null @@ -1,43 +0,0 @@ -# coding: UTF-8 - -module GeocodingUtilities - def self.included(klass) - klass.before_update :clear_coords - end - - # Returns a #Point with the coordinates of the model's address, or with (0, 0) if all else fails, and caches the coordinates so we don't hit the geocoder every time. - def coords - c = self[:coords] - if c.nil? - begin - c = coords_from_string(address.to_s(:geo)) - self[:coords] = c - self.save - rescue - c = Point.from_x_y(0, 0) - end - end - c - end - - # Sends the address contained in _string_ to a geocoder, and returns a #Point object with the resulting coordinates. - # - # _String_ is assumed to be in the format output by #Address#to_s(:geo) - # (currently <tt>"#{street}, #{city}, #{state.code}, #{zip}, #{country.code}"</tt>), - # but depending on the geocoder, other string formats are likely to work as well. - def coords_from_string(string) - host = @request.nil? ? nil : @request.host_with_port - host ||= DOMAIN - geo = Geocoding::get(string, :host => host) - if geo.status == Geocoding::GEO_SUCCESS - return Point.from_coordinates(geo[0].lonlat) - else - raise "Geocoding failed with code #{geo.status} for #{string}" - end - end - - # Clears the coordinates of the model so they will refresh themselves next time Model.coords is called - def clear_coords - self.coords = nil - end -end \ No newline at end of file diff --git a/lib/tasks/ci.rake b/lib/tasks/ci.rake new file mode 100644 index 00000000..6d3b82df --- /dev/null +++ b/lib/tasks/ci.rake @@ -0,0 +1,15 @@ +namespace :db do + desc 'Install PostGIS in the database' + task install_postgis: :environment do + ActiveRecord::Base.connection.execute 'CREATE EXTENSION postgis;' + end + + desc 'Create database and load PostGIS and schema' + task setup_with_postgis: %w(db:create db:install_postgis db:schema:load) +end + +desc 'Run CI tests (intended for Travis)' +task ci: 'db:setup_with_postgis' do + sh 'bundle exec rspec -O .rspec.travis' + sh 'bundle exec cucumber' +end \ No newline at end of file diff --git a/lib/tasks/databases.rake b/lib/tasks/databases.rake deleted file mode 100644 index dc8988d0..00000000 --- a/lib/tasks/databases.rake +++ /dev/null @@ -1,41 +0,0 @@ -# coding: UTF-8 - -namespace :db do - namespace :test do - desc "Empty the test database" - task :purge => :environment do - abcs = ActiveRecord::Base.configurations - case abcs["test"]["adapter"] - when "mysql" - ActiveRecord::Base.establish_connection(:test) - ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"]) - when "postgresql" - ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"] - ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"] - ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"] - enc_option = "-E #{abcs["test"]["encoding"]}" if abcs["test"]["encoding"] - - ActiveRecord::Base.clear_active_connections! - `dropdb -U "#{abcs["test"]["username"]}" #{abcs["test"]["database"]}` - `createdb #{enc_option} -U "#{abcs["test"]["username"]}" -T "#{abcs["test"]["template"]}" #{abcs["test"]["database"]}` - when "sqlite","sqlite3" - dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"] - File.delete(dbfile) if File.exist?(dbfile) - when "sqlserver" - dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-') - `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}` - `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` - when "oci", "oracle" - ActiveRecord::Base.establish_connection(:test) - ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl| - ActiveRecord::Base.connection.execute(ddl) - end - when "firebird" - ActiveRecord::Base.establish_connection(:test) - ActiveRecord::Base.connection.recreate_database! - else - raise "Task not supported by '#{abcs["test"]["adapter"]}'" - end - end - end -end \ No newline at end of file diff --git a/lib/tasks/rspec.rake b/lib/tasks/rspec.rake deleted file mode 100644 index 0e01578a..00000000 --- a/lib/tasks/rspec.rake +++ /dev/null @@ -1,146 +0,0 @@ -# coding: UTF-8 - -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 diff --git a/public/javascripts/events/index.js b/public/javascripts/events/index.js index ca11f7a2..06c929d9 100644 --- a/public/javascripts/events/index.js +++ b/public/javascripts/events/index.js @@ -13,6 +13,7 @@ function ajaxify_page() { 'form.attendance input[type=submit]': function () { this.hide(); }, + 'form.attendance textarea:blur': ajaxify_form, 'form.attendance select.commit:change': ajaxify_form }); }; @@ -29,10 +30,11 @@ function ajaxify_form(event) { var id = row.id; row.replace(transport.responseText); var newForm = $(id).down('form.attendance'); - - // these two statements repeat ajaxify_page; can we do better? + + // TODO: these statements repeat ajaxify_page; can we do better? newForm.down('input[type=submit]').hide(); newForm.down('select.commit').observe('change', ajaxify_form); + newForm.down('textarea').observe('blur', ajaxify_form); f = prepare_spinner.bind(newForm.down('.progress')); f(); }, diff --git a/public/javascripts/events/map.js b/public/javascripts/events/map.js index 6b665202..60306b93 100644 --- a/public/javascripts/events/map.js +++ b/public/javascripts/events/map.js @@ -1,4 +1,8 @@ -window.onload = addCodeToFunction(window.onload,function() { +var oldOnload = window.onload; +window.onload = function() { + if(oldOnload) { + oldOnload(); + } if (GBrowserIsCompatible()) { var map = new GMap2(document.getElementById("map")); var lat = parseFloat(document.getElementById('lat').innerHTML); @@ -6,12 +10,12 @@ window.onload = addCodeToFunction(window.onload,function() { var latlng = new GLatLng(lat,lng); var info = document.getElementById('info'); var marker = new GMarker(latlng); - + map.setCenter(latlng,14); marker.bindInfoWindow(info); map.addOverlay(marker); marker.openInfoWindow(info);map.addControl(new GLargeMapControl()); map.addControl(new GMapTypeControl()); } -}); +}; diff --git a/public/stylesheets/sass/_icon.scss b/public/stylesheets/sass/_icon.scss new file mode 100644 index 00000000..50a27e8b --- /dev/null +++ b/public/stylesheets/sass/_icon.scss @@ -0,0 +1,22 @@ +// size of icons from Silk set +$silk_icon_size: 16px; + +// margin around icons +$default_icon_margin: 3px; + +@mixin icon($icon_url, $icon_size: $silk_icon_size, $icon_margin: $default_icon_margin) { + background: { + image: $icon_url; + repeat: no-repeat; }; + min-height: $icon_size + $icon_margin; + padding-left: $icon_size + $icon_margin; } + +@mixin attendance_icon($status) { // $status is 'yes', 'no', or 'maybe' + $attendance_icon_size: 12px; + $extension: png; + @if $status == maybe { + $extension: gif; + } + $icon_url: url("../images/attend_#{$status}.#{$extension}"); + @include icon($icon_url, $attendance_icon_size); +} \ No newline at end of file diff --git a/public/stylesheets/sass/quorum.scss b/public/stylesheets/sass/quorum.scss index faab77e0..ddb9132f 100644 --- a/public/stylesheets/sass/quorum.scss +++ b/public/stylesheets/sass/quorum.scss @@ -1,10 +1,7 @@ -$title_blue: #3333ff; - -// size of icons from Silk set -$silk_icon_size: 16px; +@import "_icon"; -// margin around icons -$default_icon_margin: 3px; +$light_gray: #999999; +$title_blue: #3333ff; body { color: black; @@ -28,10 +25,6 @@ body { weight: bolder; size: 116%; }; } -.footer { - border-top: 1px solid black; - padding-top: 2px; } - #navbar { color: white; background-color: $title_blue; @@ -65,38 +58,34 @@ th { size: 90%; weight: lighter; }; } -span.yes { - color: lime; - font-weight: bolder; } - -span.no { - font-weight: bolder; - color: red; } - -span.maybe { - color: yellow; - font-weight: bolder; } - -$circle_size: 12px; -$attend_yes_url: url("../images/attend_yes.png"); -$attend_no_url: url("../images/attend_no.png"); -$attend_maybe_url: url("../images/attend_maybe.gif"); - td.yes p { - background-image: $attend_yes_url; } + @include attendance_icon(yes); +} td.no p { - background-image: $attend_no_url; } + @include attendance_icon(no); +} td.maybe p { - background-image: $attend_maybe_url; } + @include attendance_icon(maybe); +} -td.yes, td.no, td.maybe { +td.attendance { background-color: black; color: white; - p { - padding-left: $circle_size + $default_icon_margin; - background-repeat: no-repeat; } } + + span.yes { + color: lime; + font-weight: bolder; } + + span.no { + font-weight: bolder; + color: red; } + + span.maybe { + color: yellow; + font-weight: bolder; } +} td.actions { background-color: transparent; @@ -105,14 +94,27 @@ td.actions { a { display: inline-block; } } -// Toolbar links with icons +.comments .user { + font-weight: bolder; -@mixin icon($icon_url, $icon_size: $silk_icon_size, $icon_margin: $default_icon_margin) { - background: { - image: $icon_url; - repeat: no-repeat; }; - min-height: $icon_size + $icon_margin; - padding-left: $icon_size + $icon_margin; } + &.yes { + @include attendance_icon(yes); + } + + &.no { + @include attendance_icon(no); + } + + &.maybe { + @include attendance_icon(maybe); + } +} + +td .count { + font-weight: bolder; +} + +// Toolbar links with icons $edit_icon_url: url("../images/pencil.png"); @@ -196,6 +198,20 @@ $error_icon_url: url("../images/exclamation.png"); p { padding: 0.25em; } } +#footer { + margin-top: 2em; + border-top: 1px solid $light_gray; + .version { + font-size: 90%; + color: $light_gray; + } +} + +#map { + height: 400px; + width: 500px; +} + /* Some hCalendar properties... */ abbr { text-decoration: inherit; } diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 8e8f7007..40fb4102 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -9,17 +9,17 @@ UserSession.create false controller.admin?.should be_nil end - + it "should return true if current user is an admin" do - admin_role = Factory :admin_role - Role.should_receive(:find_by_name).with('admin').and_return admin_role - @admin = Factory(:user).tap {|u| u.permissions << Factory(:admin_permission, :user => u, :role => admin_role) } + admin_role = FactoryGirl.create :admin_role + Role.should_receive(:find_by_name).with('admin').at_least(:once).and_return admin_role + @admin = FactoryGirl.create(:user).tap {|u| u.permissions << FactoryGirl.create(:admin_permission, :user => u, :role => admin_role) } UserSession.create @admin controller.admin?.should be_true end - + it "should return false if current user is not an admin" do - @user = Factory(:user).tap {|u| u.permissions << Factory(:permission, :user => u) } + @user = FactoryGirl.create(:user).tap {|u| u.permissions << FactoryGirl.create(:permission, :user => u) } UserSession.create @user controller.admin?.should be_false end diff --git a/spec/controllers/events_controller_spec.rb b/spec/controllers/events_controller_spec.rb index 8cba4d22..2f962336 100644 --- a/spec/controllers/events_controller_spec.rb +++ b/spec/controllers/events_controller_spec.rb @@ -6,19 +6,17 @@ before(:each) do UserSession.create FactoryGirl.create(:user) end - + it "should pass sorting parameters from the URL" do order = 'name' direction = 'desc' params = {:order => order, :direction => direction} {:get => "/events/index/#{order}/#{direction}"}.should route_to params.merge(:controller => 'events', :action => 'index') - Event.should_receive(:find) do |arg1, arg2| - arg1.should == :all - arg2.should be_an_instance_of(Hash) - arg2.should have_key(:order) - arg2[:order].should == "#{order} #{direction}" - arg2.should have_key(:conditions) - conditions = arg2[:conditions] + mock_conditions = mock 'conditions' + mock_conditions.should_receive(:order).with "#{order} #{direction}" + mock_includes = mock 'includes' + Event.should_receive(:includes).and_return(mock_includes) + mock_includes.should_receive(:where) do |conditions| conditions.should be_an_instance_of(Array) conditions[0].should =~ /date >= :from_date/i conditions[1].should be_an_instance_of(Hash) @@ -27,24 +25,25 @@ conditions[1][:from_date].should == Time.zone.today # default value if not set in params conditions[1].should have_key(:to_date) conditions[1][:to_date].should be_nil + mock_conditions end get :index, params =begin TODO: when we have a search form, I suppose :) - + If to_date is not nil, then we need the following specs for the monstrosity above: conditions[0].should =~ /between :from_date and :to_date/i conditions[1][:to_date].should be_an_instance_of(Date) conditions[1][:to_date].should > Time.zone.today + 99.years =end end - + it "should have date/asc as default order and direction in URL" do pending "route_for doesn't actually seem to work this way" do route_for(:controller => 'events', :action => 'index', :order => 'date', :direction => 'asc').should == 'foo' # '/events/index' end end - + it "should pass sorting parameters on to the view" do get :index assigns[:order].should_not be_nil @@ -54,42 +53,42 @@ describe EventsController, "feed.rss" do render_views - + before(:each) do user = FactoryGirl.create :user User.stub!(:current_user).and_return(user) # we need this for some of the callbacks on Calendar and Event @calendar = FactoryGirl.create :calendar + FactoryGirl.create :permission, user: user, calendar: @calendar, role: FactoryGirl.create(:role) @one = FactoryGirl.create :event, :name => 'Event 1', :calendar => @calendar, :date => Date.civil(2008, 7, 4), :description => 'The first event.', :created_at => 1.week.ago @two = FactoryGirl.create :event, :name => 'Event 2', :calendar => @calendar, :date => Date.civil(2008, 10, 10), :description => 'The <i>second</i> event.', :created_at => 2.days.ago @events = [@one, @two] - controller.stub!(:current_objects).and_return(@events) get :feed, :format => 'rss', :key => user.single_access_token end - + it "should be successful" do response.should be_success end - + it "should set a MIME type of application/rss+xml" do response.content_type.should =~ (%r{^application/rss\+xml}) end - + it "should set RSS version 2.0 and declare the Atom namespace" do m = response.body[%r{<\s*rss(\s*[^>]*)?>}] m.should_not be_blank m.should =~ /version=(["'])2.0\1/ m.should =~ %r{xmlns:\w+=(["'])http://www.w3.org/2005/Atom\1} end - + it "should set @key to params[:key]" do controller.params[:key].should_not be_nil assigns[:key].should == controller.params[:key] end - + it "should set params[:feed_user] to the user whom the key belongs to" do controller.params[:feed_user].should == User.find_by_single_access_token(controller.params[:key]) end - + it "should have an <atom:link rel='self'> tag" do css_select('rss').each do |rss| @m = css_select(rss, 'channel')[0].to_s[%r{<\s*atom:link(\s*[^>]*)?>}] @@ -98,19 +97,19 @@ @m.should =~ /href=(["'])#{feed_events_url(:rss, controller.params[:key])}\1/ @m.should =~ /rel=(["'])self\1/ end - + it "should have an appropriate <title> tag" do response.body.should have_selector('channel > title', :content => %r{#{SITE_TITLE}}) end - + it "should link to the event list" do response.body.should have_selector('channel > link', :content => events_url) end - + it "should contain a <description> element, including (among other things) the name of the user whose feed it is" do response.body.should have_selector('channel > description', :content => %r{#{ERB::Util::html_escape controller.params[:feed_user]}}) end - + it "should contain an entry for every event, with <title>, <description> (with address and description), <link>, <guid>, and <pubDate> elements" do @events.each do |e| response.body.should have_selector('item title', :content => ERB::Util::html_escape(e.name)) # actually, this is XML escape, but close enough @@ -126,21 +125,25 @@ describe EventsController, "feed.rss (login)" do render_views - + it "should not list any events if given an invalid single_access_token" do User.stub!(:find_by_single_access_token).and_return(nil) get :feed, :format => 'rss', :key => 'fake key' Event.should_not_receive(:find) response.should_not have_selector('item') end - + it "should list events if given a valid single_access_token" do @user = FactoryGirl.create :user UserSession.create @user - calendar = FactoryGirl.create :calendar # @user will be subscribed to + calendar = FactoryGirl.create :calendar + FactoryGirl.create :permission, user: @user, calendar: calendar @events = (1..5).map { FactoryGirl.create :event, :calendar => calendar } User.stub!(:find_by_single_access_token).and_return(@user) - Event.should_receive(:find).and_return(@events) + @events.stub!(:order).and_return @events + mock_includes = mock 'includes' + mock_includes.should_receive(:where).and_return(@events) + Event.should_receive(:includes).and_return mock_includes get :feed, :format => 'rss', :key => @user.single_access_token response.body.should have_selector('item') end @@ -148,33 +151,33 @@ describe EventsController, 'index.pdf' do before(:each) do - @user = Factory(:user) + @user = FactoryGirl.create :user UserSession.create @user User.stub(:current_user).and_return @user request.env["SERVER_PROTOCOL"] = "http" # see http://iain.nl/prawn-and-controller-tests end - + context 'generic events' do before :each do - event = Factory :event - Factory(:permission, :calendar => event.calendar, :user => @user) + event = FactoryGirl.create :event + FactoryGirl.create :permission, :calendar => event.calendar, :user => @user controller.stub!(:current_objects).and_return([event]) end - + it "should be successful" do get :index, :format => 'pdf' response.should be_success end - + it "should return the appropriate MIME type for a PDF file" do get :index, :format => 'pdf' response.content_type.should =~ %r{^application/pdf} end end - + it "should set assigns[:users]" do - @perms = [Factory(:permission, :user => @user)] - @event = Factory :event, :calendar => Factory(:calendar, :permissions => @perms) + @perms = [FactoryGirl.create(:permission, :user => @user)] + @event = FactoryGirl.create :event, :calendar => FactoryGirl.create(:calendar, :permissions => @perms) controller.stub(:current_objects).and_return([@event]) get :index, :format => 'pdf' assigns[:users].should_not be_nil @@ -186,7 +189,7 @@ @user = FactoryGirl.create :user UserSession.create @user end - + it "should change attendance status for current user if called with a non-nil event id" do event = FactoryGirl.create :event commitment = FactoryGirl.create :commitment, :user => @user, :event => event, :status => true @@ -198,12 +201,12 @@ commitment.should_receive(:save!).once.and_return(true) get "change_status", :id => id, :status => status end - + it "should redirect to index on a standard request" do get 'change_status' response.should redirect_to(:action => :index) end - + it "should render an event row on an Ajax request" do event = FactoryGirl.create :event xhr :get, "change_status", :id => event.id, :status => 'yes' # status could also be :no or :maybe @@ -215,7 +218,7 @@ before(:each) do @session = UserSession.create FactoryGirl.create(:user) end - + it "should require login" do get :new response.should be_success @@ -223,55 +226,51 @@ get :new response.body.should be_blank # not sure why this works and nothing else does... end - + it "should be successful if logged in" do get :new response.should be_success end - + it "should set the page_title" do get 'new' assigns[:page_title].should_not be_nil end - + it "should create an Event object" do e = Event.new Event.should_receive(:new).and_return(e) get 'new' assigns[:event].should_not be_nil end - + it "should redirect to event list with flash after post with successful save, but not otherwise" do get 'new' response.should_not redirect_to(:action => :list) - my_event = Event.new #invalid + my_event = FactoryGirl.build :event, name: nil, calendar: nil, state: nil # invalid post :create, :event => my_event.attributes response.should_not redirect_to(:action => :list) - - my_event = Event.new(:name => 'name', :state_id => 23, :calendar_id => 'foo') # minimal valid set of attributes + + my_event = FactoryGirl.build :event post :create, :event => my_event.attributes response.should redirect_to(:action => :index) flash[:notice].should_not be_nil end - + end describe EventsController, "create" do + let(:user) { FactoryGirl.create :user } + before(:each) do - UserSession.create FactoryGirl.create(:user) + UserSession.create user end - + it "should save an Event object" do - my_event = Event.new(:name => 'name', :state_id => 23) - my_event.should_not be_nil - my_event.created_by_id.should be_nil - Event.stub!(:new).and_return(my_event) - my_event.should_receive(:save) - post :create, :event => my_event.attributes - # assigns[:event].name.should == my_event.name - # assigns[:event].id.should_not be_nil - # assigns[:event].created_by_id.should == User.current_user.id + event = FactoryGirl.build :event, created_by: nil + post :create, event: event.attributes + Event.find_by_name(event.name).created_by.should == user end end @@ -281,7 +280,7 @@ @admin = admin_user(@event.calendar) UserSession.create @admin end - + it "should redirect to list with an error if the user does not own the event and is not an admin" do @event.should_receive(:allow?).with(:edit).and_return(false) Event.should_receive(:find).and_return(@event) @@ -289,7 +288,7 @@ flash[:error].should_not be_nil response.should redirect_to(:action => :index) end - + it 'should allow editing if the user is authorized to edit the event' do @event.should_receive(:allow?).with(:edit).and_return(true) Event.should_receive(:find).and_return(@event) @@ -297,28 +296,23 @@ flash[:error].should be_nil response.should_not redirect_to(:action => :index) end - + it "should redirect to list with an error if the event does not exist" do get 'edit', :id => 0 # nonexistent flash[:error].should_not be_nil response.should redirect_to(:action => :index) end - - it "should reuse the new-event form" do - get 'edit', :id => @event.id - response.should render_template(:new) - end - + it "should set the page title" do get 'edit', :id => @event.id assigns[:page_title].should_not be_nil end - + it "should set the event" do get 'edit', :id => @event.id assigns[:event].should == @event end - + =begin it "should redirect to list with a flash error if no event id is supplied or if id is invalid" do get 'edit', :id => 'a' # invalid @@ -337,35 +331,20 @@ event.should be_valid post 'update', :event => event.attributes, :id => id # valid request.should be_post - assigns[:current_object].should_receive(:update_attributes) response.should redirect_to(:action => :index) flash[:notice].should_not be_nil - + event.name = nil # now it's invalid post 'update', :event => event.attributes, :id => id response.should_not redirect_to(:action => :index) end - - it "should reset coords to nil when saving" do - event = Event.find(:first) - id = event.id.to_s - event.coords.should_not be_nil -=begin - Event.should_receive(:find).with(id).twice.and_return(event, Event.find_by_id(id)) - event.should_receive(:update_attributes).with(an_instance_of(Hash)).once -=end - post 'edit', :event => event.attributes, :id => id - event = Event.find(id) - event.should_receive(:coords_from_string).once # calling event.coords should trigger recoding - event.coords - end end describe EventsController, "show" do before(:each) do UserSession.create FactoryGirl.create(:user) end - + it "should set the page title" do @event = FactoryGirl.create :event @event.should_receive(:allow?).with(:show).at_least(:once).and_return(true) @@ -374,7 +353,7 @@ assigns[:page_title].should_not be_nil assigns[:page_title].should =~ Regexp.new(@event.name) end - + it "should not show an event on a calendar for which the current user doesn't have access" do @event = mock_model(Event, :id => 4, :calendar_id => 57, :name => 'Event on another calendar') @event.should_receive(:allow?).with(:show).at_least(:once).and_return(false) @@ -391,14 +370,14 @@ @event = FactoryGirl.create :event, :calendar => @calendar @id = @event.id end - + it "should not work from non-admin account" do UserSession.create FactoryGirl.create(:user) @event.should_not_receive(:hide) post 'delete', :id => @id flash[:error].should_not be_nil end - + it "should work from admin account" do UserSession.create admin_user(@event.calendar) Event.should_receive(:find).with(@id.to_i).and_return(@event) @@ -414,29 +393,29 @@ UserSession.create FactoryGirl.create(:user) @one = FactoryGirl.create :event end - + it "should use the map view" do get :map, :id => @one.id response.should render_template(:map) end - + it "should get an event" do id = @one.id get :map, :id => id assigns[:event].should == @one end - + it "should set the page title" do get :map, :id => @one.id assigns[:page_title].should_not be_nil end - + it "should set the hostname" do get :map, :id => @one.id assigns[:host].should_not be_nil end - -=begin + +=begin it "should center the map on the event and add a marker and basic and scale controls" do @mock = GMap.new(:map) GMap.should_receive(:new).with(:map).and_return(@mock) @@ -460,17 +439,17 @@ @my_event = FactoryGirl.create :event Event.should_receive(:find).with(@my_event.id.to_i).and_return(@my_event) end - + it "should use the ical view" do get :export, :id => @my_event.id response.should render_template('events/ical') end - + it "should get an event" do get :export, :id => @my_event.id assigns[:event].should == @my_event end - + it "should set a MIME type of text/calendar" do get :export, :id => @my_event.id response.content_type.should =~ (%r{^text/calendar}) @@ -480,9 +459,9 @@ # Returns a User with admin permissions on the specified Calendar. def admin_user(calendar) admin = Role.find_or_create_by_name('admin') - Factory(:user).tap do |u| + FactoryGirl.create(:user).tap do |u| u.permissions.destroy_all - u.permissions << Factory(:permission, :calendar => calendar, :user => u, :role => admin) + u.permissions << FactoryGirl.create(:permission, :calendar => calendar, :user => u, :role => admin) end end diff --git a/spec/factories.rb b/spec/factories.rb index 55811ef2..032b6e86 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -34,65 +34,70 @@ date { Date.civil(rand(10) + 2100, rand(12) + 1, rand(28) + 1) } # way in the future so it shows up on the event list calendar association :created_by, :factory => :user - + factory :deleted_event do deleted { true } end end - + factory :state, :class => Acts::Addressed::State do country name { Faker::Name.last_name } # generic_name code { LETTERS.sample + LETTERS.sample } end - + factory :country, :class => Acts::Addressed::Country do name { Faker::Name.last_name } # generic_name code { LETTERS.sample + LETTERS.sample } end - + factory :calendar do name {Faker::Name.name + "'s calendar"} end - + factory :user do firstname { Faker::Name.first_name } lastname { Faker::Name.last_name } email { Faker::Internet.email } - password { (1..(rand(15) + 4)).map{(32..127).to_a.sample.chr}.join } + password {'passw0rd'} password_confirmation { password } street { Faker::Address.street_address } street2 { Faker::Address.secondary_address } city {Faker::Address.city} - association :state_raw, :factory => :state + association :state_raw, :factory => :state zip { Faker::Address.zip_code } active {true} - + factory :inactive_user do active {false} end + + factory :user_with_random_password do + password { (1..(rand(15) + 4)).map{(32..127).to_a.sample.chr}.join } + end end - + factory :commitment do - event - user + event + user status {true} + comment { Faker::Lorem.sentence } end - + factory :permission do user role calendar show_in_report {true} - + factory :admin_permission do association :role, :factory => :admin_role end end - + factory :role do name {'user'} - + factory :admin_role do name {'admin'} end diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index 26682d01..412c3cb7 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -4,31 +4,32 @@ describe EventsHelper do before(:each) do + User.delete_all # TODO: why is this necessary? @event = FactoryGirl.create :event end - + # refactor from list.html.erb_spec into here? - + it "should generate an iCal unique id as a String" do helper.ical_uid(@event).should be_a_kind_of(String) end - + it "should generate a delete link as a String" do helper.delete_link(@event).should be_a_kind_of(String) end - + it "should generate an edit link as a String" do helper.edit_link(@event).should be_a_kind_of(String) end - + it "should generate an iCal export link as a String" do helper.ical_link(@event).should be_a_kind_of(String) end - + it "should generate a map link as a String" do helper.map_link(@event).should be_a_kind_of(String) end - + it "should generate a microformat HTML date element as a String" do @event.date = Time.now # arbitrary value helper.date_element(@event).should be_a_kind_of(String) @@ -47,94 +48,56 @@ names.should include(user.to_s) end end - + it "should get an attendance status for an event and a user" do helper.attendance_status(@event, FactoryGirl.create(:user)).should == :maybe end - + + describe '#attendance_comment' do + let(:user) { FactoryGirl.create :user } + + it 'should retrieve the comment string for the event and user' do + commitment = FactoryGirl.create :commitment, event: @event, user: user + helper.attendance_comment(@event, user).should == commitment.comment + end + + it "should return nil if there's no commitment for the event and user" do + helper.attendance_comment(@event, user).should be_nil + end + end + it "should generate a distance string from an event to a user's coords," do - marnen = FactoryGirl.create :user, :coords => Point.from_x_y(5, 10) # TODO: use Faker instead of arbitrary coordinates + marnen = FactoryGirl.create :user, :coords => User.rgeo_factory_for_column(:coords).point(5, 10) # TODO: use Faker instead of arbitrary coordinates @event.coords = marnen.coords helper.distance_string(@event, marnen).should =~ /\D\d(\.\d+)? miles/ user = User.new # distance_string(@event, user).should == "" # user.coords is nil -- this spec is not working right now - @event = Event.new do |e| e.coords = Point.from_x_y(0, 2) end - user.coords = Point.from_x_y(0, 1) - helper.distance_string(@event, user).should =~ /\D6(8.7)|9.*miles.*#{h('•')}$/ # 1 degree of latitude + @event = Event.new do |e| e.coords = Event.rgeo_factory_for_column(:coords).point(0, 2) end + user.coords = User.rgeo_factory_for_column(:coords).point(0, 1) + helper.distance_string(@event, user).should =~ /\D6(8\.7)|9\D.*miles/ # 1 degree of latitude end - + it "should generate a sort link for a table header (asc unless desc is specified)" do @request.stub!(:path_parameters).and_return(:controller => 'events', :action => 'index') link = helper.sort_link("Date", :date) link.should be_a_kind_of(String) link.should have_selector("a.sort[href='#{url_for :controller => 'events', :action => 'index', :order => :date, :direction => :asc}']", :content => "Date") - + #link = sort_link("Date", :date, :desc) #link.should match(/\A<a [^>]*href="#{url_for :controller => 'events', :action => 'index', :order => :date, :direction => :desc}".*<\/a>\Z/i) end end -describe EventsHelper, "event_map" do - before(:each) do - User.stub!(:current_user).and_return(FactoryGirl.create :user) - end - - it "should return a safe string" do - helper.event_map(Factory(:event), DOMAIN).should be_html_safe - end - - it "should set up a GMap with all the options" do - event = FactoryGirl.create :event - - # TODO: since this code is now in events/map.js , translate these specs into JavaScript! -=begin - marker = GMarker.new([1.0, 2.0]) - gmap_header = "[Stubbed header for #{DOMAIN}]" - GMap.should_receive(:header).with(:host => DOMAIN).at_least(:once).and_return(gmap_header) - GMarker.stub!(:new).and_return(marker) - gmap.should_receive(:center_zoom_init) - gmap.should_receive(:overlay_init).with(marker) - marker.should_receive(:open_info_window).with(EventsHelper::ElementVar.new(helper.info(event))) - gmap.should_receive(:control_init) do |opts| - opts.should be_a_kind_of(Hash) - opts.should have_key(:large_map) - opts[:large_map].should == true - opts.should have_key(:map_type) - opts[:map_type].should == true - end - gmap.should_receive(:to_html).at_least(:once) -=end - - gmap_header = "[Stubbed header for #{DOMAIN}]" - GMap.should_receive(:header).with(:host => DOMAIN).at_least(:once).and_return(gmap_header) - gmap = GMap.new(:gmap) - gmap_div = '<div id="gmap">GMap div</div>' - gmap.should_receive(:div).and_return(gmap_div) - GMap.should_receive(:new).and_return(gmap) - - map = helper.event_map(event, DOMAIN) - {'#gmap' => nil, '#info' => nil, '#lat' => ERB::Util::h(event.coords.lat), '#lng' => ERB::Util::h(event.coords.lng)}.each do |k, v| - map.should have_selector(k, :content => v) - end - - pending "RSpec 2 has broken these lines. Not sure how to fix, but in any case we should change the helper architecture." do - assigns[:extra_headers].should_not be_nil - assigns[:extra_headers].should include(gmap_header) - assigns[:extra_headers].should include(javascript_include_tag 'events/map') - end - end -end - describe EventsHelper, "ical_escape" do it "should make newlines into '\n'" do helper.ical_escape("a\na").should == 'a\\na' end - + it "should double backslashes" do bb = '\\' + '\\' helper.ical_escape('\\c\\n\\').should == bb + 'c' + bb + 'n' + bb end - + it "should put backslashes before commas and semicolons" do helper.ical_escape('comma,semicolon;').should == 'comma\\,semicolon\\;' end @@ -142,19 +105,19 @@ describe EventsHelper, "info" do before :each do - User.stub(:current_user).and_return(Factory :user) - @event = Factory :event + User.stub(:current_user).and_return(FactoryGirl.create :user) + @event = FactoryGirl.create :event @info = helper.info(@event) end - + it "should return a safe string" do @info.should be_html_safe end - + it "should display a <h3> with the site name" do @info.should have_selector('h3', :content => @event.site) end - + it "should display the address separated by line breaks" do @info.should include([h(@event.street), h(@event.street2), h([@event.city, @event.state.code, @event.state.country.code].join(', '))].join(tag :br)) end @@ -180,7 +143,7 @@ User.current_user = user helper.rss_url.should == feed_events_url(:format => :rss, :key => user.single_access_token) end - + it "should return nil if there is no current user" do User.current_user = false helper.rss_url.should be_nil @@ -188,3 +151,9 @@ helper.rss_url.should be_nil end end + +describe EventsHelper, '#status_strings' do + it 'should return an array of status strings' do + helper.status_strings.should == {yes: 'attending', no: 'not attending', maybe: 'uncommitted'} + end +end diff --git a/spec/helpers/gmaps_helper_spec.rb b/spec/helpers/gmaps_helper_spec.rb new file mode 100644 index 00000000..547ff011 --- /dev/null +++ b/spec/helpers/gmaps_helper_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper.rb' + +describe GmapsHelper do + describe '.gmaps_api_key' do + let(:key) { Faker::Lorem.sentence.gsub /\s/, '_' } + + context 'API key is a string' do + it 'should return the string' do + stub_const 'GMAPS_API_KEY', key + helper.gmaps_api_key.should == key + end + end + + context 'API key is a hash' do + it 'should use the hostname as a key into the hash' do + hostname = "#{Faker::Internet.domain_name}:#{rand 10000}" + controller.stub_chain(:request, :host_with_port).and_return hostname + stub_const 'GMAPS_API_KEY', {hostname => key, 'other-host.com' => 'some_other_key'} + + helper.gmaps_api_key.should == key + end + end + end +end \ No newline at end of file diff --git a/spec/lib/acts/geocoded_spec.rb b/spec/lib/acts/geocoded_spec.rb new file mode 100644 index 00000000..8fa8ca93 --- /dev/null +++ b/spec/lib/acts/geocoded_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' +require 'acts/geocoded' + +describe Acts::Geocoded do + include GeocoderHelpers + + describe '.acts_as_geocoded' do + let(:klass) { Class.new ActiveRecord::Base } + + it 'should include the module' do + klass.acts_as_geocoded + klass.should include Acts::Geocoded + end + + it 'should imply acts_as_addressed' do + klass.should_receive :acts_as_addressed + klass.acts_as_geocoded + end + + context 'setting coords' do + let(:address) { Faker::Lorem.sentence } + let(:rgeo) { RGeo::Geographic::Factory.new('Spherical') } + let(:model) do + klass.stub connection: nil, columns: [], transaction: true, rgeo_factory_for_column: rgeo + klass.acts_as_geocoded + klass.new + end + + before(:each) do + model.stub address_for_geocoding: address + end + + it "should geocode based on the host's address" do + coords = {'latitude' => 1.0, 'longitude' => 2.0} + geocoder_stub address => [coords] do + point = double 'Point' + rgeo.should_receive(:point).with(2.0, 1.0).and_return point + model.should_receive(:coords=).with(point).and_return true + model.geocode + end + end + + it "should not set coords if the geocoder doesn't return anything" do + geocoder_stub address => [] do + model.should_not_receive :coords= + model.geocode + end + end + end + end +end \ No newline at end of file diff --git a/spec/models/calendar_spec.rb b/spec/models/calendar_spec.rb index 0850d545..6388f3ce 100644 --- a/spec/models/calendar_spec.rb +++ b/spec/models/calendar_spec.rb @@ -31,33 +31,33 @@ describe Calendar, '(validations)' do before(:each) do - @calendar = Factory.build :calendar + @calendar = FactoryGirl.build :calendar end - + it 'should require a name' do @calendar.should be_valid @calendar.name = nil # @calendar.should_not be_valid end - + it 'should set its creator as admin' do - @admin = Factory :admin_role + @admin = FactoryGirl.create :admin_role Role.should_receive(:find_by_name).with('admin').and_return(@admin) - @user = Factory :user + @user = FactoryGirl.create :user User.stub!(:current_user).and_return(@user) @calendar.save! @calendar.permissions(true).where(:user_id => @user, :role_id => @admin).count.should >= 1 end - + it 'should not set admin when no one is logged in' do [false, :false, nil].each do |v| User.stub!(:current_user).and_return(v) - @calendar = Factory.build :calendar + @calendar = FactoryGirl.build :calendar @calendar.permissions.should_not_receive(:create) @calendar.save! end end - + it 'should not create permissions when the calendar is invalid' do @calendar.name = nil @calendar.permissions.should_not_receive(:create) diff --git a/spec/models/commitment_spec.rb b/spec/models/commitment_spec.rb index 2a9ee4f5..fc7c3093 100644 --- a/spec/models/commitment_spec.rb +++ b/spec/models/commitment_spec.rb @@ -6,42 +6,26 @@ it "should belong to an Event" do Commitment.reflect_on_association(:event).macro.should == :belongs_to end - + it "should belong to a User" do Commitment.reflect_on_association(:user).macro.should == :belongs_to end end -describe Commitment, "(named scopes)" do - # TODO: implement have_named_scope as described at http://evolve.st/articles/11-testing-named-scope-with-rspec - - it "should have a named scope for attending" do - Commitment.should respond_to(:attending) - Commitment.attending.where_values_hash.should == {:status => true} - end - - it "should have a named scope for not attending" do - Commitment.should respond_to(:not_attending) - Commitment.not_attending.where_values_hash.should == {:status => false} - end -end - describe Commitment, "(validations)" do - before(:each) do - @commitment = Commitment.new - @commitment.event_id = 1 # arbitrary - @commitment.user_id = 5 # arbitrary - end + let(:commitment) { FactoryGirl.create :commitment, event: FactoryGirl.create(:event), user: FactoryGirl.create(:user) } + + before(:each) { User.delete_all } # TODO: why is this necessary? it "should not be valid without an event" do - @commitment.should be_valid - @commitment.event_id = nil - @commitment.should_not be_valid + commitment.should be_valid + commitment.event_id = nil + commitment.should_not be_valid end it "should not be valid without a user" do - @commitment.should be_valid - @commitment.user_id = nil - @commitment.should_not be_valid + commitment.should be_valid + commitment.user_id = nil + commitment.should_not be_valid end end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 6c87cfc1..41c62f59 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -3,20 +3,17 @@ require 'spec_helper' describe Event, "(general properties)" do - before(:each) do - end - it "should act_as_addressed" do Event.included_modules.should include(Acts::Addressed::InstanceMethods) end - + it "should belong to a State" do r = Event.reflect_on_association(:state_raw) r.macro.should == :belongs_to r.options[:class_name].should == 'Acts::Addressed::State' r.options[:foreign_key].should == 'state_id' end - + it "should belong to a Country" do pending ":include doesn't seem to be a good idea at all -- must investigate" do opts = Event.reflect_on_association(:state).options @@ -24,37 +21,37 @@ opts[:include].should == :country end end - + it "should belong to a Calendar" do Event.reflect_on_association(:calendar).macro.should == :belongs_to end - + it "should have many Commitments" do Event.reflect_on_association(:commitments).macro.should == :has_many end - + it "should have many Users through Commitments" do reflection = Event.reflect_on_association(:users) reflection.macro.should == :has_many reflection.options.should have_key(:through) reflection.options[:through].should == :commitments end - + it "should belong to a User through created_by_id" do reflection = Event.reflect_on_association(:created_by) reflection.macro.should == :belongs_to reflection.options.should have_key(:class_name) reflection.options[:class_name].should == "User" end - + it "should have a country property referred through state" do - event = Factory :event, :state => Factory(:state) + event = FactoryGirl.create :event, state: FactoryGirl.create(:state) event.state.should_not be_nil event.country.should == event.state.country end - + it "should be nil-safe on country" do - event = Event.new(:state => nil) + event = Event.new(state: nil) lambda{event.country}.should_not raise_error end @@ -63,20 +60,20 @@ aggr.should_not be_nil aggr.options[:mapping].should == [%w(street street), %w(street2 street2), %w(city city), %w(state_id state), %w(zip zip), %w(coords coords)] state = FactoryGirl.create :state - opts = {:street => '123 Main Street', :street2 => '1st floor', :city => 'Anytown', :zip => 12345, :state => state} + opts = {street: '123 Main Street', street2: '1st floor', city: 'Anytown', zip: 12345, state: state} e = Event.new(opts) e.address.should == Acts::Addressed::Address.new(opts) end - + it "should have a deleted property" do event = Event.new event.should respond_to(:deleted) end - + it "should exclude deleted events on find" do undeleted = FactoryGirl.create :event begin - deleted = FactoryGirl.create :event, :deleted => true + deleted = FactoryGirl.create :event, deleted: true rescue ActiveRecord::RecordNotFound # don't worry about it -- since default_scope excludes this record, it won't be found. end @@ -84,7 +81,7 @@ all.should include(undeleted) all.should_not include(deleted) end - + it "should have a description" do event = Event.new event.should respond_to(:description) @@ -92,166 +89,188 @@ end describe Event, "(allow?)" do - before(:each) do - @event = FactoryGirl.create :event - @alien = FactoryGirl.create :user, :permissions => [Factory(:permission)] - @nonadmin = FactoryGirl.create :user, :permissions => [Factory(:permission, :calendar => @event.calendar)] - @admin = FactoryGirl.create(:user).tap do |u| - u.permissions << Factory(:admin_permission, :calendar => @event.calendar, :user => u) + let!(:event) { FactoryGirl.create :event } + let(:admin) do + FactoryGirl.create(:user).tap do |u| + u.permissions << FactoryGirl.create(:admin_permission, calendar: event.calendar, user: u) end end - + it "should exist with one argument" do - @event.should respond_to(:allow?) - @event.method(:allow?).arity.should == 1 + event.should respond_to(:allow?) + event.method(:allow?).arity.should == 1 end - + it "should return true for :delete iff current user has a role of admin for the event's calendar, false otherwise" do - User.stub!(:current_user).and_return(@alien) - @event.allow?(:delete).should == false - User.stub!(:current_user).and_return(@admin) - @event.allow?(:delete).should == true + alien = FactoryGirl.create :user, permissions: [FactoryGirl.create(:permission)] + User.stub current_user: alien + event.allow?(:delete).should == false + User.stub current_user: admin + event.allow?(:delete).should == true end - + it "should return true for :edit iff current user has a role of admin for the event's calendar or created the event" do - @event.created_by = @nonadmin - User.stub!(:current_user).and_return(@nonadmin) - @event.allow?(:edit).should == true - User.stub!(:current_user).and_return(@admin) - @event.allow?(:edit).should == true + nonadmin = FactoryGirl.create :user, permissions: [FactoryGirl.create(:permission, calendar: event.calendar)] + event.created_by = nonadmin + User.stub current_user: nonadmin + event.allow?(:edit).should == true + User.stub current_user: admin + event.allow?(:edit).should == true end - + it "should return true for :show iff current user has any role for the event's calendar" do - user = Factory(:user).tap do |u| - u.permissions << Factory(:permission, :user => u, :calendar => @event.calendar, :role => Factory(:role, :name => Faker::Lorem.word)) + user = FactoryGirl.create(:user).tap do |u| + u.permissions << FactoryGirl.create(:permission, user: u, calendar: event.calendar, role: FactoryGirl.create(:role, name: Faker::Lorem.word)) end - User.stub!(:current_user).and_return user - @event.allow?(:show).should == true + User.stub current_user: user + event.allow?(:show).should == true - User.stub!(:current_user).and_return Factory(:user, :permissions => [Factory(:permission, :role => Factory(:role, :name => Faker::Lorem.word))]) - @event.allow?(:show).should == false + User.stub current_user: FactoryGirl.create(:user, permissions: [FactoryGirl.create(:permission, role: FactoryGirl.create(:role, name: Faker::Lorem.word))]) + event.allow?(:show).should == false end - + it "should return nil for any operation if current user is not a User object" do - User.stub!(:current_user).and_return('bogus value') - @event.allow?(:edit).should be_nil + User.stub current_user: 'bogus value' + event.allow?(:edit).should be_nil end - + it "should return nil for any operation it doesn't know about" do - User.stub!(:current_user).and_return(@admin) - - @event.allow?(:foobar).should be_nil + User.stub current_user: admin + event.allow?(:foobar).should be_nil end end describe Event, "(change_status!)" do - before(:each) do - @event = Factory :event - @user = Factory :user - end - + let(:event) { FactoryGirl.create :event } + let(:user) { FactoryGirl.create :user } + it "should be valid" do - @event.should respond_to(:change_status!) + event.should respond_to(:change_status!) end - + it "should change the status on the already existing commitment if one exists" do - commitment = Factory :commitment, :event => @event, :user => @user, :status => true + commitment = FactoryGirl.create :commitment, event: event, user: user, status: true id = commitment.id [false, nil, true].each do |status| - @event.change_status!(@user, status) + event.change_status!(user, status) commitment = Commitment.find(id) commitment.status.should == status end end - + it "should create a new commitment if there isn't one" do - @event.commitments.find_all_by_user_id(@user.id).should be_empty - @event.change_status!(@user, nil) # somewhat arbitrary choice of status - @event.commitments.find_all_by_user_id(@user.id).should_not be_empty + event.commitments.find_all_by_user_id(user.id).should be_empty + event.change_status!(user, nil) # somewhat arbitrary choice of status + event.commitments.find_all_by_user_id(user.id).should_not be_empty + end + + it "should add a comment to the commitment if one is supplied" do + comment = Faker::Lorem.sentence + event.change_status! user, true, comment + + commitment = event.commitments.find_by_user_id(user) + commitment.comment.should == comment end end -describe Event, "(find_committed)" do - before(:each) do - @event = Factory :event - @find = @event.method(:find_committed) +describe Event, '#comments' do + let(:event) { FactoryGirl.create :event } + let(:comment_names) { event.comments.collect {|comment| comment.user.lastname } } + + before(:each) { User.delete_all } # TODO: why do we need this? + + it "should order comments by user's last name" do + last_names = ['Z', 'X', 'Y'] + last_names.each do |last_name| + FactoryGirl.create :commitment, event: event, user: FactoryGirl.create(:user, lastname: last_name) + end + + comment_names.should == last_names.sort end - + + it "should not return blank comments" do + { + 'Nil' => nil, 'Whitespace' => " \t\n ", 'Nonblank' => Faker::Lorem.sentence + }.each do |last_name, comment| + event.commitments.create! user: FactoryGirl.create(:user, lastname: last_name), comment: comment + end + + comment_names.should == ['Nonblank'] + end +end + +describe Event, "#find_committed" do + let(:event) { FactoryGirl.create :event } + let(:event_with_commitments) { Event.includes(commitments: :user).find(event.id) } + + it "should exist with one argument" do - @event.should respond_to(:find_committed) - @find.arity.should == 1 + event.should respond_to(:find_committed) + event.method(:find_committed).arity.should == 1 end - + it "should get a collection of Users when called with :yes or :no" do - @attending = Factory(:user).tap do |u| - u.commitments << Factory(:commitment, :user => u, :event => @event, :status => true) + @attending = FactoryGirl.create(:user).tap do |u| + u.commitments << FactoryGirl.create(:commitment, user: u, event: event, status: true) end - @not_attending = Factory(:user).tap do |u| - u.commitments << Factory(:commitment, :user => u, :event => @event, :status => false) + @not_attending = FactoryGirl.create(:user).tap do |u| + u.commitments << FactoryGirl.create(:commitment, user: u, event: event, status: false) end - @find[:yes].should == [@attending] - @find[:no].should == [@not_attending] - end - - it 'should sort the Users on lastname or, failing that, email' do - [true, false].each do |status| - a = Factory :user, :lastname => 'a' - b = Factory :user, :email => 'b@b.com', :lastname => nil - c = Factory :user, :lastname => 'c' - users = [c, a, b] - users.each do |u| - u.commitments << Factory(:commitment, :user => u, :event => @event, :status => status) - end - - @find[status ? :yes : :no].should == [a, b, c] - - users.each do |u| - u.destroy - end + event_with_commitments.find_committed(:yes).should == [@attending] + event_with_commitments.find_committed(:no).should == [@not_attending] + end + + it 'should sort the Users on name or, failing that, email' do + a = FactoryGirl.create :user, lastname: 'a' + b = FactoryGirl.create :user, email: 'b@b.com', lastname: nil, firstname: nil + c = FactoryGirl.create :user, lastname: nil, firstname: 'c' + users = [c, a, b] + users.each do |u| + u.commitments << FactoryGirl.create(:commitment, user: u, event: event, status: true) end + + event_with_commitments.find_committed(:yes).should == [a, b, c] + end + + it 'should not make a database query' do + event_with_commitments # get the necessary query out of the way + Commitment.connection.should_not_receive(:execute).with(/\A((?!rollback).)*\Z/i) # we should have no queries except rollbacks -- see http://stackoverflow.com/questions/957379/invert-match-with-regexp + event_with_commitments.find_committed :yes end end describe Event, "(hide)" do - before(:each) do - @event = Event.new - end - + before(:each) { User.delete_all } # TODO: why do we need this? + it "should set deleted to true" do - @event.deleted.should_not == true - @event.hide - @event.deleted.should == true + event = FactoryGirl.build :event + event.deleted.should_not == true + event.hide + event.deleted.should == true end end -describe Event, "(validations)" do - before(:each) do - @event = Event.new - @event.state_id = 23 # arbitrary; should be able to use any value - @event.city = "x" # arbitrary value - @event.created_by_id = 34 # arbitrary - @event.name = "y" # arbitrary - @event.calendar_id = 'abc' # arbitrary - end - +describe Event, "(validations)" do + let(:event) { FactoryGirl.build :event } + it "should not be valid without a state" do - @event.should be_valid - @event.state_id = nil - @event.should_not be_valid + event.should be_valid + event.state_id = nil + event.should_not be_valid end - + it "should not be valid without a name" do - @event.should be_valid - @event.name = nil - @event.should_not be_valid + event.should be_valid + event.name = nil + event.should_not be_valid end - + it "should not be valid without a calendar" do - @event.should be_valid - @event.calendar_id = nil - @event.should_not be_valid + event.should be_valid + event.calendar_id = nil + event.should_not be_valid end - + =begin it "should not be valid without a city" do @event.should be_valid @@ -261,61 +280,84 @@ =end it "should assign current_user to created_by" do - user = Factory :user + user = FactoryGirl.create :user User.stub!(:current_user).and_return user - @event.created_by_id = nil - @event.save! - @event.created_by.should == user + event.created_by_id = nil + event.save! + event.created_by.should == user end - + it "should not try to set created_by if there's no current user" do [false, :false].each do |v| User.stub!(:current_user).and_return(v) - @event.created_by_id = nil - @event.should_not_receive(:created_by=) - @event.save! + event.created_by_id = nil + event.should_not_receive(:created_by=) + event.save! end end end describe Event, "(geographical features)" do - before(:each) do - @placemark = Geocoding::Placemark.new - @placemark.stub!(:latlon).and_return([1.0, 2.0]) - Geocoding::Placemark.stub!(:new).and_return(@placemark) - - # TODO: Use Webmock here. - @placemarks = Geocoding::Placemarks.new('Test Placemarks', Geocoding::GEO_SUCCESS) - @placemarks.stub!(:[]).and_return(@placemark) - Geocoding::Placemarks.stub!(:new).and_return(@placemarks) - Geocoding.stub!(:get).and_return(@placemarks) - - @event = Factory.build :event - end - - it "should have coords (Point)" do - @event.should respond_to(:coords) - @event.coords.should_not be_nil - @event.coords.should be_a_kind_of(Point) - end - - it "should save coords when successfully encoded" do - @event.should_receive(:save).once - @event.coords - end - - it "should not save coords when unsuccessfully encoded" do - Geocoding.should_receive(:get).and_return(false) - @event.should_not_receive(:save) - @event.coords - end - - it "should clear coords on update" do - User.stub!(:current_user).and_return(Factory :user) - @event.update_attributes(Factory.attributes_for :event) - @event.should_receive(:coords=) - @event.update_attributes(:name => 'foo') - # @event.should_not_receive(:coords=) - # @event.update_attributes(nil) + let(:event) { FactoryGirl.create :event } + + before(:each) { User.delete_all } # TODO: why do we need this? + + it "should have coords" do + event.should respond_to(:coords) + event.coords.should_not be_nil + event.coords.should be_a_kind_of RGeo::Geographic::SphericalPointImpl + end + + it "should reset coords on update" do + User.stub!(:current_user).and_return(FactoryGirl.create :user) + event.update_attributes(FactoryGirl.attributes_for :event) + event.should_receive(:coords=) + event.update_attributes(name: 'foo') + end +end + +describe Event, 'latitude and longitude' do # TODO: merge into geographical features context + include GeocoderHelpers + + let(:event) { User.delete_all; FactoryGirl.build :event } # TODO: Why isn't it already clearing users? + let(:address) { event.address.to_s :geo } + + around(:each) do |example| + geocoder_stub address => coordinates do + event.save + example.run + end + end + + describe 'event has coordinates' do + let(:latitude) { (rand * 180) - 90 } + let(:longitude) { (rand * 360) - 180 } + let(:coordinates) { [{'latitude' => latitude, 'longitude' => longitude}] } + + describe '#latitude' do + it 'should return the latitude coordinate' do + event.latitude.should == latitude + end + end + + describe '#longitude' do + it 'should return the latitude coordinate' do + event.longitude.should == longitude + end + end + end + + describe 'event has no coordinates' do + let(:coordinates) { [] } + + describe '#latitude' do + subject { event.latitude } + it { should be_nil } + end + + describe '#longitude' do + subject { event.longitude } + it { should be_nil } + end end end \ No newline at end of file diff --git a/spec/models/permission_spec.rb b/spec/models/permission_spec.rb index 34a8c3c0..8d42521e 100644 --- a/spec/models/permission_spec.rb +++ b/spec/models/permission_spec.rb @@ -6,7 +6,7 @@ it "should belong to a User" do Permission.reflect_on_association(:user).macro.should == :belongs_to end - + it "should belong to a Calendar" do Permission.reflect_on_association(:calendar).macro.should == :belongs_to end @@ -19,30 +19,30 @@ describe Permission, "(validations)" do context 'field validations' do before(:each) do - @permission = Factory :permission + @permission = FactoryGirl.create :permission end - + it "should not be valid without a calendar" do @permission.should be_valid @permission.calendar_id = nil @permission.should_not be_valid end - + it "should not be valid without a user" do @permission.should be_valid @permission.user_id = nil @permission.should_not be_valid end - + it "should not be valid without a role" do @permission.should be_valid @permission.role_id = nil @permission.should_not be_valid end end - + it "should be unique across all three attributes" do - opts = Factory.build(:permission).attributes # can't use attributes_for since we need the associations + opts = FactoryGirl.build(:permission).attributes # can't use attributes_for since we need the associations @one = Permission.new opts @one.should be_valid @one.save! diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index acba27eb..498cefd8 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -5,66 +5,66 @@ describe User, "(general properties)" do before(:each) do end - + it "should act_as_addressed" do User.included_modules.should include(Acts::Addressed::InstanceMethods) end - + it "should belong to a State" do r = User.reflect_on_association(:state_raw) r.macro.should == :belongs_to r.options[:class_name].should == 'Acts::Addressed::State' r.options[:foreign_key].should == 'state_id' end - + it "should have many Commitments" do User.reflect_on_association(:commitments).macro.should == :has_many end - + it "should have many Events through Commitments" do reflection = User.reflect_on_association(:events) reflection.macro.should == :has_many reflection.options.should have_key(:through) reflection.options[:through].should == :commitments end - + it "should have many Permissions" do User.reflect_on_association(:permissions).macro.should == :has_many end - + it "should have many Calendars through Permissions" do reflection = User.reflect_on_association(:calendars) reflection.macro.should == :has_many reflection.options.should have_key(:through) reflection.options[:through].should == :permissions end - + it "should be composed_of an Address" do aggr = User.reflect_on_aggregation(:address) aggr.should_not be_nil aggr.options[:mapping].should == [%w(street street), %w(street2 street2), %w(city city), %w(state_id state), %w(zip zip), %w(coords coords)] - state = Factory :state + state = FactoryGirl.create :state opts = {:street => '123 Main Street', :street2 => '1st floor', :city => 'Anytown', :zip => 12345, :state => state} - u = Factory :user, opts + u = FactoryGirl.create :user, opts u.address.should == Acts::Addressed::Address.new(opts) end - + it "should have a writable flag controlling display of personal information on contact list" do u = User.new u.should respond_to(:show_contact) u.should respond_to(:show_contact=) end - + it "should have country referred through state" do - state = Factory :state + state = FactoryGirl.create :state user = User.new user.should respond_to(:country) user.state_raw = state user.country.should == state.country end - + it "should be nil-safe on country" do - user = Factory :user, :state => nil + user = FactoryGirl.create :user, :state => nil lambda{user.country}.should_not raise_error end end @@ -75,7 +75,7 @@ @user = mock_model(Role, :name => 'user') @two = mock_model(Permission, :role => @admin, :calendar => mock_model(Calendar, :id => 2, :name => 'Calendar 2')) end - + it "should return false if user has no admin roles" do @permissions = [mock_model(Permission, :role => @user, :calendar => mock_model(Calendar, :id => 1, :name => 'Calendar 1')), mock_model(Permission, :role => @user, :calendar => mock_model(Calendar, :id => 2, :name => 'Calendar 2'))] @permissions.stub!(:find_by_role_id).and_return(nil) @@ -83,7 +83,7 @@ u.permissions << @permissions u.admin?.should == false end - + it "should return true if user has at least one admin role" do @permissions = [mock_model(Permission, :role => @user, :calendar => mock_model(Calendar, :id => 1, :name => 'Calendar 1')), @two] u = User.new @@ -105,26 +105,26 @@ it 'should create a user permission for the calendar, when there\'s only one calendar' do Calendar.destroy_all - User.stub!(:current_user).and_return(Factory :user) - calendar = Factory :calendar + User.stub!(:current_user).and_return(FactoryGirl.create :user) + calendar = FactoryGirl.create :calendar Calendar.count.should == 1 - user = User.create!(Factory.attributes_for :user) + user = User.create!(FactoryGirl.attributes_for :user) user.permissions.should_not be_nil user.permissions.should_not be_empty user.permissions[0].user.should == user user.permissions[0].calendar.should == calendar user.permissions[0].role.name.should == 'user' end - + context 'field validations' do before :each do - @user = Factory.build :user + @user = FactoryGirl.build :user end - + it 'should be valid with default data' do @user.should be_valid end - + ['password_confirmation', 'email'].each do |field| # TODO: should work for password too, but it doesn't it "requires #{field.gsub '_', ' '}" do @user.send "#{field}=", '' @@ -141,14 +141,14 @@ end =end end - + describe User, "(instance methods)" do describe "<=>" do it "should be valid" do User.new.should respond_to(:<=>) User.method(:<=>).arity.should == 1 end - + it "should sort on last name, first name, and e-mail address in that order" do attrs = ['Smith', 'John', 'jsmith1@aol.com'] smith = u(attrs) @@ -158,13 +158,13 @@ (smith <=> u([nil, nil, 'Smitty@aol.com'])).should == -1 # nil-safe (smith <=> u(attrs.collect(&:downcase))).should == 0 # not case-sensitive end - + protected def u(array) User.new(:lastname => array[0], :firstname => array[1], :email => array[2]) end end - + describe "to_s" do it "should return firstname or lastname if only one of these is defined, 'firstname lastname' if both are defined, or e-mail address if no name is defined" do @user = User.new @@ -178,86 +178,86 @@ def u(array) @user.firstname = 'f' @user.to_s.should == @user.firstname << ' ' << @user.lastname end - + it "should take an optional parameter, :first_last or :last_first" do @user = User.new lambda{@user.to_s}.should_not raise_error lambda{@user.to_s :first_last}.should_not raise_error lambda{@user.to_s :last_first}.should_not raise_error end - + describe nil do before(:each) do @user = User.new(:email => 'foo@bar.com', :firstname => 'f', :lastname => 'l') end - + it "should return lastname, firstname if :last_first is specified" do @user.to_s(:last_first).should == 'l, f' end - + it "should default to :first_last if no order is specified" do @user.to_s.should == @user.to_s(:first_last) end - + it "should raise an error if format is unrecognized" do lambda{@user.to_s :bogus}.should raise_error end end end - + describe "activate" do it "should be valid" do - Factory(:user).should respond_to(:activate) + FactoryGirl.create(:user).should respond_to(:activate) end - + it "should set the active flag to true" do - u = Factory :inactive_user + u = FactoryGirl.create :inactive_user u.activate u.active?.should be_true end end - + it "should have a 'single_access_token' property initialized to a string" do - Factory(:user).single_access_token.should_not be_blank + FactoryGirl.create(:user).single_access_token.should_not be_blank end - + it "should set single_access_token on save" do - @u = Factory :user + @u = FactoryGirl.create :user @u.single_access_token = nil @u.reload.single_access_token.should_not be_blank end - + it "should not overwrite single_access_token if already set" do - @u = Factory :user + @u = FactoryGirl.create :user token = @u.single_access_token @u.reload.single_access_token.should == token end - + it "should properly deal with regenerating single_access_token if it's a duplicate" do - @one = Factory :user - @two = Factory :user + @one = FactoryGirl.create :user + @two = FactoryGirl.create :user token = @two.single_access_token @one.single_access_token = token # TODO: Does this properly test what's being asserted here? @one.reload.single_access_token.should_not == token end - + describe "reset_password!" do it "should be a valid instance method" do User.new.should respond_to(:reset_password!) end - + it "should reset the user's password and password_confirmation to identical strings" do old_password = 'old password' - user = Factory :user, :password => old_password + user = FactoryGirl.create :user, :password => old_password lambda {user.reset_password!}.should_not raise_error(ActiveRecord::RecordInvalid) # should set password_confirmation new_password = user.password new_password.should_not == old_password end - + it "should reset password to a random hex string of length 10 (MD5 digest or similar)" do pattern = /^[a-f\d]{10}$/ - user = Factory :user + user = FactoryGirl.create :user user.reset_password! password1 = user.password password1.should =~ pattern @@ -271,38 +271,18 @@ def u(array) describe User, "(geographical features)" do before(:each) do - @placemark = Geocoding::Placemark.new - @placemark.stub!(:latlon).and_return([1.0, 2.0]) - Geocoding::Placemark.stub!(:new).and_return(@placemark) - - @placemarks = Geocoding::Placemarks.new('Test Placemarks', Geocoding::GEO_SUCCESS) - @placemarks.stub!(:[]).and_return(@placemark) - Geocoding::Placemarks.stub!(:new).and_return(@placemarks) - Geocoding.stub!(:get).and_return(@placemarks) - - @user = Factory.build :user - end - - it "should save coords when successfully encoded" do - @user.should_receive(:save).once - @user.coords + @user = FactoryGirl.create :user end - it "should have coords (Point)" do + it "should have coords" do @user.should respond_to(:coords) @user.coords.should_not be_nil - @user.coords.should be_a_kind_of(Point) + @user.coords.should be_a_kind_of RGeo::Geographic::SphericalPointImpl end - - it "should not save coords when unsuccessfully encoded" do - Geocoding.should_receive(:get).and_return(false) - @user.should_not_receive(:save) - @user.coords - end - - it "should clear coords on update" do - User.stub!(:current_user).and_return(Factory :user) - @user.update_attributes(Factory.attributes_for :user) + + it "should reset coords on update" do + User.stub!(:current_user).and_return(FactoryGirl.create :user) + @user.update_attributes(FactoryGirl.attributes_for :user) @user.should_receive(:coords=) @user.update_attributes(:name => 'foo') end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8cf6efc8..09091278 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,23 +1,21 @@ -# coding: UTF-8 - # This file is copied to spec/ when you run 'rails generate rspec:install' ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' +require 'rspec/autorun' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} RSpec.configure do |config| - # == Mock Framework + # ## Mock Framework # # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: # # config.mock_with :mocha # config.mock_with :flexmock # config.mock_with :rr - config.mock_with :rspec # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" @@ -26,4 +24,15 @@ # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true -end \ No newline at end of file + + # If true, the base class of anonymous controllers will be inferred + # automatically. This will be the default behavior in future versions of + # rspec-rails. + config.infer_base_class_for_anonymous_controllers = false + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = "random" +end diff --git a/spec/support/geocoder.rb b/spec/support/geocoder.rb new file mode 100644 index 00000000..50d07dc9 --- /dev/null +++ b/spec/support/geocoder.rb @@ -0,0 +1,8 @@ +Geocoder.configure lookup: :test + +Geocoder::Lookup::Test.set_default_stub [ + { + 'latitude' => 1.0, + 'longitude' => 2.0 + } +] \ No newline at end of file diff --git a/spec/support/geocoder_helpers.rb b/spec/support/geocoder_helpers.rb new file mode 100644 index 00000000..eb446cc6 --- /dev/null +++ b/spec/support/geocoder_helpers.rb @@ -0,0 +1,20 @@ +module GeocoderHelpers + def geocoder_stub(stubs, &block) + begin + geocoder = Geocoder::Lookup::Test + old_stubs = geocoder.stubs.dup + + stubs.each do |address, results| + geocoder.add_stub address, results + end + yield + ensure + stubs.each do |address, _| + geocoder.stubs.delete address + end + old_stubs.each do |address, results| + geocoder.add_stub address, results + end + end + end +end \ No newline at end of file diff --git a/spec/views/calendars/users.html.haml_spec.rb b/spec/views/calendars/users.html.haml_spec.rb index 9919dfd2..e358fafd 100644 --- a/spec/views/calendars/users.html.haml_spec.rb +++ b/spec/views/calendars/users.html.haml_spec.rb @@ -6,24 +6,24 @@ describe "/calendars/users" do before(:each) do - @calendar = Factory :calendar - + @calendar = FactoryGirl.create :calendar + Role.destroy_all - @admin_role = Factory :admin_role - @user_role = Factory :role - - @admin = Factory.attributes_for :permission, :calendar => @calendar, :role => @admin_role, :user => nil - @user = Factory.attributes_for :permission, :calendar => @calendar, :role => @user_role, :user => nil - - @marnen = Factory(:user, :show_contact => true).tap do |u| + @admin_role = FactoryGirl.create :admin_role + @user_role = FactoryGirl.create :role + + @admin = FactoryGirl.build(:permission, :calendar => @calendar, :role => @admin_role, :user => nil).attributes + @user = FactoryGirl.build(:permission, :calendar => @calendar, :role => @user_role, :user => nil).attributes + + @marnen = FactoryGirl.create(:user, :show_contact => true).tap do |u| u.permissions.destroy_all u.permissions.create!(@admin.merge :user => u) end - @millie = Factory(:user, :show_contact => true).tap do |u| + @millie = FactoryGirl.create(:user, :show_contact => true).tap do |u| u.permissions.destroy_all u.permissions.create!(@user.merge :user => u) end - @quentin = Factory(:user, :show_contact => false).tap do |u| + @quentin = FactoryGirl.create(:user, :show_contact => false).tap do |u| u.permissions.destroy_all u.permissions.create!(@user.merge :user => u) end @@ -34,18 +34,18 @@ assign :current_object, @calendar render :file => 'calendars/users' end - + it "should show the results in a table" do rendered.should have_selector("table.users") end - + it "should show first and last names for each user" do for u in @users rendered.should have_selector("tr#user_#{u.id} td._name", :content => u.firstname) rendered.should have_selector("tr#user_#{u.id} td._name", :content => u.lastname) end end - + it "should show street and e-mail addresses for each user who has not requested to be hidden" do @users.each do |u| Capybara.string(rendered).find("tr#user_#{u.id}").tap do |row| @@ -62,7 +62,7 @@ end end end - + it "should show each user's role in this calendar, and -- except for the current user -- should allow it to be changed" do for u in @users Capybara.string(rendered).find("tr#user_#{u.id}").tap do |row| @@ -75,7 +75,7 @@ end end end - + it "should show whether each user is visible on commitment reports" do for u in @users rendered.should have_selector("tr#user_#{u.id} td._show input[type=checkbox]") diff --git a/spec/views/events/_attendance.html.haml_spec.rb b/spec/views/events/_attendance.html.haml_spec.rb index 64a4604b..dccb0f52 100644 --- a/spec/views/events/_attendance.html.haml_spec.rb +++ b/spec/views/events/_attendance.html.haml_spec.rb @@ -8,13 +8,14 @@ describe 'events/_attendance' do before(:each) do - render :partial => 'events/attendance', :locals => {:event => Factory(:event)} + User.delete_all # TODO: why is this necessary? + render :partial => 'events/attendance', :locals => {:event => FactoryGirl.create(:event)} end - + it "should have a select element for event" do rendered.should have_selector("select.commit") end - + it "should have an empty element for the progress indicator" do rendered.should have_selector(".progress", :content => / /) end diff --git a/spec/views/events/_event.html.haml_spec.rb b/spec/views/events/_event.html.haml_spec.rb index 8bfafacc..a017fbb6 100644 --- a/spec/views/events/_event.html.haml_spec.rb +++ b/spec/views/events/_event.html.haml_spec.rb @@ -9,19 +9,20 @@ describe 'events/_event' do before(:each) do Role.destroy_all(:name => 'admin') - @event = Factory :event, :description => 'Testing use of *Markdown*.' - @user = Factory :user + User.destroy_all # TODO: why is this necessary? + @event = FactoryGirl.create :event, :description => 'Testing use of *Markdown*.' + @user = FactoryGirl.create :user [User, view].each {|x| x.stub!(:current_user).and_return @user } end - + it "should contain edit and delete links for the event, if the current user is an admin" do admin_role = Role.find_or_create_by_name 'admin' - admin = Factory(:user).tap do |u| + admin = FactoryGirl.create(:user).tap do |u| u.permissions.destroy_all - u.permissions << Factory(:permission, :role => admin_role, :calendar => @event.calendar, :user => u) + u.permissions << FactoryGirl.create(:permission, :role => admin_role, :calendar => @event.calendar, :user => u) end [User, view].each {|x| x.stub!(:current_user).and_return admin } - + render_view edit_url = url_for(:controller => 'events', :action => 'edit', :id => @event.id, :escape => false) delete_url = url_for(:controller => 'events', :action => 'delete', :id => @event.id, :escape => false) @@ -31,64 +32,64 @@ end it 'should contain calendar names for events, if the current user has more than one calendar' do - @one = Factory :calendar - @user.stub!(:calendars).and_return([@one, Factory(:calendar)]) + @one = FactoryGirl.create :calendar + @user.stub!(:calendars).and_return([@one, FactoryGirl.create(:calendar)]) @event.stub!(:calendar).and_return(@one) - + render_view rendered.should have_selector("#event_#{@event.id} .calendar", :content => @one) end - + it 'should not contain calendar names for events, if the current user has only one calendar' do - @one = Factory :calendar + @one = FactoryGirl.create :calendar @user.stub!(:calendars).and_return([@one]) @event.stub!(:calendar).and_return(@one) - + render_view rendered.should_not have_selector("#event_#{@event.id} .calendar") end - + it "should have a control to set the current user's attendance for the event" do pending "This works, but spec fails strangely." do template.should_receive(:render).with(:partial => 'attendance', :locals => {:event => @event}) render_view end end - + it "should contain a distance in miles or km for the event if it has good coords" do render_view if !@event.coords.nil? then rendered.should have_selector("#event_#{@event.id} .distance", :content => /\d (miles|km)/) end end - + it "should show the name of the event in a tag of class 'summary'" do render_view rendered.should have_selector("#event_#{@event.id} .summary", :content => @event.name) end - + it "should show the description of the event (formatted with Markdown) in a tag of class 'description'" do render_view selector = "#event_#{@event.id} .description" rendered.should have_selector(selector, :content => /Testing use of/) rendered.should have_selector("#{selector} em", :content => 'Markdown') end - + it "should show the site for the event" do render_view rendered.should have_selector("#event_#{@event.id}", :content => @event.site) - end + end it "should show the street address of the event in a tag of class 'street-address" do render_view rendered.should have_selector("#event_#{@event.id} .street-address", :content => /#{[h(@event.street), h(@event.street2)].join('.*')}/m) end - + it "should show the city of the event in a tag of class 'locality'" do render_view rendered.should have_selector("#event_#{@event.id} .locality", :content => @event.city) end - + it "should show the state code of the event in a tag of class 'region'" do render_view rendered.should have_selector("#event_#{@event.id} .region", :content => @event.state.code) @@ -98,35 +99,35 @@ render_view rendered.should have_selector("#event_#{@event.id} .country-name", :content => @event.country.code) end - + it "should show the zip code of each event in a tag of class 'postal-code'" do render_view rendered.should have_selector("#event_#{@event.id} .postal-code", :content => @event.zip) end - + it "should show a map link for the event" do render_view url = url_for(:controller => 'events', :action => 'map', :id => @event.id, :escape => false) rendered.should have_selector("#event_#{@event.id} a[href='#{url}']") end - + it "should show an iCal export link for the event, of class 'ical'" do render_view url = url_for(:controller => 'events', :action => 'export', :id => @event.id, :escape => false) rendered.should have_selector("#event_#{@event.id} a.ical[href='#{url}']") end - + it "should show a date for the event in RFC 822 format, wrapped in an <abbr> of class 'dtstart' with ical-style date as the title" do render_view date = @event.date rendered.should have_selector("#event_#{@event.id} abbr.dtstart[title='#{date.to_formatted_s(:ical)}']", :content => date.to_formatted_s(:rfc822)) end - + it "should have an element of class 'uid' for each event, containing an iCal unique ID for the event" do render_view rendered.should have_selector("#event_#{@event.id} .uid", :content => ical_uid(@event)) end - + it "should contain an edit link for each event that the current user created" do =begin How did this *ever* work? @@ -136,28 +137,28 @@ rendered.should have_selector("#event_#{event.id} a[href=" << url << "]") end =end - @user.permissions << Factory(:permission, :calendar => @event.calendar) + @user.permissions << FactoryGirl.create(:permission, :calendar => @event.calendar) @event.created_by = @user render_view url = url_for(:controller => 'events', :action => 'edit', :id => @event.id, :escape => false) rendered.should have_selector("#event_#{@event.id} a[href='#{url}']") end - + it "should not contain an edit link for events that the current (non-admin) user created" do - @user.permissions << Factory(:permission, :calendar => @event.calendar) - @event.created_by = Factory :user # some other guy + @user.permissions << FactoryGirl.create(:permission, :calendar => @event.calendar) + @event.created_by = FactoryGirl.create :user # some other guy render_view url = url_for(:controller => 'events', :action => 'edit', :id => @event.id, :escape => false) rendered.should_not have_selector("#event_#{@event.id} a[href='#{url}']") end - + it "should get a list of users attending and not attending for each event" do # TODO: figure out why each find_committed call is happening 3 times @event.should_receive(:find_committed).with(:yes).at_least(:once).and_return([]) @event.should_receive(:find_committed).with(:no).at_least(:once).and_return([]) render_view end - + it "should show the number of users attending and not attending each event" do pending "not sure how to spec this" end @@ -166,7 +167,7 @@ render_view rendered.should have_selector("form.attendance") end - + protected def render_view render :partial => 'events/event', :locals => {:event => @event} diff --git a/spec/views/events/ical.ics.erb_spec.rb b/spec/views/events/ical.ics.erb_spec.rb index 90cd395a..12c16dd4 100644 --- a/spec/views/events/ical.ics.erb_spec.rb +++ b/spec/views/events/ical.ics.erb_spec.rb @@ -5,28 +5,38 @@ describe "/events/ical.ics" do before(:each) do - @event = Factory :event + @event = FactoryGirl.create :event assign :event, @event render :file => 'events/ical.ics.erb' end - + + it "should use Windows line ends" do + rendered.should include("#{13.chr}#{10.chr}") + rendered.should_not =~ /[^#{13.chr}]#{10.chr}/ + end + + it "should countain a product identifier" do + rendered.should have_content("PRODID:-//quorum2.sf.net//#{SITE_TITLE} #{APP_VERSION}//EN") + end + it "should contain the iCal UID" do rendered.should have_content("UID:#{ical_uid @event}") end - + it "should contain the event name as a summary" do rendered.should have_content("SUMMARY:#{ical_escape @event.name}") end - + it "should contain the event address as location" do - rendered.should have_content("LOCATION:#{[@event.street, @event.street2, @event.city, @event.state.code, @event.country.code].compact.join(', ')}") + escaped_location = ical_escape [@event.street, @event.street2, @event.city, @event.state.code, @event.country.code].compact.join(', ') + rendered.should have_content("LOCATION:#{escaped_location}") end - + it "should contain the event description as description" do rendered.should have_content("DESCRIPTION:#{ical_escape @event.description}") end - + it "should contain the date in iCal format" do - rendered.should have_content("DTSTART;VALUE=DATE:#{@event.date.to_s :ical}") + rendered.should have_content("DTSTART;VALUE=DATE:#{@event.date.strftime '%Y%m%d'}") end end diff --git a/spec/views/events/index.html.haml_spec.rb b/spec/views/events/index.html.haml_spec.rb index ba9d6b46..bcb06c74 100644 --- a/spec/views/events/index.html.haml_spec.rb +++ b/spec/views/events/index.html.haml_spec.rb @@ -12,13 +12,13 @@ def name_selector(string) end before(:each) do - @events = (1..10).map { Factory :event } + @events = (1..10).map { FactoryGirl.create :event } assign :events, @events - @user = Factory :user + @user = FactoryGirl.create :user assign :current_user, @user [view, User].each {|x| x.stub!(:current_user).and_return @user } end - + it "should have a date limiting form" do render :file => 'events/index' form = "form[action='#{url_for(params.merge :escape => false)}'][method=get]" @@ -32,7 +32,7 @@ def name_selector(string) e.should have_selector("#{to_date_selector}[value='latest'][checked]") e.should have_selector("#{to_date_selector}[value='other']") e.should have_selector("input[type='submit']:not([name])") - + (1..3).each do |x| e.should have_selector("select#{name_selector "search[from_date(#{x}i)]"}") e.should have_selector("select#{name_selector "search[to_date(#{x}i)]"}") @@ -40,34 +40,34 @@ def name_selector(string) e.should_not have_selector("select#{name_selector "search[calendar_id]"}") end end - + it "should have a calendar option on the limiting form iff user has multiple calendars" do - User.current_user.stub!(:calendars).and_return((1..2).map{ Factory :calendar }) + User.current_user.stub!(:calendars).and_return((1..2).map{ FactoryGirl.create :calendar }) render :file => 'events/index' form = "form[action='#{url_for(params.merge :escape => false)}'][method=get]" rendered.should have_selector("#{form} select #{name_selector "search[calendar_id]"}") end - + it "should show a sort link in date and event column header" do render :file => 'events/index' rendered.should have_selector("th a.sort", :content => 'Date') rendered.should have_selector("th a.sort", :content => "Event") end - + it "should show a sort indicator next to headers that have been sorted by" do assign :order, 'name' assign :direction, 'desc' render :file => 'events/index' rendered.should =~ %r{Event\s?((<[^>]*>)?\s?)*↓} end - + it "should render _event for each event" do pending "Not sure how to make this work." do template.should_receive(:render).with(:partial => 'event', :collection => @events) render :file => 'events/index' end end - + it "should include events/index.js for JavaScript enhancements" do render :file => 'events/index' view.content_for(:javascript).should have_selector("script[src*='events/index.js']") @@ -77,27 +77,27 @@ def name_selector(string) render :file => 'events/index' view.content_for(:head).should have_selector("link[title='RSS'][href='#{rss_url}']") end - + it "should contain a link and a URL for the RSS feed" do render :file => 'events/index' rendered.should have_selector(".rss a[href='#{rss_url}']") rendered.should have_selector(".rss .url", :content => rss_url) end - + it "should contain a link to regenerate the RSS feed key" do render :file => 'events/index' rendered.should have_selector(".rss a[href='#{regenerate_key_path}']") end - + it "should contain a link for PDF export if the current events belong to exactly one calendar" do - @one = Factory :calendar + @one = FactoryGirl.create :calendar @events.each do |e| e.stub!(:calendar).and_return(@one) end render :file => "events/index" rendered.should have_selector("a[href='#{url_for(params.merge :format => :pdf, :escape => false)}']") end - + it "should not contain a link for PDF export if the current events belong to more than one calendar" do @one = mock_model(Calendar, :name => 'one', :id => 1) @two = mock_model(Calendar, :name => 'two', :id => 2) diff --git a/spec/views/events/new.html.haml_spec.rb b/spec/views/events/new.html.haml_spec.rb index a94d35ee..3a26ff25 100644 --- a/spec/views/events/new.html.haml_spec.rb +++ b/spec/views/events/new.html.haml_spec.rb @@ -4,35 +4,35 @@ describe "/events/new" do before(:each) do - user = Factory(:user).tap {|u| u.permissions << Factory(:permission, :user => u) } + user = FactoryGirl.create(:user).tap {|u| u.permissions << FactoryGirl.create(:permission, :user => u) } UserSession.create user [User, view].each {|x| x.stub(:current_user).and_return user } - assign :current_object, Factory(:event) + assign :event, FactoryGirl.create(:event) render :file => 'events/new' end - + it "should have a form" do rendered.should have_selector("form") end - + it "should have a table of class edit in the form" do rendered.should have_selector("form table.edit") end - + it "should have a name field" do rendered.should have_selector("table.edit input#event_name") end - + it "should have a description field" do rendered.should have_selector("table.edit textarea#event_description") end - + it "should have a Markdown hint in the description field" do header = assert_select("table.edit th", /Description/) header.should_not be_nil header.should have_selector("a", :content => 'Markdown') end - + it "should have a date selector" do rendered.should have_selector("table.edit select#event_date_2i") end @@ -40,50 +40,50 @@ it "should have a site field" do rendered.should have_selector("table.edit input#event_site") end - + it "should have two address fields" do rendered.should have_selector("table.edit input#event_street") rendered.should have_selector("table.edit input#event_street2") end - + it "should have a city field" do rendered.should have_selector("table.edit input#event_city") end - + it "should have a state field" do rendered.should have_selector("table.edit select#event_state_id") end - + it "should have a zip field" do rendered.should have_selector("table.edit input#event_zip") end - + it "should have a submit button" do rendered.should have_selector("form input[type=submit]") end - + end describe "/events/new (multiple calendars)" do before(:each) do - @one = Factory :calendar #, :id => 1, :name => 'Calendar 1' - @two = Factory :calendar #, :id => 2, :name => 'Calendar 2' - assign :current_object, Factory(:event, :date => Time.now, :calendar => @one) - @user = Factory :user, :permissions => [] + @one = FactoryGirl.create :calendar + @two = FactoryGirl.create :calendar + assign :event, FactoryGirl.create(:event, :date => Time.now, :calendar => @one) + @user = FactoryGirl.create :user, :permissions => [] UserSession.create @user [User, view].each {|x| x.stub(:current_user).and_return @user } end - + it "should display a calendar selector if current user has multiple calendars" do [@one, @two].each do |c| - @user.permissions << Factory(:permission, :user => @user, :calendar => c) + @user.permissions << FactoryGirl.create(:permission, :user => @user, :calendar => c) end render :file => '/events/new' rendered.should have_selector('select#event_calendar_id') end - + it "should not display a calendar selector if current user only has one calendar" do - @user.permissions << Factory(:permission, :user => @user, :calendar => @one) + @user.permissions << FactoryGirl.create(:permission, :user => @user, :calendar => @one) render :file => '/events/new' rendered.should_not have_selector('select#event_calendar_id') rendered.should have_selector("input[type=hidden][value='#{@one.id}']#event_calendar_id") diff --git a/spec/views/events/show.html.haml_spec.rb b/spec/views/events/show.html.haml_spec.rb index 6a5cfdef..f884cd8e 100644 --- a/spec/views/events/show.html.haml_spec.rb +++ b/spec/views/events/show.html.haml_spec.rb @@ -4,18 +4,18 @@ describe "/events/show" do before(:each) do - [User, view].each {|x| x.stub!(:current_user).and_return(Factory :user) } - @event = Factory :event - template.stub!(:current_object).and_return(@event) + [User, view].each {|x| x.stub!(:current_user).and_return(FactoryGirl.create :user) } + @event = FactoryGirl.create :event + assign :event, @event end - + it "should render the event and table_header partials" do pending "Not sure how to make this work, but it's probably a stupid spec anyway. :)" template.should_receive(:render).with(:partial => 'table_header', :locals => {:sortlinks => false}) template.should_receive(:render).with(:partial => 'event', :locals => {:event => @event}) render :file => '/events/show' end - + it "should wrap the whole thing in a <table> of class events" do render :file => '/events/show' rendered.should =~ %r{^\s*<\s*table\s+([^>]*\s*)class=(["'])events\2[^>]*>.*</table>\s*$}m diff --git a/vendor/plugins/make_resourceful/DEFAULTS b/vendor/plugins/make_resourceful/DEFAULTS deleted file mode 100644 index b77b38f4..00000000 --- a/vendor/plugins/make_resourceful/DEFAULTS +++ /dev/null @@ -1,148 +0,0 @@ -There's a rough equivalence between a basic @make_resourceful@ call -and a hand-made controller. -This: - - class PostsController < ApplicationController - make_resourceful { actions :all } - end - -Creates a controller that works more or less like the one that follows. -Note that the real code generated by make_resourceful -is more extensible in various ways. -Thus whenever possible, there are comments in the following controller -indicating how to customize various bits of the controller. - - class PostsController < ApplicationController - def index - # Override #current_objects to change this - @posts = Post.find(:all) - - # Use before :index to add something here - - # Use response_for :index to change this - respond_to { |f| f.html; f.js } - end - - def show - # Override #current_object to change this - @post = Post.find(params[:id]) - - # Use before :show to add something here - - # Use response_for :show to change this - respond_to { |f| f.html; f.js } - end - - def create - # Override #build_object to change this - @post = Post.new(params[:post]) - - # Use before :create to add something here - - if @post.save - # Use after :create to add something here - - # Use response_for :create to change this - respond_to do |f| - f.html do - flash[:notice] = "Create successful!" - redirect_to post_path(@post) - end - f.js - end - else - # Use after :create_fails to add something here - - # Use response_for :create_fails to change this - respond_to do |f| - format.html - flash[:error] = "There was a problem!" - render :action => :new, :status => 422 - end - format.js - end - end - end - - def update - # Override #current_object to change this - @post = Post.find(params[:id]) - - # Use before :update to do something here - - if @post.update_attributes params[:post] - # Use after :update to add something here - - # Use response_for :update to change this - respond_to do |f| - f.html do - flash[:notice] = "Save successful!" - redirect_to post_path(@post) - end - f.js - end - else - # Use after :update_fails to add something here - - # Use response_for :update_fails to change this - respond_to do |f| - format.html - flash[:error] = "There was a problem saving!" - render :action => :edit, :status => 422 - end - format.js - end - end - end - - def new - # Override #build_object to change this - @post = Post.new(params[:post]) - - # Use before :new to add something here - - # Use response_for :new to change this - respond_to { |f| f.html; f.js } - end - - def edit - # Override #current_object to change this - @post = Post.find(params[:id]) - - # Use before :edit to add something here - - # Use response_for :edit to change this - respond_to { |f| f.html; f.js } - end - - def destroy - # Override #current_object to change this - @post = Post.find(params[:id]) - - # Use before :destroy to do something here - - if @post.destroy - # Use after :destroy to add something here - - # Use response_for :destroy to change this - respond_to do |f| - f.html do - flash[:notice] = "Record deleted!" - redirect_to posts_path(@post) - end - f.js - end - else - # Use after :destroy_fails to add something here - - # Use response_for :destroy_fails to change this - respond_to do |f| - format.html - flash[:error] = "There was a problem deleting!" - render :back - end - format.js - end - end - end - end diff --git a/vendor/plugins/make_resourceful/LICENSE b/vendor/plugins/make_resourceful/LICENSE deleted file mode 100644 index 92a93ae6..00000000 --- a/vendor/plugins/make_resourceful/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2007 Hampton Catlin - -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. diff --git a/vendor/plugins/make_resourceful/README b/vendor/plugins/make_resourceful/README deleted file mode 100644 index 793a8593..00000000 --- a/vendor/plugins/make_resourceful/README +++ /dev/null @@ -1,239 +0,0 @@ -= make_resourceful -===== Take back control of your Controllers. Make them awesome. Make them sleek. Make them resourceful. - -REST is a fine pattern for designing controllers, -but it can be pretty repetitive. -Who wants to write out the same actions and copy the same model lookup logic -all over their application? - -make_resourceful handles all that for you. -It sets up all your RESTful actions and responses with next to no code. -Everything has full, sensible default functionality. - -Of course, no controller _only_ uses the defaults. -So make_resourceful can be massively customized, -while still keeping your controllers trim and readable. - -== Get it! - -Rails - - $ ruby script/plugin install http://svn.hamptoncatlin.com/make_resourceful/trunk - $ mv vendor/plugins/trunk vendor/plugins/make_resourceful - -Subversion - - $ svn co http://svn.hamptoncatlin.com/make_resourceful/trunk make_resourceful - -== Use it! - -The easiest way to start with make_resourceful -is to run the resource_scaffold generator. -It uses the same syntax as the Rails scaffold_resource generator: - - $ script/generate resource_scaffold post title:string body:text - -It does, however, require Haml[http://haml.hamptoncatlin.com]. -You _are_ using Haml, right? No? -I'll wait here while you go fall in love with it. - -If you want to try make_resourceful on one of your current controllers, -just replace the mess of repetition with this: - - class FooController < ApplicationController - make_resourceful do - actions :all - end - end - -Those three lines will replace the entire default controller -that comes out of the scaffold_resource generator. - -=== Really? - -Yes. - -=== Can I do nested resources? - - make_resourceful do - actions :all - belongs_to :post - end - -=== What if I want to use fancy permalinks? - - def current_object - @current_object ||= current_model.find_by_permalink(params[:id]) - end - -=== What about paging? - - def current_objects - @current_object ||= current_model.find(:all, - :order => "created_at DESC", :page => {:current => params[:page], :size => 10 } ) - end - -=== What if I want to do something in the middle of an action? - - before :show, :index do - @page_title = "Awesome!" - end - - after :create_fails do - @page_title = "Not So Awesome!" - end - -=== What about all of my awesome respond_to blocks for my XML APIs and RJS responses? - - response_for :show do |format| - format.html - format.js - format.xml - end - - response_for :update_fails do |format| - format.html { render :action => 'edit' } - format.json { render :json => false.to_json, :status => 422 } - end - -=== So I guess I have to write responses for all my actions? - -Nope! make_resourceful makes them do the right thing by default. -You only need to customize them if you want to do something special. - -=== Seriously?! - -Yes! - -== Grok it! - -=== +make_resourceful+ the Method - -The +make_resourceful+ block is where most of the action happens. -Here you specify which actions you want to auto-generate, -what code you want to run for given callbacks, -and so forth. - -You also use the block to declare various bits of information about your controller. -For instance, if the controller is nested, you'd call +belongs_to+. -If you wanted to expose your models as some sort of text format, -you'd call +publish+. - -Check out the documentation of Resourceful::Builder -for more information on the methods you can call here. - -=== Helper Methods - -make_resourceful provides lots of useful methods -that can be used in your callbacks and in your views. -They range from accessing the records you're looking up -to easily generating URLs for a record -to getting information about the action itself. - -Two of the most useful methods are +current_object+ and +current_objects+ -(note the subtle plurality difference). -+current_objects+ only works for +index+, -and returns all the records in the current model. -+current_object+ works for all actions other than +index+, -and returns the record that's currently being dealt with. - -The full documentation of the helper methods -is in Resourceful::Default::Accessors and Resourceful::Default::URLs. - -=== Nested Resources - -make_resourceful supports easy management of nested resources. -This is set up with the Resourceful::Builder#belongs_to declaration. -Pass in the name of the parent model, - - belongs_to :user - -and everything will be taken care of. -When +index+ is run for GET /users/12/albums, -parent_object[link:classes/Resourceful/Accessors.html#M000024] -will get <tt>User.find(params[:user_id])</tt>, -and current_objects[link:classes/Resourceful/Default/Accessors.html#M000010] -will get <tt>parent_object.albums</tt>. -When +create+ is run for POST /users/12/albums, -the newly created Album will automatically belong to the user -with id 12. - -The normal non-scoped actions still work, too. -GET /albums/15 runs just fine. -make_resourceful knows that since there's no <tt>params[:user_id]</tt>, -you just want to deal with the album. - -You can even have a single resource nested under several different resources. -Just pass multiple parent names to the Resourceful::Builder#belongs_to, like - - belongs_to :user, :artist - -Then /users/15/albums and /artists/7/albums will both work. - -This does, however, mean that make_resourceful only supports one level of nesting. -There's no automatic handling of /users/15/collections/437/albums. -However, this is really the best way to organize most resources anyway; -see this article[http://weblog.jamisbuck.org/2007/2/5/nesting-resources]. - -If you really need a deeply nested controller, -it should be easy enough to set up on your own. -Just override current_model[link:classes/Resourceful/Default/Accessors.html#M000018]. -See the next section for more details. - -=== Overriding Methods - -Not only are helper methods useful to the developer to use, -they're used internally by the actions created by make_resourceful. -Thus one of the main ways make_resourceful can be customized -is by overriding accessors. - -For instance, if you want to only look up the 10 most recent records for +index+, -you're override +current_objects+. -If you wanted to use a different model than that suggested by the name of the controller, -you'd override +current_model+. - -When you're overriding methods that do SQL lookups, though, be a little cautious. -By default, these methods cache their values in instance variables -so that multiple SQL queries aren't run on multiple calls. -When overriding them, it's wise for you to do the same. -For instance, - - def current_object - @current_object ||= current_model.find_by_name(params[:name]) - end - -=== For More Information... - -Haven't found all the information you need in the RDoc? -Still a little confused about something? -Don't despair, there are still more resources available! - -* Nathan Weizenbaum periodically makes blog posts about new features and versions of make_resourceful. - They may be a little outdated, but they should still be useful and explanatory. - * On nesting and associations: here[http://nex-3.com/posts/55-nesting-and-make_resourceful]. - * An overview of make_resourceful 0.2.0 and 0.2.2: here[http://localhost:3000/posts/54-make_resourceful-0-2-0]. - * On Resourceful::Builder#publish[link:classes/Resourceful/Builder.html#M000061] - and Resourceful::Serialize: - here[http://nex-3.com/posts/35-make_resourceful-the-basics-of-publish] and - here[http://nex-3.com/posts/36-make_resourceful-publish-extras]. -* There's an excellent, active Google Group (link[http://groups.google.com/group/make_resourceful]) - where people will be happy to answer your questions. -* Read the source code! - It's very straightforward, - and make_resourceful is built to encourage overriding methods - and hacking the source. - ---- - -Copyright 2007 Hampton Catlin, Nathan Weizenbaum, and Jeff Hardy. - -Contributions by: - -* Russell Norris -* Jonathan Linowes -* Cristi Balan -* Mike Ferrier -* James Golick -* Don Petersen -* Alex Ross -* Tom Stuart diff --git a/vendor/plugins/make_resourceful/Rakefile b/vendor/plugins/make_resourceful/Rakefile deleted file mode 100644 index a5c2490b..00000000 --- a/vendor/plugins/make_resourceful/Rakefile +++ /dev/null @@ -1,42 +0,0 @@ -require 'rake' -require 'rake/testtask' -require 'rake/rdoctask' -require 'spec/rake/spectask' - -desc 'Default: run unit tests.' -task :default => :test - -spec_files = Rake::FileList["spec/**/*_spec.rb"] - -desc "Run specs" -Spec::Rake::SpecTask.new do |t| - t.spec_files = spec_files - t.spec_opts = ["-c"] -end - -desc "Generate code coverage" -Spec::Rake::SpecTask.new(:coverage) do |t| - t.spec_files = spec_files - t.rcov = true - t.rcov_opts = ['--exclude', 'spec,/var/lib/gems'] -end - -desc 'Test the make_resourceful plugin.' -task :test do - Dir.chdir(File.dirname(__FILE__) + '/test') - tests = IO.popen('rake test') - - while byte = tests.read(1) - print byte - end -end - -desc 'Generate documentation for the make_resourceful plugin.' -Rake::RDocTask.new(:rdoc) do |rdoc| - rdoc.rdoc_dir = 'rdoc' - rdoc.title = 'make_resourceful' - rdoc.options << '--line-numbers' << '--inline-source' - rdoc.main = 'README' - rdoc.rdoc_files.include(FileList.new('*').exclude(/[^A-Z0-9]/)) - rdoc.rdoc_files.include('lib/**/*.rb') -end diff --git a/vendor/plugins/make_resourceful/VERSION b/vendor/plugins/make_resourceful/VERSION deleted file mode 100644 index 74ccf98d..00000000 --- a/vendor/plugins/make_resourceful/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.2.3.0 \ No newline at end of file diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/resourceful_scaffold_generator.rb b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/resourceful_scaffold_generator.rb deleted file mode 100644 index bb9f9e01..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/resourceful_scaffold_generator.rb +++ /dev/null @@ -1,87 +0,0 @@ -class ResourcefulScaffoldGenerator < 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 - alias_method :controller_file_name, :controller_underscore_name - alias_method :controller_table_name, :controller_plural_name - - 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, views, and test directories. - m.directory(File.join('app/models', class_path)) - m.directory(File.join('app/controllers', controller_class_path)) - m.directory(File.join('app/helpers', controller_class_path)) - m.directory(File.join('app/views', controller_class_path, controller_file_name)) - m.directory(File.join('test/functional', controller_class_path)) - m.directory(File.join('test/unit', class_path)) - m.directory(File.join('test/fixtures', class_path)) - - # Views - for action in scaffold_views - m.template("view_#{action}.haml", File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.haml")) - end - m.template('view_partial.haml', File.join('app/views', controller_class_path, controller_file_name, "_#{singular_name}.html.haml")) - - # Helper - m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb")) - - # Model - m.template('model.rb', File.join('app/models', class_path, "#{file_name}.rb")) - - 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 - - # Controller - m.template('controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")) - - # Tests - m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb")) - m.template('unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")) - m.template('fixtures.yml', File.join('test/fixtures', "#{table_name}.yml")) - - # Route - m.route_resources controller_file_name - end - end - - protected - - def banner - "Usage: #{$0} resourcefulscaffold ModelName [field:type, field:type]" - end - - def scaffold_views - %w[ index show new edit _form ] - end - - def model_name - class_name.demodulize - end -end diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/controller.rb b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/controller.rb deleted file mode 100644 index 61cd04c4..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/controller.rb +++ /dev/null @@ -1,5 +0,0 @@ -class <%= controller_class_name %>Controller < ApplicationController - make_resourceful do - actions :all - end -end diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/fixtures.yml b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/fixtures.yml deleted file mode 100644 index ff55a6e6..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/fixtures.yml +++ /dev/null @@ -1,10 +0,0 @@ -one: - id: 1 -<% for attribute in attributes -%> - <%= attribute.name %>: <%= attribute.default %> -<% end -%> -two: - id: 2 -<% for attribute in attributes -%> - <%= attribute.name %>: <%= attribute.default %> -<% end -%> diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/functional_test.rb b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/functional_test.rb deleted file mode 100644 index 3e404a07..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/functional_test.rb +++ /dev/null @@ -1,57 +0,0 @@ -require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../test_helper' -require '<%= controller_file_path %>_controller' - -# Re-raise errors caught by the controller. -class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end - -class <%= controller_class_name %>ControllerTest < Test::Unit::TestCase - fixtures :<%= table_name %> - - def setup - @controller = <%= controller_class_name %>Controller.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - end - - def test_should_get_index - get :index - assert_response :success - assert assigns(:<%= table_name %>) - end - - def test_should_get_new - get :new - assert_response :success - end - - def test_should_create_<%= file_name %> - old_count = <%= class_name %>.count - post :create, :<%= file_name %> => { } - assert_equal old_count + 1, <%= class_name %>.count - - assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>)) - end - - def test_should_show_<%= file_name %> - get :show, :id => 1 - assert_response :success - end - - def test_should_get_edit - get :edit, :id => 1 - assert_response :success - end - - def test_should_update_<%= file_name %> - put :update, :id => 1, :<%= file_name %> => { } - assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>)) - end - - def test_should_destroy_<%= file_name %> - old_count = <%= class_name %>.count - delete :destroy, :id => 1 - assert_equal old_count-1, <%= class_name %>.count - - assert_redirected_to <%= table_name %>_path - end -end diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/helper.rb b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/helper.rb deleted file mode 100644 index 9bd821b1..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module <%= controller_class_name %>Helper -end diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/migration.rb b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/migration.rb deleted file mode 100644 index 5711faa7..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/migration.rb +++ /dev/null @@ -1,13 +0,0 @@ -class <%= migration_name %> < ActiveRecord::Migration - def self.up - create_table :<%= table_name %>, :force => true do |t| -<% for attribute in attributes -%> - t.column :<%= attribute.name %>, :<%= attribute.type %> -<% end -%> - end - end - - def self.down - drop_table :<%= table_name %> - end -end diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/model.rb b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/model.rb deleted file mode 100644 index 8d4c89e9..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/model.rb +++ /dev/null @@ -1,2 +0,0 @@ -class <%= class_name %> < ActiveRecord::Base -end diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/unit_test.rb b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/unit_test.rb deleted file mode 100644 index 49ec7caa..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/unit_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper' - -class <%= class_name %>Test < Test::Unit::TestCase - def test_truth - assert true - end -end diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view__form.haml b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view__form.haml deleted file mode 100644 index 3c354e8e..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view__form.haml +++ /dev/null @@ -1,5 +0,0 @@ -<%- for attribute in attributes -%> -%p - %label{:for => "<%= singular_name %>_<%= attribute.name %>"} <%= attribute.column.human_name %>: - = f.<%= attribute.field_type %> :<%= attribute.name %> -<% end -%> diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_edit.haml b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_edit.haml deleted file mode 100644 index c54add07..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_edit.haml +++ /dev/null @@ -1,11 +0,0 @@ -%h1 Editing <%= singular_name %> - -= error_messages_for :<%= singular_name %> - -- form_for(:<%= singular_name %>, :url => object_url, :html => { :method => :put }) do |f| - = render :partial => "form", :locals => {:f => f} - %p= submit_tag "Update" - -= link_to 'Show', object_path -| -= link_to 'Back', objects_path diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_index.haml b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_index.haml deleted file mode 100644 index ab46c624..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_index.haml +++ /dev/null @@ -1,5 +0,0 @@ -%h1 Listing <%= plural_name %> - -= render :partial => '<%= singular_name %>', :collection => current_objects - -= link_to 'New <%= singular_name %>', new_object_path diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_new.haml b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_new.haml deleted file mode 100644 index ee16c90c..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_new.haml +++ /dev/null @@ -1,9 +0,0 @@ -%h1 Creating <%= singular_name %> - -= error_messages_for :<%= singular_name %> - -- form_for(:<%= singular_name %>, :url => objects_url) do |f| - = render :partial => "form", :locals => {:f => f} - %p= submit_tag "Create" - -= link_to 'Back', objects_path diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_partial.haml b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_partial.haml deleted file mode 100644 index 5a61b4d1..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_partial.haml +++ /dev/null @@ -1,12 +0,0 @@ -%div[<%= singular_name %>] -<% for attribute in attributes -%> - %p.<%= attribute.name %> - %strong <%= attribute.column.human_name %> - = h <%= singular_name %>.<%= attribute.name %> -<% end -%> - - = link_to 'Show', object_path(<%= singular_name %>) - | - = link_to 'Edit', edit_object_path(<%= singular_name %>) - | - = link_to 'Destroy', object_path(<%= singular_name %>), :confirm => 'Really destroy <%= singular_name %>?', :method => :delete diff --git a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_show.haml b/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_show.haml deleted file mode 100644 index 1fe80e00..00000000 --- a/vendor/plugins/make_resourceful/generators/resourceful_scaffold/templates/view_show.haml +++ /dev/null @@ -1,14 +0,0 @@ -%h1 Viewing <%= singular_name %> - -%div[current_object] -<% for attribute in attributes -%> - %p.<%= attribute.name %> - %strong <%= attribute.column.human_name %> - = h current_object.<%= attribute.name %> -<% end -%> - -= link_to 'Edit', edit_object_path -| -= link_to 'Destroy', object_path, :confirm => 'Really destroy <%= singular_name %>?', :method => :delete -| -= link_to 'Back', objects_path diff --git a/vendor/plugins/make_resourceful/init.rb b/vendor/plugins/make_resourceful/init.rb deleted file mode 100644 index 773c09ab..00000000 --- a/vendor/plugins/make_resourceful/init.rb +++ /dev/null @@ -1,3 +0,0 @@ -require 'resourceful/maker' - -ActionController::Base.extend Resourceful::Maker diff --git a/vendor/plugins/make_resourceful/lib/resourceful/base.rb b/vendor/plugins/make_resourceful/lib/resourceful/base.rb deleted file mode 100644 index 412e5ad6..00000000 --- a/vendor/plugins/make_resourceful/lib/resourceful/base.rb +++ /dev/null @@ -1,52 +0,0 @@ -# The module containing all the code for make_resourceful. -# -# For methods available in the +make_resourceful+ block, -# see Resourceful::Builder. -# -# For helper methods and methods you can override -# to customize the behavior of your actions, -# see Resourceful::Default::Accessors -# and Resourceful::Default::URLs. -module Resourceful - # We want to define some stuff before we load other modules - - # The default actions generated by make_resourceful. - ACTIONS = [:index, :show, :edit, :update, :create, :new, :destroy] - - # The actions that modify the database. - MODIFYING_ACTIONS = [:update, :create, :destroy] - - # The actions that act on multiple records. - PLURAL_ACTIONS = [:index] - - # The actions that act on just one record. - SINGULAR_ACTIONS = ACTIONS - PLURAL_ACTIONS -end - -require 'resourceful/default/accessors' -require 'resourceful/default/responses' -require 'resourceful/default/callbacks' -require 'resourceful/default/urls' - -# All modules included by this module -# are made available to the controller as accessors. -module Resourceful::Base - @@made_resourceful_callbacks = [] - - # This method is meant to be called by included classes. - # It takes a block of the same form as that given to Maker#make_resourceful. - # The Maker will then run that block - # along with the blocks given by the individual controllers. - def self.made_resourceful(&block) - if block - @@made_resourceful_callbacks << block - else - @@made_resourceful_callbacks - end - end - - include Resourceful::Default::Accessors - include Resourceful::Default::Responses - include Resourceful::Default::Callbacks - include Resourceful::Default::URLs -end diff --git a/vendor/plugins/make_resourceful/lib/resourceful/builder.rb b/vendor/plugins/make_resourceful/lib/resourceful/builder.rb deleted file mode 100644 index 64a07f41..00000000 --- a/vendor/plugins/make_resourceful/lib/resourceful/builder.rb +++ /dev/null @@ -1,354 +0,0 @@ -require 'resourceful/response' -require 'resourceful/serialize' -require 'resourceful/default/actions' - -module Resourceful - # The Maker#make_resourceful block is evaluated in the context - # of an instance of this class. - # It provides various methods for customizing the behavior of the actions - # built by make_resourceful. - # - # All instance methods of this class are available in the +make_resourceful+ block. - class Builder - # The klass of the controller on which the builder is working. - attr :controller, true - - # The constructor is only meant to be called internally. - # - # This takes the klass (class object) of a controller - # and constructs a Builder ready to apply the make_resourceful - # additions to the controller. - def initialize(kontroller) - @controller = kontroller - @inherited = !kontroller.read_inheritable_attribute(:resourceful_responses).blank? - @action_module = Resourceful::Default::Actions.dup - @ok_actions = [] - @callbacks = {:before => {}, :after => {}} - @responses = {} - @publish = {} - @parents = [] - end - - # This method is only meant to be called internally. - # - # Applies all the changes that have been declared - # via the instance methods of this Builder - # to the kontroller passed in to the constructor. - def apply - apply_publish - - kontroller = @controller - Resourceful::ACTIONS.each do |action_named| - # See if this is a method listed by #actions - unless @ok_actions.include? action_named - # If its not listed, then remove the method - # No one can hit it... if its DEAD! - @action_module.send :remove_method, action_named - end - end - - kontroller.hidden_actions.reject! &@ok_actions.method(:include?) - kontroller.send :include, @action_module - - kontroller.read_inheritable_attribute(:resourceful_callbacks).merge! @callbacks - kontroller.read_inheritable_attribute(:resourceful_responses).merge! @responses - kontroller.write_inheritable_attribute(:made_resourceful, true) - - kontroller.write_inheritable_attribute(:parents, @parents) - kontroller.before_filter :load_parent_object, :only => @ok_actions - end - - # :call-seq: - # actions(*available_actions) - # actions :all - # - # Adds the default RESTful actions to the controller. - # - # If the only argument is <tt>:all</tt>, - # adds all the actions listed in Resourceful::ACTIONS[link:classes/Resourceful.html] - # (or Resourceful::SINGULAR_ACTIONS[link:classes/Resourceful.html] - # for a singular controller). - # - # Otherwise, this adds all actions - # whose names were passed as arguments. - # - # For example: - # - # actions :show, :new, :create - # - # This adds the +show+, +new+, and +create+ actions - # to the controller. - # - # The available actions are defined in Default::Actions. - def actions(*available_actions) - if available_actions.first == :all - available_actions = controller.new.plural? ? ACTIONS : SINGULAR_ACTIONS - end - - available_actions.each { |action| @ok_actions << action.to_sym } - end - alias build actions - - # :call-seq: - # before(*events) { ... } - # - # Sets up a block of code to run before one or more events. - # - # All the default actions can be used as +before+ events: - # <tt>:index</tt>, <tt>:show</tt>, <tt>:create</tt>, <tt>:update</tt>, <tt>:new</tt>, <tt>:edit</tt>, and <tt>:destroy</tt>. - # - # +before+ events are run after any objects are loaded[link:classes/Resourceful/Default/Accessors.html#M000015], - # but before any database operations or responses. - # - # For example: - # - # before :show, :edit do - # @page_title = current_object.title - # end - # - # This will set the <tt>@page_title</tt> variable - # to the current object's title - # for the show and edit actions. - # - # Successive before blocks for the same action will be chained and executed - # in order when the event occurs. - # - # For example: - # - # before :show, :edit do - # @page_title = current_object.title - # end - # - # before :show do - # @side_bar = true - # end - # - # These before blocks will both be executed for the show action and in the - # same order as they were defined. - def before(*events, &block) - add_callback :before, *events, &block - end - - # :call-seq: - # after(*events) { ... } - # - # Sets up a block of code to run after one or more events. - # - # There are two sorts of +after+ events. - # <tt>:create</tt>, <tt>:update</tt>, and <tt>:destroy</tt> - # are run after their respective database operations - # have been completed successfully. - # <tt>:create_fails</tt>, <tt>:update_fails</tt>, and <tt>:destroy_fails</tt>, - # on the other hand, - # are run after the database operations fail. - # - # +after+ events are run after the database operations - # but before any responses. - # - # For example: - # - # after :create_fails, :update_fails do - # current_object.password = nil - # end - # - # This will nillify the password of the current object - # if the object creation/modification failed. - def after(*events, &block) - add_callback :after, *events, &block - end - - # :call-seq: - # response_for(*actions) { ... } - # response_for(*actions) { |format| ... } - # - # Sets up a block of code to run - # instead of the default responses for one or more events. - # - # If the block takes a format parameter, - # it has the same semantics as Rails' +respond_to+ method. - # Various format methods are called on the format object - # with blocks that say what to do for each format. - # For example: - # - # response_for :index do |format| - # format.html - # format.atom do - # headers['Content-Type'] = 'application/atom+xml; charset=utf-8' - # render :action => 'atom', :layout => false - # end - # end - # - # This doesn't do anything special for the HTML - # other than ensure that the proper view will be rendered, - # but for ATOM it sets the proper content type - # and renders the atom template. - # - # If you only need to set the HTML response, - # you can omit the format parameter. - # For example: - # - # response_for :new do - # render :action => 'edit' - # end - # - # This is the same as - # - # response_for :new do |format| - # format.html { render :action => 'edit' } - # end - # - # The default responses are defined by - # Default::Responses.included[link:classes/Resourceful/Default/Responses.html#M000011]. - def response_for(*actions, &block) - raise "Must specify one or more actions for response_for." if actions.empty? - - if block.arity < 1 - response_for(*actions) do |format| - format.html(&block) - end - else - response = Response.new - block.call response - - actions.each do |action| - @responses[action.to_sym] = response.formats - end - end - end - - # :call-seq: - # publish *formats, options = {}, :attributes => [ ... ] - # - # publish allows you to easily expose information about resourcess in a variety of formats. - # The +formats+ parameter is a list of formats - # in which to publish the resources. - # The formats supported by default are +xml+, +yaml+, and +json+, - # but other formats may be added by defining +to_format+ methods - # for the Array and Hash classes - # and registering the mime type with Rails' Mime::Type.register[http://api.rubyonrails.org/classes/Mime/Type.html#M001115]. - # See Resourceful::Serialize for more details.. - # - # The <tt>:attributes</tt> option is mandatory. - # It takes an array of attributes (as symbols) to make public. - # These attributes can refer to any method on current_object; - # they aren't limited to database fields. - # For example: - # - # # posts_controller.rb - # publish :yaml, :attributes => [:title, :created_at, :rendered_content] - # - # Then GET /posts/12.yaml would render - # - # --- - # post: - # title: Cool Stuff - # rendered_content: |- - # <p>This is a post.</p> - # <p>It's about <strong>really</strong> cool stuff.</p> - # created_at: 2007-04-28 04:32:08 -07:00 - # - # The <tt>:attributes</tt> array can even contain attributes - # that are themselves models. - # In this case, you must use a hash to specify their attributes as well. - # For example: - # - # # person_controller.rb - # publish :xml, :json, :attributes => [ - # :name, :favorite_color, { - # :pet_cat => [:name, :breed], - # :hat => [:type] - # }] - # - # Then GET /people/18.xml would render - # - # <?xml version="1.0" encoding="UTF-8"?> - # <person> - # <name>Nathan</name> - # <favorite-color>blue</favorite_color> - # <pet-cat> - # <name>Jasmine</name> - # <breed>panther</breed> - # </pet-cat> - # <hat> - # <type>top</type> - # </hat> - # </person> - # - # publish will also allow the +index+ action - # to render lists of objects. - # An example would be too big, - # but play with it a little on your own to see. - # - # publish takes only one optional option: <tt>only</tt>. - # This specifies which action to publish the resources for. - # By default, they're published for both +show+ and +index+. - # For example: - # - # # cats_controller.rb - # publish :json, :only => :index, :attributes => [:name, :breed] - # - # Then GET /cats.json would work, but GET /cats/294.json would fail. - def publish(*formats) - options = { - :only => [:show, :index] - }.merge(Hash === formats.last ? formats.pop : {}) - raise "Must specify :attributes option" unless options[:attributes] - - Array(options.delete(:only)).each do |action| - @publish[action] ||= [] - formats.each do |format| - format = format.to_sym - @publish[action] << [format, proc do - render_action = [:json, :xml].include?(format) ? format : :text - render render_action => (plural_action? ? current_objects : current_object).serialize(format, options) - end] - end - end - end - - # Specifies parent resources for the current resource. - # Each of these parents will be loaded automatically - # if the proper id parameter is given. - # For example, - # - # # cake_controller.rb - # belongs_to :baker, :customer - # - # Then on GET /bakers/12/cakes, - # - # params[:baker_id] #=> 12 - # parent? #=> true - # parent_name #=> "baker" - # parent_model #=> Baker - # parent_object #=> Baker.find(12) - # current_objects #=> Baker.find(12).cakes - # - def belongs_to(*parents) - @parents = parents.map(&:to_s) - end - - # This method is only meant to be called internally. - # - # Returns whether or not the Builder's controller - # inherits make_resourceful settings from a parent controller. - def inherited? - @inherited - end - - private - - def apply_publish - @publish.each do |action, types| - @responses[action.to_sym] ||= [] - @responses[action.to_sym] += types - end - end - - def add_callback(type, *events, &block) - events.each do |event| - @callbacks[type][event.to_sym] ||= [] - @callbacks[type][event.to_sym] << block - end - end - end -end diff --git a/vendor/plugins/make_resourceful/lib/resourceful/default/accessors.rb b/vendor/plugins/make_resourceful/lib/resourceful/default/accessors.rb deleted file mode 100644 index d98386d2..00000000 --- a/vendor/plugins/make_resourceful/lib/resourceful/default/accessors.rb +++ /dev/null @@ -1,346 +0,0 @@ -module Resourceful - # This module contains various methods - # that are available from actions and callbacks. - # Default::Accessors and Default::URLs are the most useful to users; - # the rest are mostly used internally. - # - # However, if you want to poke around the internals a little, - # check out Default::Actions, which has the default Action definitions, - # and Default::Responses.included, which defines the default response_for[link:classes/Resourceful/Builder.html#M000061] blocks. - module Default - # This module contains all sorts of useful methods - # that allow access to the resources being worked with, - # metadata about the controller and action, - # and so forth. - # - # Many of these accessors call other accessors - # and are called by the default make_resourceful actions[link:classes/Resourceful/Default/Actions.html]. - # This means that overriding one method - # can affect everything else. - # - # This can be dangerous, but it can also be very powerful. - # make_resourceful is designed to take advantage of overriding, - # so as long as the new methods accomplish the same purpose as the old ones, - # everything will just work. - # Even if you make a small mistake, - # it's hard to break the controller in any unexpected ways. - # - # For example, suppose your controller is called TagsController, - # but your model is called PhotoTag. - # All you have to do is override current_model_name: - # - # def current_model_name - # "PhotoTag" - # end - # - # Then current_model will return the PhotoTag model, - # current_object will call <tt>PhotoTag.find</tt>, - # and so forth. - # - # Overriding current_objects and current_object is particularly useful - # for providing customized model lookup logic. - module Accessors - # Returns an array of all the objects of the model corresponding to the controller. - # For UsersController, it essentially runs <tt>User.find(:all)</tt>. - # - # However, there are a few important differences. - # First, this method caches is results in the <tt>@current_objects</tt> instance variable. - # That way, multiple calls won't run multiple queries. - # - # Second, this method uses the current_model accessor, - # which provides a lot of flexibility - # (see the documentation for current_model for details). - def current_objects - @current_objects ||= current_model.find(:all) - end - - # Calls current_objects and stores - # the result in an instance variable - # named after the controller. - # - # This is called automatically by the default make_resourceful actions. - # You shouldn't need to use it directly unless you're creating a new action. - # - # For example, in UsersController, - # calling +load_objects+ sets <tt>@users = current_objects</tt>. - def load_objects - instance_variable_set("@#{instance_variable_name}", current_objects) - end - - # Returns the object referenced by the id parameter - # (or the newly-created object for the +new+ and +create+ actions). - # For UsersController, it essentially runs <tt>User.find(params[:id])</tt>. - # - # However, there are a few important differences. - # First, this method caches is results in the <tt>@current_objects</tt> instance variable. - # That way, multiple calls won't run multiple queries. - # - # Second, this method uses the current_model accessor, - # which provides a lot of flexibility - # (see the documentation for current_model for details). - # - # Note that this is different for a singleton controller, - # where there's only one resource per parent resource. - # Then this just returns that resource. - # For example, if Person has_one Hat, - # then in HatsController current_object essentially runs <tt>Person.find(params[:person_id]).hat</tt>. - def current_object - @current_object ||= if plural? - current_model.find(params[:id]) - else - parent_object.send(instance_variable_name) - end - end - - - # Calls current_object and stores - # the result in an instance variable - # named after the controller. - # - # This is called automatically by the default make_resourceful actions. - # You shouldn't need to use it directly unless you're creating a new action. - # - # For example, in UsersController, - # calling +load_object+ sets <tt>@user = current_object</tt>. - def load_object - instance_variable_set("@#{instance_variable_name.singularize}", current_object) - end - - # Creates a new object of the type of the current model - # with the current object's parameters. - # +current_object+ then returns this object for this action - # instead of looking up a new object. - # - # This is called automatically by the default make_resourceful actions. - # You shouldn't need to use it directly unless you're creating a new action. - # - # Note that if a parent object exists, - # the newly created object will automatically be a child of the parent object. - # For example, on POST /people/4/things, - # - # build_object - # current_object.person.id #=> 4 - # - def build_object - @current_object = if current_model.respond_to? :build - current_model.build(object_parameters) - else - returning(current_model.new(object_parameters)) do |obj| - obj.send("#{parent_name}_id=", parent_object.id) if singular? && parent? - end - end - end - - # The string name of the current model. - # By default, this is derived from the name of the controller. - def current_model_name - controller_name.singularize.camelize - end - - # An array of namespaces under which the current controller is. - # For example, in Admin::Content::PagesController: - # - # namespaces #=> [:admin, :content] - # - def namespaces - @namespaces ||= self.class.name.split('::').slice(0...-1).map(&:underscore).map(&:to_sym) - end - - # The name of the instance variable that load_object and load_objects should assign to. - def instance_variable_name - controller_name - end - - # The class of the current model. - # Note that if a parent object exists, - # this instead returns the association object. - # For example, in HatsController where Person has_many :hats, - # - # current_model #=> Person.find(params[:person_id]).hats - # - # This is useful because the association object uses duck typing - # to act like a model class. - # It supplies a find method that's automatically scoped - # to ensure that the object returned is actually a child of the parent, - # and so forth. - def current_model - if !parent? || singular? - current_model_name.constantize - else - parent_object.send(instance_variable_name) - end - end - - # Returns the hash passed as HTTP parameters - # that defines the new (or updated) attributes - # of the current object. - # This is only meaningful for +create+ or +update+. - def object_parameters - params[current_model_name.underscore] - end - - # Returns a list of the names of all the potential parents of the current model. - # For a non-nested controller, this is <tt>[]</tt>. - # For example, in HatsController where Rack has_many :hats and Person has_many :hats, - # - # parents #=> ["rack", "person"] - # - # Note that the parents must be declared via Builder#belongs_to. - def parent_names - self.class.read_inheritable_attribute :parents - end - - # Returns true if an appropriate parent id parameter has been supplied. - # For example, in HatsController where Rack has_many :hats and Person has_many :hats, - # if <tt>params[:rack_id]</tt> or <tt>params[:person_id]</tt> is given, - # - # parent? #=> true - # - # Otherwise, if both <tt>params[:rack_id]</tt> and <tt>params[:rack_id]</tt> are nil, - # - # parent? #=> false - # - # Note that parents must be declared via Builder#belongs_to. - def parent? - !!parent_name - end - - # Returns the name of the current parent object if a parent id is given, or nil otherwise. - # For example, in HatsController where Rack has_many :hats and Person has_many :hats, - # if <tt>params[:rack_id]</tt> is given, - # - # parent_name #=> "rack" - # - # If <tt>params[:person_id]</tt> is given, - # - # parent_name #=> "person" - # - # If both <tt>params[:rack_id]</tt> and <tt>params[:rack_id]</tt> are nil, - # - # parent_name #=> nil - # - # There are several things to note about this method. - # First, make_resourceful only supports single-level model nesting. - # Thus, if neither <tt>params[:rack_id]</tt> nor <tt>params[:rack_id]</tt> are nil, - # the return value of +parent_name+ is undefined. - # - # Second, don't use parent_name to check whether a parent id is given. - # It's better to use the more semantic parent? method. - # - # Third, parent_name caches its return value in the <tt>@parent_name</tt> variable, - # which you should keep in mind if you're overriding it. - # However, because <tt>@parent_name == nil</tt> could mean that there is no parent - # _or_ that the method hasn't been run yet, - # it uses <tt>defined?(@parent_name)</tt> to do the caching - # rather than <tt>@parent_name ||=</tt>. See the source code. - # - # Finally, note that parents must be declared via Builder#belongs_to. - def parent_name - return @parent_name if defined?(@parent_name) - @parent_name = parent_names.find { |name| params["#{name}_id"] } - end - - # Returns the model class of the current parent. - # For example, in HatsController where Person has_many :hats, - # if <tt>params[:person_id]</tt> is given, - # - # parent_models #=> Rack - # - # Note that parents must be declared via Builder#belongs_to. - def parent_model - parent_name.camelize.constantize - end - - # Returns the current parent object for the current object. - # For example, in HatsController where Person has_many :hats, - # if <tt>params[:person_id]</tt> is given, - # - # parent_object #=> Person.find(params[:person_id]) - # - # Note that parents must be declared via Builder#belongs_to. - # - # Note also that the results of this method are cached - # so that multiple calls don't result in multiple SQL queries. - def parent_object - @parent_object ||= parent_model.find(params["#{parent_name}_id"]) - end - - # Assigns the current parent object, as given by parent_objects, - # to its proper instance variable, as given by parent_name. - # - # This is automatically added as a before_filter. - # You shouldn't need to use it directly unless you're creating a new action. - def load_parent_object - instance_variable_set("@#{parent_name}", parent_object) if parent? - end - - # Renders a 422 error if no parent id is given. - # This is meant to be used with before_filter - # to ensure that some actions are only called with a parent id. - # For example: - # - # before_filter :ensure_parent_exists, :only => [:create, :update] - # - def ensure_parent_exists - return true if parent? - render :text => 'No parent id given', :status => 422 - return false - end - - # Returns whether or not the database update in the +create+, +update+, and +destroy+ - # was completed successfully. - def save_succeeded? - @save_succeeded - end - - # Declares that the current databse update was completed successfully. - # Causes subsequent calls to <tt>save_succeeded?</tt> to return +true+. - # - # This is mostly meant to be used by the default actions, - # but it can be used by user-defined actions as well. - def save_succeeded! - @save_succeeded = true - end - - # Declares that the current databse update was not completed successfully. - # Causes subsequent calls to <tt>save_succeeded?</tt> to return +false+. - # - # This is mostly meant to be used by the default actions, - # but it can be used by user-defined actions as well. - def save_failed! - @save_succeeded = false - end - - # Returns whether or not the current action acts upon multiple objects. - # By default, the only such action is +index+. - def plural_action? - PLURAL_ACTIONS.include?(params[:action].to_sym) - end - - # Returns whether or not the current action acts upon a single object. - # By default, this is the case for all actions but +index+. - def singular_action? - !plural_action? - end - - # Returns whether the controller is a singleton, - # implying that there is only one such resource for each parent resource. - # - # Note that the way this is determined is based on the singularity of the controller name, - # so it may yield false positives for oddly-named controllers and need to be overridden. - def singular? - instance_variable_name.singularize == instance_variable_name - end - - # Returns whether the controller is a normal plural controller, - # implying that there are multiple resources for each parent resource. - # - # Note that the way this is determined is based on the singularity of the controller name, - # so it may yield false negatives for oddly-named controllers. - # If this is the case, the singular? method should be overridden. - def plural? - !singular? - end - end - end -end diff --git a/vendor/plugins/make_resourceful/lib/resourceful/default/actions.rb b/vendor/plugins/make_resourceful/lib/resourceful/default/actions.rb deleted file mode 100644 index 61e8088a..00000000 --- a/vendor/plugins/make_resourceful/lib/resourceful/default/actions.rb +++ /dev/null @@ -1,92 +0,0 @@ -module Resourceful - module Default - # Contains the definitions of the default resourceful actions. - # These are made available with the Builder#actions method. - # - # These methods are very compact, - # so the best way to understand them is just to look at their source. - # Check out Resourceful::Accessors and Resourceful::Callbacks - # for the documentation of the methods called within the actions. - # - # Along with each action is listed the RESTful method - # which corresponds to the action. - # The controller in the examples is FoosController, - # and the id for single-object actions is 12. - module Actions - # GET /foos - def index - load_objects - before :index - response_for :index - end - - # GET /foos/12 - def show - load_object - before :show - response_for :show - rescue - response_for :show_fails - end - - # POST /foos - def create - build_object - load_object - before :create - if current_object.save - save_succeeded! - after :create - response_for :create - else - save_failed! - after :create_fails - response_for :create_fails - end - end - - # PUT /foos/12 - def update - load_object - before :update - if current_object.update_attributes object_parameters - save_succeeded! - after :update - response_for :update - else - save_failed! - after :update_fails - response_for :update_fails - end - end - - # GET /foos/new - def new - build_object - load_object - before :new - response_for :new - end - - # GET /foos/12/edit - def edit - load_object - before :edit - response_for :edit - end - - # DELETE /foos/12 - def destroy - load_object - before :destroy - if current_object.destroy - after :destroy - response_for :destroy - else - after :destroy_fails - response_for :destroy_fails - end - end - end - end -end diff --git a/vendor/plugins/make_resourceful/lib/resourceful/default/callbacks.rb b/vendor/plugins/make_resourceful/lib/resourceful/default/callbacks.rb deleted file mode 100644 index f25eff42..00000000 --- a/vendor/plugins/make_resourceful/lib/resourceful/default/callbacks.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'resourceful/builder' - -module Resourceful - module Default - # This module is mostly meant to be used by the make_resourceful default actions. - # It provides various methods that declare where callbacks set in the +make_resourceful+ block, - # like Builder#before and Builder#response_for, - # should be called. - module Callbacks - # Calls any +before+ callbacks set in the +make_resourceful+ block for the given event. - def before(event) - resourceful_fire(:before, event.to_sym) - end - - # Calls any +after+ callbacks set in the +make_resourceful+ block for the given event. - def after(event) - resourceful_fire(:after, event.to_sym) - end - - # Calls any +response_for+ callbacks set in the +make_resourceful+ block for the given event. - # Note that these aren't called directly, - # but instead passed along to Rails' respond_to method. - def response_for(event) - if responses = self.class.read_inheritable_attribute(:resourceful_responses)[event.to_sym] - respond_to do |format| - responses.each do |key, value| - format.send(key, &scope(value)) - end - end - end - end - - # Returns a block identical to the given block, - # but in the context of the current controller. - # The returned block accepts no arguments, - # even if the given block accepted them. - def scope(block) - lambda do - instance_eval(&(block || lambda {})) - end - end - - private - - def resourceful_fire(type, name) - callbacks = self.class.read_inheritable_attribute(:resourceful_callbacks)[type][name] || [] - callbacks.each { |callback| scope(callback).call } - end - end - end -end diff --git a/vendor/plugins/make_resourceful/lib/resourceful/default/responses.rb b/vendor/plugins/make_resourceful/lib/resourceful/default/responses.rb deleted file mode 100644 index 67cd5e61..00000000 --- a/vendor/plugins/make_resourceful/lib/resourceful/default/responses.rb +++ /dev/null @@ -1,118 +0,0 @@ -module Resourceful - module Default - module Responses - # Sets the default flash message. - # This message can be overridden by passing in - # an HTTP parameter of the form "_flash[type]" via POST or GET. - # - # You can use this to easily have multiple forms - # post to the same create/edit/destroy actions - # but display different flash notices - - # without modifying the controller code at all. - # - # By default, the flash types are +notice+ when the database operation completes successfully - # and +error+ when it fails. - # - #-- - # TODO: Move this out of here - #++ - def set_default_flash(type, message) - flash[type] ||= (params[:_flash] && params[:_flash][type]) || message - end - - # Sets the default redirect - # (the argument passed to +redirect_to+). - # This message can be overridden by passing in - # an HTTP parameter of the form "_redirect_on[status]" via POST or GET. - # - # You can use this to easily have multiple forms - # post to the same create/edit/destroy actions - # but redirect to different URLs - - # without modifying the controller code at all. - # - # By default, the redirect statuses are +success+ when the database operation completes successfully - # and +failure+ when it fails. - # Use the <tt>:status</tt> option to specify which status to run the redirect for. - # For example: - # - # set_default_redirect "/posts", :status => :failure - # - # This will run <tt>redirect_to params[:_redirect_on][:failure]</tt> if the parameter exists, - # or <tt>redirect_to "/posts"</tt> otherwise. - # - #-- - # TODO: Move this out of here - #++ - def set_default_redirect(to, options = {}) - status = options[:status] || :success - redirect_to (params[:_redirect_on] && params[:_redirect_on][status]) || to - end - - # This method is automatically run when this module is included in Resourceful::Base. - # It sets up the default responses for the default actions. - def self.included(base) - base.made_resourceful do - response_for(:show, :index, :edit, :new) do |format| - format.html - format.js - end - - response_for(:show_fails) do |format| - not_found = Proc.new { render :text => "No item found", :status => 404 } - format.html ¬_found - format.js ¬_found - format.xml ¬_found - end - - response_for(:create) do |format| - format.html do - set_default_flash(:notice, "Create successful!") - set_default_redirect object_path - end - format.js - end - - response_for(:create_fails) do |format| - format.html do - set_default_flash :error, "There was a problem!" - render :action => :new, :status => 422 - end - format.js - end - - response_for(:update) do |format| - format.html do - set_default_flash :notice, "Save successful!" - set_default_redirect object_path - end - format.js - end - - response_for(:update_fails) do |format| - format.html do - set_default_flash :error, "There was a problem saving!" - render :action => :edit, :status => 422 - end - format.js - end - - response_for(:destroy) do |format| - format.html do - set_default_flash :notice, "Record deleted!" - set_default_redirect objects_path - end - format.js - end - - response_for(:destroy_fails) do |format| - format.html do - set_default_flash :error, "There was a problem deleting!" - set_default_redirect :back, :status => :failure - end - format.js - end - end - end - end - end -end diff --git a/vendor/plugins/make_resourceful/lib/resourceful/default/urls.rb b/vendor/plugins/make_resourceful/lib/resourceful/default/urls.rb deleted file mode 100644 index 589ef096..00000000 --- a/vendor/plugins/make_resourceful/lib/resourceful/default/urls.rb +++ /dev/null @@ -1,137 +0,0 @@ -module Resourceful - module Default - # This file contains various methods to make URL helpers less painful. - # They provide methods analogous to the standard foo_url and foo_path helpers. - # However, they use make_resourceful's knowledge of the structure of the controller - # to allow you to avoid figuring out which method to call and which parent objects it should be passed. - module URLs - # This returns the path for the given object, - # by default current_object[link:classes/Resourceful/Default/Accessors.html#M000012]. - # For example, in HatsController the following are equivalent: - # - # object_path #=> "/hats/12" - # hat_path(@hat) #=> "/hats/12" - # - def object_path(object = current_object); object_route(object, 'path'); end - # Same as object_path, but with the protocol and hostname. - def object_url (object = current_object); object_route(object, 'url'); end - - # This is the same as object_path, - # unless a parent exists. - # Then it returns the nested path for the object. - # For example, in HatsController where Person has_many :hats and <tt>params[:person_id] == 42</tt>, - # the following are equivalent: - # - # nested_object_path #=> "/person/42/hats/12" - # person_hat_path(@person, @hat) #=> "/person/42/hats/12" - # - def nested_object_path(object = current_object); nested_object_route(object, 'path'); end - # Same as nested_object_path, but with the protocol and hostname. - def nested_object_url (object = current_object); nested_object_route(object, 'url'); end - - # This returns the path for the edit action for the given object, - # by default current_object[link:classes/Resourceful/Default/Accessors.html#M000012]. - # For example, in HatsController the following are equivalent: - # - # edit_object_path #=> "/hats/12/edit" - # edit_person_hat_path(@person, @hat) #=> "/hats/12/edit" - # - def edit_object_path(object = current_object); edit_object_route(object, 'path'); end - # Same as edit_object_path, but with the protocol and hostname. - def edit_object_url (object = current_object); edit_object_route(object, 'url'); end - - # This returns the path for the collection of the current controller. - # For example, in HatsController where Person has_many :hats and <tt>params[:person_id] == 42</tt>, - # the following are equivalent: - # - # objects_path #=> "/people/42/hats" - # person_hats_path(@person) #=> "/people/42/hats" - # - def objects_path; objects_route('path'); end - # Same as objects_path, but with the protocol and hostname. - def objects_url ; objects_route('url'); end - - # This returns the path for the new action for the current controller. - # For example, in HatsController where Person has_many :hats and <tt>params[:person_id] == 42</tt>, - # the following are equivalent: - # - # new_object_path #=> "/people/42/hats/new" - # new_person_hat_path(@person) #=> "/people/42/hats/new" - # - def new_object_path; new_object_route('path'); end - # Same as new_object_path, but with the protocol and hostname. - def new_object_url ; new_object_route('url'); end - - # This returns the path for the parent object. - # - def parent_path(object = parent_object) - instance_route(parent_name, object, 'path') - end - # Same as parent_path, but with the protocol and hostname. - def parent_url(object = parent_object) - instance_route(parent_name, object, 'url') - end - - # This prefix is added to the Rails URL helper names - # before they're called. - # By default, it's the underscored list of namespaces of the current controller, - # or nil if there are no namespaces defined. - # However, it can be overridden if another prefix is needed. - # Note that if this is overridden, - # the new method should return a string ending in an underscore. - # - # For example, in Admin::Content::PagesController: - # - # url_helper_prefix #=> "admin_content_" - # - # Then object_path is the same as <tt>admin_content_page_path(current_object)</tt>. - def url_helper_prefix - namespaces.empty? ? nil : "#{namespaces.join('_')}_" - end - - # This prefix is added to the Rails URL helper names - # for the make_resourceful collection URL helpers, - # objects_path and new_object_path. - # It's only added if url_helper_prefix returns nil. - # By default, it's the parent name followed by an underscore if a parent is given, - # and the empty string otherwise. - # - # See also url_helper_prefix. - def collection_url_prefix - parent? ? "#{parent_name}_" : '' - end - - private - - def object_route(object, type) - instance_route(current_model_name.underscore, object, type) - end - - def nested_object_route(object, type) - return object_route(object, type) unless parent? - send("#{url_helper_prefix}#{parent_name}_#{current_model_name.underscore}_#{type}", parent_object, object) - end - - def edit_object_route(object, type) - instance_route(current_model_name.underscore, object, type, "edit") - end - - def objects_route(type) - collection_route(current_model_name.pluralize.underscore, type) - end - - def new_object_route(type) - collection_route(current_model_name.underscore, type, "new") - end - - def instance_route(name, object, type, action = nil) - send("#{action ? action + '_' : ''}#{url_helper_prefix}#{name}_#{type}", object) - end - - def collection_route(name, type, action = nil) - send("#{action ? action + '_' : ''}#{url_helper_prefix || collection_url_prefix}#{name}_#{type}", - *(parent? ? [parent_object] : [])) - end - end - end -end diff --git a/vendor/plugins/make_resourceful/lib/resourceful/maker.rb b/vendor/plugins/make_resourceful/lib/resourceful/maker.rb deleted file mode 100644 index adfd8e33..00000000 --- a/vendor/plugins/make_resourceful/lib/resourceful/maker.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'resourceful/builder' -require 'resourceful/base' - -module Resourceful - # This module is extended by the ActionController::Base class object. - # It provides the actual +make_resourceful+ method - # and sets up the controller so that everything will work. - module Maker - # Called automatically on ActionController::Base. - # Initializes various inheritable attributes. - def self.extended(base) - base.write_inheritable_attribute :resourceful_callbacks, {} - base.write_inheritable_attribute :resourceful_responses, {} - base.write_inheritable_attribute :parents, [] - base.write_inheritable_attribute :made_resourceful, false - end - - # :call-seq: - # make_resourceful(options = {}) { ... } - # - # This is the central method, and namesake, of make_resourceful. - # It takes a block and evaluates it in the context of a Builder, - # allowing the controller to be customized extensively. - # - # See Resourceful::Builder for documentation on the methods available - # in the context of the block. - # - # The only option currently available is <tt>:include</tt>. - # It takes an object that responds to to_proc - # (or an array of such objects) - # and evaluates that proc in the same context as the block. - # For example: - # - # make_resourceful :include => proc { actions :all } do - # before :show do - # current_object.current_user = current_user - # end - # end - # - # This is the same as: - # - # make_resourceful do - # actions :all - # before :show do - # current_object.current_user = current_user - # end - # end - # - def make_resourceful(options = {}, &block) - # :stopdoc: - include Resourceful::Base - # :startdoc: - - builder = Resourceful::Builder.new(self) - unless builder.inherited? - Resourceful::Base.made_resourceful.each { |proc| builder.instance_eval(&proc) } - end - Array(options[:include]).each { |proc| builder.instance_eval(&proc) } - builder.instance_eval(&block) - - builder.apply - - add_helpers - end - - # Returns whether or not make_resourceful has been called - # on this controller or any controllers it inherits from. - def made_resourceful? - read_inheritable_attribute(:made_resourceful) - end - - private - - def add_helpers - helper_method(:object_path, :objects_path, :new_object_path, :edit_object_path, - :object_url, :objects_url, :new_object_url, :edit_object_url, - :parent_path, :parent_url, - :current_objects, :current_object, :current_model, :current_model_name, - :namespaces, :instance_variable_name, :parent_names, :parent_name, - :parent?, :parent_model, :parent_object, :save_succeeded?) - end - end -end diff --git a/vendor/plugins/make_resourceful/lib/resourceful/response.rb b/vendor/plugins/make_resourceful/lib/resourceful/response.rb deleted file mode 100644 index f5f0b5c5..00000000 --- a/vendor/plugins/make_resourceful/lib/resourceful/response.rb +++ /dev/null @@ -1,33 +0,0 @@ -module Resourceful - # This is the class of the object passed to the Builder#response_for method. - # It shouldn't be used by users. - # - # The Response collects format procs - # and returns them with the format method, - # in the order they were given. - # For example: - # - # response.html { redirect_to '/' } - # response.xml { render :xml => current_object.to_xml } - # response.js - # response.formats #=> [[:html, #<Proc>], [:xml, #<Proc>], [:js, #<Proc>]] - # - # Note that the <tt>:js</tt> response is the empty proc - - # the same as <tt>proc {}</tt>. - class Response # :nodoc: - # Returns a list of pairs of formats and procs - # representing the formats passed to the response object. - # See class description. - attr :formats - - # Returns a new Response with no format data. - def initialize - @formats = [] - end - - # Used to dispatch the individual format methods. - def method_missing(name, &block) - @formats.push([name, block || proc {}]) unless @formats.any? {|n,b| n == name} - end - end -end diff --git a/vendor/plugins/make_resourceful/lib/resourceful/serialize.rb b/vendor/plugins/make_resourceful/lib/resourceful/serialize.rb deleted file mode 100644 index 08b08936..00000000 --- a/vendor/plugins/make_resourceful/lib/resourceful/serialize.rb +++ /dev/null @@ -1,181 +0,0 @@ -require 'resourceful/builder' - -module Resourceful - # This module contains mixin modules - # used to implement the object serialization - # used for the Builder#publish method. - # They can also be used to get serialized representations of objects - # in other contexts. - # - # Serialization makes use of duck typing. - # Each class that can be serialized - # (just Array and ActiveRecord::Base by default) - # implements the +serialize+ and +to_serializable+ methods. - # These methods are implemented differently by the different classes, - # but the semantics of the implementations are consistent, - # so they can be used consistently. - # - # +to_serializable+ returns an object that can be directly serialized - # with a call to +to_xml+, +to_yaml+, or +to_json+. - # This object is either a hash or an array, - # and all the elements are either values, like strings and integers, - # or other serializable objects. - # This is useful for getting a model into a simple data structure format. - # The +attributes+ argument uses the same semantics - # as the <tt>:attributes</tt> option for Builder#publish. - # For example: - # - # c = Cake.new(:flavor => 'chocolate', :text => 'Happy birthday, Chris!') - # c.recipient = User.new(:name => 'Chris', :password => 'not very secure') - # c.to_serializable [ - # :flavor, :text, - # :recipient => :name - # ] - # - # This would return the Ruby hash - # - # { :flavor => 'chocolate', :text => 'Happy birthday, Chris!', - # :user => {:name => 'Chris'} } - # - # +serialize+ takes a format (<tt>:xml</tt>, <tt>:yaml</tt>, or <tt>:json</tt> - see New Formats below) - # and a hash of options. - # The only option currently recognized is <tt>:attributes</tt>, - # which has the same semantics - # as the <tt>:attributes</tt> option for Builder#publish. - # +serialize+ returns a string containing the target - # serialized in the given format. - # For example: - # - # c = CandyBag.new(:title => 'jellybag') - # c.candies << Candy.new(:type => 'jellybean', :flavor => 'root beer') - # c.candies << Candy.new(:type => 'jellybean', :flavor => 'pear') - # c.candies << Candy.new(:type => 'licorice', :flavor => 'anisey') - # c.serialize :xml, :attributes => [:title, {:candies => [:type, :flavor]}] - # - # This would return a Ruby string containing - # - # <?xml version="1.0" encoding="UTF-8"?> - # <candy-bag> - # <title>jellybag - # - # - # jellybean - # root beer - # - # - # jellybean - # pear - # - # - # licorice - # anisey - # - # - # - # - module Serialize - - # Takes an attributes option in the form passed to Builder#publish - # and returns a hash (or nil, if attributes is nil) - # containing the same data, - # but in a more consistent format. - # All keys are converted to symbols, - # and all lists are converted to hashes. - # For example: - # - # Resourceful::Serialize.normalize_attributes([:foo, :bar, {"baz" => ["boom"]}]) - # #=> {"baz"=>["boom"], :foo=>nil, :bar=>nil} - # - def self.normalize_attributes(attributes) # :nodoc: - return nil if attributes.nil? - return {attributes.to_sym => nil} if String === attributes - return {attributes => nil} if !attributes.respond_to?(:inject) - - attributes.inject({}) do |hash, attr| - if Array === attr - hash[attr[0]] = attr[1] - hash - else - hash.merge normalize_attributes(attr) - end - end - end - - # This module contains the definitions of +serialize+ and +to_serializable+ - # that are included in ActiveRecord::Base. - module Model - # :call-seq: - # serialize format, options = {}, :attributes => [ ... ] - # - # See the module documentation for Serialize for details. - def serialize(format, options) - raise "Must specify :attributes option" unless options[:attributes] - hash = self.to_serializable(options[:attributes]) - root = self.class.to_s.underscore - if format == :xml - hash.send("to_#{format}", :root => root) - else - {root => hash}.send("to_#{format}") - end - end - - # See the module documentation for Serialize for details. - def to_serializable(attributes) - raise "Must specify attributes for #{self.inspect}.to_serializable" if attributes.nil? - - Serialize.normalize_attributes(attributes).inject({}) do |hash, (key, value)| - hash[key.to_s] = attr_hash_value(self.send(key), value) - hash - end - end - - private - - # Given an attribute value - # and a normalized (see above) attribute hash, - # returns the serializable form of that attribute. - def attr_hash_value(attr, sub_attributes) - if attr.respond_to?(:to_serializable) - attr.to_serializable(sub_attributes) - else - attr - end - end - end - - # This module contains the definitions of +serialize+ and +to_serializable+ - # that are included in ActiveRecord::Base. - module Array - # :call-seq: - # serialize format, options = {}, :attributes => [ ... ] - # - # See the module documentation for Serialize for details. - def serialize(format, options) - raise "Not all elements respond to to_serializable" unless all? { |e| e.respond_to? :to_serializable } - raise "Must specify :attributes option" unless options[:attributes] - - serialized = map { |e| e.to_serializable(options[:attributes]) } - root = first.class.to_s.pluralize.underscore - - if format == :xml - serialized.send("to_#{format}", :root => root) - else - {root => serialized}.send("to_#{format}") - end - end - - # See the module documentation for Serialize for details. - def to_serializable(attributes) - if first.respond_to?(:to_serializable) - attributes = Serialize.normalize_attributes(attributes) - map { |e| e.to_serializable(attributes) } - else - self - end - end - end - end -end - -class ActiveRecord::Base; include Resourceful::Serialize::Model; end -class Array; include Resourceful::Serialize::Array; end diff --git a/vendor/plugins/make_resourceful/spec/accessors_spec.rb b/vendor/plugins/make_resourceful/spec/accessors_spec.rb deleted file mode 100644 index aa0f1927..00000000 --- a/vendor/plugins/make_resourceful/spec/accessors_spec.rb +++ /dev/null @@ -1,473 +0,0 @@ -require File.dirname(__FILE__) + '/spec_helper' - -describe Resourceful::Default::Accessors, "#current_objects" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @objects = stub_list 5, 'object' - @model = stub - @controller.stubs(:current_model).returns(@model) - end - - it "should look up all objects in the current model" do - @model.expects(:find).with(:all).returns(@objects) - @controller.current_objects.should == @objects - end - - it "should cache the result, so subsequent calls won't run multiple queries" do - @model.expects(:find).once.returns(@objects) - @controller.current_objects - @controller.current_objects - end - - it "shouldn't run a query if @current_objects is set" do - @controller.instance_variable_set('@current_objects', @objects) - @model.expects(:find).never - @controller.current_objects.should == @objects - end -end - -describe Resourceful::Default::Accessors, "#load_objects" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @objects = stub_list 5, 'object' - @controller.stubs(:current_objects).returns(@objects) - @controller.stubs(:instance_variable_name).returns("posts") - end - - it "should set the current instance variable to the object collection" do - @controller.load_objects - @controller.instance_variable_get('@posts').should == @objects - end -end - -describe Resourceful::Default::Accessors, "#current_object on a plural controller" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.stubs(:plural?).returns(true) - @controller.stubs(:params).returns(:id => "12") - - @object = stub - @model = stub - @controller.stubs(:current_model).returns(@model) - end - - it "should look up the object specified by the :id parameter in the current model" do - @model.expects(:find).with('12').returns(@object) - @controller.current_object.should == @object - end - - it "should cache the result, so subsequent calls won't run multiple queries" do - @model.expects(:find).once.returns(@object) - @controller.current_object - @controller.current_object - end - - it "shouldn't run a query if @current_object is set" do - @controller.instance_variable_set('@current_object', @object) - @model.expects(:find).never - @controller.current_object.should == @object - end -end - -describe Resourceful::Default::Accessors, "#current_object on a singular controller" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.stubs(:plural?).returns(false) - @controller.stubs(:instance_variable_name).returns("post") - - @parent = stub('parent') - @controller.stubs(:parent_object).returns(@parent) - @controller.stubs(:parent?).returns(true) - - @object = stub - end - - it "should look up the instance object of the parent object" do - @parent.expects(:post).returns(@object) - @controller.current_object.should == @object - end -end - -describe Resourceful::Default::Accessors, "#load_object" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @object = stub - @controller.stubs(:current_object).returns(@object) - @controller.stubs(:instance_variable_name).returns("posts") - end - - it "should set the current singular instance variable to the current object" do - @controller.load_object - @controller.instance_variable_get('@post').should == @object - end -end - -describe Resourceful::Default::Accessors, "#build_object with a #build-able model" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @params = {:name => "Bob", :password => "hideously insecure"} - @controller.stubs(:object_parameters).returns(@params) - - @object = stub - @model = stub - @controller.stubs(:current_model).returns(@model) - - @model.stubs(:build).returns(@object) - end - - it "should return a new object built with current_model from the object parameters" do - @model.expects(:build).with(@params).returns(@object) - @controller.build_object.should == @object - end - - it "should make current_object return the newly built object" do - @controller.build_object - @controller.current_object.should == @object - end -end - -describe Resourceful::Default::Accessors, "#build_object with a non-#build-able model" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @params = {:name => "Bob", :password => "hideously insecure"} - @controller.stubs(:object_parameters).returns(@params) - - @controller.stubs(:singular?).returns(false) - @controller.stubs(:parent?).returns(false) - - @object = stub - @model = stub - @controller.stubs(:current_model).returns(@model) - - @model.stubs(:new).returns(@object) - end - - it "should return a new instance of the current_model built with the object parameters" do - @model.expects(:new).with(@params).returns(@object) - @controller.build_object.should == @object - end -end - -describe Resourceful::Default::Accessors, "#current_model_name" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.stubs(:controller_name).returns("funky_posts") - end - - it "should return the controller's name, singularized and camel-cased" do - @controller.current_model_name.should == "FunkyPost" - end -end - -describe Resourceful::Default::Accessors, "#namespaces" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @kontroller.stubs(:name).returns("FunkyStuff::Admin::Posts") - end - - it "should return an array of underscored symbols representing the namespaces of the controller class" do - @controller.namespaces.should == [:funky_stuff, :admin] - end - - it "should cache the result, so subsequent calls won't run multiple computations" do - @kontroller.expects(:name).once.returns("Posts") - @controller.namespaces - @controller.namespaces - end -end - -describe Resourceful::Default::Accessors, "#instance_variable_name" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.stubs(:controller_name).returns("posts") - end - - it "should return controller_name" do - @controller.instance_variable_name == "posts" - end -end - -describe Resourceful::Default::Accessors, "#current_model for a singular controller" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - stub_const :Post - @controller.stubs(:singular?).returns(true) - @controller.stubs(:current_model_name).returns("Post") - - @parent = stub('parent') - @controller.stubs(:parent_object).returns(@parent) - @controller.stubs(:parent?).returns(true) - end - - it "should return the constant named by current_model_name" do - @controller.current_model.should == Post - end -end - -describe Resourceful::Default::Accessors, "#current_model for a plural controller with no parent" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - stub_const :Post - @controller.stubs(:singular?).returns(false) - @controller.stubs(:current_model_name).returns("Post") - @controller.stubs(:parent?).returns(false) - end - - it "should return the constant named by current_model_name" do - @controller.current_model.should == Post - end -end - -describe Resourceful::Default::Accessors, "#object_parameters" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @params = {"crazy_user" => {:name => "Hampton", :location => "Canada"}} - @controller.stubs(:params).returns(@params) - @controller.stubs(:current_model_name).returns("CrazyUser") - end - - it "should return the element of the params hash with the name of the model" do - @controller.object_parameters.should == @params["crazy_user"] - end -end - -describe Resourceful::Default::Accessors, " with two parent classes set on the controller class and one parent parameter supplied" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @parents = %w{post comment} - @models = @parents.map(&:camelize).map(&method(:stub_const)) - @kontroller.write_inheritable_attribute(:parents, @parents) - @controller.stubs(:singular?).returns(false) - @controller.stubs(:instance_variable_name).returns('lines') - - @params = HashWithIndifferentAccess.new :post_id => 12 - @controller.stubs(:params).returns(@params) - - @post = stub('Post') - Post.stubs(:find).returns(@post) - - @model = stub - end - - it "should return true for #parent?" do - @controller.parent?.should be_true - end - - it "should return the string names of all the parents for #parent_names" do - @controller.parent_names.should == @parents - end - - it "should return the string name of the current parent for #parent_name" do - @controller.parent_name.should == 'post' - end - - it "should return the model class for #parent_model" do - @controller.parent_model.should == Post - end - - it "should return the parent object for #parent_object" do - Post.expects(:find).with(12).returns(@post) - @controller.parent_object.should == @post - end - - it "should cache the value of #parent_object so multiple calls won't cause multiple queries" do - Post.expects(:find).returns(@post).once - @controller.parent_object - @controller.parent_object - end - - it "should bind the parent object its proper instance variable" do - @controller.load_parent_object - @controller.instance_variable_get('@post').should == @post - end - - it "should return the parent-scoped model for #current_model" do - @post.stubs(:lines).returns(@model) - @controller.current_model.should == @model - end - - it "should return true for #ensure_parent_exists" do - @controller.expects(:render).never - @controller.ensure_parent_exists.should be_true - end -end - -describe Resourceful::Default::Accessors, " with two parent classes set on the controller class but no parent parameter supplied" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @parents = %w{post comment} - @models = @parents.map(&:camelize).map(&method(:stub_const)) - @kontroller.write_inheritable_attribute(:parents, @parents) - @controller.stubs(:params).returns({}) - @controller.stubs(:controller_name).returns('line') - stub_const('Line') - end - - it "should return false for #parent?" do - @controller.parent?.should be_false - end - - it "should return nil for #parent_name" do - @controller.parent_name.should be_nil - end - - it "should return the unscoped model for #current_model" do - @controller.current_model.should == Line - end - - it "should return false and render a 422 error for #ensure_parent_exists" do - @controller.expects(:render).with(has_entry(:status, 422)) - @controller.ensure_parent_exists.should be_false - end -end - -describe Resourceful::Default::Accessors, " with no parents" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.stubs(:parents).returns([]) - @controller.stubs(:current_model_name).returns('Line') - stub_const 'Line' - end - - it "should return false for #parent?" do - @controller.parent?.should be_false - end - - it "should return nil for #parent_name" do - @controller.parent_name.should be_nil - end - - it "should return the unscoped model for #current_model" do - @controller.current_model.should == Line - end -end - -describe Resourceful::Default::Accessors, " for a singular controller with a parent" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.stubs(:singular?).returns(true) - - @model = stub_model('Thing') - @model.send(:attr_accessor, :person_id) - @controller.stubs(:current_model).returns(@model) - - @person = stub_model('Person') - @person.stubs(:id).returns 42 - @controller.stubs(:parent_object).returns(@person) - @controller.stubs(:parent_name).returns('person') - @controller.stubs(:parent?).returns(true) - - @controller.stubs(:object_parameters).returns :thinginess => 12, :bacon => true - end - - it "should set assign the parent's id to a newly built object" do - thing = @controller.build_object - thing.thinginess.should == 12 - thing.person_id.should == @person.id - end -end - -describe Resourceful::Default::Accessors, "#save_succeeded!" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.save_succeeded! - end - - it "should make #save_succeeded? return true" do - @controller.save_succeeded?.should be_true - end -end - -describe Resourceful::Default::Accessors, "#save_failed!" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.save_failed! - end - - it "should make #save_succeeded? return false" do - @controller.save_succeeded?.should be_false - end -end - -describe Resourceful::Default::Accessors, " for a plural action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.stubs(:params).returns :action => "index" - end - - it "should know it's a plural action" do - @controller.should be_a_plural_action - end - - it "should know it's not a singular action" do - @controller.should_not be_a_singular_action - end -end - -describe Resourceful::Default::Accessors, " for a singular action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.stubs(:params).returns :action => "show" - end - - it "should know it's not a plural action" do - @controller.should_not be_a_plural_action - end - - it "should know it's a singular action" do - @controller.should be_a_singular_action - end -end - -describe Resourceful::Default::Accessors, " for a singular controller" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.stubs(:instance_variable_name).returns "post" - end - - it "should know it's not plural" do - @controller.should_not be_plural - end - - it "should know it's singular" do - @controller.should be_singular - end -end - -describe Resourceful::Default::Accessors, " for a plural controller" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Accessors - @controller.stubs(:instance_variable_name).returns "posts" - end - - it "should know it's plural" do - @controller.should be_plural - end - - it "should know it's not singular" do - @controller.should_not be_singular - end -end diff --git a/vendor/plugins/make_resourceful/spec/actions_spec.rb b/vendor/plugins/make_resourceful/spec/actions_spec.rb deleted file mode 100644 index f0bcf15f..00000000 --- a/vendor/plugins/make_resourceful/spec/actions_spec.rb +++ /dev/null @@ -1,283 +0,0 @@ -require File.dirname(__FILE__) + '/spec_helper' - -describe Resourceful::Default::Actions, " index action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Actions - [:load_objects, :before, :response_for].each(&@controller.method(:stubs)) - end - - after(:each) { @controller.index } - - it "should load the object collection" do - @controller.expects(:load_objects) - end - - it "should call the before :index callback" do - @controller.expects(:before).with(:index) - end - - it "should run the response for index" do - @controller.expects(:response_for).with(:index) - end -end - -describe Resourceful::Default::Actions, " show action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Actions - [:load_object, :before, :response_for].each(&@controller.method(:stubs)) - end - - after(:each) { @controller.show } - - it "should load the instance object" do - @controller.expects(:load_object) - end - - it "should call the before :show callback" do - @controller.expects(:before).with(:show) - end - - it "should run the response for show" do - @controller.expects(:response_for).with(:show) - end - - it "should run the response for show failing if an exception is raised" do - @controller.stubs(:load_object).raises("Oh no!") - @controller.expects(:response_for).with(:show_fails) - end -end - -describe Resourceful::Default::Actions, " successful create action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Actions - [:build_object, :load_object, :before, :after, - :save_succeeded!, :response_for].each(&@controller.method(:stubs)) - @object = stub :save => true - @controller.stubs(:current_object).returns(@object) - end - - after(:each) { @controller.create } - - it "should build the object from the POSTed parameters" do - @controller.expects(:build_object) - end - - it "should load the instance object" do - @controller.expects(:load_object) - end - - it "should call the before :create callback" do - @controller.expects(:before).with(:create) - end - - it "should try to save the object" do - @object.expects(:save).returns(true) - end - - it "should record the successful save" do - @controller.expects(:save_succeeded!) - end - - it "should call the after :create callback" do - @controller.expects(:after).with(:create) - end - - it "should run the response for create" do - @controller.expects(:response_for).with(:create) - end -end - -describe Resourceful::Default::Actions, " unsuccessful create action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Actions - [:build_object, :load_object, :before, :after, - :save_failed!, :response_for].each(&@controller.method(:stubs)) - @object = stub :save => false - @controller.stubs(:current_object).returns(@object) - end - - after(:each) { @controller.create } - - it "should record the unsuccessful save" do - @controller.expects(:save_failed!) - end - - it "should call the after :create_fails callback" do - @controller.expects(:after).with(:create_fails) - end - - it "should run the response for create failing" do - @controller.expects(:response_for).with(:create_fails) - end -end - -describe Resourceful::Default::Actions, " successful update action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Actions - [:load_object, :before, :after, :object_parameters, - :save_succeeded!, :response_for].each(&@controller.method(:stubs)) - @object = stub :update_attributes => true - @controller.stubs(:current_object).returns(@object) - end - - after(:each) { @controller.update } - - it "should load the instance object" do - @controller.expects(:load_object) - end - - it "should call the before :update callback" do - @controller.expects(:before).with(:update) - end - - it "should try to update the object with the POSTed attributes" do - @controller.expects(:object_parameters).returns(:params => "stuff") - @object.expects(:update_attributes).with(:params => "stuff").returns(true) - end - - it "should record the successful save" do - @controller.expects(:save_succeeded!) - end - - it "should call the after :update callback" do - @controller.expects(:after).with(:update) - end - - it "should run the response for update" do - @controller.expects(:response_for).with(:update) - end -end - -describe Resourceful::Default::Actions, " unsuccessful update action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Actions - [:load_object, :before, :after, :object_parameters, - :save_failed!, :response_for].each(&@controller.method(:stubs)) - @object = stub :update_attributes => false - @controller.stubs(:current_object).returns(@object) - end - - after(:each) { @controller.update } - - it "should record the unsuccessful save" do - @controller.expects(:save_failed!) - end - - it "should call the after :update_fails callback" do - @controller.expects(:after).with(:update_fails) - end - - it "should run the response for update failing" do - @controller.expects(:response_for).with(:update_fails) - end -end - -describe Resourceful::Default::Actions, " new action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Actions - [:build_object, :load_object, - :before, :response_for].each(&@controller.method(:stubs)) - end - - after(:each) { @controller.new } - - it "should build the object from the POSTed parameters" do - @controller.expects(:build_object) - end - - it "should load the instance object" do - @controller.expects(:load_object) - end - - it "should call the before :new callback" do - @controller.expects(:before).with(:new) - end - - it "should run the response for new" do - @controller.expects(:response_for).with(:new) - end -end - -describe Resourceful::Default::Actions, " edit action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Actions - [:load_object, :before, :response_for].each(&@controller.method(:stubs)) - end - - after(:each) { @controller.edit } - - it "should load the instance object" do - @controller.expects(:load_object) - end - - it "should call the before :edit callback" do - @controller.expects(:before).with(:edit) - end - - it "should run the response for edit" do - @controller.expects(:response_for).with(:edit) - end -end - -describe Resourceful::Default::Actions, " successful destroy action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Actions - [:load_object, :before, - :after, :response_for].each(&@controller.method(:stubs)) - @object = stub :destroy => true - @controller.stubs(:current_object).returns(@object) - end - - after(:each) { @controller.destroy } - - it "should load the instance object" do - @controller.expects(:load_object) - end - - it "should call the before :destroy callback" do - @controller.expects(:before).with(:destroy) - end - - it "should try to destroy the object" do - @object.expects(:destroy).returns(true) - end - - it "should call the after :destroy callback" do - @controller.expects(:after).with(:destroy) - end - - it "should run the response for destroy" do - @controller.expects(:response_for).with(:destroy) - end -end - -describe Resourceful::Default::Actions, " unsuccessful destroy action" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Actions - [:load_object, :before, - :after, :response_for].each(&@controller.method(:stubs)) - @object = stub :destroy => false - @controller.stubs(:current_object).returns(@object) - end - - after(:each) { @controller.destroy } - - it "should call the after :destroy_fails callback" do - @controller.expects(:after).with(:destroy_fails) - end - - it "should run the response for destroy failing" do - @controller.expects(:response_for).with(:destroy_fails) - end -end - diff --git a/vendor/plugins/make_resourceful/spec/base_spec.rb b/vendor/plugins/make_resourceful/spec/base_spec.rb deleted file mode 100644 index f0675dba..00000000 --- a/vendor/plugins/make_resourceful/spec/base_spec.rb +++ /dev/null @@ -1,12 +0,0 @@ -require File.dirname(__FILE__) + '/spec_helper' - -describe Resourceful::Base, ".made_resourceful" do - before(:all) { @original_blocks = Resourceful::Base.made_resourceful.dup } - before(:each) { Resourceful::Base.made_resourceful.replace [] } - after(:all) { Resourceful::Base.made_resourceful.replace @original_blocks } - - it "should store blocks when called with blocks and return them when called without a block" do - 5.times { Resourceful::Base.made_resourceful(&should_be_called) } - Resourceful::Base.made_resourceful.each(&:call) - end -end diff --git a/vendor/plugins/make_resourceful/spec/builder_spec.rb b/vendor/plugins/make_resourceful/spec/builder_spec.rb deleted file mode 100644 index b46f5de6..00000000 --- a/vendor/plugins/make_resourceful/spec/builder_spec.rb +++ /dev/null @@ -1,332 +0,0 @@ -require File.dirname(__FILE__) + '/spec_helper' - -describe Resourceful::Builder, " applied without any modification" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - end - - it "should remove all resourceful actions" do - @kontroller.expects(:send).with do |name, action_module| - name == :include && (action_module.instance_methods & Resourceful::ACTIONS.map(&:to_s)).empty? - end - @builder.apply - end - - it "shouldn't un-hide any actions" do - @builder.apply - @kontroller.hidden_actions.should == Resourceful::ACTIONS - end - - it "shouldn't set any callbacks" do - @builder.apply - callbacks.should == {:before => {}, :after => {}} - end - - it "shouldn't set any responses" do - @builder.apply - responses.should be_empty - end - - it "shouldn't set any parents" do - @builder.apply - parents.should be_empty - end - - it "should set the controller as made_resourceful" do - @builder.apply - @kontroller.read_inheritable_attribute(:made_resourceful).should be_true - end - - it "should set load_parent_object as a before_filter for no actions" do - @kontroller.expects(:before_filter).with(:load_parent_object, :only => []) - @builder.apply - end -end - -describe Resourceful::Builder, " with some actions set" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - @actions = [:show, :index, :new, :create] - @builder.actions *@actions - end - - it "should include the given actions" do - @kontroller.expects(:send).with do |name, action_module| - name == :include && (action_module.instance_methods & Resourceful::ACTIONS.map(&:to_s)).sort == - @actions.map(&:to_s).sort - end - @builder.apply - end - - it "should un-hide the given actions" do - @builder.apply - (@kontroller.hidden_actions & @actions).should be_empty - end - - it "should set load_parent_object as a before_filter for the given actions" do - @kontroller.expects(:before_filter).with(:load_parent_object, :only => [:show, :index, :new, :create]) - @builder.apply - end -end - -describe Resourceful::Builder, " with all actions set for a plural controller" do - include ControllerMocks - before :each do - mock_kontroller - @kontroller.class_eval { def plural?; true; end } - create_builder - @builder.actions :all - end - - it "should include all actions" do - @kontroller.expects(:send).with do |name, action_module| - name == :include && (action_module.instance_methods & Resourceful::ACTIONS.map(&:to_s)).sort == - Resourceful::ACTIONS.map(&:to_s).sort - end - @builder.apply - end -end - -describe Resourceful::Builder, " with all actions set for a singular controller" do - include ControllerMocks - before :each do - mock_kontroller - @kontroller.class_eval { def plural?; false; end } - create_builder - @builder.actions :all - end - - it "should include all singular actions" do - @kontroller.expects(:send).with do |name, action_module| - name == :include && (action_module.instance_methods & Resourceful::ACTIONS.map(&:to_s)).sort == - Resourceful::SINGULAR_ACTIONS.map(&:to_s).sort - end - @builder.apply - end -end - -describe Resourceful::Builder, " with several before and after callbacks set" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - @builder.before(:create, :update, 'destroy', &(should_be_called { times(3) })) - @builder.after('index', &should_be_called) - @builder.after(:update, &should_be_called) - @builder.apply - end - - it "should save the callbacks as the :resourceful_callbacks inheritable_attribute" do - callbacks[:before][:create].each(&:call) - callbacks[:before][:update].each(&:call) - callbacks[:before][:destroy].each(&:call) - callbacks[:after][:index].each(&:call) - callbacks[:after][:update].each(&:call) - end -end - -describe Resourceful::Builder, " with chained before and after callbacks" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - @before_value = '' - @builder.before(:index, &lambda { @before_value += 'A' }) - @builder.before(:index, &lambda { @before_value += 'B' }) - - @after_value = '' - @builder.after(:index, &lambda { @after_value += 'A' }) - @builder.after(:index, &lambda { @after_value += 'B' }) - @builder.apply - end - - it "should save as array in the :resourceful_callbacks inheritable_attribute and execute in order" do - callbacks[:before][:index].each { |callback| callback.call } - @before_value.should == 'AB' - callbacks[:after][:index].each { |callback| callback.call } - @after_value.should == 'AB' - end -end - -describe Resourceful::Builder, " with responses set for several formats" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - @builder.response_for('create') do |f| - f.html(&should_be_called) - f.js(&should_be_called) - f.yaml(&should_be_called) - f.xml(&should_be_called) - f.txt(&should_be_called) - end - @builder.response_for(:remove_failed, 'update') do |f| - f.yaml(&(should_be_called { times(2) })) - f.png(&(should_be_called { times(2) })) - end - @builder.apply - end - - it "should save the responses as the :resourceful_responses inheritable_attribute" do - responses[:create].map(&:first).should == [:html, :js, :yaml, :xml, :txt] - responses[:create].map(&:last).each(&:call) - - responses[:remove_failed].map(&:first).should == [:yaml, :png] - responses[:remove_failed].map(&:last).each(&:call) - - responses[:update].map(&:first).should == [:yaml, :png] - responses[:update].map(&:last).each(&:call) - end -end - -describe Resourceful::Builder, " with a response set for the default format" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - @builder.response_for('index', &should_be_called) - @builder.apply - end - - it "should save the response as a response for HTML in the :resourceful_responses inheritable_attribute" do - responses[:index].map(&:first).should == [:html] - responses[:index].map(&:last).each(&:call) - end -end - -describe Resourceful::Builder, " with a response set for no actions" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - end - - it "should raise an error" do - lambda { @builder.response_for {} }.should raise_error("Must specify one or more actions for response_for.") - end -end - -describe Resourceful::Builder, " publishing without an attributes hash" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - end - - it "should raise an error" do - proc { @builder.publish :xml, :yaml }.should raise_error("Must specify :attributes option") - end -end - -describe Resourceful::Builder, " publishing several formats" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - - @model = stub_model("Thing") - @kontroller.stubs(:current_object).returns(@model) - - @models = (1..5).map { stub_model("Thing") } - @kontroller.stubs(:current_objects).returns(@models) - - @builder.publish :yaml, :json, 'xml', :additional => 'option', :attributes => [:name, :stuff] - @builder.apply - end - - it "should add a list of types as responses for index and show" do - responses[:index].map(&:first).should == [:yaml, :json, :xml] - responses[:show].map(&:first).should == [:yaml, :json, :xml] - end - - it "should respond by rendering the serialized model with the proper type, passing along un-recognized options" do - @model.expects(:serialize).with(:yaml, :additional => 'option', :attributes => [:name, :stuff]).returns('serialized') - @kontroller.expects(:render).with(:text => 'serialized') - @kontroller.instance_eval(&responses[:index].find { |type, _| type == :yaml }[1]) - end - - it "should respond render XML and JSON with the proper action" do - @model.expects(:serialize).with(:xml, :additional => 'option', :attributes => [:name, :stuff]).returns('XML serialized') - @model.expects(:serialize).with(:json, :additional => 'option', :attributes => [:name, :stuff]).returns('JSON serialized') - @kontroller.expects(:render).with(:xml => 'XML serialized') - @kontroller.expects(:render).with(:json => 'JSON serialized') - - @kontroller.instance_eval(&responses[:index].find { |type, _| type == :xml }[1]) - @kontroller.instance_eval(&responses[:index].find { |type, _| type == :json }[1]) - end - - it "should render current_objects if the action is plural" do - @kontroller.stubs(:plural_action?).returns(true) - @models.expects(:serialize).with(:yaml, :additional => 'option', :attributes => [:name, :stuff]).returns('serialized') - @kontroller.expects(:render).with(:text => 'serialized') - @kontroller.instance_eval(&responses[:index].find { |type, _| type == :yaml }[1]) - end -end - -describe Resourceful::Builder, " publishing only to #show" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - - @model = stub_model("Thing") - @kontroller.stubs(:current_object).returns(@model) - - @builder.publish :json, :yaml, :only => :show, :attributes => [:name, :stuff] - @builder.apply - end - - it "should add responses for show" do - responses[:show].map(&:first).should == [:json, :yaml] - end - - it "shouldn't add responses for index" do - responses[:index].should be_nil - end - - it "shouldn't pass the :only option to the serialize call" do - @model.expects(:serialize).with(:yaml, :attributes => [:name, :stuff]) - @kontroller.stubs(:render) - @kontroller.instance_eval(&responses[:show].find { |type, _| type == :yaml }[1]) - end -end - -describe Resourceful::Builder, " publishing in addition to other responses" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - - @builder.response_for(:index) {} - @builder.publish :json, :yaml, :attributes => [:name, :stuff] - @builder.response_for :show do |f| - f.html {} - f.js {} - end - @builder.apply - end - - it "should add published responses in addition to pre-existing ones" do - responses[:show].map(&:first).should == [:html, :js, :json, :yaml] - responses[:index].map(&:first).should == [:html, :json, :yaml] - end -end - -describe Resourceful::Builder, " belonging to several parents" do - include ControllerMocks - before :each do - mock_kontroller - create_builder - - @builder.belongs_to :post, :blat, :stang - @builder.apply - end - - it "should save the parents as the :parents inheritable_attribute" do - parents.should == ['post', 'blat', 'stang'] - end -end diff --git a/vendor/plugins/make_resourceful/spec/callbacks_spec.rb b/vendor/plugins/make_resourceful/spec/callbacks_spec.rb deleted file mode 100644 index 669f56de..00000000 --- a/vendor/plugins/make_resourceful/spec/callbacks_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -require File.dirname(__FILE__) + '/spec_helper' - -describe Resourceful::Default::Callbacks, " with a few callbacks" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Callbacks - end - - it "should fire the :before callback with the given name when #before is called" do - callbacks[:before] = { :create => [ should_be_called ] } - @controller.before(:create) - end - - it "should fire the :after callback with the given name when #after is called" do - callbacks[:after] = { :index => [ should_be_called ] } - @controller.after("index") - end -end - -describe Resourceful::Default::Callbacks, " with a few responses" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Callbacks - responses[:create_failed] = [[:html, nil], [:js, nil]] - responses[:create] = [[:html, proc { "create html" }], [:xml, proc { @xml }]] - @controller.instance_variable_set('@xml', 'create XML') - @response = Resourceful::Response.new - end - - it "should respond to each format with a call to the given block when #response_for is called" do - @controller.expects(:respond_to).yields(@response) - @controller.response_for(:create_failed) - @response.formats[0][0].should == :html - @response.formats[0][1].call.should be_nil - - @response.formats[1][0].should == :js - @response.formats[1][1].call.should be_nil - end - - it "should properly scope blocks when #response_for is called" do - @controller.expects(:respond_to).yields(@response) - @controller.response_for(:create) - @response.formats[0][0].should == :html - @response.formats[0][1].call.should == "create html" - - @response.formats[1][0].should == :xml - - # This value comes from the instance variable in @controller. - # Having it be "create XML" ensures that the block was properly scoped. - @response.formats[1][1].call.should == "create XML" - end -end - -describe Resourceful::Default::Callbacks, "#scope" do - include ControllerMocks - before(:each) { mock_controller Resourceful::Default::Callbacks } - - it "should re-bind the block to the controller's context" do - block = proc { @var } - @controller.instance_variable_set('@var', 'value') - - block.call.should == nil - @controller.scope(block).call.should == 'value' - end - - it "should make the block empty if it's passed in as nil" do - @controller.scope(nil).call.should == nil - end -end - - diff --git a/vendor/plugins/make_resourceful/spec/integration_spec.rb b/vendor/plugins/make_resourceful/spec/integration_spec.rb deleted file mode 100644 index 4ab4c366..00000000 --- a/vendor/plugins/make_resourceful/spec/integration_spec.rb +++ /dev/null @@ -1,396 +0,0 @@ -require File.dirname(__FILE__) + '/spec_helper' - -describe "ThingsController", "with all the resourceful actions", :type => :integration do - include RailsMocks - #inherit Test::Unit::TestCase - before :each do - mock_resourceful do - actions :all - end - @objects = stub_list(5, 'Thing') do |t| - [:destroy, :save, :update_attributes].each { |m| t.stubs(m).returns(true) } - t.stubs(:to_param).returns('12') - end - @object = @objects.first - Thing.stubs(:find).returns(@object) - Thing.stubs(:new).returns(@object) - end - - ## Default responses - - (Resourceful::ACTIONS - Resourceful::MODIFYING_ACTIONS).each(&method(:should_render_html)) - Resourceful::ACTIONS.each(&method(:should_render_js)) - Resourceful::ACTIONS.each(&method(:shouldnt_render_xml)) - - ## Specs for #index - - it "should find all records on GET /things" do - Thing.expects(:find).with(:all).returns(@objects) - get :index - end - - it "should return a list of objects for #current_objects after GET /things" do - Thing.stubs(:find).returns(@objects) - get :index - controller.current_objects.should == @objects - end - - it "should assign @things to a list of objects for GET /things" do - Thing.stubs(:find).returns(@objects) - get :index - assigns(:things).should == @objects - end - - ## Specs for #show - - it "should find the record with id 12 on GET /things/12" do - Thing.expects(:find).with('12').returns(@object) - get :show, :id => 12 - end - - it "should return an object for #current_object after GET /things/12" do - Thing.stubs(:find).returns(@object) - get :show, :id => 12 - controller.current_object.should == @object - end - - it "should assign @thing to an object for GET /things/12" do - Thing.stubs(:find).returns(@object) - get :show, :id => 12 - assigns(:thing).should == @object - end - - ## Specs for #edit - - it "should find the record with id 12 on GET /things/12/edit" do - Thing.expects(:find).with('12').returns(@object) - get :edit, :id => 12 - end - - it "should return an object for #current_object after GET /things/12/edit" do - Thing.stubs(:find).returns(@object) - get :edit, :id => 12 - controller.current_object.should == @object - end - - it "should assign @thing to an object for GET /things/12/edit" do - Thing.stubs(:find).returns(@object) - get :edit, :id => 12 - assigns(:thing).should == @object - end - - ## Specs for #new - - it "should create a new object from params[:thing] for GET /things/new" do - Thing.expects(:new).with('name' => "Herbert the thing").returns(@object) - get :new, :thing => {:name => "Herbert the thing"} - end - - it "should create a new object even if there aren't any params for GET /things/new" do - Thing.expects(:new).with(nil).returns(@object) - get :new - end - - it "should return the new object for #current_object after GET /things/new" do - Thing.stubs(:new).returns(@object) - get :new - controller.current_object.should == @object - end - - it "should assign @thing to the new object for GET /things/new" do - Thing.stubs(:new).returns(@object) - get :new - assigns(:thing).should == @object - end - - ## Specs for #create - - it "should create a new object from params[:thing] for POST /things" do - Thing.expects(:new).with('name' => "Herbert the thing").returns(@object) - post :create, :thing => {:name => "Herbert the thing"} - end - - it "should create a new object even if there aren't any params for POST /things" do - Thing.expects(:new).with(nil).returns(@object) - post :create - end - - it "should return the new object for #current_object after POST /things" do - Thing.stubs(:new).returns(@object) - post :create - controller.current_object.should == @object - end - - it "should assign @thing to the new object for POST /things" do - Thing.stubs(:new).returns(@object) - post :create - assigns(:thing).should == @object - end - - it "should save the new object for POST /things" do - Thing.stubs(:new).returns(@object) - @object.expects(:save) - post :create - end - - it "should set an appropriate flash notice for a successful POST /things" do - Thing.stubs(:new).returns(@object) - post :create - flash[:notice].should == "Create successful!" - end - - it "should redirect to the new object for a successful POST /things" do - Thing.stubs(:new).returns(@object) - post :create - response.should redirect_to('/things/12') - end - - it "should set an appropriate flash error for an unsuccessful POST /things" do - Thing.stubs(:new).returns(@object) - @object.stubs(:save).returns(false) - post :create - flash[:error].should == "There was a problem!" - end - - it "should give a failing response for an unsuccessful POST /things" do - Thing.stubs(:new).returns(@object) - @object.stubs(:save).returns(false) - post :create - response.should_not be_success - response.code.should == '422' - end - - it "should render the #new template for an unsuccessful POST /things" do - Thing.stubs(:new).returns(@object) - @object.stubs(:save).returns(false) - post :create - response.should render_template('new') - end - - ## Specs for #update - - it "should find the record with id 12 on PUT /things/12" do - Thing.expects(:find).with('12').returns(@object) - put :update, :id => 12 - end - - it "should return an object for #current_object after PUT /things/12" do - Thing.stubs(:find).returns(@object) - put :update, :id => 12 - controller.current_object.should == @object - end - - it "should assign @thing to an object for PUT /things/12" do - Thing.stubs(:find).returns(@object) - put :update, :id => 12 - assigns(:thing).should == @object - end - - it "should update the new object for PUT /things/12" do - Thing.stubs(:find).returns(@object) - @object.expects(:update_attributes).with('name' => "Jorje") - put :update, :id => 12, :thing => {:name => "Jorje"} - end - - it "should set an appropriate flash notice for a successful PUT /things/12" do - Thing.stubs(:find).returns(@object) - put :update, :id => 12 - flash[:notice].should == "Save successful!" - end - - it "should redirect to the updated object for a successful PUT /things/12" do - Thing.stubs(:find).returns(@object) - put :update, :id => 12 - response.should redirect_to('/things/12') - end - - it "should set an appropriate flash error for an unsuccessful PUT /things/12" do - Thing.stubs(:find).returns(@object) - @object.stubs(:update_attributes).returns(false) - put :update, :id => 12 - flash[:error].should == "There was a problem saving!" - end - - it "should give a failing response for an unsuccessful PUT /things/12" do - Thing.stubs(:find).returns(@object) - @object.stubs(:update_attributes).returns(false) - put :update, :id => 12 - response.should_not be_success - response.code.should == '422' - end - - it "should render the #edit template for an unsuccessful PUT /things/12" do - Thing.stubs(:find).returns(@object) - @object.stubs(:update_attributes).returns(false) - put :update, :id => 12 - response.should render_template('edit') - end - - ## Specs for #destroy - - it "should find the record with id 12 on DELETE /things/12" do - Thing.expects(:find).with('12').returns(@object) - delete :destroy, :id => 12 - end - - it "should return an object for #current_object after DELETE /things/12" do - Thing.stubs(:find).returns(@object) - delete :destroy, :id => 12 - controller.current_object.should == @object - end - - it "should assign @thing to an object for DELETE /things/12" do - Thing.stubs(:find).returns(@object) - delete :destroy, :id => 12 - assigns(:thing).should == @object - end - - it "should destroy the new object for DELETE /things/12" do - Thing.stubs(:find).returns(@object) - @object.expects(:destroy) - delete :destroy, :id => 12 - end - - it "should set an appropriate flash notice for a successful DELETE /things/12" do - Thing.stubs(:find).returns(@object) - delete :destroy, :id => 12 - flash[:notice].should == "Record deleted!" - end - - it "should redirect to the object list for a successful DELETE /things/12" do - Thing.stubs(:find).returns(@object) - delete :destroy, :id => 12 - response.should redirect_to('/things') - end - - it "should set an appropriate flash error for an unsuccessful DELETE /things/12" do - Thing.stubs(:find).returns(@object) - @object.stubs(:destroy).returns(false) - delete :destroy, :id => 12 - flash[:error].should == "There was a problem deleting!" - end - - it "should give a failing response for an unsuccessful DELETE /things/12" do - Thing.stubs(:find).returns(@object) - @object.stubs(:destroy).returns(false) - delete :destroy, :id => 12 - response.should_not be_success - end - - it "should redirect to the previous page for an unsuccessful DELETE /things/12" do - Thing.stubs(:find).returns(@object) - @object.stubs(:destroy).returns(false) - delete :destroy, :id => 12 - response.should redirect_to(:back) - end -end - -describe "ThingsController", "with several parent objects", :type => :integration do - include RailsMocks - before :each do - mock_resourceful do - actions :all - belongs_to :person, :category - end - stub_const 'Person' - stub_const 'Category' - - @objects = stub_list(5, 'Thing') do |t| - t.stubs(:save).returns(true) - end - @object = @objects.first - @person = stub('Person') - @category = stub('Category') - @fake_model = stub('parent_object.things') - end - - ## No parent ids - - it "should find all things on GET /things" do - Thing.expects(:find).with(:all).returns(@objects) - get :index - controller.current_objects.should == @objects - end - - it "should find the thing with id 12 regardless of scoping on GET /things/12" do - Thing.expects(:find).with('12').returns(@object) - get :show, :id => 12 - controller.current_object.should == @object - end - - it "should create a new thing without a person on POST /things" do - Thing.expects(:new).with('name' => "Lamp").returns(@object) - post :create, :thing => {:name => "Lamp"} - controller.current_object.should == @object - end - - ## Person ids - - it "should assign the proper parent variables and accessors to the person with id 4 for GET /people/4/things" do - Person.stubs(:find).returns(@person) - @person.stubs(:things).returns(@fake_model) - @fake_model.stubs(:find).with(:all).returns(@objects) - get :index, :person_id => 4 - controller.parent_object.should == @person - assigns(:person).should == @person - end - - it "should find all the things belonging to the person with id 4 on GET /people/4/things" do - Person.expects(:find).with('4').returns(@person) - @person.expects(:things).at_least_once.returns(@fake_model) - @fake_model.expects(:find).with(:all).returns(@objects) - get :index, :person_id => 4 - controller.current_objects.should == @objects - end - - it "should find the thing with id 12 if it belongs to the person with id 4 on GET /person/4/things/12" do - Person.expects(:find).with('4').returns(@person) - @person.expects(:things).at_least_once.returns(@fake_model) - @fake_model.expects(:find).with('12').returns(@object) - get :show, :person_id => 4, :id => 12 - controller.current_object.should == @object - end - - it "should create a new thing belonging to the person with id 4 on POST /person/4/things" do - Person.expects(:find).with('4').returns(@person) - @person.expects(:things).at_least_once.returns(@fake_model) - @fake_model.expects(:build).with('name' => 'Lamp').returns(@object) - post :create, :person_id => 4, :thing => {:name => "Lamp"} - controller.current_object.should == @object - end - - ## Category ids - - it "should assign the proper parent variables and accessors to the category with id 4 for GET /people/4/things" do - Category.stubs(:find).returns(@category) - @category.stubs(:things).returns(@fake_model) - @fake_model.stubs(:find).with(:all).returns(@objects) - get :index, :category_id => 4 - controller.parent_object.should == @category - assigns(:category).should == @category - end - - it "should find all the things belonging to the category with id 4 on GET /people/4/things" do - Category.expects(:find).with('4').returns(@category) - @category.expects(:things).at_least_once.returns(@fake_model) - @fake_model.expects(:find).with(:all).returns(@objects) - get :index, :category_id => 4 - controller.current_objects.should == @objects - end - - it "should find the thing with id 12 if it belongs to the category with id 4 on GET /category/4/things/12" do - Category.expects(:find).with('4').returns(@category) - @category.expects(:things).at_least_once.returns(@fake_model) - @fake_model.expects(:find).with('12').returns(@object) - get :show, :category_id => 4, :id => 12 - controller.current_object.should == @object - end - - it "should create a new thing belonging to the category with id 4 on POST /category/4/things" do - Category.expects(:find).with('4').returns(@category) - @category.expects(:things).at_least_once.returns(@fake_model) - @fake_model.expects(:build).with('name' => 'Lamp').returns(@object) - post :create, :category_id => 4, :thing => {:name => "Lamp"} - controller.current_object.should == @object - end -end diff --git a/vendor/plugins/make_resourceful/spec/maker_spec.rb b/vendor/plugins/make_resourceful/spec/maker_spec.rb deleted file mode 100644 index f295396c..00000000 --- a/vendor/plugins/make_resourceful/spec/maker_spec.rb +++ /dev/null @@ -1,91 +0,0 @@ -require File.dirname(__FILE__) + '/spec_helper' - -describe Resourceful::Maker, "when extended" do - include ControllerMocks - before(:each) { mock_kontroller } - - it "should create an empty, inheritable callbacks hash" do - @kontroller.read_inheritable_attribute(:resourceful_callbacks).should == {} - end - - it "should create an empty, inheritable responses hash" do - @kontroller.read_inheritable_attribute(:resourceful_responses).should == {} - end - - it "should create an empty, inheritable parents array" do - @kontroller.read_inheritable_attribute(:parents).should == [] - end - - it "should create a made_resourceful variable set to false" do - @kontroller.read_inheritable_attribute(:made_resourceful).should be_false - end - - it "should create a made_resourceful? method on the controller that returns the variable" do - @kontroller.should_not be_made_resourceful - @kontroller.write_inheritable_attribute(:made_resourceful, true) - @kontroller.should be_made_resourceful - end -end - -describe Resourceful::Maker, "when made_resourceful" do - include ControllerMocks - before(:each) do - mock_kontroller - mock_builder - end - - it "should include Resourceful::Base" do - @kontroller.expects(:include).with(Resourceful::Base) - @kontroller.make_resourceful {} - end - - it "should use Resourceful::Builder to build the controller" do - Resourceful::Builder.expects(:new).with(@kontroller).returns(@builder) - @kontroller.make_resourceful {} - end - - it "should evaluate the made_resourceful callbacks in the context of the builder" do - procs = (1..5).map { should_be_called { with(@builder) } } - Resourceful::Base.stubs(:made_resourceful).returns(procs) - @kontroller.make_resourceful {} - end - - it "should evaluate the :include callback in the context of the builder" do - @kontroller.make_resourceful(:include => should_be_called { with(@builder) }) {} - end - - it "should evaluate the given block in the context of the builder" do - @kontroller.make_resourceful(&(should_be_called { with(@builder) })) - end -end - -describe Resourceful::Maker, "when made_resourceful with an inherited controller" do - include ControllerMocks - before(:each) do - mock_kontroller - mock_builder :inherited - end - - it "should include Resourceful::Base" do - @kontroller.expects(:include).with(Resourceful::Base) - @kontroller.make_resourceful {} - end - - it "should use Resourceful::Builder to build the controller" do - Resourceful::Builder.expects(:new).with(@kontroller).returns(@builder) - @kontroller.make_resourceful {} - end - - it "should not evaluate the made_resourceful callbacks in the context of the builder" do - Resourceful::Base.expects(:made_resourceful).never - @kontroller.make_resourceful {} - end - - it "should evaluate the :include callback in the context of the builder" do - @kontroller.make_resourceful(:include => should_be_called { with(@builder) }) {} - end - - it "should evaluate the given block in the context of the builder" do - @kontroller.make_resourceful(&(should_be_called { with(@builder) })) - end -end diff --git a/vendor/plugins/make_resourceful/spec/response_spec.rb b/vendor/plugins/make_resourceful/spec/response_spec.rb deleted file mode 100644 index c659d954..00000000 --- a/vendor/plugins/make_resourceful/spec/response_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require File.dirname(__FILE__) + '/spec_helper' - -describe Resourceful::Response, "when first created" do - before(:each) { @response = Resourceful::Response.new } - - it "should have an empty formats array" do - @response.formats.should == [] - end -end - -describe Resourceful::Response, "with a few formats" do - before :each do - @response = Resourceful::Response.new - @response.html - @response.js {'javascript'} - @response.xml {'xml'} - end - - it "should store the formats and blocks" do - @response.formats.should have_any {|f,p| f == :js && p.call == 'javascript'} - @response.formats.should have_any {|f,p| f == :xml && p.call == 'xml'} - end - - it "should give formats without a block an empty block" do - @response.formats.should have_any {|f,p| f == :html && Proc === p && p.call.nil?} - end - - it "shouldn't allow duplicate formats" do - @response.js {'not javascript'} - @response.formats.should have_any {|f,p| f == :js && p.call == 'javascript'} - @response.formats.should_not have_any {|f,p| f == :js && p.call == 'not javascript'} - end - - it "should keep the formats in sorted order" do - @response.formats.map(&:first).should == [:html, :js, :xml] - end -end diff --git a/vendor/plugins/make_resourceful/spec/responses_spec.rb b/vendor/plugins/make_resourceful/spec/responses_spec.rb deleted file mode 100644 index 607ee794..00000000 --- a/vendor/plugins/make_resourceful/spec/responses_spec.rb +++ /dev/null @@ -1,314 +0,0 @@ -require File.dirname(__FILE__) + '/spec_helper' - -describe 'Resourceful::Default::Responses', " with a _flash parameter for :error" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Responses - @flash = {} - @controller.stubs(:flash).returns(@flash) - @params = {:_flash => {:error => 'Oh no, an error!'}} - @controller.stubs(:params).returns(@params) - end - - it "should set the flash for :error to the parameter's value when set_default_flash is called on :error" do - @controller.set_default_flash(:error, "Aw there's no error!") - @flash[:error].should == 'Oh no, an error!' - end - - it "should set the flash for :message to the default value when set_default_flash is called on :message" do - @controller.set_default_flash(:message, "All jim dandy!") - @flash[:message].should == 'All jim dandy!' - end - - it "shouldn't set the flash for :error when set_default_flash is called on :message" do - @controller.set_default_flash(:message, "All jim dandy!") - @flash[:error].should be_nil - end -end - -describe 'Resourceful::Default::Responses', " with a _redirect parameter on :failure" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Responses - @params = {:_redirect_on => {:failure => 'http://hamptoncatlin.com/'}} - @controller.stubs(:params).returns(@params) - end - - it "should set the redirect for :failure to the parameter's value when set_default_redirect is called on :failure" do - @controller.expects(:redirect_to).with('http://hamptoncatlin.com/') - @controller.set_default_redirect(:back, :status => :failure) - end - - it "should set the redirect for :success to the default value when set_default_redirect is called on :success" do - @controller.expects(:redirect_to).with(:back) - @controller.set_default_redirect(:back, :status => :success) - end - - it "shouldn't set the redirect for :failure when set_default_redirect is called on :success" do - @controller.expects(:redirect_to).with(:back) - @controller.expects(:redirect_to).with('http://hamptoncatlin.com/').never - @controller.set_default_redirect(:back, :status => :success) - end - - it "should set the default redirect for :success by default" do - @controller.expects(:redirect_to).with(:back) - @controller.set_default_redirect(:back) - end -end - -describe 'Resourceful::Default::Responses', ' for show' do - include ControllerMocks - before :each do - mock_kontroller - create_builder - made_resourceful(Resourceful::Default::Responses) - @builder.apply - end - - it "should have an empty HTML response" do - responses[:show].find { |f, p| f == :html }[1].call.should == nil - end - - it "should have an empty JS response" do - responses[:show].find { |f, p| f == :js }[1].call.should == nil - end -end - -describe 'Resourceful::Default::Responses', ' for index' do - include ControllerMocks - before :each do - mock_kontroller - create_builder - made_resourceful(Resourceful::Default::Responses) - @builder.apply - end - - it "should have an empty HTML response" do - responses[:index].find { |f, p| f == :html }[1].call.should == nil - end - - it "should have an empty JS response" do - responses[:index].find { |f, p| f == :js }[1].call.should == nil - end -end - -describe 'Resourceful::Default::Responses', ' for edit' do - include ControllerMocks - before :each do - mock_kontroller - create_builder - made_resourceful(Resourceful::Default::Responses) - @builder.apply - end - - it "should have an empty HTML response" do - responses[:edit].find { |f, p| f == :html }[1].call.should == nil - end - - it "should have an empty JS response" do - responses[:edit].find { |f, p| f == :js }[1].call.should == nil - end -end - -describe 'Resourceful::Default::Responses', ' for new' do - include ControllerMocks - before :each do - mock_kontroller - create_builder - made_resourceful(Resourceful::Default::Responses) - @builder.apply - end - - it "should have an empty HTML response" do - responses[:new].find { |f, p| f == :html }[1].call.should == nil - end - - it "should have an empty JS response" do - responses[:new].find { |f, p| f == :js }[1].call.should == nil - end -end - -describe 'Resourceful::Default::Responses', ' for show_fails' do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Callbacks - create_builder - made_resourceful(Resourceful::Default::Responses) - @builder.apply - end - - it "should give a 404 error for HTML" do - @controller.expects(:render).with(:text => "No item found", :status => 404) - @controller.scope(responses[:show_fails].find { |f, p| f == :html }[1]).call - end - - it "should give a 404 error for JS" do - @controller.expects(:render).with(:text => "No item found", :status => 404) - @controller.scope(responses[:show_fails].find { |f, p| f == :js }[1]).call - end - - it "should give a 404 error for XML" do - @controller.expects(:render).with(:text => "No item found", :status => 404) - @controller.scope(responses[:show_fails].find { |f, p| f == :xml }[1]).call - end -end - -describe 'Resourceful::Default::Responses', ' for create' do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Callbacks - create_builder - made_resourceful(Resourceful::Default::Responses) - @builder.apply - - [:set_default_flash, :set_default_redirect, :object_path].each(&@controller.method(:stubs)) - end - - it "should have an empty JS response" do - responses[:create].find { |f, p| f == :js }[1].call.should == nil - end - - it "should flash a success message to :notice by default for HTML" do - @controller.expects(:set_default_flash).with(:notice, "Create successful!") - @controller.scope(responses[:create].find { |f, p| f == :html }[1]).call - end - - it "should redirect to object_path by default for HTML" do - @controller.stubs(:object_path).returns("/posts/12") - @controller.expects(:set_default_redirect).with("/posts/12") - @controller.scope(responses[:create].find { |f, p| f == :html }[1]).call - end -end - -describe 'Resourceful::Default::Responses', ' for create_fails' do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Callbacks - create_builder - made_resourceful(Resourceful::Default::Responses) - @builder.apply - - [:set_default_flash, :render].each(&@controller.method(:stubs)) - end - - it "should have an empty JS response" do - responses[:create_fails].find { |f, p| f == :js }[1].call.should == nil - end - - it "should flash a failure message to :error by default for HTML" do - @controller.expects(:set_default_flash).with(:error, "There was a problem!") - @controller.scope(responses[:create_fails].find { |f, p| f == :html }[1]).call - end - - it "should render new with a 422 error for HTML" do - @controller.expects(:render).with(:action => :new, :status => 422) - @controller.scope(responses[:create_fails].find { |f, p| f == :html }[1]).call - end -end - -describe 'Resourceful::Default::Responses', ' for update' do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Callbacks - create_builder - made_resourceful(Resourceful::Default::Responses) - @builder.apply - - [:set_default_flash, :set_default_redirect, :object_path].each(&@controller.method(:stubs)) - end - - it "should have an empty JS response" do - responses[:update].find { |f, p| f == :js }[1].call.should == nil - end - - it "should flash a success message to :notice by default for HTML" do - @controller.expects(:set_default_flash).with(:notice, "Save successful!") - @controller.scope(responses[:update].find { |f, p| f == :html }[1]).call - end - - it "should redirect to object_path by default for HTML" do - @controller.stubs(:object_path).returns("/posts/12") - @controller.expects(:set_default_redirect).with("/posts/12") - @controller.scope(responses[:update].find { |f, p| f == :html }[1]).call - end -end - -describe 'Resourceful::Default::Responses', ' for update_fails' do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Callbacks - create_builder - made_resourceful(Resourceful::Default::Responses) - @builder.apply - - [:set_default_flash, :render].each(&@controller.method(:stubs)) - end - - it "should have an empty JS response" do - responses[:update_fails].find { |f, p| f == :js }[1].call.should == nil - end - - it "should flash a failure message to :error by default for HTML" do - @controller.expects(:set_default_flash).with(:error, "There was a problem saving!") - @controller.scope(responses[:update_fails].find { |f, p| f == :html }[1]).call - end - - it "should render edit with a 422 error for HTML" do - @controller.expects(:render).with(:action => :edit, :status => 422) - @controller.scope(responses[:update_fails].find { |f, p| f == :html }[1]).call - end -end - - -describe 'Resourceful::Default::Responses', ' for destroy' do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Callbacks - create_builder - made_resourceful(Resourceful::Default::Responses) - @builder.apply - - [:set_default_flash, :set_default_redirect, :objects_path].each(&@controller.method(:stubs)) - end - - it "should have an empty JS response" do - responses[:destroy].find { |f, p| f == :js }[1].call.should == nil - end - - it "should flash a success message to :notice by default for HTML" do - @controller.expects(:set_default_flash).with(:notice, "Record deleted!") - @controller.scope(responses[:destroy].find { |f, p| f == :html }[1]).call - end - - it "should redirect to objects_path by default for HTML" do - @controller.stubs(:objects_path).returns("/posts") - @controller.expects(:set_default_redirect).with("/posts") - @controller.scope(responses[:destroy].find { |f, p| f == :html }[1]).call - end -end - -describe 'Resourceful::Default::Responses', ' for destroy_fails' do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::Callbacks - create_builder - made_resourceful(Resourceful::Default::Responses) - @builder.apply - - [:set_default_flash, :set_default_redirect, :render].each(&@controller.method(:stubs)) - end - - it "should have an empty JS response" do - responses[:destroy_fails].find { |f, p| f == :js }[1].call.should == nil - end - - it "should flash a failure message to :error by default for HTML" do - @controller.expects(:set_default_flash).with(:error, "There was a problem deleting!") - @controller.scope(responses[:destroy_fails].find { |f, p| f == :html }[1]).call - end - - it "should redirect back on failure by default for HTML" do - @controller.expects(:set_default_redirect).with(:back, :status => :failure) - @controller.scope(responses[:destroy_fails].find { |f, p| f == :html }[1]).call - end -end diff --git a/vendor/plugins/make_resourceful/spec/rspec_on_rails/LICENSE b/vendor/plugins/make_resourceful/spec/rspec_on_rails/LICENSE deleted file mode 100644 index 58453e31..00000000 --- a/vendor/plugins/make_resourceful/spec/rspec_on_rails/LICENSE +++ /dev/null @@ -1,35 +0,0 @@ -All the code in this directory comes from the rspec_on_rails plugin. -We've pilfered it as needed to make the make_resourceful specs easier to write. -It was made available by its authors under the following license terms: - -==================================================================== -== RSpec -Copyright (c) 2005-2007 The RSpec Development Team -==================================================================== -== ARTS -Copyright (c) 2006 Kevin Clark, Jake Howerton -==================================================================== -== ZenTest -Copyright (c) 2001-2006 Ryan Davis, Eric Hodel, Zen Spider Software -==================================================================== -== AssertSelect -Copyright (c) 2006 Assaf Arkin -==================================================================== - -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. diff --git a/vendor/plugins/make_resourceful/spec/rspec_on_rails/redirect_to.rb b/vendor/plugins/make_resourceful/spec/rspec_on_rails/redirect_to.rb deleted file mode 100644 index 4ebc93e6..00000000 --- a/vendor/plugins/make_resourceful/spec/rspec_on_rails/redirect_to.rb +++ /dev/null @@ -1,81 +0,0 @@ -require 'action_controller/url_rewriter' - -class RedirectTo - def initialize(request, expected) - @expected = expected - @request = request - end - - def matches?(response) - @redirected = response.redirect? - @actual = response.redirect_url - return false unless @redirected - if @expected.instance_of? Hash - return false unless @actual =~ %r{^\w+://#{@request.host}} - return false unless actual_redirect_to_valid_route - return actual_hash == expected_hash - else - return @actual == expected_url - end - end - - def actual_hash - hash_from_url @actual - end - - def expected_hash - hash_from_url expected_url - end - - def actual_redirect_to_valid_route - actual_hash - end - - def hash_from_url(url) - query_hash(url).merge(path_hash(url)).with_indifferent_access - end - - def path_hash(url) - path = url.sub(%r{^\w+://#{@request.host}}, "").split("?", 2)[0] - path = path.split("/")[1..-1] if ::Rails::VERSION::MINOR < 2 - ActionController::Routing::Routes.recognize_path path - end - - def query_hash(url) - query = url.split("?", 2)[1] || "" - if defined?(CGIMethods) - CGIMethods.parse_query_parameters(query) - else - ActionController::AbstractRequest.parse_query_parameters(query) - end - end - - def expected_url - case @expected - when Hash - return ActionController::UrlRewriter.new(@request, {}).rewrite(@expected) - when :back - return @request.env['HTTP_REFERER'] - when %r{^\w+://.*} - return @expected - else - return "http://#{@request.host}" + (@expected.split('')[0] == '/' ? '' : '/') + @expected - end - end - - def failure_message - if @redirected - return %Q{expected redirect to #{@expected.inspect}, got redirect to #{@actual.inspect}} - else - return %Q{expected redirect to #{@expected.inspect}, got no redirect} - end - end - - def negative_failure_message - return %Q{expected not to be redirected to #{@expected.inspect}, but was} if @redirected - end - - def description - "redirect to #{@actual.inspect}" - end -end diff --git a/vendor/plugins/make_resourceful/spec/rspec_on_rails/render_template.rb b/vendor/plugins/make_resourceful/spec/rspec_on_rails/render_template.rb deleted file mode 100644 index f073ff8b..00000000 --- a/vendor/plugins/make_resourceful/spec/rspec_on_rails/render_template.rb +++ /dev/null @@ -1,28 +0,0 @@ - -class RenderTemplate - - def initialize(expected, controller) - @controller = controller - @expected = expected - end - - def matches?(response) - @actual = response.rendered_file - full_path(@actual) == full_path(@expected) - end - - def failure_message - "expected #{@expected.inspect}, got #{@actual.inspect}" - end - - def description - "render template #{@expected.inspect}" - end - - private - def full_path(path) - return nil if path.nil? - path.include?('/') ? path : "#{@controller.class.to_s.underscore.gsub('_controller','')}/#{path}" - end - -end diff --git a/vendor/plugins/make_resourceful/spec/serialize_spec.rb b/vendor/plugins/make_resourceful/spec/serialize_spec.rb deleted file mode 100644 index 42cbdbf3..00000000 --- a/vendor/plugins/make_resourceful/spec/serialize_spec.rb +++ /dev/null @@ -1,133 +0,0 @@ -require File.dirname(__FILE__) + '/spec_helper' - -describe Resourceful::Serialize, ".normalize_attributes" do - it "should return nil if given nil" do - Resourceful::Serialize.normalize_attributes(nil).should be_nil - end - - it "should return a basic hash if given a non-injectable attribute" do - Resourceful::Serialize.normalize_attributes(:foo).should == {:foo => nil} - Resourceful::Serialize.normalize_attributes(12).should == {12 => nil} - end - - it "should return a basic hash with a symbol key if given a string attribute" do - Resourceful::Serialize.normalize_attributes("foo").should == {:foo => nil} - end - - it "should preserve hashes" do - Resourceful::Serialize.normalize_attributes({:foo => nil, :bar => nil, :baz => nil}).should == - {:foo => nil, :bar => nil, :baz => nil} - Resourceful::Serialize.normalize_attributes({:foo => 3, :bar => 1, :baz => 4}).should == - {:foo => 3, :bar => 1, :baz => 4} - Resourceful::Serialize.normalize_attributes({:foo => 3, :bar => 1, :baz => [:foo, :bar]}).should == - {:foo => 3, :bar => 1, :baz => [:foo, :bar]} - end - - it "should merge injectable attributes into one big hash" do - Resourceful::Serialize.normalize_attributes([:foo, :bar, :baz]).should == - {:foo => nil, :bar => nil, :baz => nil} - Resourceful::Serialize.normalize_attributes([:foo, :bar, {:baz => nil}, - :boom, {:bop => nil, :blat => nil}]).should == - {:foo => nil, :bar => nil, :baz => nil, :boom => nil, :bop => nil, :blat => nil} - Resourceful::Serialize.normalize_attributes([:foo, :bar, {:baz => 12}, - :boom, {:bop => "foo", :blat => [:fee, :fi, :fo]}]).should == - {:foo => nil, :bar => nil, :baz => 12, :boom => nil, :bop => "foo", :blat => [:fee, :fi, :fo]} - end -end - -describe Array, " of non-serializable objects" do - before :each do - @array = [1, 2, 3, 4, "foo"] - end - - it "should return itself for #to_serializable" do - @array.to_serializable(nil).should == @array - end - - it "should raise an error for #serialize" do - lambda { @array.serialize(:yaml, :attributes => [:foo]) }.should raise_error("Not all elements respond to to_serializable") - end -end - -describe Array, " of serializable objects" do - before :each do - @cat = stub_model("Cat") - @dog = stub_model("Dog") - @array = %w{brown yellow green}.zip(%w{rex rover fido}). - map { |c, d| @cat.new(:fur => c, :friend => @dog.new(:name => d)) } - end - - it "should return an array of serializable hashes for #to_serializable" do - @array.to_serializable([:fur]).should == [{'fur' => 'brown'}, {'fur' => 'yellow'}, {'fur' => 'green'}] - end - - it "should follow deep attributes for #to_serializable" do - @array.to_serializable([:fur, {:friend => :name}]).should == - [{'fur' => 'brown', 'friend' => {'name' => 'rex'}}, - {'fur' => 'yellow', 'friend' => {'name' => 'rover'}}, - {'fur' => 'green', 'friend' => {'name' => 'fido'}}] - end - - it "should raise an error if #serialize is called without the :attributes option" do - lambda { @array.serialize(:yaml, {}) }.should raise_error("Must specify :attributes option") - end - - it "should serialize to a hash with a pluralized root for #serialize" do - YAML.load(@array.serialize(:yaml, :attributes => [:fur, {:friend => :name}])).should == - {"cats" => [{'fur' => 'brown', 'friend' => {'name' => 'rex'}}, - {'fur' => 'yellow', 'friend' => {'name' => 'rover'}}, - {'fur' => 'green', 'friend' => {'name' => 'fido'}}]} - end - - it "should serialize to an XML document with a pluralized root for #serialize(:xml, ...)" do - doc = REXML::Document.new(@array.serialize(:xml, :attributes => [:fur, {:friend => :name}]), - :ignore_whitespace_nodes => :all) - doc.root.name.should == "cats" - cats = doc.get_elements('/cats/cat') - cats.size.should == 3 - cats.zip(%w{brown yellow green}, %w{rex rover fido}) do |cat, fur, dog| - cat.children.find { |e| e.name == "fur" }.text.should == fur - cat.children.find { |e| e.name == "friend" }.children[0].text.should == dog - end - end -end - -describe ActiveRecord::Base, " with a few attributes and an association" do - before :each do - @person = stub_model("Person") - @party_hat = stub_model("PartyHat") - @model = @person.new(:name => "joe", :eye_color => "blue", :hairs => 567, - :party_hat => @party_hat.new(:color => 'blue', :size => 12, :pattern => 'stripey')) - end - - it "should raise an error if #to_serializable is called without attributes" do - lambda { @model.to_serializable(nil) }.should raise_error("Must specify attributes for #.to_serializable") - end - - it "should return an attributes hash for #to_serializable" do - @model.to_serializable([:name, :hairs, {:party_hat => [:color, :size]}]).should == - {'name' => 'joe', 'hairs' => 567, 'party_hat' => { - 'color' => 'blue', 'size' => 12 - }} - end - - it "should raise an error if #serialize is called without the :attributes option" do - lambda { @model.serialize(:yaml, {}) }.should raise_error("Must specify :attributes option") - end - - it "should serialize to a hash for #serialize" do - YAML.load(@model.serialize(:yaml, :attributes => [:hairs, :eye_color, {:party_hat => :size}])).should == - {"person" => {'hairs' => 567, 'eye_color' => 'blue', 'party_hat' => {'size' => 12}}} - end - - it "should serialize to an XML document for #serialize(:xml, ...)" do - doc = REXML::Document.new(@model.serialize(:xml, :attributes => [:name, :eye_color, {:party_hat => :pattern}]), - :ignore_whitespace_nodes => :all) - doc.root.name.should == "person" - doc.root.children.find { |e| e.name == "name" }.text.should == "joe" - doc.root.children.find { |e| e.name == "eye-color" }.text.should == "blue" - - hat = doc.root.children.find { |e| e.name == "party-hat" } - hat.children.find { |e| e.name == "pattern" }.text.should == "stripey" - end -end diff --git a/vendor/plugins/make_resourceful/spec/spec_helper.rb b/vendor/plugins/make_resourceful/spec/spec_helper.rb deleted file mode 100644 index 6c5b15b5..00000000 --- a/vendor/plugins/make_resourceful/spec/spec_helper.rb +++ /dev/null @@ -1,279 +0,0 @@ -$: << File.dirname(__FILE__) + '/../lib' -require 'rubygems' -%w[spec action_pack active_record resourceful/maker - action_controller action_controller/test_process action_controller/integration - spec/rspec_on_rails/redirect_to spec/rspec_on_rails/render_template].each &method(:require) - -Spec::Runner.configure do |config| - config.mock_with :mocha -end - -def should_be_called(&block) - pstub = stub - pstub.expects(:call).instance_eval(&(block || proc {})) - proc { |*args| pstub.call(*args) } -end - -def stub_model(name) - model = Class.new do - include Resourceful::Serialize::Model - - def self.to_s - @name - end - - def initialize(attrs = {}) - attrs.each do |k, v| - self.stubs(k).returns(v) - end - end - - def inspect - "#<#{self.class.send(:instance_variable_get, '@name')}>" - end - end - model.send(:instance_variable_set, '@name', name) - model -end - -def stub_const(name) - unless Object.const_defined?(name) - obj = Object.new - obj.metaclass.send(:define_method, :to_s) { name.to_s } - obj.metaclass.send(:alias_method, :inspect, :to_s) - Object.const_set(name, obj) - end - Object.const_get(name) -end - -def stub_list(size, name = nil, &block) - list = Array.new(size) { |i| name ? stub("#{name}_#{i}") : stub } - list.each(&block) if block - list -end - -module Spec::Matchers - def have_any(&proc) - satisfy { |a| a.any?(&proc) } - end -end - -module ControllerMocks - def mock_kontroller(*to_extend) - options = to_extend.last.is_a?(Hash) ? to_extend.slice!(-1) : {} - @kontroller = Class.new - @kontroller.extend Resourceful::Maker - to_extend.each(&@kontroller.method(:extend)) - - @hidden_actions = Resourceful::ACTIONS.dup - @kontroller.stubs(:hidden_actions).returns(@hidden_actions) - @kontroller.stubs(:plural_action?).returns(false) - @kontroller.stubs(:include) - @kontroller.stubs(:before_filter) - @kontroller.stubs(:helper_method) - end - - def mock_controller(*to_extend) - mock_kontroller - @controller = @kontroller.new - to_extend.each(&@controller.method(:extend)) - end - - def mock_builder(inherited = false) - @builder = stub - @builder.stubs(:response_for) - @builder.stubs(:apply) - @builder.stubs(:instance_eval).yields(@buildercc ) - @builder.stubs(:inherited?).returns(inherited) - Resourceful::Base.stubs(:made_resourceful).returns([]) - Resourceful::Builder.stubs(:new).returns(@builder) - end - - def create_builder - @builder = Resourceful::Builder.new(@kontroller) - class << @builder - alias_method :made_resourceful, :instance_eval - end - end - - def responses - @kontroller.read_inheritable_attribute(:resourceful_responses) - end - - def callbacks - @kontroller.read_inheritable_attribute(:resourceful_callbacks) - end - - def parents - @kontroller.read_inheritable_attribute(:parents) - end - - # Evaluates the made_resourceful block of mod (a module) - # in the context of @builder. - # @builder should be initialized via create_builder. - def made_resourceful(mod) - mod.included(@builder) - end -end - -module RailsMocks - attr_reader :response, :request, :controller, :kontroller - - def included(mod) - require 'ruby-debug' - debugger - end - - def mock_resourceful(options = {}, &block) - options = { - :name => "things" - }.merge options - - init_kontroller options - init_routes options - - stub_const(options[:name].singularize.camelize) - kontroller.make_resourceful(&block) - - init_controller options - end - - def assigns(name) - controller.instance_variable_get("@#{name}") - end - - def redirect_to(opts) - RedirectTo.new(request, opts) - end - - def render_template(path) - RenderTemplate.new(path.to_s, @controller) - end - - private - - def init_kontroller(options) - @kontroller = Class.new ActionController::Base - @kontroller.extend Resourceful::Maker - - @kontroller.metaclass.send(:define_method, :controller_name) { options[:name] } - @kontroller.metaclass.send(:define_method, :controller_path) { options[:name] } - @kontroller.metaclass.send(:define_method, :inspect) { "#{options[:name].camelize}Controller" } - @kontroller.metaclass.send(:alias_method, :to_s, :inspect) - - @kontroller.send(:define_method, :controller_name) { options[:name] } - @kontroller.send(:define_method, :controller_path) { options[:name] } - @kontroller.send(:define_method, :inspect) { "#<#{options[:name].camelize}Controller>" } - @kontroller.send(:alias_method, :to_s, :inspect) - @kontroller.send(:include, ControllerMethods) - - @kontroller - end - - def init_routes(options) - ActionController::Routing::Routes.clear! - route_block = options[:routes] || proc { |map| map.resources options[:name] } - ActionController::Routing::Routes.draw(&route_block) - end - - def init_controller(options) - @controller = kontroller.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - - @controller.request = @request - @controller.response = @response - @request.accept = '*/*' - @request.env['HTTP_REFERER'] = 'http://test.host' - - @controller - end - - def action_params(action, params = {}) - params.merge case action - when :show, :edit, :destroy: {:id => 12} - when :update: {:id => 12, :thing => {}} - when :create: {:thing => {}} - else {} - end - end - - def action_method(action) - method case action - when :index, :show, :edit, :new: :get - when :update: :put - when :create: :post - when :destroy: :delete - end - end - - module ControllerMethods - def render(options=nil, deprecated_status=nil, &block) - unless block_given? - @template.metaclass.class_eval do - define_method :file_exists? do true end - define_method :render_file do |*args| - @first_render ||= args[0] - end - end - end - - super(options, deprecated_status, &block) - end - end -end - -module Spec::Example::ExampleGroupMethods - def should_render_html(action) - it "should render HTML by default for #{action_string(action)}" do - action_method(action)[action, action_params(action)] - response.should be_success - response.content_type.should == 'text/html' - end - end - - def should_render_js(action) - it "should render JS for #{action_string(action)}" do - action_method(action)[action, action_params(action, :format => 'js')] - response.should be_success - response.content_type.should == 'text/javascript' - end - end - - def shouldnt_render_xml(action) - it "should render XML for #{action_string(action)}" do - action_method(action)[action, action_params(action, :format => 'xml')] - response.should_not be_success - response.code.should == '406' - end - end - - def action_string(action) - case action - when :index: "GET /things" - when :show: "GET /things/12" - when :edit: "GET /things/12/edit" - when :update: "PUT /things/12" - when :create: "POST /things" - when :new: "GET /things/new" - when :destroy: "DELETE /things/12" - end - end -end - -module Spec::Example - class IntegrationExampleGroup < Test::Unit::TestCase - include ExampleMethods - class << self - include ExampleGroupMethods - end - - def initialize(defined_description, &implementation) - super() - @_defined_description = defined_description - @_implementation = implementation - end - - ExampleGroupFactory.register(:integration, self) - end -end diff --git a/vendor/plugins/make_resourceful/spec/urls_spec.rb b/vendor/plugins/make_resourceful/spec/urls_spec.rb deleted file mode 100644 index 73a1862b..00000000 --- a/vendor/plugins/make_resourceful/spec/urls_spec.rb +++ /dev/null @@ -1,276 +0,0 @@ -require File.dirname(__FILE__) + '/spec_helper' - -describe Resourceful::Default::URLs, " for a controller with no parents or namespaces" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::URLs - @object = stub_model('Thing') - @controller.stubs(:current_object).returns(@object) - - @controller.stubs(:current_model_name).returns('Thing') - @controller.stubs(:parent?).returns(false) - @controller.stubs(:namespaces).returns([]) - end - - it "should return nil for #url_helper_prefix" do - @controller.url_helper_prefix.should be_nil - end - - it "should return the empty string for #collection_url_prefix" do - @controller.collection_url_prefix.should == "" - end - - it "should get the path of current_object with #object_path" do - @controller.expects(:send).with('thing_path', @object) - @controller.object_path - end - - it "should get the url of current_object with #object_url" do - @controller.expects(:send).with('thing_url', @object) - @controller.object_url - end - - it "should get the path of the passed object with #object_path" do - model = stub_model('Thing') - @controller.expects(:send).with('thing_path', model) - @controller.object_path(model) - end - - it "should get the url of the passed object with #object_url" do - model = stub_model('Thing') - @controller.expects(:send).with('thing_url', model) - @controller.object_url(model) - end - - it "should get the path of current_object with #nested_object_path" do - @controller.expects(:send).with('thing_path', @object) - @controller.nested_object_path - end - - it "should get the url of current_object with #nested_object_url" do - @controller.expects(:send).with('thing_url', @object) - @controller.nested_object_url - end - - it "should get the path of the passed object with #nested_object_path" do - model = stub_model('Thing') - @controller.expects(:send).with('thing_path', model) - @controller.nested_object_path(model) - end - - it "should get the url of the passed object with #nested_object_url" do - model = stub_model('Thing') - @controller.expects(:send).with('thing_url', model) - @controller.nested_object_url(model) - end - - it "should get the edit path of current_object with #edit_object_path" do - @controller.expects(:send).with('edit_thing_path', @object) - @controller.edit_object_path - end - - it "should get the edit url of current_object with #edit_object_url" do - @controller.expects(:send).with('edit_thing_url', @object) - @controller.edit_object_url - end - - it "should get the edit path of the passed object with #edit_object_path" do - model = stub_model('Thing') - @controller.expects(:send).with('edit_thing_path', model) - @controller.edit_object_path(model) - end - - it "should get the edit url of the passed object with #edit_object_url" do - model = stub_model('Thing') - @controller.expects(:send).with('edit_thing_url', model) - @controller.edit_object_url(model) - end - - it "should get the plural path of the current model with #objects_path" do - @controller.expects(:send).with('things_path') - @controller.objects_path - end - - it "should get the plural url of the current model with #objects_url" do - @controller.expects(:send).with('things_url') - @controller.objects_url - end - - it "should get the new path of the current model with #new_object_path" do - @controller.expects(:send).with('new_thing_path') - @controller.new_object_path - end - - it "should get the new url of the current model with #new_object_url" do - @controller.expects(:send).with('new_thing_url') - @controller.new_object_url - end -end - -describe Resourceful::Default::URLs, " for a controller with a parent object" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::URLs - @object = stub_model('Thing') - @controller.stubs(:current_object).returns(@object) - - @controller.stubs(:current_model_name).returns('Thing') - - @person = stub_model('Person') - @controller.stubs(:parent_object).returns(@person) - @controller.stubs(:parent_name).returns('person') - @controller.stubs(:parent?).returns(true) - @controller.stubs(:namespaces).returns([]) - end - - it "should return nil for #url_helper_prefix" do - @controller.url_helper_prefix.should be_nil - end - - it "should return the underscored parent name for #collection_url_prefix" do - @controller.collection_url_prefix.should == "person_" - end - - it "should get the path of current_object with #object_path" do - @controller.expects(:send).with('thing_path', @object) - @controller.object_path - end - - it "should get the nested path of current_object with #nested_object_path" do - @controller.expects(:send).with('person_thing_path', @person, @object) - @controller.nested_object_path - end - - it "should get the nested url of current_object with #nested_object_url" do - @controller.expects(:send).with('person_thing_url', @person, @object) - @controller.nested_object_url - end - - it "should get the nested path of the passed object with #nested_object_path" do - object = stub_model('Thing') - @controller.expects(:send).with('person_thing_path', @person, object) - @controller.nested_object_path object - end - - it "should get the nested url of the passed object with #nested_object_url" do - object = stub_model('Thing') - @controller.expects(:send).with('person_thing_url', @person, object) - @controller.nested_object_url object - end - - it "should get the plural path of the current model and its parent with #objects_path" do - @controller.expects(:send).with('person_things_path', @person) - @controller.objects_path - end - - it "should get the edit path of the current model with #edit_object_path" do - @controller.expects(:send).with('edit_thing_path', @object) - @controller.edit_object_path - end - - it "should get the new path of the current model and its parent with #new_object_path" do - @controller.expects(:send).with('new_person_thing_path', @person) - @controller.new_object_path - end - - it "should get the path of the parent_object with #parent_path" do - @controller.expects(:send).with('person_path', @person) - @controller.parent_path - end - - it "should get the url of the parent_object with #parent_url" do - @controller.expects(:send).with('person_url', @person) - @controller.parent_url - end - - it "should get the path of the passed object with #parent_path" do - model = stub_model('Person') - @controller.expects(:send).with('person_path', model) - @controller.parent_path model - end - - it "should get the url of the passed object with #parent_url" do - model = stub_model('Person') - @controller.expects(:send).with('person_url', model) - @controller.parent_url model - end -end - -describe Resourceful::Default::URLs, " for a controller within a namespace" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::URLs - @object = stub_model('Thing') - @controller.stubs(:current_object).returns(@object) - - @controller.stubs(:current_model_name).returns('Thing') - - @controller.stubs(:parent?).returns(false) - @controller.stubs(:namespaces).returns([:admin, :main]) - end - - it "should return the underscored list of namespaces for #url_helper_prefix" do - @controller.url_helper_prefix.should == "admin_main_" - end - - it "should get the namespaced path of current_object with #object_path" do - @controller.expects(:send).with('admin_main_thing_path', @object) - @controller.object_path - end - - it "should get the namespaced plural path of the current model with #objects_path" do - @controller.expects(:send).with('admin_main_things_path') - @controller.objects_path - end - - it "should get the edit path of the current model with #edit_object_path" do - @controller.expects(:send).with('edit_admin_main_thing_path', @object) - @controller.edit_object_path - end - - it "should get the new path of the current model with #new_object_path" do - @controller.expects(:send).with('new_admin_main_thing_path') - @controller.new_object_path - end -end - -describe Resourceful::Default::URLs, " for a controller with a parent object and within a namespace" do - include ControllerMocks - before :each do - mock_controller Resourceful::Default::URLs - @object = stub_model('Thing') - @controller.stubs(:current_object).returns(@object) - - @controller.stubs(:current_model_name).returns('Thing') - - @person = stub_model('Person') - @controller.stubs(:parent_object).returns(@person) - @controller.stubs(:parent_name).returns('person') - @controller.stubs(:parent?).returns(true) - @controller.stubs(:namespaces).returns([:admin, :main]) - end - - it "should return the underscored list of namespaces for #url_helper_prefix" do - @controller.url_helper_prefix.should == "admin_main_" - end - - it "should get the namespaced path of current_object with #object_path" do - @controller.expects(:send).with('admin_main_thing_path', @object) - @controller.object_path - end - - it "should get the namespaced plural path of the current model and its parent with #objects_path" do - @controller.expects(:send).with('admin_main_things_path', @person) - @controller.objects_path - end - - it "should get the edit path of the current model with #edit_object_path" do - @controller.expects(:send).with('edit_admin_main_thing_path', @object) - @controller.edit_object_path - end - - it "should get the new path of the current model and its parent with #new_object_path" do - @controller.expects(:send).with('new_admin_main_thing_path', @person) - @controller.new_object_path - end -end diff --git a/vendor/plugins/spatial_adapter/MIT-LICENSE b/vendor/plugins/spatial_adapter/MIT-LICENSE deleted file mode 100644 index 7a5a7080..00000000 --- a/vendor/plugins/spatial_adapter/MIT-LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2006 Guilhem Vellut - -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. \ No newline at end of file diff --git a/vendor/plugins/spatial_adapter/README.rdoc b/vendor/plugins/spatial_adapter/README.rdoc deleted file mode 100644 index d904377d..00000000 --- a/vendor/plugins/spatial_adapter/README.rdoc +++ /dev/null @@ -1,144 +0,0 @@ -= Spatial Adapter for Rails - -This is the Spatial Adapter for Rails 0.1.1. It is a plugin for Rails which manages the MySql Spatial and PostGIS geometric columns in a transparent way (that is like the other base data type columns). It also provides a way to manage these columns in migrations. It replaces both the "PostGIS Adapter for Rails" and "MySql Spatial Adapter for Rails" plugins. - -== Dependencies - -You need to install a version >= 0.1.1 of the GeoRuby gem (http://rubyforge.org/projects/georuby/): - - gem install georuby - -== Installation - -At the root of your Rails project, type : - - script/plugin install git://github.com/fragility/spatial_adapter.git - -You need to have Git installed. - -== Operations - -Geometric columns in your ActiveRecord models now appear just like any other column of other basic data types. They can also be dumped in ruby schema mode and loaded in migrations the same way as columns of basic types. - -=== Migrations - -Here is an example of code for the creation of a table with a geometric column in PostGIS, along with the addition of a spatial index on the column: - - ActiveRecord::Schema.define do - create_table "table_points", :force => true do |t| - t.column "data", :string - t.column "geom", :point, :null=>false, :srid => 123, :with_z => true - end - - add_index "table_points", "geom", :spatial=>true - end - -Here is a related statement valid for MySql version <= 5.0.16: - - ActiveRecord::Schema.define do - create_table "table_points", ;options=>"ENGINE=MyISAM", :force => true do |t| - t.column "data", :string - t.column "geom", :point, :null=>false - end - - add_index "table_points", "geom", :spatial=>true - end - -The differences with the PostGIS version are because of the following reasons: - -- On all version of MySql, the :srid and :with_z would be ignored, since they are not supported. -- On MySql versions <= 5.0.16, you have to add :options => "ENGINE=MyISAM" to the create_table - statetement, since only MyISAM tables can have geometric columns. - -=== Models - -Here is the model you would use, in both MySql and PostGIS: - - class TablePoint < ActiveRecord::Base - end - -That was easy! As you see, there is no need to declare a column as geometric. The plugin will get this information by itself. - -=== Access - -Here is an example of PostGIS row creation and access, using the model and the table defined above: - - pt = TablePoint.new(:data => "Hello!",:geom => Point.from_x_y_z(-1.6,2.8,-3.4,123)) - pt.save - pt = TablePoint.find_first - puts pt.geom.x #access the geom column like any other - -For MySQL, it is slightly different since it does not support Z dimension or SRID: - - pt = TablePoint.new(:data => "Hello!",:geom => Point.from_x_y(-1.6,2.8)) - pt.save - pt = TablePoint.find_first - puts pt.geom.x #access the geom column like any other - -=== Fixtures - -If you use fixtures for your unit tests, at some point, you will want to input a geometry. You could transform your geometries to a form suitable for YAML yourself everytime but the spatial adapter provides a method to do it for you: +to_fixture_format+. It works for both MySQL and PostGIS (although the string returned is different for each database). You would use it like this, if the geometric column is a point: - - fixture: - id: 1 - data: HELLO - geom: <%= Point.from_x_y(123.5,321.9).to_fixture_format %> - -=== Find_by & Find_all_by - -find_by_[column] has been redefined when column is of a geometric type. Instead of using the Rails default '=' operator, for which I can't see a definition for MySql spatial datatypes and which performs a bounding box equality test in PostGIS, it uses a bounding box intersection: && in PostGIS and MBRIntersects in MySQL, which can both make use of a spatial index if one is present to speed up the queries. You could use this query, for example, if you need to display data from the database: You would want only a single geometry which is in the screen rectangle and you could use a bounding box query for that. Since this is a common case, it is the default. You have 2 ways to use the find_by_[geom_column]: Either by passing a geometric object directly, or passing an array with the 2 opposite corners of a bounding box (with 2 or 3 coordinates depending of the dimension of the data). - - Park.find_by_geom(LineString.from_coordinates([[1.4,5.6],[2.7,8.9],[1.6,5.6]])) - -or - - Park.find_by_geom([[3,5.6],[19.98,5.9]]) - -In PostGIS, since you can only use operations with geometries with the same SRID, you can add a third element representing the SRID of the bounding box to the array. It is by default set to -1: - - Park.find_by_geom([[3,5.6],[19.98,5.9],123]) - -Find_all_by_[column] will return all records that occur within the bounding box or geometric object. Most useful for queries where you need to find all the geometric objects within a bounding box displayed on screen. - -=== Geometric data types - -Ruby geometric datatypes are currently made available only through the GeoRuby library (http://thepochisuperstarmegashow.com/ProjectsDoc/georuby-doc/index.html): This is where the Point.from_x_y in the example above comes from. It is a goal of a future release of the Spatial Adapter to support additional geometric datatype libraries, such as Ruby/GEOS, as long as they can support reading and writing of EWKB. - - -== Warning - -- If you use a version of MySQL before 5.0.16, only tables using the MyISAM engine can support geometric columns. After MySQL 5.0.16, any engine (incuding INNODB) is supported. - -- Since ActiveRecord seems to keep only the string values directly returned from the database, it translates from these to the correct types everytime an attribute is read, which is probably ok for simple types, but might be less than efficient for geometries, since the EWKB string has to be parsed everytime. Also it means you cannot modify the geometry object returned from an attribute directly: - - place = Place.find_first - place.the_geom.y=123456.7 - -- Since the translation to a geometry is performed everytime the_geom is read, the change to y will not be saved! You would have to do something like this : - - place = Place.find_first - the_geom = place.the_geom - the_geom.y=123456.7 - place.the_geom = the_geom - - -== Changes since last version - -- The PostGIS adapter and the MySql Spatial adapter have been merged into one plugin. The correct files to load is determined using the type of connection defined in the environment. -- Geometric columns can now be dumped just like other base data types. This means you can use the ruby schema mode, even if you use the plugin. -- Support of M dimensions in migrations. The :dimension key in the column definition has disappeared and has been replaced by :with_z and :with_m. -- Addition of unit tests. At the plugin root, Run rake test:mysql to run the mysql tests and rake test:postgis for the postgis ones. You will need to configure your connection in test/db/database_mysql.yml and test/db/database_postgis.yml. If you get errors on your platform, please report to mailto:guilhem.vellut+georuby@gmail.com. -- Addition of a find_by methods with a special behaviour for geometries -- Addition of a to_yaml method to use inside a YAML fixture - -== TODO - -- See the project Issues page at http://github.com/fragility/spatial_adapter/issues for the current list of proposals, and to make your own. - -== License - -The Spatial Adapter for Rails is released under the MIT license. - -== Support - -Any questions, enhancement proposals, bug notifications or corrections can be made via the project page at http://github.com/fragility/spatial_adapter diff --git a/vendor/plugins/spatial_adapter/init.rb b/vendor/plugins/spatial_adapter/init.rb deleted file mode 100644 index 0afab326..00000000 --- a/vendor/plugins/spatial_adapter/init.rb +++ /dev/null @@ -1,28 +0,0 @@ -class SpatialAdapterNotCompatibleError < StandardError -end - - -case ActiveRecord::Base.configurations[RAILS_ENV]['adapter'] -when 'mysql' - require 'mysql_spatial_adapter' -when 'postgresql' - require 'post_gis_adapter' -else - raise SpatialAdapterNotCompatibleError.new("Only MySQL and PostgreSQL are currently supported by the spatial adapter plugin.") -end - -=begin -# when testing RAILS_ENV isn't set so you'll need to use this style of getting the adapter name -# [TODO] come back and make sure RAILS_ENV exists when running the plugin tests -case ActiveRecord::Base.connection.adapter_name -when 'MySQL' - require 'mysql_spatial_adapter' -when 'PostgreSQL' - require 'post_gis_adapter' - else - raise SpatialAdapterNotCompatibleError.new("Only MySQL and PostgreSQL are currently supported by the spatial adapter plugin.") - end -=end - - - diff --git a/vendor/plugins/spatial_adapter/lib/common_spatial_adapter.rb b/vendor/plugins/spatial_adapter/lib/common_spatial_adapter.rb deleted file mode 100644 index 3b2a1278..00000000 --- a/vendor/plugins/spatial_adapter/lib/common_spatial_adapter.rb +++ /dev/null @@ -1,179 +0,0 @@ - -#Addition of a flag indicating if the index is spatial -ActiveRecord::ConnectionAdapters::IndexDefinition.class_eval do - attr_accessor :spatial - - def initialize(table, name, unique, spatial,columns) - super(table,name,unique,columns) - @spatial = spatial - end - -end - -ActiveRecord::SchemaDumper.class_eval do - def table(table, stream) - - columns = @connection.columns(table) - begin - tbl = StringIO.new - - if @connection.respond_to?(:pk_and_sequence_for) - pk, pk_seq = @connection.pk_and_sequence_for(table) - end - pk ||= 'id' - - tbl.print " create_table #{table.inspect}" - if columns.detect { |c| c.name == pk } - if pk != 'id' - tbl.print %Q(, :primary_key => "#{pk}") - end - else - tbl.print ", :id => false" - end - - if @connection.respond_to?(:options_for) - res = @connection.options_for(table) - tbl.print ", :options=>'#{res}'" if res - end - - tbl.print ", :force => true" - tbl.puts " do |t|" - - columns.each do |column| - - raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil? - next if column.name == pk - #need to use less_simplified_type here or have each specific geometry type be simplified to a specific simplified type in Column and each one treated separately in the Column methods - if column.is_a?(SpatialColumn) - tbl.print " t.column #{column.name.inspect}, #{column.geometry_type.inspect}" - tbl.print ", :srid => #{column.srid.inspect}" if column.srid != -1 - tbl.print ", :with_z => #{column.with_z.inspect}" if column.with_z - tbl.print ", :with_m => #{column.with_m.inspect}" if column.with_m - else - tbl.print " t.column #{column.name.inspect}, #{column.type.inspect}" - end - tbl.print ", :limit => #{column.limit.inspect}" if column.limit != @types[column.type][:limit] && column.precision.blank? && column.scale.blank? - tbl.print ", :precision => #{column.precision.inspect}" if column.precision != @types[column.type][:precision] - tbl.print ", :scale => #{column.scale.inspect}" if column.scale != @types[column.type][:scale] - tbl.print ", :default => #{default_string(column.default)}" if !column.default.nil? - tbl.print ", :null => false" if !column.null - tbl.puts - end - - tbl.puts " end" - tbl.puts - - indexes(table, tbl) - - tbl.rewind - stream.print tbl.read - rescue => e - stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}" - stream.puts "# #{e.message} #{e.backtrace}" - stream.puts - end - - stream - end - - def indexes(table, stream) - indexes = @connection.indexes(table) - indexes.each do |index| - stream.print " add_index #{index.table.inspect}, #{index.columns.inspect}, :name => #{index.name.inspect}" - stream.print ", :unique => true" if index.unique - stream.print ", :spatial=> true " if index.spatial - stream.puts - end - - stream.puts unless indexes.empty? - end -end - - - - -module SpatialAdapter - #Translation of geometric data types - def geometry_data_types - { - :point => { :name => "POINT" }, - :line_string => { :name => "LINESTRING" }, - :polygon => { :name => "POLYGON" }, - :geometry_collection => { :name => "GEOMETRYCOLLECTION" }, - :multi_point => { :name => "MULTIPOINT" }, - :multi_line_string => { :name => "MULTILINESTRING" }, - :multi_polygon => { :name => "MULTIPOLYGON" }, - :geometry => { :name => "GEOMETRY"} - } - end - -end - - -#using a mixin instead of subclassing Column since each adapter can have a specific subclass of Column -module SpatialColumn - attr_reader :geometry_type, :srid, :with_z, :with_m - - def initialize(name, default, sql_type = nil, null = true,srid=-1,with_z=false,with_m=false) - super(name,default,sql_type,null) - @geometry_type = geometry_simplified_type(@sql_type) - @srid = srid - @with_z = with_z - @with_m = with_m - end - - - #Redefines type_cast to add support for geometries - def type_cast(value) - return nil if value.nil? - case type - when :geometry then self.class.string_to_geometry(value) - else super - end - end - - #Redefines type_cast_code to add support for geometries. - # - #WARNING : Since ActiveRecord keeps only the string values directly returned from the database, it translates from these to the correct types everytime an attribute is read (using the code returned by this method), which is probably ok for simple types, but might be less than efficient for geometries. Also you cannot modify the geometry object returned directly or your change will not be saved. - def type_cast_code(var_name) - case type - when :geometry then "#{self.class.name}.string_to_geometry(#{var_name})" - else super - end - end - - - #Redefines klass to add support for geometries - def klass - case type - when :geometry then GeoRuby::SimpleFeatures::Geometry - else super - end - end - - private - - #Redefines the simplified_type method to add behabiour for when a column is of type geometry - def simplified_type(field_type) - case field_type - when /geometry|point|linestring|polygon|multipoint|multilinestring|multipolygon|geometrycollection/i then :geometry - else super - end - end - - #less simlpified geometric type to be use in migrations - def geometry_simplified_type(field_type) - case field_type - when /^point$/i then :point - when /^linestring$/i then :line_string - when /^polygon$/i then :polygon - when /^geometry$/i then :geometry - when /multipoint/i then :multi_point - when /multilinestring/i then :multi_line_string - when /multipolygon/i then :multi_polygon - when /geometrycollection/i then :geometry_collection - end - end - - -end diff --git a/vendor/plugins/spatial_adapter/lib/mysql_spatial_adapter.rb b/vendor/plugins/spatial_adapter/lib/mysql_spatial_adapter.rb deleted file mode 100644 index 695b6abf..00000000 --- a/vendor/plugins/spatial_adapter/lib/mysql_spatial_adapter.rb +++ /dev/null @@ -1,237 +0,0 @@ -require 'active_record' -require 'geo_ruby' -require 'common_spatial_adapter' - -include GeoRuby::SimpleFeatures - -#add a method to the Geometry class which will transform a geometry in a form suitable to be used in a YAML file (such as in a fixture) -GeoRuby::SimpleFeatures::Geometry.class_eval do - def to_fixture_format - "!binary | #{[(255.chr * 4) + as_wkb].pack('m').gsub(/\s+/,"")}" - end -end - -ActiveRecord::Base.class_eval do - require 'active_record/version' - - #For Rails < 1.2 - if ActiveRecord::VERSION::STRING < "1.15.1" - def self.construct_conditions_from_arguments(attribute_names, arguments) - conditions = [] - attribute_names.each_with_index do |name, idx| - if columns_hash[name].is_a?(SpatialColumn) - #when the discriminating column is spatial, always use the MBRIntersects (bounding box intersection check) operator : the user can pass either a geometric object (which will be transformed to a string using the quote method of the database adapter) or an array with the corner points of a bounding box - if arguments[idx].is_a?(Array) - #using some georuby utility : The multipoint has a bbox whose corners are the 2 points passed as parameters : [ pt1, pt2] - arguments[idx]= MultiPoint.from_coordinates(arguments[idx]) - elsif arguments[idx].is_a?(Envelope) - arguments[idx]= MultiPoint.from_points([arguments[idx].lower_corner,arguments[idx].upper_corner]) - end - conditions << "MBRIntersects(?, #{table_name}.#{connection.quote_column_name(name)}) " - else - conditions << "#{table_name}.#{connection.quote_column_name(name)} #{attribute_condition(arguments[idx])} " - end - end - [ conditions.join(" AND "), *arguments[0...attribute_names.length] ] - end - - else - def self.get_conditions(attrs) - attrs.map do |attr, value| - if columns_hash[attr].is_a?(SpatialColumn) - if value.is_a?(Array) - #using some georuby utility : The multipoint has a bbox whose corners are the 2 points passed as parameters : [ pt1, pt2] - attrs[attr]= MultiPoint.from_coordinates(value) - elsif value.is_a?(Envelope) - attrs[attr]= MultiPoint.from_points([value.lower_corner,value.upper_corner]) - end - "MBRIntersects(?, #{table_name}.#{connection.quote_column_name(attr)}) " - else - #original stuff - "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}" - end - end.join(' AND ') - end - def self.get_rails2_conditions(attrs) - attrs.map do |attr, value| - attr = attr.to_s - if columns_hash[attr].is_a?(SpatialColumn) - if value.is_a?(Array) - #using some georuby utility : The multipoint has a bbox whose corners are the 2 points passed as parameters : [ pt1, pt2] - attrs[attr.to_sym]=MultiPoint.from_coordinates(value) - elsif value.is_a?(Envelope) - attrs[attr.to_sym]=MultiPoint.from_points([value.lower_corner,value.upper_corner]) - end - "MBRIntersects(?, #{table_name}.#{connection.quote_column_name(attr)}) " - else - #original stuff - # Extract table name from qualified attribute names. - if attr.include?('.') - table_name, attr = attr.split('.', 2) - table_name = connection.quote_table_name(table_name) - else - table_name = quoted_table_name - end - begin # this works in AR 2.3.2 and later versions, it might work in earlier versions - this way of checking avoids using version numbers - attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value) - rescue ArgumentError # for some earlier versions of AR it definitely breaks - "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}" - end - end - end.join(' AND ') - end - if ActiveRecord::VERSION::STRING == "1.15.1" - def self.sanitize_sql_hash(attrs) - conditions = get_conditions(attrs) - replace_bind_variables(conditions, attrs.values) - end - elsif ActiveRecord::VERSION::STRING.starts_with?("1.15") - #For Rails >= 1.2 - def self.sanitize_sql_hash(attrs) - conditions = get_conditions(attrs) - replace_bind_variables(conditions, expand_range_bind_variables(attrs.values)) - end - else - #For Rails >= 2 - if method(:sanitize_sql_hash_for_conditions).arity == 1 - # Before Rails 2.3.3, the method had only one argument - def self.sanitize_sql_hash_for_conditions(attrs) - conditions = get_rails2_conditions(attrs) - replace_bind_variables(conditions, expand_range_bind_variables(attrs.values)) - end - elsif method(:sanitize_sql_hash_for_conditions).arity == -2 - # After Rails 2.3.3, the method had only two args, the last one optional - def self.sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name) - attrs = expand_hash_conditions_for_aggregates(attrs) - - conditions = attrs.map do |attr, value| - unless value.is_a?(Hash) - attr = attr.to_s - - # Extract table name from qualified attribute names. - if attr.include?('.') - table_name, attr = attr.split('.', 2) - table_name = connection.quote_table_name(table_name) - end - - if columns_hash[attr].is_a?(SpatialColumn) - if value.is_a?(Array) - #using some georuby utility : The multipoint has a bbox whose corners are the 2 points passed as parameters : [ pt1, pt2] - attrs[attr.to_sym]=MultiPoint.from_coordinates(value) - elsif value.is_a?(Envelope) - attrs[attr.to_sym]=MultiPoint.from_points([value.lower_corner,value.upper_corner]) - end - "MBRIntersects(?, #{table_name}.#{connection.quote_column_name(attr)}) " - else - attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value) - end - else - sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s)) - end - end.join(' AND ') - - replace_bind_variables(conditions, expand_range_bind_variables(attrs.values)) - end - else - raise "Spatial Adapter will not work with this version of Rails" - end - end - end -end - - -ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - - include SpatialAdapter - - alias :original_native_database_types :native_database_types - def native_database_types - original_native_database_types.merge!(geometry_data_types) - end - - alias :original_quote :quote - #Redefines the quote method to add behaviour for when a Geometry is encountered ; used when binding variables in find_by methods - def quote(value, column = nil) - if value.kind_of?(GeoRuby::SimpleFeatures::Geometry) - "GeomFromWKB(0x#{value.as_hex_wkb},#{value.srid})" - else - original_quote(value,column) - end - end - - #Redefinition of columns to add the information that a column is geometric - def columns(table_name, name = nil)#:nodoc: - sql = "SHOW FIELDS FROM #{table_name}" - columns = [] - execute(sql, name).each do |field| - if field[1] =~ /geometry|point|linestring|polygon|multipoint|multilinestring|multipolygon|geometrycollection/i - #to note that the column is spatial - columns << ActiveRecord::ConnectionAdapters::SpatialMysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") - else - columns << ActiveRecord::ConnectionAdapters::MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") - end - end - columns - end - - - #operations relative to migrations - - #Redefines add_index to support the case where the index is spatial - #If the :spatial key in the options table is true, then the sql string for a spatial index is created - def add_index(table_name,column_name,options = {}) - index_name = options[:name] || index_name(table_name,:column => Array(column_name)) - - if options[:spatial] - execute "CREATE SPATIAL INDEX #{index_name} ON #{table_name} (#{Array(column_name).join(", ")})" - else - super - end - end - - #Check the nature of the index : If it is SPATIAL, it is indicated in the IndexDefinition object (redefined to add the spatial flag in spatial_adapter_common.rb) - def indexes(table_name, name = nil)#:nodoc: - indexes = [] - current_index = nil - execute("SHOW KEYS FROM #{table_name}", name).each do |row| - if current_index != row[2] - next if row[2] == "PRIMARY" # skip the primary key - current_index = row[2] - indexes << ActiveRecord::ConnectionAdapters::IndexDefinition.new(row[0], row[2], row[1] == "0", row[10] == "SPATIAL",[]) - end - indexes.last.columns << row[4] - end - indexes - end - - #Get the table creation options : Only the engine for now. The text encoding could also be parsed and returned here. - def options_for(table) - result = execute("show table status like '#{table}'") - engine = result.fetch_row[1] - if engine !~ /inno/i #inno is default so do nothing for it in order not to clutter the migration - "ENGINE=#{engine}" - else - nil - end - end -end - - -module ActiveRecord - module ConnectionAdapters - class SpatialMysqlColumn < MysqlColumn - - include SpatialColumn - - #MySql-specific geometry string parsing. By default, MySql returns geometries in strict wkb format with "0" characters in the first 4 positions. - def self.string_to_geometry(string) - return string unless string.is_a?(String) - begin - GeoRuby::SimpleFeatures::Geometry.from_ewkb(string[4..-1]) - rescue Exception => exception - nil - end - end - end - end -end diff --git a/vendor/plugins/spatial_adapter/lib/post_gis_adapter.rb b/vendor/plugins/spatial_adapter/lib/post_gis_adapter.rb deleted file mode 100644 index 7b6bd31d..00000000 --- a/vendor/plugins/spatial_adapter/lib/post_gis_adapter.rb +++ /dev/null @@ -1,458 +0,0 @@ -require 'active_record' -require 'geo_ruby' -require 'common_spatial_adapter' - -include GeoRuby::SimpleFeatures -include SpatialAdapter - -#tables to ignore in migration : relative to PostGIS management of geometric columns -ActiveRecord::SchemaDumper.ignore_tables << "spatial_ref_sys" << "geometry_columns" - - -#add a method to the Geometry class which will transform a geometry in a form suitable to be used in a YAML file (such as in a fixture) -GeoRuby::SimpleFeatures::Geometry.class_eval do - def to_fixture_format - as_hex_ewkb - end -end - - -ActiveRecord::Base.class_eval do - require 'active_record/version' - - #For Rails < 1.2 - if ActiveRecord::VERSION::STRING < "1.15.1" - def self.construct_conditions_from_arguments(attribute_names, arguments) - conditions = [] - attribute_names.each_with_index do |name, idx| - if columns_hash[name].is_a?(SpatialColumn) - #when the discriminating column is spatial, always use the && (bounding box intersection check) operator : the user can pass either a geometric object (which will be transformed to a string using the quote method of the database adapter) or an array representing 2 opposite corners of a bounding box - if arguments[idx].is_a?(Array) - bbox = arguments[idx] - conditions << "#{table_name}.#{connection.quote_column_name(name)} && SetSRID(?::box3d, #{bbox[2] || DEFAULT_SRID} ) " - #Could do without the ? and replace directly with the quoted BBOX3D but like this, the flow is the same everytime - arguments[idx]= "BOX3D(" + bbox[0].join(" ") + "," + bbox[1].join(" ") + ")" - else - conditions << "#{table_name}.#{connection.quote_column_name(name)} && ? " - end - else - conditions << "#{table_name}.#{connection.quote_column_name(name)} #{attribute_condition(arguments[idx])} " - end - end - [ conditions.join(" AND "), *arguments[0...attribute_names.length] ] - end - else - #Vit Ondruch & Tilmann Singer 's patch - def self.get_conditions(attrs) - attrs.map do |attr, value| - attr = attr.to_s - if columns_hash[attr].is_a?(SpatialColumn) - if value.is_a?(Array) - attrs[attr.to_sym]= "BOX3D(" + value[0].join(" ") + "," + value[1].join(" ") + ")" - "#{table_name}.#{connection.quote_column_name(attr)} && SetSRID(?::box3d, #{value[2] || DEFAULT_SRID} ) " - elsif value.is_a?(Envelope) - attrs[attr.to_sym]= "BOX3D(" + value.lower_corner.text_representation + "," + value.upper_corner.text_representation + ")" - "#{table_name}.#{connection.quote_column_name(attr)} && SetSRID(?::box3d, #{value.srid} ) " - else - "#{table_name}.#{connection.quote_column_name(attr)} && ? " - end - else - begin # this works in AR 2.3.2 and later versions, it might work in earlier versions - this way of checking avoids using version numbers - attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value) - rescue ArgumentError # for some earlier versions of AR it definitely breaks - "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}" - end - end - end.join(' AND ') - end - if ActiveRecord::VERSION::STRING == "1.15.1" - def self.sanitize_sql_hash(attrs) - conditions = get_conditions(attrs) - replace_bind_variables(conditions, attrs.values) - end - elsif ActiveRecord::VERSION::STRING.starts_with?("1.15") - #For Rails >= 1.2 - def self.sanitize_sql_hash(attrs) - conditions = get_conditions(attrs) - replace_bind_variables(conditions, expand_range_bind_variables(attrs.values)) - end - else - #For Rails >= 2 - if method(:sanitize_sql_hash_for_conditions).arity == 1 - # Before Rails 2.3.3, the method had only one argument - def self.sanitize_sql_hash_for_conditions(attrs) - conditions = get_conditions(attrs) - replace_bind_variables(conditions, expand_range_bind_variables(attrs.values)) - end - elsif method(:sanitize_sql_hash_for_conditions).arity == -2 - # After Rails 2.3.3, the method had only two args, the last one optional - def self.sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name) - attrs = expand_hash_conditions_for_aggregates(attrs) - - conditions = attrs.map do |attr, value| - unless value.is_a?(Hash) - attr = attr.to_s - - # Extract table name from qualified attribute names. - if attr.include?('.') - table_name, attr = attr.split('.', 2) - table_name = connection.quote_table_name(table_name) - end - - if columns_hash[attr].is_a?(SpatialColumn) - if value.is_a?(Array) - attrs[attr.to_sym]= "BOX3D(" + value[0].join(" ") + "," + value[1].join(" ") + ")" - "#{table_name}.#{connection.quote_column_name(attr)} && SetSRID(?::box3d, #{value[2] || DEFAULT_SRID} ) " - elsif value.is_a?(Envelope) - attrs[attr.to_sym]= "BOX3D(" + value.lower_corner.text_representation + "," + value.upper_corner.text_representation + ")" - "#{table_name}.#{connection.quote_column_name(attr)} && SetSRID(?::box3d, #{value.srid} ) " - else - "#{table_name}.#{connection.quote_column_name(attr)} && ? " - end - else - attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value) - end - else - sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s)) - end - end.join(' AND ') - - replace_bind_variables(conditions, expand_range_bind_variables(attrs.values)) - end - else - raise "Spatial Adapter will not work with this version of Rails" - end - end - end -end - -ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do - - include SpatialAdapter - - alias :original_native_database_types :native_database_types - def native_database_types - original_native_database_types.merge!(geometry_data_types) - end - - alias :original_quote :quote - #Redefines the quote method to add behaviour for when a Geometry is encountered - def quote(value, column = nil) - if value.kind_of?(GeoRuby::SimpleFeatures::Geometry) - "'#{value.as_hex_ewkb}'" - else - original_quote(value,column) - end - end - - alias :original_tables :tables - def tables(name = nil) #:nodoc: - original_tables(name) + views(name) - end - - def views(name = nil) #:nodoc: - schemas = schema_search_path.split(/,/).map { |p| quote(p.strip) }.join(',') - query(<<-SQL, name).map { |row| row[0] } - SELECT viewname - FROM pg_views - WHERE schemaname IN (#{schemas}) - SQL - end - - def create_table(name, options = {}) - table_definition = ActiveRecord::ConnectionAdapters::PostgreSQLTableDefinition.new(self) - table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false - - yield table_definition - - if options[:force] - drop_table(name) rescue nil - end - - create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE " - create_sql << "#{name} (" - create_sql << table_definition.to_sql - create_sql << ") #{options[:options]}" - execute create_sql - - #added to create the geometric columns identified during the table definition - unless table_definition.geom_columns.nil? - table_definition.geom_columns.each do |geom_column| - execute geom_column.to_sql(name) - end - end - end - - alias :original_remove_column :remove_column - def remove_column(table_name,column_name, options = {}) - columns(table_name).each do |col| - if col.name == column_name.to_s - #check if the column is geometric - unless geometry_data_types[col.type].nil? or - (options[:remove_using_dropgeometrycolumn] == false) - execute "SELECT DropGeometryColumn('#{table_name}','#{column_name}')" - else - original_remove_column(table_name,column_name) - end - end - end - end - - alias :original_add_column :add_column - def add_column(table_name, column_name, type, options = {}) - unless geometry_data_types[type].nil? or (options[:create_using_addgeometrycolumn] == false) - geom_column = ActiveRecord::ConnectionAdapters::PostgreSQLColumnDefinition.new(self,column_name, type, nil,nil,options[:null],options[:srid] || -1 , options[:with_z] || false , options[:with_m] || false) - execute geom_column.to_sql(table_name) - else - original_add_column(table_name,column_name,type,options) - end - end - - - - #Adds a GIST spatial index to a column. Its name will be __spatial_index unless the key :name is present in the options hash, in which case its value is taken as the name of the index. - def add_index(table_name,column_name,options = {}) - index_name = options[:name] || index_name(table_name,:column => Array(column_name)) - if options[:spatial] - execute "CREATE INDEX #{index_name} ON #{table_name} USING GIST (#{Array(column_name).join(", ")} GIST_GEOMETRY_OPS)" - else - super - end - end - - - def indexes(table_name, name = nil) #:nodoc: - result = query(<<-SQL, name) - SELECT i.relname, d.indisunique, a.attname , am.amname - FROM pg_class t, pg_class i, pg_index d, pg_attribute a, pg_am am - WHERE i.relkind = 'i' - AND d.indexrelid = i.oid - AND d.indisprimary = 'f' - AND t.oid = d.indrelid - AND i.relam = am.oid - AND t.relname = '#{table_name}' - AND a.attrelid = t.oid - AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum - OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum - OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum - OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum - OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum ) - ORDER BY i.relname - SQL - - current_index = nil - indexes = [] - - result.each do |row| - if current_index != row[0] - indexes << ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, row[0], row[1] == "t", row[3] == "gist" ,[]) #index type gist indicates a spatial index (probably not totally true but let's simplify!) - current_index = row[0] - end - - indexes.last.columns << row[2] - end - - indexes - end - - def columns(table_name, name = nil) #:nodoc: - raw_geom_infos = column_spatial_info(table_name) - - column_definitions(table_name).collect do |name, type, default, notnull| - if type =~ /geometry/i - raw_geom_info = raw_geom_infos[name] - if ActiveRecord::VERSION::STRING >= "2.0.0" - if raw_geom_info.nil? - ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_simplified(name,default,notnull == "f") - else - ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.new(name, default,raw_geom_info.type, notnull == "f", raw_geom_info.srid, raw_geom_info.with_z, raw_geom_info.with_m) - end - else - if raw_geom_info.nil? - ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_simplified(name,default_value(default),notnull == "f") - else - ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.new(name, default_value(default), raw_geom_info.type, notnull == "f", raw_geom_info.srid, raw_geom_info.with_z, raw_geom_info.with_m) - end - end - else - if ActiveRecord::VERSION::STRING >= "2.0.0" - ActiveRecord::ConnectionAdapters::Column.new(name, ActiveRecord::ConnectionAdapters::PostgreSQLColumn.extract_value_from_default( default), type,notnull == "f") - else - #Vit Ondruch & Tilmann Singer 's patch - ActiveRecord::ConnectionAdapters::Column.new(name, default_value(default), translate_field_type(type),notnull == "f") - end - end - end - end - - # For version of Rails where exists disable_referential_integrity - if self.instance_methods.include? "disable_referential_integrity" - #Pete Deffendol's patch - alias :original_disable_referential_integrity :disable_referential_integrity - def disable_referential_integrity(&block) #:nodoc: - ignore_tables = %w{ geometry_columns spatial_ref_sys } + views - execute(tables.select { |name| !ignore_tables.include?(name) }.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";")) - yield - ensure - execute(tables.select { |name| !ignore_tables.include?(name)}.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";")) - end - end - - private - - def column_spatial_info(table_name) - constr = query <<-end_sql -SELECT * FROM geometry_columns WHERE f_table_name = '#{table_name}' - end_sql - - raw_geom_infos = {} - constr.each do |constr_def_a| - raw_geom_infos[constr_def_a[3]] ||= ActiveRecord::ConnectionAdapters::RawGeomInfo.new - raw_geom_infos[constr_def_a[3]].type = constr_def_a[6] - raw_geom_infos[constr_def_a[3]].dimension = constr_def_a[4].to_i - raw_geom_infos[constr_def_a[3]].srid = constr_def_a[5].to_i - - if raw_geom_infos[constr_def_a[3]].type[-1] == ?M - raw_geom_infos[constr_def_a[3]].with_m = true - raw_geom_infos[constr_def_a[3]].type.chop! - else - raw_geom_infos[constr_def_a[3]].with_m = false - end - end - - raw_geom_infos.each_value do |raw_geom_info| - #check the presence of z and m - raw_geom_info.convert! - end - - raw_geom_infos - - end - -end - -module ActiveRecord - module ConnectionAdapters - class RawGeomInfo < Struct.new(:type,:srid,:dimension,:with_z,:with_m) #:nodoc: - def convert! - self.type = "geometry" if self.type.nil? #if geometry the geometrytype constraint is not present : need to set the type here then - - if dimension == 4 - self.with_m = true - self.with_z = true - elsif dimension == 3 - if with_m - self.with_z = false - self.with_m = true - else - self.with_z = true - self.with_m = false - end - else - self.with_z = false - self.with_m = false - end - end - end - end -end - - -module ActiveRecord - module ConnectionAdapters - class PostgreSQLTableDefinition < TableDefinition - attr_reader :geom_columns - - def column(name, type, options = {}) - unless (@base.geometry_data_types[type.to_sym].nil? or - (options[:create_using_addgeometrycolumn] == false)) - - geom_column = PostgreSQLColumnDefinition.new(@base,name, type) - geom_column.null = options[:null] - geom_column.srid = options[:srid] || -1 - geom_column.with_z = options[:with_z] || false - geom_column.with_m = options[:with_m] || false - - @geom_columns = [] if @geom_columns.nil? - @geom_columns << geom_column - else - super(name,type,options) - end - end - - SpatialAdapter.geometry_data_types.keys.each do |column_type| - class_eval <<-EOV - def #{column_type}(*args) - options = args.extract_options! - column_names = args - - column_names.each { |name| column(name, '#{column_type}', options) } - end - EOV - end - end - - class PostgreSQLColumnDefinition < ColumnDefinition - attr_accessor :srid, :with_z,:with_m - attr_reader :spatial - - def initialize(base = nil, name = nil, type=nil, limit=nil, default=nil,null=nil,srid=-1,with_z=false,with_m=false) - super(base, name, type, limit, default,null) - @spatial=true - @srid=srid - @with_z=with_z - @with_m=with_m - end - - def to_sql(table_name) - if @spatial - type_sql = type_to_sql(type.to_sym) - type_sql += "M" if with_m and !with_z - if with_m and with_z - dimension = 4 - elsif with_m or with_z - dimension = 3 - else - dimension = 2 - end - - column_sql = "SELECT AddGeometryColumn('#{table_name}','#{name}',#{srid},'#{type_sql}',#{dimension})" - column_sql += ";ALTER TABLE #{table_name} ALTER #{name} SET NOT NULL" if null == false - column_sql - else - super - end - end - - - private - def type_to_sql(name, limit=nil) - base.type_to_sql(name, limit) rescue name - end - - end - - end -end - -#Would prefer creation of a PostgreSQLColumn type instead but I would need to reimplement methods where Column objects are instantiated so I leave it like this -module ActiveRecord - module ConnectionAdapters - class SpatialPostgreSQLColumn < Column - - include SpatialColumn - - #Transforms a string to a geometry. PostGIS returns a HewEWKB string. - def self.string_to_geometry(string) - return string unless string.is_a?(String) - GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(string) rescue nil - end - - def self.create_simplified(name,default,null = true) - new(name,default,"geometry",null,nil,nil,nil) - end - - end - end -end - diff --git a/vendor/plugins/spatial_adapter/rakefile.rb b/vendor/plugins/spatial_adapter/rakefile.rb deleted file mode 100644 index fbc679a3..00000000 --- a/vendor/plugins/spatial_adapter/rakefile.rb +++ /dev/null @@ -1,36 +0,0 @@ -$:.unshift(File.join(File.dirname(__FILE__) ,'../../gems/georuby/lib/')) -require 'rake' -require 'rake/testtask' -require 'rake/rdoctask' -require 'rake/gempackagetask' - -namespace :test do - Rake::TestTask::new(:mysql => "db:mysql" ) do |t| - t.test_files = FileList['test/*_mysql_test.rb'] - t.verbose = true - end - - Rake::TestTask::new(:postgis => "db:postgis" ) do |t| - t.test_files = FileList['test/*_postgis_test.rb'] - t.verbose = true - end -end - -namespace :db do - task :mysql do - load('test/schema/schema_mysql.rb') - end - - task :postgis do - load('test/schema/schema_postgis.rb') - end -end - -desc "Generate the documentation" -Rake::RDocTask::new do |rdoc| - rdoc.rdoc_dir = 'doc/' - rdoc.title = "Spatial Adapater for Rails Documentation" - rdoc.options << '--line-numbers' << '--inline-source' - rdoc.rdoc_files.include('README.rdoc') - rdoc.rdoc_files.include('lib/**/*.rb') -end diff --git a/vendor/plugins/spatial_adapter/test/access_mysql_test.rb b/vendor/plugins/spatial_adapter/test/access_mysql_test.rb deleted file mode 100644 index a8179c41..00000000 --- a/vendor/plugins/spatial_adapter/test/access_mysql_test.rb +++ /dev/null @@ -1,87 +0,0 @@ -$:.unshift(File.dirname(__FILE__)) - -require 'test/unit' -require 'common/common_mysql' -require 'models/models_mysql' - - -class AccessMysqlTest < Test::Unit::TestCase - - def test_point - pt = TablePoint.new(:data => "Test", :geom => Point.from_x_y(1.2,4.5)) - assert(pt.save) - - pt = TablePoint.find(:first) - assert(pt) - assert_equal("Test",pt.data) - assert_equal(Point.from_x_y(1.2,4.5),pt.geom) - - end - - def test_line_string - ls = TableLineString.new(:value => 3, :geom => LineString.from_coordinates([[1.4,2.5],[1.5,6.7]])) - assert(ls.save) - - ls = TableLineString.find(:first) - assert(ls) - assert_equal(3,ls.value) - assert_equal(LineString.from_coordinates([[1.4,2.5],[1.5,6.7]]),ls.geom) - - end - - def test_polygon - pg = TablePolygon.new(:geom => Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]])) - assert(pg.save) - - pg = TablePolygon.find(:first) - assert(pg) - assert_equal(Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]]),pg.geom) - end - - def test_muti_point - mp = TableMultiPoint.new(:geom => MultiPoint.from_coordinates([[12.4,-123.3],[-65.1,123.4],[123.55555555,123]])) - assert(mp.save) - - mp = TableMultiPoint.find(:first) - assert(mp) - assert_equal(MultiPoint.from_coordinates([[12.4,-123.3],[-65.1,123.4],[123.55555555,123]]),mp.geom) - end - - def test_multi_line_string - ml = TableMultiLineString.new(:geom => MultiLineString.from_line_strings([LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012]]),LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012],[45.123,123.3]])])) - assert(ml.save) - - ml = TableMultiLineString.find(:first) - assert(ml) - assert_equal(MultiLineString.from_line_strings([LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012]]),LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012],[45.123,123.3]])]),ml.geom) - end - - def test_multi_polygon - mp = TableMultiPolygon.new( :geom => MultiPolygon.from_polygons([Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]]),Polygon.from_coordinates([[[0,0],[4,0],[4,4],[0,4],[0,0]],[[1,1],[3,1],[3,3],[1,3],[1,1]]])])) - assert(mp.save) - - mp = TableMultiPolygon.find(:first) - assert(mp) - assert_equal(MultiPolygon.from_polygons([Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]]),Polygon.from_coordinates([[[0,0],[4,0],[4,4],[0,4],[0,0]],[[1,1],[3,1],[3,3],[1,3],[1,1]]])]),mp.geom) - end - - def test_geometry - gm = TableGeometry.new(:geom => LineString.from_coordinates([[12.4,-45.3],[45.4,41.6],[4.456,1.0698]])) - assert(gm.save) - - gm = TableGeometry.find(:first) - assert(gm) - assert_equal(LineString.from_coordinates([[12.4,-45.3],[45.4,41.6],[4.456,1.0698]]),gm.geom) - end - - def test_geometry_collection - gc = TableGeometryCollection.new(:geom => GeometryCollection.from_geometries([Point.from_x_y(4.67,45.4),LineString.from_coordinates([[5.7,12.45],[67.55,54]])])) - assert(gc.save) - - gc = TableGeometryCollection.find(:first) - assert(gc) - assert_equal(GeometryCollection.from_geometries([Point.from_x_y(4.67,45.4),LineString.from_coordinates([[5.7,12.45],[67.55,54]])]),gc.geom) - end - - -end diff --git a/vendor/plugins/spatial_adapter/test/access_postgis_test.rb b/vendor/plugins/spatial_adapter/test/access_postgis_test.rb deleted file mode 100644 index b6612107..00000000 --- a/vendor/plugins/spatial_adapter/test/access_postgis_test.rb +++ /dev/null @@ -1,151 +0,0 @@ -$:.unshift(File.dirname(__FILE__)) - -require 'test/unit' -require 'common/common_postgis' -require 'models/models_postgis' - - -class AccessPostgisTest < Test::Unit::TestCase - - def test_point - pt = TablePoint.new(:data => "Test", :geom => Point.from_x_y(1.2,4.5)) - assert(pt.save) - - pt = TablePoint.find(:first) - assert(pt) - assert_equal("Test",pt.data) - assert_equal(Point.from_x_y(1.2,4.5),pt.geom) - - pts = TablePoint.find(:all) - pts.each do |pt| - assert(pt.geom.is_a?(Point)) - end - - end - - def test_keyword_column_point - pt = TableKeywordColumnPoint.new(:location => Point.from_x_y(1.2,4.5)) - assert(pt.save) - - pt = TableKeywordColumnPoint.find(:first) - assert(pt) - assert_equal(Point.from_x_y(1.2,4.5),pt.location) - - end - - def test_line_string - ls = TableLineString.new(:value => 3, :geom => LineString.from_coordinates([[1.4,2.5],[1.5,6.7]])) - assert(ls.save) - - ls = TableLineString.find(:first) - assert(ls) - assert_equal(3,ls.value) - assert_equal(LineString.from_coordinates([[1.4,2.5],[1.5,6.7]]),ls.geom) - - end - - def test_polygon - pg = TablePolygon.new(:geom => Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]])) - assert(pg.save) - - pg = TablePolygon.find(:first) - assert(pg) - assert_equal(Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]]),pg.geom) - end - - def test_muti_point - mp = TableMultiPoint.new(:geom => MultiPoint.from_coordinates([[12.4,-123.3],[-65.1,123.4],[123.55555555,123]])) - assert(mp.save) - - mp = TableMultiPoint.find(:first) - assert(mp) - assert_equal(MultiPoint.from_coordinates([[12.4,-123.3],[-65.1,123.4],[123.55555555,123]]),mp.geom) - end - - def test_multi_line_string - ml = TableMultiLineString.new(:geom => MultiLineString.from_line_strings([LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012]]),LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012],[45.123,123.3]])])) - assert(ml.save) - - ml = TableMultiLineString.find(:first) - assert(ml) - assert_equal(MultiLineString.from_line_strings([LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012]]),LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012],[45.123,123.3]])]),ml.geom) - end - - def test_multi_polygon - mp = TableMultiPolygon.new( :geom => MultiPolygon.from_polygons([Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]]),Polygon.from_coordinates([[[0,0],[4,0],[4,4],[0,4],[0,0]],[[1,1],[3,1],[3,3],[1,3],[1,1]]])])) - assert(mp.save) - - mp = TableMultiPolygon.find(:first) - assert(mp) - assert_equal(MultiPolygon.from_polygons([Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]]),Polygon.from_coordinates([[[0,0],[4,0],[4,4],[0,4],[0,0]],[[1,1],[3,1],[3,3],[1,3],[1,1]]])]),mp.geom) - end - - def test_geometry - gm = TableGeometry.new(:geom => LineString.from_coordinates([[12.4,-45.3],[45.4,41.6],[4.456,1.0698]])) - assert(gm.save) - - gm = TableGeometry.find(:first) - assert(gm) - assert_equal(LineString.from_coordinates([[12.4,-45.3],[45.4,41.6],[4.456,1.0698]]),gm.geom) - end - - def test_geometry_collection - gc = TableGeometryCollection.new(:geom => GeometryCollection.from_geometries([Point.from_x_y(4.67,45.4),LineString.from_coordinates([[5.7,12.45],[67.55,54]])])) - assert(gc.save) - - gc = TableGeometryCollection.find(:first) - assert(gc) - assert_equal(GeometryCollection.from_geometries([Point.from_x_y(4.67,45.4),LineString.from_coordinates([[5.7,12.45],[67.55,54]])]),gc.geom) - end - - def test_3dz_points - pt = Table3dzPoint.new(:data => "Hello!",:geom => Point.from_x_y_z(-1.6,2.8,-3.4)) - assert(pt.save) - - pt = Table3dzPoint.find(:first) - assert(pt) - assert_equal(Point.from_x_y_z(-1.6,2.8,-3.4),pt.geom) - end - - def test_3dm_points - pt = Table3dmPoint.new(:geom => Point.from_x_y_m(-1.6,2.8,-3.4)) - assert(pt.save) - - pt = Table3dmPoint.find(:first) - assert(pt) - assert_equal(Point.from_x_y_m(-1.6,2.8,-3.4),pt.geom) - end - - def test_4d_point - pt = Table4dPoint.new(:geom => Point.from_x_y_z_m(-1.6,2.8,-3.4,15)) - assert(pt.save) - - pt = Table4dPoint.find(:first) - assert(pt) - assert_equal(Point.from_x_y_z_m(-1.6,2.8,-3.4,15),pt.geom) - end - - def test_srid_line_string - ls = TableSridLineString.new(:geom => LineString.from_coordinates([[1.4,2.5],[1.5,6.7]],123)) - assert(ls.save) - - ls = TableSridLineString.find(:first) - assert(ls) - ls_e = LineString.from_coordinates([[1.4,2.5],[1.5,6.7]],123) - assert_equal(ls_e,ls.geom) - assert_equal(ls_e.srid,ls.geom.srid) - end - - def test_srid_4d_polygon - pg = TableSrid4dPolygon.new(:geom => Polygon.from_coordinates([[[0,0,2,-45.1],[4,0,2,5],[4,4,2,4.67],[0,4,2,1.34],[0,0,2,-45.1]],[[1,1,2,12.3],[3,1,2,123],[3,3,2,12.2],[1,3,2,12],[1,1,2,12.3]]],123,true,true)) - assert(pg.save) - - pg = TableSrid4dPolygon.find(:first) - assert(pg) - pg_e = Polygon.from_coordinates([[[0,0,2,-45.1],[4,0,2,5],[4,4,2,4.67],[0,4,2,1.34],[0,0,2,-45.1]],[[1,1,2,12.3],[3,1,2,123],[3,3,2,12.2],[1,3,2,12],[1,1,2,12.3]]],123,true,true) - assert_equal(pg_e,pg.geom) - assert_equal(pg_e.srid,pg.geom.srid) - end - - -end diff --git a/vendor/plugins/spatial_adapter/test/common/common_mysql.rb b/vendor/plugins/spatial_adapter/test/common/common_mysql.rb deleted file mode 100644 index 1bdd2487..00000000 --- a/vendor/plugins/spatial_adapter/test/common/common_mysql.rb +++ /dev/null @@ -1,18 +0,0 @@ -$:.unshift(File.dirname(__FILE__) + '/../../lib') - -require 'rubygems' -require 'activerecord' - -ActiveRecord::Base.establish_connection(YAML.load_file(File.dirname(__FILE__) + '/../db/database_mysql.yml')) - -require File.dirname(__FILE__) + '/../../init.rb' - - - - - - - - - - diff --git a/vendor/plugins/spatial_adapter/test/common/common_postgis.rb b/vendor/plugins/spatial_adapter/test/common/common_postgis.rb deleted file mode 100644 index 183080f3..00000000 --- a/vendor/plugins/spatial_adapter/test/common/common_postgis.rb +++ /dev/null @@ -1,19 +0,0 @@ -$:.unshift(File.dirname(__FILE__) + '/../../lib') - -require 'rubygems' -require 'activerecord' - - -ActiveRecord::Base.establish_connection(YAML.load_file(File.dirname(__FILE__) + '/../db/database_postgis.yml')) - -require File.dirname(__FILE__) + '/../../init.rb' - - - - - - - - - - diff --git a/vendor/plugins/spatial_adapter/test/db/database_mysql.yml b/vendor/plugins/spatial_adapter/test/db/database_mysql.yml deleted file mode 100644 index 96dad012..00000000 --- a/vendor/plugins/spatial_adapter/test/db/database_mysql.yml +++ /dev/null @@ -1,5 +0,0 @@ -adapter: mysql -database: spatial_adapter_plugin_test -username: root -password: -host: localhost diff --git a/vendor/plugins/spatial_adapter/test/db/database_postgis.yml b/vendor/plugins/spatial_adapter/test/db/database_postgis.yml deleted file mode 100644 index 18ba7290..00000000 --- a/vendor/plugins/spatial_adapter/test/db/database_postgis.yml +++ /dev/null @@ -1,2 +0,0 @@ -adapter: postgresql -database: spatial_adapter_plugin_test diff --git a/vendor/plugins/spatial_adapter/test/find_mysql_test.rb b/vendor/plugins/spatial_adapter/test/find_mysql_test.rb deleted file mode 100644 index 029e7d11..00000000 --- a/vendor/plugins/spatial_adapter/test/find_mysql_test.rb +++ /dev/null @@ -1,69 +0,0 @@ -$:.unshift(File.dirname(__FILE__)) - -require 'test/unit' -require 'common/common_mysql' - -class Park < ActiveRecord::Base -end - -class FindMysqlTest < Test::Unit::TestCase - - def setup - ActiveRecord::Schema.define() do - create_table "parks", :options => "ENGINE=MyISAM" , :force => true do |t| - t.column "data" , :string, :limit => 100 - t.column "geom", :point,:null=>false - end - add_index "parks","geom",:spatial=>true,:name => "example_spatial_index" - end - - pt = Park.new(:data => "Point1", :geom => Point.from_x_y(1.2,0.75)) - assert(pt.save) - - pt = Park.new(:data => "Point2",:geom => Point.from_x_y(0.6,1.3)) - assert(pt.save) - - pt = Park.new(:data => "Point3", :geom => Point.from_x_y(2.5,2)) - assert(pt.save) - end - - def test_find_by_geom_column - #the linestring bbox is [0 0,2 2] - pts = Park.find_all_by_geom(LineString.from_coordinates([[0,0],[2,2]])) - assert(pts) - assert(pts.is_a?(Array)) - assert_equal(2,pts.length) - assert(pts[0].data == "Point1" ||pts[1].data == "Point1" ) - assert(pts[0].data == "Point2" ||pts[1].data == "Point2" ) - - #the linestring bbox is [2.49 1.99,2.51 2.01] - pts = Park.find_all_by_geom(LineString.from_coordinates([[2.49,1.99],[2.51,2.01]])) - assert(pts) - assert(pts.is_a?(Array)) - assert_equal(1,pts.length) - assert(pts[0].data == "Point3") - - end - - def test_find_by_geom_column_bbox_condition - pts = Park.find_all_by_geom([[0,0],[2,2]]) - assert(pts) - assert(pts.is_a?(Array)) - assert_equal(2,pts.length) - assert(pts[0].data == "Point1" ||pts[1].data == "Point1" ) - assert(pts[0].data == "Point2" ||pts[1].data == "Point2" ) - - pts = Park.find_all_by_geom([[2.49,1.99],[2.51,2.01]]) - assert(pts) - assert(pts.is_a?(Array)) - assert_equal(1,pts.length) - assert(pts[0].data == "Point3") - end - - def test_find_by_non_geom_column_condition - pts = Park.find_all_by_data 'Point1' - assert(pts,'find by non-geom column is not working') - end - - -end diff --git a/vendor/plugins/spatial_adapter/test/find_postgis_test.rb b/vendor/plugins/spatial_adapter/test/find_postgis_test.rb deleted file mode 100644 index e472f331..00000000 --- a/vendor/plugins/spatial_adapter/test/find_postgis_test.rb +++ /dev/null @@ -1,71 +0,0 @@ -$:.unshift(File.dirname(__FILE__)) - -require 'test/unit' -require 'common/common_postgis' - -class Park < ActiveRecord::Base -end - -class FindPostgisTest < Test::Unit::TestCase - - def setup - ActiveRecord::Schema.define() do - create_table "parks", :force => true do |t| - t.column "data" , :string, :limit => 100 - t.column "value", :integer - t.column "geom", :point,:null=>false,:srid=>123 - end - add_index "parks","geom",:spatial=>true,:name => "example_spatial_index" - end - - pt = Park.new(:data => "Point1", :geom => Point.from_x_y(1.2,0.75,123)) - assert(pt.save) - - pt = Park.new(:data => "Point2",:geom => Point.from_x_y(0.6,1.3,123)) - assert(pt.save) - - pt = Park.new(:data => "Point3", :geom => Point.from_x_y(2.5,2,123)) - assert(pt.save) - - end - - def test_find_by_geom_column - - pts = Park.find_all_by_geom(LineString.from_coordinates([[0,0],[2,2]],123)) - assert(pts) - assert(pts.is_a?(Array)) - assert_equal(2,pts.length) - assert(pts[0].data == "Point1" ||pts[1].data == "Point1" ) - assert(pts[0].data == "Point2" ||pts[1].data == "Point2" ) - - pts = Park.find_all_by_geom(LineString.from_coordinates([[2.49,1.99],[2.51,2.01]],123)) - assert(pts) - assert(pts.is_a?(Array)) - assert_equal(1,pts.length) - assert(pts[0].data == "Point3") - - end - - def test_find_by_geom_column_bbox_condition - pts = Park.find_all_by_geom([[0,0],[2,2],123]) - assert(pts) - assert(pts.is_a?(Array)) - assert_equal(2,pts.length) - assert(pts[0].data == "Point1" ||pts[1].data == "Point1" ) - assert(pts[0].data == "Point2" ||pts[1].data == "Point2" ) - - pts = Park.find_all_by_geom([[0,0],[2,2],123]) - assert(pts) - assert(pts.is_a?(Array)) - assert_equal(2,pts.length) - assert(pts[0].data == "Point1" ||pts[1].data == "Point1" ) - assert(pts[0].data == "Point2" ||pts[1].data == "Point2" ) - end - - def test_find_by_non_geom_column_condition - pts = Park.find_all_by_data 'Point1' - assert(pts,'find by non-geom column is not working') - end - - -end diff --git a/vendor/plugins/spatial_adapter/test/fixture_mysql_test.rb b/vendor/plugins/spatial_adapter/test/fixture_mysql_test.rb deleted file mode 100644 index 3d7567c7..00000000 --- a/vendor/plugins/spatial_adapter/test/fixture_mysql_test.rb +++ /dev/null @@ -1,23 +0,0 @@ -$:.unshift(File.dirname(__FILE__)) - -require 'test/unit' -require 'common/common_mysql' - - -class FixtureMysqlTest < Test::Unit::TestCase - def test_long_fixture - assert(1,Polygon.from_coordinates([[[144.857742,13.598263], -[144.862362,13.589922],[144.865169,13.587336],[144.862927,13.587665], -[144.861292,13.587321],[144.857597,13.585299],[144.847845,13.573858], -[144.846225,13.571014],[144.843605,13.566047],[144.842157,13.563831], -[144.841202,13.561991],[144.838305,13.556465],[144.834645,13.549919], -[144.834352,13.549395],[144.833825,13.548454],[144.831839,13.544451], -[144.830845,13.54081],[144.821543,13.545695],[144.8097993,13.55186285], -[144.814753,13.55755],[144.816744,13.56176944],[144.818862,13.566258], -[144.819402,13.568565],[144.822373,13.572223], -[144.8242032,13.57381149],[144.82634,13.575666],[144.83416,13.590365], -[144.83514,13.595657],[144.834284,13.59652],[144.834024,13.598031], -[144.83719,13.598061],[144.857742,13.598263]]]).to_fixture_format.split(/\s+/)) - - end -end diff --git a/vendor/plugins/spatial_adapter/test/fixture_postgis_test.rb b/vendor/plugins/spatial_adapter/test/fixture_postgis_test.rb deleted file mode 100644 index 2b3b0f6d..00000000 --- a/vendor/plugins/spatial_adapter/test/fixture_postgis_test.rb +++ /dev/null @@ -1,23 +0,0 @@ -$:.unshift(File.dirname(__FILE__)) - -require 'test/unit' -require 'common/common_postgis' - - -class FixturePostgisTest < Test::Unit::TestCase - def test_long_fixture - assert(1,Polygon.from_coordinates([[[144.857742,13.598263], -[144.862362,13.589922],[144.865169,13.587336],[144.862927,13.587665], -[144.861292,13.587321],[144.857597,13.585299],[144.847845,13.573858], -[144.846225,13.571014],[144.843605,13.566047],[144.842157,13.563831], -[144.841202,13.561991],[144.838305,13.556465],[144.834645,13.549919], -[144.834352,13.549395],[144.833825,13.548454],[144.831839,13.544451], -[144.830845,13.54081],[144.821543,13.545695],[144.8097993,13.55186285], -[144.814753,13.55755],[144.816744,13.56176944],[144.818862,13.566258], -[144.819402,13.568565],[144.822373,13.572223], -[144.8242032,13.57381149],[144.82634,13.575666],[144.83416,13.590365], -[144.83514,13.595657],[144.834284,13.59652],[144.834024,13.598031], -[144.83719,13.598061],[144.857742,13.598263]]]).to_fixture_format.split(/\s+/)) - - end -end diff --git a/vendor/plugins/spatial_adapter/test/migration_mysql_test.rb b/vendor/plugins/spatial_adapter/test/migration_mysql_test.rb deleted file mode 100644 index 7913f933..00000000 --- a/vendor/plugins/spatial_adapter/test/migration_mysql_test.rb +++ /dev/null @@ -1,204 +0,0 @@ -$:.unshift(File.dirname(__FILE__)) - -require 'test/unit' -require 'common/common_mysql' - -class Park < ActiveRecord::Base -end - -class CxGeographiclocation < ActiveRecord::Base - set_table_name "cx_geographiclocation" -end - -class MigrationMysqlTest < Test::Unit::TestCase - - def test_creation_modification - #creation - #add column - #remove column - #add index - #remove index - - connection = ActiveRecord::Base.connection - - #create a table with a geometric column - ActiveRecord::Schema.define() do - create_table "parks", :options => "ENGINE=MyISAM" , :force => true do |t| - t.column "data" , :string, :limit => 100 - t.column "value", :integer - t.column "geom", :polygon,:null=>false - end - end - - #TEST - assert_equal(4,connection.columns("parks").length) # the 3 defined + id - connection.columns("parks").each do |col| - if col.name == "geom" - assert(col.is_a?(SpatialColumn)) - assert(:polygon,col.geometry_type) - assert(:geometry,col.type) - assert(col.null == false) - end - end - - ActiveRecord::Schema.define() do - add_column "parks","geom2", :multi_point - end - - #TEST - assert_equal(5,connection.columns("parks").length) - connection.columns("parks").each do |col| - if col.name == "geom2" - assert(col.is_a?(SpatialColumn)) - assert(:multi_point,col.geometry_type) - assert(:geometry,col.type) - assert(col.null != false) - end - end - - ActiveRecord::Schema.define() do - remove_column "parks","geom2" - end - - #TEST - assert_equal(4,connection.columns("parks").length) - has_geom2= false - connection.columns("parks").each do |col| - if col.name == "geom2" - has_geom2=true - end - end - assert(!has_geom2) - - #TEST - assert_equal(0,connection.indexes("parks").length) #index on id does not count - - ActiveRecord::Schema.define() do - add_index "parks","geom",:spatial=>true - end - - #TEST - assert_equal(1,connection.indexes("parks").length) - assert(connection.indexes("parks")[0].spatial) - - ActiveRecord::Schema.define() do - remove_index "parks","geom" - end - - #TEST - assert_equal(0,connection.indexes("parks").length) - - end - - - def test_dump - #Force the creation a table - ActiveRecord::Schema.define() do - create_table "parks", :options => "ENGINE=MyISAM" , :force => true do |t| - t.column "data" , :string, :limit => 100 - t.column "value", :integer - t.column "geom", :polygon,:null=>false - end - - add_index "parks","geom",:spatial=>true,:name => "example_spatial_index" - - end - - #dump it : tables from other tests will be dumped too but not a problem - File.open('schema.rb', "w") do |file| - ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) - end - - #load it again - load('schema.rb') - - File.delete('schema.rb') - - #reset - connection = ActiveRecord::Base.connection - - columns = connection.columns("parks") - assert(4,columns.length) - - connection.columns("parks").each do |col| - if col.name == "geom" - assert(col.is_a?(SpatialColumn)) - assert(:polygon,col.geometry_type) - assert(:geometry,col.type) - assert(col.null == false) - end - end - - assert_equal(1,connection.indexes("parks").length) - assert(connection.indexes("parks")[0].spatial) - assert_equal("example_spatial_index",connection.indexes("parks")[0].name) - end - - def test_teresa - connection = ActiveRecord::Base.connection - - #creation - ActiveRecord::Schema.define() do - create_table(:cx_geographiclocation, :primary_key => "GeographicLocationID", :options=>"ENGINE=MyISAM", :force => true ) do |t| - t.column "CountryID", :integer, :null => false - t.column "AddressLine1", :string, :limit => 100, :null => false - t.column "AddressLine2", :string, :limit => 100 - t.column "AddressLine3", :string, :limit => 50 - t.column "City", :string, :limit => 50 - t.column "StateProvince", :string, :limit => 50 - t.column "PostalCode", :string, :limit => 30 - t.column "Geocode", :geometry, :null => false #:geometry ok too : col.geometry_type test to change below - end - end - - #test creation - assert_equal(9,connection.columns("cx_geographiclocation").length) - connection.columns("cx_geographiclocation").each do |col| - if col.name == "Geocode" - assert(col.is_a?(SpatialColumn)) - assert(:geometry,col.type) - assert(:point,col.geometry_type) - assert(! col.null) - end - end - - #creation index - ActiveRecord::Schema.define() do - add_index "cx_geographiclocation", ["addressline1"], :name => "ix_cx_geographiclocation_addressline1" - add_index "cx_geographiclocation", ["countryid"], :name => "ix_cx_geographiclocation_countryid" - add_index "cx_geographiclocation", ["Geocode"], :spatial=>true - end - - #test index - assert_equal(3,connection.indexes("cx_geographiclocation").length) - assert(connection.indexes("cx_geographiclocation")[2].spatial) - - #insertion points - 1.upto(10000) do |i| - pt = CxGeographiclocation.new("CountryID" => i, "AddressLine1" =>"Bouyoul", "Geocode" => Point.from_x_y(-180 + rand(360) + rand(),-90 + rand(180) + rand())) #insert floats - assert(pt.save) - end - - #should be outside the fetch by MBR - pt = CxGeographiclocation.new("CountryID" => 1337, "AddressLine1" =>"Bouyoul", "Geocode" => Point.from_x_y(-185,-90)) #insert floats - assert(pt.save) - - #fetch point and test - pt = CxGeographiclocation.find(:first) - assert(pt) - assert_equal("Bouyoul",pt.attributes["AddressLine1"]) - - - #fetch by MBR and test - pts = CxGeographiclocation.find_all_by_Geocode([[-181 + rand(),-91 + rand()],[181 + rand(),91 + rand()]]) #selects all : range limits are float - assert(pts) - assert(pts.is_a?(Array)) - assert_equal(10000,pts.length) - assert_equal("Bouyoul",pts[0].attributes["AddressLine1"]) - - end - - - - -end diff --git a/vendor/plugins/spatial_adapter/test/migration_postgis_test.rb b/vendor/plugins/spatial_adapter/test/migration_postgis_test.rb deleted file mode 100644 index b6748e52..00000000 --- a/vendor/plugins/spatial_adapter/test/migration_postgis_test.rb +++ /dev/null @@ -1,219 +0,0 @@ -$:.unshift(File.dirname(__FILE__)) - -require 'test/unit' -require 'common/common_postgis' - -class Park < ActiveRecord::Base -end - -class Viewpark < ActiveRecord::Base -end - - -class MigrationPostgisTest < Test::Unit::TestCase - - def test_creation_modification - #creation - #add column - #remove column - #add index - #remove index - - connection = ActiveRecord::Base.connection - - #create a table with a geometric column - ActiveRecord::Schema.define do - create_table "parks", :force => true do |t| - t.string "data", :limit => 100 - t.integer "value" - t.polygon "geom", :null=>false, :srid => 555 , :with_z => true,:with_m => true - end - end - - #TEST - assert_equal(4,connection.columns("parks").length) # the 3 defined + id - connection.columns("parks").each do |col| - if col.name == "geom" - assert(col.is_a?(SpatialColumn)) - assert(:polygon,col.geometry_type) - assert(:geometry,col.type) - assert(col.null == false) - assert(555,col.srid) - assert(col.with_z) - assert(col.with_m) - end - end - - ActiveRecord::Schema.define do - add_column "parks","geom2", :multi_point - end - - #TEST - assert_equal(5,connection.columns("parks").length) - connection.columns("parks").each do |col| - if col.name == "geom2" - assert(col.is_a?(SpatialColumn)) - assert(:multi_point,col.geometry_type) - assert(:geometry,col.type) - assert(col.null != false) - assert(-1,col.srid) - assert(!col.with_z) - assert(!col.with_m) - end - end - - ActiveRecord::Schema.define do - remove_column "parks","geom2" - end - - #TEST - assert_equal(4,connection.columns("parks").length) - has_geom2= false - connection.columns("parks").each do |col| - if col.name == "geom2" - has_geom2=true - end - end - assert(!has_geom2) - - #TEST - assert_equal(0,connection.indexes("parks").length) #index on id does not count - - ActiveRecord::Schema.define do - add_index "parks","geom",:spatial=>true - end - - #TEST - assert_equal(1,connection.indexes("parks").length) - assert(connection.indexes("parks")[0].spatial) - - ActiveRecord::Schema.define do - remove_index "parks", "geom" - end - - #TEST - assert_equal(0,connection.indexes("parks").length) - - ActiveRecord::Schema.define do - remove_column "parks","geom2" - end - - #testing with point - ActiveRecord::Schema.define do - add_column "parks","geom2", :point - end - - ActiveRecord::Schema.define do - add_index "parks","geom2",:spatial=>true,:name => "example_spatial_index" - end - - #TEST - assert_equal(1,connection.indexes("parks").length) - assert(connection.indexes("parks")[0].spatial) - assert_equal("example_spatial_index",connection.indexes("parks")[0].name) - - - end - - def test_keyword_column_name - ActiveRecord::Schema.define do - create_table "parks", :force => true do |t| - t.string "data", :limit => 100 - t.integer "value" - #location is a postgreSQL keyword and is surrounded by double-quotes ("") when appearing in constraint descriptions ; tests a bug corrected in version 39 - t.point "location", :null=>false,:srid => 0, :with_m => true, :with_z => true - end - end - - connection = ActiveRecord::Base.connection - columns = connection.columns("parks") - - assert_equal(4,columns.length) # the 3 defined + id - columns.each do |col| - if col.name == "location" - assert(col.is_a?(SpatialColumn)) - assert(:point,col.geometry_type) - assert(:geometry,col.type) - assert(col.null == false) - assert(0,col.srid) - assert(col.with_z) - assert(col.with_m) - end - end - end - - - def test_view - ActiveRecord::Schema.define do - create_table "parks", :force => true do |t| - t.column "data" , :string, :limit => 100 - t.column "value", :integer - t.column "geom", :point,:null=>false - end - end - - pt = Park.new(:data => "Test", :geom => Point.from_x_y(1.2,4.5)) - assert(pt.save) - - ActiveRecord::Base.connection.execute('CREATE VIEW viewparks as SELECT * from parks') - #if not ActiveRecord::Base.connection.execute("select * from geometry_columns where f_table_name = 'viewparks' and f_geometry_column = 'geom'") #do not add if already there - #mark the geom column in the view as geometric - #ActiveRecord::Base.connection.execute("insert into geometry_columns values ('','public','viewparks','geom',2,-1,'POINT')") - #end - - pt = Viewpark.find(:first) - assert(pt) - assert_equal("Test",pt.data) - assert_equal(Point.from_x_y(1.2,4.5),pt.geom) - - ActiveRecord::Base.connection.execute('DROP VIEW viewparks') - - end - - - def test_dump - #Force the creation of a table - ActiveRecord::Schema.define do - create_table "parks", :force => true do |t| - t.string "data" , :limit => 100 - t.integer "value" - t.multi_polygon "geom", :null=>false,:srid => 0, :with_m => true, :with_z => true - end - - add_index "parks","geom",:spatial=>true,:name => "example_spatial_index" - - end - - #dump it : tables from other tests will be dumped too but not a problem - File.open('schema.rb', "w") do |file| - ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) - end - - #load it again - load('schema.rb') - - #delete the schema file - File.delete('schema.rb') - - connection = ActiveRecord::Base.connection - columns = connection.columns("parks") - - assert_equal(4,columns.length) # the 3 defined + id - columns.each do |col| - if col.name == "geom" - assert(col.is_a?(SpatialColumn)) - assert(:multi_polygon,col.geometry_type) - assert(:geometry,col.type) - assert(col.null == false) - assert(0,col.srid) - assert(col.with_z) - assert(col.with_m) - end - end - - assert_equal(1,connection.indexes("parks").length) - assert(connection.indexes("parks")[0].spatial) - assert_equal("example_spatial_index",connection.indexes("parks")[0].name) - end - -end diff --git a/vendor/plugins/spatial_adapter/test/models/models_mysql.rb b/vendor/plugins/spatial_adapter/test/models/models_mysql.rb deleted file mode 100644 index df1f3bbf..00000000 --- a/vendor/plugins/spatial_adapter/test/models/models_mysql.rb +++ /dev/null @@ -1,25 +0,0 @@ -class TablePoint < ActiveRecord::Base -end - -class TableLineString < ActiveRecord::Base -end - -class TablePolygon < ActiveRecord::Base -end - -class TableMultiPoint < ActiveRecord::Base -end - -class TableMultiLineString < ActiveRecord::Base -end - -class TableMultiPolygon < ActiveRecord::Base -end - -class TableGeometry < ActiveRecord::Base -end - -class TableGeometryCollection < ActiveRecord::Base -end - - diff --git a/vendor/plugins/spatial_adapter/test/models/models_postgis.rb b/vendor/plugins/spatial_adapter/test/models/models_postgis.rb deleted file mode 100644 index 8b186aca..00000000 --- a/vendor/plugins/spatial_adapter/test/models/models_postgis.rb +++ /dev/null @@ -1,41 +0,0 @@ -class TablePoint < ActiveRecord::Base -end - -class TableKeywordColumnPoint < ActiveRecord::Base -end - -class TableLineString < ActiveRecord::Base -end - -class TablePolygon < ActiveRecord::Base -end - -class TableMultiPoint < ActiveRecord::Base -end - -class TableMultiLineString < ActiveRecord::Base -end - -class TableMultiPolygon < ActiveRecord::Base -end - -class TableGeometry < ActiveRecord::Base -end - -class TableGeometryCollection < ActiveRecord::Base -end - -class Table3dzPoint < ActiveRecord::Base -end - -class Table3dmPoint < ActiveRecord::Base -end - -class Table4dPoint < ActiveRecord::Base -end - -class TableSridLineString < ActiveRecord::Base -end - -class TableSrid4dPolygon < ActiveRecord::Base -end diff --git a/vendor/plugins/spatial_adapter/test/schema/schema_mysql.rb b/vendor/plugins/spatial_adapter/test/schema/schema_mysql.rb deleted file mode 100644 index 3ab7f74b..00000000 --- a/vendor/plugins/spatial_adapter/test/schema/schema_mysql.rb +++ /dev/null @@ -1,40 +0,0 @@ -require File.dirname(__FILE__) + '/../common/common_mysql.rb' - -ActiveRecord::Schema.define() do - - create_table "table_points", :options=> "ENGINE=MyISAM", :force => true do |t| - t.column "data", :string - t.column "geom", :point, :null=>false - end - - create_table "table_line_strings", :options=> "ENGINE=MyISAM", :force => true do |t| - t.column "value", :integer - t.column "geom", :line_string, :null=>false - end - - create_table "table_polygons", :options=> "ENGINE=MyISAM", :force => true do |t| - t.column "geom", :polygon, :null=>false - end - - create_table "table_multi_points", :options=> "ENGINE=MyISAM", :force => true do |t| - t.column "geom", :multi_point, :null=>false - end - - create_table "table_multi_line_strings", :options=> "ENGINE=MyISAM", :force => true do |t| - t.column "geom", :multi_line_string, :null=>false - end - - create_table "table_multi_polygons", :options=> "ENGINE=MyISAM", :force => true do |t| - t.column "geom", :multi_polygon, :null=>false - end - - create_table "table_geometries", :options=> "ENGINE=MyISAM", :force => true do |t| - t.column "geom", :geometry, :null=>false - end - - create_table "table_geometry_collections", :options=> "ENGINE=MyISAM", :force => true do |t| - t.column "geom", :geometry_collection, :null=>false - end - -end - diff --git a/vendor/plugins/spatial_adapter/test/schema/schema_postgis.rb b/vendor/plugins/spatial_adapter/test/schema/schema_postgis.rb deleted file mode 100644 index 01c8b828..00000000 --- a/vendor/plugins/spatial_adapter/test/schema/schema_postgis.rb +++ /dev/null @@ -1,65 +0,0 @@ -require File.dirname(__FILE__) + '/../common/common_postgis.rb' - -#add some postgis specific tables -ActiveRecord::Schema.define() do - - create_table "table_points", :force => true do |t| - t.string "data" - t.point "geom", :null=>false - end - - create_table "table_keyword_column_points", :force => true do |t| - t.point "location", :null => false - end - - create_table "table_line_strings", :force => true do |t| - t.integer "value" - t.line_string "geom", :null=>false - end - - create_table "table_polygons", :force => true do |t| - t.polygon "geom", :null=>false - end - - create_table "table_multi_points", :force => true do |t| - t.multi_point "geom", :null=>false - end - - create_table "table_multi_line_strings", :force => true do |t| - t.multi_line_string "geom", :null=>false - end - - create_table "table_multi_polygons", :force => true do |t| - t.multi_polygon "geom", :null=>false - end - - create_table "table_geometries", :force => true do |t| - t.geometry "geom", :null=>false - end - - create_table "table_geometry_collections", :force => true do |t| - t.geometry_collection "geom", :null=>false - end - - create_table "table3dz_points", :force => true do |t| - t.column "data", :string - t.point "geom", :null => false , :with_z => true - end - - create_table "table3dm_points", :force => true do |t| - t.point "geom", :null => false , :with_m => true - end - - create_table "table4d_points", :force => true do |t| - t.point "geom", :null => false, :with_m => true, :with_z => true - end - - create_table "table_srid_line_strings", :force => true do |t| - t.line_string "geom", :null => false , :srid => 123 - end - - create_table "table_srid4d_polygons", :force => true do |t| - t.polygon "geom", :with_m => true, :with_z => true, :srid => 123 - end - -end diff --git a/vendor/plugins/ym4r_gm/README b/vendor/plugins/ym4r_gm/README deleted file mode 100644 index 15f424e6..00000000 --- a/vendor/plugins/ym4r_gm/README +++ /dev/null @@ -1,416 +0,0 @@ -=YM4R/GM plugin for Rails - -This is the latest version of the YM4R/GM plugin for Rails (YM4RGMP4R?). Its aim is to facilitate the use of Google Maps from Rails application. It includes and enhances all the web application-related functionalities found in the YM4R gem as of version 0.4.1. - -==Getting Started -I present here the most common operations you would want to do with YM4R/GM. Read the rest of the documents if you want more details. - -In your controller, here is a typical initialization sequence in action +index+: - def index - @map = GMap.new("map_div") - @map.control_init(:large_map => true,:map_type => true) - @map.center_zoom_init([75.5,-42.56],4) - @map.overlay_init(GMarker.new([75.6,-42.467],:title => "Hello", :info_window => "Info! Info!")) - end - -Here I create a GMap (which will be placed inside a DIV of id +map_div+), add controls (large zoom slider and pan cross + map type selector), set the center and the zoom and add a marker. Of these 4 steps only the creation of the map and the setting of the center and the zoom are absolutely necessary. - -In your view, here is what you would have: - Test - <%= GMap.header %> - <%= @map.to_html %> - - <%= @map.div(:width => 600, :height => 400) %> - - -First you must output the header, used by all the maps of the page: It includes the Google Maps API JS code and the JS helper functions of YM4R/GM. Then we initialize the map by calling @map.to_html. In the body, we need a DIV whose +id+ is the same as the one passed to the GMap constructor in the controller. The call to @map.div outputs such a DIV. We pass to it options to set the width and height of the map in the style attribute of the DIV. - -Note that you need to set a size for the map DIV element at some point or the map will not be displayed. You have a few ways to do this: -- You define it yourself, wherever you want. Usually as part of the layout definition in an external CSS. -- In the head of the document, through a CSS instruction output by @map.header_width_height, to which you pass 2 arguments (width and height). -- When outputting the DIV with @map.div, you can also pass an option hash, with keys :width and :height and a style attribute for the DIV element will be output. - -You can update the map trough RJS. Here is an action you can call from a link_remote_tag which would do this: - def update - @map = Variable.new("map") - @marker = GMarker.new([75.89,-42.767],:title => "Update", :info_window => "I have been placed through RJS") - end - -Here, I first bind the Ruby @map variable to the JS variable map, which already exists in the client browser. +map+ is by default the name given to a map created from YM4R/GM (this could be overriden by passing a second argument to the GMap constructor). Then I create a marker. - -And you would have inside the RJS template for the action: - page << @map.clear_overlays - page << @map.add_overlay(@marker) - -Here I first clear the map of all overlays. Then I add the marker. Note that the +overlay_init+ is not used anymore since, as its name indicates, this method is only for the initialization of the map. - -==Relation between the YM4R gem and the YM4R/GM plugin -They are completely independent from each other. - -With the plugin, you don't need the YM4R gem anymore, unless you want to use the tilers or the Ruby helpers for the Yahoo! Maps Building Block API's and the Google Maps geocoding API. Please refer to the documentation of the YM4R gem to know more about the functionalities in it. - -Conversely, the YM4R gem does not need the plugin to work. - -==Installation -Install like any other Rails plugin: - ruby script/plugin install svn://rubyforge.org/var/svn/ym4r/Plugins/GM/trunk/ym4r_gm - -As part of the installation procedure, the JavaScript files found in the PLUGIN_ROOT/javascript directory will be copied to the RAILS_ROOT/public/javascripts/ directory. - -Moreover a gmaps_api_key.yml file will also be created in the RAILS_ROOT/config/ folder. If this file already exists (installed for example by a previous version of the plugin), nothing will be done to it, so you can keep your configuration data even after updating the plugin. This file is a YAML representation of a hash, similar to the database.yml file in that the primary keys in the file are the different environments (development, test, production), since you will probably need to have different Google Maps API keys depending on that criteria (for example: a key for localhost for development and test; a key for your host for production). If you don't have any special need, there will be only one key associated with each environment. If however, you need to have multiple keys (for example if your app will be accessible from multiple URLs, for which you need different keys), you can also associate a hash to the environment, whose keys will be the different hosts. In this case, you will need to pass a value to the :host key when calling the method GMap.header (usually @request.host). - -==Migration from the YM4R gem versions <= 0.4.1 -Apart from the installation of the plugin detailed above, you will also need to delete the instructions to require the file and include the Ym4r::GoogleMaps module in your controllers: - require 'ym4r' - include Ym4r::GoogleMaps -This lines are now not needed since the plugin is loaded as part of the normal Rails loading procedure and the module is included when the plugin is loaded. - -==Operations -You can use the library to display Google maps easily from Rails. The version of the API used is v2. The library is engineered so updates to the map through RJS/Ajax are possible. I have made available 2 in-depth tutorials to show some of the functionalities of the library and how it can be integrated with GeoRuby and the Spatial Adapter for Rails: -- http://thepochisuperstarmegashow.com/2006/06/02/ym4r-georuby-spatial-adapter-demo/ -- http://thepochisuperstarmegashow.com/2006/06/03/google-maps-yahoo-traffic-mash-up/ -Following is some notes about manipulating Google Maps with the library: - -===Naming conventions -The names of the Ruby class follow the ones in the JavaScript Google Maps API v2, except for GMap2, which in Ruby code is called simply GMap. To know what is possible to do with each class, you should refer to the documentation available on Google website. - -On top of that, you have some convenience methods for initializing the map (in the GMap class). Also, the constructors of some classes accept different types of arguments to be converted later in the correct JavaScript format. For example, the +GMarker+ aclass accepts an array of 2 floats as parameter, in addition of a GLatLng object, to indicate its position. It also facilitates the attribution of an HTML info window, displayed when the user clicks on it, since you can pass to the constructor an options hash with the :info_window key and the text to display as the value, instead of having to wire the response to a click event yourself. - -===Binding JavaScript and Ruby -Since the Google Maps API uses JavaScript to create and manipulate a map, most of what the library does is outputting JavaScript, although some convenience methods are provided to simplify some common operations at initialization time. When you create a YM4R mapping object (a Ruby object which includes the MappingObject module) and call methods on it, these calls are converted by the library into JavaScript code. At initialization time, you can pass arbitrary JavaScript code to the GMap#record_init and GMap#record_global_init.Then, at update time, if you use Ruby-on-Rails as your web framework, you can update your map through RJS by passing the result of the method calls to the page << method to have it then interpreted by the browser. - -For example, here is a typical initialization sequence for a map - @map = GMap.new("map_div") - @map.control_init(:large_map => true,:map_type => true) - @map.center_zoom_init([35.12313,-110.567],12) - @map.overlay_init GMarker.new([35.12878, -110.578],:title => "Hello!") - @map.record_init @map.add_overlay(GMarker.new([35.12878, -110.578],:title => "Hello!")) - -While +center_zoom_init+, +control_init+ or +overlay_init+ (and generally all the GMap methods which end in +init+) are one of the rare convenience methods that do not output JavaScript, the +add_overlay+ does. Actually, if you look at the code of the GMap class, you won't find any +add_overlay+ method, although in the documentation of the GMap2 class from the Google Maps API documentation, you will find something about the +addOverlay+ JavaScript method. In fact, when you call on a mapping object an unknow method, it is converted to a javascriptified version of it, along with its arguments, and a string of JavaScript code is output. So the @map.add_overlay... above is converted to "map.addOverlay(new GMarker(GLatLng.new(35.12878, -110.578),{title:\"Hello!\"}))", which is then passed to the +record_init+ method of a Ruby GMap object to be later output along with the rest of the initialization code. Any arbitrary JavaScript code can be passed to the +record_init+ method. Note that 2 last lines of the previous code sample are strictly equivalent and since the +overlay_init+ version is a bit simpler, it should be preferred. - -===Initialization of the map -The map is represented by a GMap object. You need to pass to the constructor the id of a DIV that will contain the map. You have to place this DIV yourself in your HTML template. You can also optionnally pass to the constructor the JavaScript name of the variable that will reference the map, which by default will be global in JavaScript. You have convenience methods to setup the controls, the center, the zoom, overlays, the interface configuration (continuous zoom, double click zoom, dragging), map types and the icons (which are also global). You can also pass arbitrary JavaScript to +record_init+ and +record_global_init+. Since, by default, the initialization of the map is performed in a callback function, if you want to have a globally accessible variable, you need to use the +global+ version. - -You can also have multiple maps. Just make sure you give them different DIV id's, as well as different global variable names, when constructing them: - @map1 = GMap("map1_div","map1") - @map2 = GMap("map2_div","map2") - -The other absolutely necessary initialization step in the controller is the setting of center and zoom: - @map1.center_zoom_init([49.12,-56.453],3) -Withouth this code the map will display an empty grey rectangle with Google's logo. - -Then in your template, you have 2 necessary calls: -- The static GMap.header: Outputs the inclusion of the JavaScript file from Google to make use of the Google Maps API and by default a style declaration for VML objects, necessary to display polylines under IE. This default can be overriddent by passing :with_vml => false as option to the +header+ method. You can also pass to this method a :host option in order to select the correct key for the location where your app is currently deployed, in case the current environment has multiple possible keys. Usually, in this case, you should pass it @request.host. Finally you can override all the key settings in the configuration by passing a value to the :key key. -- GMap#to_html: Outputs the initialization code of the map. By default, it outputs the +script+ tags and initializes the map in response to the onload event of the JavaScript window object. You can call +to_html+ on each one of your maps to have them all initialized. You can pass the option :full=>true to the method to setup a fullscreen map, which will also be resized when the window size changes. - -You can also use GMap#div to output the div element with the correct +id+. You can pass it options :height and :width to set the size of the div (although, as indicated below, you have other ways to do that). - -So you should have something like the following: - Hello - <%= GMap.header %> - <%= @map.to_html %> - - <%= @map.div(:width => 600,:height => 400) %> - - -Note that you need to set a size for the map DIV element at some point or the map will not be displayed. You have a few ways to do this: -- You define it yourself, wherever you want. Usually as part of the layout definition in an external CSS. -- In the head of the document, through a CSS instruction output by @map.header_width_height, to which you pass 2 arguments (width and height). -- When outputting the DIV with @map.div, you can also pass an option hash, with keys :width and :height and a style attribute for the DIV element will be output. - -===GMarkers -GMarkers are point of interests on a map. You can give a position to a GMarker constructor either by passing it a 2D-array of coordinates, a GLatLng object, an object of type Variable (which evaluates to a GLatLng when interpreted in the browser) or an address, which will be geocoded when the marker is initialized by the map. - -You can pass options to the GMarker to customize the info window (:info_window or :info_window_tabs options), the tooltip (:title option) or the icon used (:icon option). - -For example: - GMarker.new([12.4,65.6],:info_window => "I'm a Popup window",:title => "Tooltip") - GMarker.new(GLatLng.new([12.3,45.6])) - GMarker.new("Rue Clovis Paris France", :title => "geocoded") - - -===Update of the map -You are able to update the map through Ajax. In Ruby-on-Rails, you have something called RJS, which sends JavaScript created on the server as a response to an action, which is later interpreted by the browser. It is usually used for visual effects and replacing or adding elements. It can also accept arbitrary JavaScript and this is what YM4R uses. - -For example, if you want to add a marker to the map, you need to do a few things. First, you have to bind a Ruby mapping object to the global JavaScript map variable. By default its name is +map+, but you could have overriden that at initialization time. You need to do something like this: - @map = Variable.new("map") -+map+ in the Variable constructor is the name of the global JavaScript map variable. Then any method you call on @map will be converted in JavaScript to a method called on +map+. In your RJS code, you would do something like this to add a marker: - page << @map.add_overlay(GMarker.new([12.1,12.76],:title => "Hello again!")) -What is sent to the browser will be the fllowing JavaScript code: - map.addOverlay(new GMarker(new GLatLng(123123.1,12313.76),{title:\"Hello again!\"})) - - -===GPolyline -GPolylines are colored lines on the map. The constructor takes as argument a list of GLatLng or a list of 2-element arrays, which will be transformed into GLatLng for you. It can also take the color (in the #rrggbb form), the weight (an integer) and the opacity. These arguments are optional though. - -For example: - GPolyline.new([[12.4,65.6],[4.5,61.2]],"#ff0000",3,1.0) - -Then you add it like any other overlay: - @map.overlay_init(polyline) - -===GPolygon -GPolygons are colored areas on the map. As of 29/12, this feature is not documented in the official GMaps API, but thanks to Steven Talcott Smith (http://www.talcottsystems.com), it is possible to use it now in ym4r. - -The constructor takes as argument a list of GLatLng or a list of 2-element arrays, which will be transformed into GLatLng for you. Note that for polygons, the last point must be equal to the first, in order to have a closed loop. It can also take the color (in the #rrggbb form) of the stroke, the weight of the stroke, the opacity of the stroke, as well as the color of the fill and the opacity. These arguments are optional though. - -For example: - GPolygon.new([[12.4,6.6],[4.5,1.2],[-5.6,-12.4],[12.4,6.6]],"#ff0000",3,1.0,"#00ff00",1.0) - -Then you add it like any other overlay: - @map.overlay_init(polygon) - -===GMarkerGroup -A new type of GOverlay is available, called GMarkerGroup. - -To use it you would have to include in your HTML template the JavaScript file markerGroup.js after the call to GMap.header (because it extends the GOverlay class). You should have something like that in your template: - <%= javascript_include_tag("markerGroup") %> - -It is useful in 2 situations: -- Display and undisplay a group of markers without referencing all of them. You just declare your marker group globally and call +activate+ and +deactivate+ on this group in response, for example, to clicks on links on your page. -- Keeping an index of markers, for example, in order to show one of these markers in reponse to a click on a link (the way Google Local does with the results of the search). - -Here is how you would use it from your Ruby code: - @map = GMap.new("map_div") - marker1 = GMarker.new([123.55,123.988],:info_window => "Hello from 1!") - marker2 = GMarker.new([87.123,18.9],:info_window =>"Hello from 2!") - @map.overlay_global_init(GMarkerGroup.new(true, - 1 => marker1, - 2 => marker2),"myGroup") -Here I have created an active (ie which is going to be displayed when the map is created) marker group called +myGroup+ with 2 markers, which I want to reference later. If I did not want to reference them later (I just want to be able to display and undisplay the marker group), I could have passed an array instead of the hash. - -Then in your template, you could have that: - Click here to display marker1 - Click here to display marker2 - <%= @map.div %> -When you click on one of the links, the corresponding marker has its info window displayed. - -You can call +activate+ and +deactivate+ to display or undisplay a group of markers. You can add new markers with addMarker(marker,id). Again if you don't care about referencing the marker, you don't need to pass an id. If the marker group is active, the newly added marker will be displayed immediately. Otherwise it will be displayed the next time the group is activated. Finally, since it is an overlay, the group will be removed when calling clearOverlays on the GMap object. - -You can center and zoom on all the markers in the group by calling GMarkerGroup#centerAndZoomOnMarkers() after the group has been added to a map. So for example, if you would want to do that at initalization time, you would do the following (assuming your marker group has been declared as +group+): - @map.record_init group.center_and_zoom_on_markers - -===GMarkerManager -It is a recent (v2.67) GMaps API class that manages the efficient display of potentially thousands of markers. It is similar to the Clusterer (see below) since markers start appearing at specified zoom levels. The clustering behaviour has to be managed explicitly though by specifying the cluster for smaller zoom levels and specify the expanded cluster for larger zoom levels and so on. Note that it is not an overlay and is not added to the map through an overlay_init call. - -Here is an example of use: - @map = GMap.new("map_div") - @map.control_init(:large_map => true, :map_type => true) - @map.center_zoom_init([59.91106, 10.72223],3) - srand 1234 - markers1 = [] - 1.upto(20) do |i| - markers1 << GMarker.new([59.91106 + 6 * rand - 3, 10.72223 + 6 * rand - 3],:title => "OY-20-#{i}") - end - managed_markers1 = ManagedMarker.new(markers1,0,7) - - markers2 = [] - 1.upto(200) do |i| - markers2 << GMarker.new([59.91106 + 6 * rand - 3, 10.72223 + 6 * rand - 3],:title => "OY-200-#{i}") - end - managed_markers2 = ManagedMarker.new(markers2,8,9) - - markers3 = [] - 1.upto(1000) do |i| - markers3 << GMarker.new([59.91106 + 6 * rand - 3, 10.72223 + 6 * rand - 3],:title => "OY-300-#{i}") - end - managed_markers3= ManagedMarker.new(markers3,10) - - mm = GMarkerManager.new(@map,:managed_markers => [managed_markers1,managed_markers2,managed_markers3]) - @map.declare_init(mm,"mgr") - -===Clusterer -A Clusterer is a type of overlay that contains a potentially very large group of markers. It is engineered so markers close to each other and undistinguishable from each other at some level of zoom, appear as only one marker on the map at that level of zoom. Therefore it is possible to have a very large number of markers on the map at the same time and not having the map crawl to a halt in order to display them. It has been slightly modified from Jef Poskanzer's original Clusterer2 code (see http://www.acme.com/javascript/ for the original version). The major difference with that version is that, in YM4R, the clusterer is a GOverlay and can therefore be added to the map with map.addOverlay(clusterer) and is cleared along with the rest of overlays when map.clearOverlays() is called. - -In order to use a clusterer, you should include in your template page the clusterer.js file after the call to GMap.header (because it extends the GOverlay class). You should have something like that in your template: - <%= javascript_include_tag("clusterer") %> - -To create a clusterer, you should first create an array of all the GMarkers that you want to include in the clusterer (you can still add more after the clusterer has been added to the map though). When GMarkers close together are grouped in one cluster (represented by another marker on the map) and the user clicks on the cluster marker, a list of markers in the cluster is displayed in the info windo with a description: By default it is equal to the +title+ of the markers (which is also displayed as a tooltip when hovering on the marker with the mouse). If you don't want that, you can also pass to the GMarker constructor a key-value pair with key :description to have a description different from the title. For example, here is how you would create a clusterer: - markers = [GMarker.new([37.8,-90.4819],:info_window => "Hello",:title => "HOYOYO"), - GMarker.new([37.844,-90.47819],:info_window => "Namaste",:description => "Chopoto" , :title => "Ay"), - GMarker.new([37.83,-90.456619],:info_window => "Bonjour",:title => "Third"), - ] - clusterer = Clusterer.new(markers,:max_visible_markers => 2) -Of course, with only 3 markers, the whole clusterer thing is totally useless. Usually, using the clusterer starts being interesting with hundreds of markers. The options to pass the clusterer are: -- :max_visible_markers: Below this number of markers, no clustering is performed. Defaults to 150. -- :grid_size: The clusterer divides the visible area into a grid of this size. Defaults to 5. -- :min_markers_per_cluster: Below this number of markers a cluster is not formed. Defaults to 5. -- :max_lines_per_info_box: Number of lines to display in the info window displayed when a cluster is clicked on by the user. Defaults to 10. -- :icon: Icon to be used to mark a cluster. Defaults to G_DEFAULT_ICON (the classic red marker). - -Then to add the clusterer to the map at initialization time, you proceed as with a classic overlay: - @map.overlay_init clusterer - -To add new markers in RJS (if the clusterer has been declared globally with overlay_global_init), you should do this: - page << clusterer.add_marker(marker,description) -In this case, the :description passed to the GMarker contructor will not be taken into account. Only the +description+ passed in the call to +add_marker+ will. - -===GeoRss Overlay -An group of markers taken from a Rss feed containing location information in the W3C basic geo (WGS83 lat/lon) vocabulary and in the Simple GeoRss format. See http://georss.org for more details. The support for GeoRss relies on the MGeoRSS library by Mikel Maron (http://brainoff.com/gmaps/mgeorss.html), although a bit modified, mostly to have the GeoRssOverlay respect the GOverlay API. It has also been enhanced by Andrew Turner who added support for the GeoRss Simple format. - -Note that the geoRssOverlay.js file must be included in the HTML template in order to use this functionality. You should have something like that in your template: - <%= javascript_include_tag("geoRssOverlay") %> - -Here is how you would use it. First create the overlay at initialization: - def index - @map = GMap.new("map_div") - @map.control_init(:large_map => true) - @map.center_zoom_init([38.134557,-95.537109],0) - @map.overlay_init(GeoRssOverlay.new(url_for(:action => "earthquake_rss")) - end -Since it is not possible to make requests outside the domain where the current page comes from, there is a need for a proxy. With the GeoRssOverlay initialization above, the request will be made by the earthquake_rss action, where the address to find the RSS feed will be hardwired: - def earthquake_rss - result = Net::HTTP.get_response("earthquake.usgs.gov","/eqcenter/recenteqsww/catalogs/eqs7day-M5.xml") - render :xml => result.body - end -If you don't want to hardwire the RSS feed location in an action, you can. But you will have to pass the :proxy option to the GeoRssOverlay constructor. When requesting the RSS feed, the browser will in fact call the proxy with the actual URL of the RSS feed in the +q+ parameter of the request. Here is how you would initialize the GeoRssOverlay that way: - @map.overlay_init(GeoRssOverlay.new("http://earthquake.usgs.gov/eqcenter/recenteqsww/catalogs/eqs7day-M5.xml", - :proxy => url_for(:action => "proxy"))) -And here is an example of proxy action: - def proxy - result = Net::HTTP.get_response(URI.parse(@params[:q])) - render :xml => result.body - end -You should probably do some checks to ensure the proxy is not abused but it is of your responsibility. - -Another option can be passed to the GeoRssOverlay constructor to set an icon to be used by the markers of the overlay: :icon. By default it is equal to the default icon (the classic red one). - -In the view, you should have something like the following: - - Testing GeoRss - <%= GMap.header(:with_vml => false) %> - <%= javascript_include_tag("geoRssOverlay") %> - <%= @map.header_width_height(600,400) %> - <%= @map.to_html %> - - - <%= @map.div %> - - -Note the inclusion of the geoRssOverlay.js file. - -Other options to pass to the GeoRssOverlay constructor are the following: -- :list_div: In case you want to make a list of all the markers, with a link on which you can click in order to display the info on the marker, use this option to indicate the ID of the DIV (that you must place yourself on your page). -- :list_item_class: class of the DIV containing each item of the list. Ignored if option :list_div is not set. -- :limit: Maximum number of items to display on the map. -- :content_div: Instead of having an info window appear, indicates the ID of the DIV where this info should be displayed. - -===Adding new map types -It is now possible to easily add new map types, on top of the already existing ones, like G_SATELLITE_MAP or G_NORMAL_MAP. The imagery for these new map types can come from layers of the standard map types or can be taken either from a WMS server or from pretiled images on a server (that can be generated with a tool that comes with the YM4R gem: refer to the README of the gem to know more about it). - -For exemple, here is how you would setup layers from a public WMS server of the DMAP team of the American Navy: - layer = WMSLayer.new("http://columbo.nrlssc.navy.mil/ogcwms/servlet/WMSServlet/AccuWeather_Maps.wms", - "20:3,6:3,0:27,0:29,6:19", - :copyright => {'prefix' => "Map Copyright 2006", 'copyright_texts'=> ["DMAP"]}, - :use_geographic => true, :opacity => 0.8) -This sets up a connection to a WMS service, requesting layers 20:3,6:3,0:27,0:29,6:19 (you would have to look at the GetCapabilities document of the service to know what the valid layers are). The copyright notice attributes the data to DMAP. The images will be 80% opaque. For the rest of the options, the default values are used: default styles (:style option), PNG format (:format option), valid for all zoom levels (:zoom_range option). The option :merc_proj is irrelevant here since the :use_geographic option is true. - -The arguments :use_geographic and :merc_proj warrant some explanation. The Google Maps are in the Simple Mercator projection in the WGS84 datum and currently do not support the display of data in projections other than that (at least if you want to display markers and lines on top of it). Unfortunately, different WMS servers do not identify this projection the same way. So you can give to the WMSLayer constructor your server type through the :merc_proj option and it will figure out what is the correct identifier. Currently, this works only for :mapserver (EPSG:54004) and :geoserver (EPSG:41001). For others you can directly pass a number corresponding to the EPSG definition of the simple Mercator projection of your server. On top of that, some servers just don't support the Simple Mercator projection. This is why there is a :use_geographic option, that can be set to +true+. It is in order to tell the WMSLayer that it should request its tiles using LatLon coordinates in the WGS84 datum (which should be supported by any server in a consistant way). Unfortunately it is not perfect since the deformation is quite important for low zoom levels (< 5 for the US). Higher than that, the deformation is not that visible. However, if you control the WMS server, it is recommended that you don't use :use_geographic and instead use the :merc_proj option and setup the Mercator projection in your server if it is not done by default. - -Note that you need to include the wms-gs.js javascript file in your HTML page in order to make use of the WMSLayer functionality. You should have something like that in your template: - <%= javascript_include_tag("wms-gs") %> -This file uses code by John Deck with the participation of others (read the top of the javascript file to know more). - -Here is how to define a pretiled layer: - layer = PreTiledLayer.new("http://localhost:3000/tiles", - :copyright => {'prefix' => "Map C 2006", 'copyright_texts' => ["Open Atlas"]}, - :zoom_range => 13..13, :opacity => 0.7, :format => "gif") -I tell the PreTiledLayer constructor where I can find the tiles, setup the Copyright string, the valid zooms for the map, the opacity and the format. Tiles must have standardized names: tile_#{zoom}_#{x_tile}_#{y_tile}.#{format} (here the format is "gif"). You can use tools found in the YM4R gem to generate tiles in this format either from local maps or from WMS servers (useful to create tiles from geographic data files without having to run a map server or to cache images from slow servers). Again refer to the documentation of the gem for more information on how to do this. - -Instead of having the tiles requested directly, you can also decide to have an action on the server which takes care of it. You can used the class PreTiledLayerFromAction for this. In this case, the first argument of the constructor is an url of an action. The arguments +x+, +y+ and +z+ will be passed to it. - layer = PreTiledLayerFromAction.new(url_for(:action => :get_tile), - :copyright => {'prefix' => "Map C 2006", 'copyright_texts' => ["Open Atlas"]}, - :zoom_range => 13..14, :opacity => 0.7) -The other constructor arguments have the same meaning as PreTiledLayer. Here is an uninteresting example of action that serves tiles: - def get_tile - x = @params[:x] - y = @params[:y] - z = @params[:z] - begin - send_file "#{RAILS_ROOT}/public/tiles/tile_#{z}_#{x}_#{y}.png" , - :type => 'image/png', :disposition => 'inline' - rescue Exception - render :nothing => true - end - end - -You can add a layer to a new map type the following way: - map_type = GMapType.new(layer,"My WMS") -This is actually the simplest configuration possible. Your map type has only one data layer and is called "My WMS". You can add more that one layer: Either one that you have created yourself or existing ones. For example: - map_type = GMapType.new([GMapType::G_SATELLITE_MAP.get_tile_layers[0],layer,GMapType::G_HYBRID_MAP.get_tile_layers[1]], - "Test WMS") -Here for the "Test WMS" map type, we also take the first layer of the "Satellite" map type in the background and overlay the second layer of the "Hybrid" map type (roads, country boundaries, etc... transparently overlaid on top of the preceding layers) so when the "Test WMS" map type is selected in the interface, all three layers will be displayed. - -Finally to add a map type to a GMap: - @map.add_map_type_init(map_type) -If you want to wipe out the existing map types (for example the 3 default ones), you can add a +false+ argument to the +add_map_type_init+ method and the +map_type+ will be the only one. - -If you want to setup the map as the default one when the map is initially displayed, you should first declare the map type then add it to the map as indicated above and finally setting it as the default map type: - @map.declare_init(map_type,"my_map_type") - @map.add_map_type_init(map_type) - @map.set_map_type_init(map_type) -Future versions of the plugin may simplify that. - -===Google Geocoding -A helper to perform geocoding on the server side (in Ruby) is included. Here is an example of request: - results = Geocoding::get("Rue Clovis Paris") -You can also pass to the +get+ method an options hash to manage the various API key options (see the section on GMap.header for details). +results+ is an array of Geocoding::Placemark objects, with 2 additional attributes: +status+ and +name+. You should check if +status+ equals Geocoding::GEO_SUCCESS to know if the request has been successful. You can then access the various data elements. - -Here is an example that will display a marker on Paris: - results = Geocoding::get("Rue Clovis Paris") - if results.status == Geocoding::GEO_SUCCESS - coord = results[0].latlon - @map.overlay_init(GMarker.new(coord,:info_window => "Rue Clovis Paris")) - end - -You could also have performed the geocoding on the client side with the following code, which is functionnality equivalent to the code above: - GMarker.new("Rue Clovis Paris",:info_window => "Rue Clovis Paris") - -===Local Search -Local Search control has been added to the map and control objects. There are two places you need to implement it to get it to work. This is because the local search control needs an additional library added to the import as well as the control added to the map. - -In your controller, you add ':local_search => true' to the @map.control_init like this: -@map = GMap.new("map_div") -@map.control_init(:large_map => true, :map_type => true, :local_search => true) - -And in your view, you pass ':local_search => true' to the GMap.header like this: -<%= GMap.header(:local_search => true) %> -<%= @map.to_html %> -<%= @map.div(:width => 600, :height => 400) %> - -You can pass options to the control_init as well. They are: -:anchor => [:bottom_left (default), :bottom_right, :top_left, :top_right] -:offset_width => 10 (default) -:offset_height => 20 (default) -:local_search_options => "{suppressZoomToBounds : true, - resultList : google.maps.LocalSearch.RESULT_LIST_INLINE, - suppressInitialResultSelection : true, - searchFormHint : 'Local Search powered by Google', - linkTarget : GSearch.LINK_TARGET_BLANK}" - -So if you wanted the local search control to be at the bottom right of the map, 30 pixel in from the right and 20 pixels above the bottom and some local search options it would look like this: -@map.control_init(:large_map => true, :map_type => true, :local_search => true, :anchor => :bottom_right, :offset_width => 30, :offset_height => 20, :local_search_options => "{suppressZoomToBounds : true, resultList : google.maps.LocalSearch.RESULT_LIST_INLINE, suppressInitialResultSelection : true, searchFormHint : 'Local Search powered by Google', linkTarget : GSearch.LINK_TARGET_BLANK}") - -==Recent changes -- Local Search overlay added. See above for implementation. -- GMarker can now be placed by address (in addition to coordinates). Some code to geocode the address when the marker is initialized is added -- Addition of a +center_zoom_on_points_init+ to center and zoom on a group of pixel -- In JS, addition of methods to GMap2 and GMarkerGroup to center and zoom on a group of points or markers (thanks to Glen Barnes) -- Support for easy setup of fullscreen maps - -==TODO -- Add support for easy manipulation of external Google Maps-related libraries: Advanced tooltip manipulation (PdMarker),... -- Addition of all GeoRss vocabularies (with all features: polylines...) to the geoRssOverlay extension -- Tutorials - -==Disclaimer -This software is not endorsed in any way by Google. - -==Acknowledgement -The YM4R/GM plugin bundles JavaScript libraries from John Deck (WMS layers on Google Maps), Jef Poskanzer (Clusterer on Google Maps) and Mikel Maron (GeoRss on Google Maps). - -==License -The YM4R/GM plugin is released under the MIT license. The clusterer.js file is redistributed with a different license (but still compatible with the MIT license). Check the top of the file in PLUGIN_ROOT/javascript to know more. - -==Support -Any questions, enhancement proposals, bug notifications or corrections can be sent to mailto:guilhem.vellut+ym4r@gmail.com. diff --git a/vendor/plugins/ym4r_gm/gmaps_api_key.yml.sample b/vendor/plugins/ym4r_gm/gmaps_api_key.yml.sample deleted file mode 100644 index c4cb4578..00000000 --- a/vendor/plugins/ym4r_gm/gmaps_api_key.yml.sample +++ /dev/null @@ -1,14 +0,0 @@ -#Fill here the Google Maps API keys for your application -#In this sample: -#For development and test, we have only one possible host (localhost:3000), so there is only a single key associated with the mode. -#In production, the app can be accessed through 2 different hosts: thepochisuperstarmegashow.com and exmaple.com. There then needs a 2-key hash. If you deployed to one host, only the API key would be needed (as in development and test). - -development: - ABQIAAAAzMUFFnT9uH0xq39J0Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDKaBR6j135zrztfTGVOm2QlWnkaidDIQ - -test: - ABQIAAAAzMUFFnT9uH0xq39J0Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDKaBR6j135zrztfTGVOm2QlWnkaidDIQ - -production: - thepochisuperstarmegashow.com: ABQIAAAAzMUFFnT9uH0Sfg76Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDmlRT6e90j135zat56yhJKQlWnkaidDIQ - example.com: ABQIAAAAzMUFFnT9uH0Sfg98Y4kbhGFJQa0g3IQ9GZqIMmInSLrthJKGDmlRT98f4j135zat56yjRKQlWnkmod3TB \ No newline at end of file diff --git a/vendor/plugins/ym4r_gm/init.rb b/vendor/plugins/ym4r_gm/init.rb deleted file mode 100644 index 7316aeff..00000000 --- a/vendor/plugins/ym4r_gm/init.rb +++ /dev/null @@ -1,3 +0,0 @@ -require 'ym4r_gm' - - diff --git a/vendor/plugins/ym4r_gm/install.rb b/vendor/plugins/ym4r_gm/install.rb deleted file mode 100644 index 68c5e39c..00000000 --- a/vendor/plugins/ym4r_gm/install.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'fileutils' - -#Copy the Javascript files -FileUtils.copy(Dir[File.dirname(__FILE__) + '/javascript/*.js'], File.dirname(__FILE__) + '/../../../public/javascripts/') - -#copy the gmaps_api_key file -gmaps_config = File.dirname(__FILE__) + '/../../../config/gmaps_api_key.yml' -unless File.exist?(gmaps_config) - FileUtils.copy(File.dirname(__FILE__) + '/gmaps_api_key.yml.sample',gmaps_config) -end diff --git a/vendor/plugins/ym4r_gm/javascript/clusterer.js b/vendor/plugins/ym4r_gm/javascript/clusterer.js deleted file mode 100644 index eeb6dd96..00000000 --- a/vendor/plugins/ym4r_gm/javascript/clusterer.js +++ /dev/null @@ -1,444 +0,0 @@ -// Clusterer.js - marker clustering routines for Google Maps apps -// -// The original version of this code is available at: -// http://www.acme.com/javascript/ -// -// Copyright © 2005,2006 by Jef Poskanzer . -// All rights reserved. -// -// Modified for inclusion into the YM4R library in accordance with the -// following license: -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// For commentary on this license please see http://www.acme.com/license.html - - -// Constructor. -Clusterer = function(markers,icon,maxVisibleMarkers,gridSize,minMarkersPerCluster,maxLinesPerInfoBox) { - this.markers = []; - if(markers){ - for(var i =0 ; i< markers.length ; i++){ - this.addMarker(markers[i]); - } - } - this.clusters = []; - this.timeout = null; - - this.maxVisibleMarkers = maxVisibleMarkers || 150; - this.gridSize = gridSize || 5; - this.minMarkersPerCluster = minMarkersPerCluster || 5; - this.maxLinesPerInfoBox = maxLinesPerInfoBox || 10; - - this.icon = icon || G_DEFAULT_ICON; -} - -Clusterer.prototype = new GOverlay(); - -Clusterer.prototype.initialize = function ( map ){ - this.map = map; - this.currentZoomLevel = map.getZoom(); - - GEvent.addListener( map, 'zoomend', Clusterer.makeCaller( Clusterer.display, this ) ); - GEvent.addListener( map, 'moveend', Clusterer.makeCaller( Clusterer.display, this ) ); - GEvent.addListener( map, 'infowindowclose', Clusterer.makeCaller( Clusterer.popDown, this ) ); - //Set map for each marker - for(var i = 0,len = this.markers.length ; i < len ; i++){ - this.markers[i].setMap( map ); - } - this.displayLater(); -} - -Clusterer.prototype.remove = function(){ - for ( var i = 0; i < this.markers.length; ++i ){ - this.removeMarker(this.markers[i]); - } -} - -Clusterer.prototype.copy = function(){ - return new Clusterer(this.markers,this.icon,this.maxVisibleMarkers,this.gridSize,this.minMarkersPerCluster,this.maxLinesPerInfoBox); -} - -Clusterer.prototype.redraw = function(force){ - this.displayLater(); -} - -// Call this to change the cluster icon. -Clusterer.prototype.setIcon = function ( icon ){ - this.icon = icon; -} - -// Call this to add a marker. -Clusterer.prototype.addMarker = function ( marker, description){ - marker.onMap = false; - this.markers.push( marker ); - marker.description = marker.description || description; - if(this.map != null){ - marker.setMap(this.map); - this.displayLater(); - } -}; - - -// Call this to remove a marker. -Clusterer.prototype.removeMarker = function ( marker ){ - for ( var i = 0; i < this.markers.length; ++i ) - if ( this.markers[i] == marker ){ - if ( marker.onMap ) - this.map.removeOverlay( marker ); - for ( var j = 0; j < this.clusters.length; ++j ){ - var cluster = this.clusters[j]; - if ( cluster != null ){ - for ( var k = 0; k < cluster.markers.length; ++k ) - if ( cluster.markers[k] == marker ){ - cluster.markers[k] = null; - --cluster.markerCount; - break; - } - if ( cluster.markerCount == 0 ){ - this.clearCluster( cluster ); - this.clusters[j] = null; - } - else if ( cluster == this.poppedUpCluster ) - Clusterer.rePop( this ); - } - } - this.markers[i] = null; - break; - } - this.displayLater(); -}; - -Clusterer.prototype.displayLater = function (){ - if ( this.timeout != null ) - clearTimeout( this.timeout ); - this.timeout = setTimeout( Clusterer.makeCaller( Clusterer.display, this ), 50 ); -}; - -Clusterer.display = function ( clusterer ){ - var i, j, marker, cluster, len, len2; - - clearTimeout( clusterer.timeout ); - - var newZoomLevel = clusterer.map.getZoom(); - if ( newZoomLevel != clusterer.currentZoomLevel ){ - // When the zoom level changes, we have to remove all the clusters. - for ( i = 0 , len = clusterer.clusters.length; i < len; ++i ){ - if ( clusterer.clusters[i] != null ){ - clusterer.clearCluster( clusterer.clusters[i] ); - clusterer.clusters[i] = null; - } - } - clusterer.clusters.length = 0; - clusterer.currentZoomLevel = newZoomLevel; - } - - // Get the current bounds of the visible area. - var bounds = clusterer.map.getBounds(); - - // Expand the bounds a little, so things look smoother when scrolling - // by small amounts. - var sw = bounds.getSouthWest(); - var ne = bounds.getNorthEast(); - var dx = ne.lng() - sw.lng(); - var dy = ne.lat() - sw.lat(); - dx *= 0.10; - dy *= 0.10; - bounds = new GLatLngBounds( - new GLatLng( sw.lat() - dy, sw.lng() - dx ), - new GLatLng( ne.lat() + dy, ne.lng() + dx ) - ); - - // Partition the markers into visible and non-visible lists. - var visibleMarkers = []; - var nonvisibleMarkers = []; - for ( i = 0, len = clusterer.markers.length ; i < len; ++i ){ - marker = clusterer.markers[i]; - if ( marker != null ) - if ( bounds.contains( marker.getPoint() ) ) - visibleMarkers.push( marker ); - else - nonvisibleMarkers.push( marker ); - } - - // Take down the non-visible markers. - for ( i = 0, len = nonvisibleMarkers.length ; i < len; ++i ){ - marker = nonvisibleMarkers[i]; - if ( marker.onMap ){ - clusterer.map.removeOverlay( marker ); - marker.onMap = false; - } - } - - // Take down the non-visible clusters. - for ( i = 0, len = clusterer.clusters.length ; i < len ; ++i ){ - cluster = clusterer.clusters[i]; - if ( cluster != null && ! bounds.contains( cluster.marker.getPoint() ) && cluster.onMap ){ - clusterer.map.removeOverlay( cluster.marker ); - cluster.onMap = false; - } - } - - // Clustering! This is some complicated stuff. We have three goals - // here. One, limit the number of markers & clusters displayed, so the - // maps code doesn't slow to a crawl. Two, when possible keep existing - // clusters instead of replacing them with new ones, so that the app pans - // better. And three, of course, be CPU and memory efficient. - if ( visibleMarkers.length > clusterer.maxVisibleMarkers ){ - // Add to the list of clusters by splitting up the current bounds - // into a grid. - var latRange = bounds.getNorthEast().lat() - bounds.getSouthWest().lat(); - var latInc = latRange / clusterer.gridSize; - var lngInc = latInc / Math.cos( ( bounds.getNorthEast().lat() + bounds.getSouthWest().lat() ) / 2.0 * Math.PI / 180.0 ); - for ( var lat = bounds.getSouthWest().lat(); lat <= bounds.getNorthEast().lat(); lat += latInc ) - for ( var lng = bounds.getSouthWest().lng(); lng <= bounds.getNorthEast().lng(); lng += lngInc ){ - cluster = new Object(); - cluster.clusterer = clusterer; - cluster.bounds = new GLatLngBounds( new GLatLng( lat, lng ), new GLatLng( lat + latInc, lng + lngInc ) ); - cluster.markers = []; - cluster.markerCount = 0; - cluster.onMap = false; - cluster.marker = null; - clusterer.clusters.push( cluster ); - } - - // Put all the unclustered visible markers into a cluster - the first - // one it fits in, which favors pre-existing clusters. - for ( i = 0, len = visibleMarkers.length ; i < len; ++i ){ - marker = visibleMarkers[i]; - if ( marker != null && ! marker.inCluster ){ - for ( j = 0, len2 = clusterer.clusters.length ; j < len2 ; ++j ){ - cluster = clusterer.clusters[j]; - if ( cluster != null && cluster.bounds.contains( marker.getPoint() ) ){ - cluster.markers.push( marker ); - ++cluster.markerCount; - marker.inCluster = true; - } - } - } - } - - // Get rid of any clusters containing only a few markers. - for ( i = 0, len = clusterer.clusters.length ; i < len ; ++i ) - if ( clusterer.clusters[i] != null && clusterer.clusters[i].markerCount < clusterer.minMarkersPerCluster ){ - clusterer.clearCluster( clusterer.clusters[i] ); - clusterer.clusters[i] = null; - } - - // Shrink the clusters list. - for ( i = clusterer.clusters.length - 1; i >= 0; --i ) - if ( clusterer.clusters[i] != null ) - break; - else - --clusterer.clusters.length; - - // Ok, we have our clusters. Go through the markers in each - // cluster and remove them from the map if they are currently up. - for ( i = 0, len = clusterer.clusters.length ; i < len; ++i ){ - cluster = clusterer.clusters[i]; - if ( cluster != null ){ - for ( j = 0 , len2 = cluster.markers.length ; j < len2; ++j ){ - marker = cluster.markers[j]; - if ( marker != null && marker.onMap ){ - clusterer.map.removeOverlay( marker ); - marker.onMap = false; - } - } - } - } - - // Now make cluster-markers for any clusters that need one. - for ( i = 0, len = clusterer.clusters.length; i < len; ++i ){ - cluster = clusterer.clusters[i]; - if ( cluster != null && cluster.marker == null ){ - // Figure out the average coordinates of the markers in this - // cluster. - var xTotal = 0.0, yTotal = 0.0; - for ( j = 0, len2 = cluster.markers.length; j < len2 ; ++j ){ - marker = cluster.markers[j]; - if ( marker != null ){ - xTotal += ( + marker.getPoint().lng() ); - yTotal += ( + marker.getPoint().lat() ); - } - } - var location = new GLatLng( yTotal / cluster.markerCount, xTotal / cluster.markerCount ); - marker = new GMarker( location, { icon: clusterer.icon } ); - cluster.marker = marker; - GEvent.addListener( marker, 'click', Clusterer.makeCaller( Clusterer.popUp, cluster ) ); - } - } - } - - // Display the visible markers not already up and not in clusters. - for ( i = 0, len = visibleMarkers.length; i < len; ++i ){ - marker = visibleMarkers[i]; - if ( marker != null && ! marker.onMap && ! marker.inCluster ) - { - clusterer.map.addOverlay( marker ); - marker.addedToMap(); - marker.onMap = true; - } - } - - // Display the visible clusters not already up. - for ( i = 0, len = clusterer.clusters.length ; i < len; ++i ){ - cluster = clusterer.clusters[i]; - if ( cluster != null && ! cluster.onMap && bounds.contains( cluster.marker.getPoint() )){ - clusterer.map.addOverlay( cluster.marker ); - cluster.onMap = true; - } - } - - // In case a cluster is currently popped-up, re-pop to get any new - // markers into the infobox. - Clusterer.rePop( clusterer ); -}; - - -Clusterer.popUp = function ( cluster ){ - var clusterer = cluster.clusterer; - var html = ''; - var n = 0; - for ( var i = 0 , len = cluster.markers.length; i < len; ++i ) - { - var marker = cluster.markers[i]; - if ( marker != null ) - { - ++n; - html += ''; - if ( n == clusterer.maxLinesPerInfoBox - 1 && cluster.markerCount > clusterer.maxLinesPerInfoBox ) - { - html += ''; - break; - } - } - } - html += '
'; - if ( marker.getIcon().smallImage != null ) - html += ''; - else - html += ''; - html += '' + marker.description + '
...and ' + ( cluster.markerCount - n ) + ' more
'; - clusterer.map.closeInfoWindow(); - cluster.marker.openInfoWindowHtml( html ); - clusterer.poppedUpCluster = cluster; -}; - -Clusterer.rePop = function ( clusterer ){ - if ( clusterer.poppedUpCluster != null ) - Clusterer.popUp( clusterer.poppedUpCluster ); -}; - -Clusterer.popDown = function ( clusterer ){ - clusterer.poppedUpCluster = null; -}; - -Clusterer.prototype.clearCluster = function ( cluster ){ - var i, marker; - - for ( i = 0; i < cluster.markers.length; ++i ){ - if ( cluster.markers[i] != null ){ - cluster.markers[i].inCluster = false; - cluster.markers[i] = null; - } - } - - cluster.markers.length = 0; - cluster.markerCount = 0; - - if ( cluster == this.poppedUpCluster ) - this.map.closeInfoWindow(); - - if ( cluster.onMap ) - { - this.map.removeOverlay( cluster.marker ); - cluster.onMap = false; - } -}; - -// This returns a function closure that calls the given routine with the -// specified arg. -Clusterer.makeCaller = function ( func, arg ){ - return function () { func( arg ); }; -}; - - -// Augment GMarker so it handles markers that have been created but -// not yet addOverlayed. -GMarker.prototype.setMap = function ( map ){ - this.map = map; -}; - -GMarker.prototype.getMap = function (){ - return this.map; -} - -GMarker.prototype.addedToMap = function (){ - this.map = null; -}; - - -GMarker.prototype.origOpenInfoWindow = GMarker.prototype.openInfoWindow; -GMarker.prototype.openInfoWindow = function ( node, opts ){ - if ( this.map != null ) - return this.map.openInfoWindow( this.getPoint(), node, opts ); - else - return this.origOpenInfoWindow( node, opts ); -}; - -GMarker.prototype.origOpenInfoWindowHtml = GMarker.prototype.openInfoWindowHtml; -GMarker.prototype.openInfoWindowHtml = function ( html, opts ){ - if ( this.map != null ) - return this.map.openInfoWindowHtml( this.getPoint(), html, opts ); - else - return this.origOpenInfoWindowHtml( html, opts ); -}; - -GMarker.prototype.origOpenInfoWindowTabs = GMarker.prototype.openInfoWindowTabs; -GMarker.prototype.openInfoWindowTabs = function ( tabNodes, opts ){ - if ( this.map != null ) - return this.map.openInfoWindowTabs( this.getPoint(), tabNodes, opts ); - else - return this.origOpenInfoWindowTabs( tabNodes, opts ); -}; - -GMarker.prototype.origOpenInfoWindowTabsHtml = GMarker.prototype.openInfoWindowTabsHtml; -GMarker.prototype.openInfoWindowTabsHtml = function ( tabHtmls, opts ){ - if ( this.map != null ) - return this.map.openInfoWindowTabsHtml( this.getPoint(), tabHtmls, opts ); - else - return this.origOpenInfoWindowTabsHtml( tabHtmls, opts ); -}; - -GMarker.prototype.origShowMapBlowup = GMarker.prototype.showMapBlowup; -GMarker.prototype.showMapBlowup = function ( opts ){ - if ( this.map != null ) - return this.map.showMapBlowup( this.getPoint(), opts ); - else - return this.origShowMapBlowup( opts ); -}; - - -function addDescriptionToMarker(marker, description){ - marker.description = description; - return marker; -} diff --git a/vendor/plugins/ym4r_gm/javascript/geoRssOverlay.js b/vendor/plugins/ym4r_gm/javascript/geoRssOverlay.js deleted file mode 100644 index 315c26d0..00000000 --- a/vendor/plugins/ym4r_gm/javascript/geoRssOverlay.js +++ /dev/null @@ -1,194 +0,0 @@ -// GeoRssOverlay: GMaps API extension to display a group of markers from -// a RSS feed -// -// Copyright 2006 Mikel Maron (email: mikel_maron yahoo com) -// -// The original version of this code is called MGeoRSS and can be found -// at the following address: -// http://brainoff.com/gmaps/mgeorss.html -// -// Modified by Andrew Turner to add support for the GeoRss Simple vocabulary -// -// Modified and bundled with YM4R in accordance with the following -// license: -// -// This work is public domain - -function GeoRssOverlay(rssurl,icon,proxyurl,options){ - this.rssurl = rssurl; - this.icon = icon; - this.proxyurl = proxyurl; - if(options['visible'] == undefined) - this.visible = true; - else - this.visible = options['visible']; - this.listDiv = options['listDiv']; //ID of the item list DIV - this.contentDiv = options['contentDiv']; //ID of the content DIV - this.listItemClass = options['listItemClass']; //Class of the list item DIV - this.limitItems = options['limit']; //Maximum number of displayed entries - this.request = false; - this.markers = []; -} - -GeoRssOverlay.prototype = new GOverlay(); - -GeoRssOverlay.prototype.initialize=function(map) { - this.map = map; - this.load(); -} - -GeoRssOverlay.prototype.redraw = function(force){ - //nothing to do : the markers are already taken care of -} - -GeoRssOverlay.prototype.remove = function(){ - for(var i= 0, len = this.markers.length ; i< len; i++){ - this.map.removeOverlay(this.markers[i]); - } -} - -GeoRssOverlay.prototype.showHide=function() { - if (this.visible) { - for (var i=0;i" + title + "

" + description; - - if(this.contentDiv == undefined){ - GEvent.addListener(marker, "click", function() { - marker.openInfoWindowHtml(html); - }); - }else{ - var contentDiv = this.contentDiv; - GEvent.addListener(marker, "click", function() { - document.getElementById(contentDiv).innerHTML = html; - }); - } - - if(this.listDiv != undefined){ - var a = document.createElement('a'); - a.innerHTML = title; - a.setAttribute("href","#"); - var georss = this; - a.onclick = function(){ - georss.showMarker(index); - return false; - }; - var div = document.createElement('div'); - if(this.listItemClass != undefined){ - div.setAttribute("class",this.listItemClass); - } - div.appendChild(a); - document.getElementById(this.listDiv).appendChild(div); - } - - return marker; -} diff --git a/vendor/plugins/ym4r_gm/javascript/markerGroup.js b/vendor/plugins/ym4r_gm/javascript/markerGroup.js deleted file mode 100644 index 02fe6249..00000000 --- a/vendor/plugins/ym4r_gm/javascript/markerGroup.js +++ /dev/null @@ -1,114 +0,0 @@ -function GMarkerGroup(active, markers, markersById) { - this.active = active; - this.markers = markers || new Array(); - this.markersById = markersById || new Object(); -} - -GMarkerGroup.prototype = new GOverlay(); - -GMarkerGroup.prototype.initialize = function(map) { - this.map = map; - - if(this.active){ - for(var i = 0 , len = this.markers.length; i < len; i++) { - this.map.addOverlay(this.markers[i]); - } - for(var id in this.markersById){ - this.map.addOverlay(this.markersById[id]); - } - } -} - -//If not already done (ie if not inactive) remove all the markers from the map -GMarkerGroup.prototype.remove = function() { - this.deactivate(); -} - -GMarkerGroup.prototype.redraw = function(force){ - //Nothing to do : markers are already taken care of -} - -//Copy the data to a new Marker Group -GMarkerGroup.prototype.copy = function() { - var overlay = new GMarkerGroup(this.active); - overlay.markers = this.markers; //Need to do deep copy - overlay.markersById = this.markersById; //Need to do deep copy - return overlay; -} - -//Inactivate the Marker group and clear the internal content -GMarkerGroup.prototype.clear = function(){ - //deactivate the map first (which removes the markers from the map) - this.deactivate(); - //Clear the internal content - this.markers = new Array(); - this.markersById = new Object(); -} - -//Add a marker to the GMarkerGroup ; Adds it now to the map if the GMarkerGroup is active -GMarkerGroup.prototype.addMarker = function(marker,id){ - if(id == undefined){ - this.markers.push(marker); - }else{ - this.markersById[id] = marker; - } - if(this.active && this.map != undefined ){ - this.map.addOverlay(marker); - } -} - -//Open the info window (or info window tabs) of a marker -GMarkerGroup.prototype.showMarker = function(id){ - var marker = this.markersById[id]; - if(marker != undefined){ - GEvent.trigger(marker,"click"); - } -} - -//Activate (or deactivate depending on the argument) the GMarkerGroup -GMarkerGroup.prototype.activate = function(active){ - active = (active == undefined) ? true : active; - if(!active){ - if(this.active){ - if(this.map != undefined){ - for(var i = 0 , len = this.markers.length; i < len; i++){ - this.map.removeOverlay(this.markers[i]) - } - for(var id in this.markersById){ - this.map.removeOverlay(this.markersById[id]); - } - } - this.active = false; - } - }else{ - if(!this.active){ - if(this.map != undefined){ - for(var i = 0 , len = this.markers.length; i < len; i++){ - this.map.addOverlay(this.markers[i]); - } - for(var id in this.markersById){ - this.map.addOverlay(this.markersById[id]); - } - } - this.active = true; - } - } -} - -GMarkerGroup.prototype.centerAndZoomOnMarkers = function() { - if(this.map != undefined){ - //merge markers and markersById - var tmpMarkers = this.markers.slice(); - for (var id in this.markersById){ - tmpMarkers.push(this.markersById[id]); - } - if(tmpMarkers.length > 0){ - this.map.centerAndZoomOnMarkers(tmpMarkers); - } - } -} - -//Deactivate the Group Overlay (convenience method) -GMarkerGroup.prototype.deactivate = function(){ - this.activate(false); -} diff --git a/vendor/plugins/ym4r_gm/javascript/wms-gs.js b/vendor/plugins/ym4r_gm/javascript/wms-gs.js deleted file mode 100644 index c67146b8..00000000 --- a/vendor/plugins/ym4r_gm/javascript/wms-gs.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Call generic wms service for GoogleMaps v2 - * John Deck, UC Berkeley - * Inspiration & Code from: - * Mike Williams http://www.econym.demon.co.uk/googlemaps2/ V2 Reference & custommap code - * Brian Flood http://www.spatialdatalogic.com/cs/blogs/brian_flood/archive/2005/07/11/39.aspx V1 WMS code - * Kyle Mulka http://blog.kylemulka.com/?p=287 V1 WMS code modifications - * http://search.cpan.org/src/RRWO/GPS-Lowrance-0.31/lib/Geo/Coordinates/MercatorMeters.pm - * - * Modified by Chris Holmes, TOPP to work by default with GeoServer. - * - * Bundled with YM4R with John Deck's permission. - * Slightly modified to fit YM4R. - * See johndeck.blogspot.com for the original version and for examples and instructions of how to use it. - */ - -var WGS84_SEMI_MAJOR_AXIS = 6378137.0; //equatorial radius -var WGS84_ECCENTRICITY = 0.0818191913108718138; -var DEG2RAD=0.0174532922519943; -var PI=3.14159267; - -function dd2MercMetersLng(p_lng) { - return WGS84_SEMI_MAJOR_AXIS * (p_lng*DEG2RAD); -} - -function dd2MercMetersLat(p_lat) { - var lat_rad = p_lat * DEG2RAD; - return WGS84_SEMI_MAJOR_AXIS * Math.log(Math.tan((lat_rad + PI / 2) / 2) * Math.pow( ((1 - WGS84_ECCENTRICITY * Math.sin(lat_rad)) / (1 + WGS84_ECCENTRICITY * Math.sin(lat_rad))), (WGS84_ECCENTRICITY/2))); -} - -function addWMSPropertiesToLayer(tile_layer,base_url,layers,styles,format,merc_proj,use_geo){ - tile_layer.format = format; - tile_layer.baseURL = base_url; - tile_layer.styles = styles; - tile_layer.layers = layers; - tile_layer.mercatorEpsg = merc_proj; - tile_layer.useGeographic = use_geo; - return tile_layer; -} - -getTileUrlForWMS=function(a,b,c) { - var lULP = new GPoint(a.x*256,(a.y+1)*256); - var lLRP = new GPoint((a.x+1)*256,a.y*256); - var lUL = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lULP,b,c); - var lLR = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lLRP,b,c); - - if (this.useGeographic){ - var lBbox=lUL.x+","+lUL.y+","+lLR.x+","+lLR.y; - var lSRS="EPSG:4326"; - }else{ - var lBbox=dd2MercMetersLng(lUL.x)+","+dd2MercMetersLat(lUL.y)+","+dd2MercMetersLng(lLR.x)+","+dd2MercMetersLat(lLR.y); - var lSRS="EPSG:" + this.mercatorEpsg; - } - var lURL=this.baseURL; - lURL+="?REQUEST=GetMap"; - lURL+="&SERVICE=WMS"; - lURL+="&VERSION=1.1.1"; - lURL+="&LAYERS="+this.layers; - lURL+="&STYLES="+this.styles; - lURL+="&FORMAT=image/"+this.format; - lURL+="&BGCOLOR=0xFFFFFF"; - lURL+="&TRANSPARENT=TRUE"; - lURL+="&SRS="+lSRS; - lURL+="&BBOX="+lBbox; - lURL+="&WIDTH=256"; - lURL+="&HEIGHT=256"; - lURL+="&reaspect=false"; - return lURL; -} diff --git a/vendor/plugins/ym4r_gm/javascript/ym4r-gm.js b/vendor/plugins/ym4r_gm/javascript/ym4r-gm.js deleted file mode 100644 index 1c768dfb..00000000 --- a/vendor/plugins/ym4r_gm/javascript/ym4r-gm.js +++ /dev/null @@ -1,117 +0,0 @@ -// JS helper functions for YM4R - -function addInfoWindowToMarker(marker,info,options){ - GEvent.addListener(marker, "click", function() {marker.openInfoWindowHtml(info,options);}); - return marker; -} - -function addInfoWindowTabsToMarker(marker,info,options){ - GEvent.addListener(marker, "click", function() {marker.openInfoWindowTabsHtml(info,options);}); - return marker; -} - -function addPropertiesToLayer(layer,getTile,copyright,opacity,isPng){ - layer.getTileUrl = getTile; - layer.getCopyright = copyright; - layer.getOpacity = opacity; - layer.isPng = isPng; - return layer; -} - -function addOptionsToIcon(icon,options){ - for(var k in options){ - icon[k] = options[k]; - } - return icon; -} - -function addCodeToFunction(func,code){ - if(func == undefined) - return code; - else{ - return function(){ - func(); - code(); - } - } -} - -function addGeocodingToMarker(marker,address){ - marker.orig_initialize = marker.initialize; - orig_redraw = marker.redraw; - marker.redraw = function(force){}; //empty the redraw method so no error when called by addOverlay. - marker.initialize = function(map){ - new GClientGeocoder().getLatLng(address, - function(latlng){ - if(latlng){ - marker.redraw = orig_redraw; - marker.orig_initialize(map); //init before setting point - marker.setPoint(latlng); - }//do nothing - }); - }; - return marker; -} - - - -GMap2.prototype.centerAndZoomOnMarkers = function(markers) { - var bounds = new GLatLngBounds(markers[0].getPoint(), - markers[0].getPoint()); - for (var i=1, len = markers.length ; i:key) or a host, (:host). - def self.get(request,options = {}) - api_key = ApiKey.get(options) - output = options[:output] || "kml" - url = "http://maps.google.com/maps/geo?q=#{URI.encode(request)}&key=#{api_key}&output=#{output}" - - res = open(url).read - - case output.to_sym - when :json - res = eval(res.gsub(":","=>")) #!!!EVAL EVAL EVAL EVAL!!! hopefully we can trust google... - placemarks = Placemarks.new(res['name'],res['Status']['code']) - if res['Placemark'] - placemark = res['Placemark'] - - placemark.each do |data| - - data_country = data['Country']['CountryNameCode'] rescue "" - data_administrative = data['Country']['AdministrativeArea']['AdministrativeAreaName'] rescue "" - data_sub_administrative = data['Country']['AdministrativeArea']['SubAdministrativeArea']['SubAdministrativeAreaName'] rescue "" - data_locality = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['LocalityName'] rescue "" - data_dependent_locality = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['DependentLocalityName'] rescue "" - data_thoroughfare = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['Thoroughfare']['ThoroughfareName'] rescue "" - data_postal_code = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['Thoroughfare']['PostalCode']['PostalCodeNumber'] rescue "" - lon, lat = data['Point']['coordinates'][0,2] - data_accuracy = data['Accuracy'] - unless data_accuracy.nil? - data_accuracy = data_accuracy.to_i - end - - placemarks << Geocoding::Placemark.new(data['address'], - data_country, - data_administrative, - data_sub_administrative, - data_locality, - data_dependent_locality, - data_thoroughfare, - data_postal_code, - lon, lat, data_accuracy) - - end - end - when :kml, :xml - - doc = REXML::Document.new(res) - - response = doc.elements['//Response'] - placemarks = Placemarks.new(response.elements['name'].text,response.elements['Status/code'].text.to_i) - response.elements.each(".//Placemark") do |placemark| - data = placemark.elements - data_country = data['.//CountryNameCode'] - data_administrative = data['.//AdministrativeAreaName'] - data_sub_administrative = data['.//SubAdministrativeAreaName'] - data_locality = data['.//LocalityName'] - data_dependent_locality = data['.//DependentLocalityName'] - data_thoroughfare = data['.//ThoroughfareName'] - data_postal_code = data['.//PostalCodeNumber'] - lon, lat = data['.//coordinates'].text.split(",")[0..1].collect {|l| l.to_f } - data_accuracy = data['.//*[local-name()="AddressDetails"]'].attributes['Accuracy'] - unless data_accuracy.nil? - data_accuracy = data_accuracy.to_i - end - placemarks << Geocoding::Placemark.new(data['address'].text, - data_country.nil? ? "" : data_country.text, - data_administrative.nil? ? "" : data_administrative.text, - data_sub_administrative.nil? ? "" : data_sub_administrative.text, - data_locality.nil? ? "" : data_locality.text, - data_dependent_locality.nil? ? "" : data_dependent_locality.text, - data_thoroughfare.nil? ? "" : data_thoroughfare.text, - data_postal_code.nil? ? "" : data_postal_code.text, - lon, lat, data_accuracy ) - end - end - - placemarks - end - - #Group of placemarks returned by the Geocoding service. If the result is valid the +status+ attribute should be equal to Geocoding::GE0_SUCCESS - class Placemarks < Array - attr_accessor :name,:status - - def initialize(name,status) - super(0) - @name = name - @status = status - end - end - - #A result from the Geocoding service. - class Placemark < Struct.new(:address,:country_code,:administrative_area,:sub_administrative_area,:locality,:dependent_locality,:thoroughfare,:postal_code,:longitude,:latitude,:accuracy) - def lonlat - [longitude,latitude] - end - - def latlon - [latitude,longitude] - end - end - end - end -end diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/helper.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/helper.rb deleted file mode 100644 index ddaf8bbc..00000000 --- a/vendor/plugins/ym4r_gm/lib/gm_plugin/helper.rb +++ /dev/null @@ -1,41 +0,0 @@ - -Ym4r::GmPlugin::GPolyline.class_eval do - #Creates a GPolyline object from a georuby line string. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. - def self.from_georuby(line_string,color = nil,weight = nil,opacity = nil) - GPolyline.new(line_string.points.collect { |point| GLatLng.new([point.y,point.x])},color,weight,opacity) - end -end - -Ym4r::GmPlugin::GMarker.class_eval do - #Creates a GMarker object from a georuby point. Accepts the same options as the GMarker constructor. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. - def self.from_georuby(point,options = {}) - GMarker.new([point.y,point.x],options) - end -end - -Ym4r::GmPlugin::GLatLng.class_eval do - #Creates a GLatLng object from a georuby point. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. - def self.from_georuby(point,unbounded = nil) - GLatLng.new([point.y,point.x],unbounded) - end -end - -Ym4r::GmPlugin::GLatLngBounds.class_eval do - #Creates a GLatLng object from a georuby point. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. - def self.from_georuby(envelope) - GLatLngBounds.new(GLatLng.from_georuby(envelope.lower_corner), - GLatLng.from_georuby(envelope.upper_corner)) - end -end - -Ym4r::GmPlugin::GPolygon.class_eval do - #Creates a GPolygon object from a georuby polygon or line string. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. - def self.from_georuby(ls_or_p, stroke_color="#000000",stroke_weight=1,stroke_opacity=1.0,color="#ff0000",opacity=1.0) - if ls_or_p.is_a?(GeoRuby::SimpleFeatures::LineString) - GPolygon.new(ls_or_p.collect { |point| GLatLng.new([point.y,point.x])},stroke_color,stroke_weight,stroke_opacity,color,opacity) - else - GPolygon.new(ls_or_p[0].collect { |point| GLatLng.new([point.y,point.x])},stroke_color,stroke_weight,stroke_opacity,color,opacity) - end - end -end - diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/key.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/key.rb deleted file mode 100644 index 0de9c188..00000000 --- a/vendor/plugins/ym4r_gm/lib/gm_plugin/key.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Ym4r - module GmPlugin - class GMapsAPIKeyConfigFileNotFoundException < StandardError - end - - class AmbiguousGMapsAPIKeyException < StandardError - end - - #Class fo the manipulation of the API key - class ApiKey - #Read the API key config for the current ENV - unless File.exist?(RAILS_ROOT + '/config/gmaps_api_key.yml') - raise GMapsAPIKeyConfigFileNotFoundException.new("File RAILS_ROOT/config/gmaps_api_key.yml not found") - else - env = ENV['RAILS_ENV'] || RAILS_ENV - GMAPS_API_KEY = YAML.load_file(RAILS_ROOT + '/config/gmaps_api_key.yml')[env] - end - - def self.get(options = {}) - if options.has_key?(:key) - options[:key] - elsif GMAPS_API_KEY.is_a?(Hash) - #For this environment, multiple hosts are possible. - #:host must have been passed as option - if options.has_key?(:host) - GMAPS_API_KEY[options[:host]] - else - raise AmbiguousGMapsAPIKeyException.new(GMAPS_API_KEY.keys.join(",")) - end - else - #Only one possible key: take it and ignore the :host option if it is there - GMAPS_API_KEY - end - end - end - end -end diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/layer.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/layer.rb deleted file mode 100644 index d7fc151f..00000000 --- a/vendor/plugins/ym4r_gm/lib/gm_plugin/layer.rb +++ /dev/null @@ -1,126 +0,0 @@ -module Ym4r - module GmPlugin - #Map types of the map - class GMapType - include MappingObject - - G_NORMAL_MAP = Variable.new("G_NORMAL_MAP") - G_SATELLITE_MAP = Variable.new("G_SATELLITE_MAP") - G_HYBRID_MAP = Variable.new("G_HYBRID_MAP") - G_PHYSICAL_MAP = Variable.new("G_PHYSICAL_MAP") - - attr_accessor :layers, :name, :projection, :options - - #The options can be any of the GMapType options detailed in the documentation + a :projection. - def initialize(layers, name, options = {}) - @layers = layers - @name = name - @projection = options.delete(:projection) || GMercatorProjection.new - @options = options - end - - def create - "new GMapType(#{MappingObject.javascriptify_variable(Array(layers))}, #{MappingObject.javascriptify_variable(projection)}, #{MappingObject.javascriptify_variable(name)}, #{MappingObject.javascriptify_variable(options)})" - end - end - - #Represents a mercator projection for zoom levels 0 to 17 (more than that by passing an argument to the constructor) - class GMercatorProjection - include MappingObject - - attr_accessor :n - - def initialize(n = nil) - @n = n - end - - def create - if n.nil? - return "G_NORMAL_MAP.getProjection()" - else - "new GMercatorProjection(#{@n})" - end - end - end - - #Abstract Tile layer. Subclasses must implement a get_tile_url method. - class GTileLayer - include MappingObject - - attr_accessor :opacity, :zoom_range, :copyright, :format - - #Options are the following, with default values: - #:zoom_range (0..17), :copyright ({'prefix' => '', 'copyright_texts' => [""]}), :opacity (1.0), :format ("png") - def initialize(options = {}) - @opacity = options[:opacity] || 1.0 - @zoom_range = options[:zoom_range] || (0..17) - @copyright = options[:copyright] || {'prefix' => '', 'copyright_texts' => [""]} - @format = (options[:format] || "png").to_s - end - - def create - "addPropertiesToLayer(new GTileLayer(new GCopyrightCollection(\"\"),#{zoom_range.begin},#{zoom_range.end}),#{get_tile_url},function(a,b) {return #{MappingObject.javascriptify_variable(@copyright)};}\n,function() {return #{@opacity};},function(){return #{@format == "png"};})" - end - - #for subclasses to implement - def get_tile_url - end - end - - #Represents a pre tiled layer, taking images directly from a server, without using a server script. - class PreTiledLayer < GTileLayer - attr_accessor :base_url - - #Possible options are the same as for the GTileLayer constructor - def initialize(base_url,options = {}) - super(options) - @base_url = base_url - end - - #Returns the code to determine the url to fetch the tile. Follows the convention adopted by the tiler: {base_url}/tile_{b}_{a.x}_{a.y}.{format} - def get_tile_url - "function(a,b) { return '#{@base_url}/tile_' + b + '_' + a.x + '_' + a.y + '.#{format}';}" - end - end - - #Represents a pretiled layer (it actually does not really matter where the tiles come from). Calls an action on the server to get back the tiles. It passes the action arguments x, y (coordinates of the tile) and z (zoom level). It can be used, for example, to return default tiles when the requested tile is not present. - class PreTiledLayerFromAction < PreTiledLayer - def get_tile_url - "function(a,b) { return '#{base_url}?x=' + a.x + '&y=' + a.y + '&z=' + b ;}" - end - end - - #Represents a TileLayer where the tiles are generated dynamically from a WMS server (MapServer, GeoServer,...) - #You need to include the JavaScript file wms-gs.js for this to work - #see http://docs.codehaus.org/display/GEOSDOC/Google+Maps - class WMSLayer < GTileLayer - attr_accessor :base_url, :layers, :styles, :merc_proj, :use_geographic - - #Options are the same as with GTileLayer + :styles (""), :merc_proj (:mapserver), :use_geographic (false) - def initialize(base_url, layers, options = {}) - super(options) - @base_url = base_url.gsub(/\?$/,"") #standardize the url - @layers = layers - @styles = options[:styles] || "" - merc_proj = options[:merc_proj] || :mapserver - @merc_proj = if merc_proj == :mapserver - "54004" - elsif merc_proj == :geoserver - "41001" - else - merc_proj.to_s - end - @use_geographic = options.has_key?(:use_geographic)? options[:use_geographic] : false - puts format - end - - def get_tile_url - "getTileUrlForWMS" - end - - def create - "addWMSPropertiesToLayer(#{super},#{MappingObject.javascriptify_variable(@base_url)},#{MappingObject.javascriptify_variable(@layers)},#{MappingObject.javascriptify_variable(@styles)},#{MappingObject.javascriptify_variable(format)},#{MappingObject.javascriptify_variable(@merc_proj)},#{MappingObject.javascriptify_variable(@use_geographic)})" - end - end - end -end diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/map.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/map.rb deleted file mode 100644 index 22ddf4c1..00000000 --- a/vendor/plugins/ym4r_gm/lib/gm_plugin/map.rb +++ /dev/null @@ -1,282 +0,0 @@ -module Ym4r - module GmPlugin - #Representing the Google Maps API class GMap2. - class GMap - include MappingObject - - #A constant containing the declaration of the VML namespace, necessary to display polylines under IE. - VML_NAMESPACE = "xmlns:v=\"urn:schemas-microsoft-com:vml\"" - - #The id of the DIV that will contain the map in the HTML page. - attr_reader :container - - #By default the map in the HTML page will be globally accessible with the name +map+. - def initialize(container, variable = "map") - @container = container - @variable = variable - @init = [] - @init_end = [] #for stuff that must be initialized at the end (controls) - @init_begin = [] #for stuff that must be initialized at the beginning (center + zoom) - @global_init = [] - end - - #Deprecated. Use the static version instead. - def header(with_vml = true) - GMap.header(:with_vml => with_vml) - end - - #Outputs the header necessary to use the Google Maps API, by including the JS files of the API, as well as a file containing YM4R/GM helper functions. By default, it also outputs a style declaration for VML elements. This default can be overriddent by passing :with_vml => false as option to the method. You can also pass a :host option in order to select the correct API key for the location where your app is currently running, in case the current environment has multiple possible keys. Usually, in this case, you should pass it @request.host. If you have defined only one API key for the current environment, the :host option is ignored. Finally you can override all the key settings in the configuration by passing a value to the :key key. You can pass a language for the map type buttons with the :hl option (possible values are: Japanese (ja), French (fr), German (de), Italian (it), Spanish (es), Catalan (ca), Basque (eu) and Galician (gl): no values means english). Finally, you can pass :local_search => true to get the header css and js information needed for the local search control. If you do want local search you must also add :local_search => true to the @map.control_init method. - def self.header(options = {}) - options[:with_vml] = true unless options.has_key?(:with_vml) - options[:hl] ||= '' - options[:local_search] = false unless options.has_key?(:local_search) - api_key = ApiKey.get(options) - a = "\n" - a << "\n" unless options[:without_js] - a << "" if options[:with_vml] - a << "" if options[:local_search] - a << "\n" if options[:local_search] - a << "" if options[:local_search] - a - end - - #Outputs the

which has been configured to contain the map. You can pass :width and :height as options to output this in the style attribute of the DIV element (you could also achieve the same effect by putting the dimension info into a CSS or using the instance method GMap#header_width_height). You can aslo pass :class to set the classname of the div. - def div(options = {}) - attributes = "id=\"#{@container}\" " - if options.has_key?(:height) && options.has_key?(:width) - width = options.delete(:width) - if width.is_a?(Integer) or width =~ /^[0-9]+$/ - width = width.to_s + "px" - end - height = options.delete(:height) - if height.is_a?(Integer) or height =~ /^[0-9]+$/ - height = height.to_s + "px" - end - attributes += "style=\"width:#{width};height:#{height}\" " - end - if options.has_key?(:class) - attributes += options.keys.map {|opt| "#{opt}=\"#{options[opt]}\"" }.join(" ") - end - "
" - end - - #Outputs a style declaration setting the dimensions of the DIV container of the map. This info can also be set manually in a CSS. - def header_width_height(width,height) - "" - end - - #Records arbitrary JavaScript code and outputs it during initialization inside the +load+ function. - def record_init(code) - @init << code - end - - #Initializes the controls: you can pass a hash with keys :small_map, :large_map, :small_zoom, :scale, :map_type, :overview_map and a boolean value as the value (usually true, since the control is not displayed by default), :local_search and :local_search_options - def control_init(controls = {}) - @init_end << add_control(GSmallMapControl.new) if controls[:small_map] - @init_end << add_control(GLargeMapControl.new) if controls[:large_map] - @init_end << add_control(GSmallZoomControl.new) if controls[:small_zoom] - @init_end << add_control(GScaleControl.new) if controls[:scale] - @init_end << add_control(GMapTypeControl.new) if controls[:map_type] - @init_end << add_control(GHierarchicalMapTypeControl.new) if controls[:hierarchical_map_type] - @init_end << add_control(GOverviewMapControl.new) if controls[:overview_map] - @init_end << add_control(GLocalSearchControl.new(controls[:anchor], controls[:offset_width], controls[:offset_height], controls[:local_search_options])) if controls[:local_search] - end - - #Initializes the interface configuration: double-click zoom, dragging, continuous zoom,... You can pass a hash with keys :dragging, :info_window, :double_click_zoom, :continuous_zoom and :scroll_wheel_zoom. The values should be true or false. Check the google maps API doc to know what the default values are. - def interface_init(interface = {}) - if !interface[:dragging].nil? - if interface[:dragging] - @init << enableDragging() - else - @init << disableDragging() - end - end - if !interface[:info_window].nil? - if interface[:info_window] - @init << enableInfoWindow() - else - @init << disableInfoWindow() - end - end - if !interface[:double_click_zoom].nil? - if interface[:double_click_zoom] - @init << enableDoubleClickZoom() - else - @init << disableDoubleClickZoom() - end - end - if !interface[:continuous_zoom].nil? - if interface[:continuous_zoom] - @init << enableContinuousZoom() - else - @init << disableContinuousZoom() - end - end - if !interface[:scroll_wheel_zoom].nil? - if interface[:scroll_wheel_zoom] - @init << enableScrollWheelZoom() - else - @init << disableScrollWheelZoom() - end - end - end - - #Initializes the initial center and zoom of the map. +center+ can be both a GLatLng object or a 2-float array. - def center_zoom_init(center, zoom) - if center.is_a?(GLatLng) - @init_begin << set_center(center,zoom) - else - @init_begin << set_center(GLatLng.new(center),zoom) - end - end - - #Center and zoom based on the coordinates passed as argument (either 2D arrays or GLatLng objects) - def center_zoom_on_points_init(*points) - if(points.length > 0) - if(points[0].is_a?(Array)) - points = points.collect { |point| GLatLng.new(point) } - end - @init_begin << center_and_zoom_on_points(points) - end - end - - #Center and zoom based on the bbox corners. Pass a GLatLngBounds object, an array of 2D coordinates (sw and ne) or an array of GLatLng objects (sw and ne). - def center_zoom_on_bounds_init(latlngbounds) - if(latlngbounds.is_a?(Array)) - if latlngbounds[0].is_a?(Array) - latlngbounds = GLatLngBounds.new(GLatLng.new(latlngbounds[0]),GLatLng.new(latlngbounds[1])) - elsif latlngbounds[0].is_a?(GLatLng) - latlngbounds = GLatLngBounds.new(*latlngbounds) - end - end - #else it is already a latlngbounds object - - @init_begin << center_and_zoom_on_bounds(latlngbounds) - end - - #Initializes the map by adding an overlay (marker or polyline). - def overlay_init(overlay) - @init << add_overlay(overlay) - end - - #Sets up a new map type. If +add+ is false, all the other map types of the map are wiped out. If you want to access the map type in other methods, you should declare the map type first (with +declare_init+). - def add_map_type_init(map_type, add = true) - unless add - @init << get_map_types.set_property(:length,0) - end - @init << add_map_type(map_type) - end - #for legacy purpose - alias :map_type_init :add_map_type_init - - #Sets the map type displayed by default after the map is loaded. It should be known from the map (ie either the default map types or a user-defined map type added with add_map_type_init). Use set_map_type_init(GMapType::G_SATELLITE_MAP) or set_map_type_init(GMapType::G_HYBRID_MAP) to initialize the map with repsecitvely the Satellite view and the hybrid view. - def set_map_type_init(map_type) - @init << set_map_type(map_type) - end - - #Locally declare a MappingObject with variable name "name" - def declare_init(variable, name) - @init << variable.declare(name) - end - - #Records arbitrary JavaScript code and outputs it during initialization outside the +load+ function (ie globally). - def record_global_init(code) - @global_init << code - end - - #Deprecated. Use icon_global_init instead. - def icon_init(icon , name) - icon_global_init(icon , name) - end - - #Initializes an icon and makes it globally accessible through the JavaScript variable of name +variable+. - def icon_global_init(icon , name, options = {}) - declare_global_init(icon,name,options) - end - - #Registers an event - def event_init(object,event,callback) - @init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});" - end - - #Registers an event globally - def event_global_init(object,event,callback) - @global_init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});" - end - - #Declares the overlay globally with name +name+ - def overlay_global_init(overlay,name, options = {}) - declare_global_init(overlay,name, options) - @init << add_overlay(overlay) - end - - #Globally declare a MappingObject with variable name "name". Option :local_construction should be passed if the construction has to be done inside the onload callback method (for exsample if it depends on the GMap to be initialized) - def declare_global_init(variable,name, options = {}) - unless options[:local_construction] - @global_init << "var #{variable.assign_to(name)}" - else - @global_init << "var #{name};" - @init << variable.assign_to(name) - end - end - - #Outputs the initialization code for the map. By default, it outputs the script tags, performs the initialization in response to the onload event of the window and makes the map globally available. If you pass +true+ to the option key :full, the map will be setup in full screen, in which case it is not necessary (but not harmful) to set a size for the map div. - def to_html(options = {}) - no_load = options[:no_load] - no_script_tag = options[:no_script_tag] - no_declare = options[:no_declare] - no_global = options[:no_global] - fullscreen = options[:full] - load_pr = options[:proto_load] #to prevent some problems when the onload event callback from Prototype is used - - html = "" - html << "" if !no_script_tag - - if fullscreen - #setting up the style in case of full screen - html << "" - end - - html - end - - #Outputs in JavaScript the creation of a GMap2 object - def create - "new GMap2(document.getElementById(\"#{@container}\"))" - end - end - end -end - diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/mapping.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/mapping.rb deleted file mode 100644 index 66d825eb..00000000 --- a/vendor/plugins/ym4r_gm/lib/gm_plugin/mapping.rb +++ /dev/null @@ -1,128 +0,0 @@ -module Ym4r - module GmPlugin - #The module where all the Ruby-to-JavaScript conversion takes place. It is included by all the classes in the YM4R library. - module MappingObject - #The name of the variable in JavaScript space. - attr_reader :variable - - #Creates javascript code for missing methods + takes care of listeners - def method_missing(name,*args) - str_name = name.to_s - if str_name =~ /^on_(.*)/ - if args.length != 1 - raise ArgumentError("Only 1 argument is allowed on on_ methods"); - else - Variable.new("GEvent.addListener(#{to_javascript},\"#{MappingObject.javascriptify_method($1)}\",#{args[0]})") - end - else - args.collect! do |arg| - MappingObject.javascriptify_variable(arg) - end - Variable.new("#{to_javascript}.#{MappingObject.javascriptify_method(str_name)}(#{args.join(",")})") - end - end - - #Creates javascript code for array or hash indexing - def [](index) #index could be an integer or string - return Variable.new("#{to_javascript}[#{MappingObject.javascriptify_variable(index)}]") - end - - #Transforms a Ruby object into a JavaScript string : MAppingObject, String, Array, Hash and general case (using to_s) - def self.javascriptify_variable(arg) - if arg.is_a?(MappingObject) - arg.to_javascript - elsif arg.is_a?(String) - "\"#{MappingObject.escape_javascript(arg)}\"" - elsif arg.is_a?(Array) - "[" + arg.collect{ |a| MappingObject.javascriptify_variable(a)}.join(",") + "]" - elsif arg.is_a?(Hash) - "{" + arg.to_a.collect do |v| - "#{MappingObject.javascriptify_method(v[0].to_s)} : #{MappingObject.javascriptify_variable(v[1])}" - end.join(",") + "}" - elsif arg.nil? - "undefined" - else - arg.to_s - end - end - - #Escape string to be used in JavaScript. Lifted from rails. - def self.escape_javascript(javascript) - javascript.gsub(/\r\n|\n|\r/, "\\n").gsub("\"") { |m| "\\#{m}" } - end - - #Transform a ruby-type method name (like add_overlay) to a JavaScript-style one (like addOverlay). - def self.javascriptify_method(method_name) - method_name.gsub(/_(\w)/){|s| $1.upcase} - end - - #Declares a Mapping Object bound to a JavaScript variable of name +variable+. - def declare(variable) - @variable = variable - "var #{@variable} = #{create};" - end - - #declare with a random variable name - def declare_random(init,size = 8) - s = init.clone - 6.times { s << (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr } - declare(s) - end - - #Checks if the MappinObject has been declared - def declared? - !@variable.nil? - end - - #Binds a Mapping object to a previously declared JavaScript variable of name +variable+. - def assign_to(variable) - @variable = variable - "#{@variable} = #{create};" - end - - #Assign the +value+ to the +property+ of the MappingObject - def set_property(property, value) - "#{to_javascript}.#{MappingObject.javascriptify_method(property.to_s)} = #{MappingObject.javascriptify_variable(value)}" - end - - #Returns the code to get a +property+ from the MappingObject - def get_property(property) - Variable.new("#{to_javascript}.#{MappingObject.javascriptify_method(property.to_s)}") - end - - #Returns a Javascript code representing the object - def to_javascript - unless @variable.nil? - @variable - else - create - end - end - - #Creates a Mapping Object in JavaScript. - #To be implemented by subclasses if needed - def create - end - end - - #Used to bind a ruby variable to an already existing JavaScript one. It doesn't have to be a variable in the sense "var variable" but it can be any valid JavaScript expression that has a value. - class Variable - include MappingObject - - def initialize(variable) - @variable = variable - end - #Returns the javascript expression contained in the object. - def create - @variable - end - #Returns the expression inside the Variable followed by a ";" - def to_s - @variable + ";" - end - - UNDEFINED = Variable.new("undefined") - end - end -end - diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/overlay.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/overlay.rb deleted file mode 100644 index 5e0953af..00000000 --- a/vendor/plugins/ym4r_gm/lib/gm_plugin/overlay.rb +++ /dev/null @@ -1,386 +0,0 @@ -module Ym4r - module GmPlugin - #A graphical marker positionned through geographic coordinates (in the WGS84 datum). An HTML info window can be set to be displayed when the marker is clicked on. - class GMarker - include MappingObject - attr_accessor :point, :options, :info_window, :info_window_tabs, :address - #The +points+ argument can be either a GLatLng object or an array of 2 floats. The +options+ keys can be: :icon, :clickable, :title, :info_window and :info_window_tabs, as well as :max_width. The value of the +info_window+ key is a string of HTML code that will be displayed when the markers is clicked on. The value of the +info_window_tabs+ key is an array of GInfoWindowTab objects or a hash directly, in which case it will be transformed to an array of GInfoWindowTabs, with the keys as the tab headers and the values as the content. - def initialize(position, options = {}) - if position.is_a?(Array) - @point = GLatLng.new(position) - elsif position.is_a?(String) - @point = Variable.new("INVISIBLE") #default coordinates: won't appear anyway - @address = position - else - @point = position - end - @info_window = options.delete(:info_window) - @info_window_tabs = options.delete(:info_window_tabs) - if options.has_key?(:max_url) - @info_window_options = {:max_url => options.delete(:max_url) } - else - @info_window_options = {} - end - @options = options - end - #Creates a marker: If an info_window or info_window_tabs is present, the response to the click action from the user is setup here. - def create - if @options.empty? - creation = "new GMarker(#{MappingObject.javascriptify_variable(@point)})" - else - creation = "new GMarker(#{MappingObject.javascriptify_variable(@point)},#{MappingObject.javascriptify_variable(@options)})" - end - if @info_window && @info_window.is_a?(String) - creation = "addInfoWindowToMarker(#{creation},#{MappingObject.javascriptify_variable(@info_window)},#{MappingObject.javascriptify_variable(@info_window_options)})" - elsif @info_window_tabs && @info_window_tabs.is_a?(Hash) - creation = "addInfoWindowTabsToMarker(#{creation},#{MappingObject.javascriptify_variable(@info_window_tabs.to_a.collect{|kv| GInfoWindowTab.new(kv[0],kv[1] ) })},#{MappingObject.javascriptify_variable(@info_window_options)})" - elsif @info_window_tabs - creation = "addInfoWindowTabsToMarker(#{creation},#{MappingObject.javascriptify_variable(Array(@info_window_tabs))},#{MappingObject.javascriptify_variable(@info_window_options)})" - end - if @address.nil? - creation - else - "addGeocodingToMarker(#{creation},#{MappingObject.javascriptify_variable(@address)})" - end - end - end - - #Represents a tab to be displayed in a bubble when a marker is clicked on. - class GInfoWindowTab < Struct.new(:tab,:content) - include MappingObject - def create - "new GInfoWindowTab(#{MappingObject.javascriptify_variable(tab)},#{MappingObject.javascriptify_variable(content)})" - end - end - - #Represents a definition of an icon. You can pass rubyfied versions of the attributes detailed in the Google Maps API documentation. You can initialize global icons to be used in the application by passing a icon object, along with a variable name, to GMap#icon_init. If you want to declare an icon outside this, you will need to declare it first, since the JavaScript constructor does not accept any argument. - class GIcon - include MappingObject - DEFAULT = Variable.new("G_DEFAULT_ICON") - attr_accessor :options, :copy_base - - #Options can contain all the attributes (in rubyfied format) of a GIcon object (see Google's doc), as well as :copy_base, which indicates if the icon is copied from another one. - def initialize(options = {}) - @copy_base = options.delete(:copy_base) - @options = options - end - #Creates a GIcon. - def create - if @copy_base - c = "new GIcon(#{MappingObject.javascriptify_variable(@copy_base)})" - else - c = "new GIcon()" - end - if !options.empty? - "addOptionsToIcon(#{c},#{MappingObject.javascriptify_variable(@options)})" - else - c - end - end - end - - #A polyline. - class GPolyline - include MappingObject - attr_accessor :points,:color,:weight,:opacity - #Can take an array of +GLatLng+ or an array of 2D arrays. A method to directly build a polyline from a GeoRuby linestring is provided in the helper.rb file. - def initialize(points,color = nil,weight = nil,opacity = nil) - if !points.empty? and points[0].is_a?(Array) - @points = points.collect { |pt| GLatLng.new(pt) } - else - @points = points - end - @color = color - @weight = weight - @opacity = opacity - end - #Creates a new polyline. - def create - a = "new GPolyline(#{MappingObject.javascriptify_variable(points)}" - a << ",#{MappingObject.javascriptify_variable(@color)}" if @color - a << ",#{MappingObject.javascriptify_variable(@weight)}" if @weight - a << ",#{MappingObject.javascriptify_variable(@opacity)}" if @opacity - a << ")" - end - end - - #Encoded GPolyline class - class GPolylineEncoded - include MappingObject - attr_accessor :points,:color,:weight,:opacity,:levels,:zoom_factor,:num_levels - - def initialize(options={}) - #points = options[:points] - #if !points.empty? and points[0].is_a?(Array) - # @points = points.collect { |pt| GLatLng.new(pt) } - #else - #@points = points - #end - @points = options[:points] - @color = options[:color] - @weight = options[:weight] - @opacity = options[:opacity] - @levels = options[:levels] || "BBBBBBBBBBBB" - @zoom_factor = options[:zoom_factor] || 32 - @num_levels = options[:num_levels] || 4 - end - def create - a = "new GPolyline.fromEncoded({points: #{MappingObject.javascriptify_variable(points)},\n" - a << "levels: #{MappingObject.javascriptify_variable(@levels)}," - a << "zoomFactor: #{MappingObject.javascriptify_variable(@zoom_factor)}," - a << "numLevels: #{MappingObject.javascriptify_variable(@num_levels)}" - a << ",color: #{MappingObject.javascriptify_variable(@color)}" if @color - a << ",weight: #{MappingObject.javascriptify_variable(@weight)}" if @weight - a << ",opacity: #{MappingObject.javascriptify_variable(@opacity)}" if @opacity - a << "})" - end - end - - #A basic Latitude/longitude point. - class GLatLng - include MappingObject - attr_accessor :lat,:lng,:unbounded - - def initialize(latlng,unbounded = nil) - @lat = latlng[0] - @lng = latlng[1] - @unbounded = unbounded - end - def create - unless @unbounded - "new GLatLng(#{MappingObject.javascriptify_variable(@lat)},#{MappingObject.javascriptify_variable(@lng)})" - else - "new GLatLng(#{MappingObject.javascriptify_variable(@lat)},#{MappingObject.javascriptify_variable(@lng)},#{MappingObject.javascriptify_variable(@unbounded)})" - end - end - end - - #A rectangular bounding box, defined by its south-western and north-eastern corners. - class GLatLngBounds < Struct.new(:sw,:ne) - include MappingObject - def create - "new GLatLngBounds(#{MappingObject.javascriptify_variable(sw)},#{MappingObject.javascriptify_variable(ne)})" - end - end - - #Polygon. Not documented yet in the Google Maps API - class GPolygon - include MappingObject - - attr_accessor :points,:stroke_color,:stroke_weight,:stroke_opacity,:color,:opacity - - #Can take an array of +GLatLng+ or an array of 2D arrays. A method to directly build a polygon from a GeoRuby polygon is provided in the helper.rb file. - def initialize(points,stroke_color="#000000",stroke_weight=1,stroke_opacity=1.0,color="#ff0000",opacity=1.0,encoded=false) - if !points.empty? and points[0].is_a?(Array) - @points = points.collect { |pt| GLatLng.new(pt) } - else - @points = points - end - @stroke_color = stroke_color - @stroke_weight = stroke_weight - @stroke_opacity = stroke_opacity - @color = color - @opacity = opacity - end - - #Creates a new polygon - def create - a = "new GPolygon(#{MappingObject.javascriptify_variable(points)}" - a << ",#{MappingObject.javascriptify_variable(@stroke_color)}" - a << ",#{MappingObject.javascriptify_variable(@stroke_weight)}" - a << ",#{MappingObject.javascriptify_variable(@stroke_opacity)}" - a << ",#{MappingObject.javascriptify_variable(@color)}" - a << ",#{MappingObject.javascriptify_variable(@opacity)}" - a << ")" - end - end - - class GPolygonEncoded - include MappingObject - - attr_accessor :polyline, :color, :opacity, :outline, :fill - - def initialize(polylines,fill=true,color="#000000",opacity=0.5,outline=false) - #force polylines to be an array - if polylines.is_a? Array - @polylines = polylines - else - @polylines = [polylines] - end - @color = color - @fill = fill - @opacity = opacity - @outline = outline - end - - #Creates a new polygon. - def create - polylines_for_polygon= [] - @polylines.each do |p| - x = "{points: #{MappingObject.javascriptify_variable(p.points)}," - x << "levels: #{MappingObject.javascriptify_variable(p.levels)}," - x << "zoomFactor: #{MappingObject.javascriptify_variable(p.zoom_factor)}," - x << "numLevels: #{MappingObject.javascriptify_variable(p.num_levels)} " - x << "}" - polylines_for_polygon << x - end - - polylines_for_polygon = "[" + polylines_for_polygon.join(",") + "]" - - a = "new GPolygon.fromEncoded({polylines: #{polylines_for_polygon}," - a << "fill: #{MappingObject.javascriptify_variable(@fill)}," - a << "color: #{MappingObject.javascriptify_variable(@color)}," - a << "opacity: #{MappingObject.javascriptify_variable(@opacity)}," - a << "outline: #{MappingObject.javascriptify_variable(@outline)}" - a << "})" - end - end - - class ELabel - attr_accessor :point, :text, :style - include MappingObject - - def initialize(point, text=nil, style=nil) - @point = point - @text = text - @style = style - end - - def create - a = "new ELabel(#{MappingObject.javascriptify_variable(@point)}" - a << ",#{MappingObject.javascriptify_variable(@text)}" if @text - a << ",#{MappingObject.javascriptify_variable(@style)}" if @style - a << ")" - end - end - - - #A GGeoXml object gets data from a GeoRSS or KML feed and displays it. Use overlay_init to add it to a map at initialization time. - class GGeoXml - include MappingObject - - attr_accessor :url - - def initialize(url) - @url = url - end - - def create - "new GGeoXml(#{MappingObject.javascriptify_variable(@url)})" - end - - end - - #A GOverlay representing a group of GMarkers. The GMarkers can be identified with an id, which can be used to show the info window of a specific marker, in reponse, for example, to a click on a link. The whole group can be shown on and off at once. It should be declared global at initialization time to be useful. - class GMarkerGroup - include MappingObject - attr_accessor :active, :markers, :markers_by_id - - def initialize(active = true , markers = nil) - @active = active - @markers = [] - @markers_by_id = {} - if markers.is_a?(Array) - @markers = markers - elsif markers.is_a?(Hash) - @markers_by_id = markers - end - end - - def create - "new GMarkerGroup(#{MappingObject.javascriptify_variable(@active)},#{MappingObject.javascriptify_variable(@markers)},#{MappingObject.javascriptify_variable(@markers_by_id)})" - end - end - - #Can be used to implement a clusterer, similar to the clusterer below, except that there is more stuff to manage explicitly byt the programmer (but this is also more flexible). See the README for usage esamples. - class GMarkerManager - include MappingObject - - attr_accessor :map,:options,:managed_markers - - #options can be :border_padding, :max_zoom, :track_markers and :managed_markers: managed_markers must be an array of ManagedMarker objects - def initialize(map, options = {}) - @map = map - @managed_markers = Array(options.delete(:managed_markers)) #[] if nil - @options = options - end - - def create - puts @options.inspect - "addMarkersToManager(new GMarkerManager(#{MappingObject.javascriptify_variable(@map)},#{MappingObject.javascriptify_variable(@options)}),#{MappingObject.javascriptify_variable(@managed_markers)})" - end - - end - - #A set of similarly managed markers: They share the same minZoom and maxZoom. - class ManagedMarker - include MappingObject - - attr_accessor :markers,:min_zoom, :max_zoom - - def initialize(markers,min_zoom,max_zoom = nil) - @markers = markers - @min_zoom = min_zoom - @max_zoom = max_zoom - end - - def create - "new ManagedMarker(#{MappingObject.javascriptify_variable(@markers)},#{MappingObject.javascriptify_variable(@min_zoom)},#{MappingObject.javascriptify_variable(@max_zoom)})" - end - - end - - #Makes the link with the Clusterer2 library by Jef Poskanzer (slightly modified though). Is a GOverlay making clusters out of its GMarkers, so that GMarkers very close to each other appear as one when the zoom is low. When the zoom gets higher, the individual markers are drawn. - class Clusterer - include MappingObject - attr_accessor :markers,:icon, :max_visible_markers, :grid_size, :min_markers_per_cluster , :max_lines_per_info_box - - def initialize(markers = [], options = {}) - @markers = markers - @icon = options[:icon] || GIcon::DEFAULT - @max_visible_markers = options[:max_visible_markers] || 150 - @grid_size = options[:grid_size] || 5 - @min_markers_per_cluster = options[:min_markers_per_cluster] || 5 - @max_lines_per_info_box = options[:max_lines_per_info_box] || 10 - end - - def create - js_marker = '[' + @markers.collect do |marker| - add_description(marker) - end.join(",") + ']' - - "new Clusterer(#{js_marker},#{MappingObject.javascriptify_variable(@icon)},#{MappingObject.javascriptify_variable(@max_visible_markers)},#{MappingObject.javascriptify_variable(@grid_size)},#{MappingObject.javascriptify_variable(@min_markers_per_cluster)},#{MappingObject.javascriptify_variable(@max_lines_per_info_box)})" - end - - private - def add_description(marker) - "addDescriptionToMarker(#{MappingObject.javascriptify_variable(marker)},#{MappingObject.javascriptify_variable(marker.options[:description] || marker.options[:title] || '')})" - end - end - - #Makes the link with the MGeoRSS extension by Mikel Maron (a bit modified though). It lets you overlay on top of Google Maps the items present in a RSS feed that has GeoRss data. This data can be either in W3C Geo vocabulary or in the GeoRss Simple format. See http://georss.org to know more about GeoRss. - class GeoRssOverlay - include MappingObject - attr_accessor :url, :proxy, :icon, :options - - #You can pass the following options: - #- :icon: An icon for the items of the feed. Defaults to the classic red balloon icon. - #- :proxy: An URL on your server where fetching the RSS feed will be taken care of. - #- :list_div: In case you want a list of all the markers, with a link on which you can click in order to display the info on the marker, use this option to indicate the ID of the div (that you must place yourself). - #- :list_item_class: class of the DIV containing each item of the list. Ignored if option :list_div is not set. - #- :limit: Maximum number of items to display on the map. - #- :content_div: Instead of having an info window appear, indicates the ID of the DIV where this info should be displayed. - def initialize(url, options = {}) - @url = url - @icon = options.delete(:icon) || GIcon::DEFAULT - @proxy = options.delete(:proxy) || Variable::UNDEFINED - @options = options - end - - def create - "new GeoRssOverlay(#{MappingObject.javascriptify_variable(@url)},#{MappingObject.javascriptify_variable(@icon)},#{MappingObject.javascriptify_variable(@proxy)},#{MappingObject.javascriptify_variable(@options)})" - end - end - - end -end diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/point.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/point.rb deleted file mode 100644 index bcde4e28..00000000 --- a/vendor/plugins/ym4r_gm/lib/gm_plugin/point.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Ym4r - module GmPlugin - #A point in pixel coordinates - class GPoint < Struct.new(:x,:y) - include MappingObject - def create - "new GPoint(#{x},#{y})" - end - end - #A rectangular that contains all the pixel points passed as arguments - class GBounds - include MappingObject - attr_accessor :points - #Accepts both an array of GPoint and an array of 2-element arrays - def initialize(points) - if !points.empty? and points[0].is_a?(Array) - @points = points.collect { |pt| GPoint.new(pt[0],pt[1]) } - else - @points = points - end - end - def create - "new GBounds([#{@points.map { |pt| pt.to_javascript}.join(",")}])" - end - end - #A size object, in pixel space - class GSize < Struct.new(:width,:height) - include MappingObject - def create - "new GSize(#{width},#{height})" - end - end - end -end diff --git a/vendor/plugins/ym4r_gm/lib/ym4r_gm.rb b/vendor/plugins/ym4r_gm/lib/ym4r_gm.rb deleted file mode 100644 index 24c9068f..00000000 --- a/vendor/plugins/ym4r_gm/lib/ym4r_gm.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'gm_plugin/key' -require 'gm_plugin/mapping' -require 'gm_plugin/map' -require 'gm_plugin/control' -require 'gm_plugin/point' -require 'gm_plugin/overlay' -require 'gm_plugin/layer' -require 'gm_plugin/helper' -require 'gm_plugin/geocoding' - -include Ym4r::GmPlugin diff --git a/vendor/plugins/ym4r_gm/rakefile.rb b/vendor/plugins/ym4r_gm/rakefile.rb deleted file mode 100644 index e2d46bfd..00000000 --- a/vendor/plugins/ym4r_gm/rakefile.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'rake' -require 'rake/testtask' -require 'rake/rdoctask' - -desc 'Default: run unit tests.' -task :default => :test - -desc 'Test the gm plugin.' -Rake::TestTask.new(:test) do |t| - t.libs << 'lib' - t.pattern = 'test/**/*_test.rb' - t.verbose = true -end - -desc 'Generate documentation for the gm plugin.' -Rake::RDocTask.new(:rdoc) do |rdoc| - rdoc.rdoc_dir = 'ym4r_gm-doc' - rdoc.title = 'GM' - rdoc.options << '--line-numbers' << '--inline-source' - rdoc.rdoc_files.include('README') - rdoc.rdoc_files.include('lib/**/*.rb') -end diff --git a/vendor/plugins/ym4r_gm/tasks/gm_tasks.rake b/vendor/plugins/ym4r_gm/tasks/gm_tasks.rake deleted file mode 100644 index 40745d0e..00000000 --- a/vendor/plugins/ym4r_gm/tasks/gm_tasks.rake +++ /dev/null @@ -1,4 +0,0 @@ -# desc "Explaining what the task does" -# task :gm do -# # Task goes here -# end \ No newline at end of file diff --git a/vendor/plugins/ym4r_gm/test/gm_test.rb b/vendor/plugins/ym4r_gm/test/gm_test.rb deleted file mode 100644 index cea8447a..00000000 --- a/vendor/plugins/ym4r_gm/test/gm_test.rb +++ /dev/null @@ -1,79 +0,0 @@ -$:.unshift(File.dirname(__FILE__) + '/../lib') - -require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment") - - -require 'ym4r_gm' -require 'test/unit' - -include Ym4r::GmPlugin - -class TestGoogleMaps< Test::Unit::TestCase - def test_javascriptify_method - assert_equal("addOverlayToHello",MappingObject::javascriptify_method("add_overlay_to_hello")) - end - - def test_javascriptify_variable_mapping_object - map = GMap.new("div") - assert_equal(map.to_javascript,MappingObject::javascriptify_variable(map)) - end - - def test_javascriptify_variable_numeric - assert_equal("123.4",MappingObject::javascriptify_variable(123.4)) - end - - def test_javascriptify_variable_array - map = GMap.new("div") - assert_equal("[123.4,#{map.to_javascript},[123.4,#{map.to_javascript}]]",MappingObject::javascriptify_variable([123.4,map,[123.4,map]])) - end - - def test_javascriptify_variable_hash - map = GMap.new("div") - test_str = MappingObject::javascriptify_variable("hello" => map, "chopotopoto" => [123.55,map]) - assert("{hello : #{map.to_javascript},chopotopoto : [123.55,#{map.to_javascript}]}" == test_str || "{chopotopoto : [123.55,#{map.to_javascript}],hello : #{map.to_javascript}}" == test_str) - end - - def test_method_call_on_mapping_object - map = GMap.new("div","map") - assert_equal("map.addHello(123.4);",map.add_hello(123.4).to_s) - end - - def test_nested_calls_on_mapping_object - gmap = GMap.new("div","map") - assert_equal("map.addHello(map.hoYoYo(123.4),map);",gmap.add_hello(gmap.ho_yo_yo(123.4),gmap).to_s) - end - - def test_declare_variable_latlng - point = GLatLng.new([123.4,123.6]) - assert_equal("var point = new GLatLng(123.4,123.6);",point.declare("point")) - assert_equal("point",point.variable) - end - - def test_array_indexing - obj = Variable.new("obj") - assert_equal("obj[0]",obj[0].variable) - end - - def test_google_maps_geocoding - - - placemarks = Geocoding.get("Rue Clovis Paris") - assert_equal(Geocoding::GEO_SUCCESS,placemarks.status) - assert_equal(1,placemarks.length) - placemark = placemarks[0] - assert_equal("FR",placemark.country_code) - assert_equal("Paris",placemark.locality) - assert_equal("75005",placemark.postal_code) - - #test iwht multiple placemarks - placemarks = Geocoding.get('hoogstraat, nl') - assert_equal(Geocoding::GEO_SUCCESS,placemarks.status) - assert(placemarks.length > 1) - assert(placemarks[0].latitude != placemarks[1].latitude ) - - - end - - -end -