activematrix 0.0.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.
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'matrix_sdk/util/tinycache_adapter'
4
+
5
+ module MatrixSdk::Util
6
+ module Tinycache
7
+ CACHE_LEVELS = {
8
+ none: 0,
9
+ some: 1,
10
+ all: 2
11
+ }.freeze
12
+
13
+ def self.adapter
14
+ @adapter ||= TinycacheAdapter
15
+ end
16
+
17
+ def self.adapter=(adapter)
18
+ @adapter = adapter
19
+ end
20
+
21
+ def self.extended(base)
22
+ helper_name = base.send(:cache_helper_module_name)
23
+ base.send :remove_const, helper_name if base.const_defined?(helper_name)
24
+ base.prepend base.const_set(helper_name, Module.new)
25
+
26
+ base.include InstanceMethods
27
+ end
28
+
29
+ def cached(*methods, **opts)
30
+ methods.each { |method| build_cache_methods(method, **opts) }
31
+ end
32
+
33
+ module InstanceMethods
34
+ def tinycache_adapter
35
+ @tinycache_adapter ||= Tinycache.adapter.new.tap do |adapter|
36
+ adapter.config = self.class.tinycache_adapter_config if adapter.respond_to? :config=
37
+ adapter.client = client if respond_to?(:client) && adapter.respond_to?(:client=)
38
+ end
39
+ end
40
+ end
41
+
42
+ def tinycache_adapter_config
43
+ @tinycache_adapter_config ||= {}
44
+ end
45
+
46
+ private
47
+
48
+ def default_cache_key
49
+ proc do |method_name, _method_args|
50
+ method_name.to_sym
51
+ end
52
+ end
53
+
54
+ def cache_helper_module_name
55
+ class_name = name&.gsub(/:/, '') || to_s.gsub(/[^a-zA-Z_0-9]/, '')
56
+ "#{class_name}Tinycache"
57
+ end
58
+
59
+ def build_cache_methods(method_name, cache_key: default_cache_key, cache_level: :none, expires_in: nil, **opts)
60
+ raise ArgumentError, 'Cache key must be a three-arg proc' unless cache_key.is_a? Proc
61
+
62
+ method_names = build_method_names(method_name)
63
+ tinycache_adapter_config[method_name] = {
64
+ level: cache_level,
65
+ expires: expires_in || (1 * 365 * 24 * 60 * 60) # 1 year
66
+ }
67
+
68
+ helper = const_get(cache_helper_module_name)
69
+ return if method_names.any? { |k, _| helper.respond_to? k }
70
+
71
+ helper.class_eval do
72
+ define_method(method_names[:cache_key]) do |*args|
73
+ cache_key.call(method_name, args)
74
+ end
75
+
76
+ define_method(method_names[:with_cache]) do |*args|
77
+ tinycache_adapter.fetch(__send__(method_names[:cache_key], *args), expires_in: expires_in) do
78
+ named = args.delete_at(-1) if args.last.is_a? Hash
79
+ named ||= {}
80
+
81
+ __send__(method_names[:without_cache], *args, **named)
82
+ end
83
+ end
84
+
85
+ define_method(method_names[:without_cache]) do |*args|
86
+ orig = method(method_name).super_method
87
+ named = args.delete_at(-1) if args.last.is_a? Hash
88
+ named ||= {}
89
+
90
+ orig.call(*args, **named)
91
+ end
92
+
93
+ define_method(method_names[:clear_cache]) do |*args|
94
+ tinycache_adapter.delete(__send__(method_names[:cache_key], *args))
95
+ end
96
+
97
+ define_method(method_names[:cached]) do
98
+ true
99
+ end
100
+
101
+ define_method(method_names[:has_value]) do |*args|
102
+ tinycache_adapter.valid?(__send__(method_names[:cache_key], *args))
103
+ end
104
+
105
+ define_method(method_name) do |*args|
106
+ unless_proc = opts[:unless].is_a?(Symbol) ? opts[:unless].to_proc : opts[:unless]
107
+
108
+ raise ArgumentError, 'Invalid proc provided (must have arity between 1..3)' if unless_proc && !(1..3).include?(unless_proc.arity)
109
+
110
+ skip_cache = false
111
+ skip_cache ||= unless_proc.call(self, method_name, args) if unless_proc&.arity == 3
112
+ skip_cache ||= unless_proc.call(method_name, args) if unless_proc&.arity == 2
113
+ skip_cache ||= unless_proc.call(args) if unless_proc&.arity == 1
114
+ skip_cache ||= CACHE_LEVELS[client&.cache || :all] < CACHE_LEVELS[cache_level]
115
+
116
+ if skip_cache
117
+ __send__(method_names[:without_cache], *args)
118
+ else
119
+ __send__(method_names[:with_cache], *args)
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ def build_method_names(method)
126
+ # Clean up method name (split any suffix)
127
+ method_name = method.to_s.sub(/([?!=])$/, '')
128
+ punctuation = Regexp.last_match(-1)
129
+
130
+ {
131
+ cache_key: "#{method_name}_cache_key#{punctuation}",
132
+ with_cache: "#{method_name}_with_cache#{punctuation}",
133
+ without_cache: "#{method_name}_without_cache#{punctuation}",
134
+ clear_cache: "clear_#{method_name}_cache#{punctuation}",
135
+ cached: "#{method}_cached?",
136
+ has_value: "#{method}_has_value?"
137
+ }
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MatrixSdk::Util
4
+ class TinycacheAdapter
5
+ extend MatrixSdk::Extensions
6
+
7
+ attr_accessor :config, :client
8
+
9
+ ignore_inspect :client
10
+
11
+ def initialize
12
+ @config = {}
13
+
14
+ clear
15
+ end
16
+
17
+ def read(key)
18
+ cache[key]&.value
19
+ end
20
+
21
+ def write(key, value, expires_in: nil, cache_level: nil)
22
+ expires_in ||= config.dig(key, :expires)
23
+ expires_in ||= 24 * 60 * 60
24
+ cache_level ||= client&.cache
25
+ cache_level ||= :all
26
+ cache_level = Tinycache::CACHE_LEVELS[cache_level] unless cache_level.is_a? Integer
27
+
28
+ return value if cache_level < Tinycache::CACHE_LEVELS[config.dig(key, :level) || :none]
29
+
30
+ cache[key] = Value.new(value, Time.now, Time.now + expires_in)
31
+ value
32
+ end
33
+
34
+ def exist?(key)
35
+ cache.key?(key)
36
+ end
37
+
38
+ def valid?(key)
39
+ exist?(key) && !cache[key].expired?
40
+ end
41
+
42
+ def fetch(key, expires_in: nil, cache_level: nil, **_opts)
43
+ expires_in ||= config.dig(key, :expires)
44
+ cache_level ||= client&.cache
45
+ cache_level ||= :all
46
+ cache_level = Tinycache::CACHE_LEVELS[cache_level]
47
+
48
+ return read(key) if exist?(key) && !cache[key].expired?
49
+
50
+ value = yield
51
+ write(key, value, expires_in: expires_in, cache_level: cache_level)
52
+ end
53
+
54
+ def delete(key)
55
+ return false unless exist?(key)
56
+
57
+ cache.delete key
58
+ true
59
+ end
60
+
61
+ def expire(key)
62
+ return unless exist? key
63
+
64
+ cache[key].expires_at = Time.at(0)
65
+ end
66
+
67
+ def clear
68
+ @cache = {}
69
+ end
70
+
71
+ def cleanup
72
+ @cache.select { |_, v| v.expired? }.each { |_, v| v.value = nil }
73
+ end
74
+
75
+ private
76
+
77
+ Value = Struct.new(:value, :timestamp, :expires_at) do
78
+ def expired?
79
+ return false if expires_at.nil?
80
+
81
+ Time.now > expires_at
82
+ end
83
+ end
84
+
85
+ attr_reader :cache
86
+ end
87
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ module URI
6
+ # A mxc:// Matrix content URL
7
+ class MXC < Generic
8
+ def full_path
9
+ select(:host, :port, :path, :query, :fragment)
10
+ .compact
11
+ .join
12
+ end
13
+ end
14
+
15
+ # TODO: Use +register_scheme+ on Ruby >=3.1 and fall back to old behavior
16
+ # for older Rubies. May be removed at EOL of Ruby 3.0.
17
+ if respond_to? :register_scheme
18
+ register_scheme 'MXC', MXC
19
+ else
20
+ @@schemes['MXC'] = MXC
21
+ end
22
+
23
+ unless scheme_list.key? 'MATRIX'
24
+ # A matrix: URI according to MSC2312
25
+ class MATRIX < Generic
26
+ attr_reader :authority, :action, :mxid, :mxid2, :via
27
+
28
+ def initialize(*args)
29
+ super(*args)
30
+
31
+ @action = nil
32
+ @authority = nil
33
+ @mxid = nil
34
+ @mxid2 = nil
35
+ @via = nil
36
+
37
+ raise InvalidComponentError, 'missing opaque part for matrix URL' if !@opaque && !@path
38
+
39
+ if @path
40
+ @authority = @host
41
+ @authority += ":#{@port}" if @port
42
+ else
43
+ @path, @query = @opaque.split('?')
44
+ @query, @fragment = @query.split('#') if @query&.include? '#'
45
+ @path, @fragment = @path.split('#') if @path&.include? '#'
46
+ @path = "/#{path}"
47
+ @opaque = nil
48
+ end
49
+
50
+ components = @path.delete_prefix('/').split('/', -1)
51
+ raise InvalidComponentError, 'component count must be 2 or 4' if components.size != 2 && components.size != 4
52
+
53
+ sigil = case components.shift
54
+ when 'u', 'user'
55
+ '@'
56
+ when 'r', 'room'
57
+ '#'
58
+ when 'roomid'
59
+ '!'
60
+ else
61
+ raise InvalidComponentError, 'invalid component in path'
62
+ end
63
+
64
+ component = components.shift
65
+ raise InvalidComponentError, "component can't be empty" if component.nil? || component.empty?
66
+
67
+ @mxid = MatrixSdk::MXID.new("#{sigil}#{component}")
68
+
69
+ if components.size == 2
70
+ sigil2 = case components.shift
71
+ when 'e', 'event'
72
+ '$'
73
+ else
74
+ raise InvalidComponentError, 'invalid component in path'
75
+ end
76
+ component = components.shift
77
+ raise InvalidComponentError, "component can't be empty" if component.nil? || component.empty?
78
+
79
+ @mxid2 = MatrixSdk::MXID.new("#{sigil2}#{component}")
80
+ end
81
+
82
+ return unless @query
83
+
84
+ @action = @query.match(/action=([^&]+)/)&.captures&.first&.to_sym
85
+ @via = @query.scan(/via=([^&]+)/)&.flatten&.compact
86
+ end
87
+
88
+ def mxid2?
89
+ !@mxid2.nil?
90
+ end
91
+ end
92
+
93
+ # TODO: Use +register_scheme+ on Ruby >=3.1 and fall back to old behavior
94
+ # for older Rubies. May be removed at EOL of Ruby 3.0.
95
+ if respond_to? :register_scheme
96
+ register_scheme 'MATRIX', MATRIX
97
+ else
98
+ @@schemes['MATRIX'] = MATRIX
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MatrixSdk
4
+ VERSION = '0.0.0'
5
+ end
data/lib/matrix_sdk.rb ADDED
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'matrix_sdk/util/extensions'
4
+ require 'matrix_sdk/util/uri'
5
+ require 'matrix_sdk/version'
6
+
7
+ require 'json'
8
+
9
+ autoload :Logging, 'logging'
10
+
11
+ module MatrixSdk
12
+ autoload :Api, 'matrix_sdk/api'
13
+ # autoload :ApplicationService, 'matrix_sdk/application_service'
14
+ autoload :Client, 'matrix_sdk/client'
15
+ autoload :MXID, 'matrix_sdk/mxid'
16
+ autoload :Response, 'matrix_sdk/response'
17
+ autoload :Room, 'matrix_sdk/room'
18
+ autoload :User, 'matrix_sdk/user'
19
+
20
+ autoload :MatrixError, 'matrix_sdk/errors'
21
+ autoload :MatrixRequestError, 'matrix_sdk/errors'
22
+ autoload :MatrixNotAuthorizedError, 'matrix_sdk/errors'
23
+ autoload :MatrixForbiddenError, 'matrix_sdk/errors'
24
+ autoload :MatrixNotFoundError, 'matrix_sdk/errors'
25
+ autoload :MatrixConflictError, 'matrix_sdk/errors'
26
+ autoload :MatrixTooManyRequestsError, 'matrix_sdk/errors'
27
+ autoload :MatrixConnectionError, 'matrix_sdk/errors'
28
+ autoload :MatrixTimeoutError, 'matrix_sdk/errors'
29
+ autoload :MatrixUnexpectedResponseError, 'matrix_sdk/errors'
30
+
31
+ module Bot
32
+ autoload :Base, 'matrix_sdk/bot/base'
33
+ end
34
+
35
+ module Rooms
36
+ autoload :Space, 'matrix_sdk/rooms/space'
37
+ end
38
+
39
+ module Util
40
+ autoload :AccountDataCache, 'matrix_sdk/util/account_data_cache'
41
+ autoload :StateEventCache, 'matrix_sdk/util/state_event_cache'
42
+ autoload :Tinycache, 'matrix_sdk/util/tinycache'
43
+ autoload :TinycacheAdapter, 'matrix_sdk/util/tinycache_adapter'
44
+ end
45
+
46
+ module Protocols
47
+ autoload :AS, 'matrix_sdk/protocols/as'
48
+ autoload :CS, 'matrix_sdk/protocols/cs'
49
+ autoload :IS, 'matrix_sdk/protocols/is'
50
+ autoload :SS, 'matrix_sdk/protocols/ss'
51
+
52
+ # Non-final protocol extensions
53
+ autoload :MSC, 'matrix_sdk/protocols/msc'
54
+ end
55
+
56
+ def self.debug!
57
+ logger.level = :debug
58
+ end
59
+
60
+ def self.logger
61
+ @logger ||= ::Logging.logger[self].tap do |logger|
62
+ logger.add_appenders ::Logging.appenders.stdout
63
+ logger.level = :info
64
+ end
65
+ end
66
+
67
+ def self.logger=(global_logger)
68
+ @logger = global_logger
69
+ @global_logger = !global_logger.nil?
70
+ end
71
+
72
+ def self.global_logger?
73
+ @global_logger ||= false
74
+ end
75
+ end
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activematrix
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Abdelkader Boudih
8
+ - Alexander Olofsson
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mocha
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: simplecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activerecord
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '8.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '8.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: railties
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '8.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '8.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: logging
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2'
111
+ description: A Ruby on Rails gem that provides seamless integration with the Matrix
112
+ protocol, enabling Rails applications to connect and communicate with Matrix servers.
113
+ email:
114
+ - terminale@gmail.com
115
+ - ace@haxalot.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files:
119
+ - CHANGELOG.md
120
+ - LICENSE.txt
121
+ - README.md
122
+ files:
123
+ - CHANGELOG.md
124
+ - LICENSE.txt
125
+ - README.md
126
+ - lib/matrix_sdk.rb
127
+ - lib/matrix_sdk/api.rb
128
+ - lib/matrix_sdk/bot.rb
129
+ - lib/matrix_sdk/bot/base.rb
130
+ - lib/matrix_sdk/bot/main.rb
131
+ - lib/matrix_sdk/client.rb
132
+ - lib/matrix_sdk/errors.rb
133
+ - lib/matrix_sdk/mxid.rb
134
+ - lib/matrix_sdk/protocols/as.rb
135
+ - lib/matrix_sdk/protocols/cs.rb
136
+ - lib/matrix_sdk/protocols/is.rb
137
+ - lib/matrix_sdk/protocols/msc.rb
138
+ - lib/matrix_sdk/protocols/ss.rb
139
+ - lib/matrix_sdk/response.rb
140
+ - lib/matrix_sdk/room.rb
141
+ - lib/matrix_sdk/rooms/space.rb
142
+ - lib/matrix_sdk/user.rb
143
+ - lib/matrix_sdk/util/account_data_cache.rb
144
+ - lib/matrix_sdk/util/events.rb
145
+ - lib/matrix_sdk/util/extensions.rb
146
+ - lib/matrix_sdk/util/state_event_cache.rb
147
+ - lib/matrix_sdk/util/tinycache.rb
148
+ - lib/matrix_sdk/util/tinycache_adapter.rb
149
+ - lib/matrix_sdk/util/uri.rb
150
+ - lib/matrix_sdk/version.rb
151
+ homepage: https://github.com/seuros/activematrix
152
+ licenses:
153
+ - MIT
154
+ metadata: {}
155
+ rdoc_options: []
156
+ require_paths:
157
+ - lib
158
+ required_ruby_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '3.4'
163
+ required_rubygems_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ requirements: []
169
+ rubygems_version: 3.6.7
170
+ specification_version: 4
171
+ summary: Rails gem for connecting to Matrix protocol
172
+ test_files: []