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 +4 -4
- data/README.md +28 -82
- data/lib/active_project/adapters/base.rb +0 -7
- data/lib/active_project/adapters/basecamp_adapter.rb +5 -17
- data/lib/active_project/adapters/jira_adapter.rb +4 -5
- data/lib/active_project/adapters/trello_adapter.rb +0 -1
- data/lib/active_project/association_proxy.rb +1 -1
- data/lib/active_project/configuration.rb +25 -17
- data/lib/active_project/resources/comment.rb +0 -5
- data/lib/active_project/resources/issue.rb +0 -2
- data/lib/active_project/resources/project.rb +0 -2
- data/lib/active_project/resources/user.rb +0 -1
- data/lib/active_project/version.rb +1 -1
- data/lib/activeproject.rb +67 -15
- metadata +11 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b69a05303fa485956e8bdf7e56984aa5690df8f4bb7b9119253d760f0940916
|
4
|
+
data.tar.gz: 45dab21a69a42325b067f01510f47e16f9b8c9ebb312b11793e1b0c447f563a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 (
|
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
|
72
|
+
Configure multiple adapters, optionally with named instances (default is `:primary`):
|
73
73
|
|
74
74
|
```ruby
|
75
75
|
ActiveProject.configure do |config|
|
76
|
-
#
|
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'),
|
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
|
-
#
|
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
|
-
#
|
91
|
-
|
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/
|
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}
|
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
|
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"]
|
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"]
|
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.
|
@@ -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
|
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(
|
31
|
-
unless
|
32
|
-
raise ArgumentError, "Adapter
|
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
|
-
|
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
|
41
|
-
@adapter_configs[
|
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
|
-
|
45
|
-
|
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[
|
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
|
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(
|
56
|
-
|
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
|
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
|
-
#
|
27
|
-
# @
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
43
|
-
|
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
|
-
|
46
|
-
|
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.
|
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-
|
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/
|
105
|
+
homepage: https://github.com/seuros/active_project
|
105
106
|
licenses:
|
106
107
|
- MIT
|
107
108
|
metadata:
|
108
|
-
homepage_uri: https://github.com/seuros/
|
109
|
-
source_code_uri: https://github.com/seuros/
|
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.
|
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.).
|