toast 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,32 +1,31 @@
1
1
  Summary
2
2
  =======
3
3
 
4
- Toast is an extension to Ruby on Rails that lets you expose any
5
- ActiveRecord model as a resource according to the REST paradigm. The
6
- representation format is JSON.
4
+ Toast is an extension to Ruby on Rails to build web services with low
5
+ programming effort in a coherent way. Toast exends ActiveRecord such
6
+ that each model can be declared to be a web resource, exposing defined
7
+ attributes for reading and writing using HTTP.
7
8
 
8
- In contrast to other plugins, gems and Rails' inbuilt REST features
9
- toast takes a data centric approach: Tell the model to be a resource
10
- and what attributes and associations are to be exposed. That's it. No
11
- controller boiler plate code for every model, no routing setup.
9
+ Its main features are:
12
10
 
13
- Toast is a Rails engine that runs one generic controller and a sets up
14
- the routing according to the definition in the models, which is
15
- denoted using a block oriented DSL.
11
+ * declaration of web resources based on ActiveRecord models
12
+ * generic controller handles all actions
13
+ * automated routing
14
+ * exposing data values with JSON maps
15
+ * exposing associations by links (URLs)
16
16
 
17
- REST is more than some pretty URIs, the use of the HTTP verbs and
18
- response codes. It's on the toast user to invent media types that
19
- control the application's state and introduce semantics. With toast you
20
- can build REST services or tightly coupled server-client applications,
21
- which ever suits the task best. That's why TOAST stands for:
17
+ Toast works with Ruby on Rails >= 3.1.0 (currently tested up to 3.2.6)
22
18
 
23
- > **TOast Ain't reST**
19
+ WARNING
20
+ =======
21
+
22
+ This version is experimental and probably not bullet
23
+ proof. As soon as the gem is loaded a controller with ready routing is
24
+ enabled serving the annotated model's data records.
24
25
 
25
- *Be careful*: This version is experimental and probably not bullet
26
- proof. As soon as the gem is loaded a controller with ready routing
27
- is enabled serving the annotated model's data records for reading,
28
- updating and deleting. There are no measures to prevent XSS and CSFR
29
- attacks.
26
+ Version 1.0.0 of Toast is planned to be a production-ready implementation,
27
+ which will be finished within 2012. Until then API/DSL changes must
28
+ be expected with each minor update.
30
29
 
31
30
  Example
32
31
  =======
@@ -40,89 +39,89 @@ Let the table `bananas` have the following schema:
40
39
  t.integer "apple_id"
41
40
  end
42
41
 
43
- and let a corresponding model class have a *resourceful_model* annotation:
42
+ and let a corresponding model class have a *acts_as_resource* annotation:
44
43
 
45
44
  class Banana < ActiveRecord::Base
46
45
  belongs_to :apple
47
46
  has_many :coconuts
48
- scope :find_some, where("number < 100")
47
+
48
+ scope :less_than_100, where("number < 100")
49
49
 
50
- resourceful_model do
51
- # attributes or association names
50
+ acts_as_resource do
51
+ # exposed attributes or association names
52
52
  readables :coconuts, :apple
53
- writables :name, :number
53
+ writables :name, :number
54
54
 
55
- # class methods of Banana returning an Array of Banana records
56
- collections :find_some, :all
55
+ # exposed class methods of Banana returning an Array of Banana records
56
+ collections :less_than_100, :all
57
57
  end
58
58
  end
59
59
 
60
- The above definition inside the `resourceful_model` block exposes the
60
+ The above definition inside the `acts_as_resource` block exposes the
61
61
  records of the model Banana automatically via a generic controller to
62
62
  the outside world, accepting and delivering JSON representations of
63
63
  the records. Let the associated models Apple and Coconut be
64
- exposed as a resource, too.
64
+ exposed as a resource, too:
65
65
 
66
66
  ### Get a collection
67
67
  GET /bananas
68
- --> 200, '[{"uri":"http://www.example.com/bananas/23",
69
- "name": "Fred",
70
- "number": 33,
68
+ --> 200, '[{"self": "http://www.example.com/bananas/23",
69
+ "name": "Fred",
70
+ "number": 33,
71
71
  "coconuts": "http://www.example.com/bananas/23/coconuts",
72
- "apple":"http://www.example.com/bananas/23/apple,
73
- {"uri":"http://www.example.com/bananas/24",
72
+ "apple": "http://www.example.com/bananas/23/apple,
73
+ {"self": "http://www.example.com/bananas/24",
74
74
  ... }, ... ]
75
75
  ### Get a customized collection (filtered, paging, etc.)
76
-
77
- GET /bananas/find_some
78
- --> 200, '[SOME BANANAS]'
76
+ GET /bananas/less_than_100
77
+ --> 200, '[{BANANA}, {BANANA}, ...]'
79
78
 
80
79
  ### Get a single resource representation:
81
80
  GET /bananas/23
82
- --> 200, '{"uri":"http://www.example.com/bananas/23"
83
- "name": "Fred",
84
- "number": 33,
81
+ --> 200, '{"self": "http://www.example.com/bananas/23"
82
+ "name": "Fred",
83
+ "number": 33,
85
84
  "coconuts": "http://www.example.com/bananas/23/coconuts",
86
- "apple": "http://www.example.com/bananas/23/apple" }'
85
+ "apple": "http://www.example.com/bananas/23/apple" }'
87
86
 
88
- ### Get a associated collection
87
+ ### Get an associated collection
89
88
  "GET" /bananas/23/coconuts
90
89
  --> 200, '[{COCNUT},{COCONUT},...]',
91
90
 
92
91
  ### Update a single resource:
93
- PUT /bananas/23, '{"uri":"http://www.example.com/bananas/23"
94
- "name": "Barney",
92
+ PUT /bananas/23, '{"self": "http://www.example.com/bananas/23"
93
+ "name": "Barney",
95
94
  "number": 44}'
96
- --> 200, '{"uri":"http://www.example.com/bananas/23"
97
- "name": "Barney",
98
- "number": 44,
95
+ --> 200, '{"self": "http://www.example.com/bananas/23"
96
+ "name": "Barney",
97
+ "number": 44,
99
98
  "coconuts": "http://www.example.com/bananas/23/coconuts",
100
- "apple": "http://www.example.com/bananas/23/apple"}'
99
+ "apple": "http://www.example.com/bananas/23/apple"}'
101
100
 
102
101
  ### Create a new record
103
102
  "POST" /bananas, '{"name": "Johnny",
104
103
  "number": 888}'
105
- --> 201, {"uri":"http://www.example.com/bananas/102"
106
- "name": "Johnny",
107
- "number": 888,
104
+ --> 201, {"self": "http://www.example.com/bananas/102"
105
+ "name": "Johnny",
106
+ "number": 888,
108
107
  "coconuts": "http://www.example.com/bananas/102/coconuts" ,
109
- "apple": "http://www.example.com/bananas/102/apple }
108
+ "apple": "http://www.example.com/bananas/102/apple }
110
109
 
111
110
  ### Create an associated record
112
111
  "POST" /bananas/23/coconuts, '{COCONUT}'
113
- --> 201, {"uri":"http://www.example.com/coconuts/432,
112
+ --> 201, {"self":"http://www.example.com/coconuts/432,
114
113
  ...}
115
114
 
116
115
  ### Delete records
117
116
  DELETE /bananas/23
118
117
  --> 200
119
118
 
120
- More details and configuration options are documented in the manual... (_comming soon_)
119
+ More details and configuration options are documented in the manual.
121
120
 
122
121
  Installation
123
122
  ============
124
123
 
125
- With bundler
124
+ With bundler from (rubygems.org)
126
125
 
127
126
  gem "toast"
128
127
 
@@ -130,29 +129,14 @@ the latest Git:
130
129
 
131
130
  gem "toast", :git => "https://github.com/robokopp/toast.git"
132
131
 
133
- Test Suite
134
- ==========
135
-
136
- In `test/rails_app` you can find a rails application with tests. To run
137
- the tests you need to
138
-
139
- 0. Install the *jeweler* gem:
140
-
141
- gem install jeweler
142
-
143
- 1. install the toast gem from this git clone:
144
-
145
- rake install
146
-
147
- 2. initialize the test application
148
-
149
- cd test/rails_app
150
- bundle install
151
-
152
- 3. Now you can run the test suite from within the test application
132
+ Remarks
133
+ =======
153
134
 
154
- rake
135
+ REST is more than some pretty URIs, the use of the HTTP verbs and
136
+ response codes. It's on the Toast user to invent meaningful media
137
+ types that control the application's state and introduce
138
+ semantics. With toast you can build REST services or tightly coupled
139
+ server-client applications, which ever suits the task best. That's why
140
+ TOAST stands for:
155
141
 
156
- Or you may call `rake test` from the root directory of the working
157
- copy. This will reinstall the toast gem before running tests
158
- automatically.
142
+ > **TOast Ain't reST**
@@ -25,7 +25,7 @@ class ToastController < ApplicationController
25
25
 
26
26
  rescue Exception => e
27
27
  log_exception e
28
- raise e if Rails.env == "test"
28
+ puts e if Rails.env == "test"
29
29
  return head(:internal_server_error)
30
30
  end
31
31
 
data/config/routes.rb CHANGED
@@ -14,7 +14,7 @@ Rails.application.routes.draw do
14
14
 
15
15
  namespaces << tc.namespace
16
16
 
17
- match("#{tc.namespace}/#{resource_name}(/:id(/:subresource))" => 'toast#catch_all',
17
+ match("#{tc.namespace}/#{resource_name}(/:id(/:subresource(/:scope)))" => 'toast#catch_all',
18
18
  :constraints => { :id => /\d+/ },
19
19
  :resource => resource_name,
20
20
  :as => resource_name,
@@ -44,15 +44,28 @@ module Toast
44
44
  # Like ActiveRecord::Base.attributes, but result Hash includes
45
45
  # only attributes from the list _attr_names_ plus the
46
46
  # associations _assoc_names_ as links and the 'self' link
47
- def represent attr_names, assoc_names, base_uri
47
+ def represent attr_names, assoc_names, base_uri, media_type
48
48
  props = {}
49
49
 
50
50
  attr_names.each do |name|
51
+ unless self.respond_to?(name) && self.method(name).arity == 0
52
+ raise "Toast Error: Connot find instance method '#{self.class}##{name}' of arity 0"
53
+ end
51
54
  props[name] = self.send(name)
52
55
  end
53
56
 
54
57
  assoc_names.each do |name|
55
58
  props[name] = "#{base_uri}#{self.uri_path}/#{name}"
59
+
60
+ # collections (scopes) actiing on associacions:
61
+
62
+ reflect = self.class.reflect_on_association(name.to_sym)
63
+ if reflect.collection?
64
+ reflect.klass.toast_config(media_type).collections.each do |collection_name|
65
+ next if collection_name == "all"
66
+ props[name+":"+collection_name] = "#{base_uri}#{self.uri_path}/#{name}/#{collection_name}"
67
+ end
68
+ end
56
69
  end
57
70
 
58
71
  props["self"] = base_uri + self.uri_path
@@ -22,6 +22,11 @@ module Toast
22
22
  end
23
23
 
24
24
  def get
25
+
26
+ unless @record.class.reflect_on_all_associations.detect{|a| a.name.to_s == @assoc}
27
+ raise "Toast Error: Association '#{@assoc}' not found in model '#{@record.class}'"
28
+ end
29
+
25
30
  result = @record.send(@assoc)
26
31
 
27
32
  raise ResourceNotFound if result.nil?
@@ -31,7 +36,8 @@ module Toast
31
36
  :json => result.map{|r|
32
37
  r.represent( @associate_config_out.in_collection.exposed_attributes,
33
38
  @associate_config_out.in_collection.exposed_associations,
34
- @base_uri )
39
+ @base_uri,
40
+ @associate_config_out.media_type )
35
41
  },
36
42
  :status => :ok,
37
43
  :content_type => @associate_config_out.in_collection.media_type
@@ -40,7 +46,8 @@ module Toast
40
46
  {
41
47
  :json => result.represent( @associate_config_out.exposed_attributes,
42
48
  @associate_config_out.exposed_associations,
43
- @base_uri ),
49
+ @base_uri,
50
+ @associate_config_out.media_type),
44
51
  :status => :ok,
45
52
  :content_type => @associate_config_out.media_type
46
53
  }
@@ -81,7 +88,8 @@ module Toast
81
88
  {
82
89
  :json => record.represent( @associate_config_out.exposed_attributes,
83
90
  @associate_config_out.exposed_associations,
84
- @base_uri ),
91
+ @base_uri,
92
+ @associate_config_out.media_type),
85
93
  :location => self.base_uri + record.uri_path,
86
94
  :status => :created,
87
95
  :content_type => @associate_config_out.media_type
@@ -3,7 +3,7 @@ module Toast
3
3
 
4
4
  attr_reader :model
5
5
 
6
- def initialize model, subresource_name, params, config_in, config_out
6
+ def initialize model_or_relation, subresource_name, params, config_in, config_out
7
7
 
8
8
  subresource_name ||= "all"
9
9
 
@@ -11,7 +11,11 @@ module Toast
11
11
  raise ResourceNotFound
12
12
  end
13
13
 
14
- @model = model
14
+ @target_model = (model_or_relation.klass rescue false) ?
15
+ model_or_relation.klass :
16
+ model_or_relation
17
+
18
+ @model_or_relation = model_or_relation
15
19
  @collection = subresource_name
16
20
  @params = params
17
21
  @format = params[:format]
@@ -21,24 +25,43 @@ module Toast
21
25
 
22
26
  def get
23
27
 
24
- records = if @config_out.pass_params_to.include?(@collection)
25
- @model.send(@collection, @params)
28
+ unless @model_or_relation.respond_to?(@collection)
29
+ raise "Toast Error: Cannot find class method '#{@collection}' of model '#{@model_or_relation}', which is configured in 'acts_as_resource > collections'."
30
+ end
31
+
32
+ # FIXME: This is a lot of hallooballoo to check if the #send
33
+ # will be successful, but if it's not checked the error
34
+ # message is not helpful to find the error.
35
+
36
+ records = if @config_out.pass_params_to.include?(@collection)
37
+ if @target_model.method(@collection).arity != 1
38
+ raise "Toast Error: Class method '#{@collection}' of model '#{@target_model}' must accept one parameter, as configured by 'acts_as_resource > pass_params_to'."
39
+ end
40
+ # fetch results
41
+ @model_or_relation.send(@collection, @params)
42
+
26
43
  else
27
- @model.send(@collection)
44
+
45
+ if @target_model.method(@collection).arity > 0
46
+ raise "Toast Error: Class method '#{@collection}' of model '#{@model_or_relation}' must not accept any parameter, as configured by 'acts_as_resource'"
47
+ end
48
+ # fetch results
49
+ @model_or_relation.send(@collection)
28
50
  end
29
51
 
30
52
  case @format
31
53
  when "html"
32
54
  {
33
- :template => "resources/#{model.to_s.pluralize.underscore}",
34
- :locals => { model.to_s.pluralize.underscore.to_sym => records }
55
+ :template => "resources/#{@target_model.to_s.pluralize.underscore}",
56
+ :locals => { @target_model.to_s.pluralize.underscore.to_sym => records }
35
57
  }
36
58
  when "json"
37
59
  {
38
60
  :json => records.map{|r|
39
61
  r.represent( @config_out.in_collection.exposed_attributes,
40
62
  @config_out.in_collection.exposed_associations,
41
- @base_uri )
63
+ self.base_uri,
64
+ @config_out.media_type)
42
65
  },
43
66
  :status => :ok,
44
67
  :content_type => @config_out.in_collection.media_type
@@ -78,13 +101,14 @@ module Toast
78
101
  end
79
102
 
80
103
  begin
81
- record = @model.create! payload
104
+ record = @model_or_relation.create! payload
82
105
 
83
106
  {
84
107
  :json => record.represent( @config_out.exposed_attributes,
85
108
  @config_out.exposed_associations,
86
- @base_uri ),
87
- :location => @base_uri + record.uri_path,
109
+ self.base_uri,
110
+ @config_out.media_type),
111
+ :location => self.base_uri + record.uri_path,
88
112
  :status => :created,
89
113
  :content_type => @config_out.media_type
90
114
  }
data/lib/toast/engine.rb CHANGED
@@ -4,6 +4,7 @@ require 'toast/collection'
4
4
  require 'toast/association'
5
5
  require 'toast/record'
6
6
  require 'toast/single'
7
+ require 'toast/scoped_association'
7
8
 
8
9
  require 'action_dispatch/http/request'
9
10
  require 'rack/accept_media_types'
data/lib/toast/record.rb CHANGED
@@ -41,6 +41,11 @@ module Toast
41
41
 
42
42
  # set the virtual attributes
43
43
  (@config_in.writables - @record.attribute_names - @config_in.exposed_associations).each do |vattr|
44
+
45
+ unless (@record.respond_to?("#{vattr}=") && @record.method("#{vattr}=").arity == 1)
46
+ raise "Toast Error: Connot find setter '#{@record.class}##{vattr}='"
47
+ end
48
+
44
49
  @record.send("#{vattr}=", payload.delete(vattr))
45
50
  end
46
51
 
@@ -49,7 +54,8 @@ module Toast
49
54
  {
50
55
  :json => @record.represent( @config_out.exposed_attributes,
51
56
  @config_out.exposed_associations,
52
- @base_uri ),
57
+ @base_uri,
58
+ @config_out.media_type ),
53
59
  :status => :ok,
54
60
  :location => self.base_uri + @record.uri_path,
55
61
  :content_type => @config_out.media_type
@@ -67,7 +73,8 @@ module Toast
67
73
  {
68
74
  :json => @record.represent( @config_out.exposed_attributes,
69
75
  @config_out.exposed_associations,
70
- @base_uri ),
76
+ @base_uri,
77
+ @config_out.media_type),
71
78
  :status => :ok,
72
79
  :content_type => @config_out.media_type
73
80
  }
@@ -22,11 +22,7 @@ module Toast
22
22
  id = params[:id]
23
23
  subresource_name = params[:subresource]
24
24
  format = params[:format]
25
-
26
- #### Debugging stop
27
- # binding.pry if $halt
28
- ###
29
-
25
+ scope = params[:scope]
30
26
  begin
31
27
 
32
28
  # determine model
@@ -39,7 +35,7 @@ module Toast
39
35
  config_in = model.toast_config request.media_type
40
36
 
41
37
  # ... or in case of an association request
42
- config_assoc_src = model.toast_config request.headers["Assoc-source-type"]
38
+ config_assoc_src = model.toast_config request.headers["Assoc-source-type"] # ?
43
39
 
44
40
  # base URI for returned object
45
41
  base_uri = request.base_url + request.script_name +
@@ -53,9 +49,10 @@ module Toast
53
49
  elsif subresource_name.nil?
54
50
  Toast::Record.new(model, id, format, config_in, config_out)
55
51
  elsif (config_assoc_src && config_assoc_src.exposed_associations.include?(subresource_name))
56
-
52
+
57
53
  # determine associated model
58
- assoc_model = get_class_by_resource_name subresource_name
54
+ assoc_model =
55
+ model.reflect_on_all_associations.detect{|a| a.name.to_s == subresource_name}.klass
59
56
 
60
57
  # determine config for representation of assoc. model
61
58
  assoc_config_out = assoc_model.toast_config request.accept_media_types.prefered
@@ -65,8 +62,13 @@ module Toast
65
62
  base_uri = request.base_url + request.script_name +
66
63
  (assoc_config_out.namespace ? "/" + assoc_config_out.namespace : "")
67
64
 
68
- Toast::Association.new(model, id, subresource_name, format, config_assoc_src,
69
- assoc_model, assoc_config_in, assoc_config_out)
65
+ if scope.nil?
66
+ Toast::Association.new(model, id, subresource_name, format, config_assoc_src,
67
+ assoc_model, assoc_config_in, assoc_config_out)
68
+ else
69
+ Toast::ScopedAssociation.new(model, id, subresource_name, scope, params.clone,
70
+ assoc_config_in, assoc_config_out)
71
+ end
70
72
  else
71
73
  raise ResourceNotFound
72
74
  end
@@ -74,6 +76,8 @@ module Toast
74
76
  # set base to be prepended to URIs
75
77
  rsc.base_uri = base_uri
76
78
 
79
+
80
+
77
81
  rsc
78
82
  rescue NameError
79
83
  raise ResourceNotFound
@@ -0,0 +1,23 @@
1
+ module Toast
2
+ class ScopedAssociation < Resource
3
+ # delegeates everything to Toast::Collection, where the model is replaced
4
+ # by the Relation this association creates
5
+
6
+ def initialize(model, id, association_name, scope_name, params,
7
+ config_in, config_out)
8
+
9
+ record = model.find(id)
10
+ assoc_model = record.class
11
+
12
+ @collection = Toast::Collection.new(record.send(association_name),
13
+ scope_name,
14
+ params,
15
+ config_in,
16
+ config_out)
17
+
18
+ end
19
+
20
+ delegate :get, :put, :post, :delete, :base_uri, :base_uri=, :to => :@collection
21
+
22
+ end
23
+ end
data/lib/toast/single.rb CHANGED
@@ -25,10 +25,20 @@ module Toast
25
25
  @params = params
26
26
  @format = params[:format]
27
27
 
28
+ unless @model.respond_to?(subresource_name)
29
+ raise "Toast Error: Cannot find class method '#{@model}.#{subresource_name}', which is configured in 'acts_as_resource > singles'."
30
+ end
28
31
 
29
32
  @record = if @config_out.pass_params_to.include?(subresource_name)
33
+ if @model.method(subresource_name).arity != 1
34
+ raise "Toast Error: Class method '#{@model}.#{subresource_name}' must accept one parameter, as configured by 'acts_as_resource > pass_params_to'."
35
+ end
30
36
  @model.send(subresource_name, @params)
31
37
  else
38
+ if(@model.method(subresource_name).arity < -1 or
39
+ @model.method(subresource_name).arity > 0)
40
+ raise "Toast Error: Class method '#{@model}.#{subresource_name}' must be callable w/o parameters"
41
+ end
32
42
  @model.send(subresource_name)
33
43
  end
34
44
 
@@ -46,7 +56,8 @@ module Toast
46
56
  {
47
57
  :json => @record.represent( @config_out.exposed_attributes,
48
58
  @config_out.exposed_associations,
49
- @base_uri ),
59
+ @base_uri,
60
+ @config_out.media_type),
50
61
  :status => :ok
51
62
  }
52
63
  else
@@ -0,0 +1,3 @@
1
+ module Toast
2
+ VERSION = '0.7.0'
3
+ end
metadata CHANGED
@@ -4,23 +4,49 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 6
7
+ - 7
8
8
  - 0
9
- version: 0.6.0
9
+ version: 0.7.0
10
10
  platform: ruby
11
11
  authors:
12
- - Robert Annies
12
+ - "robokopp (Robert Anni\xC3\xA9s)"
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-05-31 00:00:00 +02:00
17
+ date: 2012-07-10 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- name: blockenspiel
21
+ name: shoulda
22
22
  prerelease: false
23
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :development
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: rails
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 3
41
+ - 1
42
+ - 0
43
+ version: 3.1.0
44
+ type: :runtime
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: blockenspiel
48
+ prerelease: false
49
+ requirement: &id003 !ruby/object:Gem::Requirement
24
50
  requirements:
25
51
  - - ~>
26
52
  - !ruby/object:Gem::Version
@@ -30,11 +56,11 @@ dependencies:
30
56
  - 2
31
57
  version: 0.4.2
32
58
  type: :runtime
33
- version_requirements: *id001
59
+ version_requirements: *id003
34
60
  - !ruby/object:Gem::Dependency
35
61
  name: rack-accept-media-types
36
62
  prerelease: false
37
- requirement: &id002 !ruby/object:Gem::Requirement
63
+ requirement: &id004 !ruby/object:Gem::Requirement
38
64
  requirements:
39
65
  - - ~>
40
66
  - !ruby/object:Gem::Version
@@ -43,11 +69,23 @@ dependencies:
43
69
  - 9
44
70
  version: "0.9"
45
71
  type: :runtime
46
- version_requirements: *id002
72
+ version_requirements: *id004
73
+ - !ruby/object:Gem::Dependency
74
+ name: ffaker
75
+ prerelease: false
76
+ requirement: &id005 !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ type: :runtime
84
+ version_requirements: *id005
47
85
  description: |-
48
86
  Toast is an extension to Ruby on Rails that lets you expose any
49
- ActiveRecord model as a resource according to the REST paradigm. The
50
- representation format is JSON.
87
+ ActiveRecord model as a resource according to the REST paradigm via a generic controller. The default
88
+ representation format is JSON or can be anything
51
89
  email: robokopp@fernwerk.net
52
90
  executables: []
53
91
 
@@ -59,6 +97,7 @@ files:
59
97
  - app/controller/toast_controller.rb
60
98
  - config/routes.rb
61
99
  - lib/toast.rb
100
+ - lib/toast/version.rb
62
101
  - lib/toast/active_record_extensions.rb
63
102
  - lib/toast/association.rb
64
103
  - lib/toast/collection.rb
@@ -67,6 +106,7 @@ files:
67
106
  - lib/toast/record.rb
68
107
  - lib/toast/resource.rb
69
108
  - lib/toast/single.rb
109
+ - lib/toast/scoped_association.rb
70
110
  - README.md
71
111
  has_rdoc: true
72
112
  homepage: https://github.com/robokopp/toast