immutability 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.metrics +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +24 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +7 -0
- data/Guardfile +14 -0
- data/LICENSE +21 -0
- data/README.md +201 -0
- data/Rakefile +34 -0
- data/config/metrics/STYLEGUIDE +230 -0
- data/config/metrics/cane.yml +5 -0
- data/config/metrics/churn.yml +6 -0
- data/config/metrics/flay.yml +2 -0
- data/config/metrics/metric_fu.yml +14 -0
- data/config/metrics/reek.yml +1 -0
- data/config/metrics/roodi.yml +24 -0
- data/config/metrics/rubocop.yml +71 -0
- data/config/metrics/saikuro.yml +3 -0
- data/config/metrics/simplecov.yml +6 -0
- data/config/metrics/yardstick.yml +37 -0
- data/immutability.gemspec +25 -0
- data/lib/immutability.rb +126 -0
- data/lib/immutability/rspec.rb +4 -0
- data/lib/immutability/version.rb +9 -0
- data/lib/immutability/with_memory.rb +70 -0
- data/spec/shared/user.rb +16 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/immutable.rb +25 -0
- data/spec/unit/immutability/with_memory_spec.rb +101 -0
- data/spec/unit/immutability_spec.rb +63 -0
- metadata +113 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
folders: # The list of folders to be used by any metric.
|
3
|
+
- lib
|
4
|
+
metrics: # The list of allowed metrics. The other metrics are disabled.
|
5
|
+
- cane
|
6
|
+
- churn
|
7
|
+
- flay
|
8
|
+
- flog
|
9
|
+
- reek
|
10
|
+
- roodi
|
11
|
+
- saikuro
|
12
|
+
format: html
|
13
|
+
output: tmp/metric_fu
|
14
|
+
verbose: false
|
@@ -0,0 +1 @@
|
|
1
|
+
---
|
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
AssignmentInConditionalCheck:
|
3
|
+
CaseMissingElseCheck:
|
4
|
+
ClassLineCountCheck:
|
5
|
+
line_count: 100
|
6
|
+
ClassNameCheck:
|
7
|
+
pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
|
8
|
+
ClassVariableCheck:
|
9
|
+
CyclomaticComplexityBlockCheck:
|
10
|
+
complexity: 2
|
11
|
+
CyclomaticComplexityMethodCheck:
|
12
|
+
complexity: 3
|
13
|
+
EmptyRescueBodyCheck:
|
14
|
+
ForLoopCheck:
|
15
|
+
MethodLineCountCheck:
|
16
|
+
line_count: 6
|
17
|
+
MethodNameCheck:
|
18
|
+
pattern: !ruby/regexp /^[\||\^|\&|\!]$|^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
|
19
|
+
ModuleLineCountCheck:
|
20
|
+
line_count: 120
|
21
|
+
ModuleNameCheck:
|
22
|
+
pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
|
23
|
+
ParameterNumberCheck:
|
24
|
+
parameter_count: 4
|
@@ -0,0 +1,71 @@
|
|
1
|
+
---
|
2
|
+
AllCops:
|
3
|
+
Exclude:
|
4
|
+
- '**/db/schema.rb'
|
5
|
+
|
6
|
+
Lint/HandleExceptions:
|
7
|
+
Exclude:
|
8
|
+
- '**/*_spec.rb'
|
9
|
+
|
10
|
+
Lint/RescueException:
|
11
|
+
Exclude:
|
12
|
+
- '**/*_spec.rb'
|
13
|
+
|
14
|
+
Style/AccessorMethodName:
|
15
|
+
Exclude:
|
16
|
+
- '**/*_spec.rb'
|
17
|
+
|
18
|
+
Style/AsciiComments:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/ClassAndModuleChildren:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Style/Documentation:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Style/EmptyLinesAroundBlockBody:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Style/EmptyLinesAroundClassBody:
|
31
|
+
Enabled: false
|
32
|
+
|
33
|
+
Style/EmptyLinesAroundMethodBody:
|
34
|
+
Enabled: false
|
35
|
+
|
36
|
+
Style/EmptyLinesAroundModuleBody:
|
37
|
+
Enabled: false
|
38
|
+
|
39
|
+
Style/EmptyLineBetweenDefs:
|
40
|
+
Enabled: false
|
41
|
+
|
42
|
+
Style/FileName:
|
43
|
+
Enabled: false
|
44
|
+
|
45
|
+
Style/NumericLiterals:
|
46
|
+
Enabled: false
|
47
|
+
|
48
|
+
Style/RaiseArgs:
|
49
|
+
EnforcedStyle: compact
|
50
|
+
|
51
|
+
Style/SingleLineMethods:
|
52
|
+
Exclude:
|
53
|
+
- '**/*_spec.rb'
|
54
|
+
|
55
|
+
Style/SingleSpaceBeforeFirstArg:
|
56
|
+
Enabled: false
|
57
|
+
|
58
|
+
Style/SpecialGlobalVars:
|
59
|
+
Exclude:
|
60
|
+
- '**/Gemfile'
|
61
|
+
- '**/*.gemspec'
|
62
|
+
|
63
|
+
Style/StringLiterals:
|
64
|
+
EnforcedStyle: double_quotes
|
65
|
+
|
66
|
+
Style/StringLiteralsInInterpolation:
|
67
|
+
EnforcedStyle: double_quotes
|
68
|
+
|
69
|
+
Style/TrivialAccessors:
|
70
|
+
Exclude:
|
71
|
+
- '**/*_spec.rb'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
---
|
2
|
+
# Settings added by the 'hexx-suit' gem
|
3
|
+
output: "tmp/yardstick/output.log"
|
4
|
+
path: "lib/**/*.rb"
|
5
|
+
rules:
|
6
|
+
ApiTag::Presence:
|
7
|
+
enabled: true
|
8
|
+
exclude: []
|
9
|
+
ApiTag::Inclusion:
|
10
|
+
enabled: true
|
11
|
+
exclude: []
|
12
|
+
ApiTag::ProtectedMethod:
|
13
|
+
enabled: true
|
14
|
+
exclude: []
|
15
|
+
ApiTag::PrivateMethod:
|
16
|
+
enabled: false
|
17
|
+
exclude: []
|
18
|
+
ExampleTag:
|
19
|
+
enabled: true
|
20
|
+
exclude: []
|
21
|
+
ReturnTag:
|
22
|
+
enabled: true
|
23
|
+
exclude: []
|
24
|
+
Summary::Presence:
|
25
|
+
enabled: true
|
26
|
+
exclude: []
|
27
|
+
Summary::Length:
|
28
|
+
enabled: true
|
29
|
+
exclude: []
|
30
|
+
Summary::Delimiter:
|
31
|
+
enabled: true
|
32
|
+
exclude: []
|
33
|
+
Summary::SingleLine:
|
34
|
+
enabled: true
|
35
|
+
exclude: []
|
36
|
+
threshold: 100
|
37
|
+
verbose: false
|
@@ -0,0 +1,25 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "immutability/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
|
6
|
+
gem.name = "immutability"
|
7
|
+
gem.version = Immutability::VERSION.dup
|
8
|
+
gem.author = "Andrew Kozin"
|
9
|
+
gem.email = "andrew.kozin@gmail.com"
|
10
|
+
gem.homepage = "https://github.com/nepalez/immutability"
|
11
|
+
gem.summary = "Make instances immutable (deeply frozen) and versioned"
|
12
|
+
gem.license = "MIT"
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
15
|
+
gem.test_files = Dir["spec/**/*.rb"]
|
16
|
+
gem.extra_rdoc_files = Dir["README.md", "LICENSE"]
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
|
19
|
+
gem.required_ruby_version = ">= 1.9.3"
|
20
|
+
|
21
|
+
gem.add_runtime_dependency "ice_nine", "~> 0.11.1"
|
22
|
+
|
23
|
+
gem.add_development_dependency "hexx-rspec", "~> 0.5"
|
24
|
+
|
25
|
+
end # Gem::Specification
|
data/lib/immutability.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "ice_nine"
|
4
|
+
|
5
|
+
require_relative "immutability/with_memory"
|
6
|
+
|
7
|
+
# Makes the object immutable (deeply frozen) with possibility to remember
|
8
|
+
# and forget previous states (snapshots).
|
9
|
+
#
|
10
|
+
# Uses 'ice_nine' to freeze object deeply by the initializer
|
11
|
+
#
|
12
|
+
# @example Without memory
|
13
|
+
# require "immutability"
|
14
|
+
#
|
15
|
+
# class User
|
16
|
+
# include Immutability
|
17
|
+
#
|
18
|
+
# attr_reader :name
|
19
|
+
#
|
20
|
+
# def initialize(name)
|
21
|
+
# @name = name
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# user = User.new "Andre"
|
26
|
+
# user.name # => "Andre"
|
27
|
+
# # It is frozen deeply
|
28
|
+
# user.frozen? # => true
|
29
|
+
# user.name.frozen? # => true
|
30
|
+
#
|
31
|
+
# new_user = user.update { @name = "Andrew" }
|
32
|
+
# new_user.name # => "Andrew"
|
33
|
+
# # It is frozen deeply
|
34
|
+
# new_user.frozen? # => true
|
35
|
+
# new_user.name.frozen? # => true
|
36
|
+
#
|
37
|
+
# @example With memory
|
38
|
+
# class User
|
39
|
+
# include Immutability.with_memory
|
40
|
+
#
|
41
|
+
# attr_reader :name
|
42
|
+
#
|
43
|
+
# def initialize(name)
|
44
|
+
# @name = name
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# user = User.new "Andre"
|
49
|
+
# user.name # => "Andre"
|
50
|
+
# # It has version and reference to the parent (nil for the first version)
|
51
|
+
# user.version # => 0
|
52
|
+
# user.parent # => nil
|
53
|
+
#
|
54
|
+
# new_user = user.update { @name = "Andrew" }
|
55
|
+
# new_user.name # => "Andrew"
|
56
|
+
# # It stores current version and reference to the parent
|
57
|
+
# new_user.version # => 1
|
58
|
+
# new_user.parent # => #<User @name="Andre">
|
59
|
+
#
|
60
|
+
# mankurt = new_user.forget_history
|
61
|
+
# mankurt.name # => "Andrew"
|
62
|
+
# # It doesn't refer to previous states (they can be garbage collected)
|
63
|
+
# mankurt.version # => 0
|
64
|
+
# mankurt.parent # => nil
|
65
|
+
#
|
66
|
+
# @author Andrew Kozin <Andrew.Kozin@gmail.com>
|
67
|
+
#
|
68
|
+
module Immutability
|
69
|
+
|
70
|
+
# Methods to be added to class, that included the `Immutability` module
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
#
|
74
|
+
module ClassMethods
|
75
|
+
|
76
|
+
# Reloads instance's constructor to make it immutable
|
77
|
+
#
|
78
|
+
# @api private
|
79
|
+
#
|
80
|
+
# @param [Object, Array<Object>] args
|
81
|
+
#
|
82
|
+
# @return [Object]
|
83
|
+
#
|
84
|
+
def new(*args)
|
85
|
+
IceNine.deep_freeze __new__(*args)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def __new__(*args, &block)
|
91
|
+
allocate.tap do |instance|
|
92
|
+
instance.__send__(:initialize, *args)
|
93
|
+
instance.instance_eval(&block) if block_given?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end # module ClassMethods
|
98
|
+
|
99
|
+
# Returns the new immutable instances that preserves old variables and
|
100
|
+
# updates some of them from inside the block
|
101
|
+
#
|
102
|
+
# @param [Proc] block
|
103
|
+
# The block to be evaluated by new instance's "initializer"
|
104
|
+
#
|
105
|
+
# @return [Object] the updated instance
|
106
|
+
#
|
107
|
+
def update(&block)
|
108
|
+
instance = dup
|
109
|
+
instance.instance_eval(&block) if block_given?
|
110
|
+
IceNine.deep_freeze(instance)
|
111
|
+
end
|
112
|
+
|
113
|
+
# @private
|
114
|
+
def self.included(klass)
|
115
|
+
klass.instance_exec(ClassMethods) { |mod| extend(mod) }
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the module extended by features to record history
|
119
|
+
#
|
120
|
+
# @return [Module]
|
121
|
+
#
|
122
|
+
def self.with_memory
|
123
|
+
WithMemory
|
124
|
+
end
|
125
|
+
|
126
|
+
end # module Immutability
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Immutability
|
4
|
+
|
5
|
+
# Extends +Immutability+ to remember version and previous state of instance.
|
6
|
+
#
|
7
|
+
# @author Andrew Kozin <Andrew.Kozin@gmail.com>
|
8
|
+
#
|
9
|
+
module WithMemory
|
10
|
+
|
11
|
+
# @private
|
12
|
+
class << self
|
13
|
+
private
|
14
|
+
|
15
|
+
def included(klass)
|
16
|
+
klass.__send__ :include, Immutability
|
17
|
+
klass.__send__ :extend, ClassMethods
|
18
|
+
klass.__send__ :define_method, :update, update
|
19
|
+
end
|
20
|
+
|
21
|
+
# Redefines the +update+ so that it increment version and refer
|
22
|
+
# to the previous snapshot of the continuous object
|
23
|
+
#
|
24
|
+
def update
|
25
|
+
proc do |&block|
|
26
|
+
current = [(version + 1), self]
|
27
|
+
super() do
|
28
|
+
@version, @parent = current
|
29
|
+
instance_eval(&block) if block
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Adds version and parent variables to newly created instance
|
36
|
+
#
|
37
|
+
module ClassMethods
|
38
|
+
private
|
39
|
+
|
40
|
+
def __new__(*args)
|
41
|
+
super(*args) { @version, @parent = 0 }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @!attribute [r] version
|
46
|
+
#
|
47
|
+
# @return [Integer] the current version number of the instance
|
48
|
+
#
|
49
|
+
attr_reader :version
|
50
|
+
|
51
|
+
# @!attribute [r] parent
|
52
|
+
#
|
53
|
+
# @return [Object] the previous version (immutable instance)
|
54
|
+
#
|
55
|
+
attr_reader :parent
|
56
|
+
|
57
|
+
# Forgets the previous history of the object
|
58
|
+
#
|
59
|
+
# Returns a new instance with the same variables,
|
60
|
+
# except for [#version] and [#parent] that are set to +0+ and +nil+.
|
61
|
+
#
|
62
|
+
# @return [Object]
|
63
|
+
#
|
64
|
+
def forget_history
|
65
|
+
update { @version, @parent = 0 }
|
66
|
+
end
|
67
|
+
|
68
|
+
end # module WithMemory
|
69
|
+
|
70
|
+
end # module Immutability
|
data/spec/shared/user.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
begin
|
4
|
+
require "hexx-suit"
|
5
|
+
Hexx::Suit.load_metrics_for(self)
|
6
|
+
rescue LoadError
|
7
|
+
require "hexx-rspec"
|
8
|
+
Hexx::RSpec.load_metrics_for(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Loads the code under test
|
12
|
+
require "immutability"
|
13
|
+
|
14
|
+
# Loads custom matchers
|
15
|
+
require "immutability/rspec"
|
16
|
+
|
17
|
+
# Loads shared examples
|
18
|
+
require_relative "shared/user"
|