methodist 0.1.0

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