acts_as_api 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. data/.gitignore +10 -0
  2. data/Gemfile +12 -0
  3. data/History.txt +8 -0
  4. data/README.rdoc +29 -225
  5. data/Rakefile +20 -28
  6. data/acts_as_api.gemspec +31 -0
  7. data/examples/introduction/docco.css +186 -0
  8. data/examples/introduction/index.html +340 -0
  9. data/examples/introduction/index.rb +132 -0
  10. data/examples/introduction/layout.mustache +64 -0
  11. data/lib/acts_as_api.rb +11 -25
  12. data/lib/acts_as_api/api_template.rb +14 -0
  13. data/lib/acts_as_api/base.rb +61 -56
  14. data/lib/acts_as_api/config.rb +34 -0
  15. data/lib/acts_as_api/rails_renderer.rb +15 -0
  16. data/lib/acts_as_api/rendering.rb +11 -8
  17. data/lib/acts_as_api/version.rb +4 -0
  18. data/spec/controllers/respond_with_users_controller_spec.rb +5 -0
  19. data/spec/controllers/users_controller_spec.rb +161 -0
  20. data/spec/models/base_spec.rb +437 -0
  21. data/spec/rails_app/.gitignore +4 -0
  22. data/spec/rails_app/Rakefile +7 -0
  23. data/spec/rails_app/app/controllers/application_controller.rb +3 -0
  24. data/spec/rails_app/app/controllers/respond_with_users_controller.rb +15 -0
  25. data/spec/rails_app/app/controllers/users_controller.rb +21 -0
  26. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  27. data/spec/rails_app/app/models/task.rb +3 -0
  28. data/spec/rails_app/app/models/untouched.rb +2 -0
  29. data/spec/rails_app/app/models/user.rb +69 -0
  30. data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
  31. data/spec/rails_app/config.ru +4 -0
  32. data/spec/rails_app/config/application.rb +42 -0
  33. data/spec/rails_app/config/boot.rb +6 -0
  34. data/spec/rails_app/config/database.yml +23 -0
  35. data/spec/rails_app/config/environment.rb +5 -0
  36. data/spec/rails_app/config/environments/development.rb +26 -0
  37. data/spec/rails_app/config/environments/production.rb +49 -0
  38. data/spec/rails_app/config/environments/test.rb +35 -0
  39. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  40. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  41. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  42. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  43. data/spec/rails_app/config/initializers/session_store.rb +8 -0
  44. data/spec/rails_app/config/locales/en.yml +5 -0
  45. data/spec/rails_app/config/routes.rb +7 -0
  46. data/spec/rails_app/db/migrate/20110214201640_create_tables.rb +35 -0
  47. data/spec/rails_app/db/schema.rb +34 -0
  48. data/spec/rails_app/db/seeds.rb +7 -0
  49. data/spec/rails_app/lib/tasks/.gitkeep +0 -0
  50. data/spec/rails_app/public/404.html +26 -0
  51. data/spec/rails_app/public/422.html +26 -0
  52. data/spec/rails_app/public/500.html +26 -0
  53. data/spec/rails_app/public/favicon.ico +0 -0
  54. data/spec/rails_app/public/images/rails.png +0 -0
  55. data/spec/rails_app/public/index.html +239 -0
  56. data/spec/rails_app/public/javascripts/application.js +2 -0
  57. data/spec/rails_app/public/javascripts/controls.js +965 -0
  58. data/spec/rails_app/public/javascripts/dragdrop.js +974 -0
  59. data/spec/rails_app/public/javascripts/effects.js +1123 -0
  60. data/spec/rails_app/public/javascripts/prototype.js +6001 -0
  61. data/spec/rails_app/public/javascripts/rails.js +191 -0
  62. data/spec/rails_app/public/robots.txt +5 -0
  63. data/spec/rails_app/public/stylesheets/.gitkeep +0 -0
  64. data/spec/rails_app/script/rails +6 -0
  65. data/spec/spec_helper.rb +12 -13
  66. data/spec/support/api_test_helpers.rb +23 -0
  67. metadata +137 -35
  68. data/Manifest.txt +0 -15
  69. data/script/console +0 -10
  70. data/script/destroy +0 -14
  71. data/script/generate +0 -14
  72. data/spec/acts_as_api_spec.rb +0 -87
  73. data/tasks/rspec.rake +0 -21
@@ -0,0 +1,64 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
5
+ <title>acts_as_api</title>
6
+ <link rel="stylesheet" href="./docco.css">
7
+ <link href="http://fonts.googleapis.com/css?family=Copse:regular" rel="stylesheet" type="text/css" >
8
+ <style>
9
+ h1, h2, h3, h4, h5 {
10
+ font-family: 'Copse', serif;
11
+ font-style: normal;
12
+ font-weight: 700;
13
+ text-shadow: none;
14
+ text-decoration: none;
15
+ text-transform: none;
16
+ letter-spacing: 0em;
17
+ word-spacing: 0em;
18
+ line-height: 1.2;
19
+ }
20
+ </style>
21
+ </head>
22
+ <body>
23
+ <div id='container'>
24
+ <div id="background"></div>
25
+ {{#sources?}}
26
+ <div id="jump_to">
27
+ Jump To &hellip;
28
+ <div id="jump_wrapper">
29
+ <div id="jump_page">
30
+ {{#sources}}
31
+ <a class="source" href="{{ url }}">{{ basename }}</a>
32
+ {{/sources}}
33
+ </div>
34
+ </div>
35
+ </div>
36
+ {{/sources?}}
37
+ <table cellspacing=0 cellpadding=0>
38
+ <thead>
39
+ <tr>
40
+ <th class=docs>
41
+ <h1>acts_as_api</h1>
42
+ <p>Makes creating XML/JSON responses in Rails 3 easy and fun.</p>
43
+ </th>
44
+ <th class=code></th>
45
+ </tr>
46
+ </thead>
47
+ <tbody>
48
+ {{#sections}}
49
+ <tr id='section-{{ section_id }}'>
50
+ <td class=docs>
51
+ <div class="pilwrap">
52
+ <a class="pilcrow" href="#section-{{ section_id }}">&#182;</a>
53
+ </div>
54
+ {{{ docs }}}
55
+ </td>
56
+ <td class=code>
57
+ <div class='highlight'><pre>{{{ code }}}</pre></div>
58
+ </td>
59
+ </tr>
60
+ {{/sections}}
61
+ </table>
62
+ </div>
63
+ <a href="https://github.com/fabrik42/acts_as_api"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://assets1.github.com/img/7afbc8b248c68eb468279e8c17986ad46549fb71?repo=&url=http%3A%2F%2Fs3.amazonaws.com%2Fgithub%2Fribbons%2Fforkme_right_darkblue_121621.png&path=" alt="Fork me on GitHub"></a>
64
+ </body>
@@ -1,11 +1,12 @@
1
- require "rubygems"
1
+ require 'set'
2
+ require 'active_model'
3
+ require 'active_support/core_ext/class'
2
4
 
3
5
  $:.unshift(File.dirname(__FILE__)) unless
4
6
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
5
-
6
- require "acts_as_api/base"
7
- require "acts_as_api/rendering"
8
- require "acts_as_api/array"
7
+
8
+ require "acts_as_api/array"
9
+ require "acts_as_api/rails_renderer"
9
10
 
10
11
  # acts_as_api is a gem that aims to make the construction of JSON and XML
11
12
  # responses in rails 3 easy and fun.
@@ -16,26 +17,10 @@ require "acts_as_api/array"
16
17
  # acts_as_api uses the default serializers of your rails app and doesn't
17
18
  # force you into more dependencies.
18
19
  module ActsAsApi
19
- VERSION = '0.2.2'
20
-
21
- # The accepted response formats
22
- # Default is +[:xml, :json]+
23
- ACCEPTED_API_FORMATS = [:xml, :json]
24
-
25
- # Holds references to formats that need
26
- # to get added an additional root node
27
- # with the name of the model.
28
- ADD_ROOT_NODE_FOR = [:json]
29
-
30
- # Holds formats that should be dasherized
31
- DASHERIZE_FOR = [:xml]
32
-
33
- # The default name of a root node of a response
34
- # if no root paramter is passed in render_for_api
35
- # and the gem is not able to determine a root name
36
- # automatically
37
- DEFAULT_ROOT = :record
38
-
20
+ autoload :Config, "acts_as_api/config"
21
+ autoload :ApiTemplate, "acts_as_api/api_template"
22
+ autoload :Base, "acts_as_api/base"
23
+ autoload :Rendering, "acts_as_api/rendering"
39
24
  end
40
25
 
41
26
  # Attach ourselves to active record
@@ -46,4 +31,5 @@ end
46
31
  # Attach ourselves to the action controller of rails
47
32
  if defined?(ActionController::Base)
48
33
  ActionController::Base.send :include, ActsAsApi::Rendering
34
+ ActsAsApi::RailsRenderer.setup
49
35
  end
@@ -0,0 +1,14 @@
1
+ module ActsAsApi
2
+ #
3
+ class ApiTemplate < Hash
4
+
5
+ def add(val, options = {})
6
+ self[(options[:as] || val).to_sym] = val
7
+ end
8
+
9
+ def remove(key)
10
+ self.delete(key)
11
+ end
12
+
13
+ end
14
+ end
@@ -16,6 +16,10 @@ module ActsAsApi
16
16
  extend ActsAsApi::Base::ClassMethods
17
17
  end
18
18
 
19
+ if block_given?
20
+ yield ActsAsApi::Config
21
+ end
22
+
19
23
  end
20
24
 
21
25
  module ClassMethods
@@ -28,12 +32,28 @@ module ActsAsApi
28
32
  # *Note*: There is only whitelisting for api accessible attributes.
29
33
  # So once the model acts as api, you have to determine all attributes here that should
30
34
  # be contained in the api responses.
31
- def api_accessible(api_templates)
35
+ def api_accessible_deprecated(api_templates)
32
36
  api_templates.each do |api_template, attributes|
33
37
  write_inheritable_attribute("api_accessible_#{api_template}".to_sym, Set.new(attributes) + (api_accessible_attributes(api_template) || []))
34
38
  end
35
39
  end
36
40
 
41
+ def api_accessible(api_template, options = {}, &block)
42
+
43
+ # return api_accessible_deprecated([api_template]) if api_template.is_a? Hash
44
+
45
+
46
+ attributes = api_accessible_attributes(api_template) || ApiTemplate.new
47
+
48
+ attributes.merge!(api_accessible_attributes(options[:extend])) if options[:extend]
49
+
50
+ if block_given?
51
+ yield attributes
52
+ end
53
+
54
+ write_inheritable_attribute("api_accessible_#{api_template}".to_sym, attributes)
55
+ end
56
+
37
57
  # Returns an array of all the attributes that have been made accessible to the api response.
38
58
  def api_accessible_attributes(api_template)
39
59
  read_inheritable_attribute("api_accessible_#{api_template}".to_sym)
@@ -52,82 +72,67 @@ module ActsAsApi
52
72
 
53
73
  return api_output if api_attributes.nil?
54
74
 
55
- api_attributes.each do |attribute|
56
-
57
- case attribute
58
- when Symbol
59
-
60
- if self.respond_to?(attribute)
61
- out = send attribute
62
-
63
- if out.respond_to?(:as_api_response)
64
- out = out.send(:as_api_response, api_template)
65
- end
66
-
67
- api_output[attribute] = out
68
-
69
- end
70
-
71
- when String
72
- # go up the call chain
73
- out = self
74
- attribute.split(".").each do |method|
75
- out = out.send(method.to_sym)
76
- end
77
- api_output[attribute] = out
78
-
79
- when Hash
80
-
81
- queue = []
82
- queue << { :parent => api_output, :item => attribute}
75
+ queue = []
76
+ queue << { :parent => api_output, :item => api_attributes}
83
77
 
84
- until queue.empty? do
78
+ until queue.empty? do
85
79
 
86
- leaf = queue.pop
80
+ leaf = queue.pop
87
81
 
88
- leaf[:item].each do |k,v|
82
+ leaf[:item].each do |k,v|
89
83
 
90
- case v
91
- when Symbol
84
+ case v
85
+ when Symbol
92
86
 
93
- if self.respond_to?(v)
94
- out = send v
95
-
96
- if out.respond_to?(:as_api_response)
97
- out = out.send(:as_api_response, api_template)
98
- end
99
-
100
- leaf[:parent][k] = out
87
+ if self.respond_to?(v)
88
+ out = send v
101
89
 
90
+ if out.respond_to?(:as_api_response)
91
+ out = out.send(:as_api_response, api_template)
102
92
  end
103
93
 
104
- when String
105
- # go up the call chain
106
- out = self
107
- v.split(".").each do |method|
108
- out = out.send(method.to_sym)
109
- end
110
94
  leaf[:parent][k] = out
111
95
 
112
- when Hash
113
- leaf[:parent][k] ||= {}
114
- queue << { :parent => leaf[:parent][k], :item => v}
115
96
  end
116
97
 
98
+ when Proc
99
+
100
+ out = v.call(self)
101
+
102
+ if out.respond_to?(:as_api_response)
103
+ out = out.send(:as_api_response, api_template)
104
+ end
105
+
106
+ leaf[:parent][k] = out
107
+
108
+ when String
109
+ # go up the call chain
110
+ out = self
111
+ v.split(".").each do |method|
112
+ out = out.send(method.to_sym)
113
+ end
114
+
115
+ if out.respond_to?(:as_api_response)
116
+ out = out.send(:as_api_response, api_template)
117
+ end
118
+
119
+ leaf[:parent][k] = out
120
+
121
+ when Hash
122
+ leaf[:parent][k] ||= {}
123
+ queue << { :parent => leaf[:parent][k], :item => v}
117
124
  end
118
125
 
119
126
  end
120
127
 
121
128
  end
122
129
 
123
- end
130
+ api_output
124
131
 
125
- api_output
132
+ end
126
133
 
127
134
  end
128
135
 
129
136
  end
130
-
131
-
137
+
132
138
  end
133
- end
@@ -0,0 +1,34 @@
1
+ module ActsAsApi
2
+
3
+ module Config
4
+
5
+ class << self
6
+
7
+ # The accepted response formats
8
+ # Default is <tt>[:xml, :json]</tt>
9
+ attr_accessor_with_default :accepted_api_formats, [:xml, :json] # :nodoc:
10
+
11
+ # Holds formats that should be dasherized
12
+ # Default is <tt>[:xml]</tt>
13
+ attr_accessor_with_default :dasherize_for, [:xml]
14
+
15
+ # Holds references to formats that need
16
+ # to get added an additional root node
17
+ # with the name of the model.
18
+ attr_accessor_with_default :add_root_node_for, [:json]
19
+
20
+ # The default name of a root node of a response
21
+ # if no root paramter is passed in render_for_api
22
+ # and the gem is not able to determine a root name
23
+ # automatically
24
+ attr_accessor_with_default :default_root, :record
25
+
26
+ attr_accessor_with_default :allow_jsonp_callback, false
27
+
28
+ attr_accessor_with_default :add_http_status_to_jsonp_response, true
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,15 @@
1
+ module ActsAsApi
2
+ # Contains rails specific renderers used by acts_as_api
3
+ module RailsRenderer
4
+
5
+ def self.setup
6
+ ActionController.add_renderer :acts_as_api_jsonp do |json, options|
7
+ json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str)
8
+ json = "#{options[:callback]}(#{json}, #{response.status})" unless options[:callback].blank?
9
+ self.content_type ||= Mime::JSON
10
+ self.response_body = json
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -13,7 +13,7 @@ module ActsAsApi
13
13
  # extract the api format and model
14
14
  api_format_options = {}
15
15
 
16
- ActsAsApi::ACCEPTED_API_FORMATS.each do |item|
16
+ ActsAsApi::Config.accepted_api_formats.each do |item|
17
17
  if render_options.has_key?(item)
18
18
  api_format_options[item] = render_options[item]
19
19
  render_options.delete item
@@ -28,13 +28,12 @@ module ActsAsApi
28
28
  # set the params to render
29
29
  output_params = render_options
30
30
 
31
-
32
31
  api_root_name = nil
33
32
 
34
33
  if !output_params[:root].nil?
35
-
36
34
  api_root_name = output_params[:root].to_s
37
-
35
+ elsif api_model.respond_to?(:collection_name)
36
+ api_root_name = api_model.collection_name
38
37
  elsif api_model.respond_to?(:model_name)
39
38
  api_root_name = api_model.model_name
40
39
  elsif api_model.is_a?(Array) && !api_model.empty? && api_model.first.class.respond_to?(:model_name)
@@ -42,7 +41,7 @@ module ActsAsApi
42
41
  elsif api_model.class.respond_to?(:model_name)
43
42
  api_root_name = api_model.class.model_name
44
43
  else
45
- api_root_name = ActsAsApi::DEFAULT_ROOT.to_s
44
+ api_root_name = ActsAsApi::Config.default_root.to_s
46
45
  end
47
46
 
48
47
  api_root_name = api_root_name.underscore.tr('/', '_')
@@ -51,7 +50,7 @@ module ActsAsApi
51
50
  api_root_name = api_root_name.pluralize
52
51
  end
53
52
 
54
- api_root_name = api_root_name.dasherize if ActsAsApi::DASHERIZE_FOR.include? api_format.to_sym
53
+ api_root_name = api_root_name.dasherize if ActsAsApi::Config.dasherize_for.include? api_format.to_sym
55
54
 
56
55
  output_params[:root] = api_root_name
57
56
 
@@ -60,17 +59,21 @@ module ActsAsApi
60
59
 
61
60
  api_response = api_model.as_api_response(api_template)
62
61
 
63
- if meta_hash or ActsAsApi::ADD_ROOT_NODE_FOR.include? api_format
62
+ if meta_hash or ActsAsApi::Config.add_root_node_for.include? api_format
64
63
  api_response = { api_root_name.to_sym => api_response}
65
64
  end
66
65
 
67
66
  api_response = meta_hash.merge api_response if meta_hash
67
+
68
+ if ActsAsApi::Config.allow_jsonp_callback && params[:callback]
69
+ output_params[:callback] = params[:callback]
70
+ api_format = :acts_as_api_jsonp if ActsAsApi::Config.add_http_status_to_jsonp_response
71
+ end
68
72
 
69
73
  # create the Hash as response
70
74
  output_params[api_format] = api_response
71
75
 
72
76
  render output_params
73
-
74
77
  end
75
78
 
76
79
  end
@@ -0,0 +1,4 @@
1
+ module ActsAsApi
2
+
3
+ VERSION = "0.3.0"
4
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe RespondWithUsersController do
4
+ pending "we need to fix the root node for json first"
5
+ end
@@ -0,0 +1,161 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe UsersController do
4
+
5
+ include ApiTestHelpers
6
+
7
+ before(:each) do
8
+ @luke = User.create({ :first_name => 'Luke', :last_name => 'Skywalker', :age => 25, :active => true })
9
+ @han = User.create({ :first_name => 'Han', :last_name => 'Solo', :age => 35, :active => true })
10
+ @leia = User.create({ :first_name => 'Princess', :last_name => 'Leia', :age => 25, :active => false })
11
+ end
12
+
13
+ after(:each) do
14
+ User.delete_all
15
+ end
16
+
17
+ describe 'xml responses' do
18
+
19
+ describe 'get all users' do
20
+
21
+ before(:each) do
22
+ get :index, :format => 'xml', :api_template => :name_only
23
+ end
24
+
25
+ it "should have a root node named users" do
26
+ response_body.should have_selector("users")
27
+ end
28
+
29
+ it "should contain all users" do
30
+ response_body.should have_selector("users > user") do |users|
31
+ users.size.should eql(3)
32
+ end
33
+ end
34
+
35
+ it "should contain the specified attributes" do
36
+ response_body.should have_selector("users > user > first-name")
37
+ response_body.should have_selector("users > user > last-name")
38
+ end
39
+
40
+ end
41
+
42
+ describe 'get a single user' do
43
+
44
+ before(:each) do
45
+ get :show, :format => 'xml', :api_template => :name_only, :id => @luke.id
46
+ end
47
+
48
+ it "should have a root node named user" do
49
+ response_body.should have_selector("user")
50
+ end
51
+
52
+ it "should contain the specified attributes" do
53
+ response_body.should have_selector("user > first-name")
54
+ response_body.should have_selector("user > last-name")
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+
62
+ describe 'json responses' do
63
+
64
+ describe 'get all users' do
65
+
66
+ before(:each) do
67
+ get :index, :format => 'json', :api_template => :name_only
68
+ end
69
+
70
+ it "should have a root node named users" do
71
+ response_body_json.should have_key("users")
72
+ end
73
+
74
+ it "should contain all users" do
75
+ response_body_json["users"].should be_a(Array)
76
+ end
77
+
78
+ it "should contain the specified attributes" do
79
+ response_body_json["users"].first.should have_key("first_name")
80
+ response_body_json["users"].first.should have_key("last_name")
81
+ end
82
+
83
+ it "should contain the specified values" do
84
+ response_body_json["users"].first["first_name"].should eql("Luke")
85
+ response_body_json["users"].first["last_name"].should eql("Skywalker")
86
+ end
87
+
88
+ end
89
+
90
+ describe 'get a single user' do
91
+
92
+ before(:each) do
93
+ get :show, :format => 'json', :api_template => :name_only, :id => @luke.id
94
+ end
95
+
96
+ it "should have a root node named user" do
97
+ response_body_json.should have_key("user")
98
+ end
99
+
100
+ it "should contain the specified attributes" do
101
+ response_body_json["user"].should have_key("first_name")
102
+ response_body_json["user"].should have_key("last_name")
103
+ end
104
+
105
+ it "should contain the specified values" do
106
+ response_body_json["user"]["first_name"].should eql("Luke")
107
+ response_body_json["user"]["last_name"].should eql("Skywalker")
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+
114
+
115
+ describe 'jsonp responses with callback' do
116
+
117
+ it "should be disabled by default" do
118
+ @callback = "mycallback"
119
+ get :index, :format => 'json', :api_template => :name_only, :callback => @callback
120
+ response_body_jsonp(@callback).should be_nil
121
+ end
122
+
123
+ describe "enabled jsonp callbacks" do
124
+
125
+ before(:each) do
126
+ @callback = "mycallback"
127
+
128
+ User.acts_as_api do |config|
129
+ config.allow_jsonp_callback = true
130
+ end
131
+ end
132
+
133
+ describe 'get all users' do
134
+
135
+ before(:each) do
136
+ get :index, :format => 'json', :api_template => :name_only, :callback => @callback
137
+ end
138
+
139
+ it "should wrap the response in the callback" do
140
+ response_body_jsonp(@callback).should_not be_nil
141
+ end
142
+
143
+ end
144
+
145
+ describe 'get a single user' do
146
+
147
+ before(:each) do
148
+ get :show, :format => 'json', :api_template => :name_only, :id => @luke.id, :callback => @callback
149
+ end
150
+
151
+ it "should wrap the response in the callback" do
152
+ response_body_jsonp(@callback).should_not be_nil
153
+ end
154
+
155
+ end
156
+
157
+ end
158
+ end
159
+
160
+
161
+ end