activeproject 0.0.0 → 0.1.1

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +28 -82
  3. data/Rakefile +4 -2
  4. data/lib/active_project/adapters/base.rb +3 -14
  5. data/lib/active_project/adapters/basecamp/comments.rb +27 -0
  6. data/lib/active_project/adapters/basecamp/connection.rb +49 -0
  7. data/lib/active_project/adapters/basecamp/issues.rb +139 -0
  8. data/lib/active_project/adapters/basecamp/lists.rb +54 -0
  9. data/lib/active_project/adapters/basecamp/projects.rb +110 -0
  10. data/lib/active_project/adapters/basecamp/webhooks.rb +73 -0
  11. data/lib/active_project/adapters/basecamp_adapter.rb +46 -449
  12. data/lib/active_project/adapters/jira/comments.rb +28 -0
  13. data/lib/active_project/adapters/jira/connection.rb +47 -0
  14. data/lib/active_project/adapters/jira/issues.rb +132 -0
  15. data/lib/active_project/adapters/jira/projects.rb +100 -0
  16. data/lib/active_project/adapters/jira/transitions.rb +68 -0
  17. data/lib/active_project/adapters/jira/webhooks.rb +89 -0
  18. data/lib/active_project/adapters/jira_adapter.rb +59 -486
  19. data/lib/active_project/adapters/trello/comments.rb +21 -0
  20. data/lib/active_project/adapters/trello/connection.rb +37 -0
  21. data/lib/active_project/adapters/trello/issues.rb +117 -0
  22. data/lib/active_project/adapters/trello/lists.rb +27 -0
  23. data/lib/active_project/adapters/trello/projects.rb +82 -0
  24. data/lib/active_project/adapters/trello/webhooks.rb +91 -0
  25. data/lib/active_project/adapters/trello_adapter.rb +54 -377
  26. data/lib/active_project/association_proxy.rb +10 -3
  27. data/lib/active_project/configuration.rb +23 -17
  28. data/lib/active_project/configurations/trello_configuration.rb +1 -3
  29. data/lib/active_project/resource_factory.rb +20 -10
  30. data/lib/active_project/resources/comment.rb +0 -5
  31. data/lib/active_project/resources/issue.rb +0 -5
  32. data/lib/active_project/resources/project.rb +0 -3
  33. data/lib/active_project/resources/user.rb +0 -1
  34. data/lib/active_project/version.rb +3 -1
  35. data/lib/activeproject.rb +67 -15
  36. metadata +26 -8
@@ -23,13 +23,11 @@ module ActiveProject
23
23
  # Call adapter method with appropriate arguments
24
24
  if primary_arg
25
25
  @adapter.send(list_method, primary_arg, options)
26
- else
26
+ elsif @adapter.method(list_method).arity.zero?
27
27
  # Handle case where list method might not take options (like list_projects)
28
- if @adapter.method(list_method).arity == 0
29
- @adapter.send(list_method)
30
- else
31
- @adapter.send(list_method, options)
32
- end
28
+ @adapter.send(list_method)
29
+ else
30
+ @adapter.send(list_method, options)
33
31
  end
34
32
  end
35
33
 
@@ -89,7 +87,7 @@ module ActiveProject
89
87
  def create(attributes = {})
90
88
  # Determine the correct adapter create method based on resource type
91
89
  create_method = determine_create_method
92
- # Note: Assumes create methods on adapters take attributes hash directly
90
+ # NOTE: Assumes create methods on adapters take attributes hash directly
93
91
  # Context like project_id needs to be part of the attributes hash if required by adapter
94
92
  @adapter.send(create_method, attributes)
95
93
  # A full implementation would likely involve build then save:
@@ -106,7 +104,11 @@ module ActiveProject
106
104
  when "ActiveProject::Resources::Issue" then :list_issues
107
105
  else raise "Cannot determine list method for #{@resource_class.name}"
108
106
  end
109
- raise NotImplementedError, "#{@adapter.class.name} does not implement ##{method_name}" unless @adapter.respond_to?(method_name)
107
+ unless @adapter.respond_to?(method_name)
108
+ raise NotImplementedError,
109
+ "#{@adapter.class.name} does not implement ##{method_name}"
110
+ end
111
+
110
112
  method_name
111
113
  end
112
114
 
@@ -116,14 +118,22 @@ module ActiveProject
116
118
  when "ActiveProject::Resources::Issue" then :find_issue
117
119
  else raise "Cannot determine find method for #{@resource_class.name}"
118
120
  end
119
- raise NotImplementedError, "#{@adapter.class.name} does not implement ##{method_name}" unless @adapter.respond_to?(method_name)
121
+ unless @adapter.respond_to?(method_name)
122
+ raise NotImplementedError,
123
+ "#{@adapter.class.name} does not implement ##{method_name}"
124
+ end
125
+
120
126
  method_name
121
127
  end
122
128
 
123
129
  def determine_create_method
124
130
  singular_name = @resource_class.name.split("::").last.downcase.to_sym
125
131
  method_name = :"create_#{singular_name}"
126
- raise NotImplementedError, "#{@adapter.class.name} does not implement ##{method_name}" unless @adapter.respond_to?(method_name)
132
+ unless @adapter.respond_to?(method_name)
133
+ raise NotImplementedError,
134
+ "#{@adapter.class.name} does not implement ##{method_name}"
135
+ end
136
+
127
137
  method_name
128
138
  end
129
139
  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
@@ -7,8 +7,6 @@ module ActiveProject
7
7
  def_members :id, :key, :title, :description, :status, :assignees,
8
8
  :reporter, :project_id, :created_at, :updated_at, :due_on,
9
9
  :priority, :adapter_source
10
- # raw_data and adapter are inherited from BaseResource
11
-
12
10
 
13
11
  # Saves the issue (creates if new, updates if existing).
14
12
  # Placeholder - Full implementation requires attribute tracking and adapter delegation.
@@ -28,14 +26,11 @@ module ActiveProject
28
26
  raise NotImplementedError, "#update not yet implemented for #{self.class.name}"
29
27
  end
30
28
 
31
-
32
29
  # Returns an association proxy for accessing comments on this issue.
33
30
  # @return [AssociationProxy<Resources::Comment>]
34
31
  def comments
35
32
  AssociationProxy.new(owner: self, adapter: @adapter, association_name: :comments)
36
33
  end
37
-
38
- # Add issue-specific methods here later (e.g., comments association, save, update)
39
34
  end
40
35
  end
41
36
  end
@@ -7,14 +7,11 @@ module ActiveProject
7
7
  def_members :id, :key, :name, :adapter_source
8
8
  # raw_data and adapter are inherited from BaseResource
9
9
 
10
-
11
10
  # Returns an association proxy for accessing issues within this project.
12
11
  # @return [AssociationProxy<Resources::Issue>]
13
12
  def issues
14
13
  AssociationProxy.new(owner: self, adapter: @adapter, association_name: :issues)
15
14
  end
16
-
17
- # Add project-specific methods here later (e.g., issues association)
18
15
  end
19
16
  end
20
17
  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,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveProject
2
- VERSION = "0.0.0"
4
+ VERSION = "0.1.1"
3
5
  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
+ 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
+ def adapter_registry
103
+ @adapter_registry ||= Concurrent::Map.new
104
+ end
53
105
  end
54
106
  end
55
107
 
metadata CHANGED
@@ -1,13 +1,13 @@
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.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-07 00:00:00.000000000 Z
10
+ date: 2025-04-10 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -35,14 +35,14 @@ dependencies:
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
- version: '0'
38
+ version: '2.0'
39
39
  type: :runtime
40
40
  prerelease: false
41
41
  version_requirements: !ruby/object:Gem::Requirement
42
42
  requirements:
43
43
  - - ">="
44
44
  - !ruby/object:Gem::Version
45
- version: '0'
45
+ version: '2.0'
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: faraday-retry
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -84,8 +84,26 @@ files:
84
84
  - README.md
85
85
  - Rakefile
86
86
  - lib/active_project/adapters/base.rb
87
+ - lib/active_project/adapters/basecamp/comments.rb
88
+ - lib/active_project/adapters/basecamp/connection.rb
89
+ - lib/active_project/adapters/basecamp/issues.rb
90
+ - lib/active_project/adapters/basecamp/lists.rb
91
+ - lib/active_project/adapters/basecamp/projects.rb
92
+ - lib/active_project/adapters/basecamp/webhooks.rb
87
93
  - lib/active_project/adapters/basecamp_adapter.rb
94
+ - lib/active_project/adapters/jira/comments.rb
95
+ - lib/active_project/adapters/jira/connection.rb
96
+ - lib/active_project/adapters/jira/issues.rb
97
+ - lib/active_project/adapters/jira/projects.rb
98
+ - lib/active_project/adapters/jira/transitions.rb
99
+ - lib/active_project/adapters/jira/webhooks.rb
88
100
  - lib/active_project/adapters/jira_adapter.rb
101
+ - lib/active_project/adapters/trello/comments.rb
102
+ - lib/active_project/adapters/trello/connection.rb
103
+ - lib/active_project/adapters/trello/issues.rb
104
+ - lib/active_project/adapters/trello/lists.rb
105
+ - lib/active_project/adapters/trello/projects.rb
106
+ - lib/active_project/adapters/trello/webhooks.rb
89
107
  - lib/active_project/adapters/trello_adapter.rb
90
108
  - lib/active_project/association_proxy.rb
91
109
  - lib/active_project/configuration.rb
@@ -101,12 +119,12 @@ files:
101
119
  - lib/active_project/version.rb
102
120
  - lib/active_project/webhook_event.rb
103
121
  - lib/activeproject.rb
104
- homepage: https://github.com/seuros/activeproject
122
+ homepage: https://github.com/seuros/active_project
105
123
  licenses:
106
124
  - MIT
107
125
  metadata:
108
- homepage_uri: https://github.com/seuros/activeproject
109
- source_code_uri: https://github.com/seuros/activeproject
126
+ homepage_uri: https://github.com/seuros/active_project
127
+ source_code_uri: https://github.com/seuros/active_project
110
128
  rdoc_options: []
111
129
  require_paths:
112
130
  - lib
@@ -121,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
139
  - !ruby/object:Gem::Version
122
140
  version: '0'
123
141
  requirements: []
124
- rubygems_version: 3.6.2
142
+ rubygems_version: 3.6.5
125
143
  specification_version: 4
126
144
  summary: A standardized Ruby interface for multiple project management APIs (Jira,
127
145
  Basecamp, Trello, etc.).