rails-patterns 0.2.0 → 0.3.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/Gemfile +4 -0
- data/Gemfile.lock +10 -0
- data/README.md +53 -0
- data/VERSION +1 -1
- data/lib/patterns/collection.rb +29 -0
- data/lib/rails-patterns.rb +1 -0
- data/rails-patterns.gemspec +5 -3
- data/spec/patterns/collection_spec.rb +141 -0
- data/spec/spec_helper.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2318f3f2a2753ab5159fa535d0245110eca7133c
|
4
|
+
data.tar.gz: 6031c700f903e0a62ef3a9951f83932d590a887c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4eabca7c85d89675918642a3f07c72effce0fccb4d709cc4d13c986cb549b9216dc7913cf2303ca464efa69c157b168f3d71301dd0422af13c738fce639e47e4
|
7
|
+
data.tar.gz: b3d706d159d0abedab70f3aadb818c1b977463aa37e683f8a5bd2b3d139a187e3ac72d4b18b98def990ff739ce5d5b224b9de2f03c27e5585283f4213d4479df
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -15,6 +15,7 @@ GEM
|
|
15
15
|
addressable (2.4.0)
|
16
16
|
arel (7.1.4)
|
17
17
|
builder (3.2.3)
|
18
|
+
coderay (1.1.1)
|
18
19
|
concurrent-ruby (1.0.5)
|
19
20
|
descendants_tracker (0.0.4)
|
20
21
|
thread_safe (~> 0.3, >= 0.3.1)
|
@@ -43,6 +44,7 @@ GEM
|
|
43
44
|
rdoc
|
44
45
|
semver
|
45
46
|
jwt (1.5.6)
|
47
|
+
method_source (0.8.2)
|
46
48
|
mime-types (2.99.3)
|
47
49
|
mini_portile2 (2.1.0)
|
48
50
|
minitest (5.10.1)
|
@@ -57,6 +59,12 @@ GEM
|
|
57
59
|
multi_json (~> 1.3)
|
58
60
|
multi_xml (~> 0.5)
|
59
61
|
rack (>= 1.2, < 3)
|
62
|
+
pry (0.10.4)
|
63
|
+
coderay (~> 1.1.0)
|
64
|
+
method_source (~> 0.8.1)
|
65
|
+
slop (~> 3.4)
|
66
|
+
pry-rails (0.3.6)
|
67
|
+
pry (>= 0.10.4)
|
60
68
|
rack (2.0.1)
|
61
69
|
rake (12.0.0)
|
62
70
|
rdoc (5.1.0)
|
@@ -74,6 +82,7 @@ GEM
|
|
74
82
|
rspec-support (~> 3.5.0)
|
75
83
|
rspec-support (3.5.0)
|
76
84
|
semver (1.0.1)
|
85
|
+
slop (3.6.0)
|
77
86
|
thread_safe (0.3.6)
|
78
87
|
tzinfo (1.2.3)
|
79
88
|
thread_safe (~> 0.1)
|
@@ -85,6 +94,7 @@ DEPENDENCIES
|
|
85
94
|
activerecord (>= 4.2.6)
|
86
95
|
bundler (~> 1.0)
|
87
96
|
juwelier (~> 2.1.0)
|
97
|
+
pry-rails
|
88
98
|
rspec
|
89
99
|
|
90
100
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -117,6 +117,59 @@ end
|
|
117
117
|
user_activation.result # <User id: 5803143, email: "tony@patterns.dev ...
|
118
118
|
```
|
119
119
|
|
120
|
+
## Collection
|
121
|
+
|
122
|
+
### When to use it
|
123
|
+
|
124
|
+
One should consider using collection pattern when in need to add a method that relates to the collection a whole.
|
125
|
+
Popular example for such situation is for paginated collections, where for instance `#current_page` getter makes sense only in collection context.
|
126
|
+
Also collections can be used as a container for mapping or grouping logic (especially if the mapping is not 1-1 in terms of size).
|
127
|
+
Collection might also act as a replacement for models not inheriting from ActiveRecord::Base (e.g. `StatusesCollection`, `ColorsCollection` etc.).
|
128
|
+
What is more, collections can be used if we need to encapsulate "flagging" logic - for instance if we need to render a separator element between collection elements based on some specific logic, we can move this logic from view layer to collection and yield an additional flag to control rendering in view.
|
129
|
+
|
130
|
+
### Assumptions and rules
|
131
|
+
|
132
|
+
* Collections include `Enumerable`
|
133
|
+
* Collections can be initialized using `.new`, `.from` and `.for` (aliases)
|
134
|
+
* Collections have to implement `#collection` method that returns object responding to `#each`
|
135
|
+
* Collections provide access to consecutive keyword arguments using `#options` hash
|
136
|
+
* Collections provide access to first argument using `#subject`
|
137
|
+
|
138
|
+
### Examples
|
139
|
+
|
140
|
+
#### Declaration
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
class ColorsCollection < Patterns::Collection
|
144
|
+
AVAILABLE_COLORS = { red: "#FF0000", green: "#00FF00", blue: "#0000FF" }
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def collection
|
149
|
+
AVAILABLE_COLORS
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class CustomerEventsByTypeCollection < Patterns::Collection
|
154
|
+
private
|
155
|
+
|
156
|
+
def collection
|
157
|
+
subject.
|
158
|
+
events.
|
159
|
+
group_by(&:type).
|
160
|
+
transform_values{ |event| event.public_send(options.fetch(:label_method, "description")) }
|
161
|
+
end
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
#### Usage
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
ColorsCollection.new
|
169
|
+
CustomerEventsCollection.for(customer)
|
170
|
+
CustomerEventsCollection.for(customer, label_method: "name")
|
171
|
+
```
|
172
|
+
|
120
173
|
## Further reading
|
121
174
|
|
122
175
|
* [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.3.0
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Patterns
|
2
|
+
class Collection
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
@options = args.extract_options!
|
7
|
+
@subject = args.first
|
8
|
+
end
|
9
|
+
|
10
|
+
def each
|
11
|
+
collection.each do |*args|
|
12
|
+
yield(*args)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
alias from new
|
18
|
+
alias for new
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :options, :subject
|
24
|
+
|
25
|
+
def collection
|
26
|
+
raise NotImplementedError, "#collection not implemented"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/rails-patterns.rb
CHANGED
data/rails-patterns.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
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.3.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.3.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]
|
13
13
|
s.authors = ["Stevo".freeze]
|
14
|
-
s.date = "2017-04-
|
14
|
+
s.date = "2017-04-19"
|
15
15
|
s.description = "A collection of lightweight, standardized, rails-oriented patterns.".freeze
|
16
16
|
s.email = "b.kosmowski@selleo.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
@@ -28,10 +28,12 @@ Gem::Specification.new do |s|
|
|
28
28
|
"Rakefile",
|
29
29
|
"VERSION",
|
30
30
|
"lib/patterns.rb",
|
31
|
+
"lib/patterns/collection.rb",
|
31
32
|
"lib/patterns/query.rb",
|
32
33
|
"lib/patterns/service.rb",
|
33
34
|
"lib/rails-patterns.rb",
|
34
35
|
"rails-patterns.gemspec",
|
36
|
+
"spec/patterns/collection_spec.rb",
|
35
37
|
"spec/patterns/query_spec.rb",
|
36
38
|
"spec/patterns/service_spec.rb",
|
37
39
|
"spec/spec_helper.rb"
|
@@ -0,0 +1,141 @@
|
|
1
|
+
RSpec.describe Patterns::Collection do
|
2
|
+
after { Object.send(:remove_const, :CustomCollection) if defined?(CustomCollection) }
|
3
|
+
|
4
|
+
it "includes Enumerable" do
|
5
|
+
CustomCollection = Class.new(Patterns::Collection)
|
6
|
+
|
7
|
+
expect(CustomCollection).to be < Enumerable
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ".new" do
|
11
|
+
it "exposes all keyword arguments using #options by default" do
|
12
|
+
CustomCollection = Class.new(Patterns::Collection) do
|
13
|
+
private
|
14
|
+
|
15
|
+
def collection
|
16
|
+
[options[:arg_1], options[:arg_2]]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
collection = CustomCollection.new(arg_1: 20, arg_2: 30)
|
21
|
+
|
22
|
+
expect { |b| collection.each(&b) }.to yield_successive_args(20, 30)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "exposes first parameter using #subject by default" do
|
26
|
+
CustomCollection = Class.new(Patterns::Collection) do
|
27
|
+
private
|
28
|
+
|
29
|
+
def collection
|
30
|
+
subject
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
collection = CustomCollection.new([1, 2, 4, 8])
|
35
|
+
|
36
|
+
expect { |b| collection.each(&b) }.to yield_successive_args(1, 2, 4, 8)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe ".from" do
|
41
|
+
it "returns collection instance" do
|
42
|
+
CustomCollection = Class.new(Patterns::Collection) do
|
43
|
+
end
|
44
|
+
|
45
|
+
collection = CustomCollection.from
|
46
|
+
|
47
|
+
expect(collection).to be_a_kind_of(CustomCollection)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "exposes all keyword arguments using #options by default" do
|
51
|
+
CustomCollection = Class.new(Patterns::Collection) do
|
52
|
+
private
|
53
|
+
|
54
|
+
def collection
|
55
|
+
[options[:arg_1], options[:arg_2]]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
collection = CustomCollection.from(arg_1: 20, arg_2: 30)
|
60
|
+
|
61
|
+
expect { |b| collection.each(&b) }.to yield_successive_args(20, 30)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "exposes first parameter using #subject by default" do
|
65
|
+
CustomCollection = Class.new(Patterns::Collection) do
|
66
|
+
private
|
67
|
+
|
68
|
+
def collection
|
69
|
+
subject
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
collection = CustomCollection.from([1, 2, 4, 8])
|
74
|
+
|
75
|
+
expect { |b| collection.each(&b) }.to yield_successive_args(1, 2, 4, 8)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe ".for" do
|
80
|
+
it "returns collection instance" do
|
81
|
+
CustomCollection = Class.new(Patterns::Collection) do
|
82
|
+
end
|
83
|
+
|
84
|
+
collection = CustomCollection.for
|
85
|
+
|
86
|
+
expect(collection).to be_a_kind_of(CustomCollection)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "exposes all keyword arguments using #options by default" do
|
90
|
+
CustomCollection = Class.new(Patterns::Collection) do
|
91
|
+
private
|
92
|
+
|
93
|
+
def collection
|
94
|
+
[options[:arg_1], options[:arg_2]]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
collection = CustomCollection.for(arg_1: 20, arg_2: 30)
|
99
|
+
|
100
|
+
expect { |b| collection.each(&b) }.to yield_successive_args(20, 30)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "exposes first parameter using #subject by default" do
|
104
|
+
CustomCollection = Class.new(Patterns::Collection) do
|
105
|
+
private
|
106
|
+
|
107
|
+
def collection
|
108
|
+
subject
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
collection = CustomCollection.for([1, 2, 4, 8])
|
113
|
+
|
114
|
+
expect { |b| collection.each(&b) }.to yield_successive_args(1, 2, 4, 8)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "#each" do
|
119
|
+
it "requires #collection method being implemented" do
|
120
|
+
CustomCollection = Class.new(Patterns::Collection)
|
121
|
+
|
122
|
+
collection = CustomCollection.new
|
123
|
+
|
124
|
+
expect { collection.each }.to raise_error(NotImplementedError, "#collection not implemented")
|
125
|
+
end
|
126
|
+
|
127
|
+
it "performs #each on result of #collection" do
|
128
|
+
CustomCollection = Class.new(Patterns::Collection) do
|
129
|
+
private
|
130
|
+
|
131
|
+
def collection
|
132
|
+
[[1, "a"], [2, "b"], [3, "c"]]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
collection = CustomCollection.new
|
137
|
+
|
138
|
+
expect { |b| collection.each(&b) }.to yield_successive_args([1, "a"], [2, "b"], [3, "c"])
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-patterns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stevo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-04-
|
11
|
+
date: 2017-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -83,10 +83,12 @@ files:
|
|
83
83
|
- Rakefile
|
84
84
|
- VERSION
|
85
85
|
- lib/patterns.rb
|
86
|
+
- lib/patterns/collection.rb
|
86
87
|
- lib/patterns/query.rb
|
87
88
|
- lib/patterns/service.rb
|
88
89
|
- lib/rails-patterns.rb
|
89
90
|
- rails-patterns.gemspec
|
91
|
+
- spec/patterns/collection_spec.rb
|
90
92
|
- spec/patterns/query_spec.rb
|
91
93
|
- spec/patterns/service_spec.rb
|
92
94
|
- spec/spec_helper.rb
|