rails-patterns 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e92ba62b5937443b24c3399655e678b6deb2d273
4
- data.tar.gz: '0973bdde4d88392f6cd3a2a8a08fe77a3cf1f007'
3
+ metadata.gz: 2318f3f2a2753ab5159fa535d0245110eca7133c
4
+ data.tar.gz: 6031c700f903e0a62ef3a9951f83932d590a887c
5
5
  SHA512:
6
- metadata.gz: 8fd3b635495855827654b7b818ee074ac562a919e6610c470cce226afcdf0dfe9944ff5867858d945054732c503babd63588d3775c517f9270846901f56eeb00
7
- data.tar.gz: '068e1a602e2e1c591853e1491cfb1bf13e48f56dbaff45ee0db72417be176036939073769212d983da7b098ca5ecccd6935b0812fd83f761ee7db9cd176488e0'
6
+ metadata.gz: 4eabca7c85d89675918642a3f07c72effce0fccb4d709cc4d13c986cb549b9216dc7913cf2303ca464efa69c157b168f3d71301dd0422af13c738fce639e47e4
7
+ data.tar.gz: b3d706d159d0abedab70f3aadb818c1b977463aa37e683f8a5bd2b3d139a187e3ac72d4b18b98def990ff739ce5d5b224b9de2f03c27e5585283f4213d4479df
data/Gemfile CHANGED
@@ -11,3 +11,7 @@ group :development do
11
11
  gem "bundler", "~> 1.0"
12
12
  gem "juwelier", "~> 2.1.0"
13
13
  end
14
+
15
+ group "test" do
16
+ gem "pry-rails"
17
+ end
@@ -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.2.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
@@ -1,3 +1,4 @@
1
1
  require "patterns"
2
2
  require "patterns/query"
3
3
  require "patterns/service"
4
+ require "patterns/collection"
@@ -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.2.0 ruby lib
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.2.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-13"
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
@@ -18,6 +18,7 @@
18
18
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
19
 
20
20
  require "rails-patterns"
21
+ require "pry"
21
22
 
22
23
  RSpec.configure do |config|
23
24
  # rspec-expectations config goes here. You can use an alternate
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.2.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-13 00:00:00.000000000 Z
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