wrappix 0.1.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.
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ class Configuration
5
+ attr_accessor :base_url, :headers, :timeout
6
+ attr_reader :resources
7
+
8
+ def initialize
9
+ @base_url = nil
10
+ @headers = {}
11
+ @timeout = 30
12
+ @resources = {}
13
+ end
14
+
15
+ def add_resource(name, options = {})
16
+ @resources[name.to_sym] = options
17
+ end
18
+
19
+ def resources=(resource_hash)
20
+ resource_hash.each do |name, options|
21
+ add_resource(name, options)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ class Request
5
+ def initialize(url, config = Wrappix.configuration)
6
+ @url = url
7
+ @config = config
8
+ end
9
+
10
+ def get(params: {}, headers: {})
11
+ handle_response connection.get(@url, params, headers)
12
+ end
13
+
14
+ def post(body: {}, headers: {})
15
+ handle_response connection.post(@url, body, headers)
16
+ end
17
+
18
+ def patch(body: {}, headers: {})
19
+ handle_response connection.patch(@url, body, headers)
20
+ end
21
+
22
+ def put(body: {}, headers: {})
23
+ handle_response connection.put(@url, body, headers)
24
+ end
25
+
26
+ def delete(params: {}, headers: {})
27
+ handle_response connection.delete(@url, params, headers)
28
+ end
29
+
30
+ private
31
+
32
+ def connection
33
+ @connection ||= Faraday.new do |conn|
34
+ conn.url_prefix = @config.base_url
35
+
36
+ # Configurar autenticación según el tipo
37
+ case @config.auth_type
38
+ when :oauth
39
+ conn.request :authorization, :Bearer, @config.access_token
40
+ when :basic
41
+ conn.basic_auth(@config.username, @config.password)
42
+ when :api_key
43
+ conn.headers[@config.api_key_header] = @config.api_key
44
+ end
45
+
46
+ conn.request :json
47
+ conn.response :json, content_type: "application/json", parser_options: { symbolize_names: true }
48
+ conn.adapter Faraday.default_adapter
49
+ end
50
+ end
51
+
52
+ def handle_response(response)
53
+ return response if response.status.between?(200, 299)
54
+
55
+ error_message = if response.body.is_a?(Hash)
56
+ response.body[:message] || response.body[:error] || "Error #{response.status}"
57
+ else
58
+ "Error #{response.status}"
59
+ end
60
+
61
+ raise Wrappix::Error.new(error_message, response.body, response.status)
62
+ end
63
+ end
64
+
65
+ class Error < StandardError
66
+ attr_reader :body, :status
67
+
68
+ def initialize(message, body = nil, status = nil)
69
+ @body = body
70
+ @status = status
71
+ super(message)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ module Templates
5
+ class Cache
6
+ def self.render(module_name, _config)
7
+ <<~RUBY
8
+ # frozen_string_literal: true
9
+
10
+ module #{module_name}
11
+ # Memory cache implementation
12
+ class MemoryCache
13
+ def initialize
14
+ @store = {}
15
+ end
16
+
17
+ def read(key)
18
+ @store[key]
19
+ end
20
+
21
+ def write(key, value)
22
+ @store[key] = value
23
+ end
24
+
25
+ def delete(key)
26
+ @store.delete(key)
27
+ end
28
+
29
+ def clear
30
+ @store = {}
31
+ end
32
+ end
33
+
34
+ # File cache implementation
35
+ class FileCache
36
+ def initialize(path = "#{module_name.downcase}_cache.yaml")
37
+ require "yaml/store"
38
+ @store = YAML::Store.new(path)
39
+ end
40
+
41
+ def read(key)
42
+ @store.transaction(true) { @store[key] }
43
+ end
44
+
45
+ def write(key, value)
46
+ @store.transaction { @store[key] = value }
47
+ end
48
+
49
+ def delete(key)
50
+ @store.transaction { @store.delete(key) }
51
+ end
52
+
53
+ def clear
54
+ @store.transaction { @store.roots.each { |key| @store.delete(key) } }
55
+ end
56
+ end
57
+ end
58
+ RUBY
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ module Templates
5
+ class Client
6
+ def self.render(module_name, config)
7
+ <<~RUBY
8
+ # frozen_string_literal: true
9
+
10
+ module #{module_name}
11
+ class Client
12
+ def initialize(configuration = #{module_name}.configuration)
13
+ @configuration = configuration
14
+ end
15
+
16
+ #{resource_methods(module_name, config)}
17
+ end
18
+ end
19
+ RUBY
20
+ end
21
+
22
+ def self.resource_methods(_module_name, config)
23
+ resources = config["resources"] || {}
24
+
25
+ resources.map do |name, _config|
26
+ <<~RUBY.strip
27
+ def #{name}
28
+ @#{name} ||= Resources::#{name.capitalize}.new(self)
29
+ end
30
+ RUBY
31
+ end.join("\n\n")
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ # lib/wrappix/templates/collection.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Wrappix
5
+ module Templates
6
+ class Collection
7
+ def self.render(module_name, _config)
8
+ <<~RUBY
9
+ # frozen_string_literal: true
10
+
11
+ module #{module_name}
12
+ class Collection
13
+ attr_reader :data, :next_href
14
+
15
+ def self.from_response(response_body, type:)
16
+ new(
17
+ data: response_body[:data]&.map { |attrs| type.new(attrs) },
18
+ next_href: response_body[:next_href]
19
+ )
20
+ end
21
+
22
+ def initialize(data:, next_href:)
23
+ @data = data
24
+ @next_href = next_href
25
+ end
26
+ end
27
+ end
28
+ RUBY
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ module Templates
5
+ class Configuration
6
+ def self.render(module_name, config)
7
+ <<~RUBY
8
+ # frozen_string_literal: true
9
+
10
+ module #{module_name}
11
+ class Configuration
12
+ attr_accessor :base_url, :timeout, :headers
13
+ #{auth_config_attributes(config)}
14
+
15
+ def initialize
16
+ @base_url = "#{config["base_url"] || "https://api.example.com"}"
17
+ @timeout = 30
18
+ @headers = {
19
+ "Content-Type" => "application/json",
20
+ "Accept" => "application/json"
21
+ }
22
+ #{auth_config_initialization(config)}
23
+ end
24
+ end
25
+ end
26
+ RUBY
27
+ end
28
+
29
+ def self.auth_config_attributes(config)
30
+ case config["auth_type"]
31
+ when "oauth"
32
+ "attr_accessor :client_id, :client_secret, :token_url, :access_token"
33
+ when "basic"
34
+ "attr_accessor :username, :password"
35
+ when "api_key"
36
+ "attr_accessor :api_key, :api_key_header"
37
+ else
38
+ ""
39
+ end
40
+ end
41
+
42
+ def self.auth_config_initialization(config)
43
+ case config["auth_type"]
44
+ when "oauth"
45
+ <<~RUBY.strip
46
+ @client_id = nil
47
+ @client_secret = nil
48
+ @token_url = "#{config["token_url"] || "https://api.example.com/oauth/token"}"
49
+ @access_token = nil
50
+ RUBY
51
+ when "basic"
52
+ <<~RUBY.strip
53
+ @username = nil
54
+ @password = nil
55
+ RUBY
56
+ when "api_key"
57
+ <<~RUBY.strip
58
+ @api_key = nil
59
+ @api_key_header = "#{config["api_key_header"] || "X-Api-Key"}"
60
+ RUBY
61
+ else
62
+ ""
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ module Templates
5
+ class Documentation
6
+ def self.render(_api_name, module_name, config)
7
+ base_url = config["base_url"] || "https://api.example.com"
8
+ resources = config["resources"] || {}
9
+
10
+ # Cabecera y descripción general
11
+ doc = <<~MARKDOWN
12
+ # #{module_name} API Documentation
13
+
14
+ This document provides detailed information about the endpoints available in the #{module_name} API client.
15
+
16
+ **API Base URL:** `#{base_url}`
17
+
18
+ ## Table of Contents
19
+
20
+ - [Authentication](#authentication)
21
+ #{resources.keys.map { |r| "- [#{r.capitalize}](##{r})" }.join("\n ")}
22
+
23
+ ## Authentication
24
+
25
+ #{render_authentication_docs(config)}
26
+
27
+ ## Resources
28
+
29
+ MARKDOWN
30
+
31
+ # Añadir documentación para cada recurso
32
+ resources.each do |resource_name, resource_config|
33
+ doc += render_resource_docs(resource_name, resource_config, module_name, base_url)
34
+ end
35
+
36
+ doc
37
+ end
38
+
39
+ def self.render_authentication_docs(config)
40
+ case config["auth_type"]
41
+ when "oauth"
42
+ <<~MARKDOWN
43
+ This API uses OAuth 2.0 authentication. You need to obtain an access token from the authorization server.
44
+
45
+ ```ruby
46
+ #{config["api_name"].gsub("-", "_").capitalize}.configure do |config|
47
+ config.client_id = "YOUR_CLIENT_ID"
48
+ config.client_secret = "YOUR_CLIENT_SECRET"
49
+ end
50
+ ```
51
+ MARKDOWN
52
+ when "basic"
53
+ <<~MARKDOWN
54
+ This API uses HTTP Basic Authentication.
55
+
56
+ ```ruby
57
+ #{config["api_name"].gsub("-", "_").capitalize}.configure do |config|
58
+ config.username = "YOUR_USERNAME"
59
+ config.password = "YOUR_PASSWORD"
60
+ end
61
+ ```
62
+ MARKDOWN
63
+ when "api_key"
64
+ header = config["api_key_header"] || "X-Api-Key"
65
+ <<~MARKDOWN
66
+ This API uses API Key authentication. The key should be provided in the `#{header}` header.
67
+
68
+ ```ruby
69
+ #{config["api_name"].gsub("-", "_").capitalize}.configure do |config|
70
+ config.api_key = "YOUR_API_KEY"
71
+ end
72
+ ```
73
+ MARKDOWN
74
+ else
75
+ "This API does not require authentication."
76
+ end
77
+ end
78
+
79
+ def self.render_resource_docs(resource_name, resource_config, module_name, base_url)
80
+ endpoints = resource_config["endpoints"] || []
81
+ singular_name = resource_name.end_with?("s") ? resource_name.chop : resource_name
82
+
83
+ doc = <<~MARKDOWN
84
+
85
+ <a name="#{resource_name}"></a>
86
+ ## #{resource_name.capitalize}
87
+
88
+ MARKDOWN
89
+
90
+ endpoints.each do |endpoint|
91
+ doc += render_endpoint_docs(endpoint, resource_name, singular_name, module_name, base_url)
92
+ end
93
+
94
+ doc
95
+ end
96
+
97
+ def self.render_endpoint_docs(endpoint, resource_name, singular_name, _module_name, base_url)
98
+ name = endpoint["name"]
99
+ method = endpoint["method"]&.upcase || "GET"
100
+ path = endpoint["path"] || name
101
+
102
+ # Extraer los parámetros del path
103
+ path_params = path.scan(/\{([^}]+)\}/).flatten
104
+
105
+ # Generar la URL completa para la documentación
106
+ full_url = "#{base_url.chomp("/")}/#{path}"
107
+
108
+ # Generar la llamada al método para la documentación
109
+ method_params = []
110
+ method_params.concat(path_params)
111
+ method_params << "params" if endpoint["params"]
112
+ method_params << "body" if %w[POST PUT PATCH].include?(method)
113
+
114
+ client_call = "client.#{resource_name}.#{name}(#{method_params.join(", ")})"
115
+
116
+ # Documentación del endpoint
117
+ doc = <<~MARKDOWN
118
+
119
+ ### #{name}
120
+
121
+ **#{method}** `#{full_url}`
122
+
123
+ #{endpoint["description"] || "No description provided."}
124
+
125
+ #### Parameters
126
+ MARKDOWN
127
+
128
+ # Añadir documentación de parámetros
129
+ if path_params.empty? && !endpoint["params"] && !%w[POST PUT PATCH].include?(method)
130
+ doc += "\nThis endpoint does not require any parameters.\n"
131
+ else
132
+ if path_params.any?
133
+ doc += "\n**Path Parameters:**\n\n"
134
+ path_params.each do |param|
135
+ doc += "- `#{param}`: Required. #{param_description(param)}\n"
136
+ end
137
+ end
138
+
139
+ if endpoint["params"]
140
+ doc += "\n**Query Parameters:**\n\n"
141
+ doc += "- This endpoint accepts additional query parameters.\n"
142
+ end
143
+
144
+ if %w[POST PUT PATCH].include?(method)
145
+ doc += "\n**Request Body:**\n\n"
146
+ doc += "- This endpoint accepts a request body with the resource attributes.\n"
147
+ end
148
+ end
149
+
150
+ # Añadir ejemplos de uso
151
+ doc += <<~MARKDOWN
152
+
153
+ #### Example Usage
154
+
155
+ ```ruby
156
+ #{client_call}
157
+ ```
158
+
159
+ #### Response
160
+
161
+ #{response_example(name, singular_name, endpoint["collection"])}
162
+ MARKDOWN
163
+
164
+ doc
165
+ end
166
+
167
+ def self.param_description(param)
168
+ case param
169
+ when "id"
170
+ "The unique identifier of the resource."
171
+ when "customer_id"
172
+ "The identifier of the customer."
173
+ when /^(\w+)_id$/
174
+ "The identifier of the #{::Regexp.last_match(1)}."
175
+ else
176
+ "Description not available."
177
+ end
178
+ end
179
+
180
+ def self.response_example(name, resource_name, _is_collection)
181
+ case name
182
+ when "list", "all", "index", "search"
183
+ <<~MARKDOWN
184
+ ```ruby
185
+ # Returns a Collection object
186
+ collection.data.each do |#{resource_name}|
187
+ puts #{resource_name}.id
188
+ puts #{resource_name}.name
189
+ # Other attributes...
190
+ end
191
+
192
+ # Pagination information
193
+ puts collection.next_href # URL for the next page, if available
194
+ ```
195
+ MARKDOWN
196
+ when "get", "find", "show"
197
+ <<~MARKDOWN
198
+ ```ruby
199
+ # Returns a single Object
200
+ puts #{resource_name}.id
201
+ puts #{resource_name}.name
202
+ # Other attributes...
203
+ ```
204
+ MARKDOWN
205
+ when "create"
206
+ <<~MARKDOWN
207
+ ```ruby
208
+ # Returns the created object
209
+ puts #{resource_name}.id
210
+ puts #{resource_name}.created_at
211
+ # Other attributes...
212
+ ```
213
+ MARKDOWN
214
+ when "update"
215
+ <<~MARKDOWN
216
+ ```ruby
217
+ # Returns the updated object
218
+ puts #{resource_name}.id
219
+ puts #{resource_name}.updated_at
220
+ # Other attributes...
221
+ ```
222
+ MARKDOWN
223
+ when "delete", "destroy", "remove"
224
+ <<~MARKDOWN
225
+ ```ruby
226
+ # Returns a success indicator or the deleted object
227
+ puts "Resource deleted successfully"
228
+ ```
229
+ MARKDOWN
230
+ else
231
+ <<~MARKDOWN
232
+ ```ruby
233
+ # Returns a response specific to this endpoint
234
+ puts response # Examine the response structure
235
+ ```
236
+ MARKDOWN
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ module Templates
5
+ class Error
6
+ def self.render(module_name, _config)
7
+ <<~RUBY
8
+ # frozen_string_literal: true
9
+
10
+ module #{module_name}
11
+ class Error < StandardError
12
+ attr_reader :body, :status
13
+
14
+ def initialize(message, body = nil, status = nil)
15
+ @body = body
16
+ @status = status
17
+ super(message)
18
+ end
19
+ end
20
+ end
21
+ RUBY
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ module Templates
5
+ class Main
6
+ def self.render(api_name, module_name, config)
7
+ # api_name ya debería estar normalizado aquí
8
+ resources = config["resources"] || {}
9
+ resource_requires = resources.keys.map do |r|
10
+ "require_relative \"#{api_name}/resources/#{r}\""
11
+ end.join("\n")
12
+
13
+ <<~RUBY
14
+ # frozen_string_literal: true
15
+
16
+ require_relative "#{api_name}/version"
17
+ require_relative "#{api_name}/configuration"
18
+ require_relative "#{api_name}/error"
19
+ require_relative "#{api_name}/request"
20
+ require_relative "#{api_name}/object"
21
+ require_relative "#{api_name}/collection"
22
+ require_relative "#{api_name}/client"
23
+ require_relative "#{api_name}/cache"
24
+
25
+ # Resources
26
+ #{resource_requires}
27
+
28
+ module #{module_name}
29
+ class << self
30
+ attr_accessor :configuration, :cache
31
+
32
+ def configure
33
+ self.configuration ||= Configuration.new
34
+ yield(configuration) if block_given?
35
+ self
36
+ end
37
+
38
+ # Método para acceder al cliente
39
+ def client
40
+ @client ||= Client.new(configuration)
41
+ end
42
+ end
43
+
44
+ # Default to memory cache
45
+ self.cache = MemoryCache.new
46
+ self.configuration = Configuration.new
47
+ end
48
+ RUBY
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ module Templates
5
+ class Object
6
+ def self.render(module_name, _config)
7
+ <<~RUBY
8
+ # frozen_string_literal: true
9
+
10
+ require "ostruct"
11
+
12
+ module #{module_name}
13
+ class Object < OpenStruct
14
+ def initialize(attributes)
15
+ super(to_ostruct(attributes))
16
+ end
17
+
18
+ def to_ostruct(obj)
19
+ if obj.is_a?(Hash)
20
+ OpenStruct.new(obj.transform_values { |val| to_ostruct(val) })
21
+ elsif obj.is_a?(Array)
22
+ obj.map { |o| to_ostruct(o) }
23
+ else
24
+ obj
25
+ end
26
+ end
27
+ end
28
+ end
29
+ RUBY
30
+ end
31
+ end
32
+ end
33
+ end