rails-patterns 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +49 -0
- data/VERSION +1 -1
- data/lib/patterns/service.rb +18 -0
- data/lib/rails-patterns.rb +1 -0
- data/rails-patterns.gemspec +4 -2
- data/spec/patterns/service_spec.rb +65 -0
- data/spec/spec_helper.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e92ba62b5937443b24c3399655e678b6deb2d273
|
4
|
+
data.tar.gz: '0973bdde4d88392f6cd3a2a8a08fe77a3cf1f007'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8fd3b635495855827654b7b818ee074ac562a919e6610c470cce226afcdf0dfe9944ff5867858d945054732c503babd63588d3775c517f9270846901f56eeb00
|
7
|
+
data.tar.gz: '068e1a602e2e1c591853e1491cfb1bf13e48f56dbaff45ee0db72417be176036939073769212d983da7b098ca5ecccd6935b0812fd83f761ee7db9cd176488e0'
|
data/README.md
CHANGED
@@ -71,3 +71,52 @@ class User < ApplicationRecord
|
|
71
71
|
scope :recenty_activated, RecentlyActivatedUsersQuery
|
72
72
|
end
|
73
73
|
```
|
74
|
+
|
75
|
+
## Service
|
76
|
+
|
77
|
+
### When to use it
|
78
|
+
|
79
|
+
Service objects are commonly used to mitigate problems with model callbacks that interact with external classes ([read more...](http://samuelmullen.com/2013/05/the-problem-with-rails-callbacks/)).
|
80
|
+
Service objects are also useful for handling processes involving multiple steps. E.g. a controller that performs more than one operation on its subject (usually a model instance) is a possible candidate for Extract ServiceObject (or Extract FormObject) refactoring.
|
81
|
+
|
82
|
+
### Assumptions and rules
|
83
|
+
|
84
|
+
* Service objects are always used by calling class-level `.call` method
|
85
|
+
* Service objects have to implement `#call` method
|
86
|
+
* Calling service object's `.call` method executes `#call` and returns service object instance
|
87
|
+
* A result of `#call` method is accessible through `#result` method
|
88
|
+
* It is recommended for `#call` method to be the only public method of service object (besides state readers)
|
89
|
+
* It is recommended to name service object classes after commands (e.g. `ActivateUser` instead of `UserActivation`)
|
90
|
+
|
91
|
+
### Examples
|
92
|
+
|
93
|
+
#### Declaration
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
class ActivateUser < Patterns::Service
|
97
|
+
def initialize(user)
|
98
|
+
@user = user
|
99
|
+
end
|
100
|
+
|
101
|
+
def call
|
102
|
+
user.activate!
|
103
|
+
NotificationsMailer.user_activation_notification(user).deliver_now
|
104
|
+
user
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
attr_reader :user
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
#### Usage
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
user_activation = ActivateUser.call(user)
|
117
|
+
user_activation.result # <User id: 5803143, email: "tony@patterns.dev ...
|
118
|
+
```
|
119
|
+
|
120
|
+
## Further reading
|
121
|
+
|
122
|
+
* [7 ways to decompose fat active record models](http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Patterns
|
2
|
+
class Service
|
3
|
+
attr_reader :result
|
4
|
+
|
5
|
+
def self.call(*args)
|
6
|
+
new(*args).tap do |service|
|
7
|
+
service.instance_variable_set(
|
8
|
+
"@result",
|
9
|
+
service.call
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/rails-patterns.rb
CHANGED
data/rails-patterns.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: rails-patterns 0.
|
5
|
+
# stub: rails-patterns 0.2.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "rails-patterns".freeze
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.2.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
@@ -29,9 +29,11 @@ Gem::Specification.new do |s|
|
|
29
29
|
"VERSION",
|
30
30
|
"lib/patterns.rb",
|
31
31
|
"lib/patterns/query.rb",
|
32
|
+
"lib/patterns/service.rb",
|
32
33
|
"lib/rails-patterns.rb",
|
33
34
|
"rails-patterns.gemspec",
|
34
35
|
"spec/patterns/query_spec.rb",
|
36
|
+
"spec/patterns/service_spec.rb",
|
35
37
|
"spec/spec_helper.rb"
|
36
38
|
]
|
37
39
|
s.homepage = "http://github.com/selleo/pattern".freeze
|
@@ -0,0 +1,65 @@
|
|
1
|
+
RSpec.describe Patterns::Service do
|
2
|
+
after { Object.send(:remove_const, :DoSomething) if defined?(DoSomething) }
|
3
|
+
|
4
|
+
describe ".call" do
|
5
|
+
it "returns instance of service object" do
|
6
|
+
DoSomething = Class.new(Patterns::Service) do
|
7
|
+
def call; end
|
8
|
+
end
|
9
|
+
|
10
|
+
expect(DoSomething.call).to be_kind_of(DoSomething)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "instantiates service object passing arguments to constructor" do
|
14
|
+
DoSomething = Class.new(Patterns::Service) do
|
15
|
+
def initialize(argument_1:, argument_2:); end
|
16
|
+
def call; end
|
17
|
+
end
|
18
|
+
|
19
|
+
expect {
|
20
|
+
DoSomething.call
|
21
|
+
}.to raise_error ArgumentError
|
22
|
+
|
23
|
+
expect {
|
24
|
+
DoSomething.call(argument_1: 10, argument_2: 20)
|
25
|
+
}.not_to raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
it "calls #call method on service object instance" do
|
29
|
+
Spy = Class.new do
|
30
|
+
def self.some_method; end
|
31
|
+
end
|
32
|
+
allow(Spy).to receive(:some_method)
|
33
|
+
DoSomething = Class.new(Patterns::Service) do
|
34
|
+
def call
|
35
|
+
Spy.some_method
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
DoSomething.call
|
40
|
+
|
41
|
+
expect(Spy).to have_received(:some_method)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "requires #call method to be implemented" do
|
45
|
+
DoSomething = Class.new(Patterns::Service)
|
46
|
+
|
47
|
+
expect {
|
48
|
+
DoSomething.call
|
49
|
+
}.to raise_error NotImplementedError
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#result" do
|
54
|
+
it "returns a result of expression within #call method" do
|
55
|
+
DoSomething = Class.new(Patterns::Service) do
|
56
|
+
def call
|
57
|
+
50
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
service = DoSomething.call
|
62
|
+
expect(service.result).to eq 50
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-patterns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stevo
|
@@ -84,9 +84,11 @@ files:
|
|
84
84
|
- VERSION
|
85
85
|
- lib/patterns.rb
|
86
86
|
- lib/patterns/query.rb
|
87
|
+
- lib/patterns/service.rb
|
87
88
|
- lib/rails-patterns.rb
|
88
89
|
- rails-patterns.gemspec
|
89
90
|
- spec/patterns/query_spec.rb
|
91
|
+
- spec/patterns/service_spec.rb
|
90
92
|
- spec/spec_helper.rb
|
91
93
|
homepage: http://github.com/selleo/pattern
|
92
94
|
licenses:
|