mtrack 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.ruby-version +1 -0
- data/.travis.yml +11 -0
- data/Gemfile +2 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +190 -0
- data/Rakefile +10 -0
- data/lib/mtrack.rb +4 -0
- data/lib/mtrack/core.rb +79 -0
- data/lib/mtrack/module_mixin.rb +52 -0
- data/lib/mtrack/state.rb +113 -0
- data/lib/mtrack/state/group.rb +55 -0
- data/lib/mtrack/version.rb +24 -0
- data/mtrack.gemspec +32 -0
- data/spec/lib/mtrack/core_spec.rb +377 -0
- data/spec/lib/mtrack/state/group_spec.rb +38 -0
- data/spec/lib/mtrack/state_spec.rb +135 -0
- data/spec/lib/mtrack/version_spec.rb +5 -0
- data/spec/spec_helper.rb +21 -0
- data/tasks/console.rb +13 -0
- data/tasks/coverage.rb +9 -0
- metadata +157 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 09f257cc34dc5d1126287a8d5086b567de5bb4be
|
4
|
+
data.tar.gz: 0131aac3c34421089a18246398b58ca2d69627dd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e477c84966256fa56a36de12e8031b3272e98f1980e6904cd0720fc8206491a38afd40c64e0ff259462e55b741291ab26e0c10477f96195f987e213b39f1641
|
7
|
+
data.tar.gz: a6b86548b5075ed830d586c12074e7edf2d1ca7c2f4d9dd12c2dbb993861247578f00002aaffd6033512937adcf97421e85b3eb374878279ec5333a8e1339d9a
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.2
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Gabriel de Oliveira
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
# MTrack
|
2
|
+
|
3
|
+
[![Gem Version](http://img.shields.io/gem/v/mtrack.svg)][gem]
|
4
|
+
[![Build Status](http://img.shields.io/travis/gdeoliveira/mtrack.svg)][travis]
|
5
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/gdeoliveira/mtrack.svg)][codeclimate]
|
6
|
+
[![Test Coverage](http://img.shields.io/codeclimate/coverage/github/gdeoliveira/mtrack.svg)][codeclimate]
|
7
|
+
[![Inline docs](http://inch-ci.org/github/gdeoliveira/mtrack.svg?branch=master)][inch-ci]
|
8
|
+
|
9
|
+
[gem]: https://rubygems.org/gems/mtrack
|
10
|
+
[travis]: http://travis-ci.org/gdeoliveira/mtrack
|
11
|
+
[codeclimate]: https://codeclimate.com/github/gdeoliveira/mtrack
|
12
|
+
[inch-ci]: http://inch-ci.org/github/gdeoliveira/mtrack
|
13
|
+
|
14
|
+
MTrack extends the functionality of Modules and Classes and enables them to
|
15
|
+
define public methods within groups. These methods can then be queried back even
|
16
|
+
through a hierarchy of inclusion and/or inheritance.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem "mtrack"
|
24
|
+
```
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
$ gem install mtrack
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
To track a group of methods within a Module (or a Class).
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require "mtrack"
|
40
|
+
|
41
|
+
module Stooges
|
42
|
+
def shemp; end
|
43
|
+
|
44
|
+
track_methods do
|
45
|
+
def curly; end
|
46
|
+
def larry; end
|
47
|
+
def moe; end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Stooges.tracked_methods #=> #<Set: {:curly, :larry, :moe}>
|
52
|
+
```
|
53
|
+
|
54
|
+
Methods can be grouped using an optional name.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
require "mtrack"
|
58
|
+
|
59
|
+
module Numbers
|
60
|
+
def zero; end
|
61
|
+
|
62
|
+
track_methods :integers do
|
63
|
+
track_methods :odd do
|
64
|
+
def one; end
|
65
|
+
def three; end
|
66
|
+
end
|
67
|
+
|
68
|
+
track_methods :even do
|
69
|
+
def two; end
|
70
|
+
def four; end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
Numbers.tracked_methods :integers #=> #<Set: {:one, :three, :two, :four}>
|
76
|
+
Numbers.tracked_methods :odd #=> #<Set: {:one, :three}>
|
77
|
+
Numbers.tracked_methods :even #=> #<Set: {:two, :four}>
|
78
|
+
```
|
79
|
+
|
80
|
+
Tracked methods can be carried to other Modules and Classes via inclusion and
|
81
|
+
inheritance.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
# We're using the previously defined Stooges and Numbers modules here.
|
85
|
+
|
86
|
+
class MyClass
|
87
|
+
include Stooges
|
88
|
+
include Numbers
|
89
|
+
end
|
90
|
+
|
91
|
+
class MySubClass < MyClass
|
92
|
+
end
|
93
|
+
|
94
|
+
MySubClass.tracked_methods #=> #<Set: {:curly, :larry, :moe}>
|
95
|
+
MySubClass.tracked_methods :integers #=> #<Set: {:one, :three, :two, :four}>
|
96
|
+
```
|
97
|
+
|
98
|
+
## Example
|
99
|
+
|
100
|
+
### Simple State Machine
|
101
|
+
|
102
|
+
We'll create a simple state machine using MTrack. First, let's create an
|
103
|
+
abstraction for the state machine.
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
require "mtrack"
|
107
|
+
|
108
|
+
class SimpleStateMachine
|
109
|
+
class << self
|
110
|
+
private
|
111
|
+
|
112
|
+
alias_method :allow_while, :track_methods
|
113
|
+
|
114
|
+
def actions(transitions)
|
115
|
+
transitions.each do |action, state|
|
116
|
+
define_method action, transition_implementation(action, state)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def transition_implementation(action, new_state)
|
121
|
+
proc do
|
122
|
+
if self.class.tracked_methods(state).include? action
|
123
|
+
self.state = new_state
|
124
|
+
state_changed action
|
125
|
+
else
|
126
|
+
state_not_changed action
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def initialize(state)
|
133
|
+
self.state = state
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
attr_accessor :state
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
And now we'll implement a box that can be either _locked_, _closed_ or _open_.
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
class Box < SimpleStateMachine
|
146
|
+
def initialize
|
147
|
+
super :locked
|
148
|
+
end
|
149
|
+
|
150
|
+
allow_while(:locked) { actions :unlock => :closed }
|
151
|
+
allow_while(:closed) { actions :lock => :locked, :open => :open }
|
152
|
+
allow_while(:open) { actions :close => :closed }
|
153
|
+
|
154
|
+
def look
|
155
|
+
"The box is #{state}."
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def state_changed(action)
|
161
|
+
"You #{action} the box."
|
162
|
+
end
|
163
|
+
|
164
|
+
def state_not_changed(action)
|
165
|
+
"You can't #{action} the box while it is #{state}!"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
```
|
169
|
+
|
170
|
+
A sample output for our box could be:
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
box = Box.new #=> #<Box:0x007f00f0be30c8 @state=:locked>
|
174
|
+
box.look #=> "The box is locked."
|
175
|
+
box.open #=> "You can't open the box while it is locked!"
|
176
|
+
box.unlock #=> "You unlock the box."
|
177
|
+
box.look #=> "The box is closed."
|
178
|
+
box.open #=> "You open the box."
|
179
|
+
box.lock #=> "You can't lock the box while it is open!"
|
180
|
+
box.close #=> "You close the box."
|
181
|
+
box.lock #=> "You lock the box."
|
182
|
+
```
|
183
|
+
|
184
|
+
## Contributing
|
185
|
+
|
186
|
+
1. Fork it ( https://github.com/gdeoliveira/mtrack/fork )
|
187
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
188
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
189
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
190
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/lib/mtrack.rb
ADDED
data/lib/mtrack/core.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require "mtrack/state"
|
2
|
+
|
3
|
+
module MTrack
|
4
|
+
|
5
|
+
##
|
6
|
+
# Implements the core tracking functionality of the gem by extending those
|
7
|
+
# Modules and Classes that use MTrack::ModuleMixin#track_methods. Additionally
|
8
|
+
# it will extend Modules and Classes that include a Module that is tracking
|
9
|
+
# methods and Classes that inherit from a Class that is tracking methods.
|
10
|
+
module Core
|
11
|
+
|
12
|
+
##
|
13
|
+
# call-seq:
|
14
|
+
# tracked_methods(group_name = nil) => set
|
15
|
+
#
|
16
|
+
# Returns a set containing the currently tracked methods. An optional
|
17
|
+
# +group_name+ parameter can be passed to get the tracked methods of groups
|
18
|
+
# othen than the default +nil+ group.
|
19
|
+
#
|
20
|
+
# class C
|
21
|
+
# track_methods :my_group do
|
22
|
+
# def method_1; end
|
23
|
+
# def method_2; end
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# C.tracked_methods :my_group #=> #<Set: {:method_1, :method_2}>
|
28
|
+
def tracked_methods(group_name = nil)
|
29
|
+
@__mtrack__.tracked group_name
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
##
|
35
|
+
# Sets this state as a super-state of the Class or Module that has included
|
36
|
+
# the current Module.
|
37
|
+
def included(submodule)
|
38
|
+
state = @__mtrack__
|
39
|
+
submodule.instance_eval do
|
40
|
+
if @__mtrack__.nil?
|
41
|
+
@__mtrack__ = State.new(state)
|
42
|
+
extend Core
|
43
|
+
else
|
44
|
+
@__mtrack__.add_super_state state
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Sets this state as a super-state of the Class that has inherited from the
|
51
|
+
# current Class.
|
52
|
+
def inherited(subclass)
|
53
|
+
state = @__mtrack__
|
54
|
+
subclass.instance_eval { @__mtrack__ = State.new(state) }
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Allows method +name+ to be displayed on #tracked_methods once again after
|
59
|
+
# being disabled by a call to #method_undefined.
|
60
|
+
def method_added(name)
|
61
|
+
@__mtrack__.delete_undefined name
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Stops tracking method +name+ in the current Class or Module.
|
66
|
+
def method_removed(name)
|
67
|
+
@__mtrack__.delete_tracked name
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Stops tracking method +name+ in the current Class or Module and prevents
|
72
|
+
# homonymous methods tracked in super-states from being displayed as
|
73
|
+
# #tracked_methods.
|
74
|
+
def method_undefined(name)
|
75
|
+
@__mtrack__.delete_tracked name
|
76
|
+
@__mtrack__.add_undefined name
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
require "mtrack/core"
|
4
|
+
require "mtrack/state"
|
5
|
+
|
6
|
+
module MTrack
|
7
|
+
|
8
|
+
##
|
9
|
+
# Provides the #track_methods method to all Classes and Modules by being mixed
|
10
|
+
# into the +Module+ class.
|
11
|
+
module ModuleMixin
|
12
|
+
private
|
13
|
+
|
14
|
+
##
|
15
|
+
# call-seq:
|
16
|
+
# track_methods(group_name = nil) => set
|
17
|
+
# track_methods(group_name = nil) {|| ... } => set
|
18
|
+
#
|
19
|
+
# Sets up an MTrack::State instance for this Class or Module and extends it
|
20
|
+
# using MTrack::Core.
|
21
|
+
#
|
22
|
+
# If a block is provided all the methods defined within the block will be
|
23
|
+
# tracked under the optional +group_name+ parameter.
|
24
|
+
#
|
25
|
+
# Returns a set containing the methods that were defined within the block or
|
26
|
+
# an empty set otherwise.
|
27
|
+
#
|
28
|
+
# class C
|
29
|
+
# track_methods do
|
30
|
+
# def method_1; end
|
31
|
+
# track_methods(:inner_group_1) { def method_2; end }
|
32
|
+
# def method_3; end
|
33
|
+
# end
|
34
|
+
# end #=> #<Set: {:method_1, :method_2, :method_3}>
|
35
|
+
def track_methods(group_name = nil, &b)
|
36
|
+
@__mtrack__ ||= State.new
|
37
|
+
extend Core
|
38
|
+
|
39
|
+
if block_given?
|
40
|
+
old_methods = public_instance_methods(false)
|
41
|
+
begin
|
42
|
+
module_eval(&b)
|
43
|
+
ensure
|
44
|
+
tracked = (public_instance_methods(false) - old_methods).map(&:to_sym).to_set
|
45
|
+
@__mtrack__[group_name].merge_tracked tracked unless tracked.empty?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
tracked || Set.new
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/mtrack/state.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
require "mtrack/state/group"
|
4
|
+
|
5
|
+
module MTrack
|
6
|
+
|
7
|
+
##
|
8
|
+
# Holds the internal state of tracked methods on Modules and Classes.
|
9
|
+
class State
|
10
|
+
|
11
|
+
##
|
12
|
+
# call-seq:
|
13
|
+
# new(super_state = nil) => new_state
|
14
|
+
#
|
15
|
+
# Creates a new State instance. An optional +super_state+ parameter can be
|
16
|
+
# passed that will be added to #super_states.
|
17
|
+
def initialize(super_state = nil)
|
18
|
+
self.groups = {}
|
19
|
+
self.super_states = super_state ? Set[super_state] : Set.new
|
20
|
+
self.undefined = Set.new
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# call-seq:
|
25
|
+
# state[group_name] => group
|
26
|
+
#
|
27
|
+
# Returns a particular group in #groups. If the group doesn't exist it will
|
28
|
+
# be created.
|
29
|
+
def [](group_name)
|
30
|
+
groups[group_name] ||= Group.new
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# call-seq:
|
35
|
+
# add_super_state(state) => state
|
36
|
+
#
|
37
|
+
# Adds a new state to #super_states.
|
38
|
+
def add_super_state(state)
|
39
|
+
super_states.add state
|
40
|
+
state
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# call-seq:
|
45
|
+
# add_undefined(name) => name
|
46
|
+
#
|
47
|
+
# Adds +name+ to the #undefined methods set.
|
48
|
+
def add_undefined(name)
|
49
|
+
undefined.add name
|
50
|
+
name
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# call-seq:
|
55
|
+
# delete_tracked(name) => name
|
56
|
+
#
|
57
|
+
# Removes method +name+ from all #groups.
|
58
|
+
def delete_tracked(name)
|
59
|
+
groups.each {|k, v| v.delete_tracked name }
|
60
|
+
name
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# call-seq:
|
65
|
+
# delete_undefined(name) => name
|
66
|
+
#
|
67
|
+
# Removes +name+ from the #undefined methods set.
|
68
|
+
def delete_undefined(name)
|
69
|
+
undefined.delete name
|
70
|
+
name
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# call-seq:
|
75
|
+
# tracked(group_name = nil) => set
|
76
|
+
#
|
77
|
+
# Returns a set containing the currently tracked methods. An optional
|
78
|
+
# +group_name+ parameter can be passed to get the tracked methods of groups
|
79
|
+
# othen than the default +nil+ group.
|
80
|
+
def tracked(group_name = nil)
|
81
|
+
ret_val = merge_super_states group_name
|
82
|
+
ret_val.merge groups[group_name].tracked unless groups[group_name].nil?
|
83
|
+
ret_val.subtract undefined
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
##
|
89
|
+
# A Hash containing the groups defined in the current Class or Module.
|
90
|
+
attr_accessor :groups
|
91
|
+
|
92
|
+
##
|
93
|
+
# A set of references to the states of inherited Classes and included
|
94
|
+
# Modules.
|
95
|
+
attr_accessor :super_states
|
96
|
+
|
97
|
+
##
|
98
|
+
# A set of methods that are currently undefined on this Class or Module.
|
99
|
+
attr_accessor :undefined
|
100
|
+
|
101
|
+
##
|
102
|
+
# call-seq:
|
103
|
+
# merge_super_states(group_name) => set
|
104
|
+
#
|
105
|
+
# Returns a set containing all the methods being tracked for +group_name+
|
106
|
+
# by the #super_states.
|
107
|
+
def merge_super_states(group_name)
|
108
|
+
super_states.inject(Set.new) do |set, state|
|
109
|
+
set.merge state.tracked(group_name)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|