relevium 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []