methodist 0.1.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,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 229590fd037e63ac5773c8a8e11c9ef451d3d01a5cc417c6e97c6c62fe5f30dc
4
+ data.tar.gz: acaaf7d57b19ab0b440fb132a43d85bbed2fd838027d2befef5e9d77329dfca2
5
+ SHA512:
6
+ metadata.gz: 71f861ed48e4a2a390c78c570207ea9ef7fdc00989c383a7dcaec373c951a8a8cef9e165ae8d37bc6b78af50d7bee1edd88e47d9b0f865d39e32b2019d3a9f73
7
+ data.tar.gz: ed50190b816143494934cb50018f04e3f88a2e5e8f147bc34956bc7c1c3211a6643dab95f4d85b3d93d0f14a5030d93a7013deb05d9a18d54e6423960e5c1688
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Sergey Nesterov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,56 @@
1
+ # Methodist
2
+ [![Build Status](https://travis-ci.org/QNester/methodist.svg?branch=master)](https://travis-ci.org/QNester/methodist)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/eebe37d3169041579416/maintainability)](https://codeclimate.com/github/QNester/methodist/maintainability)
4
+ [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gem-methodist/methodist?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5
+
6
+ Methodist - a gem for Ruby on Rails created to stop chaos in your buisness logic.
7
+ This gem adds generators to your rails application using some patterns:
8
+
9
+ - __Interactor__: a class for doing some complex job.
10
+ - __Observer__: notifies one part of an application about changes in another part of an application.
11
+ - __Builder__: is used to create an object with complex configuration (including your business logic, validation etc.)
12
+ - __Service__: a class with collection of methods. Useful when using internal services.
13
+
14
+
15
+ ## Installation
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'methodist'
20
+ ```
21
+
22
+ And then execute:
23
+ ```bash
24
+ $ bundle
25
+ ```
26
+
27
+ Or install it yourself:
28
+ ```bash
29
+ $ gem install methodist
30
+ ```
31
+
32
+ ## Usage
33
+ Just execute in a terminal
34
+ ```
35
+ rails g <pattern_name> <generated_class>
36
+ ```
37
+ where _<pattern_name>_ is one of the patterns (observer, interactor, etc.)
38
+
39
+ About every Methodist pattern you can read [here](https://github.com/QNester/methodist/wiki)
40
+
41
+ ## Contributing
42
+ To contribute just:
43
+ 1) Create issue. Issue should contain information about goals of your
44
+ future changes.
45
+ 2) Fork project.
46
+ 3) Create branch. The name of the branch should begin with the ID of your issue.
47
+ Examle: `ISSUE-205-create-new-pattern-generator`.
48
+ 4) Make changes.
49
+ 5) *Write tests*.
50
+ 6) Make a commit. The name of the commit should begin with the ID of your issue.
51
+ Examle: `[ISSUE-205] Create new pattern generator`.
52
+ 7) Push.
53
+ 8) Create a pull request to the `develop` branch.
54
+
55
+ ## License
56
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,23 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Methodist'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ require 'bundler/gem_tasks'
18
+
19
+ require "rspec/core/rake_task"
20
+
21
+ RSpec::Core::RakeTask.new(:spec)
22
+
23
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Generator for creating a Builder class based on Methodist::Builder
3
+
4
+ Example:
5
+ rails generate builder Thing
6
+
7
+ This will create:
8
+ app/builders/thing.rb
9
+ spec/builders/thing_spec.rb
@@ -0,0 +1,27 @@
1
+ require_relative '../methodist_generator'
2
+
3
+ class BuilderGenerator < MethodistGenerator
4
+ desc 'Create a builder'
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ PATTERN_FOLDER = 'builders'.freeze
8
+ TEMPLATE_FILE = 'builder.erb'.freeze
9
+ TEMPLATE_SPEC_FILE = 'builder_spec.erb'.freeze
10
+
11
+ class_option 'path', type: :string, desc: "Parent module for a new builder", default: PATTERN_FOLDER
12
+
13
+ def generate
14
+ template(
15
+ TEMPLATE_FILE,
16
+ "#{filename_with_path}_builder.rb"
17
+ )
18
+ end
19
+
20
+ def generate_spec
21
+ template(
22
+ TEMPLATE_SPEC_FILE,
23
+ "#{filename_with_path(prefix: 'spec')}_builder_spec.rb"
24
+ )
25
+ end
26
+ end
27
+
@@ -0,0 +1,10 @@
1
+ <%- unless options['clean'] -%>
2
+ # See docs to understand how to use a builder:
3
+ # https://github.com/QNester/methodist/tree/master/docs/builder.md
4
+ <% end -%>
5
+ class <%= name.camelcase %>Builder < Methodist::Builder
6
+ <%- unless options['clean'] -%>
7
+ # Use `field` method to define a builder attribute.
8
+ # Example: field :title, ->(val) { val.is_a?(String) }
9
+ <%- end -%>
10
+ end
@@ -0,0 +1,5 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe <%= name.camelcase %>Builder do
4
+ it 'test your builder'
5
+ end
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Generator for creating a Interactor class based on Methodist::Interactor
3
+
4
+ Example:
5
+ rails generate interactor Thing
6
+
7
+ This will create:
8
+ app/interactors/thing.rb
9
+ spec/interactors/thing_spec.rb
@@ -0,0 +1,28 @@
1
+ require_relative '../methodist_generator'
2
+
3
+ class InteractorGenerator < MethodistGenerator
4
+ desc 'Create an interactor'
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ PATTERN_FOLDER = 'interactors'.freeze
8
+ TEMPLATE_FILE = 'interactor.erb'.freeze
9
+ TEMPLATE_SPEC_FILE = 'interactor_spec.erb'.freeze
10
+
11
+ class_option 'skip-validations', type: :boolean, desc: "Skip validations in generated files", default: false
12
+ class_option 'path', type: :string, desc: "Parent module for a new interactor", default: PATTERN_FOLDER
13
+
14
+ def generate
15
+ template(
16
+ TEMPLATE_FILE,
17
+ "#{filename_with_path}.rb"
18
+ )
19
+ end
20
+
21
+ def generate_spec
22
+ template(
23
+ TEMPLATE_SPEC_FILE,
24
+ "#{filename_with_path(prefix: 'spec')}_spec.rb"
25
+ )
26
+ end
27
+ end
28
+
@@ -0,0 +1,22 @@
1
+ <% unless options['clean'] -%>
2
+ # Methodist interactors use dry-transaction.
3
+ # Check docs here: https://github.com/dry-rb/dry-transaction
4
+ <% end -%>
5
+ class <%= name.camelcase %> < Methodist::Interactor
6
+ <% unless options['skip-validations'] %>
7
+ # See here the syntax for validation http://dry-rb.org/gems/dry-validation/
8
+ schema do
9
+ # required(:name).value(:str?)
10
+ end
11
+ <% end %>
12
+ <% unless options['clean'] -%>
13
+ # Use your step, tee, try or map
14
+ # More info: http://dry-rb.org/gems/dry-transaction/step-adapters/
15
+ <% end -%>
16
+ <%- unless options['skip-validations'] %>
17
+ step :validate
18
+ <%- end -%>
19
+ <%- unless options['clean'] -%>
20
+ # step :do_something
21
+ <%- end %>
22
+ end
@@ -0,0 +1,31 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe <%= name.camelcase %> do
4
+ subject { described_class.new.call(input) }
5
+
6
+ <%- unless options['skip-validations'] %>
7
+ context 'valid input' do
8
+ let!(:input) do
9
+ <%- unless options['clean'] %>
10
+ # valid input for your interactor
11
+ <%- end %>
12
+ end
13
+
14
+ it 'returns success result' do
15
+ expect(subject).to be_success
16
+ end
17
+ end
18
+
19
+ context 'invalid input' do
20
+ let!(:input) do
21
+ <%- unless options['clean'] %>
22
+ # failure input for yout interactor
23
+ <%- end %>
24
+ end
25
+
26
+ it 'returns success result' do
27
+ expect(subject).to be_failure
28
+ end
29
+ end
30
+ <%- end -%>
31
+ end
@@ -0,0 +1,18 @@
1
+ class MethodistGenerator < Rails::Generators::NamedBase
2
+ DEFAULT_PREFIX = 'app'.freeze
3
+
4
+ class_option 'clean', type: :boolean, desc: "Skip comments and annotations in generated files", default: false
5
+ class_option 'in-lib', type: :boolean, desc: "Create files in lib path, not in app", default: false
6
+
7
+ private
8
+
9
+ def filename_with_path(prefix: DEFAULT_PREFIX)
10
+ prefix = 'lib' if options['in-lib'] && prefix != 'spec'
11
+ prefix = 'spec/lib' if options['in-lib'] && prefix == 'spec'
12
+
13
+ name_as_arr = name.split('/')
14
+ path = name_as_arr.first(name_as_arr.size - 1).join('/')
15
+ name = name_as_arr.last
16
+ "#{prefix}/#{options['path']}/#{path}/#{name}"
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Generate a new observer
3
+
4
+ Example:
5
+ rails generate observer Notification
6
+
7
+ This will create:
8
+ config/initializers/observers/notification_observer.rb
9
+ spec/observers/initialized/notification_observer_spec.rb
@@ -0,0 +1,22 @@
1
+ require_relative '../methodist_generator'
2
+
3
+ class ObserverGenerator < MethodistGenerator
4
+ desc 'Create an observer'
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ PATTERN_FOLDER = 'observers'.freeze
8
+ TEMPLATE_FILE = 'observer.erb'.freeze
9
+ TEMPLATE_SPEC_FILE = 'observer_spec.erb'.freeze
10
+ DEFAULT_PREFIX = 'config/initializers'.freeze
11
+
12
+ class_option 'skip-validations', type: :boolean, desc: "Skip validations in generated files", default: false
13
+ class_option 'path', type: :string, desc: "Parent module for new a interactor", default: PATTERN_FOLDER
14
+
15
+ def generate
16
+ template(
17
+ TEMPLATE_FILE,
18
+ "#{filename_with_path(prefix: DEFAULT_PREFIX)}_observer.rb"
19
+ )
20
+ end
21
+ end
22
+
@@ -0,0 +1,13 @@
1
+ class <%= name.camelcase %>Observer < Methodist::Observer
2
+ # observe Klass, :instance_method
3
+
4
+ execute do |klass, instance_method|
5
+ # Write here actions you want to trigger
6
+ # Example:
7
+ # CounterService.new.increment("#{klass.downcase.demodulize}_instance_method")
8
+ end
9
+
10
+ class << self
11
+ # Define here any methods you want to use in the execute block.
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Generator for creating a Service class based on Methodist::Service
3
+
4
+ Example:
5
+ rails generate builder Service
6
+
7
+ This will create:
8
+ app/builders/service.rb
9
+ spec/builders/service_spec.rb
@@ -0,0 +1,27 @@
1
+ require_relative '../methodist_generator'
2
+
3
+ class ServiceGenerator < MethodistGenerator
4
+ desc 'Create service'
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ PATTERN_FOLDER = 'services'.freeze
8
+ TEMPLATE_FILE = 'service.erb'.freeze
9
+ TEMPLATE_SPEC_FILE = 'service_spec.erb'.freeze
10
+
11
+ class_option 'path', type: :string, desc: "Parent module for a new service", default: PATTERN_FOLDER
12
+
13
+ def generate
14
+ template(
15
+ TEMPLATE_FILE,
16
+ "#{filename_with_path}_service.rb"
17
+ )
18
+ end
19
+
20
+ def generate_spec
21
+ template(
22
+ TEMPLATE_SPEC_FILE,
23
+ "#{filename_with_path(prefix: 'spec')}_service_spec.rb"
24
+ )
25
+ end
26
+ end
27
+
@@ -0,0 +1,10 @@
1
+ <%- unless options['clean'] -%>
2
+ # See docs to learn how to use service:
3
+ # https://github.com/QNester/methodist/tree/master/docs/service.md
4
+ <% end -%>
5
+ class <%= name.camelcase %>Service < Methodist::Service
6
+ <%- unless options['clean'] -%>
7
+ # Use the `client` method to define your client.
8
+ # Example: client Redis.new(Settings.redis.to_hash)
9
+ <%- end -%>
10
+ end
@@ -0,0 +1,5 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe <%= name.camelcase %>Service do
4
+ it 'test your service'
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'active_support'
2
+ require "methodist/railtie"
3
+ require 'methodist/pattern'
4
+ require 'methodist/interactor'
5
+ require 'methodist/observer'
6
+ require 'methodist/builder'
7
+ require 'methodist/service'
8
+
9
+ module Methodist
10
+ end
@@ -0,0 +1,100 @@
1
+ require_relative 'pattern'
2
+
3
+ class Methodist::Builder < Methodist::Pattern
4
+ class << self
5
+ def attr_accessor(*args)
6
+ @attributes ||= []
7
+ @attributes += args
8
+ @attributes.uniq!
9
+ super(*args)
10
+ end
11
+
12
+ ##
13
+ # Define attribute and set validation for value
14
+ #
15
+ # ==== Parameters
16
+ # * +attr_name+ [String] - name of defined attribute
17
+ # * +proc+ [Proc] - proc for validate contains value. If proc call result
18
+ # will returns true then #valid_attr? will returns true.
19
+ #
20
+ ##
21
+ def field(attr_name, proc = nil)
22
+ attr_accessor(attr_name)
23
+ set_proc_to_const(proc, attr_name) if proc
24
+ end
25
+
26
+ ##
27
+ # Define multiple attributes and set validation for values
28
+ #
29
+ # ==== Parameters
30
+ # * +attr_names+ [Args] - name of defined attribute
31
+ # * +proc+ [Proc] - proc for validate contains values.
32
+ #
33
+ ##
34
+ def fields(*attr_names, &block)
35
+ attr_names.each { |attr_name| field(attr_name, &block) }
36
+ end
37
+
38
+ ##
39
+ # Array of defined attributes
40
+ ##
41
+ def attributes
42
+ @attributes || []
43
+ end
44
+
45
+ alias_method :attrs, :attributes
46
+
47
+ def set_proc_to_const(proc, attr_name)
48
+ const_set(proc_const_name(attr_name), proc)
49
+ end
50
+
51
+ def proc_const_name(attr_name)
52
+ "VALIDATION_PROC_#{attr_name.upcase}"
53
+ end
54
+ end
55
+
56
+ ##
57
+ # Convert attributes and values to hash.
58
+ #
59
+ # ==== Example
60
+ # In builder with attributes 'title', 'author'
61
+ # when you will use this method you will have:
62
+ # { title: 'Title', author: 'Author' }
63
+ ##
64
+ def to_h
65
+ hash = {}
66
+ self.class.attributes.each do |att|
67
+ hash[att] = self.send(att)
68
+ end
69
+ hash
70
+ end
71
+
72
+ alias_method :to_hash, :to_h
73
+
74
+ ##
75
+ # Validate all attributes.
76
+ ##
77
+ def valid?
78
+ self.class.attributes.all? { |att| valid_attr?(att) }
79
+ end
80
+
81
+ ##
82
+ # Validate attribute value.
83
+ # Proc passed in #field method use for validation.
84
+ # If proc was not defined just return true.
85
+ #
86
+ # ==== Parameters
87
+ # * +attr_name+ [String/Symbol] - name of attribute
88
+ ##
89
+ def valid_attr?(attr_name)
90
+ proc = get_proc(attr_name)
91
+ return true if proc.nil?
92
+ proc.call(self.send(attr_name))
93
+ end
94
+
95
+ private
96
+
97
+ def get_proc(attr_name)
98
+ self.class.const_get(self.class.proc_const_name(attr_name)) # rescue nil
99
+ end
100
+ end
@@ -0,0 +1,112 @@
1
+ require "dry/transaction"
2
+ require "dry/validation"
3
+
4
+ ##
5
+ # == Methodist::Interactor
6
+ # Base class for a Methodist interactor.
7
+ #
8
+ # Methodist::Interactor dependency from dry-rb transactions and dry-rb validations
9
+ #
10
+ #
11
+ class Methodist::Interactor < Methodist::Pattern
12
+ include Dry::Transaction
13
+
14
+ attr_accessor :validation_result
15
+
16
+ SCHEMA_CONST = 'SCHEMA'
17
+
18
+ class << self
19
+ ##
20
+ # Method set Dry::Validation schema for an interactor.
21
+ #
22
+ # Receive block for generating Dry::Validation schema
23
+ # result.
24
+ #
25
+ # class InteractorClass < Methodist::Interactor
26
+ # schema do
27
+ # required(:name).value(:str?)
28
+ # end
29
+ #
30
+ # step :validate
31
+ # end
32
+ #
33
+ # InteractorClass.new.call(name: nil) #=> Failure(ValidationError)
34
+ #
35
+ #
36
+ # ==== See
37
+ # http://dry-rb.org/gems/dry-validation/
38
+ #
39
+ # https://github.com/dry-rb/dry-transaction
40
+ ##
41
+ def schema(&block)
42
+ if block_given?
43
+ const_set SCHEMA_CONST, Dry::Validation.Schema(&block)
44
+ else
45
+ raise SchemaDefinitionError, 'You must pass block to `schema`'
46
+ end
47
+ end
48
+ end
49
+
50
+ ##
51
+ # Method for validation input of interactor parameters.
52
+ #
53
+ # ==== Parameters
54
+ # * +input+ [Hash] - parameters for interactor
55
+ #
56
+ # ==== Example
57
+ #
58
+ # # app/interactors/interactor_class.rb
59
+ # class InteractorClass < Methodist::Interactor
60
+ # schema do
61
+ # required(:name).value(:str?)
62
+ # end
63
+ #
64
+ # step :validate
65
+ # end
66
+ #
67
+ # # your controller action
68
+ # def create
69
+ # result = InteractorClass.new.call(name: nil) #=> Failure(ValidationError)
70
+ # raise InteractorError, result.value if result.failure?
71
+ # end
72
+ #
73
+ # ==== Return
74
+ # * +Dry::Monads::Result::Success+ - success result of interactor step
75
+ # * +Dry::Monads::Result::Failure+ - failure result of interactor step
76
+ #
77
+ # ==== Raise
78
+ # * +SchemaDefinitionError+ - raise if method was called without a schema definition
79
+ #
80
+ #
81
+ # ==== Attention
82
+ # You can redefine failure_validation_value for a custom
83
+ # value returning in case of validation Failure
84
+ ##
85
+ def validate(input)
86
+ input = {} unless input
87
+ raise InputClassError, 'If you want to use custom #validate, you have to pass a hash to an interactor' unless input.is_a?(Hash)
88
+ schema = self.class.const_get SCHEMA_CONST rescue nil
89
+ raise SchemaDefinitionError, 'You have to define a schema with #schema method' unless schema
90
+ @validation_result = schema.call(input)
91
+ return Success(validation_result.to_h) if validation_result.success?
92
+ Failure(failure_validation_value)
93
+ end
94
+
95
+ private
96
+
97
+ ##
98
+ # Method for validation input of interactor parameters.
99
+ ##
100
+ def failure_validation_value
101
+ field = validation_result.errors.keys.first
102
+ {
103
+ error: 'ValidationError',
104
+ field: field,
105
+ reason: "#{field}: #{validation_result.errors[field].first}"
106
+ }
107
+ end
108
+
109
+
110
+ class SchemaDefinitionError < StandardError; end
111
+ class InputClassError < StandardError; end
112
+ end
@@ -0,0 +1,131 @@
1
+ ##
2
+ # == Methodist::Observer
3
+ # Base class for Methodist observers
4
+ #
5
+ #
6
+ class Methodist::Observer < Methodist::Pattern
7
+ CONST_EXECUTION_BLOCK = 'EXEC_BLOCK'.freeze
8
+
9
+ class << self
10
+ ##
11
+ # Subscribe to the instance method of the klass to observe
12
+ #
13
+ # ==== Parameters
14
+ # * +klass+ [Class] - The owner of the method for observation
15
+ # * +method_name+ [String/Symbol] - Observation method name
16
+ #
17
+ # ===== Options
18
+ # * +skip_if+ [Proc] - Skip triggered execution if condition is true
19
+ #
20
+ # ==== Yield
21
+ # main_block - execution block. If this block was passed,
22
+ # `#execute` block will be ignored
23
+ #
24
+ ##
25
+ def observe(klass, method_name, skip_if: nil, &main_block)
26
+ method_name = method_name.to_sym
27
+ original_method = klass.instance_method(method_name)
28
+ method_observe = observer_method(method_name)
29
+ method_dump = method_dump(method_name)
30
+ me = self
31
+
32
+ return false if method_defined?(klass, method_dump)
33
+
34
+ klass.send(:alias_method, method_dump, method_name) # dump method
35
+
36
+ observer_method_proc = -> (*args, &block) do
37
+ result = original_method.bind(self).call(*args, &block)
38
+ unless skip_if.nil?
39
+ return if skip_if.call(result)
40
+ end
41
+ if block_given?
42
+ main_block.call(klass, method_name)
43
+ else
44
+ me.trigger!(klass, method_name)
45
+ end
46
+ result # return the result of the original method
47
+ end
48
+
49
+ klass.send(:define_method, method_observe, observer_method_proc)
50
+
51
+ klass.send(:alias_method, method_name, method_observe) # redefine the original method
52
+ add_observed(klass, method_name)
53
+ true
54
+ end
55
+
56
+ ##
57
+ # Stop observation instance method of klass for observe
58
+ #
59
+ # ==== Parameters
60
+ # * +klass+ [Class] - Klass owner of the observed method
61
+ # * +method_name+ [String/Symbol] - Name of the observed method
62
+ ##
63
+ def stop_observe(klass, method_name)
64
+ method_observe = observer_method(method_name)
65
+ method_dump = method_dump(method_name)
66
+ return false unless method_defined?(klass, method_observe) && method_defined?(klass, method_dump)
67
+
68
+ klass.send(:alias_method, method_name, method_dump) # restore dumped method
69
+ klass.send(:remove_method, method_observe) # remove observed method
70
+ klass.send(:remove_method, method_dump) # remove dump method
71
+ remove_from_observed(klass, method_name)
72
+ true
73
+ end
74
+
75
+ ##
76
+ # The executable block is passed to #execute
77
+ # Parameters *klass* and *method_name* are passed to block call
78
+ #
79
+ # ==== Parameters
80
+ # * +klass+ [Class] - Klass owner of the observed method
81
+ # * +method+ [Class] - Name of the observed method
82
+ #
83
+ # ===== Raise
84
+ # +ExecuteBlockWasNotDefined+ - when no block was passed to the execute method in the observer class
85
+ ##
86
+ def trigger!(klass, method_name)
87
+ block = const_get(CONST_EXECUTION_BLOCK) rescue nil
88
+ raise ExecuteBlockWasNotDefined, "You must define execute block in your #{self.name}" unless block
89
+ block.call(klass, method_name)
90
+ end
91
+
92
+ ##
93
+ # Method for passing execution block for execution after observed method
94
+ ##
95
+ def execute(&block)
96
+ const_set(CONST_EXECUTION_BLOCK, block)
97
+ end
98
+
99
+ def observed_methods
100
+ @observed_method ||= []
101
+ end
102
+
103
+ private
104
+
105
+ def observer_method(method)
106
+ "#{method}_observer"
107
+ end
108
+
109
+ def method_dump(method)
110
+ "#{method}_dump"
111
+ end
112
+
113
+ def klass_with_method(klass, method)
114
+ "#{klass}##{method}"
115
+ end
116
+
117
+ def method_defined?(klass, method)
118
+ klass.instance_methods(false).include?(method.to_sym)
119
+ end
120
+
121
+ def add_observed(klass, method_name)
122
+ observed_methods << klass_with_method(klass, method_name)
123
+ end
124
+
125
+ def remove_from_observed(klass, method_name)
126
+ observed_methods.delete(klass_with_method(klass, method_name))
127
+ end
128
+
129
+ class ExecuteBlockWasNotDefined < StandardError; end
130
+ end
131
+ end
@@ -0,0 +1,5 @@
1
+ class Methodist::Pattern
2
+ def methodistic?
3
+ true
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module Methodist
2
+ class Railtie < ::Rails::Railtie
3
+ end
4
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'pattern'
2
+
3
+ class Methodist::Service < Methodist::Pattern
4
+ CONST_CLIENT = 'CLIENT'
5
+
6
+ class << self
7
+ ##
8
+ # Define client for service
9
+ #
10
+ # ==== Parameters
11
+ # * +client_instance+ [Instance of client klass] - Instance of client class.
12
+ # It could be different clients like redis, elastic, internal api etc.
13
+ ##
14
+ def client(client_instance)
15
+ const_set(CONST_CLIENT, client_instance)
16
+ end
17
+ end
18
+
19
+ ##
20
+ # Instance method use to get defined client
21
+ ##
22
+ def client
23
+ @client ||= self.class.const_get(CONST_CLIENT) #rescue nil
24
+ end
25
+
26
+ class ResponseError < StandardError; end
27
+ end
@@ -0,0 +1,3 @@
1
+ module Methodist
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :methodist do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: methodist
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sergey Nesterov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-05-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-validation
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.11'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: dry-transaction
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.11'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: generator_spec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.9'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.9'
97
+ - !ruby/object:Gem::Dependency
98
+ name: ffaker
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.9'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.9'
111
+ description: "Methodist - a gem for Ruby on Rails created to stop chaos in your business
112
+ logic. This gem adds generators to your rails application using some patterns: interactor,
113
+ builder, observer and service. Just use `rails g <pattern> <new_class_name>`. \nDocs:
114
+ https://github.com/QNester/methodist/wiki"
115
+ email:
116
+ - qnesterr@gmail.com
117
+ executables: []
118
+ extensions: []
119
+ extra_rdoc_files: []
120
+ files:
121
+ - MIT-LICENSE
122
+ - README.md
123
+ - Rakefile
124
+ - lib/generators/builder/USAGE
125
+ - lib/generators/builder/builder_generator.rb
126
+ - lib/generators/builder/templates/builder.erb
127
+ - lib/generators/builder/templates/builder_spec.erb
128
+ - lib/generators/interactor/USAGE
129
+ - lib/generators/interactor/interactor_generator.rb
130
+ - lib/generators/interactor/templates/interactor.erb
131
+ - lib/generators/interactor/templates/interactor_spec.erb
132
+ - lib/generators/methodist_generator.rb
133
+ - lib/generators/observer/USAGE
134
+ - lib/generators/observer/observer_generator.rb
135
+ - lib/generators/observer/templates/observer.erb
136
+ - lib/generators/service/USAGE
137
+ - lib/generators/service/service_generator.rb
138
+ - lib/generators/service/templates/service.erb
139
+ - lib/generators/service/templates/service_spec.erb
140
+ - lib/methodist.rb
141
+ - lib/methodist/builder.rb
142
+ - lib/methodist/interactor.rb
143
+ - lib/methodist/observer.rb
144
+ - lib/methodist/pattern.rb
145
+ - lib/methodist/railtie.rb
146
+ - lib/methodist/service.rb
147
+ - lib/methodist/version.rb
148
+ - lib/tasks/methodist_tasks.rake
149
+ homepage: https://github.com/QNester/methodist
150
+ licenses:
151
+ - MIT
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.7.6
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: Gem for Ruby on Rails created to stop chaos in your buisness logic.
173
+ test_files: []