toast 0.4.1 → 0.5.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.
@@ -4,15 +4,15 @@ class ToastController < ApplicationController
4
4
 
5
5
  begin
6
6
 
7
- @resource = Toast::Resource.build( params, request )
7
+ @resource = Toast::Resource.build( params, request )
8
+
9
+ render @resource.apply(request.method, request.body.read)
8
10
 
9
- render @resource.apply(request.method, request.body.read)
10
-
11
11
  rescue Toast::ResourceNotFound => e
12
12
  return head(:not_found)
13
13
 
14
14
  rescue Toast::PayloadInvalid => e
15
- return render :text => e.message, :status => :forbidden
15
+ return render :text => e.message, :status => :forbidden
16
16
 
17
17
  rescue Toast::PayloadFormatError => e
18
18
  return head(:bad_request)
@@ -8,6 +8,7 @@ Rails.application.routes.draw do
8
8
  match("#{model.toast_config.namespace}/#{resource_name}(/:id(/:subresource))" => 'toast#catch_all',
9
9
  :constraints => { :id => /\d+/ },
10
10
  :resource => resource_name,
11
+ :as => resource_name,
11
12
  :defaults => { :format => 'json' })
12
13
 
13
14
  match("#{model.toast_config.namespace}/#{resource_name}/:subresource" => 'toast#catch_all',
@@ -25,9 +25,12 @@ module Toast
25
25
 
26
26
  # add instance methods
27
27
  self.class_eval do
28
- # Return toast's standard uri for a record
29
- def uri
30
- "#{self.class.uri_base}/#{self.class.to_s.pluralize.underscore}/#{self.id}"
28
+ # Return the path segment of the URI of this record
29
+ def uri_fullpath
30
+ "/" +
31
+ (self.class.toast_config.namespace ? self.class.toast_config.namespace+"/" : "") +
32
+ self.class.to_s.pluralize.underscore + "/" +
33
+ self.id.to_s
31
34
  end
32
35
 
33
36
  # Returns a Hash with all exposed attributes
@@ -45,22 +48,13 @@ module Toast
45
48
  acc
46
49
  end
47
50
 
48
- # association URIs
49
- exposed_assoc =
50
- options[:in_collection] ? self.class.toast_config.in_collection.exposed_associations :
51
- self.class.toast_config.exposed_associations
52
-
53
- exposed_assoc.each do |assoc|
54
- out[assoc] = self.uri + "/" + assoc
55
- end
56
-
57
- out["uri"] = self.uri if options[:with_uri]
58
-
59
51
  out
60
52
  end
61
53
  end
62
54
  end
63
55
 
56
+ alias acts_as_resource resourceful_model
57
+
64
58
  # defaults for non resourceful-models
65
59
  def is_resourceful_model?
66
60
  false
@@ -24,12 +24,16 @@ module Toast
24
24
 
25
25
  if result.is_a? Array
26
26
  {
27
- :json => result.map{|r| r.exposed_attributes(:in_collection => true)},
27
+ :json => result.map{|r|
28
+ r.exposed_attributes(:in_collection => true).
29
+ merge( uri_fields(r, true) )
30
+ },
28
31
  :status => :ok
29
32
  }
30
33
  else
31
34
  {
32
- :json => result.exposed_attributes(:in_collection => true),
35
+ :json => result.exposed_attributes(:in_collection => true).
36
+ merge( uri_fields(result) ),
33
37
  :status => :ok
34
38
  }
35
39
  end
@@ -37,48 +41,11 @@ module Toast
37
41
  end
38
42
 
39
43
  def put payload
40
- # only for has_one/belongs_to assocs
41
- raise MethodNotAllowed if @is_collection
42
-
43
-
44
- begin
45
- payload = ActiveSupport::JSON.decode(payload)
46
- rescue
47
- raise PayloadFormatError
48
- end
49
-
50
-
51
- unless payload.is_a? Hash
52
- raise PayloadFormatError
53
- end
54
-
55
- # update see record
56
- if self.media_type != @associate_model.toast_config.media_type
57
- raise UnsupportedMediaType
58
- end
59
-
60
- # silently ignore all exposed readable, but not writable fields
61
- (@associate_model.toast_config.readables - @associate_model.toast_config.writables).each do |rof|
62
- payload.delete(rof)
63
- end
64
-
65
- record = @record.send(@assoc)
66
-
67
- # set the virtual attributes
68
- (payload.keys.to_set - record.attribute_names.to_set).each do |vattr|
69
- record.send("#{vattr}=", payload.delete(vattr))
70
- end
71
-
72
- # mass-update for the rest
73
- record.update_attributes payload
74
- {
75
- :json => record.exposed_attributes,
76
- :status => :ok,
77
- :location => record.uri
78
- }
44
+ raise MethodNotAllowed
79
45
  end
80
46
 
81
47
  def post payload
48
+ raise MethodNotAllowed unless @model.toast_config.writables.include? @assoc
82
49
 
83
50
  if self.media_type != @associate_model.toast_config.media_type
84
51
  raise UnsupportedMediaType
@@ -105,8 +72,8 @@ module Toast
105
72
  record = @record.send(@assoc).create! payload
106
73
 
107
74
  {
108
- :json => record.exposed_attributes,
109
- :location => record.uri,
75
+ :json => record.exposed_attributes.merge( uri_fields(record) ),
76
+ :location => self.base_uri + record.uri_fullpath,
110
77
  :status => :created
111
78
  }
112
79
 
@@ -7,12 +7,13 @@ module Toast
7
7
 
8
8
  def initialize model
9
9
  @model = model
10
- @readables = ["uri"]
10
+ @readables = []
11
11
  @writables = []
12
12
  @collections = []
13
13
  @singles = []
14
14
  @media_type = "application/json"
15
- @disallow_methods = []
15
+ @deletable = false
16
+ @postable = false
16
17
  @pass_params_to = []
17
18
  @in_collection = ConfigDSL::InCollection.new model, self
18
19
  end
@@ -28,43 +29,40 @@ module Toast
28
29
  end
29
30
 
30
31
  def readables= arg
31
- #if arg.first == :all
32
- # @readables.push @model.attribute_names - ConfigDSL.sanitize(args.last[:except], "readables")
33
- #else
34
- @readables.push *ConfigDSL.sanitize(arg,"readables")
35
- #end
32
+ @readables.push *ConfigDSL.sanitize(arg,"readables")
36
33
  end
37
34
 
38
35
  # args: Array or :all, :except => Array
39
36
  def readables *arg
40
37
  return(@readables) if arg.empty?
41
- self.readables = arg
38
+ self.readables = arg
42
39
  end
43
40
 
44
41
  def writables= arg
45
- #if arg.first == :all
46
- # @writables.push @model.attribute_names - ConfigDSL.sanitize(args.last[:except], "writables")
47
- #else
48
- # white list writables (protect the rest from mass-assignment)
49
42
  @model.attr_accessible *arg
50
- @writables.push *ConfigDSL.sanitize(arg,"writables")
51
- #end
43
+ @writables.push *ConfigDSL.sanitize(arg,"writables")
52
44
  end
53
45
 
54
46
  # args: Array or :all, :except => Array
55
47
  def writables *arg
56
48
  return(@writables) if arg.empty?
57
- self.writables = arg
49
+ self.writables = arg
50
+ end
51
+
52
+ def deletable
53
+ @deletable = true
58
54
  end
59
55
 
60
-
61
- def disallow_methods= arg
62
- @disallow_methods.push *ConfigDSL.sanitize(arg,"disallow methods")
56
+ def deletable?
57
+ @deletable
63
58
  end
64
59
 
65
- def disallow_methods *arg
66
- return(@disallow_methods) if arg.empty?
67
- self.disallow_methods = arg
60
+ def postable
61
+ @postable = true
62
+ end
63
+
64
+ def postable?
65
+ @postable
68
66
  end
69
67
 
70
68
  def pass_params_to= arg
@@ -73,9 +71,9 @@ module Toast
73
71
 
74
72
  def pass_params_to *arg
75
73
  return(@pass_params_to) if arg.empty?
76
- self.pass_params_to = arg
74
+ self.pass_params_to = arg
77
75
  end
78
-
76
+
79
77
  def collections= collections=[]
80
78
  @collections = ConfigDSL.sanitize(collections, "collections")
81
79
  end
@@ -111,15 +109,14 @@ module Toast
111
109
 
112
110
  def initialize model, base_config
113
111
  @model = model
114
- @readables = base_config.readables # must assign a reference
115
- @writables = base_config.writables # must assign a reference
116
- @disallow_methods = []
112
+ @readables = base_config.readables # must assign a reference
113
+ @writables = base_config.writables # must assign a reference
117
114
  @media_type = "application/json"
118
115
  end
119
116
 
120
117
  def readables= readables
121
118
  @writables = [] # forget inherited writables
122
- @readables = ConfigDSL.sanitize(readables,"readables") << "uri"
119
+ @readables = ConfigDSL.sanitize(readables,"readables")
123
120
  end
124
121
 
125
122
  def readables *arg
@@ -146,16 +143,6 @@ module Toast
146
143
  assocs = @model.reflect_on_all_associations.map{|a| a.name.to_s}
147
144
  (@readables + @writables).uniq.select{|f| assocs.include?(f)}
148
145
  end
149
-
150
- def disallow_methods= arg
151
- @disallow_methods.push *ConfigDSL.sanitize(arg,"disallow methods")
152
- end
153
-
154
- def disallow_methods *arg
155
- return(@disallow_methods) if arg.empty?
156
- self.disallow_methods = arg
157
- end
158
-
159
146
  end
160
147
 
161
148
 
@@ -24,6 +24,23 @@ module Toast
24
24
  # raised when DB is not setup yet. (rake db:schema:load)
25
25
  end
26
26
 
27
+ # Monkey patch the request class for Rails 3.0, Rack 1.2
28
+ # Backport from Rack 1.3
29
+ if Rack.release == "1.2"
30
+ class Rack::Request
31
+ def base_url
32
+ url = scheme + "://"
33
+ url << host
34
+
35
+ if scheme == "https" && port != 443 ||
36
+ scheme == "http" && port != 80
37
+ url << ":#{port}"
38
+ end
39
+
40
+ url
41
+ end
42
+ end
43
+ end
27
44
  end
28
45
  end
29
46
  end
@@ -31,12 +31,12 @@ module Toast
31
31
  unless payload.is_a? Hash
32
32
  raise PayloadFormatError
33
33
  end
34
-
35
- # silently ignore all exposed readable, but not writable fields
36
- (@model.toast_config.readables - @model.toast_config.writables).each do |rof|
34
+
35
+ # ignore all exposed readable, but not writable fields
36
+ (@model.toast_config.readables - @model.toast_config.writables + ["uri"]).each do |rof|
37
37
  payload.delete(rof)
38
38
  end
39
-
39
+
40
40
  # set the virtual attributes
41
41
  (payload.keys.to_set - @record.attribute_names.to_set).each do |vattr|
42
42
  @record.send("#{vattr}=", payload.delete(vattr))
@@ -45,9 +45,9 @@ module Toast
45
45
  # mass-update for the rest
46
46
  @record.update_attributes payload
47
47
  {
48
- :json => @record.exposed_attributes,
48
+ :json => @record.exposed_attributes.merge( uri_fields(@record) ),
49
49
  :status => :ok,
50
- :location => @record.uri
50
+ :location => self.base_uri + @record.uri_fullpath
51
51
  }
52
52
  end
53
53
 
@@ -58,9 +58,9 @@ module Toast
58
58
  :template => "resources/#{model.to_s.underscore}",
59
59
  :locals => { model.to_s.underscore.to_sym => @record } # full record, view should filter
60
60
  }
61
- when "json"
61
+ when "json"
62
62
  {
63
- :json => @record.exposed_attributes,
63
+ :json => @record.exposed_attributes.merge( uri_fields(@record) ),
64
64
  :status => :ok
65
65
  }
66
66
  else
@@ -69,11 +69,14 @@ module Toast
69
69
  end
70
70
 
71
71
  def delete
72
+ raise MethodNotAllowed unless @model.toast_config.deletable?
73
+
72
74
  @record.destroy
73
75
  {
74
76
  :nothing => true,
75
77
  :status => :ok
76
78
  }
77
79
  end
80
+
78
81
  end
79
82
  end
@@ -10,40 +10,37 @@ module Toast
10
10
  # Record, RootCollection, Association, Single
11
11
  class Resource
12
12
 
13
- attr_accessor :media_type
13
+ attr_accessor :media_type, :base_uri
14
14
 
15
15
  def initialize
16
16
  raise 'ToastResource#new: use #build to create an instance'
17
17
  end
18
18
 
19
19
  def self.build params, request
20
-
21
20
  resource_name = params[:resource]
22
21
  id = params[:id]
23
22
  subresource_name = params[:subresource]
24
23
  format = params[:format]
25
24
 
26
25
  begin
27
-
26
+
28
27
  model = get_class_by_resource_name resource_name
29
-
30
- # base is complete URL until the resource name
31
- model.uri_base = request.url.match(/(.*)\/#{resource_name}(?:\..+|\/|\z)/)[1]
32
28
 
33
29
  # decide which sub type
34
30
  rsc = if id.nil? and model.toast_config.singles.include?(subresource_name)
35
- Toast::Single.new(model, subresource_name, params.clone)
31
+ Toast::Single.new(model, subresource_name, params.clone)
36
32
  elsif id.nil?
37
33
  Toast::RootCollection.new(model, subresource_name, params.clone)
38
34
  elsif subresource_name.nil?
39
35
  Toast::Record.new(model, id, format)
40
36
  elsif model.toast_config.exposed_associations.include? subresource_name
41
- Toast::Association.new(model, id, subresource_name, format)
37
+ Toast::Association.new(model, id, subresource_name, format)
42
38
  else
43
39
  raise ResourceNotFound
44
40
  end
45
-
41
+
46
42
  rsc.media_type = request.media_type
43
+ rsc.base_uri = request.base_url
47
44
 
48
45
  rsc
49
46
  rescue NameError
@@ -67,18 +64,31 @@ module Toast
67
64
  end
68
65
 
69
66
  def apply method, payload
70
-
71
- raise MethodNotAllowed if self.model.toast_config.disallow_methods.include?(method.downcase)
72
-
73
67
  case method
74
68
  when "PUT","POST"
75
69
  self.send(method.downcase, payload)
76
70
  when "DELETE","GET"
77
- self.send(method.downcase)
71
+ self.send(method.downcase)
78
72
  else
79
73
  raise MethodNotAllowed
80
- end
74
+ end
75
+ end
76
+
77
+ private
78
+ def uri_fields record, in_collection=false
79
+ out = {}
80
+
81
+ exposed_assoc =
82
+ in_collection ? record.class.toast_config.in_collection.exposed_associations :
83
+ record.class.toast_config.exposed_associations
84
+
85
+ exposed_assoc.each do |assoc|
86
+ out[assoc] = "#{self.base_uri}#{record.uri_fullpath}/#{assoc}"
87
+ end
88
+
89
+ out["uri"] = self.base_uri + record.uri_fullpath
90
+
91
+ out
81
92
  end
82
93
  end
83
94
  end
84
-
@@ -18,9 +18,6 @@ module Toast
18
18
  end
19
19
 
20
20
  def get
21
- if @model.toast_config.in_collection.disallow_methods.include? "get"
22
- raise MethodNotAllowed
23
- end
24
21
 
25
22
  records = if @model.toast_config.pass_params_to.include?(@collection)
26
23
  @model.send(@collection, @params)
@@ -36,7 +33,10 @@ module Toast
36
33
  }
37
34
  when "json"
38
35
  {
39
- :json => records.map{|r| r.exposed_attributes(:in_collection => true)},
36
+ :json => records.map{|r|
37
+ r.exposed_attributes(:in_collection => true).
38
+ merge( uri_fields(r, true) )
39
+ },
40
40
  :status => :ok
41
41
  }
42
42
  else
@@ -49,10 +49,8 @@ module Toast
49
49
  end
50
50
 
51
51
  def post payload
52
- if @model.toast_config.in_collection.disallow_methods.include? "post"
53
- raise MethodNotAllowed
54
- end
55
-
52
+ raise MethodNotAllowed unless @model.toast_config.postable?
53
+
56
54
  if @collection != "all"
57
55
  raise MethodNotAllowed
58
56
  end
@@ -71,7 +69,7 @@ module Toast
71
69
  end
72
70
 
73
71
  # silently ignore all exposed readable, but not writable fields
74
- (@model.toast_config.readables - @model.toast_config.writables).each do |rof|
72
+ (@model.toast_config.readables - @model.toast_config.writables + ["uri"]).each do |rof|
75
73
  payload.delete(rof)
76
74
  end
77
75
 
@@ -79,8 +77,8 @@ module Toast
79
77
  record = @model.create! payload
80
78
 
81
79
  {
82
- :json => record.exposed_attributes,
83
- :location => record.uri,
80
+ :json => record.exposed_attributes.merge( uri_fields(record) ),
81
+ :location => self.base_uri + record.uri_fullpath,
84
82
  :status => :created
85
83
  }
86
84
 
@@ -6,47 +6,43 @@ module Toast
6
6
 
7
7
  # The single resource name must be a class method of the model and
8
8
  # must return nil or an instance.
9
-
9
+
10
10
  # GET is the only allowed verb. To make changes the URI with ID has
11
11
  # to be used.
12
12
  class Single < Resource
13
-
13
+
14
14
  attr_reader :model
15
-
15
+
16
16
  def initialize model, subresource_name, params
17
-
17
+
18
18
  unless model.toast_config.singles.include? subresource_name
19
19
  raise ResourceNotFound
20
20
  end
21
21
 
22
22
  @model = model
23
- @single_finder = subresource_name
24
23
  @params = params
25
24
  @format = params[:format]
26
- end
27
25
 
28
- def get
29
- if @model.toast_config.in_collection.disallow_methods.include? "get"
30
- raise MethodNotAllowed
31
- end
32
-
33
- record = if @model.toast_config.pass_params_to.include?(@single_finder)
34
- @model.send(@single_finder, @params)
26
+ @record = if @model.toast_config.pass_params_to.include?(subresource_name)
27
+ @model.send(subresource_name, @params)
35
28
  else
36
- @model.send(@single_finder)
29
+ @model.send(subresource_name)
37
30
  end
38
31
 
39
- raise ResourceNotFound if record.nil?
40
-
32
+ raise ResourceNotFound if @record.nil?
33
+
34
+ end
35
+
36
+ def get
41
37
  case @format
42
38
  when "html"
43
39
  {
44
40
  :template => "resources/#{model.to_s.underscore}",
45
- :locals => { model.to_s.pluralize.underscore.to_sym => record }
41
+ :locals => { model.to_s.pluralize.underscore.to_sym => @record }
46
42
  }
47
- when "json"
43
+ when "json"
48
44
  {
49
- :json => record.exposed_attributes,
45
+ :json => @record.exposed_attributes.merge( uri_fields(@record) ),
50
46
  :status => :ok
51
47
  }
52
48
  else
@@ -54,6 +50,7 @@ module Toast
54
50
  end
55
51
  end
56
52
 
53
+
57
54
  def put
58
55
  raise MethodNotAllowed
59
56
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 4
8
- - 1
9
- version: 0.4.1
7
+ - 5
8
+ - 0
9
+ version: 0.5.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Robert Annies
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-03-13 00:00:00 +01:00
17
+ date: 2012-03-20 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency