toast 0.8.13 → 0.8.14

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.
data/README.md CHANGED
@@ -23,16 +23,21 @@ Toast works with
23
23
 
24
24
  See the [User Manual](https://github.com/robokopp/toast/wiki/User-Manual) for a detailed description.
25
25
 
26
+ Status
27
+ =====
28
+
29
+ Toast is ready for production and is being used in productive
30
+ applications since 2012. However, misconfigurations can have undesired
31
+ effects.
32
+
26
33
  WARNING
27
34
  =======
28
35
 
29
- This version is experimental and probably not bullet
30
- proof. As soon as the gem is loaded a controller with ready routing is
31
- enabled serving the annotated model's data records.
36
+ A soon the gem is loaded a controller with ready routing is enabled
37
+ serving the annotated model's data records at least for reading for
38
+ everybody.
32
39
 
33
- Version 1.0.0 of Toast is planned to be a production-ready implementation,
34
- which will be finished within 2012. Until then API/DSL changes must
35
- be expected with each minor update.
40
+ You need to implement authorization yourself.
36
41
 
37
42
  Example
38
43
  =======
@@ -1,3 +1,5 @@
1
+ require 'rack-link_headers'
2
+
1
3
  class ToastController < ApplicationController
2
4
 
3
5
  def catch_all
@@ -11,7 +13,37 @@ class ToastController < ApplicationController
11
13
  request.headers["LINK"] =~ /(#{request.protocol + request.host + request.script_name})(.*)/
12
14
  end
13
15
 
14
- render @resource.apply(request.method, request.body.read, request.content_type, $2)
16
+ toast_response = @resource.apply(request.method, request.body.read, request.content_type, $2)
17
+
18
+ # pagination
19
+ if pi = toast_response[:pagination_info]
20
+ # URL w/o parameters
21
+
22
+ url = request.url.split('?').first
23
+ qpar = request.query_parameters.clone
24
+
25
+ # change/add page parameter
26
+ link_header = []
27
+
28
+ if pi[:prev]
29
+ qpar[:page] = pi[:prev]
30
+ response.link "#{url}?#{qpar.to_query}", :rel => :prev
31
+ end
32
+
33
+ if pi[:next]
34
+ qpar[:page] = pi[:next]
35
+ response.link "#{url}?#{qpar.to_query}", :rel => :next
36
+ end
37
+
38
+ qpar[:page] = pi[:last]
39
+ response.link "#{url}?#{qpar.to_query}", :rel => :last
40
+
41
+ qpar[:page] = 1
42
+ response.link "#{url}?#{qpar.to_query}", :rel => :first
43
+
44
+ end
45
+
46
+ render toast_response
15
47
 
16
48
  rescue Toast::ResourceNotFound => e
17
49
  return head(:not_found)
@@ -22,6 +54,9 @@ class ToastController < ApplicationController
22
54
  rescue Toast::PayloadFormatError => e
23
55
  return head(:bad_request)
24
56
 
57
+ rescue Toast::BadRequest => e
58
+ return head(:bad_request)
59
+
25
60
  rescue Toast::MethodNotAllowed => e
26
61
  return head(:method_not_allowed)
27
62
 
@@ -29,18 +29,24 @@ module Toast
29
29
 
30
30
  reflection = @model.reflect_on_association(@assoc.to_sym)
31
31
 
32
- result =
32
+ if reflection.collection?
33
+
34
+
33
35
  if(@config.pass_params_to.include?(@assoc) and
34
36
  reflection.options[:extend] and
35
37
  reflection.options[:extend].detect{|e| e.method_defined? :find_by_params} )
36
- @record.send(@assoc).find_by_params(@params)
38
+
39
+ result, pagination_info = paginate_query( @config, @assoc,
40
+ @record.send(@assoc).find_by_params(@params), @params)
37
41
  else
38
- @record.send(@assoc)
42
+
43
+ result, pagination_info = paginate_query( @config, @assoc,
44
+ @record.send(@assoc), @params)
45
+
39
46
  end
40
47
 
41
- raise ResourceNotFound if result.nil?
48
+ raise ResourceNotFound if result.nil?
42
49
 
43
- if result.is_a? ActiveRecord::Relation or result.is_a? Array
44
50
  {
45
51
  :json => result.map{|r|
46
52
  r.represent( @associate_config_out.in_collection.exposed_attributes,
@@ -49,9 +55,23 @@ module Toast
49
55
  @associate_config_out.media_type )
50
56
  },
51
57
  :status => :ok,
52
- :content_type => @associate_config_out.in_collection.media_type
58
+ :content_type => @associate_config_out.in_collection.media_type,
59
+ :pagination_info => pagination_info
53
60
  }
61
+
54
62
  else
63
+
64
+ result =
65
+ if(@config.pass_params_to.include?(@assoc) and
66
+ reflection.options[:extend] and
67
+ reflectin.options[:extend].detect{|e| e.method_defined? :find_by_params} )
68
+ @record.send(@assoc).find_by_params(@params)
69
+ else
70
+ @record.send(@assoc)
71
+ end
72
+
73
+ raise ResourceNotFound if result.nil?
74
+
55
75
  {
56
76
  :json => result.represent( @associate_config_out.exposed_attributes,
57
77
  @associate_config_out.exposed_associations,
@@ -32,27 +32,33 @@ module Toast
32
32
  # will be successful, but if it's not checked the error
33
33
  # message is not helpful to find the error.
34
34
 
35
- records = if @config_out.pass_params_to.include?(@collection)
36
- if @model.method(@collection).arity**2 != 1
37
- raise "Toast Error: Class method '#{@collection}' of model '#{@model}' must accept one parameter, as configured by 'acts_as_resource > pass_params_to'."
38
- end
39
- # fetch results
40
- @model.send(@collection, @params)
41
-
42
- else
43
-
44
- if @model.method(@collection).arity > 0
45
- raise "Toast Error: Class method '#{@collection}' of model '#{@model}' must not accept any parameter, as configured by 'acts_as_resource'"
46
- end
47
- # fetch results
48
- @model.send(@collection)
49
- end
35
+ if @config_out.pass_params_to.include?(@collection)
36
+ if @model.method(@collection).arity**2 != 1
37
+ raise "Toast Error: Class method '#{@collection}' of model '#{@model}' must accept one parameter, as configured by 'acts_as_resource > pass_params_to'."
38
+ end
39
+
40
+ # fetch results
41
+ #binding.pry if $halt
42
+ records, pagination_info = paginate_query( @config_out, @collection,
43
+ @model.send(@collection, @params),
44
+ @params )
45
+ else
46
+
47
+ if @model.method(@collection).arity > 0
48
+ raise "Toast Error: Class method '#{@collection}' of model '#{@model}' must not accept any parameter, as configured by 'acts_as_resource'"
49
+ end
50
+
51
+ records, pagination_info = paginate_query( @config_out, @collection,
52
+ @model.send(@collection=='all'? 'scoped': @collection), # #scoped ?: #all would trigger query too early
53
+ @params )
54
+ end
50
55
 
51
56
  case @format
52
57
  when "html"
53
58
  {
54
59
  :template => "resources/#{@model.to_s.pluralize.underscore}",
55
- :locals => { @model.to_s.pluralize.underscore.to_sym => records }
60
+ :locals => { @model.to_s.pluralize.underscore.to_sym => records,
61
+ :pagination_info => pagination_info }
56
62
  }
57
63
  when "json"
58
64
  {
@@ -63,7 +69,8 @@ module Toast
63
69
  @config_out.media_type)
64
70
  },
65
71
  :status => :ok,
66
- :content_type => @config_out.in_collection.media_type
72
+ :content_type => @config_out.in_collection.media_type,
73
+ :pagination_info => pagination_info
67
74
  }
68
75
  else
69
76
  raise ResourceNotFound
@@ -127,5 +134,6 @@ module Toast
127
134
  def unlink l
128
135
  raise MethodNotAllowed
129
136
  end
137
+
130
138
  end
131
139
  end
@@ -18,7 +18,7 @@ module Toast
18
18
  @in_collection = ConfigDSL::InCollection.new model, self
19
19
  @media_type = "application/json"
20
20
  @apidoc = {}
21
-
21
+ @paginations = {}
22
22
  @model.attr_accessible []
23
23
  end
24
24
 
@@ -112,12 +112,23 @@ module Toast
112
112
  @apidoc = arg
113
113
  end
114
114
 
115
+ def paginate collection_name, options={}
116
+ options.reverse_merge! :page_size => 30
117
+
118
+ @paginations[collection_name.to_s] = {:page_size => options[:page_size]}
119
+ end
120
+
115
121
  # non DSL methods
116
122
  dsl_methods false
117
123
 
118
124
  def field_comments
119
125
  @field_comments
120
126
  end
127
+
128
+ def paginations
129
+ @paginations
130
+ end
131
+
121
132
  end
122
133
 
123
134
  class InCollection
@@ -8,6 +8,7 @@ module Toast
8
8
  class UnsupportedMediaType < Exception; end
9
9
  class RequestedVersionNotDefined < Exception; end
10
10
  class ResourceNotAcceptable < Exception; end
11
+ class BadRequest < Exception; end
11
12
 
12
13
  # Represents a resource. There are following resource types as sub classes:
13
14
  # Record, Collection, Association, Single
@@ -135,5 +136,40 @@ module Toast
135
136
 
136
137
  out
137
138
  end
139
+
140
+
141
+ def paginate_query config, relation_name, relation, params
142
+
143
+ if pagination = config.paginations[relation_name] and params[:page] =~ /\d+/
144
+
145
+ raise Toast::BadRequest.new("Page 0 not defined. Paging starts with 1") if params[:page].to_i == 0
146
+
147
+ ps = pagination[:page_size].to_i
148
+ total = relation.count
149
+
150
+ page = params[:page].to_i
151
+ total_pages = (total/ps) + (total%ps == 0 ? 0 : 1)
152
+
153
+ pagination_info = {}
154
+
155
+ if total_pages > 0
156
+
157
+ if page > total_pages
158
+ pagination_info[:prev] = total_pages
159
+ else
160
+ pagination_info[:prev] = page - 1 unless page == 1
161
+ pagination_info[:next] = page + 1 if page < total_pages
162
+ end
163
+
164
+ pagination_info[:last] = total_pages
165
+ else
166
+ pagination_info[:last] = 1
167
+ end
168
+
169
+ [relation.limit(ps).offset((page-1) * ps), pagination_info]
170
+ else
171
+ [relation, nil]
172
+ end
173
+ end
138
174
  end
139
175
  end
data/lib/toast/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Toast
2
- VERSION = '0.8.13'
2
+ VERSION = '0.8.14'
3
3
  end
metadata CHANGED
@@ -1,81 +1,112 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: toast
3
- version: !ruby/object:Gem::Version
4
- version: 0.8.13
3
+ version: !ruby/object:Gem::Version
4
+ hash: 35
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 8
9
+ - 14
10
+ version: 0.8.14
5
11
  platform: ruby
6
- authors:
7
- - robokopp (Robert Anniés)
12
+ authors:
13
+ - "robokopp (Robert Anni\xC3\xA9s)"
8
14
  autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
- date: 2014-02-17 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
17
+
18
+ date: 2014-04-14 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
14
22
  name: rails
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - '>='
18
- - !ruby/object:Gem::Version
19
- version: 3.1.0
20
- type: :runtime
21
23
  prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - '>='
25
- - !ruby/object:Gem::Version
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 3
32
+ - 1
33
+ - 0
26
34
  version: 3.1.0
27
- - !ruby/object:Gem::Dependency
28
- name: blockenspiel
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ~>
32
- - !ruby/object:Gem::Version
33
- version: 0.4.2
34
35
  type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: blockenspiel
35
39
  prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
38
43
  - - ~>
39
- - !ruby/object:Gem::Version
44
+ - !ruby/object:Gem::Version
45
+ hash: 11
46
+ segments:
47
+ - 0
48
+ - 4
49
+ - 2
40
50
  version: 0.4.2
41
- - !ruby/object:Gem::Dependency
42
- name: rack-accept-media-types
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ~>
46
- - !ruby/object:Gem::Version
47
- version: '0.9'
48
51
  type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: rack-accept-media-types
49
55
  prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
52
59
  - - ~>
53
- - !ruby/object:Gem::Version
54
- version: '0.9'
55
- - !ruby/object:Gem::Dependency
60
+ - !ruby/object:Gem::Version
61
+ hash: 25
62
+ segments:
63
+ - 0
64
+ - 9
65
+ version: "0.9"
66
+ type: :runtime
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
56
69
  name: ffaker
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - '>='
60
- - !ruby/object:Gem::Version
61
- version: '0'
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
62
80
  type: :runtime
81
+ version_requirements: *id004
82
+ - !ruby/object:Gem::Dependency
83
+ name: rack-link_headers
63
84
  prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - '>='
67
- - !ruby/object:Gem::Version
68
- version: '0'
85
+ requirement: &id005 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 2
93
+ - 2
94
+ - 2
95
+ version: 2.2.2
96
+ type: :runtime
97
+ version_requirements: *id005
69
98
  description: |-
70
99
  Toast is an extension to Ruby on Rails that lets you expose any
71
100
  ActiveRecord model as a resource according to the REST paradigm via a generic controller. The default
72
101
  representation format is JSON or can be anything
73
102
  email: robokopp@fernwerk.net
74
103
  executables: []
104
+
75
105
  extensions: []
76
- extra_rdoc_files:
106
+
107
+ extra_rdoc_files:
77
108
  - README.md
78
- files:
109
+ files:
79
110
  - app/controller/toast_controller.rb
80
111
  - config/routes.rb
81
112
  - lib/toast.rb
@@ -89,27 +120,39 @@ files:
89
120
  - lib/toast/resource.rb
90
121
  - lib/toast/single.rb
91
122
  - README.md
123
+ has_rdoc: true
92
124
  homepage: https://github.com/robokopp/toast
93
125
  licenses: []
94
- metadata: {}
126
+
95
127
  post_install_message:
96
128
  rdoc_options: []
97
- require_paths:
129
+
130
+ require_paths:
98
131
  - lib
99
- required_ruby_version: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - '>='
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- required_rubygems_version: !ruby/object:Gem::Requirement
105
- requirements:
106
- - - '>='
107
- - !ruby/object:Gem::Version
108
- version: '0'
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ hash: 3
138
+ segments:
139
+ - 0
140
+ version: "0"
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ hash: 3
147
+ segments:
148
+ - 0
149
+ version: "0"
109
150
  requirements: []
151
+
110
152
  rubyforge_project:
111
- rubygems_version: 2.0.3
153
+ rubygems_version: 1.6.2
112
154
  signing_key:
113
- specification_version: 4
155
+ specification_version: 3
114
156
  summary: Toast adds a RESTful interface to ActiveRecord models in Ruby on Rails.
115
157
  test_files: []
158
+
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: b312ad41453a5e1c9d8c38fb0ebfc38d0d1c26fe
4
- data.tar.gz: c09def11305a4ec61d570f98ef71de7982da41af
5
- SHA512:
6
- metadata.gz: 6aad28550213a443d1d8b3d0a8b4f4dbf282f83a1295b90c40f7e3cc1155900aaab227276455f24b5343f6bf6b670cc56d2dabe13bcb3799652a4d799692694d
7
- data.tar.gz: 42421ba7a9854ce1b8ce0aceaf3c5716fe069ab3e396c90167a895a8a7b84cce0ddcaf4d6b68d38dfbbb86ee61acda3ac9b4f455d8ce8984108f8b93f8eaa819