Skip to content

Commit

Permalink
Vijay: Merging bguthrie/master (cfbaf0f)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vijay Aravamudhan committed May 6, 2010
2 parents 74926a8 + cfbaf0f commit 57646e2
Show file tree
Hide file tree
Showing 20 changed files with 237 additions and 74 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
.tmtags
*.tmproj
.idea
*.gem
16 changes: 7 additions & 9 deletions README.rdoc
@@ -1,16 +1,14 @@
= ResourceFull 0.7.5
= ResourceFull

* http://github.com/bguthrie/resource_full/

== DESCRIPTION

ResourceFull integrated with ActionController to provide a comprehensive
RESTful resource modeling and querying framework. It provides parameter
ResourceFull provides a fully-compliant ActiveResource server implementation
built on ActionController. Additionally, it provides RESTful parameter
queryability, paging, sorting, separation of controller concerns, multiple
formats (HTML, XML, JSON), CRUD access permissions, and API metadata
surrounding the resource itself. It's opinionated but is intended to provide
you with as much as possible without limiting your ability to customize its
behavior.
behavior. Unless overridden, it uses Rails' default to_xml and to_json methods
to provide object serialization, which ActiveResource expects but many REST
clients will not.

== GOALS

Expand Down Expand Up @@ -78,7 +76,7 @@ add +map.api+ to your +routes.rb+ file.

(The MIT License)

Copyright (c) 2009 Brian Guthrie
Copyright (c) 2010 Brian Guthrie

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down
10 changes: 0 additions & 10 deletions init.rb

This file was deleted.

1 change: 0 additions & 1 deletion install.rb

This file was deleted.

35 changes: 24 additions & 11 deletions lib/resource_full.rb
@@ -1,14 +1,27 @@
# Dependencies
require 'active_record'
require 'action_controller'
require 'action_pack'

# Extensions
Dir[File.dirname(__FILE__) + "/resource_full/core_extensions/*.rb"].each do |extension|
require extension
end

# Core library
require 'resource_full/core_extensions/exception'
require 'resource_full/core_extensions/from_json'
require 'resource_full/dispatch'
require 'resource_full/query'
require 'resource_full/render'
require 'resource_full/retrieve'
require 'resource_full/version'
require 'resource_full/base'
require File.dirname(__FILE__) + '/resource_full/dispatch'
require File.dirname(__FILE__) + '/resource_full/query'

require File.dirname(__FILE__) + '/resource_full/render/html'
require File.dirname(__FILE__) + '/resource_full/render/json'
require File.dirname(__FILE__) + '/resource_full/render/xml'

require File.dirname(__FILE__) + '/resource_full/render'
require File.dirname(__FILE__) + '/resource_full/retrieve'
require File.dirname(__FILE__) + '/resource_full/version'
require File.dirname(__FILE__) + '/resource_full/base'

# REST API
require 'resource_full/controllers/resources_controller'
require 'resource_full/controllers/routes_controller'
require 'resource_full/core_extensions/api'
require File.dirname(__FILE__) + '/resource_full/models/resourced_route'
require File.dirname(__FILE__) + '/resource_full/controllers/resources_controller'
require File.dirname(__FILE__) + '/resource_full/controllers/routes_controller'
2 changes: 1 addition & 1 deletion lib/resource_full/base.rb
Expand Up @@ -2,7 +2,7 @@ module ResourceFull
class ResourceNotFound < Exception; end

class Base < ActionController::Base
unless Rails.version == "2.3.2"
if ActionPack::VERSION::STRING < "2.3.0"
session :off, :if => lambda { |request| request.format.xml? || request.format.json? }
end

Expand Down
4 changes: 1 addition & 3 deletions lib/resource_full/core_extensions/from_json.rb
Expand Up @@ -9,7 +9,5 @@ def from_json(json)
end

class Hash
class << self
include ResourceFull::CoreExtensions::Hash
end
extend ResourceFull::CoreExtensions::Hash
end
13 changes: 13 additions & 0 deletions lib/resource_full/core_extensions/module.rb
@@ -0,0 +1,13 @@
module ResourceFull
module CoreExtensions
module Module
def simple_name
name.split("::").last
end
end
end
end

class Module
include ResourceFull::CoreExtensions::Module
end
11 changes: 10 additions & 1 deletion lib/resource_full/query.rb
Expand Up @@ -255,15 +255,18 @@ module ClassMethods
# Indicates that the resource should be queryable with the given parameters, which will be pulled from
# the params hash on an index or count call. Accepts the following options:
#
# * :scope => (scope) : Use a scope. The value of this parameter may be a symbol (named scope), lambda, or hash.
# Most other parameter options build scopes internally.
# * :fuzzy => true : Use a LIKE query instead of =.
# * :columns / :column => ... : Override the default column, or provide a list of columns to query for this value.
# * :from => :join_name : Indicate that this value should be queried by joining on another model. Should use
# a valid relationship from this controller's exposed model (e.g., :account if belongs_to :account is specified.)
# * :resource_identifier => true : Try to look up the resource controller for this value and honor its
# specified resource identifier. Useful for nesting relationships.
# * :allow_nils => true : Indicates that a nil value for a parameter should be taken to literally indicate
# * :allow_nil => true : Indicates that a nil value for a parameter should be taken to literally indicate
# that null values should be returned. This may be changed in the future to expect the literal string 'null'
# or some other reasonable standin.
# * :default => (value) : Default to this value if the parameter is not physically present in the request.
#
# Examples:
#
Expand All @@ -285,6 +288,12 @@ def queryable_with(*args)
end
end

def filter_with_scope(scope=nil, &block_scope)
raise ArgumentError, "must provide a scope name, standard scope definition, or block scope" unless (scope || block_scope)
# TODO These should not require the use of dummy parameter names.
queryable_with "__unused__", :default => true, :scope => scope || block_scope
end

# :nodoc:
def clear_queryable_params!
@queryable_params = []
Expand Down
16 changes: 9 additions & 7 deletions lib/resource_full/render.rb
Expand Up @@ -8,10 +8,18 @@ def self.included(controller)
controller.rescue_from Exception, :with => :handle_generic_exception_with_correct_response_format
end

# Override this method to provide custom error handling for the errors generated by
# your model object.
def http_error_code_for(errors)
if errors.any? { |message| message.include? CONFLICT_MESSAGE }
:conflict
else :unprocessable_entity end
end

private

CONFLICT_MESSAGE = if defined?(ActiveRecord::Errors)
if ([Rails::VERSION::MAJOR, Rails::VERSION::MINOR] <=> [2,1]) >= 0 # if the rails version is 2.1 or greater...Í
if ActiveRecord::VERSION::STRING >= '2.1.0' && defined?(I18n)
(I18n.translate 'activerecord.errors.messages')[:taken]
else
ActiveRecord::Errors.default_error_messages[:taken]
Expand All @@ -20,12 +28,6 @@ def self.included(controller)
"has already been taken"
end

def status_for(errors)
if errors.any? { |message| message.include? CONFLICT_MESSAGE }
:conflict
else :unprocessable_entity end
end

def handle_generic_exception_with_correct_response_format(exception)
if request.format.xml?
if defined?(ExceptionNotifiable) && defined?(ExceptionNotifier) && self.is_a?(ExceptionNotifiable) && !(consider_all_requests_local || local_request?)
Expand Down
43 changes: 34 additions & 9 deletions lib/resource_full/render/json.rb
Expand Up @@ -12,7 +12,12 @@ def show_json_options
end
def show_json
self.model_object = send("find_#{model_name}")
render :json => model_object.to_json(show_json_options)

json_representation = with_root_included_in_json do
model_object.to_json(show_json_options)
end

render :json => json_representation
rescue ActiveRecord::RecordNotFound => e
render :json => e.to_json, :status => :not_found
rescue => e
Expand All @@ -24,7 +29,12 @@ def index_json_options
end
def index_json
self.model_objects = send("find_all_#{model_name.pluralize}")
render :json => model_objects.to_json(index_json_options)

json_representation = with_root_included_in_json do
model_objects.to_json(index_json_options)
end

render :json => json_representation
end

def count_json
Expand All @@ -36,7 +46,11 @@ def new_json_options
{}
end
def new_json
render :json => send("new_#{model_name}").to_json(new_json_options)
json_representation = with_root_included_in_json do
send("new_#{model_name}").to_json(new_json_options)
end

render :json => json_representation
end

def create_json_options
Expand All @@ -45,12 +59,12 @@ def create_json_options
def create_json
self.model_object = transactional_create_model_object
if model_object.errors.empty?
render :json => model_object.to_json(create_json_options), :status => :created, :location => send("#{model_name}_url", model_object.id)
render :json => model_object.to_json(create_json_options), :status => :created, :location => send("#{model_name}_url", model_object.id, :format => :json)
else
json_data = model_object.attributes
json_data[:errors] = {:list => model_object.errors,
:full_messages => model_object.errors.full_messages}
render :json => {json_class_name(model_object) => json_data}.to_json, :status => status_for(model_object.errors)
render :json => {json_class_name(model_object) => json_data}.to_json, :status => http_error_code_for(model_object.errors)
end
rescue => e
handle_generic_error_in_json(e)
Expand All @@ -74,7 +88,7 @@ def update_json
json_data = model_object.attributes
json_data[:errors] = {:list => model_object.errors,
:full_messages => model_object.errors.full_messages}
render :json => {json_class_name(model_object) => json_data}.to_json, :status => status_for(model_object.errors)
render :json => {json_class_name(model_object) => json_data}.to_json, :status => http_error_code_for(model_object.errors)
end
rescue ActiveRecord::RecordNotFound => e
render :json => e.to_json, :status => :not_found
Expand All @@ -94,14 +108,25 @@ def destroy_json
end
rescue ActiveRecord::RecordNotFound => e
render :json => e.to_json, :status => :not_found
rescue ActiveRecord::RecordInvalid => e
render :json => e.to_json, :status => :unprocessable_entity
rescue => e
handle_generic_error_in_json(e)
end

private
def handle_generic_error_in_json(exception)
render :json => exception, :status => :unprocessable_entity
end

def handle_generic_error_in_json(exception)
render :json => exception, :status => :internal_server_error
end

def with_root_included_in_json
old_value = ActiveRecord::Base.include_root_in_json
ActiveRecord::Base.include_root_in_json = true
yield
ensure
ActiveRecord::Base.include_root_in_json = old_value
end
end
end
end
11 changes: 7 additions & 4 deletions lib/resource_full/render/xml.rb
Expand Up @@ -53,9 +53,9 @@ def create_xml_options
def create_xml
self.model_object = transactional_create_model_object
if model_object.errors.empty?
render :xml => model_object.to_xml({:root => model_name}.merge(create_xml_options)), :status => :created, :location => send("#{model_name}_url", model_object.id)
render :xml => model_object.to_xml({:root => model_name}.merge(create_xml_options)), :status => :created, :location => send("#{model_name}_url", model_object.id, :format => :xml)
else
render :xml => model_object.errors.to_xml, :status => status_for(model_object.errors)
render :xml => model_object.errors.to_xml, :status => http_error_code_for(model_object.errors)
end
rescue => e
handle_generic_error_in_xml(e)
Expand All @@ -76,7 +76,7 @@ def update_xml
if model_object.errors.empty?
render :xml => model_object.to_xml({:root => model_name}.merge(update_xml_options))
else
render :xml => model_object.errors.to_xml, :status => status_for(model_object.errors)
render :xml => model_object.errors.to_xml, :status => http_error_code_for(model_object.errors)
end
rescue ActiveRecord::RecordNotFound => e
render :xml => e.to_xml, :status => :not_found
Expand All @@ -93,13 +93,16 @@ def destroy_xml
end
rescue ActiveRecord::RecordNotFound => e
render :xml => e.to_xml, :status => :not_found
rescue ActiveRecord::RecordInvalid => e
render :xml => e.to_xml, :status => :unprocessable_entity
rescue => e
handle_generic_error_in_xml(e)
end

private
def handle_generic_error_in_xml(exception)
render :xml => exception, :status => :unprocessable_entity
logger.error exception
render :xml => exception, :status => :internal_server_error
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/resource_full/version.rb
@@ -1,8 +1,8 @@
module Actionresource #:nodoc:
module ResourceFull #:nodoc:
module VERSION #:nodoc:
MAJOR = 0
MINOR = 7
TINY = 5
TINY = 9

STRING = [MAJOR, MINOR, TINY].join('.')
end
Expand Down
16 changes: 12 additions & 4 deletions resource_full.gemspec
Expand Up @@ -3,17 +3,25 @@ require 'rake'
Gem::Specification.new do |s|
s.name = 'resource_full'
s.summary = 'A library for building controllers that correctly interact with ActiveResource.'
s.version = '0.7.6'
s.version = '0.7.9'
s.description = <<-EOS
ResourceFull provides a fully-compliant ActiveResource server implementation
built on ActionController. Additionally, it provides RESTful parameter
queryability, paging, sorting, separation of controller concerns, multiple
formats (HTML, XML, JSON), CRUD access permissions, and API metadata.
EOS

s.author = 'Brian Guthrie'
s.email = 'btguthrie@gmail.com'
s.homepage = 'http://github.com/bguthrie/resource_full'
s.has_rdoc = false
s.has_rdoc = true

s.files = FileList['lib/**/*.rb', '[A-Z]*', 'spec/**/*'].to_a
s.test_files = FileList['spec/resource_full/**/*.rb']

s.add_dependency 'action_controller', '>= 2.1.0'
s.add_dependency 'active_record', '>= 2.1.0'
s.add_dependency 'actionpack', '>= 2.1.0'
s.add_dependency 'activerecord', '>= 2.1.0'

s.add_development_dependency 'rspec'
s.add_development_dependency 'mocha'
end
3 changes: 1 addition & 2 deletions spec/resource_full/dispatch_spec.rb
Expand Up @@ -97,7 +97,6 @@

it "responds successfully to supported actions" do
controller.class.responds_to :xml, :only => :read
controller.stubs(:index)
get :index, :format => "xml"
response.should be_success
end
Expand Down Expand Up @@ -201,7 +200,7 @@ def find_all_resource_full_mocks; "another list of mocks"; end
it "should render the count" do
ResourceFullMock.stubs(:count).returns(12)
get :count, :format => 'html'
response.body.should == ""
response.body.should == ["", []]
end
end

Expand Down

0 comments on commit 57646e2

Please sign in to comment.