docker-swarm 0.2.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b43832405fbf723559703102c4d84a42c27c677d601625de43c81ceb2db804d0
4
- data.tar.gz: 57d9349d4ce67bc9e157c6d235e5cf4925b0af05df53ce0f29fffd2bd888fbb7
3
+ metadata.gz: 78ad878518bedd7c1ac7355efbb6372b49f2721fd7db5a3b0536b0872ed9f3ea
4
+ data.tar.gz: 6a5b9b06a1c43e8735d111cda5e9eb65820101fe515832fdf64647834310ad12
5
5
  SHA512:
6
- metadata.gz: 72c9fa985935caac2386f9b782ac3728f4c5ffbd121db6f5b36a6e1291eafed9bc5500bd0841385caad69ad5430152b5d11d10261c4ad73fbda86bd9cc7053b3
7
- data.tar.gz: e841eaa3cad32138f809a40556a2c798fb97e7c92bf40975d2076adbd6650a699eb5635880f6c50d598de2c1017045d61771ff88f7a794f8482a71bdb32b7fe3
6
+ metadata.gz: 762b554eb9e1321d1cb2cb82867809b816ba7e7a84ee55e1e34b571b0942aba9e13020d8b7019dc40c70d4457b3307f9d72ca5cc98cd5de26207c9bf9ce70c1b
7
+ data.tar.gz: e092c1f28b3de38986f3f5e207dd1aafeac8449448e861675aa0be88ee4763f71aa663fc9ee8c1b3bafcfe1997b3917a0924ca9dea777b484b5163bd4b197d45
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/docker-swarm.svg)](https://badge.fury.io/rb/docker-swarm)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- `docker-swarm` es un ORM ligero y cliente API robusto para interactuar con Docker Swarm desde Ruby. Diseñado para sentirse familiar a los desarrolladores de Rails, utiliza `ActiveModel` para ofrecer una interfaz limpia y potente.
6
+ `docker-swarm` es un ORM ligero y cliente API robusto para interactuar con Docker Swarm desde Ruby. Diseñado para sentirse familiar a los desarrolladores de Rails, utiliza `ActiveModel` para ofrecer una interfaz limpia y potente con estándares de observabilidad de Wispro.
7
7
 
8
8
  ## 🚀 Inicio Rápido
9
9
 
@@ -13,42 +13,37 @@ require 'docker_swarm'
13
13
  # Configurar (opcional, usa defaults)
14
14
  DockerSwarm.configure do |config|
15
15
  config.socket_path = "unix:///var/run/docker.sock"
16
+ config.log_level = Logger::INFO
17
+ config.read_timeout = 30
18
+ config.max_retries = 3
16
19
  end
17
20
 
18
- # Listar servicios
21
+ # Listar servicios (con soporte para Indifferent Access en arrays)
19
22
  services = DockerSwarm::Service.all
20
- services.each { |s| puts "#{s.ID}: #{s.Spec['Name']}" }
23
+ services.each { |s| puts "#{s.ID}: #{s.Spec[:Name]}" }
21
24
 
22
25
  # Crear un nuevo servicio
23
26
  service = DockerSwarm::Service.create(
27
+ Name: "my-webapp",
24
28
  Spec: {
25
- Name: "my-webapp",
26
29
  TaskTemplate: {
27
30
  ContainerSpec: { Image: "nginx:latest" }
28
31
  }
29
32
  }
30
33
  )
31
34
 
32
- # Obtener logs
35
+ # Obtener logs (stdout y stderr habilitados por defecto)
33
36
  puts service.logs
34
37
  ```
35
38
 
36
- ## 📖 Documentación Completa
37
-
38
- Para profundizar en el uso de la gema, consulta las siguientes guías:
39
-
40
- 1. **[Guía de Configuración](docs/configuration.md)**: Cómo configurar el socket, logger y opciones globales.
41
- 2. **[Uso del ORM (Modelos)](docs/models.md)**: Todo sobre el ciclo de vida de los recursos (`Service`, `Node`, `Task`, etc.).
42
- 3. **[Cliente de API (Bajo Nivel)](docs/api.md)**: Cómo realizar peticiones personalizadas directamente a la API de Docker.
43
- 4. **[Manejo de Errores](docs/errors.md)**: Jerarquía de excepciones y mapeo de errores de Docker.
44
- 5. **[Pruebas y Mocking](docs/testing.md)**: Guía para testear tu aplicación sin depender de un socket de Docker real.
45
-
46
39
  ## 🛠 Características Clave
47
40
 
41
+ - **Observabilidad Wispro**: Logging estructurado (KV) con `component`, `event`, `source` y `duration_ms` usando reloj monotónico.
42
+ - **Seguridad**: Enmascaramiento automático de secretos (`password`, `token`, etc.) en los logs.
43
+ - **Deep Indifferent Access**: Acceso a atributos mediante símbolos o strings, incluso en resultados de listados (`.all`).
44
+ - **Resiliencia**: Gestión inteligente de timeouts (`read`, `write`, `connect`) y reintentos automáticos para errores de red.
48
45
  - **Mapeo PascalCase**: Mantiene la fidelidad con los atributos de Docker (e.g., `s.ID`, `s.Spec`) evitando transformaciones costosas.
49
46
  - **ActiveModel Ready**: Soporta validaciones, serialización JSON y comportamientos estándar de modelos Ruby.
50
- - **Surgical Updates**: Actualizaciones precisas enviando solo el índice de versión y el payload necesario.
51
- - **Excon Stack**: Basado en `Excon` con middlewares para encoding de peticiones, parseo de respuestas y gestión de errores.
52
47
 
53
48
  ## 🤝 Contribuir
54
49
 
@@ -3,12 +3,19 @@
3
3
  module DockerSwarm
4
4
  class Base
5
5
  include ActiveModel::Model
6
+ include Concerns::Inspectable
6
7
 
7
8
  class << self
8
9
  def resource_name
9
10
  name.demodulize.downcase.pluralize
10
11
  end
11
12
 
13
+ # Cache of attributes already defined for this class
14
+ # @return [Set]
15
+ def defined_attributes
16
+ @defined_attributes ||= Set.new([ :validation_context, :errors, :context_for_validation ])
17
+ end
18
+
12
19
  def routes
13
20
  Api::ENDPOINTS[resource_name.to_sym]
14
21
  end
@@ -156,8 +163,10 @@ module DockerSwarm
156
163
  private
157
164
 
158
165
  def _define_dynamic_accessor(key)
159
- return if self.class.method_defined?("#{key}=")
166
+ return if self.class.defined_attributes.include?(key.to_sym)
167
+
160
168
  self.class.send(:attr_accessor, key)
169
+ self.class.defined_attributes.add(key.to_sym)
161
170
  end
162
171
 
163
172
  def _valid_attribute_name?(name)
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DockerSwarm
4
+ module Concerns
5
+ module Inspectable
6
+ extend ActiveSupport::Concern
7
+
8
+ def inspect
9
+ inspection = if respond_to?(:ID) && ID.present?
10
+ "ID: #{ID}"
11
+ else
12
+ "not persisted"
13
+ end
14
+
15
+ # Intentar añadir el nombre si existe
16
+ name_attr = attributes["Name"] || (respond_to?(:Name) ? Name : nil)
17
+ inspection += ", Name: #{name_attr}" if name_attr.present?
18
+
19
+ # Intentar añadir la imagen para servicios/contenedores
20
+ spec = attributes["Spec"] || (respond_to?(:Spec) ? Spec : nil)
21
+ image = spec&.dig("TaskTemplate", "ContainerSpec", "Image") || attributes["Image"] || (respond_to?(:Image) ? Image : nil)
22
+ inspection += ", Image: #{image}" if image.present?
23
+
24
+ "#<#{self.class.name} #{inspection}>"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DockerSwarm
4
+ module Concerns
5
+ module Loggable
6
+ extend ActiveSupport::Concern
7
+
8
+ # Fetches logs for the resource
9
+ # @param query_params [Hash] Query parameters for the logs endpoint (stdout, stderr, follow, etc.)
10
+ # @return [String] The raw log stream
11
+ def logs(query_params = { stdout: 1, stderr: 1 })
12
+ Api.request(action: self.class.routes[:logs], arguments: { id: self.ID }, query_params: query_params)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -5,11 +5,32 @@ require "logger"
5
5
  module DockerSwarm
6
6
  class Configuration
7
7
  attr_accessor :socket_path, :logger, :log_level
8
+ attr_reader :read_timeout, :write_timeout, :connect_timeout, :max_retries
8
9
 
9
10
  def initialize
10
11
  @socket_path = "unix:///var/run/docker.sock"
11
12
  @logger = Logger.new($stdout)
12
13
  @log_level = Logger::INFO
14
+ @read_timeout = 60.0
15
+ @write_timeout = 60.0
16
+ @connect_timeout = 10.0
17
+ @max_retries = 3
18
+ end
19
+
20
+ def read_timeout=(val)
21
+ @read_timeout = val&.to_f
22
+ end
23
+
24
+ def write_timeout=(val)
25
+ @write_timeout = val&.to_f
26
+ end
27
+
28
+ def connect_timeout=(val)
29
+ @connect_timeout = val&.to_f
30
+ end
31
+
32
+ def max_retries=(val)
33
+ @max_retries = val&.to_i
13
34
  end
14
35
  end
15
36
  end
@@ -11,37 +11,43 @@ module DockerSwarm
11
11
 
12
12
  def request(options = {})
13
13
  start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
14
-
15
- log_event("request_started",
14
+
15
+ log_event("request_started",
16
16
  level: :debug,
17
- data: { method: options[:method], path: options[:path] })
18
-
19
- response = client.request(options)
20
-
21
- log_event("request_success",
22
- data: {
23
- method: options[:method],
24
- path: options[:path],
25
- status: response.status,
26
- duration_ms: calculate_duration(start_time)
27
- })
28
-
17
+ data: options.merge(path: options[:path]))
18
+
19
+ # Combinar opciones por defecto de la gema con las de la petición
20
+ request_options = {
21
+ idempotent: true,
22
+ retry_errors: [ Excon::Error::Socket, Excon::Error::Timeout ],
23
+ read_timeout: DockerSwarm.configuration.read_timeout.to_f,
24
+ write_timeout: DockerSwarm.configuration.write_timeout.to_f,
25
+ connect_timeout: DockerSwarm.configuration.connect_timeout.to_f,
26
+ retries: DockerSwarm.configuration.max_retries.to_i
27
+ }.merge(options)
28
+
29
+ response = client.request(request_options)
30
+
31
+ log_event("request_success",
32
+ data: options.merge(
33
+ status: response.status,
34
+ duration_ms: calculate_duration(start_time)
35
+ ))
36
+
29
37
  response.body
30
38
  rescue => e
31
39
  # Excon suele envolver excepciones de middleware en Excon::Error::Socket.
32
40
  # Intentamos recuperar la causa original si es una de nuestras excepciones.
33
41
  actual_error = (e.respond_to?(:cause) && e.cause&.class&.name&.include?("DockerSwarm::Error")) ? e.cause : e
34
-
35
- log_event("request_failure",
42
+
43
+ log_event("request_failure",
36
44
  level: :error,
37
- data: {
38
- method: options[:method],
39
- path: options[:path],
45
+ data: options.merge(
40
46
  error: actual_error.class.name,
41
47
  message: actual_error.message,
42
- duration_ms: calculate_duration(start_time)
43
- })
44
-
48
+ duration_ms: calculate_duration(start_time)
49
+ ))
50
+
45
51
  if actual_error.class.name.include?("DockerSwarm::Error")
46
52
  raise actual_error
47
53
  elsif actual_error.is_a?(Excon::Error::Socket)
@@ -59,20 +65,11 @@ module DockerSwarm
59
65
  return if level == :debug && logger.level > Logger::DEBUG
60
66
 
61
67
  log_block = proc do
62
- begin
63
- payload = {
64
- component: "docker_swarm.connection",
65
- event: event,
66
- source: "http"
67
- }.merge(data)
68
-
69
- payload.map do |k, v|
70
- val = k.to_s =~ /password|token|api_key|auth|secret/i ? "[FILTERED]" : v
71
- "#{k}=#{val}"
72
- end.join(" ")
73
- rescue
74
- "component=docker_swarm.connection event=logging_error"
75
- end
68
+ LogHelper.format_kv({
69
+ component: "docker_swarm.connection",
70
+ event: event,
71
+ source: "http"
72
+ }.merge(data))
76
73
  end
77
74
 
78
75
  if level == :debug
@@ -99,9 +96,9 @@ module DockerSwarm
99
96
 
100
97
  def client
101
98
  debug_enabled = logger&.level == Logger::DEBUG
102
-
103
- options = {
104
- middlewares: common_middlewares,
99
+
100
+ options = {
101
+ middlewares: common_middlewares,
105
102
  logger: logger,
106
103
  debug_request: debug_enabled,
107
104
  debug_response: debug_enabled,
@@ -111,9 +108,9 @@ module DockerSwarm
111
108
 
112
109
  @client ||= if socket_path.start_with?("unix://")
113
110
  Excon.new("unix:///", options.merge(socket: socket_path.sub(/^unix:\/\//, "")))
114
- else
111
+ else
115
112
  Excon.new(socket_path, options)
116
- end
113
+ end
117
114
  end
118
115
  end
119
116
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DockerSwarm
4
+ # Helper module to centralize logging logic and formatting
5
+ module LogHelper
6
+ SENSITIVE_KEYS = /password|token|api_key|auth|secret/i.freeze
7
+
8
+ # Formats a hash into a KV structured string with sensitive data masking
9
+ # @param payload [Hash] The data to format
10
+ # @return [String] KV formatted string
11
+ def self.format_kv(payload)
12
+ payload.map do |k, v|
13
+ val = k.to_s =~ SENSITIVE_KEYS ? "[FILTERED]" : v
14
+ "#{k}=#{val}"
15
+ end.join(" ")
16
+ rescue
17
+ "event=logging_error"
18
+ end
19
+ end
20
+ end
@@ -40,7 +40,7 @@ module DockerSwarm
40
40
  return unless logger
41
41
 
42
42
  begin
43
- payload = {
43
+ kv_string = LogHelper.format_kv({
44
44
  component: "docker_swarm.middleware.error_handler",
45
45
  event: "business_error",
46
46
  source: "http",
@@ -48,12 +48,7 @@ module DockerSwarm
48
48
  message: message,
49
49
  method: env[:method],
50
50
  path: env[:path]
51
- }
52
-
53
- kv_string = payload.map do |k, v|
54
- val = k.to_s =~ /password|token|api_key|auth|secret/i ? "[FILTERED]" : v
55
- "#{k}=#{val}"
56
- end.join(" ")
51
+ })
57
52
 
58
53
  logger.error(kv_string)
59
54
  rescue
@@ -4,12 +4,28 @@ module DockerSwarm
4
4
  module Middleware
5
5
  class RequestEncoder < Excon::Middleware::Base
6
6
  def request_call(env)
7
- if env[:body] && env[:body].is_a?(Hash)
8
- env[:body] = env[:body].to_json
9
- env[:headers]["Content-Type"] ||= "application/json"
7
+ if env[:body] && !env[:body].is_a?(String)
8
+ content_type = (env[:headers]["Content-Type"] || env[:headers][:content_type]).to_s
9
+ env[:body] = serialize_body(env[:body], content_type)
10
+
11
+ if content_type.blank? || content_type.include?("application/json")
12
+ env[:headers]["Content-Type"] ||= "application/json"
13
+ end
10
14
  end
11
15
  @stack.request_call(env)
12
16
  end
17
+
18
+ private
19
+
20
+ def serialize_body(body, content_type)
21
+ if content_type.include?("application/x-www-form-urlencoded")
22
+ URI.encode_www_form(body)
23
+ elsif content_type.include?("multipart/form-data")
24
+ body
25
+ else
26
+ body.to_json
27
+ end
28
+ end
13
29
  end
14
30
  end
15
31
  end
@@ -9,17 +9,29 @@ module DockerSwarm
9
9
  headers = env[:response][:headers] || {}
10
10
 
11
11
  if body && !body.empty? && headers["Content-Type"]&.include?("application/json")
12
- begin
13
- parsed = JSON.parse(body)
14
- env[:response][:body] = parsed.is_a?(Hash) ? parsed.with_indifferent_access : parsed
15
- rescue JSON::ParserError
16
- # Keep original body if parsing fails
17
- end
12
+ env[:response][:body] = parse_json(body)
18
13
  end
19
14
  end
20
-
15
+
21
16
  @stack.response_call(env)
22
17
  end
18
+
19
+ private
20
+
21
+ def parse_json(body)
22
+ result = body.is_a?(String) ? JSON.parse(body) : body
23
+
24
+ case result
25
+ when Hash
26
+ result.with_indifferent_access
27
+ when Array
28
+ result.map { |item| item.is_a?(Hash) ? item.with_indifferent_access : item }
29
+ else
30
+ result
31
+ end
32
+ rescue JSON::ParserError
33
+ body
34
+ end
23
35
  end
24
36
  end
25
37
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerSwarm
4
+ # Represents a Docker Swarm Config
5
+ # @see https://docs.docker.com/engine/api/v1.41/#tag/Config
4
6
  class Config < Base
5
7
  include Concerns::Creatable
6
8
  include Concerns::Deletable
@@ -1,19 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerSwarm
4
+ # Represents a Docker Container
5
+ # @see https://docs.docker.com/engine/api/v1.41/#tag/Container
4
6
  class Container < Base
5
7
  include Concerns::Deletable
8
+ include Concerns::Loggable
6
9
 
10
+ # Starts the container
11
+ # @return [Boolean] true if successful
7
12
  def start
8
13
  Api.request(action: self.class.routes[:start], arguments: { id: self.ID })
14
+ true
9
15
  end
10
16
 
17
+ # Stops the container
18
+ # @return [Boolean] true if successful
11
19
  def stop
12
20
  Api.request(action: self.class.routes[:stop], arguments: { id: self.ID })
13
- end
14
-
15
- def logs(query_params = { stdout: 1, stderr: 1 })
16
- Api.request(action: self.class.routes[:logs], arguments: { id: self.ID }, query_params: query_params)
21
+ true
17
22
  end
18
23
  end
19
24
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerSwarm
4
+ # Represents a Docker Image
5
+ # @see https://docs.docker.com/engine/api/v1.41/#tag/Image
4
6
  class Image < Base
5
7
  include Concerns::Creatable
6
8
  include Concerns::Deletable
@@ -5,14 +5,5 @@ module DockerSwarm
5
5
  include Concerns::Creatable
6
6
  include Concerns::Updatable
7
7
  include Concerns::Deletable
8
-
9
- validate :validate_name_presence
10
-
11
- private
12
-
13
- def validate_name_presence
14
- name = attributes["Name"] || (respond_to?(:Name) ? Name : nil)
15
- errors.add(:Name, "can't be blank") if name.blank?
16
- end
17
8
  end
18
9
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerSwarm
4
+ # Represents a Docker Swarm Node
5
+ # @see https://docs.docker.com/engine/api/v1.41/#tag/Node
4
6
  class Node < Base
5
7
  include Concerns::Updatable
6
8
  include Concerns::Deletable
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerSwarm
4
+ # Represents a Docker Swarm Secret
5
+ # @see https://docs.docker.com/engine/api/v1.41/#tag/Secret
4
6
  class Secret < Base
5
7
  include Concerns::Creatable
6
8
  include Concerns::Deletable
@@ -7,21 +7,6 @@ module DockerSwarm
7
7
  include Concerns::Creatable
8
8
  include Concerns::Updatable
9
9
  include Concerns::Deletable
10
-
11
- validate :validate_name_presence
12
-
13
- # Fetches logs for the service
14
- # @param query_params [Hash] Query parameters for the logs endpoint (stdout, stderr, follow, etc.)
15
- # @return [String] The raw log stream
16
- def logs(query_params = { stdout: 1, stderr: 1 })
17
- Api.request(action: self.class.routes[:logs], arguments: { id: self.ID }, query_params: query_params)
18
- end
19
-
20
- private
21
-
22
- def validate_name_presence
23
- name = attributes["Name"] || (respond_to?(:Name) ? Name : nil)
24
- errors.add(:Name, "can't be blank") if name.blank?
25
- end
10
+ include Concerns::Loggable
26
11
  end
27
12
  end
@@ -1,11 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerSwarm
4
+ # Access point for Docker Swarm cluster information
5
+ # @see https://docs.docker.com/engine/api/v1.41/#tag/Swarm
4
6
  class Swarm < Base
7
+ # Override resource name to match API endpoint
8
+ # @return [String]
5
9
  def self.resource_name
6
10
  "swarm"
7
11
  end
8
12
 
13
+ # Returns information about the swarm
14
+ # @return [Hash] Raw swarm data
9
15
  def self.show
10
16
  Api.request(action: routes[:show])
11
17
  end
@@ -1,23 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerSwarm
4
+ # Access point for Docker System information and operations
5
+ # @see https://docs.docker.com/engine/api/v1.41/#tag/System
4
6
  class System < Base
7
+ # Override resource name to match API endpoint
8
+ # @return [String]
5
9
  def self.resource_name
6
10
  "system"
7
11
  end
8
12
 
13
+ # Returns system information
14
+ # @return [Hash]
9
15
  def self.info
10
16
  Api.request(action: routes[:info])
11
17
  end
12
18
 
19
+ # Returns Docker version information
20
+ # @return [Hash]
13
21
  def self.version
14
22
  Api.request(action: routes[:version])
15
23
  end
16
24
 
25
+ # Checks if the Docker daemon is responding
26
+ # @return [String] "OK" if up
17
27
  def self.up
18
28
  Api.request(action: routes[:up])
19
29
  end
20
30
 
31
+ # Returns system-wide data usage information
32
+ # @return [Hash]
21
33
  def self.df
22
34
  Api.request(action: routes[:df])
23
35
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerSwarm
4
+ # Represents a Docker Swarm Task
5
+ # @see https://docs.docker.com/engine/api/v1.41/#tag/Task
4
6
  class Task < Base
5
- def logs(query_params = { stdout: 1, stderr: 1 })
6
- Api.request(action: self.class.routes[:logs], arguments: { id: self.ID }, query_params: query_params)
7
- end
7
+ include Concerns::Loggable
8
8
  end
9
9
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerSwarm
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/docker_swarm.rb CHANGED
@@ -6,6 +6,7 @@ require "active_model"
6
6
  require "excon"
7
7
  require "json"
8
8
  require "logger"
9
+ require "set"
9
10
 
10
11
  module DockerSwarm
11
12
  class << self
@@ -14,11 +15,11 @@ module DockerSwarm
14
15
  def configure
15
16
  @configuration ||= Configuration.new
16
17
  yield(@configuration) if block_given?
17
-
18
+
18
19
  if @configuration.logger.respond_to?(:level=)
19
20
  @configuration.logger.level = @configuration.log_level
20
21
  end
21
-
22
+
22
23
  @connection = nil
23
24
  end
24
25
 
@@ -35,6 +36,7 @@ end
35
36
 
36
37
  # Primero cargamos la configuración porque la clase DockerSwarm la usa arriba
37
38
  require_relative "docker_swarm/configuration"
39
+ require_relative "docker_swarm/log_helper"
38
40
  require_relative "docker_swarm/version"
39
41
  require_relative "docker_swarm/errors"
40
42
  require_relative "docker_swarm/middleware/request_encoder"
@@ -42,10 +44,15 @@ require_relative "docker_swarm/middleware/response_json_parser"
42
44
  require_relative "docker_swarm/middleware/error_handler"
43
45
  require_relative "docker_swarm/connection"
44
46
  require_relative "docker_swarm/api"
45
- require_relative "docker_swarm/base"
47
+
48
+ # Concerns deben cargarse antes que Base si Base los incluye
46
49
  require_relative "docker_swarm/concerns/creatable"
47
50
  require_relative "docker_swarm/concerns/updatable"
48
51
  require_relative "docker_swarm/concerns/deletable"
52
+ require_relative "docker_swarm/concerns/loggable"
53
+ require_relative "docker_swarm/concerns/inspectable"
54
+
55
+ require_relative "docker_swarm/base"
49
56
 
50
57
  # Models
51
58
  require_relative "docker_swarm/models/swarm"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docker-swarm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriel
@@ -95,10 +95,13 @@ files:
95
95
  - lib/docker_swarm/base.rb
96
96
  - lib/docker_swarm/concerns/creatable.rb
97
97
  - lib/docker_swarm/concerns/deletable.rb
98
+ - lib/docker_swarm/concerns/inspectable.rb
99
+ - lib/docker_swarm/concerns/loggable.rb
98
100
  - lib/docker_swarm/concerns/updatable.rb
99
101
  - lib/docker_swarm/configuration.rb
100
102
  - lib/docker_swarm/connection.rb
101
103
  - lib/docker_swarm/errors.rb
104
+ - lib/docker_swarm/log_helper.rb
102
105
  - lib/docker_swarm/middleware/error_handler.rb
103
106
  - lib/docker_swarm/middleware/request_encoder.rb
104
107
  - lib/docker_swarm/middleware/response_json_parser.rb