xavier 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 497980d580ff1c8ec587dd58c3e645cf3a3544c4
4
+ data.tar.gz: 6b36d63bbafa06107025eb9072f24f987638f4bf
5
+ SHA512:
6
+ metadata.gz: ca1e35db421d915af1e168df52aba56d49958633abb3513182709b2e97a376e17b8fd5e28a0e2339684c29f365995340358d71fae0d88d3cd335febfdf9f0907
7
+ data.tar.gz: c130c40a35ac7684a5bcadaa4d843fc98a2cfe89bc9da55faa9668b794ef5fe9ec0cf7eb334954a22f58808450d1c2b101b2d569353c183c09ac5e82d133ecf1
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /measurements
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,41 @@
1
+ require: rubocop-rspec
2
+
3
+ AllCops:
4
+ DisplayCopNames: true
5
+
6
+ # ---------------------- Layout -----------------------
7
+
8
+ Layout/SpaceInsideHashLiteralBraces:
9
+ Enabled: false
10
+
11
+ # ---------------------- Metrics ----------------------
12
+
13
+ Metrics/BlockLength:
14
+ Exclude:
15
+ - spec/**/*_spec.rb
16
+ - xavier.gemspec
17
+
18
+ Metrics/LineLength:
19
+ Max: 120
20
+
21
+ # ----------------------- RSpec -----------------------
22
+
23
+ RSpec/NestedGroups:
24
+ Enabled: false
25
+
26
+ # ----------------------- Style -----------------------
27
+
28
+ Style/FrozenStringLiteralComment:
29
+ Exclude:
30
+ - spec/**/*.rb
31
+
32
+ Style/ClassVars:
33
+ Exclude:
34
+ - spec/support/*.rb
35
+
36
+ # ----------------------- Naming ----------------------
37
+
38
+ Naming/UncommunicativeMethodParamName:
39
+ Exclude:
40
+ - lib/xavier/mutation_strategies/*.rb
41
+ - lib/xavier/mutator.rb
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.2
5
+ before_install: gem install bundler -v 1.16.0
@@ -0,0 +1 @@
1
+ --plugin junk
@@ -0,0 +1,31 @@
1
+ threshold: 100
2
+ rules:
3
+ ApiTag::Presence:
4
+ enabled: true
5
+ exclude: []
6
+ ApiTag::Inclusion:
7
+ enabled: true
8
+ exclude: []
9
+ ApiTag::ProtectedMethod:
10
+ enabled: true
11
+ exclude: []
12
+ ApiTag::PrivateMethod:
13
+ enabled: true
14
+ exclude: []
15
+ ExampleTag:
16
+ enabled: true
17
+ exclude: []
18
+ ReturnTag:
19
+ enabled: true
20
+ exclude: []
21
+ Summary::Presence:
22
+ enabled: true
23
+ exclude: []
24
+ Summary::Length:
25
+ enabled: false
26
+ Summary::Delimiter:
27
+ enabled: false
28
+ exclude: []
29
+ Summary::SingleLine:
30
+ enabled: true
31
+ exclude: []
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
+ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+ Nothing.
9
+
10
+ ## 0.1.0 - 2018-03-17
11
+ ### Added
12
+ - Initial version of the gem.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in xavier.gemspec
8
+ gemspec
@@ -0,0 +1,73 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ xavier (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.0)
10
+ backports (3.11.1)
11
+ coderay (1.1.2)
12
+ diff-lcs (1.3)
13
+ method_source (0.9.0)
14
+ parallel (1.12.1)
15
+ parser (2.5.0.4)
16
+ ast (~> 2.4.0)
17
+ powerpack (0.1.1)
18
+ pry (0.11.3)
19
+ coderay (~> 1.1.0)
20
+ method_source (~> 0.9.0)
21
+ rainbow (3.0.0)
22
+ rake (10.5.0)
23
+ rspec (3.7.0)
24
+ rspec-core (~> 3.7.0)
25
+ rspec-expectations (~> 3.7.0)
26
+ rspec-mocks (~> 3.7.0)
27
+ rspec-core (3.7.1)
28
+ rspec-support (~> 3.7.0)
29
+ rspec-expectations (3.7.0)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.7.0)
32
+ rspec-mocks (3.7.0)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.7.0)
35
+ rspec-support (3.7.1)
36
+ rubocop (0.53.0)
37
+ parallel (~> 1.10)
38
+ parser (>= 2.5)
39
+ powerpack (~> 0.1)
40
+ rainbow (>= 2.2.2, < 4.0)
41
+ ruby-progressbar (~> 1.7)
42
+ unicode-display_width (~> 1.0, >= 1.0.1)
43
+ rubocop-rspec (1.24.0)
44
+ rubocop (>= 0.53.0)
45
+ ruby-progressbar (1.9.0)
46
+ tty-color (0.4.2)
47
+ unicode-display_width (1.3.0)
48
+ yard (0.9.12)
49
+ yard-junk (0.0.7)
50
+ backports
51
+ rainbow
52
+ tty-color
53
+ yard
54
+ yardstick (0.9.9)
55
+ yard (~> 0.8, >= 0.8.7.2)
56
+
57
+ PLATFORMS
58
+ ruby
59
+
60
+ DEPENDENCIES
61
+ bundler (~> 1.16)
62
+ pry (~> 0.11)
63
+ rake (~> 10.0)
64
+ rspec (~> 3.7)
65
+ rubocop (~> 0.53)
66
+ rubocop-rspec (~> 1.24)
67
+ xavier!
68
+ yard (~> 0.9)
69
+ yard-junk (~> 0.0.7)
70
+ yardstick (~> 0.9)
71
+
72
+ BUNDLED WITH
73
+ 1.16.0
@@ -0,0 +1,92 @@
1
+ # Xavier
2
+
3
+ Xavier tracks and reverts state mutations (changes in `instance`, `class`, and `class instance` variables).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'xavier'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install xavier
20
+
21
+ ## Usage
22
+
23
+ Observing a class:
24
+
25
+ ```ruby
26
+ class EvilSingleton
27
+ @@mutated = false
28
+ @mutated = false
29
+
30
+ def self.mutate
31
+ @@mutated = true
32
+ @mutated = true
33
+ end
34
+
35
+ def self.mutated?
36
+ @@mutated && @mutated
37
+ end
38
+ end
39
+
40
+ Xavier.observe(EvilSingleton) do
41
+ EvilSingleton.mutated? # => false
42
+ EvilSingleton.mutate
43
+ EvilSingleton.mutated? # => true
44
+ end
45
+
46
+ EvilSingleton.mutated? # => false
47
+ ```
48
+
49
+ Observing an instance:
50
+
51
+ ```ruby
52
+ class InstanceSingleton
53
+ def initialize
54
+ @mutated = false
55
+ end
56
+
57
+ def mutate
58
+ @mutated = true
59
+ end
60
+
61
+ def mutated?
62
+ @mutated
63
+ end
64
+ end
65
+
66
+ evil_singleton = InstanceSingleton.new
67
+
68
+ Xavier.observe(evil_singleton) do
69
+ evil_singleton.mutated? # => false
70
+ evil_singleton.mutate
71
+ evil_singleton.mutated? # => true
72
+ end
73
+
74
+ evil_singleton.mutated? # => false
75
+ ```
76
+
77
+ ## Development
78
+
79
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
80
+ You can also run:
81
+ * `bin/console` for an interactive prompt that will allow you to experiment.
82
+ * `rake rubocop` to lint the code.
83
+ * `rake verify_measurements` to generate a report of the Yard documentation.
84
+ * `rake yard:junk` to lint the Yard documentation.
85
+
86
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the
87
+ version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version,
88
+ push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
89
+
90
+ ## Contributing
91
+
92
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/xavier.
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'yard/rake/yardoc_task'
6
+ require 'yard-junk/rake'
7
+ require 'yardstick/rake/measurement'
8
+ require 'yardstick/rake/verify'
9
+
10
+ yardstick_options = YAML.load_file('.yardstick.yml')
11
+
12
+ RSpec::Core::RakeTask.new(:spec)
13
+ YARD::Rake::YardocTask.new
14
+ YardJunk::Rake.define_task
15
+ Yardstick::Rake::Measurement.new(:yardstick_measure, yardstick_options)
16
+ Yardstick::Rake::Verify.new
17
+
18
+ # Remove the report on rake clobber
19
+ CLEAN.include('measurements')
20
+
21
+ task default: :spec
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'xavier'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'xavier/observer'
4
+ require 'xavier/version'
5
+
6
+ # Wraps the gem logic in an accessible way.
7
+ module Xavier
8
+ # Raised when attempting to observe a class or instance already under observation.
9
+ AlreadyObserved = Class.new(RuntimeError)
10
+
11
+ # Observes an object, yields a block and then reverts the observed object's class and instance variables.
12
+ #
13
+ # @example Observing a class
14
+ # class EvilSingleton
15
+ # @@mutated = false
16
+ # @mutated = false
17
+ #
18
+ # def self.mutate
19
+ # @@mutated = true
20
+ # @mutated = true
21
+ # end
22
+ #
23
+ # def self.mutated?
24
+ # @@mutated && @mutated
25
+ # end
26
+ # end
27
+ #
28
+ # Xavier.observe(EvilSingleton) do
29
+ # EvilSingleton.mutated? # => false
30
+ # EvilSingleton.mutate
31
+ # EvilSingleton.mutated? # => true
32
+ # end
33
+ #
34
+ # EvilSingleton.mutated? # => false
35
+ #
36
+ # @example Observing an instance
37
+ # class InstanceSingleton
38
+ # def initialize
39
+ # @mutated = false
40
+ # end
41
+ #
42
+ # def mutate
43
+ # @mutated = true
44
+ # end
45
+ #
46
+ # def mutated?
47
+ # @mutated
48
+ # end
49
+ # end
50
+ #
51
+ # evil_singleton = InstanceSingleton.new
52
+ #
53
+ # Xavier.observe(evil_singleton) do
54
+ # evil_singleton.mutated? # => false
55
+ # evil_singleton.mutate
56
+ # evil_singleton.mutated? # => true
57
+ # end
58
+ #
59
+ # evil_singleton.mutated? # => false
60
+ #
61
+ # @raise [AlreadyObserved] When attempting to observe the same object twice before the block returning.
62
+ #
63
+ # @param observable The object whose state should be observed. It can be a class or an instance.
64
+ #
65
+ # @yield The block to be executed before the observable's state is reverted.
66
+ #
67
+ # @return [Integer] The object_id of the observable.
68
+ #
69
+ # @api public
70
+ def self.observe(observable, &block)
71
+ @observer ||= Observer.new
72
+ @observer.observe(observable, &block)
73
+ end
74
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xavier
4
+ module MutationStrategies
5
+ # Copies the class variables from one object to other.
6
+ #
7
+ # @api private
8
+ module ClassCopy
9
+ # Copies the class variables from one object to other.
10
+ #
11
+ # @param from An object where the state will be copied from.
12
+ # @param to An object where the state will be copied to.
13
+ #
14
+ # @return [Array<String>] A list of variable names that were copied.
15
+ def self.copy(from:, to:)
16
+ vars = from.class_variables.map(&:to_s)
17
+ vars.each { |name| to.class_variable_set(name, from.class_variable_get(name)) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xavier
4
+ module MutationStrategies
5
+ # Copies the instance variables from one object to other.
6
+ #
7
+ # @api private
8
+ module InstanceCopy
9
+ # Copies the instance variables from one object to other.
10
+ #
11
+ # @param from An object where the state will be copied from.
12
+ # @param to An object where the state will be copied to.
13
+ #
14
+ # @return [Array<String>] A list of variable names that were copied.
15
+ def self.copy(from:, to:)
16
+ vars = from.instance_variables.map(&:to_s)
17
+ vars.each { |name| to.instance_variable_set(name, from.instance_variable_get(name)) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'xavier/state'
4
+ require 'xavier/mutation_strategies/class_copy'
5
+ require 'xavier/mutation_strategies/instance_copy'
6
+
7
+ module Xavier
8
+ # Applies and unapplies state mutations to objects.
9
+ #
10
+ # @api private
11
+ class Mutator
12
+ # Returns a state representation from a given +observable+.
13
+ #
14
+ # @param observable The class or instance to copy the state from.
15
+ # @param [Array] strategies Array of mutation mutation_strategies that define how the state should be copied.
16
+ #
17
+ # @return [State] The state representation of the given +observable+
18
+ def create_state_from(observable, strategies:)
19
+ state = State.new(observable.object_id)
20
+ strategies.each { |strategy| strategy.copy(from: observable, to: state) }
21
+ state
22
+ end
23
+
24
+ # Applies the given +state+ to the given +observable+.
25
+ #
26
+ # @param from An object where the state will be copied from.
27
+ # @param to An object where the state will be copied to.
28
+ # @param [Array] strategies Array of mutation mutation_strategies that define how the state should be copied.
29
+ #
30
+ # @return [Array] An array of mutation_strategies.
31
+ def apply_state(from:, to:, strategies:)
32
+ strategies.each { |strategy| strategy.copy(from: from, to: to) }
33
+ end
34
+
35
+ # Finds the mutation mutation_strategies for a given +observable+.
36
+ #
37
+ # @param observable Any class or instance
38
+ #
39
+ # @return [Array] An array of mutation mutation_strategies that define how the state should be copied.
40
+ def mutation_strategies_for(observable)
41
+ [MutationStrategies::InstanceCopy].tap do |strategies|
42
+ strategies.push(MutationStrategies::ClassCopy) if observable.is_a?(Class)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'xavier/mutator'
4
+ require 'xavier/states'
5
+
6
+ module Xavier
7
+ # Observes an object for state mutations.
8
+ #
9
+ # @api private
10
+ class Observer
11
+ # Creates an instance of +Observer+.
12
+ #
13
+ # @param [Mutator] mutator Applies and unapplies state modifications.
14
+ # @param [States] states States of objects under observation. Empty by default.
15
+ def initialize(mutator = Mutator.new, states = States.new)
16
+ @states = states
17
+ @mutator = mutator
18
+ end
19
+
20
+ # Observes an object, yields a block and then reverts the observed object's class and instance variables.
21
+ #
22
+ # @example Observing a class
23
+ # class EvilSingleton
24
+ # @@mutated = false
25
+ # @mutated = false
26
+ #
27
+ # def self.mutate
28
+ # @@mutated = true
29
+ # @mutated = true
30
+ # end
31
+ #
32
+ # def self.mutated?
33
+ # @@mutated && @mutated
34
+ # end
35
+ # end
36
+ #
37
+ # Xavier.observe(EvilSingleton) do
38
+ # EvilSingleton.mutated? # => false
39
+ # EvilSingleton.mutate
40
+ # EvilSingleton.mutated? # => true
41
+ # end
42
+ #
43
+ # EvilSingleton.mutated? # => false
44
+ #
45
+ # @example Observing an instance
46
+ # class InstanceSingleton
47
+ # def initialize
48
+ # @mutated = false
49
+ # end
50
+ #
51
+ # def mutate
52
+ # @mutated = true
53
+ # end
54
+ #
55
+ # def mutated?
56
+ # @mutated
57
+ # end
58
+ # end
59
+ #
60
+ # evil_singleton = InstanceSingleton.new
61
+ #
62
+ # Xavier.observe(evil_singleton) do
63
+ # evil_singleton.mutated? # => false
64
+ # evil_singleton.mutate
65
+ # evil_singleton.mutated? # => true
66
+ # end
67
+ #
68
+ # evil_singleton.mutated? # => false
69
+ #
70
+ # @raise [AlreadyObserved] When attempting to observe the same object twice before the block returning.
71
+ #
72
+ # @param observable The object whose state should be observed. It can be a class or an instance.
73
+ #
74
+ # @yield The block to be executed before the observable's state is reverted.
75
+ #
76
+ # @return [Integer] The object_id of the observable.
77
+ #
78
+ # @api private
79
+ def observe(observable)
80
+ raise ArgumentError, 'This method expects a block. Without a block it is useless.' unless block_given?
81
+ raise AlreadyObserved, 'Objects can only be observed once per block.' if being_observed?(observable)
82
+
83
+ strategies = mutator.mutation_strategies_for(observable)
84
+ original_state = mutator.create_state_from(observable, strategies: strategies)
85
+ save_state(original_state)
86
+
87
+ yield
88
+
89
+ mutator.apply_state(from: original_state, to: observable, strategies: strategies)
90
+ delete_state(original_state)
91
+ end
92
+
93
+ private
94
+
95
+ # Returns the state mutator.
96
+ #
97
+ # @return [Xavier::Mutator] Applies and unapplies state modifications.
98
+ attr_reader :mutator
99
+
100
+ # Returns the observed objects states.
101
+ #
102
+ # @return [Xavier::States] States of objects under observation. Used to revert the object to its original state.
103
+ attr_reader :states
104
+
105
+ # Returns whether the given +observable+ is already under observation.
106
+ #
107
+ # @param observable A class or instance to be checked.
108
+ #
109
+ # @return [Boolean] Whether the object is being observed.
110
+ def being_observed?(observable)
111
+ states.contain?(observable.object_id)
112
+ end
113
+
114
+ # Stores a state representation of an observable.
115
+ #
116
+ # @param [State] state The state to be stored.
117
+ #
118
+ # @return [States] The whole states collection.
119
+ def save_state(state)
120
+ states.add(state)
121
+ end
122
+
123
+ # Deletes the state representation of an observable.
124
+ #
125
+ # @param [State] state State representation of an object under observation.
126
+ #
127
+ # @return [Integer] The object_id of the observable.
128
+ def delete_state(state)
129
+ states.remove(state)
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xavier
4
+ # A storage for an observed class or object's internal state.
5
+ # @api private
6
+ class State < BasicObject
7
+ # Returns the object id of the observed object
8
+ #
9
+ # @return [Integer] The +object_id+ of the observed object.
10
+ attr_reader :observed_object_id
11
+
12
+ # Creates a new state representation.
13
+ #
14
+ # @param [Integer] observed_object_id The +object_id+ of the observed object.
15
+ def initialize(observed_object_id)
16
+ @observed_object_id = observed_object_id
17
+ @class_variables = {}
18
+ @instance_variables = {}
19
+ end
20
+
21
+ # Sets the instance variable named by +name+ to the given object.
22
+ #
23
+ # @param [String|Symbol] name Name of the instance variable.
24
+ # @param value Value of the instance variable.
25
+ #
26
+ # @return The value of the given instance variable.
27
+ def instance_variable_set(name, value)
28
+ @instance_variables[name.to_sym] = value
29
+ end
30
+
31
+ # Returns the value of the given instance variable, or +nil+ if the instance variable is not set.
32
+ #
33
+ # @param [String|Symbol] name Name of the instance variable.
34
+ #
35
+ # @return The value of the given instance variable.
36
+ def instance_variable_get(name)
37
+ @instance_variables[name.to_sym]
38
+ end
39
+
40
+ # Sets the class variable named by +name+ to the given object.
41
+ #
42
+ # @param [String|Symbol] name Name of the instance variable.
43
+ # @param value Value of the instance variable.
44
+ #
45
+ # @return The value of the given class variable.
46
+ def class_variable_set(name, value)
47
+ @class_variables[name.to_sym] = value
48
+ end
49
+
50
+ # Returns the value of the given class variable, or nil if the class variable is not set.
51
+ #
52
+ # @param [String|Symbol] name Name of the class variable.
53
+ #
54
+ # @return The value of the given class variable.
55
+ def class_variable_get(name)
56
+ @class_variables[name.to_sym]
57
+ end
58
+
59
+ # Returns true if class is the class of +self+, or if +clazz+ is one of the superclasses of +self+ or modules.
60
+ #
61
+ # @return [Boolean] Whether the given class is the class, superclass or modules of +self+.
62
+ def is_a?(clazz)
63
+ return true if clazz == ::Class
64
+ self.class == clazz
65
+ end
66
+
67
+ # Returns the class +State+.
68
+ #
69
+ # @return [State] The class +State+.
70
+ def class
71
+ State
72
+ end
73
+
74
+ # Returns an array of instance variable names for the receiver.
75
+ #
76
+ # @return [Array<Symbol>] Array of instance variable names.
77
+ def instance_variables
78
+ @instance_variables.keys
79
+ end
80
+
81
+ # Returns an array of the names of class variables in mod.
82
+ #
83
+ # @return [Array<Symbol>] Array of class variable names.
84
+ def class_variables
85
+ @class_variables.keys
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xavier
4
+ # Collection of state representations of objects under observation.
5
+ #
6
+ # @api private
7
+ class States
8
+ # Creates an instance of States.
9
+ def initialize
10
+ @collection = {}
11
+ end
12
+
13
+ # Stores the state representation of an object.
14
+ #
15
+ # @param [State] state State representation of an object under observation.
16
+ #
17
+ # @return [States] The whole states collection.
18
+ def add(state)
19
+ collection[state.observed_object_id] = state
20
+ self
21
+ end
22
+
23
+ # Deletes the state representation of an object.
24
+ #
25
+ # @param [State] state State representation of an object under observation.
26
+ #
27
+ # @return [Integer] The object_id of the observable.
28
+ def remove(state)
29
+ collection.delete(state.observed_object_id)
30
+ end
31
+
32
+ # Returns whether the states collection contains a state representation of an object, using its unique object_id.
33
+ #
34
+ # @param [Integer] object_id The object_id of the observable.
35
+ #
36
+ # @return [Boolean] Whether the states collection contains a state representation of an object.
37
+ def contain?(object_id)
38
+ collection.key?(object_id)
39
+ end
40
+
41
+ private
42
+
43
+ # Returns a hash where the state representations are stored.
44
+ # @return [Hash] A hash where the state representations are stored.
45
+ attr_reader :collection
46
+ end
47
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xavier
4
+ # The version of the gem
5
+ VERSION = '0.1.0'
6
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'xavier/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = 'xavier'
10
+ spec.version = Xavier::VERSION
11
+ spec.authors = ['Wilson Silva']
12
+ spec.email = ['me@wilsonsilva.net']
13
+
14
+ spec.summary = 'Reverts state mutations.'
15
+ spec.description = 'Reverts state mutations.'
16
+ spec.homepage = 'https://github.com/wilsonsilva/xavier'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
21
+
22
+ spec.metadata['yard.run'] = 'yri' # use "yard" to build full HTML docs.
23
+
24
+ spec.bindir = 'exe'
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ['lib']
27
+
28
+ spec.add_development_dependency 'bundler', '~> 1.16'
29
+ spec.add_development_dependency 'pry', '~> 0.11'
30
+ spec.add_development_dependency 'rake', '~> 10.0'
31
+ spec.add_development_dependency 'rspec', '~> 3.7'
32
+ spec.add_development_dependency 'rubocop', '~> 0.53'
33
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.24'
34
+ spec.add_development_dependency 'yard', '~> 0.9'
35
+ spec.add_development_dependency 'yard-junk', '~> 0.0.7'
36
+ spec.add_development_dependency 'yardstick', '~> 0.9'
37
+ end
metadata ADDED
@@ -0,0 +1,192 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xavier
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Wilson Silva
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-03-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.11'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.7'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.53'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.53'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.24'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.24'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.9'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.9'
111
+ - !ruby/object:Gem::Dependency
112
+ name: yard-junk
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.0.7
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.0.7
125
+ - !ruby/object:Gem::Dependency
126
+ name: yardstick
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.9'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.9'
139
+ description: Reverts state mutations.
140
+ email:
141
+ - me@wilsonsilva.net
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".rspec"
148
+ - ".rubocop.yml"
149
+ - ".travis.yml"
150
+ - ".yardopts"
151
+ - ".yardstick.yml"
152
+ - CHANGELOG.md
153
+ - Gemfile
154
+ - Gemfile.lock
155
+ - README.md
156
+ - Rakefile
157
+ - bin/console
158
+ - bin/setup
159
+ - lib/xavier.rb
160
+ - lib/xavier/mutation_strategies/class_copy.rb
161
+ - lib/xavier/mutation_strategies/instance_copy.rb
162
+ - lib/xavier/mutator.rb
163
+ - lib/xavier/observer.rb
164
+ - lib/xavier/state.rb
165
+ - lib/xavier/states.rb
166
+ - lib/xavier/version.rb
167
+ - xavier.gemspec
168
+ homepage: https://github.com/wilsonsilva/xavier
169
+ licenses: []
170
+ metadata:
171
+ yard.run: yri
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ requirements: []
187
+ rubyforge_project:
188
+ rubygems_version: 2.6.12
189
+ signing_key:
190
+ specification_version: 4
191
+ summary: Reverts state mutations.
192
+ test_files: []