blix-rest 0.1.30

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,50 @@
1
+ module Blix::Rest
2
+
3
+ module ResourceCache
4
+
5
+ private
6
+
7
+ def _cache_hash
8
+ @_cache ||= {}
9
+ end
10
+
11
+ def _cache_get(*args,&block)
12
+ field = args[0].to_s
13
+ if block && args.length == 1
14
+ if _cache?(field)
15
+ _cache_hash[field]
16
+ else
17
+ _cache_hash[field]= block.call
18
+ end
19
+ elsif args.length == 1
20
+ _cache_hash[field]
21
+ elsif args.length == 2
22
+ if _cache?(field)
23
+ _cache_hash[field]
24
+ else
25
+ _cache_hash[field]= args[1]
26
+ end
27
+ else
28
+ raise "wrong number of arguments:#{args.length} for 1 or 2"
29
+ end
30
+ end
31
+
32
+ def _cache_set(field,val)
33
+ _cache_hash[field.to_s] = val
34
+ end
35
+
36
+ def _cache_reset(field)
37
+ _cache_hash.delete field.to_s
38
+ end
39
+
40
+ def _cache(field)
41
+ _cache_hash[field.to_s]
42
+ end
43
+
44
+ def _cache?(field)
45
+ _cache_hash.key?(field.to_s)
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,26 @@
1
+ # pass a response object to the controller to set
2
+ # header status and content.
3
+
4
+ module Blix::Rest
5
+
6
+
7
+ class Response
8
+
9
+ attr_accessor :status
10
+ attr_reader :headers
11
+ attr_accessor :content
12
+
13
+ def initialize
14
+ @status = 200
15
+ @headers = {}
16
+ @content = nil
17
+ end
18
+
19
+ def set(status,content=nil,headers=nil)
20
+ @status = status if status
21
+ @content = String.new(content) if content
22
+ @headers.merge!(headers) if headers
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blix::Rest
4
+ class Server
5
+
6
+ def initialize(opts = {})
7
+ @_parsers = {}
8
+ @_mime_types = {}
9
+
10
+ # register the default parsers and any passed in as options.
11
+
12
+ register_parser('html', HtmlFormatParser.new)
13
+ register_parser('json', JsonFormatParser.new)
14
+ register_parser('xml', XmlFormatParser.new)
15
+ register_parser('raw', RawFormatParser.new)
16
+ extract_parsers_from_options(opts)
17
+ @_options = opts
18
+ end
19
+
20
+ def _cache
21
+ @_cache ||= {}
22
+ end
23
+
24
+ def extract_parsers_from_options(opts)
25
+ opts.each do |k, v|
26
+ next unless k =~ /^(\w*)_parser&/
27
+
28
+ format = Regexp.last_match(1)
29
+ parser = v
30
+ register_parser(format, parser)
31
+ end
32
+ end
33
+
34
+ def set_custom_headers(format, headers)
35
+ parser = get_parser(format)
36
+ raise "parser not found for custom headers format=>#{format}" unless parser
37
+
38
+ parser.__custom_headers = headers
39
+ end
40
+
41
+ def get_parser(format)
42
+ @_parsers[format.to_s]
43
+ end
44
+
45
+ def get_parser_from_type(type)
46
+ @_mime_types[type.downcase]
47
+ end
48
+
49
+ def register_parser(format, parser)
50
+ raise "#{k} must be an object with parent class Blix::Rest::FormatParser" unless parser.is_a?(FormatParser)
51
+
52
+ parser._format = format
53
+ @_parsers[format.to_s.downcase] = parser
54
+ parser._types.each { |t| @_mime_types[t.downcase] = parser } # register each of the mime types
55
+ end
56
+
57
+ def retrieve_params(env)
58
+ post_params = {}
59
+ body = ''
60
+ params = env['params'] || {}
61
+ params.merge!(::Rack::Utils.parse_nested_query(env['QUERY_STRING']))
62
+
63
+ if env['rack.input']
64
+ post_params = ::Rack::Utils::Multipart.parse_multipart(env)
65
+ unless post_params
66
+ body = env['rack.input'].read
67
+ env['rack.input'].rewind
68
+
69
+ if body.empty?
70
+ post_params = {}
71
+ else
72
+ begin
73
+ post_params = case (env['CONTENT_TYPE'])
74
+ when URL_ENCODED
75
+ ::Rack::Utils.parse_nested_query(body)
76
+ when JSON_ENCODED then
77
+ json = MultiJson.load(body)
78
+ if json.is_a?(Hash)
79
+ json
80
+ else
81
+ { '_json' => json }
82
+ end
83
+ else
84
+ {}
85
+ end
86
+ rescue StandardError => e
87
+ raise BadRequestError, "Invalid parameters: #{e.class}"
88
+ end
89
+ end
90
+ end
91
+ end
92
+ [params, post_params, body]
93
+ end
94
+
95
+ # accept header can have multiple entries. match on regexp
96
+ # can look like this
97
+ # text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 !!!!!
98
+
99
+ def get_format(env)
100
+ case env['HTTP_ACCEPT']
101
+ when JSON_ENCODED then :json
102
+ when HTML_ENCODED then :html
103
+ when XML_ENCODED then :xml
104
+ end
105
+ end
106
+
107
+ def get_format_from_mime(mime)
108
+ case mime
109
+ when 'application/json' then :json
110
+ when 'text/html' then :html
111
+ when 'application/xml' then :xml
112
+ when 'application/xhtml+xml' then :xhtml
113
+ when '*/*' then :*
114
+ end
115
+ end
116
+
117
+ # attempt to handle mjltiple accept formats here..
118
+ # mime can include '.../*' and '*/*'
119
+ # FIXME
120
+ def get_format_new(env, options)
121
+ accept = options && options[:accept] || :json
122
+ accept = [accept].flatten
123
+
124
+ requested = env['HTTP_ACCEPT'].to_s.split(',')
125
+ requested.each do |request|
126
+ parts = request.split(';') # the quality part is after a ;
127
+ mime = parts[0].strip # the mime type
128
+ try = get_format_from_mime(mime)
129
+ next unless try
130
+ return accept[0] || :json if try == :*
131
+ return try if accept.include?(try)
132
+ end
133
+ nil # no match found
134
+ end
135
+
136
+ # convert the response to the appropriate format
137
+ def format_error(_message, _format)
138
+ parser
139
+ end
140
+
141
+ def call(env)
142
+ req = Rack::Request.new(env)
143
+
144
+ verb = env['REQUEST_METHOD']
145
+ path = req.path
146
+
147
+ blk, path_params, options = RequestMapper.match(verb, path)
148
+
149
+ blk, path_params, options = RequestMapper.match('ALL', path) unless blk
150
+
151
+ default_format = options && options[:default] && options[:default].to_sym
152
+ force_format = options && options[:force] && options[:force].to_sym
153
+ do_cache = options && options[:cache]
154
+ clear_cache = options && options[:cache_reset]
155
+
156
+ query_format = options && options[:query] && req.GET['format'] && req.GET['format'].to_sym
157
+
158
+ format = query_format || path_params[:format] || get_format_new(env, options) || default_format || :json
159
+
160
+ parser = get_parser(force_format || format)
161
+
162
+ return [406, {}, ["Invalid Format: #{format}"]] unless parser
163
+
164
+ parser._options = options
165
+
166
+ # check for cached response end return with cached response if found.
167
+ #
168
+ if do_cache && _cache["#{verb}|#{format}|#{path}"]
169
+ response = _cache["#{verb}|#{format}|#{path}"]
170
+ return [response.status, response.headers.merge('X-Blix-Cache' => 'cached'), [response.content]]
171
+ end
172
+
173
+ response = Response.new
174
+
175
+ if parser.__custom_headers
176
+ response.headers.merge! parser.__custom_headers
177
+ else
178
+ parser.set_default_headers(response.headers)
179
+ end
180
+
181
+ if blk
182
+
183
+ begin
184
+ params = env['params']
185
+ value = blk.call(path_params, params, req, format, response, @_options)
186
+ rescue ServiceError => e
187
+ response.set(e.status, parser.format_error(e.message), e.headers)
188
+ rescue AuthorizationError => e
189
+ response.set(401, parser.format_error(e.message), AUTH_HEADER => "#{e.type} realm=\"#{e.realm}\", charset=\"UTF-8\"")
190
+ rescue Exception => e
191
+ response.set(500, parser.format_error('internal error'))
192
+ ::Blix::Rest.logger << "----------------------------\n#{$!}\n----------------------------"
193
+ ::Blix::Rest.logger << "----------------------------\n#{$@}\n----------------------------"
194
+ else # no error
195
+ parser.format_response(value, response)
196
+ # cache response if requested
197
+ _cache.clear if clear_cache
198
+ _cache["#{verb}|#{format}|#{path}"] = response if do_cache
199
+ end
200
+
201
+ else
202
+ response.set(404, parser.format_error('Invalid Url'))
203
+ end
204
+ [response.status, response.headers, [response.content]]
205
+ end
206
+
207
+ end
208
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ unless Hash.method_defined? :transform_keys
4
+ class Hash
5
+ # Returns a new hash with all keys converted using the block operation.
6
+ #
7
+ # hash = { name: 'Rob', age: '28' }
8
+ #
9
+ # hash.transform_keys{ |key| key.to_s.upcase }
10
+ # # => {"NAME"=>"Rob", "AGE"=>"28"}
11
+ def transform_keys
12
+ result = {}
13
+ each_key do |key|
14
+ result[yield(key)] = self[key]
15
+ end
16
+ result
17
+ end
18
+ end
19
+ end
20
+
21
+ module Blix::Rest
22
+ # indifferent hash for symbols or string keys.
23
+ # stores keys as a string
24
+
25
+ class StringHash < Hash
26
+
27
+ alias_method :parent_merge!, :merge!
28
+
29
+ # initialize without conversion. params must be in
30
+ # string key format.
31
+ def initialize(*params)
32
+ super()
33
+ parent_merge!(*params) unless params.empty?
34
+ end
35
+
36
+ # create with conversion
37
+ def self.create(params)
38
+ h = new
39
+ h.merge(params)
40
+ h
41
+ end
42
+
43
+ def [](k)
44
+ super(k.to_s)
45
+ end
46
+
47
+ def get(k, default = nil)
48
+ if key?(k.to_s)
49
+ self[k]
50
+ else
51
+ default
52
+ end
53
+ end
54
+
55
+ def merge(*params)
56
+ super(* params.map { |h| h.transform_keys(&:to_s) })
57
+ end
58
+
59
+ def merge!(*params)
60
+ super(* params.map { |h| h.transform_keys(&:to_s) })
61
+ end
62
+
63
+ def replace(h)
64
+ super(h.transform_keys(&:to_s))
65
+ end
66
+
67
+ def has_key?(key)
68
+ super(key.to_s)
69
+ end
70
+
71
+ def member?(key)
72
+ super(key.to_s)
73
+ end
74
+
75
+ def store(key, value)
76
+ super(key.to_s, value)
77
+ end
78
+
79
+ def key(key)
80
+ super(key.to_s)
81
+ end
82
+
83
+ def key?(key)
84
+ super(key.to_s)
85
+ end
86
+
87
+ def []=(k, v)
88
+ super(k.to_s, v)
89
+ end
90
+
91
+ def include?(k)
92
+ super(k.to_s)
93
+ end
94
+
95
+ def delete(k)
96
+ super(k.to_s)
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,5 @@
1
+ module Blix
2
+ module Rest
3
+ VERSION = "0.1.30"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require_relative 'utils/misc'
2
+ require_relative 'utils/yaml_config'
@@ -0,0 +1,62 @@
1
+
2
+
3
+ module Blix
4
+
5
+ def self.require_dir(path)
6
+ raise "invalid dir path:#{path}" unless File.directory?(path)
7
+ Dir.glob("#{path}/*.rb").each {|file| require File.expand_path(file)[0..-4] }
8
+ end
9
+
10
+
11
+
12
+ # filter the hash using the supplied filter
13
+ #
14
+ # the filter is an array of keys that are permitted
15
+ # returns a hash containing only the permitted keys and values
16
+ def self.filter_hash(filter,hash)
17
+ hash = hash || {}
18
+ hash.select {|key, value| filter.include?(key.to_sym) || filter.include?(key.to_s)}
19
+ end
20
+
21
+ # used to raise errors on
22
+ module DatamapperExceptions
23
+ def save(*args)
24
+ raise ServiceError, errors.full_messages.join(',') unless super
25
+ self
26
+ end
27
+
28
+ def update(*args)
29
+ raise ServiceError, errors.full_messages.join(',') unless super
30
+ self
31
+ end
32
+
33
+ def destroy(*args)
34
+ raise ServiceError, errors.full_messages.join(',') unless super
35
+ true
36
+ end
37
+
38
+ module ClassMethods
39
+
40
+ end
41
+
42
+ def self.included(mod)
43
+ mod.extend DatamapperExceptions::ClassMethods
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ class String
51
+
52
+ # try to convert utf special characters to normal characters.
53
+ def to_ascii
54
+ unicode_normalize(:nfd).gsub(/[\u0300-\u036f]/, "")
55
+ end
56
+
57
+ # standardize utf strings
58
+ def normalize
59
+ unicode_normalize(:nfc)
60
+ end
61
+
62
+ end