toast 0.9.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,49 @@
1
+ class Toast::ConfigDSL::ViaVerb
2
+ include Toast::ConfigDSL::Common
3
+
4
+ def allow &block
5
+ stack_push 'allow' do
6
+
7
+ unless block.arity.in? [3, -2, -1]
8
+ raise_config_error 'Allow rule must list arguments as |*all|, |auth, *rest| or |auth, request_model, uri_params|'
9
+ end
10
+
11
+ @config_data.permissions << block
12
+ end
13
+ end
14
+
15
+ def handler &block
16
+ stack_push 'handler' do
17
+
18
+ # arity check for custom handlers
19
+ expected_arity = case Toast::ConfigDSL.stack[-3]
20
+ when /\Asingle/
21
+ 1
22
+ when /\Acollection/
23
+ case Toast::ConfigDSL.stack[-2]
24
+ when 'via_get' then 1
25
+ when 'via_post' then 2
26
+ end
27
+
28
+ when /\Aassociation/
29
+ case Toast::ConfigDSL.stack[-2]
30
+ when 'via_get' then 2
31
+ when /\Avia_(post|link|unlink)\z/ then 3
32
+ end
33
+
34
+ when /\Aexpose/
35
+ case Toast::ConfigDSL.stack[-2]
36
+ when /\Avia_(get|delete)\z/ then 2
37
+ when 'via_patch' then 3
38
+ end
39
+ end
40
+
41
+
42
+ if block.arity != expected_arity
43
+ raise_config_error "Handler block must take exactly #{expected_arity} argument#{expected_arity==1?'':'s'}"
44
+ end
45
+
46
+ @config_data.handler = block
47
+ end
48
+ end
49
+ end
@@ -1,233 +1,68 @@
1
1
  module Toast
2
2
  module ConfigDSL
3
3
 
4
- class Base
5
- include Blockenspiel::DSL
6
- dsl_attr_accessor :namespace, :media_type
7
-
8
- def initialize model
9
- @model = model
10
- @readables = []
11
- @writables = []
12
- @field_comments = {}
13
- @collections = []
14
- @singles = []
15
- @deletable = false
16
- @postable = false
17
- @pass_params_to = []
18
- @in_collection = ConfigDSL::InCollection.new model, self
19
- @media_type = "application/json"
20
- @apidoc = {}
21
- @paginations = {}
22
- @model.attr_accessible []
23
- end
24
-
25
- def exposed_attributes
26
- assocs = @model.reflect_on_all_associations.map{|a| a.name.to_s}
27
- (@writables + @readables).uniq.select{|f| !assocs.include?(f)}
28
- end
29
-
30
- def exposed_associations
31
- assocs = @model.reflect_on_all_associations.map{|a| a.name.to_s}
32
- (@writables + @readables).uniq.select{|f| assocs.include?(f)}
33
- end
34
-
35
- def readables= arg
36
- @field_comments.merge! ConfigDSL.get_comments(arg, 'ro')
37
- @readables.push *ConfigDSL.normalize(arg,"readables")
38
- end
39
-
40
- # args: Array or :all, :except => Array
41
- def readables *arg
42
- return(@readables) if arg.empty?
43
- self.readables = arg
44
- end
45
-
46
- def writables= arg
47
- @field_comments.merge! ConfigDSL.get_comments(arg, 'rw')
48
- @writables.push *ConfigDSL.normalize(arg,"writables")
49
- @model.attr_accessible *@writables
50
- end
51
-
52
- # args: Array or :all, :except => Array
53
- def writables *arg
54
- return(@writables) if arg.empty?
55
- self.writables = arg
56
- end
57
-
58
- def deletable
59
- @deletable = true
60
- end
61
-
62
- def deletable?
63
- @deletable
64
- end
65
-
66
- def postable
67
- @postable = true
68
- end
69
-
70
- def postable?
71
- @postable
72
- end
73
-
74
- def pass_params_to= arg
75
- @pass_params_to.push *ConfigDSL.normalize(arg,"pass_params_to")
76
- end
77
-
78
- def pass_params_to *arg
79
- return(@pass_params_to) if arg.empty?
80
- self.pass_params_to = arg
81
- end
82
-
83
- def collections= collections=[]
84
- @collections = ConfigDSL.normalize(collections, "collections")
85
- end
86
-
87
- def collections *arg
88
- return(@collections) if arg.empty?
89
- self.collections = arg
90
- end
91
-
92
- def singles= singles=[]
93
- @singles = ConfigDSL.normalize(singles, "singles")
94
- end
95
-
96
- def singles *arg
97
- return(@singles) if arg.empty?
98
- self.singles = arg
99
- end
100
-
101
- def in_collection &block
102
- if block_given?
103
- Blockenspiel.invoke( block, @in_collection)
104
- else
105
- @in_collection
106
- end
107
- end
108
-
109
- def apidoc arg=nil
110
- return(@apidoc) if arg.nil?
111
- raise "Toast Config Error (#{model.class}): apidoc argument must be a Hash." unless arg.is_a?(Hash)
112
- @apidoc = arg
113
- end
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
-
121
- # non DSL methods
122
- dsl_methods false
123
-
124
- def field_comments
125
- @field_comments
126
- end
127
-
128
- def paginations
129
- @paginations
130
- end
131
-
4
+ # the currently process config file name
5
+ mattr_accessor :cfg_name
6
+
7
+ # traces the descent into the configuration, while gathering config items
8
+ mattr_accessor :stack
9
+
10
+ # Interprets one config file and stores config info in the @expositions Array using OpenStruct.
11
+ #
12
+ # Params:
13
+ # +config+:: [String] The configuration file's content as string
14
+ # +fname+:: [String] The file name of the config file with path relative to +Rails.root+
15
+ #
16
+ # Return: +nil+
17
+ def self.get_config _toast_config, _toast_cfg_name
18
+
19
+ # using _toast_ prefix for all locals here, because they end up in the
20
+ # handler/allow block scope (closure)
21
+ # also freeze them to prevent accidental changes
22
+ # is there a way to remove closure vars with instance_eval?
23
+
24
+ _toast_config.freeze
25
+ _toast_cfg_name.freeze
26
+
27
+ @@cfg_name = _toast_cfg_name
28
+ @@stack = []
29
+
30
+ _toast_base = ConfigDSL::Base.new
31
+ _toast_base.freeze
32
+ _toast_base.instance_eval(_toast_config, _toast_cfg_name)
33
+
34
+ rescue ArgumentError, NameError => _toast_error
35
+ _toast_base.raise_config_error _toast_error.message
132
36
  end
133
37
 
134
- class InCollection
135
- include Blockenspiel::DSL
136
-
137
- dsl_attr_accessor :media_type
138
-
139
- def initialize model, base_config
140
- @model = model
141
- @media_type = "application/json"
142
- @readables = base_config.readables # must assign a reference
143
- @writables = base_config.writables # must assign a reference
144
- end
145
-
146
- def readables= readables
147
- @writables = [] # forget inherited writables
148
- @readables = ConfigDSL.normalize(readables,"readables")
149
- end
150
-
151
- def readables *arg
152
- return(@readables) if arg.empty?
153
- self.readables = arg
154
- end
155
-
156
- def writables *arg
157
- self.writables = 42
158
- end
159
-
160
- def writables= arg
161
- puts
162
- puts "Toast Config Warning (#{@model.class}): Defining \"writables\" in collection definition has no effect."
163
- puts
164
- end
165
-
166
- def namespace *arg
167
- self.writables = 42
168
- end
169
-
170
- def namespace= arg
171
- puts
172
- puts "Toast Config Warning (#{@model.class}): Defining \"namespace\" in collection definition has no effect."
173
- puts
174
- end
175
-
176
- def exposed_attributes
177
- assocs = @model.reflect_on_all_associations.map{|a| a.name.to_s}
178
- (@readables + @writables).uniq.select{|f| !assocs.include?(f)}
179
- end
180
-
181
- def exposed_associations
182
- assocs = @model.reflect_on_all_associations.map{|a| a.name.to_s}
183
- (@readables + @writables).uniq.select{|f| assocs.include?(f)}
184
- end
185
-
186
- end # class InCollection
187
-
188
- # Helper
189
-
190
- # checks if list is made of symbols and strings
191
- # converts a single value to an Array
192
- # converts all symbols to strings
193
- def self.normalize list, name
194
- # make single element list
195
- list = [list] unless list.is_a? Array
196
-
197
- # remove all comments
198
- list = list.map{|x| x.is_a?(Array) ? x.first : x}
199
-
200
- # flatten
201
- list = [list].flatten
202
-
203
- # check class and stringify
204
- list.map do |x|
205
- if (!x.is_a?(Symbol) && !x.is_a?(String))
206
- raise "Toast Config Error: '#{name}' should be a list of Symbols"
207
- else
208
- x.to_s
209
- end
210
- end
211
- end
212
-
213
- # fields (readables and writables) can have comments , if passed by Arrays of the form:
214
- # [symbol, comment]
215
- # returns a Hash of all commented or uncommented fields as:
216
- # { FIELD_NAME => {:comment => COMMENT, :type => type } }
217
- def self.get_comments arg, access
218
- comments = {}
219
-
220
- if arg.is_a? Array
221
- arg.each do |f|
222
- if f.is_a? Array
223
- comments[ f.first.to_s ] = {:comment => f[1].to_s, :access => access, :type => f[2]}
224
- else
225
- comments[ f.to_s ] = {:comment => '[ no comment ]', :access => access, :type => nil}
226
- end
227
- end
228
- end
229
-
230
- comments
38
+ # Interprets the global settings file and stores data using OpenStruct
39
+ # Params:
40
+ # +config+:: [String] The configuration file's content as string
41
+ # +fname+:: [String] The file name of the config file with path relative to +Rails.root+
42
+ #
43
+ # Return: +nil+
44
+ def self.get_settings config, cfg_name
45
+ @@cfg_name = cfg_name
46
+ @@stack = []
47
+
48
+ # defaults
49
+ Toast.settings = OpenStruct.new
50
+ Toast.settings.max_window = 42
51
+ Toast.settings.link_unlink_via_post = false
52
+ Toast.settings.authenticate = lambda{|r| r}
53
+
54
+ settings = ConfigDSL::Settings.new
55
+ settings.instance_eval(config, cfg_name)
56
+
57
+ rescue ArgumentError, NameError => error
58
+ settings.raise_config_error error.message
231
59
  end
232
60
  end
233
61
  end
62
+
63
+ require 'toast/config_dsl/common'
64
+ require 'toast/config_dsl/base'
65
+ require 'toast/config_dsl/expose'
66
+ require 'toast/config_dsl/association'
67
+ require 'toast/config_dsl/via_verb'
68
+ require 'toast/config_dsl/settings'
data/lib/toast/engine.rb CHANGED
@@ -1,45 +1,34 @@
1
- require 'toast/active_record_extensions.rb'
2
- require 'toast/resource.rb'
3
- require 'toast/collection'
4
- require 'toast/association'
5
- require 'toast/record'
6
- require 'toast/single'
7
-
8
- require 'action_dispatch/http/request'
9
- require 'rack/accept_media_types'
10
-
11
1
  module Toast
12
2
  class Engine < Rails::Engine
13
3
 
14
4
  # configure our plugin on boot. other extension points such
15
5
  # as configuration, rake tasks, etc, are also available
16
6
  initializer "toast.initialize" do |app|
17
- # Add 'acts_as_resource' declaration to ActiveRecord::Base
18
- ActiveRecord::Base.extend Toast::ActiveRecordExtensions
19
-
20
- # Load all models in app/models early to setup routing
21
- begin
22
- Dir["#{Rails.root}/app/models/**/*.rb"].each{|m| require_dependency m }
23
7
 
24
- rescue
25
- # raised when DB is not setup yet. (rake db:schema:load)
8
+ # allow LINK and UNLINK methods
9
+ if ActionDispatch::Request::HTTP_METHOD_LOOKUP['LINK'] == :link and
10
+ ActionDispatch::Request::HTTP_METHOD_LOOKUP['UNLINK'] == :unlink
11
+ Toast.info "INFO: LINK and UNLINK allowed by this Rails version: #{Rails.version}"
12
+ else
13
+ ActionDispatch::Request::HTTP_METHOD_LOOKUP['LINK'] = :link
14
+ ActionDispatch::Request::HTTP_METHOD_LOOKUP['UNLINK'] = :unlink
26
15
  end
27
16
 
28
- # Monkey patch the request class for Rails 3.0, Rack 1.2
29
- # Backport from Rack 1.3
30
- if Rack.release == "1.2"
31
- class Rack::Request
32
- def base_url
33
- url = scheme + "://"
34
- url << host
17
+ # for LINK/UNLINK via POST and x-http-method-override header
18
+ app.middleware.unshift Rack::MethodOverride
35
19
 
36
- if scheme == "https" && port != 443 ||
37
- scheme == "http" && port != 80
38
- url << ":#{port}"
39
- end
20
+ # skip init if in test mode: Toast.init should be called in each test
21
+ unless Rails.env == 'test'
22
+ begin
23
+ Toast.info 'Loading Toast'
24
+ Toast.init
25
+ Toast.info "Exposed model classes: #{Toast.expositions.map{|e| e.model_class.name}.join(' ')}"
40
26
 
41
- url
27
+ rescue Toast::ConfigError => error
28
+ error.message.split("\n").each do |line|
29
+ Toast.info line
42
30
  end
31
+ Toast.disable
43
32
  end
44
33
  end
45
34
  end
@@ -0,0 +1,41 @@
1
+ module Toast::Errors
2
+ class ConfigNotFound < StandardError; end
3
+
4
+ class HandlerError < StandardError
5
+ attr_accessor :orig_error, :source_location
6
+
7
+ def initialize orig_error, source_location
8
+ @orig_error = orig_error
9
+ @source_location = source_location
10
+ super ''
11
+ end
12
+ end
13
+
14
+ class AllowError < HandlerError; end
15
+
16
+ class NotAllowed < StandardError
17
+ attr_accessor :source_location
18
+
19
+ def initialize source_location
20
+ @source_location = source_location
21
+ super ''
22
+ end
23
+ end
24
+
25
+ class BadRequest < StandardError
26
+ attr_accessor :source_location
27
+
28
+ def initialize message, source_location
29
+ @source_location = source_location
30
+ super message
31
+ end
32
+ end
33
+
34
+ class CustomAuthFailure < StandardError
35
+ attr_accessor :response_data
36
+
37
+ def initialize response_data
38
+ @response_data = response_data
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,17 @@
1
+ class Toast::HttpRange
2
+ attr_reader :start, :end, :size
3
+
4
+ def initialize range
5
+ if range.nil?
6
+ @start = 0
7
+ @end = nil
8
+ @size = nil
9
+ else
10
+ range =~ /\Aitems=(\d*)-(\d*)/
11
+ @start = Integer($1) rescue 0
12
+ @end = Integer($2) rescue nil
13
+ @end = nil if (@end and (@end < @start))
14
+ @size = @end - @start + 1 rescue nil
15
+ end
16
+ end
17
+ end