blix-rest 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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