attestor 0.0.1

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