toast 0.9.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,85 +0,0 @@
1
- require 'blockenspiel'
2
- require 'toast/config_dsl'
3
-
4
- module Toast
5
- module ActiveRecordExtensions
6
-
7
- # Configuration DSL
8
- def acts_as_resource &block
9
-
10
- @toast_configs ||= Array.new
11
- @toast_configs << Toast::ConfigDSL::Base.new(self)
12
-
13
- Blockenspiel.invoke( block, @toast_configs.last)
14
-
15
- # add class methods
16
- self.instance_eval do
17
-
18
- def is_resourceful_model?
19
- true
20
- end
21
-
22
- def toast_configs
23
- @toast_configs
24
- end
25
-
26
- # get a config by media type or first one if none matches
27
- def toast_config media_type
28
- @toast_configs.find do |tc|
29
- tc.media_type == media_type || tc.in_collection.media_type == media_type
30
- end || @toast_configs.first
31
- end
32
-
33
- def toast_resource_uri_prefix
34
- @toast_resource_uri_prefix ||= self.to_s.pluralize.underscore
35
- end
36
-
37
- end
38
-
39
- # add instance methods
40
- self.class_eval do
41
- # Return the path segment of the URI of this record
42
- def uri_path
43
- "/" +
44
- self.class.toast_resource_uri_prefix + "/" +
45
- self.id.to_s
46
- end
47
-
48
- # Like ActiveRecord::Base.attributes, but result Hash includes
49
- # only attributes from the list _attr_names_ plus the
50
- # associations _assoc_names_ as links and the 'self' link
51
- def represent attr_names, assoc_names, base_uri, media_type
52
- props = {}
53
-
54
- attr_names.each do |name|
55
- unless self.respond_to?(name)
56
- raise "Toast Error: Connot find instance method '#{self.class}##{name}'"
57
- end
58
- props[name] = self.send(name)
59
- end
60
-
61
- assoc_names.each do |name|
62
- props[name] = "#{base_uri}#{self.uri_path}/#{name}"
63
- end
64
-
65
- props["self"] = base_uri + self.uri_path
66
-
67
- props
68
- end
69
- end
70
- end
71
-
72
- # defaults for non resourceful-models
73
- def is_resourceful_model?
74
- false
75
- end
76
- def resourceful_model_options
77
- nil
78
- end
79
- end
80
-
81
- def self.resourceful_models
82
- Module.constants.select{ |c| (eval c).respond_to?(:toast_config)}.sort.map{|c| c.constantize}
83
- end
84
-
85
- end
@@ -1,219 +0,0 @@
1
- module Toast
2
- class Association < Resource
3
-
4
- attr_reader :model
5
-
6
- def initialize model, id, subresource_name, params, format, config, assoc_model, assoc_config_in, assoc_config_out
7
-
8
- unless config.exposed_associations.include? subresource_name
9
- raise ResourceNotFound
10
- end
11
-
12
- @model = model
13
- @record = model.find(id) rescue raise(ResourceNotFound)
14
- @assoc = subresource_name
15
- @format = format
16
- @is_collection = [:has_many, :has_and_belongs_to_many].include? @model.reflect_on_association(@assoc.to_sym).macro
17
- @config = config
18
- @associate_model = assoc_model
19
- @associate_config_in = assoc_config_in
20
- @associate_config_out = assoc_config_out
21
- @params = params
22
- end
23
-
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
-
30
- reflection = @model.reflect_on_association(@assoc.to_sym)
31
-
32
- if reflection.collection?
33
-
34
-
35
- if(@config.pass_params_to.include?(@assoc) and
36
- reflection.options[:extend] and
37
- reflection.options[:extend].detect{|e| e.method_defined? :find_by_params} )
38
-
39
- result, pagination_info = paginate_query( @config, @assoc,
40
- @record.send(@assoc).find_by_params(@params), @params)
41
- else
42
-
43
- result, pagination_info = paginate_query( @config, @assoc,
44
- @record.send(@assoc), @params)
45
-
46
- end
47
-
48
- raise ResourceNotFound if result.nil?
49
-
50
- {
51
- :json => result.map{|r|
52
- r.represent( @associate_config_out.in_collection.exposed_attributes,
53
- @associate_config_out.in_collection.exposed_associations,
54
- @base_uri,
55
- @associate_config_out.media_type )
56
- },
57
- :status => :ok,
58
- :content_type => @associate_config_out.in_collection.media_type,
59
- :pagination_info => pagination_info
60
- }
61
-
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
-
75
- {
76
- :json => result.represent( @associate_config_out.exposed_attributes,
77
- @associate_config_out.exposed_associations,
78
- @base_uri,
79
- @associate_config_out.media_type),
80
- :status => :ok,
81
- :content_type => @associate_config_out.media_type
82
- }
83
- end
84
-
85
-
86
- rescue ActiveRecord::RecordNotFound
87
- raise ResourceNotFound
88
- end
89
-
90
- def put payload, media_type
91
- raise MethodNotAllowed
92
- end
93
-
94
- def post payload, media_type
95
- raise MethodNotAllowed unless @config.writables.include? @assoc
96
-
97
- if media_type != @associate_config_in.media_type
98
- raise UnsupportedMediaType
99
- end
100
-
101
- begin
102
- payload = ActiveSupport::JSON.decode(payload).with_indifferent_access
103
- rescue
104
- raise PayloadFormatError
105
- end
106
-
107
- unless payload.is_a? Hash
108
- raise PayloadFormatError
109
- end
110
-
111
- payload.delete_if {|key,value|
112
- !@associate_config_in.writables.include?(key) or
113
- @associate_config_in.exposed_associations.include?(key)
114
- }
115
-
116
- begin
117
- reflection = @model.reflect_on_association(@assoc.to_sym)
118
-
119
- if(@config.pass_params_to.include?(@assoc) and
120
- reflection.options[:extend] and
121
- reflection.options[:extend].detect{|e| e.method_defined? :create!})
122
-
123
- record = @record.send(@assoc).create! payload, @params
124
- else
125
- record = @record.send(@assoc).create! payload
126
- end
127
- {
128
- :json => record.represent( @associate_config_out.exposed_attributes,
129
- @associate_config_out.exposed_associations,
130
- @base_uri,
131
- @associate_config_out.media_type),
132
- :location => self.base_uri + record.uri_path,
133
- :status => :created,
134
- :content_type => @associate_config_out.media_type
135
- }
136
-
137
- rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, ActiveRecord::RecordNotUnique => e
138
- # model validation failed
139
- raise PayloadInvalid.new(e.message)
140
- end
141
- end
142
-
143
- def delete
144
- raise MethodNotAllowed
145
- end
146
-
147
- def link link_path_info
148
- slash, link_resource_name, link_id = link_path_info.split('/')
149
- link_model = Resource.get_class_by_resource_name(link_resource_name)
150
- link_record = link_model.find(link_id)
151
-
152
- reflection = @model.reflect_on_association(@assoc.to_sym)
153
-
154
- if(reflection.collection? )
155
- # has_many, hbtm
156
- if( @config.pass_params_to.include?(@assoc) and
157
- reflection.options[:extend] and
158
- reflection.options[:extend].detect{|e| e.method_defined? :<<})
159
-
160
- @record.send(@assoc).<<(link_record,@params)
161
- else
162
- @record.send(@assoc) << link_record
163
- end
164
- else
165
- # has_one, belongs_to
166
- @record.send(@assoc+"=",link_record)
167
- @record.save
168
- end
169
-
170
- {
171
- :nothing => true,
172
- :status => :no_content
173
- }
174
-
175
- rescue ActiveRecord::AssociationTypeMismatch
176
- raise Toast::ResourceNotAcceptable
177
- rescue ActiveRecord::RecordNotFound
178
- raise Toast::ResourceNotFound
179
- rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, ActiveRecord::RecordNotUnique => e
180
- # model validation failed (join model used for linking)
181
- raise PayloadInvalid.new(e.message)
182
- end
183
-
184
- def unlink link_path_info
185
-
186
- slash, link_resource_name, link_id = link_path_info.split('/')
187
- link_model = Resource.get_class_by_resource_name(link_resource_name)
188
- link_record = link_model.find(link_id)
189
-
190
- reflection = @model.reflect_on_association(@assoc.to_sym)
191
-
192
- if @model.reflect_on_association(@assoc.to_sym).collection?
193
- # has_many, hbtm
194
- if( @config.pass_params_to.include?(@assoc) and
195
- reflection.options[:extend] and
196
- reflection.options[:extend].detect{|e| e.method_defined? :delete})
197
-
198
- @record.send(@assoc).delete(link_record,@params)
199
- else
200
- @record.send(@assoc).delete(link_record) unless link_record.nil?
201
- end
202
- else
203
-
204
- # has_one, belongs_to
205
- if @record.send(@assoc) == link_record
206
- @record.send(@assoc+"=",nil)
207
- @record.save!
208
- end
209
- end
210
-
211
- {
212
- :nothing => true,
213
- :status => :no_content
214
- }
215
- rescue ActiveRecord::RecordNotFound
216
- raise ResourceNotFound
217
- end
218
- end
219
- end
@@ -1,139 +0,0 @@
1
- module Toast
2
- class Collection < Resource
3
-
4
- attr_reader :model
5
-
6
- def initialize model, subresource_name, params, config_in, config_out
7
-
8
- subresource_name ||= "all"
9
-
10
- unless config_out.collections.include? subresource_name
11
- if subresource_name == "all"
12
- ToastController.logger.debug "\n\tToast Debug: you may want to declare 'collections :all' in model '#{model}' to enable delivery of the collection '/#{model.to_s.underscore.pluralize}'\n"
13
- end
14
- raise ResourceNotFound
15
- end
16
-
17
- @model = model
18
- @collection = subresource_name
19
- @params = params
20
- @format = params[:format]
21
- @config_out = config_out
22
- @config_in = config_in
23
- end
24
-
25
- def get
26
-
27
- unless @model.respond_to?(@collection)
28
- raise "Toast Error: Cannot find class method '#{@collection}' of model '#{@model}', which is configured in 'acts_as_resource > collections'."
29
- end
30
-
31
- # FIXME: This is a lot of hallooballoo to check if the #send
32
- # will be successful, but if it's not checked the error
33
- # message is not helpful to find the error.
34
-
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
55
-
56
- case @format
57
- when "html"
58
- {
59
- :template => "resources/#{@model.to_s.pluralize.underscore}",
60
- :locals => { @model.to_s.pluralize.underscore.to_sym => records,
61
- :pagination_info => pagination_info }
62
- }
63
- when "json"
64
- {
65
- :json => records.map{|r|
66
- r.represent( @config_out.in_collection.exposed_attributes,
67
- @config_out.in_collection.exposed_associations,
68
- self.base_uri,
69
- @config_out.media_type)
70
- },
71
- :status => :ok,
72
- :content_type => @config_out.in_collection.media_type,
73
- :pagination_info => pagination_info
74
- }
75
- else
76
- raise ResourceNotFound
77
- end
78
- end
79
-
80
- def put
81
- raise MethodNotAllowed
82
- end
83
-
84
- def post payload, media_type
85
- raise MethodNotAllowed unless @config_in.postable?
86
-
87
-
88
- if media_type != @config_in.media_type
89
- raise UnsupportedMediaType
90
- end
91
-
92
- begin
93
- payload = ActiveSupport::JSON.decode(payload).with_indifferent_access
94
- rescue
95
- raise PayloadFormatError
96
- end
97
- unless payload.is_a? Hash
98
- raise PayloadFormatError
99
- end
100
-
101
- payload.delete_if {|key,value|
102
- !@config_in.writables.include?(key) or
103
- @config_in.exposed_associations.include?(key)
104
- }
105
-
106
- begin
107
-
108
- record = @model.create! payload
109
-
110
- {
111
- :json => record.represent( @config_out.exposed_attributes,
112
- @config_out.exposed_associations,
113
- self.base_uri,
114
- @config_out.media_type),
115
- :location => self.base_uri + record.uri_path,
116
- :status => :created,
117
- :content_type => @config_out.media_type
118
- }
119
-
120
- rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, ActiveRecord::RecordNotUnique => e
121
- # model validation failed
122
- raise PayloadInvalid.new(e.message)
123
- end
124
- end
125
-
126
- def delete
127
- raise MethodNotAllowed
128
- end
129
-
130
- def link l
131
- raise MethodNotAllowed
132
- end
133
-
134
- def unlink l
135
- raise MethodNotAllowed
136
- end
137
-
138
- end
139
- end
data/lib/toast/record.rb DELETED
@@ -1,123 +0,0 @@
1
- module Toast
2
- class Record < Resource
3
-
4
- attr_reader :model
5
-
6
- def initialize model, id, format, params, config_in, config_out
7
- @model = model
8
- @record = model.find(id) rescue raise(ResourceNotFound.new)
9
- @format = format
10
- @config_in = config_in
11
- @config_out = config_out
12
- @params = params
13
- end
14
-
15
- def post payload, media_type
16
- raise MethodNotAllowed
17
- end
18
-
19
- # get, put, delete, post return a Hash that can be used as
20
- # argument for ActionController#render
21
-
22
- def put payload, media_type
23
-
24
- if media_type != @config_in.media_type
25
- raise UnsupportedMediaType
26
- end
27
-
28
- begin
29
- payload = ActiveSupport::JSON.decode(payload).with_indifferent_access
30
- rescue
31
- raise PayloadFormatError
32
- end
33
-
34
- unless payload.is_a? Hash
35
- raise PayloadFormatError
36
- end
37
-
38
- # ignore all not explicitly writable attribute
39
- payload.delete_if {|key,value|
40
- !@config_in.writables.include?(key) or
41
- @config_in.exposed_associations.include?(key)
42
- }
43
-
44
- # set the virtual attributes
45
- (@config_in.writables - @record.attribute_names - @config_in.exposed_associations).each do |vattr|
46
-
47
- unless (@record.respond_to?("#{vattr}=") && @record.method("#{vattr}=").arity == 1)
48
- raise "Toast Error: Connot find setter '#{@record.class}##{vattr}='"
49
- end
50
-
51
- @record.send("#{vattr}=", payload.delete(vattr)) if payload.key?(vattr)
52
- end
53
-
54
- # mass-update for the rest
55
- if @config_in.pass_params_to.include? 'self'
56
- # parameter passing
57
- @record.update_attributes! payload, @params
58
- else
59
- @record.update_attributes! payload
60
- end
61
-
62
- {
63
- :json => @record.represent( @config_out.exposed_attributes,
64
- @config_out.exposed_associations,
65
- @base_uri,
66
- @config_out.media_type ),
67
- :status => :ok,
68
- :location => self.base_uri + @record.uri_path,
69
- :content_type => @config_out.media_type
70
- }
71
-
72
- rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, ActiveRecord::RecordNotUnique => e
73
- # model validation failed
74
- raise PayloadInvalid.new(e.message)
75
- end
76
-
77
- def get
78
- case @format
79
- when "html", "xml"
80
- {
81
- :template => "resources/#{model.to_s.underscore}",
82
- :locals => { model.to_s.underscore.to_sym => @record } # full record, view should filter
83
- }
84
- when "json"
85
- {
86
- :json => @record.represent( @config_out.exposed_attributes,
87
- @config_out.exposed_associations,
88
- @base_uri,
89
- @config_out.media_type),
90
- :status => :ok,
91
- :content_type => @config_out.media_type
92
- }
93
- else
94
- raise ResourceNotFound
95
- end
96
- end
97
-
98
- def delete
99
- raise MethodNotAllowed unless @config_out.deletable?
100
-
101
- if @record.destroy
102
- {
103
- :nothing => true,
104
- :status => :no_content
105
- }
106
- else
107
- {
108
- :nothing => true,
109
- :status => :conflict
110
- }
111
- end
112
-
113
- end
114
-
115
- def link l
116
- raise MethodNotAllowed
117
- end
118
-
119
- def unlink l
120
- raise MethodNotAllowed
121
- end
122
- end
123
- end