relevium 0.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: 44b99ef9b55ed25a5a759e50028e9a8277d07885c3a6326d44c07c39a93cadd3
4
+ data.tar.gz: 9890b23b1a7eb169031fa80c9dae4263a77caa84d3f26d95353e0bfedebf9c0f
5
+ SHA512:
6
+ metadata.gz: ec1b431da475adea43c8ead05e35dda1134803666480b75ffcdc9744d4be9ef75418392550353074995f34eb51e9578bfa8f47d74184a0904f98c24598bb769a
7
+ data.tar.gz: 7c2f1f38cdea797feb9e9ea122dd955ff52986a96d0a1ffd891239806353a0355714afc0161cfc7aa17450362a959d3bffe88f7d5db47cfe922ece6db653189c
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore debugger history
11
+ .byebug_history
12
+
13
+ # Ignore IDE files
14
+ .idea
15
+
16
+ # Ignore local gems
17
+ *.gem
18
+
19
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ gemspec
6
+
7
+ gem 'activemodel', '>= 4.2.6'
8
+
9
+ group :development do
10
+ gem 'rspec', '>= 3.5.0'
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ relevium (0.0.1)
5
+ activemodel (>= 4.2.6)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (4.2.11.3)
11
+ activesupport (= 4.2.11.3)
12
+ builder (~> 3.1)
13
+ activesupport (4.2.11.3)
14
+ i18n (~> 0.7)
15
+ minitest (~> 5.1)
16
+ thread_safe (~> 0.3, >= 0.3.4)
17
+ tzinfo (~> 1.1)
18
+ builder (3.2.4)
19
+ byebug (11.0.1)
20
+ concurrent-ruby (1.1.7)
21
+ diff-lcs (1.4.4)
22
+ i18n (0.9.5)
23
+ concurrent-ruby (~> 1.0)
24
+ minitest (5.14.1)
25
+ rake (10.5.0)
26
+ rspec (3.5.0)
27
+ rspec-core (~> 3.5.0)
28
+ rspec-expectations (~> 3.5.0)
29
+ rspec-mocks (~> 3.5.0)
30
+ rspec-core (3.5.4)
31
+ rspec-support (~> 3.5.0)
32
+ rspec-expectations (3.5.0)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.5.0)
35
+ rspec-mocks (3.5.0)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.5.0)
38
+ rspec-support (3.5.0)
39
+ thread_safe (0.3.6)
40
+ tzinfo (1.2.7)
41
+ thread_safe (~> 0.1)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ activemodel (>= 4.2.6)
48
+ bundler (>= 1.15)
49
+ byebug (>= 11.0.1)
50
+ rake (>= 10.0)
51
+ relevium!
52
+ rspec (>= 3.5.0)
53
+
54
+ BUNDLED WITH
55
+ 1.17.3
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Kirill Bazeltsev
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,356 @@
1
+ # Relevium
2
+ ![Gem](https://img.shields.io/gem/v/gem?style=flat-square)
3
+
4
+ #### Simple ruby gem for rails projects with some useful patterns that would help you build more scalable apps
5
+
6
+ ## Table of contents <a name='table-of-contents'></a>
7
+ 1. [Service object](#service-object)
8
+ 1. [Listeners](#listeners)
9
+ 2. [Background service](#background-service)
10
+ 3. [Form object](#form)
11
+ 1. [Serialization with forms](#form)
12
+
13
+
14
+ ## Service object <a name='service-object'></a>
15
+ [To the start](#table-of-contents)
16
+
17
+ Interface for running services - a place to move your business logic.
18
+
19
+ Example:
20
+ ```ruby
21
+ class TestService < Relevium::Service
22
+ attr_reader :user_id
23
+
24
+ def initialize(user_id)
25
+ @user_id = user_id
26
+ end
27
+
28
+ def call
29
+ return broadcast(:fail) unless user_valid?
30
+
31
+ do_some_stuff
32
+ book = user.books.last
33
+ user.delete
34
+ broadcast(:ok, book)
35
+ end
36
+
37
+ private
38
+
39
+ def user_valid?
40
+ user.name == 'Kyle'
41
+ end
42
+
43
+ def do_some_stuff
44
+ #..more_code_here..
45
+ end
46
+
47
+ def user
48
+ @user ||= User.find_by(id: user_id)
49
+ end
50
+ end
51
+ ```
52
+
53
+ Results:
54
+
55
+ ```ruby
56
+ @user = User.last # Name is 'Kyle'
57
+
58
+ TestService.call(@user.id)
59
+ # Will delete user and broadcast method won't take affect
60
+
61
+ TestService.call(@user.id) do |obj|
62
+ obj.on(:fail) { raise 'Some error' } # Listener for fail broadcast
63
+ obj.on(:ok) { |book| @book = book } # Listener for ok broadcast
64
+ end
65
+ # User will be deleted and @book variable will be assigned
66
+
67
+ @user.update(name: 'Stan')
68
+ TestService.call(@user.id) do |obj|
69
+ obj.on(:fail) { raise 'Some error' }
70
+ obj.on(:ok) { |book| @book = book }
71
+ end
72
+ # User won't be deleted and Some error' will be raised
73
+ ```
74
+
75
+ ### Adding listeners <a name='listeners'></a>
76
+ [To the start](#table-of-contents)
77
+
78
+ Example:
79
+ ```ruby
80
+ class TestListener < Relevium::Service
81
+ attr_reader :email
82
+
83
+ def initialize(email)
84
+ @email = email
85
+ end
86
+
87
+ def call
88
+ unsubscribe_from_sendgrid
89
+ end
90
+
91
+ private
92
+
93
+ def unsubscribe_from_sendgrid
94
+ # your code
95
+ end
96
+ end
97
+ ```
98
+
99
+ ```ruby
100
+ class TestService < Relevium::Service
101
+ set_listener TestListener, :ok
102
+ attr_reader :user_id
103
+
104
+ def initialize(user_id)
105
+ @user_id = user_id
106
+ end
107
+
108
+ def call
109
+ do_some_stuff
110
+ user.delete
111
+ user.persisted? ? broadcast(:ok, user.email) : broadcast(:fail)
112
+ end
113
+
114
+ private
115
+
116
+ def do_some_stuff
117
+ #..more_code_here..
118
+ end
119
+
120
+ def user
121
+ @user ||= User.find_by(id: user_id)
122
+ end
123
+ end
124
+ ```
125
+
126
+ Results:
127
+
128
+ ```ruby
129
+ @user = User.last
130
+
131
+ TestService.call(@user.id)
132
+ # Will delete user and broadcast method won't take affect.
133
+ # However listener's `call` function will be called
134
+ # with `email` passed as the argument to initialize function.
135
+ ```
136
+ ---
137
+ Specify which function from listener to call:
138
+
139
+ ```ruby
140
+ class TestListener < Relevium::Service
141
+ def initialize(arg)
142
+ @arg = arg
143
+ end
144
+
145
+ def on_ok
146
+ # ok code
147
+ end
148
+
149
+ def on_fail
150
+ # fail code
151
+ end
152
+ end
153
+
154
+ set_listener TestListener, :ok, function: :on_ok
155
+ set_listener TestListener, :fail, function: :on_fail
156
+ ```
157
+ ---
158
+ Specify arguments that should be passed to the listener:
159
+ ```ruby
160
+ class TestListener < Relevium::Service
161
+ attr_reader :arg
162
+
163
+ def initialize(arg)
164
+ @arg = arg
165
+ end
166
+
167
+ def call
168
+ puts arg
169
+ end
170
+ end
171
+
172
+ class TestService < Relevium::Service
173
+ set_listener TestListener, :ok, args: :user_id
174
+
175
+ def initialize(user_id)
176
+ @user_id = user_id
177
+ end
178
+
179
+ def call
180
+ # some code
181
+ broadcast(:ok, 'test')
182
+ end
183
+ end
184
+ ```
185
+
186
+ Result:
187
+ ```ruby
188
+ TestService.call(1) do |service|
189
+ service.on(:ok) { |argument| puts argument }
190
+ end
191
+
192
+ # Output:
193
+ # test
194
+ # 1
195
+ ```
196
+ ---
197
+ Set up condition to call listener:
198
+ ```ruby
199
+ class TestService < Relevium::Service
200
+ set_listener TestListener, :ok, if: Proc.new { |service| !service.user.persisted? }
201
+
202
+ def initialize(user_id)
203
+ @user = User.find(user_id)
204
+ end
205
+
206
+ def call
207
+ @user.delete
208
+ broadcast(:ok)
209
+ end
210
+ end
211
+ ```
212
+
213
+ Results:
214
+ ```ruby
215
+ TestService.call(User.last.id)
216
+ # Listener would be trigger only if user was deleted.
217
+ ```
218
+
219
+ ## Background service <a name='background-service'></a>
220
+ [To the start](#table-of-contents)
221
+
222
+ Simple interface for running background jobs as services.
223
+ Meant to be inherited like this:
224
+ ```ruby
225
+ class SidekiqService < Relevium::BackgroundService
226
+ def initialize(options)
227
+ super(options, ServiceObjectWorker)
228
+ end
229
+ end
230
+ ```
231
+ Needs a sidekiq worker in order to work, that should look like this:
232
+ ```ruby
233
+ class ServiceObjectWorker
234
+ include Sidekiq::Worker
235
+ sidekiq_options queue: :default, retry: true
236
+
237
+ def perform(klass, *args)
238
+ klass.constantize.call(args)
239
+ end
240
+ end
241
+
242
+ ```
243
+
244
+ Now you can create a service like this:
245
+ ```ruby
246
+ class SimpleService < SidekiqService
247
+ attr_reader :foo
248
+
249
+ def initialize(foo, options = {})
250
+ super(options)
251
+ @foo = foo
252
+ end
253
+
254
+ private
255
+
256
+ def perform
257
+ puts foo
258
+ end
259
+ end
260
+
261
+ ```
262
+
263
+ And invoke it like this:
264
+ ```ruby
265
+ SimpleService.call('foo', background: true, perform_in: 15.minutes)
266
+ # Will set sidekiq worker to be performd in 15 minutes,
267
+ # that will print 'foo' in sidekiq console
268
+ ```
269
+ Note that listeners won't work when service is being called as background job.
270
+
271
+ ## Form object <a name='form'></a>
272
+ [To the start](#table-of-contents)
273
+
274
+ Form object is used to move validations from models. Usually used when similar model needs different validations on
275
+ different forms. Can be used to build attributes for model to save.
276
+
277
+ Example:
278
+ ```ruby
279
+ class UserForm < Relevium::Form
280
+
281
+ attribute :user_id, Integer
282
+ attribute :user_name, String
283
+ attribute :sibling_name, String, remove_from_hash: true
284
+
285
+ include_in_hash :sibling_id
286
+
287
+ validates :user_id, :user_name, presence: true
288
+ validate :is_kyle
289
+
290
+ private
291
+
292
+ def is_kyle
293
+ return true if user_name == 'Kyle'
294
+
295
+ errors.add(:user_name, 'should be Kyle')
296
+ end
297
+
298
+ def sibling_id
299
+ @sibling_id ||= User.find_by(name: sibling_name)
300
+ end
301
+ end
302
+ ```
303
+ Results:
304
+ ```ruby
305
+ user_params = {user_id: 1, user_name: 'Kyle', sibling_name: 'John'}
306
+ form = UserForm.new(user_params)
307
+ form.valid? # true
308
+ form = UserForm.new(user_params.merge(user_name: 'Steve'))
309
+ form.valid? # false
310
+ form.errors.full_messages # ["User name should be Kyle"]
311
+ form = UserForm.new(user_params.merge(user_id: 'test'))
312
+ form.valid? # false
313
+ form.errors.full_messages # ["User can't be blank"]
314
+ form = UserForm.new(user_params.merge(user_id: '1')) # Will convert user_id into Integer
315
+ form.to_h # { user_id: 1, user_name: 'Kyle', sibling_id: 12 }
316
+
317
+ form.set(user_name, 'Stan')
318
+ form.set_attributes(user_id: 2, sibling_name: 'Ken')
319
+ form.to_h # { user_id: 2, user_name: 'Stan', sibling_name: 'Ken' }
320
+ ```
321
+ ---
322
+ ### Serialization with forms <a name='serialization'></a>
323
+ [To the start](#table-of-contents)
324
+
325
+ ```ruby
326
+ class UserForm < Relevium::Form
327
+ attribute :available_cash, Float
328
+
329
+ serialize_attributes :user_id, :sibling_id, :available_cash, :user_full_name
330
+
331
+ def user_full_name
332
+ first_name + ' ' + last_name
333
+ end
334
+ end
335
+ ```
336
+
337
+ Now you can use this form to serialize active records to hash:
338
+ ```ruby
339
+ ap User
340
+ # User < ActiveRecord::Base {
341
+ # :user_id => :integer,
342
+ # :available_cash => :string,
343
+ # :sibling_id => :integer,
344
+ # :first_name => :string,
345
+ # :last_name => :string
346
+ # }
347
+ UserForm.from_model(User.last).serialize
348
+ # Output:
349
+ # { user_id: 1, sibling_id: 2, available_cash: 123.45, user_full_name: 'Ken Stevenson' }
350
+ ```
351
+ Also you can serialize active record collection or array of active records:
352
+ ```ruby
353
+ UserForm.serialize_relation(User.where(id: (1..15)))
354
+ UserForm.serialize_relation(User.last(3).to_a)
355
+ ```
356
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "spec"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["rspec spec/*/"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,16 @@
1
+ # Template Ruby build
2
+
3
+ # This template allows you to validate your Ruby code.
4
+ # The workflow allows running tests and code linting on the default branch.
5
+
6
+ image: ruby:2.7
7
+
8
+ pipelines:
9
+ default:
10
+ - parallel:
11
+ - step:
12
+ name: Run Rspec
13
+ script:
14
+ - gem install bundler -v 1.17.2
15
+ - bundle install
16
+ - bundle exec rspec
data/lib/relevium.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'relevium/version'
2
+ require 'relevium/service'
3
+ require 'relevium/form'
4
+ require 'relevium/background_service'
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'relevium/service'
4
+
5
+ module Relevium
6
+ class BackgroundService < Service
7
+ attr_reader :options, :worker_class
8
+
9
+ def initialize(options, worker_class)
10
+ @options = options
11
+ @worker_class = worker_class
12
+ end
13
+
14
+ def call
15
+ return broadcast(:fail) unless valid?
16
+
17
+ background? ? setup_worker : perform
18
+ end
19
+
20
+ private
21
+
22
+ def perform
23
+ NoMethodError
24
+ end
25
+
26
+ def self.worker_attributes(*attributes)
27
+ @worker_attributes_array = attributes
28
+ end
29
+
30
+ def self.worker_attributes_array
31
+ @worker_attributes_array ||= []
32
+ end
33
+
34
+ def fetch_worker_attributes
35
+ self.class.worker_attributes_array.map do |worker_attribute|
36
+ instance_variable_get("@#{worker_attribute}")
37
+ end
38
+ end
39
+
40
+ def setup_worker
41
+ worker_class.perform_in(delay, self.class, *fetch_worker_attributes)
42
+ end
43
+
44
+ def delay
45
+ options[:perform_in] || 5.minutes
46
+ end
47
+
48
+ def background?
49
+ !options[:background].nil? && options[:background]
50
+ end
51
+
52
+ def valid?
53
+ true
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'active_model'
5
+
6
+ module Relevium
7
+ class Form
8
+ include ActiveModel::Model
9
+
10
+ def initialize(hash)
11
+ set_attributes(hash)
12
+ self
13
+ end
14
+
15
+ def self.from_model(model)
16
+ new(model.attributes)
17
+ rescue StandardError => _e
18
+ nil
19
+ end
20
+
21
+ def to_h
22
+ hash = {}
23
+ self.instance_variables.each do |var|
24
+ var_name = var.to_s.delete('@')
25
+ attribute = self.class.attributes.find { |attr| attr.attribute_name == var_name.to_sym }
26
+ hash[var_name] = instance_variable_get(var) if attribute&.remove_from_hash == false
27
+ end
28
+ self.class.methods_for_hash.each do |method|
29
+ hash[method] = self.send(method)
30
+ end
31
+ hash.default_proc = proc { |h, k| h.key?(k.to_s) ? h[k.to_s] : nil }
32
+ hash
33
+ end
34
+
35
+ def serialize
36
+ hash = {}
37
+ self.class.attributes_to_serialize.each do |attribute|
38
+ hash[attribute] = self.send(attribute) rescue instance_variable_get("@#{attribute}")
39
+ end
40
+ hash.default_proc = proc { |h, k| h.key?(k.to_s) ? h[k.to_s] : nil }
41
+ hash
42
+ end
43
+
44
+ def set(name, value)
45
+ attribute = self.class.attributes.find { |attr| attr.attribute_name.to_s == name.to_s }
46
+ if attribute.present?
47
+ set_attribute(attribute, value)
48
+ else
49
+ instance_variable_set("@#{name}", value)
50
+ end
51
+ self
52
+ end
53
+
54
+ def set_attributes(hash)
55
+ hash.to_h.to_a.each do |attr_array|
56
+ set(attr_array[0], attr_array[1])
57
+ end
58
+ self
59
+ end
60
+
61
+ def errors_to_string
62
+ errors.full_messages.to_sentence
63
+ end
64
+
65
+ def self.i18n_scope
66
+ :activerecord
67
+ end
68
+
69
+ private
70
+
71
+ def set_attribute(attribute, value)
72
+ instance_variable_set(attribute.name_to_instance_variable, attribute.normalized_value(value))
73
+ end
74
+
75
+ def self.attribute(attribute, type = nil, remove_from_hash: false)
76
+ attributes << Attribute.new(attribute, type, remove_from_hash)
77
+ self.class_eval { attr_reader attribute }
78
+ end
79
+
80
+ def self.attributes
81
+ @attributes ||= Set.new
82
+ end
83
+
84
+ def self.include_in_hash(*methods)
85
+ @methods_for_hash = methods
86
+ end
87
+
88
+ def self.methods_for_hash
89
+ @methods_for_hash ||= []
90
+ end
91
+
92
+ def self.serialize_attributes(*attributes)
93
+ @attributes_to_serialize = attributes
94
+ end
95
+
96
+ def self.attributes_to_serialize
97
+ @attributes_to_serialize ||= []
98
+ end
99
+
100
+ def self.serialize_relation(relation)
101
+ relation.map { |model| from_model(model).serialize }
102
+ end
103
+
104
+ class Boolean; end
105
+ end
106
+
107
+ class Attribute
108
+ DATE_TYPES = [Date, Time, DateTime].freeze
109
+ TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].freeze
110
+
111
+ attr_reader :attribute_name, :type, :remove_from_hash
112
+
113
+ def initialize(attribute_name, type = nil, remove_from_hash = false)
114
+ @attribute_name = attribute_name
115
+ @type = type
116
+ @remove_from_hash = remove_from_hash
117
+ end
118
+
119
+ def normalized_value(value)
120
+ return nil if value.nil?
121
+ return value unless type
122
+ return value if value.is_a?(type)
123
+
124
+ cast_value_to_type(value)
125
+ rescue ArgumentError => _e
126
+ nil
127
+ end
128
+
129
+ def cast_value_to_type(value)
130
+ return TRUE_VALUES.include?(value) if type == ::Relevium::Form::Boolean
131
+ return method(type.to_s).call(value) unless DATE_TYPES.include?(type)
132
+
133
+ value.send("to_#{type.to_s.underscore}")
134
+ end
135
+
136
+ def name_to_instance_variable
137
+ "@#{attribute_name}"
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'byebug'
4
+
5
+ module Relevium
6
+ class Service
7
+ def self.call(*args)
8
+ obj = new(*args)
9
+ yield obj if block_given?
10
+ obj.call
11
+ end
12
+
13
+ def initialize(*args); end
14
+
15
+ def on(name, &block)
16
+ local_registrations << BlockRegistration.new(name, block)
17
+ self
18
+ end
19
+
20
+ private
21
+
22
+ def broadcast(name, *args)
23
+ set_off_local_listeners(name, *args)
24
+ set_off_global_listeners(name, *args)
25
+ self
26
+ end
27
+
28
+ def set_off_local_listeners(name, *args)
29
+ local_registrations.select { |registration| registration.listener == name }.first&.broadcast(*args)
30
+ end
31
+
32
+ def set_off_global_listeners(name, *args)
33
+ select_proc = Proc.new { |registration| registration.message == name }
34
+ registrations = self.class.global_registrations.select(&select_proc)
35
+ registrations += self.class.superclass&.global_registrations&.select(&select_proc)
36
+ registrations.each do |registration|
37
+ next unless registration.condition.nil? || registration.condition.call(self)
38
+
39
+ registration.args.nil? ? registration.broadcast(*args) : registration.broadcast(*get_args(*registration.args))
40
+ end
41
+ end
42
+
43
+ def get_args(*args)
44
+ args.map { |arg| instance_variable_get("@#{arg}") }
45
+ end
46
+
47
+ def transaction(&block)
48
+ ActiveRecord::Base.transaction(&block) if block_given?
49
+ end
50
+
51
+ def local_registrations
52
+ @local_registrations ||= Set.new
53
+ end
54
+
55
+ def self.set_listener(klass, message, options = {})
56
+ global_registrations << GlobalRegistration.new(klass, message, options, self)
57
+ end
58
+
59
+ def self.global_registrations
60
+ @global_registrations ||= Set.new
61
+ end
62
+
63
+ def call
64
+ raise NoMethodError
65
+ end
66
+ end
67
+
68
+ class BlockRegistration
69
+ attr_reader :listener, :message
70
+
71
+ def initialize(listener, message)
72
+ @listener = listener
73
+ @message = message
74
+ end
75
+
76
+ def broadcast(*args)
77
+ message.call(*args)
78
+ end
79
+ end
80
+
81
+ class GlobalRegistration < BlockRegistration
82
+ attr_reader :function, :condition, :args, :whisperer
83
+
84
+ def initialize(listener, message, options = {}, whisperer = nil)
85
+ super(listener, message)
86
+ @function = options[:function] || 'call'
87
+ @condition = options[:if]
88
+ @args = options[:args]
89
+ @whisperer = whisperer
90
+ end
91
+
92
+ def broadcast(*args)
93
+ listener_obj = listener.new(*args)
94
+ listener_obj.instance_variable_set('@whisperer', whisperer)
95
+ listener_obj.send(function)
96
+ end
97
+
98
+ def validate!
99
+ raise 'Invalid function name for listener' unless [String, Symbol].include?(function.class)
100
+ raise 'Invalid condition for listener' unless condition.is_a?(Proc)
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Relevium
4
+ VERSION = '0.0.1'
5
+ end
data/relevium.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "relevium/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "relevium"
8
+ spec.version = Relevium::VERSION
9
+ spec.authors = ['Bazeltsev Kirill']
10
+ spec.email = ['kirill.bazeltsev@flender.ie']
11
+
12
+ spec.summary = 'A Ruby gem, that helps keep models and controllers thin'
13
+ spec.description = ''
14
+ spec.homepage = ''
15
+ spec.license = 'MIT'
16
+
17
+ spec.add_dependency 'activemodel', '>= 4.2.6'
18
+
19
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
20
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
21
+
22
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
23
+ f.match(%r{^(test|spec|features)/})
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_development_dependency "bundler", ">= 1.15"
30
+ spec.add_development_dependency "rake", ">= 10.0"
31
+ spec.add_development_dependency "rspec", ">= 3.5.0"
32
+ spec.add_development_dependency "byebug", '>= 11.0.1'
33
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: relevium
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Bazeltsev Kirill
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-02-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.6
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.6
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.15'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.15'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 3.5.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 3.5.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 11.0.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 11.0.1
83
+ description: ''
84
+ email:
85
+ - kirill.bazeltsev@flender.ie
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - LICENSE
94
+ - README.md
95
+ - Rakefile
96
+ - bitbucket-pipelines.yml
97
+ - lib/relevium.rb
98
+ - lib/relevium/background_service.rb
99
+ - lib/relevium/form.rb
100
+ - lib/relevium/service.rb
101
+ - lib/relevium/version.rb
102
+ - relevium.gemspec
103
+ homepage: ''
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubygems_version: 3.0.8
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: A Ruby gem, that helps keep models and controllers thin
126
+ test_files: []