attestor 0.0.1

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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +9 -0
  4. data/.metrics +9 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +2 -0
  7. data/.travis.yml +10 -0
  8. data/.yardopts +3 -0
  9. data/Gemfile +5 -0
  10. data/Guardfile +15 -0
  11. data/LICENSE +21 -0
  12. data/README.md +308 -0
  13. data/Rakefile +22 -0
  14. data/attestor.gemspec +24 -0
  15. data/config/metrics/STYLEGUIDE +230 -0
  16. data/config/metrics/cane.yml +5 -0
  17. data/config/metrics/churn.yml +6 -0
  18. data/config/metrics/flay.yml +2 -0
  19. data/config/metrics/metric_fu.yml +15 -0
  20. data/config/metrics/reek.yml +1 -0
  21. data/config/metrics/roodi.yml +24 -0
  22. data/config/metrics/rubocop.yml +75 -0
  23. data/config/metrics/saikuro.yml +3 -0
  24. data/config/metrics/simplecov.yml +6 -0
  25. data/config/metrics/yardstick.yml +37 -0
  26. data/lib/attestor/invalid_error.rb +44 -0
  27. data/lib/attestor/policy/and.rb +36 -0
  28. data/lib/attestor/policy/factory.rb +88 -0
  29. data/lib/attestor/policy/negator.rb +53 -0
  30. data/lib/attestor/policy/node.rb +58 -0
  31. data/lib/attestor/policy/not.rb +48 -0
  32. data/lib/attestor/policy/or.rb +36 -0
  33. data/lib/attestor/policy/xor.rb +36 -0
  34. data/lib/attestor/policy.rb +121 -0
  35. data/lib/attestor/validations/collection.rb +73 -0
  36. data/lib/attestor/validations/item.rb +87 -0
  37. data/lib/attestor/validations/message.rb +55 -0
  38. data/lib/attestor/validations.rb +81 -0
  39. data/lib/attestor/version.rb +9 -0
  40. data/lib/attestor.rb +26 -0
  41. data/spec/spec_helper.rb +14 -0
  42. data/spec/support/policies.rb +49 -0
  43. data/spec/tests/invalid_error_spec.rb +57 -0
  44. data/spec/tests/policy/and_spec.rb +40 -0
  45. data/spec/tests/policy/factory_spec.rb +100 -0
  46. data/spec/tests/policy/negator_spec.rb +57 -0
  47. data/spec/tests/policy/node_spec.rb +44 -0
  48. data/spec/tests/policy/not_spec.rb +40 -0
  49. data/spec/tests/policy/or_spec.rb +40 -0
  50. data/spec/tests/policy/xor_spec.rb +48 -0
  51. data/spec/tests/policy_spec.rb +111 -0
  52. data/spec/tests/validations/collection_spec.rb +100 -0
  53. data/spec/tests/validations/item_spec.rb +153 -0
  54. data/spec/tests/validations/message_spec.rb +71 -0
  55. data/spec/tests/validations_spec.rb +126 -0
  56. metadata +143 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 30d3fe02039b7df2f24461bd4eb4387deda2e93c
4
+ data.tar.gz: f1ec4731ded236180c094171dff71d10fad448db
5
+ SHA512:
6
+ metadata.gz: 595eaa79f730fdf98e8119e5d98c30f0711278ada88dde24b87dc3ea770a8206fef0d0a2c899714504299b530f4eeb93898f1666fb2d61601e01954a76ed6436
7
+ data.tar.gz: 47078bc23acc2749312d84e9b343ae9236b300b099bafbb7a4554a5c4b34a7c0f7579716597ccdd736d7b3e05edc2956f7b8ad7d353cb226aafbe31e844933d2
data/.coveralls.yml ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ service_name: travis-ci
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ *.lock
3
+ .bundle/
4
+ .yardoc/
5
+ coverage/
6
+ doc/
7
+ log/
8
+ pkg/
9
+ tmp/
data/.metrics ADDED
@@ -0,0 +1,9 @@
1
+ # Settings for metric_fu and its packages are collected in the `config/metrics`
2
+ # and loaded by the Hexx::Suit::Metrics::MetricFu.
3
+
4
+ begin
5
+ require "hexx-suit"
6
+ Hexx::Suit::Metrics::MetricFu.load
7
+ rescue LoadError
8
+ false
9
+ end
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --require spec_helper
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ inherit_from: "./config/metrics/rubocop.yml"
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ ---
2
+ language: ruby
3
+ bundler_args: --without metrics
4
+ script: rake test:coverage:run
5
+ rvm:
6
+ - '2.0'
7
+ - ruby-head
8
+ - rbx-2 --2.0
9
+ - jruby-head-20mode
10
+ - jruby-head-21mode
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ --asset LICENSE
2
+ --exclude lib/attestor/version.rb
3
+ --output doc/api
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "hexx-suit", "~> 2.1", group: :metrics if RUBY_ENGINE == "ruby"
data/Guardfile ADDED
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ guard :rspec, cmd: "bundle exec rspec" do
4
+
5
+ watch(%r{^lib/attestor/(.+)\.rb$}) do |m|
6
+ "spec/tests/#{ m[1] }_spec.rb"
7
+ end
8
+
9
+ watch(%r{^spec/tests/.+_spec.rb})
10
+
11
+ watch("lib/*.rb") { "spec" }
12
+ watch("spec/spec_helper.rb") { "spec" }
13
+ watch("spec/support/**/*.rb") { "spec" }
14
+
15
+ end # guard :rspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2015 Andrew Kozin (nepalez), andrew.kozin@gmail.com
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,308 @@
1
+ Attestor
2
+ =====
3
+
4
+ Validations and policies for immutable Ruby objects
5
+
6
+ [![Gem Version](https://img.shields.io/gem/v/attestor.svg?style=flat)][gem]
7
+ [![Build Status](https://img.shields.io/travis/nepalez/attestor/master.svg?style=flat)][travis]
8
+ [![Dependency Status](https://img.shields.io/gemnasium/nepalez/attestor.svg?style=flat)][gemnasium]
9
+ [![Code Climate](https://img.shields.io/codeclimate/github/nepalez/attestor.svg?style=flat)][codeclimate]
10
+ [![Coverage](https://img.shields.io/coveralls/nepalez/attestor.svg?style=flat)][coveralls]
11
+ [![Inline docs](http://inch-ci.org/github/nepalez/attestor.svg)][inch]
12
+
13
+ [codeclimate]: https://codeclimate.com/github/nepalez/attestor
14
+ [coveralls]: https://coveralls.io/r/nepalez/attestor
15
+ [gem]: https://rubygems.org/gems/attestor
16
+ [gemnasium]: https://gemnasium.com/nepalez/attestor
17
+ [travis]: https://travis-ci.org/nepalez/attestor
18
+ [inch]: https://inch-ci.org/github/nepalez/attestor
19
+
20
+ Motivation
21
+ ----------
22
+
23
+ I like the [ActiveModel::Validations] more than any other part of the whole [Rails]. The more I like it the more painful the problem that **it mutates validated objects**.
24
+
25
+ Every time you run validations, the collection of object's `#errors` is cleared and populated with new messages. So you can't validate frozen (immutable) objects without magic tricks.
26
+
27
+ To solve the problem, the `attestor` gem:
28
+
29
+ * Provides a simplest API for validating immutable objects.
30
+ * Makes it possible to isolate validators (as [policy objects]) from their targets.
31
+ * Allows policy objects to be composed by logical operations to provide complex policies.
32
+
33
+ [ActiveModel::Validations]: http://apidock.com/rails/ActiveModel/Validations
34
+ [Rails]: http://rubyonrails.org/
35
+ [policy objects]: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
36
+
37
+ Approach
38
+ --------
39
+
40
+ Instead of collecting errors inside the object, the module's `validate` instance method just raises an exception (`Attestor::InvalidError`), that carries errors outside of the object. The object stays untouched (and can be made immutable).
41
+
42
+ So to speak, validation just attests at the object and complains loudly when things goes wrong.
43
+
44
+ Installation
45
+ ------------
46
+
47
+ Add this line to your application's Gemfile:
48
+
49
+ ```ruby
50
+ # Gemfile
51
+ gem "attestor"
52
+ ```
53
+
54
+ Then execute:
55
+
56
+ ```
57
+ bundle
58
+ ```
59
+
60
+ Or add it manually:
61
+
62
+ ```
63
+ gem install attestor
64
+ ```
65
+
66
+ Basic Use
67
+ ----------
68
+
69
+ `Attestor::Validations` API consists of 1 class method `.validate` and 2 instance methods (`validate` and `invalid`).
70
+
71
+ Declare validation in the same way as ActiveModel's `.validate` method does:
72
+
73
+ ```ruby
74
+ class Transfer < Struct.new(:debet, :credit)
75
+ include Attestor::Validations
76
+
77
+ validate :consistent
78
+ end
79
+ ```
80
+
81
+ You have to define an instance validator method (that can be private):
82
+
83
+ ```ruby
84
+ class Transfer < Struct.new(:debet, :credit)
85
+ # ...
86
+
87
+ private
88
+
89
+ def consistent
90
+ fraud = credit.sum - debet.sum
91
+ invalid :inconsistent, fraud: fraud if fraud != 0
92
+ end
93
+ end
94
+ ```
95
+
96
+ The `#invalid` method translates its argument in a current class scope and raises an exception.
97
+
98
+ ```ruby
99
+ # config/locales/en.yml
100
+ en:
101
+ attestor:
102
+ validations:
103
+ transfer:
104
+ inconsistent: "Credit differs from debet by %{fraud}"
105
+ ```
106
+
107
+ To run validations use the `#validate` instance method:
108
+
109
+ ```ruby
110
+ debet = OpenStruct.new(sum: 100)
111
+ credit = OpenStruct.new(sum: 90)
112
+ fraud_transfer = Transfer.new(debet, credit)
113
+
114
+ begin
115
+ transfer.validate
116
+ rescue => error
117
+ error.object == transfer # => true
118
+ error.messages
119
+ # => ["Credit differs from debet by 10"]
120
+ end
121
+ ```
122
+
123
+ Adding Contexts
124
+ ---------------
125
+
126
+ Sometimes you need to validate the object agaist the subset of validations, not all of them.
127
+
128
+ To do this use `:except` and `:only` options of the `.validate` class method.
129
+
130
+ ```ruby
131
+ class Transfer < Struct.new(:debet, :credit)
132
+ include Attestor::Validations
133
+
134
+ validate :consistent, except: :steal_of_money
135
+ end
136
+ ```
137
+
138
+ Then call a validate method with that context:
139
+
140
+ ```ruby
141
+ fraud_transfer.validate # => InvalidError
142
+ fraud_transfer.validate :steal_of_money # => PASSES!
143
+ ```
144
+
145
+ Just as the `:except` option blacklists validations, the `:only` method whitelists them:
146
+
147
+ ```ruby
148
+ class Transfer < Struct.new(:debet, :credit)
149
+ include Attestor::Validations
150
+
151
+ validate :consistent, only: :fair_trade
152
+ end
153
+
154
+ fraud_transfer.validate # => PASSES
155
+ fraud_transfer.validate :fair_trade # => InvalidError
156
+ ```
157
+
158
+ Policy Objects
159
+ --------------
160
+
161
+ Extract a validator to the separate object (policy). Basically the policy includes `Attestor::Validations` with additional methods.
162
+
163
+ ```ruby
164
+ class ConsistencyPolicy < Struct.new(:debet, :credit)
165
+ include Attestor::Policy
166
+
167
+ validate :consistent
168
+
169
+ private
170
+
171
+ def consistent
172
+ fraud = credit - debet
173
+ invalid :inconsistent, fraud: fraud if fraud != 0
174
+ end
175
+ end
176
+ ```
177
+
178
+ This looks mainly the same as before. But the policy's debet and credit are numbers, not the transactions. **The policy knows nothing about the nature of its attributes** - whether they are sums of transactions, or anything else.
179
+
180
+ This is the core part of the [Policy Object design pattern] - it isolates the rule from unsignificant details of the target.
181
+
182
+ From the other hand, the target needs to know nothing about how the policy works with data:
183
+
184
+ ```ruby
185
+ class Transfer < Struct.new(:debet, :credit)
186
+ include Attestor::Validations
187
+
188
+ validate :constistent
189
+
190
+ private
191
+
192
+ def consistent
193
+ policy = ConsistencyPolicy.new(debet.sum, credit.sum)
194
+ invalid :inconsistent if policy.invalid?
195
+ end
196
+ end
197
+ ```
198
+
199
+ The "new" method `valid?` just returns true or false, trowing error messages out as unsignificant details.
200
+
201
+ If you need messages from policy, you can use `validate` method and capture its exception. But should you?! Instead you'd better to provie the message, that makes sense in the Transfer context.
202
+
203
+ [Policy Object design pattern]: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
204
+
205
+ Complex Policies
206
+ ----------------
207
+
208
+ Now that we isolated policies, we can provide complex policies from simpler ones.
209
+
210
+ Suppose we have two policy objects:
211
+
212
+ ```ruby
213
+ valid_policy.valid? # => true
214
+ invalid_policy.valid? # => false
215
+ ```
216
+
217
+ Use `Policy` factory methods to provide compositions:
218
+
219
+ ```ruby
220
+ complex_policy = valid_policy.not
221
+ complex_policy.validate # => fails
222
+
223
+ complex_policy = valid_policy.and(valid_policy, invalid_policy)
224
+ complex_policy.validate # => fails
225
+
226
+ complex_policy = invalid_policy.or(invalid_policy, valid_policy)
227
+ complex_policy.validate # => passes
228
+
229
+ complex_policy = valid_policy.xor(valid_poicy, valid_policy)
230
+ complex_policy.validate # => fails
231
+
232
+ complex_policy = valid_policy.xor(valid_poicy, invalid_policy)
233
+ complex_policy.validate # => passes
234
+ ```
235
+
236
+ The `or`, `and` and `xor` methods, called without argument(s), don't provide a policy object. They return lazy composer, expecting `#not` method.
237
+
238
+ ```ruby
239
+ complex_policy = valid_policy.and.not(invalid_policy, invalid_policy)
240
+ # this is the same as:
241
+ valid_policy.and(invalid_policy.not, invalid_policy.not)
242
+ ```
243
+
244
+ If you prefer wrapping to chaining, use the `Policy` factory methods instead:
245
+
246
+ ```ruby
247
+ Policy.and(valid_policy, invalid_policy)
248
+ # this is the same as: valid_policy.and invalid_policy
249
+
250
+ Policy.or(valid_policy, invalid_policy)
251
+ # this is the same as: valid_policy.or invalid_policy
252
+
253
+ Policy.xor(valid_policy, invalid_policy)
254
+ # this is the same as: valid_policy.xor invalid_policy
255
+
256
+ Policy.not(valid_policy)
257
+ # this is the same as: valid_policy.not
258
+ ```
259
+
260
+ As before, you can use any number of policies (except for negation of a single policy) at any number of nesting.
261
+
262
+ This can be used either in targets or in complex policies. In the later case do it like this:
263
+
264
+ ```ruby
265
+ class ComplexPolicy < Struct.new(:a, :b, :c)
266
+ include Attestor::Policy
267
+
268
+ validate :complex_rule
269
+
270
+ private
271
+
272
+ def complex_rule
273
+ first_policy = FirstPolicy.new(a, b)
274
+ second_policy = SecondPolicy.new(b, c)
275
+
276
+ invalid :base unless first_policy.xor(second_policy).valid?
277
+ end
278
+ end
279
+ ```
280
+
281
+ Compatibility
282
+ -------------
283
+
284
+ Tested under rubies compatible to rubies with API 2.0+:
285
+
286
+ * MRI 2.0+
287
+ * Rubinius-2 (mode 2.0)
288
+ * JRuby 9000+ (mode 2.0+)
289
+
290
+ Uses [RSpec] 3.0+ for testing and [hexx-suit] for dev/test tools collection.
291
+
292
+ Contributing
293
+ ------------
294
+
295
+ * Fork the project.
296
+ * Read the [STYLEGUIDE](config/metrics/STYLEGUIDE).
297
+ * Make your feature addition or bug fix.
298
+ * Add tests for it. This is important so I don't break it in a
299
+ future version unintentionally.
300
+ * Commit, do not mess with Rakefile or version
301
+ (if you want to have your own version, that is fine but bump version
302
+ in a commit by itself I can ignore when I pull)
303
+ * Send me a pull request. Bonus points for topic branches.
304
+
305
+ License
306
+ -------
307
+
308
+ See the [MIT LICENSE](LICENSE).
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ begin
3
+ require "bundler/setup"
4
+ rescue LoadError
5
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
6
+ exit
7
+ end
8
+
9
+ # Loads bundler tasks
10
+ Bundler::GemHelper.install_tasks
11
+
12
+ # Loads the Hexx::RSpec and its tasks
13
+ begin
14
+ require "hexx-suit"
15
+ Hexx::Suit.install_tasks
16
+ rescue LoadError
17
+ require "hexx-rspec"
18
+ Hexx::RSpec.install_tasks
19
+ end
20
+
21
+ # Sets the Hexx::RSpec :test task to default
22
+ task default: "test:coverage:run"
data/attestor.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "attestor/version"
3
+
4
+ Gem::Specification.new do |gem|
5
+
6
+ gem.name = "attestor"
7
+ gem.version = Attestor::VERSION.dup
8
+ gem.author = "Andrew Kozin"
9
+ gem.email = "andrew.kozin@gmail.com"
10
+ gem.homepage = "https://github.com/nepalez/attestor"
11
+ gem.summary = "Validations for immutable Ruby objects"
12
+ gem.description = gem.summary
13
+ gem.license = "MIT"
14
+
15
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
16
+ gem.test_files = Dir["spec/**/*.rb"]
17
+ gem.extra_rdoc_files = Dir["README.md", "LICENSE"]
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.required_ruby_version = "~> 2.0"
21
+ gem.add_runtime_dependency "extlib", "~> 0.9"
22
+ gem.add_development_dependency "hexx-rspec", "~> 0.4"
23
+
24
+ end # Gem::Specification
@@ -0,0 +1,230 @@
1
+ = Ruby Style Guide
2
+
3
+ Adapted from Dan Kubb's Ruby Style Guide
4
+ https://github.com/dkubb/styleguide/blob/master/RUBY-STYLE
5
+
6
+ == Commiting:
7
+
8
+ * Write descriptive commit messages, following the pattern:
9
+
10
+ [TYPE] name
11
+
12
+ The message, describing the changes being made
13
+
14
+ * Use the types below to mark commits:
15
+
16
+ - FEATURE - for adding new features, or backward-compatible changes;
17
+ - CHANGE - for backward-incompatible changes;
18
+ - BUG FIX - for fixing bugs;
19
+ - REFACTORING - for other changes of the code not affecting the API;
20
+ - OTHER - for changes in documentaton, metrics etc, not touching the code;
21
+ - VERSION - for version changes.
22
+
23
+ * Always separate commits of different types (such as FEATURE and CHANGE).
24
+
25
+ * Try to separate various features from each other.
26
+
27
+ * Include specification to the same commit as the code.
28
+
29
+ * Run all tests before making a commit.
30
+ Never commit the code that break unit tests.
31
+
32
+ * Use metric (run `rake check`) before making a commit.
33
+
34
+ * Do refactoring before making a commit. Best writing is rewriting.
35
+
36
+ * Follow semantic versioning.
37
+
38
+ http://semver.org/
39
+
40
+ * For versions name the commit after a version number, following the pattern:
41
+
42
+ VERSION 1.0.0-rc2
43
+
44
+
45
+ == Formatting:
46
+
47
+ * Use UTF-8. Declare encoding in the first line of every file.
48
+
49
+ # encoding: utf-8
50
+
51
+ * Use 2 space indent, no tabs.
52
+
53
+ * Use Unix-style line endings.
54
+
55
+ * Use spaces around operators, after commas, colons and semicolons,
56
+ around { and before }.
57
+
58
+ * No spaces after (, [ and before ], ).
59
+
60
+ * Align `when` and `else` with `case`.
61
+
62
+ * Use an empty line before the return value of a method (unless it
63
+ only has one line), and an empty line between defs.
64
+
65
+ * Use empty lines to break up a long method into logical paragraphs.
66
+
67
+ * Keep lines fewer than 80 characters.
68
+
69
+ * Strip trailing whitespace.
70
+
71
+
72
+ == Syntax:
73
+
74
+ * Write for 2.0.
75
+
76
+ * Use double quotes
77
+
78
+ http://viget.com/extend/just-use-double-quoted-ruby-strings
79
+
80
+ * Use def with parentheses when there are arguments.
81
+
82
+ * Never use for, unless you exactly know why.
83
+
84
+ * Never use then, except in case statements.
85
+
86
+ * Use when x then ... for one-line cases.
87
+
88
+ * Use &&/|| for boolean expressions, and/or for control flow. (Rule
89
+ of thumb: If you have to use outer parentheses, you are using the
90
+ wrong operators.)
91
+
92
+ * Avoid double negation (!!), unless Null Objects are expected.
93
+
94
+ http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness
95
+
96
+ * Avoid multiline ?:, use if.
97
+
98
+ * Use {...} when defining blocks on one line. Use do...end for multiline
99
+ blocks.
100
+
101
+ * Avoid return where not required.
102
+
103
+ * Use ||= freely.
104
+
105
+ * Use OO regexps, and avoid =~ $0-9, $~, $` and $' when possible.
106
+
107
+ * Do not use Enumerable#inject when the "memo" object does not change between
108
+ iterations, use Enumerable#each_with_object instead (in ruby 1.9,
109
+ active_support and backports).
110
+
111
+ * Prefer ENV.fetch to ENV[] syntax.
112
+ Prefer block syntax for ENV.fetch to usage of the second argument.
113
+
114
+
115
+ == Naming:
116
+
117
+ * Use snake_case for methods.
118
+
119
+ * Use CamelCase for classes and modules. (Keep acronyms like HTTP,
120
+ RFC, XML uppercase.)
121
+
122
+ * Use SCREAMING_SNAKE_CASE for other constants.
123
+
124
+ * Do not use single letter variable names. Avoid uncommunicative names.
125
+
126
+ * Use consistent variable names. Try to keep the variable names close
127
+ to the object class name.
128
+
129
+ * Use names prefixed with _ for unused variables.
130
+
131
+ * When defining a predicate method that compares against another object of
132
+ a similar type, name the argument "other".
133
+
134
+ * Prefer map over collect, detect over find, select over find_all.
135
+
136
+ * Use def self.method to define singleton methods.
137
+
138
+ * Avoid alias when alias_method will do.
139
+
140
+
141
+ == Comments:
142
+
143
+ * Use YARD and its conventions for API documentation. Don't put an
144
+ empty line between the comment block and the def.
145
+
146
+ * Comments longer than a word are capitalized and use punctuation.
147
+ Use one space after periods.
148
+
149
+ * Avoid superfluous comments.
150
+
151
+
152
+ == Code structuring:
153
+
154
+ * Break code into packages, decoupled from the environment.
155
+
156
+ * Wrap packages into gems.
157
+
158
+ * Inject dependencies explicitly.
159
+ Leave all outer references on the border of any package. Inside
160
+ the package use internal references only.
161
+
162
+ * Follow SOLID principles.
163
+
164
+ http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
165
+
166
+ * Only give a method one purpose for existing. If you pass in a boolean
167
+ to a method, what you're saying is that this method has two different
168
+ behaviours. Just split it into two single purpose methods. If you have
169
+ to use the words "AND" or "OR" to describe what the method does it
170
+ probably does too much.
171
+
172
+ * Avoid long methods.
173
+ Try to keep them at no more than 6 lines long, and preferably 4 or less.
174
+
175
+ If sections of a method are logically separate by blank lines, then
176
+ that's probably a sign that those sections should be split into separate
177
+ methods.
178
+
179
+ * Avoid hashes-as-optional-parameters. Does the method do too much?
180
+
181
+ * Avoid long parameter lists.
182
+
183
+ * Add "global" methods to Kernel (if you have to) and make them private.
184
+
185
+ * Use OptionParser for parsing complex command line options and
186
+ ruby -s for trivial command line options.
187
+
188
+ * Avoid needless metaprogramming.
189
+
190
+ * Always freeze objects assigned to constants.
191
+
192
+
193
+ == General:
194
+
195
+ * Code in a functional way, avoid mutation when it makes sense.
196
+
197
+ * Try to have methods either return the state of the object and have
198
+ no side effects, or return self and have side effects. This is
199
+ otherwise known as Command-query separation (CQS):
200
+
201
+ http://en.wikipedia.org/wiki/Command-query_separation
202
+
203
+ * Do not mutate arguments unless that is the purpose of the method.
204
+
205
+ * Try following TRUE heuristics by Sandi Metz
206
+
207
+ http://designisrefactoring.com/2015/02/08/introducing-sandi-metz-true/
208
+
209
+ * Do not mess around in core classes when writing libraries.
210
+ Namespace your code inside the modules, or wrap core classes to
211
+ decorators of your own.
212
+
213
+ * Do not program defensively.
214
+
215
+ http://www.erlang.se/doc/programming_rules.shtml#HDR11
216
+
217
+ * Keep the code simple.
218
+
219
+ * Don't overdesign.
220
+
221
+ * Don't underdesign.
222
+
223
+ * Avoid bugs.
224
+
225
+ * Read other style guides and apply the parts that don't dissent with
226
+ this list.
227
+
228
+ * Be consistent.
229
+
230
+ * Use common sense.
@@ -0,0 +1,5 @@
1
+ ---
2
+ abc_max: "10"
3
+ line_length: "80"
4
+ no_doc: "y"
5
+ no_readme: "y"