api_recipes 1.0.0 → 2.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc2b26e8c66139a2ff051ef066bb512b389a48bcbfa2ba2b391f1dc29719426d
4
- data.tar.gz: 281afb372a91edac6bbc1bc6335da2fb413e856ed7f32cff476f9c527d4dc459
3
+ metadata.gz: febe7d307961dd407eae97ef0519280abf463e896f8a83471d3242b6f8a7b8a1
4
+ data.tar.gz: 15df723f847728e9c433223eef128069204aa15344856a4388847860c45304f2
5
5
  SHA512:
6
- metadata.gz: 610ff709c864f8cb71f29f42a0a50cd75318e0ce184801f8aa40102d35301b937afdd1c3a56381f783e405763aad2ac4886205809c2b26a07d73a9433e368c81
7
- data.tar.gz: a3a0cd7ac6a1b7aaea7fe3f5989d4e1c7b071f6d7b18ffc99b4fbff56689114407310f37e459cee183a56b78529f51fe39b59712b4dc5cbd31577fc7e451b2b7
6
+ metadata.gz: 94627389adf247362789b3213f6ec2f45c4443e43c4212ec1fffa96f4796c4de9c097288ef0d328d657c98eb8590fa6908e89f77f080c09be1b0cb0b9c7f44d9
7
+ data.tar.gz: a887790e2ac8c3ec7853d2a24735f6c79a7761287e76265854963e80f18320b63bb598365ba75fb316064ebf9c424569e09160f4dc3eb79ce4c09e6326e1972f
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)
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)
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
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,69 @@
1
+ module ApiRecipes
2
+ class Api
3
+
4
+ attr_accessor :name, :configs, :authorization, :basic_auth
5
+ attr_reader :base_configs
6
+
7
+ BASE_CONFIGS_KEYS = [:protocol, :host, :port, :api_version, :timeout, :on_bad_code]
8
+
9
+ def initialize(name, configs)
10
+ @name = name
11
+ @configs = ApiRecipes::Settings::DEFAULT.merge configs
12
+
13
+ # Generate some_api.some_endpoint methods
14
+ # e.g. github.users
15
+ endpoints = @configs[:endpoints]
16
+ if endpoints && endpoints.is_a?(Hash)
17
+ endpoints.each do |ep_name, params|
18
+ unless respond_to? ep_name
19
+ define_singleton_method ep_name do |*request_params, &block|
20
+ # puts "API params: #{params}"
21
+ Endpoint.new api: self, name: ep_name, path: path, params: params, request_params: request_params, &block
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def authorization=(auth)
29
+ @authorization = auth
30
+
31
+ # Check if I'm the global api
32
+ if global?
33
+ # Set authorization also on every "children" (classes that define the same api)
34
+ ApiRecipes.set_authorization_for_api auth, name
35
+ end
36
+ end
37
+
38
+ def basic_auth=(auth)
39
+ @basic_auth = auth
40
+
41
+ # Check if I'm the global api
42
+ if global?
43
+ # Set authorization also on every "children" (classes that define the same api)
44
+ ApiRecipes.set_basic_auth_for_api auth, name
45
+ end
46
+ end
47
+
48
+ def base_configs
49
+ @configs.select { |c| BASE_CONFIGS_KEYS.include? c }
50
+ end
51
+
52
+ private
53
+
54
+ def check_route_name_does_not_clash(route_name)
55
+ # Check if a method named route_name has already been defined on this object
56
+ if respond_to? route_name
57
+ raise RouteNameClashWithExistentMethod.new(@name, route_name)
58
+ end
59
+ end
60
+
61
+ def global?
62
+ ApiRecipes._aprcps_global_storage[name] == self
63
+ end
64
+
65
+ def path
66
+ "/#{configs[:base_url]}/"
67
+ end
68
+ end
69
+ end
@@ -3,17 +3,17 @@ module ApiRecipes
3
3
 
4
4
  attr_accessor :log_to, :log_level
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,126 @@
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
+ def initialize(api: nil, name: nil, path: nil, params: {}, request_params: [], &block)
7
+ @api = api
8
8
  @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
9
+ @path = path.to_s
10
+ new_params = params.dup || {}
11
+ self.params = new_params
12
+ @children = new_params.delete :endpoints
13
+ @route = nil
14
+ @request_params = request_params.extract_options!
15
+ self.path_params = request_params
16
+
17
+ generate_route
18
+ generate_children
19
+ if block_given?
20
+ run &block
21
+ end
22
+ end
23
+
24
+ def run(&block)
25
+ if @route
26
+ @route.start_request &block
27
+ else
28
+ raise NoRouteExists.new @name
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def generate_route
35
+ # Check if we have to generate route for this endpoint
36
+ if create_route?
37
+ ensure_route_does_not_clash @name
38
+ # Generate route
39
+ attrs = params.dup
40
+ attrs.delete(:endpoints)
41
+ # puts "Generating route '#{@name}' with path '#{build_path}'"
42
+ @route = Route.new(api: @api, endpoint: self, path: build_path, attributes: attrs, req_pars: @request_params)
43
+ end
44
+ end
45
+
46
+ def generate_children
47
+ # Generate children endpoints if any
48
+ # puts "generating children of #{@name}: #{children.inspect}"
49
+ if children
50
+ children.each do |ep_name, pars|
51
+ # puts "Creating Endpoint '#{@name}' child '#{ep_name}' passing path #{build_path}"
52
+ define_singleton_method ep_name do |*request_params, &block|
53
+ Endpoint.new api: @api, name: ep_name, path: build_path, params: pars, request_params: request_params, &block
20
54
  end
21
55
  end
22
56
  end
57
+ # Route.new(api: @api, endpoint: self, name: route_name, attributes: route_attrs, req_pars: request_params).start_request &block
23
58
  end
24
59
 
25
- def authorization=(auth)
26
- @authorization = auth
27
-
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
60
+ def ensure_route_does_not_clash(route_name)
61
+ # Check if a method named route_name has already been defined on this object
62
+ if respond_to? route_name
63
+ raise RouteNameClashWithExistentMethod.new(@name, route_name)
32
64
  end
33
65
  end
34
66
 
35
- def basic_auth=(auth)
36
- @basic_auth = auth
67
+ def create_route?
68
+ res = params[:route].eql?('yes') || params[:route].eql?(true)
69
+ res
70
+ end
71
+
72
+ def absolute_path
73
+ # Append path passed to initialize and (params[:path] || @name)
74
+ append = params[:path] || @name
75
+ unless append.empty?
76
+ "#{@path}/#{append}"
77
+ else
78
+ "#{@path}"
79
+ end.gsub(/\/+/, '/') # remove multiple consecutive '//'
80
+ end
37
81
 
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
82
+ def params=(attrs)
83
+ unless attrs.is_a? Hash
84
+ raise ArgumentError, "provided 'attrs' must be an Hash"
85
+ end
86
+ # Merge DEFAULT_ROUTE_ATTRIBUTES with Api base_configs
87
+ # Then merge the result with provided attributes
88
+
89
+ @params = Settings::DEFAULT_ROUTE_ATTRIBUTES.inject({}) do |out, key_val|
90
+ new_val = @api.base_configs[key_val.first]
91
+ out[key_val.first] = new_val.nil? ? key_val.last : new_val
92
+ out
93
+ end.inject({}) do |out, key_val|
94
+ new_val = attrs[key_val.first]
95
+ out[key_val.first] = new_val.nil? ? key_val.last : new_val
96
+ out
42
97
  end
43
98
  end
44
99
 
45
- private
100
+ def build_path
101
+ final_path = absolute_path
102
+ # Check if provided path_params match with required path params
103
+ req_params = required_params_for_path
104
+ if @path_params.size != req_params.size
105
+ # puts "\nWARNING\n"
106
+ raise PathParamsMismatch.new(final_path, req_params, @path_params)
107
+ end
108
+ # Replace required_params present in path with params provided by user (@path_params)
109
+ @path_params.each { |par| final_path.sub! /(:[^\/]+)/, par.to_s }
110
+
111
+ final_path
112
+ end
113
+
114
+ def path_params=(params)
115
+ unless params.is_a? Array
116
+ raise ArgumentError, 'path params must be an Array'
117
+ end
118
+ # Merge route attributes with defaults and deep clone route attributes
119
+ @path_params = params
120
+ end
46
121
 
47
- def global?
48
- ApiRecipes._aprcps_global_storage[name] == self
122
+ def required_params_for_path
123
+ absolute_path.scan(/:(\w+)/).flatten.map { |p| p.to_sym }
49
124
  end
50
125
  end
51
126
  end