api_recipes 0.7.1 → 2.6.2

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: 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