activeproject 0.0.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f3323a59c15ce296050ec1c7b231416d2cac1aa6f78fe133c8d7d2fd3ebb639
4
- data.tar.gz: 0f60e5b29d28e3adca3ce7e26df63929ed3c0d4b898887213823cd284efa365a
3
+ metadata.gz: 7b69a05303fa485956e8bdf7e56984aa5690df8f4bb7b9119253d760f0940916
4
+ data.tar.gz: 45dab21a69a42325b067f01510f47e16f9b8c9ebb312b11793e1b0c447f563a6
5
5
  SHA512:
6
- metadata.gz: 756f9f83b0f443ceb6d426514d79c7c7cca5c8f6b410ab6fbad9469426354735d706d0b00be102a1f36ec4dcb4a7ee04d8b110c6aaaeda3ac8ccce2b61695623
7
- data.tar.gz: 5397faf8bae3c5fe4ab86a98360de55c92237270806ac3372cdf7cff08742fa857ccff9f4a139998116ce366cd321df5afab6d81368a78970822de853d43c68c
6
+ metadata.gz: fcd6726cf79cfa6819ecfe3de62d5502d94317bfbb824a4f5e4c3579b836fa65a06ee408ff69f601d1e9ba47b7d3d45848e326e48608b413a7431b87e208dbff
7
+ data.tar.gz: 23191e09bd239c8fc21552c64520de969e70875fbedcb514449dbe1fe4f157cc204b150eb45a9b71ddb0af0edf0774fc4934ba1deb9cafe5202ca64b1245d7e2
data/README.md CHANGED
@@ -18,7 +18,7 @@ The ActiveProject gem aims to solve this by providing a unified, opinionated int
18
18
 
19
19
  The initial focus is on integrating with platforms primarily via their **REST APIs**:
20
20
 
21
- * **Jira (Cloud & Server):** REST API (v2/v3)
21
+ * **Jira (Cloud & Server):** REST API (v3)
22
22
  * **Basecamp (v3+):** REST API
23
23
  * **Trello:** REST API
24
24
 
@@ -69,29 +69,49 @@ $ gem install activeproject
69
69
 
70
70
  ### Configuration
71
71
 
72
- Configure the adapters in an initializer (e.g., `config/initializers/active_project.rb`):
72
+ Configure multiple adapters, optionally with named instances (default is `:primary`):
73
73
 
74
74
  ```ruby
75
75
  ActiveProject.configure do |config|
76
- # Configure Jira Adapter
76
+ # Primary Jira instance (default name :primary)
77
77
  config.add_adapter(:jira,
78
78
  site_url: ENV.fetch('JIRA_SITE_URL'),
79
- username: ENV.fetch('JIRA_USERNAME'), # Your Jira email
79
+ username: ENV.fetch('JIRA_USERNAME'),
80
80
  api_token: ENV.fetch('JIRA_API_TOKEN')
81
81
  )
82
82
 
83
+ # Secondary Jira instance
84
+ config.add_adapter(:jira, :secondary,
85
+ site_url: ENV.fetch('JIRA_SECOND_SITE_URL'),
86
+ username: ENV.fetch('JIRA_SECOND_USERNAME'),
87
+ api_token: ENV.fetch('JIRA_SECOND_API_TOKEN')
88
+ )
83
89
 
84
- # Configure Basecamp Adapter
90
+ # Basecamp primary instance
85
91
  config.add_adapter(:basecamp,
86
92
  account_id: ENV.fetch('BASECAMP_ACCOUNT_ID'),
87
93
  access_token: ENV.fetch('BASECAMP_ACCESS_TOKEN')
88
94
  )
89
95
 
90
- # Configure other adapters later (e.g., :trello, :basecamp)
91
- # config.add_adapter(:trello, key: '...', token: '...')
96
+ # Trello primary instance
97
+ config.add_adapter(:trello,
98
+ key: ENV.fetch('TRELLO_KEY'),
99
+ token: ENV.fetch('TRELLO_TOKEN')
100
+ )
92
101
  end
93
102
  ```
94
103
 
104
+ ### Accessing adapters
105
+
106
+ Fetch a specific adapter instance:
107
+
108
+ ```ruby
109
+ jira_primary = ActiveProject.adapter(:jira) # defaults to :primary
110
+ jira_secondary = ActiveProject.adapter(:jira, :secondary)
111
+ basecamp = ActiveProject.adapter(:basecamp) # defaults to :primary
112
+ trello = ActiveProject.adapter(:trello) # defaults to :primary
113
+ ```
114
+
95
115
  ### Basic Usage (Jira Example)
96
116
 
97
117
  ```ruby
@@ -215,80 +235,6 @@ rescue => e
215
235
  end
216
236
  ```
217
237
 
218
-
219
-
220
- ### Webhook Handling
221
-
222
- The gem provides helpers for parsing webhook payloads and verifying signatures (where applicable), but you need to implement your own webhook receiver endpoint (e.g., a Rails controller action).
223
-
224
- ```ruby
225
- # Example Rails Controller Action
226
- class WebhooksController < ApplicationController
227
- # Disable CSRF protection for webhook endpoints
228
- skip_before_action :verify_authenticity_token
229
-
230
- def jira_webhook
231
- adapter = ActiveProject.adapter(:jira)
232
- request_body = request.body.read
233
-
234
- # Verification (if applicable and implemented for your Jira setup)
235
- # signature = request.headers['X-Jira-Signature'] # Example header
236
- # unless adapter.verify_webhook_signature(request_body, signature)
237
- # render plain: 'Invalid signature', status: :unauthorized
238
- # return
239
- # end
240
-
241
- # Parse the event
242
- event = adapter.parse_webhook(request_body, request.headers)
243
-
244
- if event
245
- puts "Received Jira Event: #{event.event_type} for #{event.object_kind} #{event.object_key || event.object_id}"
246
- # Process the event (e.g., queue a background job)
247
- # handle_event(event)
248
- else
249
- puts "Received unhandled or unparseable Jira webhook"
250
- end
251
-
252
- head :ok # Respond to Jira quickly
253
- end
254
-
255
- def trello_webhook
256
- adapter = ActiveProject.adapter(:trello)
257
- request_body = request.body.read
258
- signature = request.headers['X-Trello-Webhook'] # Trello signature header
259
- callback_url = request.original_url # The URL Trello sent the webhook to
260
- trello_api_secret = ENV.fetch('TRELLO_API_SECRET') # You need your secret
261
-
262
- # Verification (Manual comparison using the helper)
263
- expected_signature = ActiveProject::Adapters::TrelloAdapter.compute_webhook_signature(
264
- callback_url,
265
- request_body,
266
- trello_api_secret
267
- )
268
-
269
- unless ActiveSupport::SecurityUtils.secure_compare(signature, expected_signature)
270
- render plain: 'Invalid Trello signature', status: :unauthorized
271
- return
272
- end
273
-
274
- # Parse the event
275
- event = adapter.parse_webhook(request_body)
276
-
277
- if event
278
- puts "Received Trello Event: #{event.event_type} for #{event.object_kind} #{event.object_id}"
279
- # Process the event
280
- else
281
- puts "Received unhandled or unparseable Trello webhook"
282
- end
283
-
284
- head :ok
285
- end
286
-
287
- # Add similar actions for other adapters like Basecamp
288
-
289
- end
290
- ```
291
-
292
238
  ## Development
293
239
 
294
240
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -297,7 +243,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
297
243
 
298
244
  ## Contributing
299
245
 
300
- Bug reports and pull requests are welcome on GitHub at https://github.com/seuros/activeproject.
246
+ Bug reports and pull requests are welcome on GitHub at https://github.com/seuros/active_project.
301
247
 
302
248
  ## License
303
249
 
@@ -122,13 +122,6 @@ module ActiveProject
122
122
  def connected?
123
123
  raise NotImplementedError, "#{self.class.name} must implement #connected?"
124
124
  end
125
-
126
-
127
- # Placeholder comments for data structures (to be defined elsewhere)
128
- # Example:
129
- # Project = Struct.new(:id, :key, :name, :adapter_source, keyword_init: true)
130
- # Issue = Struct.new(:id, :key, :title, :description, :status, :assignee, :project_id, :adapter_source, keyword_init: true)
131
- # Comment = Struct.new(:id, :body, :author, :created_at, :issue_id, :adapter_source, keyword_init: true)
132
125
  end
133
126
  end
134
127
  end
@@ -97,9 +97,10 @@ module ActiveProject
97
97
  def find_project(project_id)
98
98
  path = "projects/#{project_id}.json"
99
99
  project_data = make_request(:get, path)
100
+ return nil unless project_data
100
101
 
101
102
  # Raise NotFoundError if the project is trashed
102
- if project_data["status"] == "trashed"
103
+ if project_data && project_data["status"] == "trashed"
103
104
  raise NotFoundError, "Basecamp project ID #{project_id} is trashed."
104
105
  end
105
106
 
@@ -182,8 +183,8 @@ module ActiveProject
182
183
  # @param project_id [String, Integer] The ID of the project to recover.
183
184
  # @return [Boolean] true if recovery was successful (API returns 204).
184
185
  def untrash_project(project_id)
185
- path = "projects/#{project_id}/trash/recover.json"
186
- make_request(:put, path)
186
+ path = "projects/#{project_id}.json"
187
+ make_request(:put, path, { "status": "active" }.to_json)
187
188
  true # Return true if make_request doesn't raise an error
188
189
  end
189
190
 
@@ -193,9 +194,6 @@ module ActiveProject
193
194
  true # Return true if make_request doesn't raise an error
194
195
  end
195
196
 
196
-
197
-
198
-
199
197
  # Lists To-dos within a specific project.
200
198
  # @param project_id [String, Integer] The ID of the Basecamp project.
201
199
  # @param options [Hash] Optional options. Accepts :todolist_id.
@@ -449,15 +447,9 @@ module ActiveProject
449
447
  # Helper method for making requests.
450
448
  def make_request(method, path, body = nil, query_params = {})
451
449
  full_path = path.start_with?("/") ? path[1..] : path
452
- # Removed debug puts for cleaner output
453
- # puts "[DEBUG BC Request] Method: #{method.upcase}"
454
- # puts "[DEBUG BC Request] Path: #{full_path}"
455
- # puts "[DEBUG BC Request] Body: #{body.inspect}"
456
- # puts "[DEBUG BC Request] Query Params: #{query_params.inspect}"
457
450
 
458
451
  response = @connection.run_request(method, full_path, body, nil) do |req|
459
452
  req.params.update(query_params) unless query_params.empty?
460
- # puts "[DEBUG BC Request] Headers: #{req.headers.inspect}"
461
453
  end
462
454
  return nil if response.status == 204 # Handle No Content for POST/DELETE completion
463
455
  JSON.parse(response.body) if response.body && !response.body.empty?
@@ -469,10 +461,6 @@ module ActiveProject
469
461
  def handle_faraday_error(error)
470
462
  status = error.response_status
471
463
  body = error.response_body
472
- # Removed debug puts for cleaner output
473
- # puts "[DEBUG BC Response] Status: #{error.response_status}"
474
- # puts "[DEBUG BC Response] Headers: #{error.response_headers.inspect}"
475
- # puts "[DEBUG BC Response] Body: #{error.response_body.inspect}"
476
464
 
477
465
  parsed_body = JSON.parse(body) rescue { "error" => body }
478
466
  message = parsed_body["error"] || parsed_body["message"] || "Unknown Basecamp Error"
@@ -520,7 +508,7 @@ module ActiveProject
520
508
  status: status,
521
509
  assignees: assignees, # Use mapped User resources
522
510
  reporter: reporter, # Use mapped User resource
523
- project_id: project_id.to_i,
511
+ project_id: project_id,
524
512
  created_at: todo_data["created_at"] ? Time.parse(todo_data["created_at"]) : nil,
525
513
  updated_at: todo_data["updated_at"] ? Time.parse(todo_data["updated_at"]) : nil,
526
514
  due_on: todo_data["due_on"] ? Date.parse(todo_data["due_on"]) : nil,
@@ -68,7 +68,7 @@ module ActiveProject
68
68
 
69
69
  projects_data.each do |project_data|
70
70
  all_projects << Resources::Project.new(self, # Pass adapter instance
71
- id: project_data["id"]&.to_i, # Convert to integer
71
+ id: project_data["id"], # Convert to integer
72
72
  key: project_data["key"],
73
73
  name: project_data["name"],
74
74
  adapter_source: :jira,
@@ -94,13 +94,12 @@ module ActiveProject
94
94
  project_data = make_request(:get, path)
95
95
 
96
96
  Resources::Project.new(self, # Pass adapter instance
97
- id: project_data["id"]&.to_i, # Convert to integer
97
+ id: project_data["id"].to_i, # Convert to integer
98
98
  key: project_data["key"],
99
99
  name: project_data["name"],
100
100
  adapter_source: :jira,
101
101
  raw_data: project_data
102
102
  )
103
- # Note: make_request handles raising NotFoundError on 404
104
103
  end
105
104
 
106
105
  # Creates a new project in Jira.
@@ -549,9 +548,9 @@ module ActiveProject
549
548
 
550
549
  # Maps raw Jira issue data hash to an Issue resource.
551
550
  def map_issue_data(issue_data)
552
- fields = issue_data["fields"]
551
+ fields = issue_data && issue_data["fields"]
553
552
  # Ensure assignee is mapped correctly into an array
554
- assignee_user = map_user_data(fields["assignee"])
553
+ assignee_user = fields && map_user_data(fields["assignee"])
555
554
  assignees_array = assignee_user ? [ assignee_user ] : []
556
555
 
557
556
  Resources::Issue.new(self, # Pass adapter instance
@@ -14,7 +14,6 @@ module ActiveProject
14
14
  # API Docs: https://developer.atlassian.com/cloud/trello/rest/
15
15
  class TrelloAdapter < Base
16
16
  BASE_URL = "https://api.trello.com/1/"
17
- USER_AGENT = "ActiveProject Gem (github.com/seuros/activeproject)"
18
17
 
19
18
  # Computes the expected Trello webhook signature.
20
19
  # @param callback_url [String] The exact URL registered for the webhook.
@@ -18,7 +18,7 @@ module ActiveProject
18
18
  # Add other associations like :project for an issue?
19
19
  else raise "Unknown association: #{association_name}"
20
20
  end
21
- end # End initialize
21
+ end
22
22
 
23
23
  # --- Proxy Methods ---
24
24
 
@@ -16,44 +16,52 @@ module ActiveProject
16
16
 
17
17
  def initialize
18
18
  @adapter_configs = {}
19
+ @user_agent = "ActiveProject Gem (github.com/seuros/active_project)"
19
20
  end
20
- @user_agent = "ActiveProject Gem (github.com/seuros/activeproject)"
21
21
 
22
22
  # Adds or updates the configuration for a specific adapter.
23
23
  # If a block is given and a specific configuration class exists for the adapter,
24
24
  # an instance of that class is yielded to the block. Otherwise, a basic
25
25
  # configuration object is created from the options hash.
26
26
  #
27
- # @param name [Symbol] The name of the adapter (e.g., :jira, :trello).
27
+ # @param adapter_type [Symbol] The name of the adapter (e.g., :basecamp, :jira, :trello).
28
+ # @param instance_name [Symbol, Hash] The name of the adapter instance (default: :primary) or options hash.
28
29
  # @param options [Hash] Configuration options for the adapter (e.g., site, api_key, token).
29
30
  # @yield [BaseAdapterConfiguration] Yields an adapter-specific configuration object if a block is given.
30
- def add_adapter(name, options = {}, &block)
31
- unless name.is_a?(Symbol)
32
- raise ArgumentError, "Adapter name must be a Symbol (e.g., :jira)"
31
+ def add_adapter(adapter_type, instance_name = :primary, options = {}, &block)
32
+ unless adapter_type.is_a?(Symbol)
33
+ raise ArgumentError, "Adapter type must be a Symbol (e.g., :basecamp)"
33
34
  end
34
35
 
35
- config_class = ADAPTER_CONFIG_CLASSES[name]
36
+ # Handle the case where instance_name is actually the options hash
37
+ if instance_name.is_a?(Hash) && options.empty?
38
+ options = instance_name
39
+ instance_name = :primary
40
+ end
41
+
42
+ key = "#{adapter_type}_#{instance_name}".to_sym
43
+
44
+ config_class = ADAPTER_CONFIG_CLASSES[adapter_type]
36
45
 
37
- # Use specific config class if block is given and class exists
38
46
  if block && config_class
39
47
  adapter_config_obj = config_class.new(options)
40
- yield adapter_config_obj # Allow block to modify the specific config object
41
- @adapter_configs[name] = adapter_config_obj.freeze
42
- # Use specific config class if no block but class exists (handles options like status_mappings passed directly)
48
+ yield adapter_config_obj
49
+ @adapter_configs[key] = adapter_config_obj.freeze
43
50
  elsif config_class
44
- adapter_config_obj = config_class.new(options)
45
- @adapter_configs[name] = adapter_config_obj.freeze
46
- # Fallback to base config class if no specific class or no block
51
+ adapter_config_obj = config_class.new(options)
52
+ @adapter_configs[key] = adapter_config_obj.freeze
47
53
  else
48
- @adapter_configs[name] = Configurations::BaseAdapterConfiguration.new(options).freeze
54
+ @adapter_configs[key] = Configurations::BaseAdapterConfiguration.new(options).freeze
49
55
  end
50
56
  end
51
57
 
52
58
  # Retrieves the configuration object for a specific adapter.
53
- # @param name [Symbol] The name of the adapter.
59
+ # @param adapter_type [Symbol] The name of the adapter (e.g., :jira, :trello).
60
+ # @param instance_name [Symbol] The name of the adapter instance (default: :primary).
54
61
  # @return [BaseAdapterConfiguration, nil] The configuration object or nil if not found.
55
- def adapter_config(name)
56
- @adapter_configs[name]
62
+ def adapter_config(adapter_type, instance_name = :primary)
63
+ key = "#{adapter_type}_#{instance_name}".to_sym
64
+ @adapter_configs[key]
57
65
  end
58
66
  end
59
67
  end
@@ -1,16 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_resource"
4
-
5
3
  module ActiveProject
6
4
  module Resources
7
5
  # Represents a Comment on an Issue
8
6
  class Comment < BaseResource
9
7
  def_members :id, :body, :author, :created_at, :updated_at, :issue_id,
10
8
  :adapter_source
11
- # raw_data and adapter are inherited from BaseResource
12
-
13
- # Add comment-specific methods here later (e.g., save, update, delete)
14
9
  end
15
10
  end
16
11
  end
@@ -34,8 +34,6 @@ module ActiveProject
34
34
  def comments
35
35
  AssociationProxy.new(owner: self, adapter: @adapter, association_name: :comments)
36
36
  end
37
-
38
- # Add issue-specific methods here later (e.g., comments association, save, update)
39
37
  end
40
38
  end
41
39
  end
@@ -13,8 +13,6 @@ module ActiveProject
13
13
  def issues
14
14
  AssociationProxy.new(owner: self, adapter: @adapter, association_name: :issues)
15
15
  end
16
-
17
- # Add project-specific methods here later (e.g., issues association)
18
16
  end
19
17
  end
20
18
  end
@@ -7,7 +7,6 @@ module ActiveProject
7
7
  # Represents a User
8
8
  class User < BaseResource
9
9
  def_members :id, :name, :email, :adapter_source
10
- # raw_data and adapter are inherited from BaseResource
11
10
  end
12
11
  end
13
12
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveProject
2
- VERSION = "0.0.0"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/activeproject.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "zeitwerk"
2
+ require "concurrent"
2
3
  require_relative "active_project/errors"
3
4
  require_relative "active_project/version"
4
5
 
@@ -14,6 +15,11 @@ module ActiveProject
14
15
  yield(configuration)
15
16
  end
16
17
 
18
+ # Resets all cached adapters, forcing them to be re-initialized with current configuration
19
+ # @return [void]
20
+ def reset_adapters
21
+ adapter_registry.clear if defined?(@adapter_registry) && @adapter_registry
22
+ end
17
23
 
18
24
  # Returns the configured User-Agent string, including the gem version.
19
25
  # @return [String] The User-Agent string.
@@ -23,33 +29,79 @@ module ActiveProject
23
29
  end
24
30
 
25
31
  # Returns a memoized instance of the requested adapter.
26
- # @param adapter_name [Symbol] The name of the adapter (e.g., :jira, :trello).
27
- # @return [Adapters::Base] An instance of the requested adapter.
32
+ # Thread-safe implementation using Concurrent::Map for the adapter registry.
33
+ # @param adapter_type [Symbol] The name of the adapter (e.g., :jira, :trello).
34
+ # @param instance_name [Symbol] The name of the adapter instance (default: :primary).
35
+ # @return [ActiveProject::Adapters::Base] An instance of a specific adapter class that inherits from Base.
28
36
  # @raise [ArgumentError] if the adapter configuration is missing or invalid.
29
37
  # @raise [LoadError] if the adapter class cannot be found.
30
- def adapter(adapter_name)
31
- @adapters ||= {}
32
- @adapters[adapter_name] ||= begin
33
- config = configuration.adapter_config(adapter_name)
38
+ # @raise [NameError] if the adapter class cannot be found after loading the file.
39
+ def adapter(adapter_type, instance_name = :primary)
40
+ key = "#{adapter_type}_#{instance_name}".to_sym
41
+
42
+ adapter_registry.fetch_or_store(key) do
43
+ config = configuration.adapter_config(adapter_type, instance_name)
34
44
 
35
45
  unless config.is_a?(ActiveProject::Configurations::BaseAdapterConfiguration)
36
- raise ArgumentError, "Configuration for adapter ':#{adapter_name}' not found or invalid. Use ActiveProject.configure."
46
+ available_configs = list_available_configurations
47
+
48
+ error_message = "Configuration for adapter ':#{adapter_type}' (instance ':#{instance_name}') not found or invalid.\n\n"
49
+
50
+ if available_configs.empty?
51
+ error_message += "No adapters are currently configured. "
52
+ else
53
+ error_message += "Available configurations:\n"
54
+ available_configs.each do |adapter_key, config_type|
55
+ error_message += " * #{adapter_key} (#{config_type})\n"
56
+ end
57
+ end
58
+
59
+ error_message += "\nTo configure, use:\n"
60
+ error_message += " ActiveProject.configure do |config|\n"
61
+ error_message += " config.add_adapter :#{adapter_type}, :#{instance_name}, { your_options_here }\n"
62
+ error_message += " end"
63
+
64
+ raise ArgumentError, error_message
37
65
  end
38
66
 
39
- # Use string-based constant lookup with the full namespace path
40
- adapter_class_name = "ActiveProject::Adapters::#{adapter_name.to_s.capitalize}Adapter"
67
+ adapter_class_name = "ActiveProject::Adapters::#{adapter_type.to_s.capitalize}Adapter"
41
68
 
42
- # Ensure the adapter class is loaded
43
- require "active_project/adapters/#{adapter_name}_adapter"
69
+ begin
70
+ require "active_project/adapters/#{adapter_type}_adapter"
71
+ rescue LoadError => e
72
+ error_message = "Could not load adapter '#{adapter_type}'.\n"
73
+ error_message += "Make sure you have defined the class #{adapter_class_name} in active_project/adapters/#{adapter_type}_adapter.rb"
74
+ raise LoadError, error_message
75
+ end
44
76
 
45
- # Get the constant with the full path
46
- adapter_class = Object.const_get(adapter_class_name)
77
+ begin
78
+ adapter_class = Object.const_get(adapter_class_name)
79
+ rescue NameError => e
80
+ error_message = "Could not find adapter class #{adapter_class_name}.\n"
81
+ error_message += "Make sure you have defined the class correctly in active_project/adapters/#{adapter_type}_adapter.rb"
82
+ raise NameError, error_message
83
+ end
47
84
 
48
85
  adapter_class.new(config: config)
49
- rescue LoadError, NameError => e
50
- raise LoadError, "Could not find adapter class #{adapter_class_name}: #{e.message}"
51
86
  end
52
87
  end
88
+
89
+ # Lists all available configurations in the format adapter_name:instance_name
90
+ # @return [Hash] A hash mapping configuration keys to their configuration types
91
+ private def list_available_configurations
92
+ result = {}
93
+ configuration.adapter_configs.each do |key, config|
94
+ config_type = config.class.name.split("::").last
95
+ result[key] = config_type
96
+ end
97
+ result
98
+ end
99
+
100
+ # Returns a thread-safe map that stores adapter instances
101
+ # @return [Concurrent::Map] Thread-safe hash implementation
102
+ private def adapter_registry
103
+ @adapter_registry ||= Concurrent::Map.new
104
+ end
53
105
  end
54
106
  end
55
107
 
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeproject
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 2025-04-07 00:00:00.000000000 Z
11
+ date: 2025-04-10 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: activesupport
@@ -35,14 +36,14 @@ dependencies:
35
36
  requirements:
36
37
  - - ">="
37
38
  - !ruby/object:Gem::Version
38
- version: '0'
39
+ version: '2.0'
39
40
  type: :runtime
40
41
  prerelease: false
41
42
  version_requirements: !ruby/object:Gem::Requirement
42
43
  requirements:
43
44
  - - ">="
44
45
  - !ruby/object:Gem::Version
45
- version: '0'
46
+ version: '2.0'
46
47
  - !ruby/object:Gem::Dependency
47
48
  name: faraday-retry
48
49
  requirement: !ruby/object:Gem::Requirement
@@ -101,12 +102,13 @@ files:
101
102
  - lib/active_project/version.rb
102
103
  - lib/active_project/webhook_event.rb
103
104
  - lib/activeproject.rb
104
- homepage: https://github.com/seuros/activeproject
105
+ homepage: https://github.com/seuros/active_project
105
106
  licenses:
106
107
  - MIT
107
108
  metadata:
108
- homepage_uri: https://github.com/seuros/activeproject
109
- source_code_uri: https://github.com/seuros/activeproject
109
+ homepage_uri: https://github.com/seuros/active_project
110
+ source_code_uri: https://github.com/seuros/active_project
111
+ post_install_message:
110
112
  rdoc_options: []
111
113
  require_paths:
112
114
  - lib
@@ -121,7 +123,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
123
  - !ruby/object:Gem::Version
122
124
  version: '0'
123
125
  requirements: []
124
- rubygems_version: 3.6.2
126
+ rubygems_version: 3.5.22
127
+ signing_key:
125
128
  specification_version: 4
126
129
  summary: A standardized Ruby interface for multiple project management APIs (Jira,
127
130
  Basecamp, Trello, etc.).