the-active-rpc 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7633a03bfd747226a57e031d46a3c7f22319e3602da77e6722ed989807d56cf5
4
+ data.tar.gz: cdb02a60468c746b81dde375f49f3133bd60b029640dc766c682a5b27b86b74a
5
+ SHA512:
6
+ metadata.gz: 8b0c1b6fd916af175584915a0b2002c9970cd9df969f8a2e524da725ae008dd70c5871ff9c56b19a20ea0ee3c4b1a0c4d1f03031f4fa3d0d103a86bd9096a41a
7
+ data.tar.gz: 95b9a7d6ca83b1f0c0e147d82230bddd7833916f024c87d9de9c4203993bcaf738c5915c335a173ac78cf90247f6c56805d2a17f7c631fa923f4c282a252af96
data/CHANGELOG.md ADDED
@@ -0,0 +1,82 @@
1
+ # Changelog
2
+
3
+ ## [1.0.1](https://github.com/shqear93/the-active-rpc/compare/the-active-rpc/v1.0.0...the-active-rpc/v1.0.1) (2026-06-01)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * trigger gem publish after repo rename ([0b231d1](https://github.com/shqear93/the-active-rpc/commit/0b231d1730579305c6a88d69ff4d9da85b54f49d))
9
+
10
+ ## 1.0.0 (2026-06-01)
11
+
12
+
13
+ ### ⚠ BREAKING CHANGES
14
+
15
+ * initial release of the-active-rpc
16
+
17
+ ### Features
18
+
19
+ * initial active_rpc gem extraction ([efe3202](https://github.com/shqear93/active_rpc/commit/efe32024077d6bfeff97c9ede6d7134648bf7fb0))
20
+ * initial release of the-active-rpc ([ee304aa](https://github.com/shqear93/active_rpc/commit/ee304aaaab5c45eccb60e4efec53c38d16884ac6))
21
+ * port specs, clean debug output, add CI/release-please ([1ac312c](https://github.com/shqear93/active_rpc/commit/1ac312c7a15b13968e9c5a5a36315ce5eb0ac1a5))
22
+
23
+
24
+ ### Bug Fixes
25
+
26
+ * correct Quick Start heading case in README ([1d23fb0](https://github.com/shqear93/active_rpc/commit/1d23fb045d4dbbab4985dcb36456be00d641e2ce))
27
+ * pin dry-configurable to ~> 1.3.0 for Ruby 3.1/3.2 compat ([b5ed5a2](https://github.com/shqear93/active_rpc/commit/b5ed5a29b4029dec3fc78b9b3c786cc3dcdc0aba))
28
+ * pin pagy to ~> 9.4 for Ruby 3.1/3.2 compat ([15b9fde](https://github.com/shqear93/active_rpc/commit/15b9fdeed1fde4201e999a886ad160486402a5ba))
29
+ * trigger release 1.2.2 ([7ac02bf](https://github.com/shqear93/active_rpc/commit/7ac02bf449e1854927a5144fde0bc9d3a57300a5))
30
+ * update README heading ([f1ae9f5](https://github.com/shqear93/active_rpc/commit/f1ae9f500c67fb22460ca184e01951d49599ad39))
31
+
32
+ ## [1.2.1](https://github.com/shqear93/active_rpc/compare/the-active-rpc/v1.2.0...the-active-rpc/v1.2.1) (2026-06-01)
33
+
34
+
35
+ ### Bug Fixes
36
+
37
+ * update README heading ([f1ae9f5](https://github.com/shqear93/active_rpc/commit/f1ae9f500c67fb22460ca184e01951d49599ad39))
38
+
39
+ ## [1.2.0](https://github.com/shqear93/active_rpc/compare/the-active-rpc-v1.1.2...the-active-rpc/v1.2.0) (2026-06-01)
40
+
41
+
42
+ ### Features
43
+
44
+ * initial active_rpc gem extraction ([efe3202](https://github.com/shqear93/active_rpc/commit/efe32024077d6bfeff97c9ede6d7134648bf7fb0))
45
+ * port specs, clean debug output, add CI/release-please ([1ac312c](https://github.com/shqear93/active_rpc/commit/1ac312c7a15b13968e9c5a5a36315ce5eb0ac1a5))
46
+
47
+
48
+ ### Bug Fixes
49
+
50
+ * correct Quick Start heading case in README ([1d23fb0](https://github.com/shqear93/active_rpc/commit/1d23fb045d4dbbab4985dcb36456be00d641e2ce))
51
+ * pin dry-configurable to ~> 1.3.0 for Ruby 3.1/3.2 compat ([b5ed5a2](https://github.com/shqear93/active_rpc/commit/b5ed5a29b4029dec3fc78b9b3c786cc3dcdc0aba))
52
+ * pin pagy to ~> 9.4 for Ruby 3.1/3.2 compat ([15b9fde](https://github.com/shqear93/active_rpc/commit/15b9fdeed1fde4201e999a886ad160486402a5ba))
53
+
54
+ ## [1.1.2](https://github.com/shqear93/active_rpc/compare/active_rpc/v1.1.1...active_rpc/v1.1.2) (2026-06-01)
55
+
56
+
57
+ ### Bug Fixes
58
+
59
+ * pin dry-configurable to ~> 1.3.0 for Ruby 3.1/3.2 compat ([b5ed5a2](https://github.com/shqear93/active_rpc/commit/b5ed5a29b4029dec3fc78b9b3c786cc3dcdc0aba))
60
+ * pin pagy to ~> 9.4 for Ruby 3.1/3.2 compat ([15b9fde](https://github.com/shqear93/active_rpc/commit/15b9fdeed1fde4201e999a886ad160486402a5ba))
61
+
62
+ ## [1.1.1](https://github.com/shqear93/active_rpc/compare/active_rpc/v1.1.0...active_rpc/v1.1.1) (2026-06-01)
63
+
64
+
65
+ ### Bug Fixes
66
+
67
+ * correct Quick Start heading case in README ([1d23fb0](https://github.com/shqear93/active_rpc/commit/1d23fb045d4dbbab4985dcb36456be00d641e2ce))
68
+
69
+ ## [1.1.0](https://github.com/shqear93/active_rpc/compare/active_rpc-v1.0.0...active_rpc/v1.1.0) (2026-06-01)
70
+
71
+
72
+ ### Features
73
+
74
+ * initial active_rpc gem extraction ([efe3202](https://github.com/shqear93/active_rpc/commit/efe32024077d6bfeff97c9ede6d7134648bf7fb0))
75
+ * port specs, clean debug output, add CI/release-please ([1ac312c](https://github.com/shqear93/active_rpc/commit/1ac312c7a15b13968e9c5a5a36315ce5eb0ac1a5))
76
+
77
+ ## 1.0.0
78
+
79
+ - Extract ActiveRpc from athar_rpc into standalone gem
80
+ - Generic namespace (`ActiveRpc`) with no Athar-specific proto definitions
81
+ - Configurable service resolver
82
+ - Backward compatibility aliases in `athar_rpc` wrapper gem
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Athar Association
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # The Active RPC
2
+ # ActiveRpc
3
+
4
+ ActiveRecord associations for gRPC services.
5
+
6
+ ## Quick start
7
+
8
+ ```ruby
9
+ class Employee < ApplicationRecord
10
+ include ActiveRpc::ModelExtensions
11
+
12
+ active_rpc :core, :User, foreign_key: :user_id do
13
+ attribute :name, :string
14
+ attribute :email, :string
15
+ attribute :department, :string
16
+ end
17
+ end
18
+ ```
19
+
20
+ ```ruby
21
+ # Bulk load user data in one gRPC call
22
+ Employee.all.with_user_data
23
+
24
+ # Filter employees by remote user attribute
25
+ Employee.where_rpc(:user_ids, department: 'Engineering')
26
+ ```
27
+
28
+ ## Features
29
+
30
+ - **rpc_belongs_to** — Treat remote gRPC resources like local associations
31
+ - **Bulk loading** — `with_user_data` fetches all related records in one gRPC call
32
+ - **Cross-service filtering** — `where_rpc` filters local records by remote attributes
33
+ - **Dirty tracking** — Change remote attributes locally, propagate on save
34
+ - **Bidirectional create/update** — Creating/updating local records creates/updates remote resources
35
+
36
+ ## Installation
37
+
38
+ Add to your Gemfile:
39
+
40
+ ```ruby
41
+ gem 'the-active-rpc'
42
+ ```
43
+
44
+ ## Configuration
45
+
46
+ ```ruby
47
+ ActiveRpc.configure do |config|
48
+ config.service_resolver = ->(subsystem, service_name) {
49
+ "::MyApp::#{subsystem.camelize}::#{service_name}".constantize
50
+ }
51
+ end
52
+ ```
53
+
54
+ ## License
55
+
56
+ MIT
57
+
58
+ ## License
59
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,30 @@
1
+ require 'dry-configurable'
2
+ require 'yaml'
3
+
4
+ module ActiveRpc
5
+ module ClientConfig
6
+ extend Dry::Configurable
7
+
8
+ setting :configs, default: {}, reader: true
9
+
10
+ def self.load_config
11
+ config_file = File.join(Rails.root, 'config', 'grpc_clients.yml')
12
+
13
+ unless File.exist?(config_file)
14
+ raise "gRPC client config file not found: #{config_file}"
15
+ end
16
+
17
+ configure do |config|
18
+ config.configs = YAML.safe_load(
19
+ File.read(config_file),
20
+ aliases: true,
21
+ permitted_classes: [Symbol]
22
+ )[Rails.env]
23
+ end
24
+ end
25
+
26
+ def self.for(subsystem)
27
+ configs.fetch(subsystem.to_s, {})
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,61 @@
1
+ require 'gruf'
2
+ require 'singleton'
3
+ require 'ostruct'
4
+
5
+ module ActiveRpc
6
+ class DummyClient
7
+ def initialize(subsystem, service_name)
8
+ @subsystem = subsystem
9
+ @service_name = service_name
10
+ end
11
+
12
+ def call(method, params = {})
13
+ if defined?(Rails)
14
+ Rails.logger.warn("[ActiveRpc::DummyClient] Called #{method} on #{@subsystem}/#{@service_name} with params: #{params.inspect}")
15
+ end
16
+ OpenStruct.new(message: nil)
17
+ end
18
+ end
19
+
20
+ class ClientFactory
21
+ include Singleton
22
+
23
+ def initialize
24
+ @clients = {}
25
+ end
26
+
27
+ def client_for(subsystem, service_name)
28
+ key = "#{subsystem}_#{service_name}"
29
+ @clients[key] ||= create_client(subsystem, service_name)
30
+ end
31
+
32
+ private
33
+
34
+ def create_client(subsystem, service_name)
35
+ if ClientConfig.configs.empty? && defined?(Rails)
36
+ Rails.logger.warn('ActiveRpc::ClientConfig not loaded yet, attempting to load it now')
37
+ ClientConfig.load_config rescue nil
38
+ end
39
+
40
+ config = ClientConfig.for(subsystem)
41
+ if config.empty?
42
+ if defined?(Rails) && Rails.env.production?
43
+ Rails.logger.error("No gRPC configuration found for #{subsystem} in production environment")
44
+ return DummyClient.new(subsystem, service_name)
45
+ else
46
+ raise "No gRPC configuration found for #{subsystem}"
47
+ end
48
+ end
49
+
50
+ service_class = ActiveRpc.configuration.service_resolver.call(subsystem, service_name)
51
+
52
+ Gruf::Client.new(
53
+ service: service_class,
54
+ options: {
55
+ hostname: "#{config['grpc_host']}:#{config['grpc_port']}",
56
+ use_ssl: config['use_ssl']
57
+ }
58
+ )
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,50 @@
1
+ module ActiveRpc
2
+ class Configuration
3
+ attr_accessor :local_mode, :local_mode_data, :service_resolver
4
+
5
+ def initialize
6
+ @local_mode = false
7
+ @local_mode_data = nil
8
+ @service_resolver = default_service_resolver
9
+ end
10
+
11
+ private
12
+
13
+ def default_service_resolver
14
+ ->(subsystem, service_name) { "::#{subsystem.to_s.camelize}::#{service_name}".constantize }
15
+ end
16
+ end
17
+
18
+ class << self
19
+ def configuration
20
+ @configuration ||= Configuration.new
21
+ end
22
+
23
+ def configure
24
+ yield(configuration)
25
+ end
26
+
27
+ def enable_local_mode!
28
+ @local_mode_override = true
29
+ end
30
+
31
+ def disable_local_mode!
32
+ @local_mode_override = false
33
+ end
34
+
35
+ def local_mode?
36
+ return @local_mode_override unless @local_mode_override.nil?
37
+ configuration.local_mode || false
38
+ end
39
+
40
+ def with_local_mode
41
+ original = @local_mode_override
42
+ @local_mode_override = true
43
+ begin
44
+ yield
45
+ ensure
46
+ @local_mode_override = original
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveRpc
2
+ module ModelExtensions
3
+ class AttributeDsl
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ def define_rpc_attribute(name, type, options = {})
9
+ @config.attributes << name.to_sym
10
+ @config.attribute_types[name.to_sym] = type
11
+ @config.attribute_defaults[name.to_sym] = options[:default] if options.key?(:default)
12
+ end
13
+
14
+ alias_method :attribute, :define_rpc_attribute
15
+
16
+ def query_config(options = {})
17
+ @config.query_config = options
18
+ end
19
+
20
+ def scope(name, description = nil)
21
+ @config.scopes ||= {}
22
+ @config.scopes[name] = { description: description }
23
+ end
24
+
25
+ def where_rpc_mapping(mappings = {})
26
+ @config.where_rpc_mappings ||= {}
27
+ @config.where_rpc_mappings.merge!(mappings)
28
+ end
29
+ end
30
+ end
31
+ end