toast 0.8.13 → 0.8.14

Sign up to get free protection for your applications and to get access to all the features.
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