mtrack 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .buildpath
16
+ .project
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.2
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - ruby-head
4
+ - 2.1
5
+ - 2.0
6
+ - 1.9.3
7
+ - 1.9.2
8
+ - 1.8.7
9
+ addons:
10
+ code_climate:
11
+ repo_token: cc41eb6449075390a8bdb804fa3ff5865a7fbf22384fcd1757730b702eefdb0a
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard :rspec, cmd: "rspec" do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) {|m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch("spec/spec_helper.rb") { "spec" }
5
+ end
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
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ Dir[File.join(File.dirname(__FILE__), "tasks", "**", "*.rb")].each do |file|
5
+ require file
6
+ end
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ task :default => :coverage
data/lib/mtrack.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "mtrack/module_mixin"
2
+ require "mtrack/version"
3
+
4
+ Module.send(:include, MTrack::ModuleMixin)
@@ -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
@@ -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