aspectual 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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