api_recipes 1.0.0 → 2.5.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: 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