api_recipes 0.7.1 → 2.6.2

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: dfd5a3d85babd54baa4d9001c52362cc5dfff4421cf3f9e7a2417e78b3f5eb2b
4
- data.tar.gz: 316bd1cefa28669906a4e87a56ef7e64ed14369cf2099262ad5ed85fa0544b05
3
+ metadata.gz: 2ce6553d9d7e1e72dca69781d977310670ac25a3c88b39a974b3b4d5865c0f0f
4
+ data.tar.gz: '08ff597bfc3b86e31d4f7d467cf7b0a7f67bd5d08783d9b901ce15f05e0f85ac'
5
5
  SHA512:
6
- metadata.gz: 28bd374672f163b8912794f12e33bbaaa9168f03940651dda9fba2bb88899b5202ea51cace5ed0a814508eef4dcbb195ac32826590c12d1e5d91a31f66a93d84
7
- data.tar.gz: 0bbb7e6ec0b1f02254449edfb03731b05927e5ac3bdc51a3fc2c2a89ae8ccbe79238f70ebe022978611ca4e49a0e9b1b95c96a3c4977360f36686424371b492e
6
+ metadata.gz: ddfbfdedaba0d774e87c868836f22e493bd0e336e015fa0907939d57756bc6f93b7540836887ea194ef27041b2c4848ccff258b69a9eb59142d591951bfe5827
7
+ data.tar.gz: ebca5a942daeb2d98522bb8fc811e1f0ad4b0916e6299a7bf7b6ad3c1b549d3ef28a1ebfe4cbdaf51103513578b97fd4732e730608174b356fafe8ef58a993a1
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
@@ -1,59 +1,59 @@
1
1
  require 'http'
2
- require 'oj'
3
2
 
4
3
  require 'api_recipes/utils'
5
4
  require 'api_recipes/exceptions'
6
5
  require 'api_recipes/configuration'
7
- require 'api_recipes/resource'
6
+ require 'api_recipes/route'
8
7
  require 'api_recipes/endpoint'
8
+ require 'api_recipes/api'
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
- define_method endpoint_name do
30
- unless ApiRecipes._aprcps_thread_storage[endpoint_name]
31
- ApiRecipes._aprcps_thread_storage[endpoint_name] = {}
32
- end
33
- unless ApiRecipes._aprcps_thread_storage[endpoint_name][self.class]
34
- ep = Endpoint.new(endpoint_name, configs)
35
- ApiRecipes.copy_global_authorizations_to_endpoint ep
36
- ApiRecipes._aprcps_thread_storage[endpoint_name][self.class] = ep
37
- end
38
-
39
- 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
40
38
  end
41
- define_singleton_method endpoint_name do
42
- unless ApiRecipes._aprcps_thread_storage[endpoint_name]
43
- ApiRecipes._aprcps_thread_storage[endpoint_name] = {}
44
- end
45
- unless ApiRecipes._aprcps_thread_storage[endpoint_name][self]
46
- ep = Endpoint.new(endpoint_name, configs)
47
- ApiRecipes.copy_global_authorizations_to_endpoint ep
48
- ApiRecipes._aprcps_thread_storage[endpoint_name][self] = ep
49
- end
50
-
51
- 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
52
44
  end
53
45
  end
54
46
  end
55
47
  end
56
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
+
57
57
  def self.configuration
58
58
  unless @configuration
59
59
  @configuration = Configuration.new
@@ -69,45 +69,45 @@ module ApiRecipes
69
69
  end
70
70
  end
71
71
 
72
- def self.copy_global_authorizations_to_endpoint(endpoint)
73
- if _aprcps_global_storage[endpoint.name]
74
- if auth = _aprcps_global_storage[endpoint.name].basic_auth
75
- 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
76
76
  end
77
- if auth = _aprcps_global_storage[endpoint.name].authorization
78
- endpoint.authorization = auth
77
+ if auth = _aprcps_global_storage[api.name].authorization
78
+ api.authorization = auth
79
79
  end
80
80
  end
81
81
  end
82
82
 
83
- def self.set_authorization_for_endpoint(authorization, endpoint_name)
84
- endpoint_name = endpoint_name.to_sym
83
+ def self.set_authorization_for_api(authorization, api_name)
84
+ api_name = api_name.to_sym
85
85
 
86
86
  # Set authorization on thread storage
87
- if _aprcps_thread_storage[endpoint_name]
88
- _aprcps_thread_storage[endpoint_name].each do |_, endpoint|
89
- endpoint.authorization = authorization
87
+ if _aprcps_thread_storage[api_name]
88
+ _aprcps_thread_storage[api_name].each do |_, api|
89
+ api.authorization = authorization
90
90
  end
91
91
  end
92
92
  end
93
93
 
94
- def self.set_basic_auth_for_endpoint(basic_auth, endpoint_name)
95
- 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
96
96
 
97
97
  # Set authorization on thread storage
98
- if _aprcps_thread_storage[endpoint_name]
99
- _aprcps_thread_storage[endpoint_name].each do |_, endpoint|
100
- 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
101
101
  end
102
102
  end
103
103
  end
104
104
 
105
- def self._aprcps_define_global_endpoints
106
- configuration.endpoints_configs.each do |endpoint_name, endpoint_configs|
107
- endpoint_name = endpoint_name.to_sym
108
- _aprcps_global_storage[endpoint_name] = Endpoint.new endpoint_name, endpoint_configs
109
- define_singleton_method endpoint_name do
110
- _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]
111
111
  end
112
112
  end
113
113
  end
@@ -126,15 +126,15 @@ module ApiRecipes
126
126
  Thread.current[:api_recipes]
127
127
  end
128
128
 
129
- def self._aprcps_merge_endpoints_configs(endpoint_name, configs = nil)
130
- unless endpoint_name.is_a?(String) || endpoint_name.is_a?(Symbol)
131
- 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}"
132
132
  end
133
- unless ApiRecipes.configuration.endpoints_configs[endpoint_name]
134
- ApiRecipes.configuration.endpoints_configs[endpoint_name] = {}
133
+ unless ApiRecipes.configuration.apis_configs[api_name]
134
+ ApiRecipes.configuration.apis_configs[api_name] = {}
135
135
  end
136
136
  if configs
137
- 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|
138
138
  if new_val.nil?
139
139
  old_val
140
140
  else
@@ -142,7 +142,7 @@ module ApiRecipes
142
142
  end
143
143
  end
144
144
  else
145
- ApiRecipes.configuration.endpoints_configs[endpoint_name]
145
+ ApiRecipes.configuration.apis_configs[api_name]
146
146
  end
147
147
  end
148
148
  end
@@ -0,0 +1,70 @@
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
+ # TODO: store endpoints, routes and routes_urls
20
+ define_singleton_method ep_name do |*request_params, &block|
21
+ # puts "API params: #{params}"
22
+ Endpoint.new api: self, name: ep_name, path: path, params: params, request_params: request_params, &block
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def authorization=(auth)
30
+ @authorization = auth
31
+
32
+ # Check if I'm the global api
33
+ if global?
34
+ # Set authorization also on every "children" (classes that define the same api)
35
+ ApiRecipes.set_authorization_for_api auth, name
36
+ end
37
+ end
38
+
39
+ def basic_auth=(auth)
40
+ @basic_auth = auth
41
+
42
+ # Check if I'm the global api
43
+ if global?
44
+ # Set authorization also on every "children" (classes that define the same api)
45
+ ApiRecipes.set_basic_auth_for_api auth, name
46
+ end
47
+ end
48
+
49
+ def base_configs
50
+ @configs.select { |c| BASE_CONFIGS_KEYS.include? c }
51
+ end
52
+
53
+ private
54
+
55
+ def check_route_name_does_not_clash(route_name)
56
+ # Check if a method named route_name has already been defined on this object
57
+ if respond_to? route_name
58
+ raise RouteNameClashWithExistentMethod.new(@name, route_name)
59
+ end
60
+ end
61
+
62
+ def global?
63
+ ApiRecipes._aprcps_global_storage[name] == self
64
+ end
65
+
66
+ def path
67
+ "/#{configs[:base_url]}/"
68
+ end
69
+ end
70
+ 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,130 @@
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
+ def url
33
+ @route.url
34
+ end
35
+
36
+ private
37
+
38
+ def generate_route
39
+ # Check if we have to generate route for this endpoint
40
+ if create_route?
41
+ ensure_route_does_not_clash @name
42
+ # Generate route
43
+ attrs = params.dup
44
+ attrs.delete(:endpoints)
45
+ # puts "Generating route '#{@name}' with path '#{build_path}'"
46
+ @route = Route.new(api: @api, endpoint: self, path: build_path, attributes: attrs, req_pars: @request_params)
47
+ end
48
+ end
49
+
50
+ def generate_children
51
+ # Generate children endpoints if any
52
+ # puts "generating children of #{@name}: #{children.inspect}"
53
+ if children
54
+ children.each do |ep_name, pars|
55
+ # puts "Creating Endpoint '#{@name}' child '#{ep_name}' passing path #{build_path}"
56
+ define_singleton_method ep_name do |*request_params, &block|
57
+ Endpoint.new api: @api, name: ep_name, path: build_path, params: pars, request_params: request_params, &block
20
58
  end
21
59
  end
22
60
  end
61
+ # Route.new(api: @api, endpoint: self, name: route_name, attributes: route_attrs, req_pars: request_params).start_request &block
23
62
  end
24
63
 
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
64
+ def ensure_route_does_not_clash(route_name)
65
+ # Check if a method named route_name has already been defined on this object
66
+ if respond_to? route_name
67
+ raise RouteNameClashWithExistentMethod.new(@name, route_name)
32
68
  end
33
69
  end
34
70
 
35
- def basic_auth=(auth)
36
- @basic_auth = auth
71
+ def create_route?
72
+ res = params[:route].eql?('yes') || params[:route].eql?(true)
73
+ res
74
+ end
75
+
76
+ def absolute_path
77
+ # Append path passed to initialize and (params[:path] || @name)
78
+ append = params[:path] || @name
79
+ unless append.empty?
80
+ "#{@path}/#{append}"
81
+ else
82
+ "#{@path}"
83
+ end.gsub(/\/+/, '/') # remove multiple consecutive '//'
84
+ end
37
85
 
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
86
+ def params=(attrs)
87
+ unless attrs.is_a? Hash
88
+ raise ArgumentError, "provided 'attrs' must be an Hash"
89
+ end
90
+ # Merge DEFAULT_ROUTE_ATTRIBUTES with Api base_configs
91
+ # Then merge the result with provided attributes
92
+
93
+ @params = Settings::DEFAULT_ROUTE_ATTRIBUTES.inject({}) do |out, key_val|
94
+ new_val = @api.base_configs[key_val.first]
95
+ out[key_val.first] = new_val.nil? ? key_val.last : new_val
96
+ out
97
+ end.inject({}) do |out, key_val|
98
+ new_val = attrs[key_val.first]
99
+ out[key_val.first] = new_val.nil? ? key_val.last : new_val
100
+ out
42
101
  end
43
102
  end
44
103
 
45
- private
104
+ def build_path
105
+ final_path = absolute_path
106
+ # Check if provided path_params match with required path params
107
+ req_params = required_params_for_path
108
+ if @path_params.size != req_params.size
109
+ # puts "\nWARNING\n"
110
+ raise PathParamsMismatch.new(final_path, req_params, @path_params)
111
+ end
112
+ # Replace required_params present in path with params provided by user (@path_params)
113
+ @path_params.each { |par| final_path.sub! /(:[^\/]+)/, par.to_s }
114
+
115
+ final_path
116
+ end
117
+
118
+ def path_params=(params)
119
+ unless params.is_a? Array
120
+ raise ArgumentError, 'path params must be an Array'
121
+ end
122
+ # Merge route attributes with defaults and deep clone route attributes
123
+ @path_params = params
124
+ end
46
125
 
47
- def global?
48
- ApiRecipes._aprcps_global_storage[name] == self
126
+ def required_params_for_path
127
+ absolute_path.scan(/:(\w+)/).flatten.map { |p| p.to_sym }
49
128
  end
50
129
  end
51
130
  end