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,258 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ module Templates
5
+ class Readme
6
+ def self.render(api_name, module_name, config)
7
+ base_url = config["base_url"] || "https://api.example.com"
8
+ auth_instructions = auth_setup_instructions(config)
9
+ resource_docs = generate_resource_docs(module_name, config)
10
+
11
+ <<~MARKDOWN
12
+ # #{module_name} API Client
13
+
14
+ A Ruby API wrapper for the #{api_name} API.
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem '#{api_name}'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ ```bash
27
+ $ bundle install
28
+ ```
29
+
30
+ Or install it yourself as:
31
+
32
+ ```bash
33
+ $ gem install #{api_name}
34
+ ```
35
+
36
+ ## Configuration
37
+
38
+ ```ruby
39
+ #{module_name}.configure do |config|
40
+ config.base_url = "#{base_url}"
41
+ #{auth_instructions}
42
+ end
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ ### Initializing the client
48
+
49
+ ```ruby
50
+ # Initialize the client
51
+ client = #{module_name}.client
52
+ ```
53
+
54
+ ### Examples
55
+
56
+ ```ruby
57
+ #{usage_examples(module_name, config)}
58
+ ```
59
+
60
+ ### Working with responses
61
+
62
+ Objects returned by the API can be accessed using dot notation:
63
+
64
+ ```ruby
65
+ user = client.users.get(123)
66
+ puts user.id # => 123
67
+ puts user.name # => "John Doe"
68
+ puts user.email # => "john@example.com"
69
+ ```
70
+
71
+ Collections include pagination support:
72
+
73
+ ```ruby
74
+ users = client.users.list
75
+
76
+ # Iterate through items
77
+ users.data.each do |user|
78
+ puts user.name
79
+ end
80
+
81
+ # Check pagination info
82
+ if users.next_href
83
+ # More results available
84
+ end
85
+ ```
86
+
87
+ ## Resources and Endpoints
88
+
89
+ #{resource_docs}
90
+
91
+ ## Error Handling
92
+
93
+ ```ruby
94
+ begin
95
+ response = client.users.get(123)
96
+ rescue #{module_name}::Error => e
97
+ puts "Error: \#{e.message}"
98
+ puts "Status: \#{e.status}"
99
+ puts "Details: \#{e.body}"
100
+ end
101
+ ```
102
+
103
+ ## API Documentation
104
+
105
+ Detailed API documentation is available in the `docs/api.md` file, which includes:
106
+
107
+ - All available endpoints
108
+ - Required parameters
109
+ - Example requests and responses
110
+ - Authentication details
111
+
112
+ ## Advanced Usage
113
+
114
+ ### Caching
115
+
116
+ #{module_name} uses a caching solution to improve efficiency (e.g., for caching tokens). By default, it uses a simple memory cache,
117
+ but you can change the cache method by setting the `#{module_name}.cache` attribute.
118
+
119
+ ```ruby
120
+ # Use Redis cache
121
+ #{module_name}.cache = Redis.new
122
+
123
+ # Or use Rails cache
124
+ #{module_name}.cache = Rails.cache
125
+
126
+ # Or use file-based cache
127
+ #{module_name}.cache = #{module_name}::FileCache.new
128
+
129
+ # Or any object that responds to read/write/delete/clear
130
+ #{module_name}.cache = YourCustomCache.new
131
+ ```
132
+
133
+ ### Custom Headers
134
+
135
+ You can set custom headers for all requests:
136
+
137
+ ```ruby
138
+ #{module_name}.configure do |config|
139
+ config.headers["User-Agent"] = "MyApp/1.0"
140
+ config.headers["X-Custom-Header"] = "Value"
141
+ end
142
+ ```
143
+ MARKDOWN
144
+ end
145
+
146
+ def self.auth_setup_instructions(config)
147
+ case config["auth_type"]
148
+ when "oauth"
149
+ <<~RUBY.strip
150
+ config.client_id = "your_client_id"
151
+ config.client_secret = "your_client_secret"
152
+ config.access_token = "your_access_token"
153
+ RUBY
154
+ when "basic"
155
+ <<~RUBY.strip
156
+ config.username = "your_username"
157
+ config.password = "your_password"
158
+ RUBY
159
+ when "api_key"
160
+ <<~RUBY.strip
161
+ config.api_key = "your_api_key"
162
+ config.api_key_header = "#{config["api_key_header"] || "X-Api-Key"}"
163
+ RUBY
164
+ else
165
+ "# No authentication required"
166
+ end
167
+ end
168
+
169
+ def self.usage_examples(_module_name, config)
170
+ resources = config["resources"] || {}
171
+ examples = []
172
+
173
+ resources.each do |resource_name, resource_config|
174
+ endpoints = resource_config["endpoints"] || []
175
+ next if endpoints.empty?
176
+
177
+ # Take the first endpoint as an example
178
+ endpoint = endpoints.first
179
+ method = endpoint["method"] || "get"
180
+ has_params = endpoint["path"].to_s.include?("{")
181
+
182
+ if has_params
183
+ # Extract parameters
184
+ params = endpoint["path"].scan(/\{([^}]+)\}/).flatten
185
+ param_values = params.map { |_p| "123" } # Example values
186
+ args = param_values.join(", ")
187
+
188
+ examples << "# #{resource_name.capitalize} - #{endpoint["name"]} example"
189
+ examples << "response = client.#{resource_name}.#{endpoint["name"]}(#{args})"
190
+ else
191
+ examples << "# #{resource_name.capitalize} - #{endpoint["name"]} example"
192
+ examples << if %w[post put patch].include?(method)
193
+ "response = client.#{resource_name}.#{endpoint["name"]}({name: 'value', other_field: 'value'})"
194
+ else
195
+ "response = client.#{resource_name}.#{endpoint["name"]}"
196
+ end
197
+ end
198
+
199
+ examples << ""
200
+ end
201
+
202
+ examples.join("\n")
203
+ end
204
+
205
+ def self.generate_resource_docs(_module_name, config)
206
+ resources = config["resources"] || {}
207
+ docs = []
208
+
209
+ resources.each do |resource_name, resource_config|
210
+ docs << "### #{resource_name.capitalize}"
211
+ endpoints = resource_config["endpoints"] || []
212
+
213
+ endpoints.each do |endpoint|
214
+ name = endpoint["name"]
215
+ method = endpoint["method"] || "get"
216
+ path = endpoint["path"] || name
217
+ has_params = path.include?("{")
218
+
219
+ docs << "#### `#{name}`"
220
+ docs << "- HTTP Method: `#{method.upcase}`"
221
+ docs << "- Path: `#{path}`"
222
+
223
+ if has_params
224
+ params = path.scan(/\{([^}]+)\}/).flatten
225
+ docs << "- Path Parameters: #{params.map { |p| "`#{p}`" }.join(", ")}"
226
+ end
227
+
228
+ docs << "- Accepts additional query parameters" if endpoint["params"]
229
+
230
+ # Usage example
231
+ docs << "\n```ruby"
232
+ if has_params
233
+ params = path.scan(/\{([^}]+)\}/).flatten
234
+ param_args = params.map { |p| "#{p}: 123" }.join(", ")
235
+
236
+ docs << if endpoint["params"]
237
+ "client.#{resource_name}.#{name}(#{param_args}, {param1: 'value', param2: 'value'})"
238
+ else
239
+ "client.#{resource_name}.#{name}(#{param_args})"
240
+ end
241
+ else
242
+ docs << if %w[post put patch].include?(method)
243
+ "client.#{resource_name}.#{name}({field1: 'value', field2: 'value'})"
244
+ elsif endpoint["params"]
245
+ "client.#{resource_name}.#{name}({param1: 'value', param2: 'value'})"
246
+ else
247
+ "client.#{resource_name}.#{name}"
248
+ end
249
+ end
250
+ docs << "```\n"
251
+ end
252
+ end
253
+
254
+ docs.join("\n")
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ module Templates
5
+ class Request
6
+ def self.render(module_name, config)
7
+ oauth_token_logic = if config["auth_type"] == "oauth"
8
+ <<~RUBY
9
+ def get_access_token
10
+ # Try to get token from cache first
11
+ token = #{module_name}.cache.read("access_token")
12
+ return token if token
13
+
14
+ # If not in cache, fetch new token
15
+ response = Faraday.post(@config.token_url, {
16
+ client_id: @config.client_id,
17
+ client_secret: @config.client_secret,
18
+ grant_type: "client_credentials"
19
+ })
20
+
21
+ if response.status == 200
22
+ data = JSON.parse(response.body)
23
+ token = data["access_token"]
24
+ expires_in = data["expires_in"] || 3600
25
+
26
+ # Cache the token
27
+ #{module_name}.cache.write("access_token", token)
28
+
29
+ # Cache expiration handling could be improved
30
+ token
31
+ else
32
+ raise #{module_name}::Error.new("Failed to obtain access token", response.body, response.status)
33
+ end
34
+ end
35
+ RUBY
36
+ else
37
+ ""
38
+ end
39
+
40
+ <<~RUBY
41
+ # frozen_string_literal: true
42
+
43
+ require "faraday"
44
+ require "json"
45
+
46
+ module #{module_name}
47
+ class Request
48
+ def initialize(path, config = #{module_name}.configuration)
49
+ @path = path
50
+ @config = config
51
+ @base_url = config.base_url
52
+ end
53
+
54
+ def get(params: {}, headers: {})
55
+ make_request(:get, params: params, headers: headers)
56
+ end
57
+
58
+ def post(body: {}, headers: {})
59
+ make_request(:post, body: body, headers: headers)
60
+ end
61
+
62
+ def put(body: {}, headers: {})
63
+ make_request(:put, body: body, headers: headers)
64
+ end
65
+
66
+ def patch(body: {}, headers: {})
67
+ make_request(:patch, body: body, headers: headers)
68
+ end
69
+
70
+ def delete(params: {}, headers: {})
71
+ make_request(:delete, params: params, headers: headers)
72
+ end
73
+
74
+ private
75
+
76
+ def make_request(method, params: {}, body: nil, headers: {})
77
+ response = connection.public_send(method) do |req|
78
+ req.url @path
79
+ req.params = params if params && !params.empty?
80
+ req.body = body.to_json if body && !body.empty?
81
+ req.headers.merge!(headers) if headers && !headers.empty?
82
+ req.options.timeout = @config.timeout
83
+ end
84
+
85
+ handle_response(response)
86
+ end
87
+
88
+ def connection
89
+ @connection ||= Faraday.new(url: @base_url) do |conn|
90
+ #{connection_auth_config(config)}
91
+ conn.headers = @config.headers
92
+ conn.response :json, content_type: /\\bjson$/
93
+ conn.adapter Faraday.default_adapter
94
+ end
95
+ end
96
+
97
+ def handle_response(response)
98
+ return response.body if response.status.between?(200, 299)
99
+
100
+ error_message = if response.body.is_a?(Hash)
101
+ response.body["message"] || response.body["error"] || "Error \#{response.status}"
102
+ else
103
+ "Error \#{response.status}"
104
+ end
105
+
106
+ raise #{module_name}::Error.new(error_message, response.body, response.status)
107
+ end
108
+
109
+ #{oauth_token_logic}
110
+ end
111
+ end
112
+ RUBY
113
+ end
114
+
115
+ def self.connection_auth_config(config)
116
+ case config["auth_type"]
117
+ when "oauth"
118
+ "conn.request :authorization, 'Bearer', get_access_token if @config.client_id && @config.client_secret"
119
+ when "basic"
120
+ "conn.basic_auth(@config.username, @config.password) if @config.username && @config.password"
121
+ when "api_key"
122
+ "conn.headers[@config.api_key_header] = @config.api_key if @config.api_key"
123
+ else
124
+ ""
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrappix
4
+ module Templates
5
+ class Resource
6
+ def self.render(module_name, resource_name, resource_config)
7
+ endpoints = resource_config["endpoints"] || []
8
+ methods = endpoints.map do |endpoint|
9
+ endpoint_method(module_name, resource_name, endpoint, resource_config)
10
+ end.join("\n\n")
11
+
12
+ # Para normalizar el nombre del recurso
13
+ class_name = resource_name.capitalize
14
+
15
+ <<~RUBY
16
+ # frozen_string_literal: true
17
+
18
+ module #{module_name}
19
+ module Resources
20
+ class #{class_name}
21
+ def initialize(client)
22
+ @client = client
23
+ @config = #{module_name}.configuration
24
+ end
25
+
26
+ #{methods}
27
+ end
28
+ end
29
+ end
30
+ RUBY
31
+ end
32
+
33
+ def self.endpoint_method(module_name, _resource_name, endpoint, resource_config)
34
+ name = endpoint["name"]
35
+ method = endpoint["method"] || "get"
36
+ path = endpoint["path"] || name
37
+
38
+ # Obtener configuración de formato de respuesta
39
+ response_format = resource_config["response_format"] || {}
40
+
41
+ # Determinar si es una colección
42
+ is_collection = endpoint["collection"] || %w[all list index search].include?(name)
43
+
44
+ # Procesar parámetros del path
45
+ has_params = path.include?("{")
46
+ param_list = has_params ? path.scan(/\{([^}]+)\}/).flatten : []
47
+
48
+ # Preparar los parámetros del método
49
+ endpoint_params = []
50
+ endpoint_params.concat(param_list)
51
+ endpoint_params << "params = {}" if endpoint["params"]
52
+ endpoint_params << "body = {}" if %w[post put patch].include?(method)
53
+
54
+ # Preparar argumentos para el método request
55
+ request_args = []
56
+ request_args << "params: params" if endpoint["params"]
57
+ request_args << "body: body" if %w[post put patch].include?(method)
58
+ request_options = request_args.empty? ? "" : "(#{request_args.join(", ")})"
59
+
60
+ # Generar path con reemplazos de variables
61
+ path_with_params = path
62
+ param_list.each do |param|
63
+ path_with_params = path_with_params.gsub(/\{#{param}\}/, "\#{#{param}}")
64
+ end
65
+
66
+ # Código para transformar la respuesta basada en la configuración
67
+ response_transform = if is_collection
68
+ "#{module_name}::Collection.from_response(response, type: #{module_name}::Object)"
69
+ else
70
+ item_root = response_format["item_root"]
71
+ if item_root
72
+ "#{module_name}::Object.new(response[:#{item_root}] || response[\"#{item_root}\"] || response)"
73
+ else
74
+ "#{module_name}::Object.new(response)"
75
+ end
76
+ end
77
+
78
+ <<~RUBY.strip
79
+ def #{name}(#{endpoint_params.join(", ")})
80
+ request = #{module_name}::Request.new("#{path_with_params}")
81
+ response = request.#{method}#{request_options}
82
+
83
+ #{response_transform}
84
+ end
85
+ RUBY
86
+ end
87
+
88
+ def self.generate_response_transform(module_name, is_collection, response_format)
89
+ if is_collection
90
+ collection_root = response_format["collection_root"] || "data"
91
+ pagination_config = response_format["pagination"] || {}
92
+ next_page_key = pagination_config["next_page_key"] || "next_href"
93
+
94
+ # Código para extraer datos considerando la estructura de paginación
95
+ <<~RUBY
96
+ data = response[:#{collection_root}] || response["#{collection_root}"] || []
97
+ next_href = response[:#{next_page_key}] || response["#{next_page_key}"]
98
+
99
+ #{module_name}::Collection.from_response({
100
+ data: data,
101
+ next_href: next_href
102
+ }, type: #{module_name}::Object)
103
+ RUBY
104
+ else
105
+ item_root = response_format["item_root"]
106
+ if item_root
107
+ "#{module_name}::Object.new(response[:#{item_root}] || response[\"#{item_root}\"] || response)"
108
+ else
109
+ "#{module_name}::Object.new(response)"
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end