api_recipes 1.0.0 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc2b26e8c66139a2ff051ef066bb512b389a48bcbfa2ba2b391f1dc29719426d
4
- data.tar.gz: 281afb372a91edac6bbc1bc6335da2fb413e856ed7f32cff476f9c527d4dc459
3
+ metadata.gz: 8ac0841819d455adb4d85f88d7858e33cfaa17bf35316811f5a6d7c75879b271
4
+ data.tar.gz: ddacdca3095b42cdc14c51e066a52d237c85dfc0ba79419342168d7667f0b12e
5
5
  SHA512:
6
- metadata.gz: 610ff709c864f8cb71f29f42a0a50cd75318e0ce184801f8aa40102d35301b937afdd1c3a56381f783e405763aad2ac4886205809c2b26a07d73a9433e368c81
7
- data.tar.gz: a3a0cd7ac6a1b7aaea7fe3f5989d4e1c7b071f6d7b18ffc99b4fbff56689114407310f37e459cee183a56b78529f51fe39b59712b4dc5cbd31577fc7e451b2b7
6
+ metadata.gz: ec9f37f976b33f9386b4f201f4200ce33fdc526510f31984b591823c093c187037c7c8fc6c967fd33094b41f5343b4810ae251a89d3a9922e55b565f7f648503
7
+ data.tar.gz: 4c2f41152de95562744e8b60e8d14ca4adbbd4a88487d89f52149ba9bfd6b9bff3bf830bedc1aee9c59787e541ad5386d151baea552b11e7c63a8eac1e795b85
File without changes
data/README.md CHANGED
@@ -28,7 +28,7 @@ Or install it yourself as:
28
28
 
29
29
  ## Compatibility
30
30
 
31
- Ruby 1.9.3 or higher.
31
+ Ruby 2.5.0 or higher.
32
32
 
33
33
  ## Usage
34
34
 
@@ -38,7 +38,7 @@ In the meantime take a look at the ``` examples ``` folder. Thank you.
38
38
 
39
39
  ## TODO
40
40
 
41
- * Write specs (I know... this code has been wrote down on a hurry so please do not beat me)
41
+ * Re-write specs
42
42
  * Write a good Usage
43
43
  * Add more examples
44
44
  * Add documentation
@@ -3,56 +3,57 @@ require 'http'
3
3
  require 'api_recipes/utils'
4
4
  require 'api_recipes/exceptions'
5
5
  require 'api_recipes/configuration'
6
- require 'api_recipes/resource'
6
+ require 'api_recipes/route'
7
7
  require 'api_recipes/endpoint'
8
+ require 'api_recipes/api'
8
9
  require 'api_recipes/response'
9
10
  require 'api_recipes/settings'
10
11
 
12
+ # TODO: Sistema i default nelle config
13
+
11
14
  module ApiRecipes
12
15
 
13
16
  def self.included(receiver)
14
17
 
15
- def receiver.endpoint(endpoint_name, configs = {})
16
- unless endpoint_name.is_a?(String) || endpoint_name.is_a?(Symbol)
17
- raise ArgumentError, "endpoint name must be a Symbol or String"
18
+ def receiver.api(api_name, configs = {})
19
+ unless api_name.is_a?(String) || api_name.is_a?(Symbol)
20
+ raise ArgumentError, "api name must be a Symbol or String"
18
21
  end
19
22
 
20
23
  if configs && !configs.is_a?(Hash)
21
- raise ApiRecipes::EndpointConfigIsNotAnHash.new(endpoint_name)
24
+ raise ApiRecipes::ApiConfigIsNotAnHash.new(api_name)
22
25
  end
23
26
 
24
- endpoint_name = endpoint_name.to_sym
25
- configs = ApiRecipes._aprcps_merge_endpoints_configs(endpoint_name, configs.deep_symbolize_keys)
26
- if self.respond_to? endpoint_name
27
- raise EndpointNameClashError.new(self, endpoint_name)
27
+ api_name = api_name.to_sym
28
+ # configs = ApiRecipes._aprcps_merge_apis_configs(api_name, configs.deep_symbolize_keys)
29
+ if self.respond_to? api_name
30
+ raise ApiNameClashError.new(self, api_name)
28
31
  else
29
- ep = Endpoint.new(endpoint_name, configs)
30
- ApiRecipes.copy_global_authorizations_to_endpoint ep
31
- ApiRecipes._aprcps_thread_storage[endpoint_name] = {}
32
- ApiRecipes._aprcps_thread_storage[endpoint_name][self] = ep
33
-
34
- define_method endpoint_name do
35
- unless ApiRecipes._aprcps_thread_storage[endpoint_name]
36
- ApiRecipes._aprcps_thread_storage[endpoint_name] = {}
37
- end
38
- unless ApiRecipes._aprcps_thread_storage[endpoint_name][self.class]
39
- ApiRecipes._aprcps_thread_storage[endpoint_name][self.class] = ep.clone
40
- end
41
- ApiRecipes._aprcps_thread_storage[endpoint_name][self.class]
32
+
33
+ define_method api_name do
34
+ configs = ApiRecipes._aprcps_merge_apis_configs(api_name, configs.deep_symbolize_keys)
35
+ api = Api.new(api_name, configs, self)
36
+ ApiRecipes.copy_global_authorizations_to_api api
37
+ api
42
38
  end
43
- define_singleton_method endpoint_name do
44
- unless ApiRecipes._aprcps_thread_storage[endpoint_name]
45
- ApiRecipes._aprcps_thread_storage[endpoint_name] = {}
46
- end
47
- unless ApiRecipes._aprcps_thread_storage[endpoint_name][self]
48
- ApiRecipes._aprcps_thread_storage[endpoint_name][self] = ep.clone
49
- end
50
- ApiRecipes._aprcps_thread_storage[endpoint_name][self]
39
+ define_singleton_method api_name do
40
+ configs = ApiRecipes._aprcps_merge_apis_configs(api_name, configs.deep_symbolize_keys)
41
+ api = Api.new(api_name, configs, self)
42
+ ApiRecipes.copy_global_authorizations_to_api api
43
+ api
51
44
  end
52
45
  end
53
46
  end
54
47
  end
55
48
 
49
+ def self.print_urls=(value)
50
+ @print_urls = value
51
+ end
52
+
53
+ def self.print_urls
54
+ @print_urls || false
55
+ end
56
+
56
57
  def self.configuration
57
58
  unless @configuration
58
59
  @configuration = Configuration.new
@@ -68,45 +69,45 @@ module ApiRecipes
68
69
  end
69
70
  end
70
71
 
71
- def self.copy_global_authorizations_to_endpoint(endpoint)
72
- if _aprcps_global_storage[endpoint.name]
73
- if auth = _aprcps_global_storage[endpoint.name].basic_auth
74
- endpoint.authorization = auth
72
+ def self.copy_global_authorizations_to_api(api)
73
+ if _aprcps_global_storage[api.name]
74
+ if auth = _aprcps_global_storage[api.name].basic_auth
75
+ api.basic_auth = auth
75
76
  end
76
- if auth = _aprcps_global_storage[endpoint.name].authorization
77
- endpoint.authorization = auth
77
+ if auth = _aprcps_global_storage[api.name].authorization
78
+ api.authorization = auth
78
79
  end
79
80
  end
80
81
  end
81
82
 
82
- def self.set_authorization_for_endpoint(authorization, endpoint_name)
83
- endpoint_name = endpoint_name.to_sym
83
+ def self.set_authorization_for_api(authorization, api_name)
84
+ api_name = api_name.to_sym
84
85
 
85
86
  # Set authorization on thread storage
86
- if _aprcps_thread_storage[endpoint_name]
87
- _aprcps_thread_storage[endpoint_name].each do |_, endpoint|
88
- endpoint.authorization = authorization
87
+ if _aprcps_thread_storage[api_name]
88
+ _aprcps_thread_storage[api_name].each do |_, api|
89
+ api.authorization = authorization
89
90
  end
90
91
  end
91
92
  end
92
93
 
93
- def self.set_basic_auth_for_endpoint(basic_auth, endpoint_name)
94
- endpoint_name = endpoint_name.to_sym
94
+ def self.set_basic_auth_for_api(basic_auth, api_name)
95
+ api_name = api_name.to_sym
95
96
 
96
97
  # Set authorization on thread storage
97
- if _aprcps_thread_storage[endpoint_name]
98
- _aprcps_thread_storage[endpoint_name].each do |_, endpoint|
99
- endpoint.authorization = basic_auth
98
+ if _aprcps_thread_storage[api_name]
99
+ _aprcps_thread_storage[api_name].each do |_, api|
100
+ api.authorization = basic_auth
100
101
  end
101
102
  end
102
103
  end
103
104
 
104
- def self._aprcps_define_global_endpoints
105
- configuration.endpoints_configs.each do |endpoint_name, endpoint_configs|
106
- endpoint_name = endpoint_name.to_sym
107
- _aprcps_global_storage[endpoint_name] = Endpoint.new endpoint_name, endpoint_configs
108
- define_singleton_method endpoint_name do
109
- _aprcps_global_storage[endpoint_name]
105
+ def self._aprcps_define_global_apis
106
+ configuration.apis_configs.each do |api_name, api_configs|
107
+ api_name = api_name.to_sym
108
+ _aprcps_global_storage[api_name] = Api.new api_name, api_configs, self
109
+ define_singleton_method api_name do
110
+ _aprcps_global_storage[api_name]
110
111
  end
111
112
  end
112
113
  end
@@ -125,15 +126,15 @@ module ApiRecipes
125
126
  Thread.current[:api_recipes]
126
127
  end
127
128
 
128
- def self._aprcps_merge_endpoints_configs(endpoint_name, configs = nil)
129
- unless endpoint_name.is_a?(String) || endpoint_name.is_a?(Symbol)
130
- raise ArgumentError, "no enpoint_name provided. Given: #{endpoint_name.inspect}"
129
+ def self._aprcps_merge_apis_configs(api_name, configs = nil)
130
+ unless api_name.is_a?(String) || api_name.is_a?(Symbol)
131
+ raise ArgumentError, "no api_name provided. Given: #{api_name.inspect}"
131
132
  end
132
- unless ApiRecipes.configuration.endpoints_configs[endpoint_name]
133
- ApiRecipes.configuration.endpoints_configs[endpoint_name] = {}
133
+ unless ApiRecipes.configuration.apis_configs[api_name]
134
+ ApiRecipes.configuration.apis_configs[api_name] = {}
134
135
  end
135
136
  if configs
136
- ApiRecipes.configuration.endpoints_configs[endpoint_name].merge(configs) do |_, old_val, new_val|
137
+ ApiRecipes.configuration.apis_configs[api_name].merge(configs) do |_, old_val, new_val|
137
138
  if new_val.nil?
138
139
  old_val
139
140
  else
@@ -141,7 +142,7 @@ module ApiRecipes
141
142
  end
142
143
  end
143
144
  else
144
- ApiRecipes.configuration.endpoints_configs[endpoint_name]
145
+ ApiRecipes.configuration.apis_configs[api_name]
145
146
  end
146
147
  end
147
148
  end
@@ -0,0 +1,71 @@
1
+ module ApiRecipes
2
+ class Api
3
+
4
+ attr_accessor :name, :configs, :authorization, :basic_auth
5
+ attr_reader :base_configs, :object
6
+
7
+ BASE_CONFIGS_KEYS = [:protocol, :host, :port, :api_version, :timeout, :on_bad_code, :verify_with]
8
+
9
+ def initialize(name, configs, object)
10
+ @name = name
11
+ @configs = ApiRecipes::Settings::DEFAULT.merge configs
12
+ @object = object
13
+
14
+ # Generate some_api.some_endpoint methods
15
+ # e.g. github.users
16
+ endpoints = @configs[:endpoints]
17
+ if endpoints && endpoints.is_a?(Hash)
18
+ endpoints.each do |ep_name, params|
19
+ unless respond_to? ep_name
20
+ # TODO: store endpoints, routes and routes_urls
21
+ define_singleton_method ep_name do |*request_params, &block|
22
+ # puts "API params: #{params}"
23
+ Endpoint.new api: self, name: ep_name, path: path, params: params, request_params: request_params, &block
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ def authorization=(auth)
31
+ @authorization = auth
32
+
33
+ # Check if I'm the global api
34
+ if global?
35
+ # Set authorization also on every "children" (classes that define the same api)
36
+ ApiRecipes.set_authorization_for_api auth, name
37
+ end
38
+ end
39
+
40
+ def basic_auth=(auth)
41
+ @basic_auth = auth
42
+
43
+ # Check if I'm the global api
44
+ if global?
45
+ # Set authorization also on every "children" (classes that define the same api)
46
+ ApiRecipes.set_basic_auth_for_api auth, name
47
+ end
48
+ end
49
+
50
+ def base_configs
51
+ @configs.select { |c| BASE_CONFIGS_KEYS.include? c }
52
+ end
53
+
54
+ private
55
+
56
+ def check_route_name_does_not_clash(route_name)
57
+ # Check if a method named route_name has already been defined on this object
58
+ if respond_to? route_name
59
+ raise RouteNameClashWithExistentMethod.new(@name, route_name)
60
+ end
61
+ end
62
+
63
+ def global?
64
+ ApiRecipes._aprcps_global_storage[name] == self
65
+ end
66
+
67
+ def path
68
+ "/#{configs[:base_url]}/"
69
+ end
70
+ end
71
+ end
@@ -1,19 +1,19 @@
1
1
  module ApiRecipes
2
2
  class Configuration
3
3
 
4
- attr_accessor :log_to, :log_level
4
+ attr_accessor :log_to, :log_level, :print_urls
5
5
 
6
- def endpoints_configs=(configs = {})
7
- raise ArgumentError, 'endpoints_configs must be an Hash' unless configs.is_a? Hash
8
- @endpoints_configs = configs.deep_symbolize_keys
9
- ApiRecipes._aprcps_define_global_endpoints
6
+ def apis_configs=(configs = {})
7
+ raise ArgumentError, 'apis_configs must be an Hash' unless configs.is_a? Hash
8
+ @apis_configs = configs.deep_symbolize_keys
9
+ ApiRecipes._aprcps_define_global_apis
10
10
  end
11
11
 
12
- def endpoints_configs
13
- unless @endpoints_configs
14
- @endpoints_configs = {}
12
+ def apis_configs
13
+ unless @apis_configs
14
+ @apis_configs = {}
15
15
  end
16
- @endpoints_configs
16
+ @apis_configs
17
17
  end
18
18
 
19
19
  def logger=(logger)
@@ -1,51 +1,137 @@
1
1
  module ApiRecipes
2
2
  class Endpoint
3
3
 
4
- attr_accessor :name, :configs, :authorization, :basic_auth
5
- attr_reader :resources
4
+ attr_reader :api, :name, :params, :route, :children
6
5
 
7
- def initialize(name, configs)
6
+ FORWARDABLE_PARAMS = %i[verify_with].freeze
7
+
8
+ def initialize(api: nil, name: nil, path: nil, params: {}, request_params: [], &block)
9
+ @api = api
8
10
  @name = name
9
- @configs = ApiRecipes::Settings::DEFAULT.merge configs
10
-
11
- # Generate some_endpoint.some_resource methods
12
- # e.g. github.users
13
- @resources = [] unless @resources
14
- @configs[:routes].each do |resource, routes|
15
- @resources << resource
16
- res = Resource.new resource, self, routes
17
- unless respond_to? resource
18
- define_singleton_method resource do
19
- res
11
+ @path = path.to_s
12
+ new_params = params.dup || {}
13
+ self.params = new_params
14
+ @children = new_params.delete :endpoints
15
+ @route = nil
16
+ @request_params = request_params.extract_options!
17
+ self.path_params = request_params
18
+
19
+ generate_route
20
+ generate_children
21
+ if block_given?
22
+ run &block
23
+ end
24
+ end
25
+
26
+ def run(&block)
27
+ if @route
28
+ @route.start_request &block
29
+ else
30
+ raise NoRouteExists.new @name
31
+ end
32
+ end
33
+
34
+ def url
35
+ @route.url
36
+ end
37
+
38
+ private
39
+
40
+ def generate_route
41
+ # Check if we have to generate route for this endpoint
42
+ if create_route?
43
+ ensure_route_does_not_clash @name
44
+ # Generate route
45
+ attrs = params.dup
46
+ attrs.delete(:endpoints)
47
+ # puts "Generating route '#{@name}' with path '#{build_path}'"
48
+ @route = Route.new(api: @api, endpoint: self, path: build_path, attributes: attrs, req_pars: @request_params)
49
+ end
50
+ end
51
+
52
+ def generate_children
53
+ # Generate children endpoints if any
54
+ # puts "generating children of #{@name}: #{children.inspect}"
55
+ if children
56
+ children.each do |ep_name, pars|
57
+ pars = forwardable_params.merge(pars) if pars
58
+ # TODO: Merge pars with params
59
+ # puts "Creating Endpoint '#{@name}' child '#{ep_name}' passing path #{build_path}"
60
+ define_singleton_method ep_name do |*request_params, &block|
61
+ Endpoint.new api: @api, name: ep_name, path: build_path, params: pars, request_params: request_params, &block
20
62
  end
21
63
  end
22
64
  end
65
+ # Route.new(api: @api, endpoint: self, name: route_name, attributes: route_attrs, req_pars: request_params).start_request &block
66
+ end
67
+
68
+ def ensure_route_does_not_clash(route_name)
69
+ # Check if a method named route_name has already been defined on this object
70
+ if respond_to? route_name
71
+ raise RouteNameClashWithExistentMethod.new(@name, route_name)
72
+ end
23
73
  end
24
74
 
25
- def authorization=(auth)
26
- @authorization = auth
75
+ def create_route?
76
+ res = params[:route].eql?('yes') || params[:route].eql?(true)
77
+ res
78
+ end
79
+
80
+ def absolute_path
81
+ # Append path passed to initialize and (params[:path] || @name)
82
+ append = params[:path] || @name
83
+ unless append.empty?
84
+ "#{@path}/#{append}"
85
+ else
86
+ "#{@path}"
87
+ end.gsub(/\/+/, '/') # remove multiple consecutive '//'
88
+ end
27
89
 
28
- # Check if I'm the global endpoint
29
- if global?
30
- # Set authorization also on every "children" (classes that define the same endpoint)
31
- ApiRecipes.set_authorization_for_endpoint auth, name
90
+ def params=(attrs)
91
+ unless attrs.is_a? Hash
92
+ raise ArgumentError, "provided 'attrs' must be an Hash"
93
+ end
94
+ # Merge DEFAULT_ROUTE_ATTRIBUTES with Api base_configs
95
+ # Then merge the result with provided attributes
96
+ @params = Settings::DEFAULT_ROUTE_ATTRIBUTES.inject({}) do |out, key_val|
97
+ new_val = @api.base_configs[key_val.first]
98
+ out[key_val.first] = new_val.nil? ? key_val.last : new_val
99
+ out
100
+ end.inject({}) do |out, key_val|
101
+ new_val = attrs[key_val.first]
102
+ out[key_val.first] = new_val.nil? ? key_val.last : new_val
103
+ out
32
104
  end
33
105
  end
34
106
 
35
- def basic_auth=(auth)
36
- @basic_auth = auth
107
+ def build_path
108
+ final_path = absolute_path
109
+ # Check if provided path_params match with required path params
110
+ req_params = required_params_for_path
111
+ if @path_params.size != req_params.size
112
+ # puts "\nWARNING\n"
113
+ raise PathParamsMismatch.new(final_path, req_params, @path_params)
114
+ end
115
+ # Replace required_params present in path with params provided by user (@path_params)
116
+ @path_params.each { |par| final_path.sub! /(:[^\/]+)/, par.to_s }
117
+
118
+ final_path
119
+ end
37
120
 
38
- # Check if I'm the global endpoint
39
- if global?
40
- # Set authorization also on every "children" (classes that define the same endpoint)
41
- ApiRecipes.set_basic_auth_for_endpoint auth, name
121
+ def path_params=(params)
122
+ unless params.is_a? Array
123
+ raise ArgumentError, 'path params must be an Array'
42
124
  end
125
+ # Merge route attributes with defaults and deep clone route attributes
126
+ @path_params = params
43
127
  end
44
128
 
45
- private
129
+ def required_params_for_path
130
+ absolute_path.scan(/:(\w+)/).flatten.map { |p| p.to_sym }
131
+ end
46
132
 
47
- def global?
48
- ApiRecipes._aprcps_global_storage[name] == self
133
+ def forwardable_params
134
+ params.select { |k, v| FORWARDABLE_PARAMS.include? k }
49
135
  end
50
136
  end
51
137
  end