aspectual 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5e0539219c7424a96ef89cc5e2b1e282de09f628
4
- data.tar.gz: 0fd69a69034f8a5b7e14cb336a0252b61355dc03
3
+ metadata.gz: 791be340bcb932e339d64f0735635b806b0ed860
4
+ data.tar.gz: e85f88a62334e97d685f2d626d7d9e7b8a45c8ff
5
5
  SHA512:
6
- metadata.gz: d4ad4996acbfcecbbe2ed8079629f837befe16e77f99610fb97ab20ab7f010792dd663940453ddbde0829782e24f1f8d623dce35477223d79d56efa111599136
7
- data.tar.gz: 2ea04111ca09aea033b3aa5f0fe1e5290744853c1a76fd89a9f9ff82b0f02c0f7d3b92317afc6e13809585e6c575ea98fd6a15f324db8af66bb4cd78c8d1a1bb
6
+ metadata.gz: d20e5e8d7ec23d34f6664626248f49e29276f2cb963383f19579bc6300e8182ce791eccc639d8d8bda12e3929f3aa3210d807504a3c96e3170001da86ccf07dc
7
+ data.tar.gz: c5dd639e0b3fa4f1e187fc445ba6682aef248a100bc99453e6e6d61a657c34284e6cf2edd2f9723e9390798ea21bcfd81f329f2a8c37f1f6d34fb729f64da7ef
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in overload.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,27 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ aspectual (0.0.2)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ rake (10.3.1)
11
+ rspec (2.14.1)
12
+ rspec-core (~> 2.14.0)
13
+ rspec-expectations (~> 2.14.0)
14
+ rspec-mocks (~> 2.14.0)
15
+ rspec-core (2.14.8)
16
+ rspec-expectations (2.14.5)
17
+ diff-lcs (>= 1.1.3, < 2.0)
18
+ rspec-mocks (2.14.6)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ aspectual!
25
+ bundler (~> 1.3)
26
+ rake
27
+ rspec
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ [![Gem Version](https://badge.fury.io/rb/aspectual.png)](http://badge.fury.io/rb/aspectual)
2
+
3
+ # Aspectual
4
+
5
+ A simple gem to support minimal Aspect Oriented Programming in ruby.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'aspectual'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install aspectual
20
+
21
+ ## Usage
22
+
23
+ Extend the module
24
+
25
+ extend Aspectual
26
+
27
+ Create any methods you want as aspects (Aspectual will not add methods you don't have as aspects)
28
+
29
+ Then declare your aspects
30
+
31
+ aspects before: :logging, after: :more_logging
32
+ def foo
33
+ "foo"
34
+ end
35
+
36
+ aspects before: [:notify_user, :notify_admin], after: [:remove_temp_files, :play_sound]
37
+ def bar
38
+ "bar"
39
+ end
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
Binary file
data/aspectual.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'aspectual/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "aspectual"
8
+ spec.version = Aspectual::VERSION
9
+ spec.authors = ["Alex Sunderland"]
10
+ spec.email = ["agentantelope+aspectual@gmail.com"]
11
+ spec.description = %q{
12
+ A simple gem to support minimal Aspect Oriented Programming in ruby.
13
+ }
14
+ spec.summary = %q{A gem to help with AOP.}
15
+ spec.homepage = "https://github.com/AgentAntelope/aspectual"
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ end
@@ -0,0 +1,3 @@
1
+ module Aspectual
2
+ VERSION = '0.0.2'
3
+ end
data/lib/aspectual.rb ADDED
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Aspectual
4
+ def aspects(aspects)
5
+ # The before aspect have to be reversed so that when multiple before
6
+ # aspects are added to one method, the first declared method will be
7
+ # added last.
8
+ @_before_aspects = Array(aspects[:before]).reverse
9
+ @_after_aspects = Array(aspects[:after])
10
+ end
11
+
12
+ def method_added(method_symbol)
13
+ return unless has_aspects_declared?
14
+ return if @_defining_method
15
+ if method_defined?(method_symbol)
16
+
17
+ %w{before after}.each do |position|
18
+ current_aspects = instance_variable_get("@_#{position}_aspects")
19
+ if current_aspects
20
+ current_aspects.each do |aspect|
21
+ define_aspect_method(method_symbol, position, aspect)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def has_aspects_declared?
31
+ @_before_aspects || @_after_aspects
32
+ end
33
+
34
+ def define_aspect_method(method_symbol, position, aspect)
35
+ # This is to prevent us from looping because we're about to define some methods.
36
+ @_defining_method = true
37
+
38
+ with_aspect_method_name = method_symbol.to_s + "_with_#{position}_" + aspect.to_s
39
+ without_aspect_method_name = method_symbol.to_s + "_without_#{position}_" + aspect.to_s
40
+
41
+ case position
42
+ when "after"
43
+ aspect_proc = lambda {|*args| send(without_aspect_method_name, *args); send(aspect, *args)}
44
+ when "before"
45
+ aspect_proc = lambda {|*args| send(aspect, *args); send(without_aspect_method_name, *args)}
46
+ end
47
+
48
+ define_method(with_aspect_method_name, aspect_proc)
49
+
50
+ scope_method_to_parent(method_symbol, with_aspect_method_name)
51
+
52
+ alias_method_chain(method_symbol, aspect.to_sym, position)
53
+
54
+ @_defining_method = false
55
+ end
56
+
57
+ def scope_method_to_parent(without_method, target)
58
+ case
59
+ when public_method_defined?(without_method)
60
+ public target
61
+ when protected_method_defined?(without_method)
62
+ protected target
63
+ when private_method_defined?(without_method)
64
+ private target
65
+ end
66
+ end
67
+
68
+ # adapted from active support
69
+ def alias_method_chain(target, feature, position)
70
+ # Strip out punctuation on predicates or bang methods since
71
+ # e.g. target?_without_position_feature is not a valid method name.
72
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
73
+
74
+ with_method = "#{aliased_target}_with_#{position}_#{feature}#{punctuation}"
75
+ without_method = "#{aliased_target}_without_#{position}_#{feature}#{punctuation}"
76
+
77
+ alias_method without_method, target
78
+ alias_method target, with_method
79
+
80
+ scope_method_to_parent(without_method, target)
81
+ end
82
+ end
@@ -0,0 +1,173 @@
1
+ require_relative '../../lib/aspectual'
2
+
3
+ describe Aspectual do
4
+ class TestClass
5
+ extend Aspectual
6
+
7
+ attr_reader :methods_called
8
+
9
+ def initialize
10
+ # This is to ensure that all methods are properly called within the
11
+ # context of the current instance of this object.
12
+ @methods_called = []
13
+ end
14
+
15
+ # These definitions could be looped over to make this class shorter, but the
16
+ # decision has been made to go for clarity over brevity.
17
+ def single_test_method
18
+ methods_called << "single_test_method"
19
+ self
20
+ end
21
+
22
+ def array_test_method_0
23
+ methods_called << "array_test_method_0"
24
+ self
25
+ end
26
+
27
+ def array_test_method_1
28
+ methods_called << "array_test_method_1"
29
+ self
30
+ end
31
+
32
+ def before_test_method_0
33
+ methods_called << "before_test_method_0"
34
+ self
35
+ end
36
+
37
+ def before_test_method_1
38
+ methods_called << "before_test_method_1"
39
+ self
40
+ end
41
+
42
+ def after_test_method_0
43
+ methods_called << "after_test_method_0"
44
+ self
45
+ end
46
+
47
+ def after_test_method_1
48
+ methods_called << "after_test_method_1"
49
+ self
50
+ end
51
+
52
+ aspects before: :single_test_method
53
+ def single_before_test_method
54
+ methods_called << "single_before_test_method"
55
+ self
56
+ end
57
+
58
+ aspects after: :single_test_method
59
+ def single_after_test_method
60
+ methods_called << "single_after_test_method"
61
+ self
62
+ end
63
+
64
+ aspects before: [:array_test_method_0, :array_test_method_1]
65
+ def array_before_test_method
66
+ methods_called << "array_before_test_method"
67
+ self
68
+ end
69
+
70
+ aspects after: [:array_test_method_0, :array_test_method_1]
71
+ def array_after_test_method
72
+ methods_called << "array_after_test_method"
73
+ self
74
+ end
75
+
76
+ aspects before: [:before_test_method_0, :before_test_method_1], after: [:after_test_method_0, :after_test_method_1]
77
+ def before_and_after_aspects_test_method
78
+ methods_called << "before_and_after_aspects_test_method"
79
+ self
80
+ end
81
+
82
+ def public_aspect_method
83
+ methods_called << "public_aspect_method"
84
+ self
85
+ end
86
+
87
+ protected
88
+ aspects before: :public_aspect_method
89
+ def protected_method
90
+ methods_called << "protected_method"
91
+ self
92
+ end
93
+
94
+ private
95
+ aspects before: :public_aspect_method
96
+ def private_method
97
+ methods_called << "private_method"
98
+ self
99
+ end
100
+ end
101
+
102
+ describe "before aspects" do
103
+ it "calls before aspect methods before the called method" do
104
+ test_instance = TestClass.new.single_before_test_method
105
+ expect(test_instance.methods_called).to match_array(%w{
106
+ single_test_method
107
+ single_before_test_method
108
+ })
109
+ end
110
+
111
+ it "allows multiple aspects to be declared" do
112
+ test_instance = TestClass.new.array_before_test_method
113
+ expect(test_instance.methods_called).to match_array(%w{
114
+ array_test_method_0
115
+ array_test_method_1
116
+ array_before_test_method
117
+ })
118
+ end
119
+ end
120
+
121
+ describe "after aspects" do
122
+ it "calls before aspect methods before the called method" do
123
+ test_instance = TestClass.new.single_after_test_method
124
+ expect(test_instance.methods_called).to match_array(%w{
125
+ single_test_method
126
+ single_after_test_method
127
+ })
128
+ end
129
+
130
+ it "allows multiple aspects to be declared" do
131
+ test_instance = TestClass.new.array_after_test_method
132
+ expect(test_instance.methods_called).to match_array(%w{
133
+ array_after_test_method
134
+ array_test_method_0
135
+ array_test_method_1
136
+ })
137
+ end
138
+ end
139
+
140
+ describe "mixed aspects" do
141
+ it "calls all the aspect declarations in the correct order" do
142
+ test_instance = TestClass.new.before_and_after_aspects_test_method
143
+ expect(test_instance.methods_called).to match_array(%w{
144
+ before_test_method_0
145
+ before_test_method_1
146
+ before_and_after_aspects_test_method
147
+ after_test_method_0
148
+ after_test_method_1
149
+ })
150
+ end
151
+ end
152
+
153
+ describe "public methods" do
154
+ it "stay public" do
155
+ test_instance = TestClass.new.single_before_test_method
156
+ expect(test_instance.public_methods).to include(:single_before_test_method)
157
+ end
158
+ end
159
+
160
+ describe "protected methods" do
161
+ it "stay protected" do
162
+ test_instance = TestClass.new
163
+ expect(test_instance.protected_methods).to include(:protected_method)
164
+ end
165
+ end
166
+
167
+ describe "private methods" do
168
+ it "stay private" do
169
+ test_instance = TestClass.new
170
+ expect(test_instance.private_methods).to include(:private_method)
171
+ end
172
+ end
173
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aspectual
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Sunderland
@@ -59,8 +59,16 @@ email:
59
59
  executables: []
60
60
  extensions: []
61
61
  extra_rdoc_files: []
62
- files: []
63
- homepage: ''
62
+ files:
63
+ - Gemfile
64
+ - Gemfile.lock
65
+ - README.md
66
+ - aspectual-0.0.1.gem
67
+ - aspectual.gemspec
68
+ - lib/aspectual.rb
69
+ - lib/aspectual/version.rb
70
+ - spec/lib/aspectual_spec.rb
71
+ homepage: https://github.com/AgentAntelope/aspectual
64
72
  licenses:
65
73
  - MIT
66
74
  metadata: {}
@@ -84,4 +92,5 @@ rubygems_version: 2.0.6
84
92
  signing_key:
85
93
  specification_version: 4
86
94
  summary: A gem to help with AOP.
87
- test_files: []
95
+ test_files:
96
+ - spec/lib/aspectual_spec.rb