aop 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: 71f2faab1dfdd248cc8a9c2aea4c166106dbd1e3
4
- data.tar.gz: fe1c1dc3713fa1789c7ace4c12c5f55c8b51c428
3
+ metadata.gz: af9f44c8cac54ec4197e54a135d3c6829b8badd6
4
+ data.tar.gz: f9ffd78f10555e9ef4f09c2d14231d7248f4c82f
5
5
  SHA512:
6
- metadata.gz: f9fc488c1c3c6ca225f89f24f934c051141ddbfbcb5dd3eaac203a679f55a73967c019830a53651c77fa692fbe5e0b8527d597bcf6fceb4f51c766ce2968e534
7
- data.tar.gz: bcbb2ab9c96b2fde4cdf45727ba7f426c5a26963cef3fc6bd6804f593519688ce327a16bec838d6aa3b1a53c24c8c22d86c780c31866581097fc7f682df78cfc
6
+ metadata.gz: f24dd40907b9bd09ac43e491651cfb64cedaa4044363d99fba028e3b23c85afe2a5d1a12baa6d2c440a6e7dec0aec8f65413048753584183cb63efb8a8451b2a
7
+ data.tar.gz: af8f883d3f73072302b052afd3e69cca24d4d5d0517bb404cb30c68bc9f9be906e987aa3e1a596c9d3e625a96e028a28c5f7166708f405e66aa184b23282ca71
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1
7
+ - 2.2
8
+ - jruby-18mode
9
+ - jruby-19mode
10
+ - rbx
11
+
12
+ script: bundle exec rspec
13
+ bundler_args: --without development
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in aop.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'rspec'
8
+ end
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Aop
2
2
 
3
+ [![Build Status](https://travis-ci.org/waterlink/aop.svg?branch=master)](https://travis-ci.org/waterlink/aop)
4
+
3
5
  Very thin AOP gem for Ruby.
4
6
 
5
7
  Thin and fast framework for Aspect Oriented Programming in Ruby.
@@ -24,11 +26,9 @@ Or install it yourself as:
24
26
 
25
27
  ### Before advice
26
28
 
27
- *Not implemented*
28
-
29
29
  ```ruby
30
30
  module Authentication
31
- Aop["BankAccount#transfer:before"] do |account, *args, &blk|
31
+ Aop["BankAccount#transfer:before"].advice do |account, *args, &blk|
32
32
  can!(:transfer, account)
33
33
  end
34
34
 
@@ -40,11 +40,9 @@ end
40
40
 
41
41
  ### After advice
42
42
 
43
- *Not implemented*
44
-
45
43
  ```ruby
46
44
  module Analytics
47
- Aop["User#sign_in:after"] do |target, *args, &blk|
45
+ Aop["User#sign_in:after"].advice do |target, *args, &blk|
48
46
  report("sign_in", user.id)
49
47
  end
50
48
  end
@@ -56,7 +54,7 @@ end
56
54
 
57
55
  ```ruby
58
56
  module Transactional
59
- Aop["BankAccount#transfer:around"] do |joint_point, account, *args, &blk|
57
+ Aop["BankAccount#transfer:around"].advice do |joint_point, account, *args, &blk|
60
58
  start_transaction
61
59
  joint_point.call
62
60
  finish_transaction
@@ -66,7 +64,7 @@ end
66
64
 
67
65
  ## TODO (to specify)
68
66
 
69
- - multiple classes, methods and types of advices
67
+ - multiple classes, methods and types of advices at once
70
68
 
71
69
  ## Contributing
72
70
 
data/lib/aop.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require "aop/version"
2
+ require "aop/pointcut"
2
3
 
3
4
  module Aop
4
- # Your code goes here...
5
5
  end
@@ -0,0 +1,106 @@
1
+ require "securerandom"
2
+
3
+ module Aop
4
+ def self.[](pointcut_spec)
5
+ Pointcut.new(pointcut_spec)
6
+ end
7
+
8
+ class Pointcut
9
+ def initialize(spec)
10
+ @spec = spec
11
+
12
+ @class_spec = spec.scan(/^[^#\.]+/).first || ""
13
+ @class_names = @class_spec.split(",")
14
+ @classes = @class_names.map { |name| Object.const_get(name) }
15
+
16
+ @method_spec = spec.scan(/[#\.][^#\.:]+/)
17
+ @methods = @method_spec.map { |m| MethodReference.from(m) }
18
+
19
+ @advices = spec.scan(/[^:]:([^:]+)/).flatten
20
+ end
21
+
22
+ def advice(&advised)
23
+ advices = @advices
24
+ advices.each do |advice|
25
+ _advice(advice, advised)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def _advice(advice, advised)
32
+ return before_advice(advised) if advice == "before"
33
+ after_advice(advised)
34
+ end
35
+
36
+ def generic_advice(advised, &body)
37
+ methods = @methods
38
+ @classes.each do |klass|
39
+ klass.class_eval do
40
+ methods.each do |method_ref|
41
+ method_ref.decorate(klass, &body[method_ref])
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def before_advice(advised)
48
+ generic_advice(advised) do |method_ref|
49
+ lambda do |*args, &blk|
50
+ advised.call(self, *args, &blk)
51
+ method_ref.call(self, *args, &blk)
52
+ end
53
+ end
54
+ end
55
+
56
+ def after_advice(advised)
57
+ generic_advice(advised) do |method_ref|
58
+ lambda do |*args, &blk|
59
+ result = method_ref.call(self, *args, &blk)
60
+ advised.call(self, *args, &blk)
61
+ result
62
+ end
63
+ end
64
+ end
65
+
66
+ class MethodReference
67
+ def self.from(m)
68
+ return new(m) if m[0...1] == "#"
69
+ Singleton.new(m)
70
+ end
71
+
72
+ def initialize(m)
73
+ @name = m[1..-1]
74
+ end
75
+
76
+ def decorate(target, &with)
77
+ name = @name
78
+ new_name = alias_name
79
+ alias_target(target).class_eval do
80
+ alias_method(new_name, name)
81
+ define_method(name, &with)
82
+ end
83
+ end
84
+
85
+ def call(target, *args, &blk)
86
+ alias_target(target).send(alias_name, *args, &blk)
87
+ end
88
+
89
+ private
90
+
91
+ def alias_target(target)
92
+ target
93
+ end
94
+
95
+ def alias_name
96
+ @_alias_name ||= "__aop_#{SecureRandom.hex(10)}"
97
+ end
98
+
99
+ class Singleton < self
100
+ def alias_target(target)
101
+ class << target; self; end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -1,3 +1,3 @@
1
1
  module Aop
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,24 @@
1
+ RSpec.describe "After advice" do
2
+ include FixtureLoader
3
+
4
+ let(:spy) { double("Spy") }
5
+
6
+ before do
7
+ load_fixture("BankAccount", "bank_account")
8
+
9
+ Aop["BankAccount#transfer:after"].advice do |account, *args, &blk|
10
+ spy.after(account, *args, &blk)
11
+ end
12
+ end
13
+
14
+ it "fires after #transfer" do
15
+ account = BankAccount.new(spy)
16
+ other = BankAccount.new(spy)
17
+ amount = 55
18
+
19
+ expect(spy).to receive(:inside).with(other, amount).ordered.once
20
+ expect(spy).to receive(:after).with(account, other, amount).ordered.once
21
+
22
+ expect(account.transfer(other, amount)).to eq(:a_result)
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ RSpec.describe "Before advice" do
2
+ include FixtureLoader
3
+
4
+ let(:spy) { double("Spy") }
5
+
6
+ before do
7
+ load_fixture("BankAccount", "bank_account")
8
+
9
+ Aop["BankAccount#transfer:before"].advice do |account, *args, &blk|
10
+ spy.before(account, *args, &blk)
11
+ end
12
+ end
13
+
14
+ it "fires before #transfer" do
15
+ account = BankAccount.new(spy)
16
+ other = BankAccount.new(spy)
17
+ amount = 55
18
+
19
+ expect(spy).to receive(:before).with(account, other, amount).ordered.once
20
+ expect(spy).to receive(:inside).with(other, amount).ordered.once
21
+
22
+ expect(account.transfer(other, amount)).to eq(:a_result)
23
+ end
24
+ end
@@ -0,0 +1,6 @@
1
+ class BankAccount < Struct.new(:spy)
2
+ def transfer(to, amount)
3
+ spy.inside(to, amount)
4
+ :a_result
5
+ end
6
+ end
@@ -0,0 +1,29 @@
1
+ require "aop"
2
+ require "./spec/support/loader"
3
+
4
+ RSpec.configure do |config|
5
+ config.expect_with :rspec do |expectations|
6
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
7
+ end
8
+
9
+ config.mock_with :rspec do |mocks|
10
+ mocks.verify_partial_doubles = true
11
+ end
12
+
13
+ config.filter_run :focus
14
+ config.run_all_when_everything_filtered = true
15
+
16
+ config.disable_monkey_patching!
17
+
18
+ config.warnings = true
19
+
20
+ if config.files_to_run.one?
21
+ config.default_formatter = 'doc'
22
+ end
23
+
24
+ config.profile_examples = 10
25
+
26
+ config.order = :random
27
+
28
+ Kernel.srand config.seed
29
+ end
@@ -0,0 +1,23 @@
1
+ module FixtureLoader
2
+ def self.included(base)
3
+ base.after { reset_fixtures }
4
+ end
5
+
6
+ def load_fixture(name, filename)
7
+ return if loaded[name]
8
+ load "./spec/fixtures/#{filename}.rb"
9
+ loaded[name] = 1
10
+ end
11
+
12
+ def reset_fixtures
13
+ loaded.each do |name, _|
14
+ Object.send(:remove_const, name)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def loaded
21
+ @_loaded ||= {}
22
+ end
23
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aop
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 Fedorov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-22 00:00:00.000000000 Z
11
+ date: 2015-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -46,13 +46,21 @@ extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
48
  - ".gitignore"
49
+ - ".rspec"
50
+ - ".travis.yml"
49
51
  - Gemfile
50
52
  - LICENSE.txt
51
53
  - README.md
52
54
  - Rakefile
53
55
  - aop.gemspec
54
56
  - lib/aop.rb
57
+ - lib/aop/pointcut.rb
55
58
  - lib/aop/version.rb
59
+ - spec/aop/after_spec.rb
60
+ - spec/aop/before_spec.rb
61
+ - spec/fixtures/bank_account.rb
62
+ - spec/spec_helper.rb
63
+ - spec/support/loader.rb
56
64
  homepage: https://github.com/waterlink/aop
57
65
  licenses:
58
66
  - MIT
@@ -77,4 +85,9 @@ rubygems_version: 2.2.2
77
85
  signing_key:
78
86
  specification_version: 4
79
87
  summary: Very thin AOP gem for Ruby
80
- test_files: []
88
+ test_files:
89
+ - spec/aop/after_spec.rb
90
+ - spec/aop/before_spec.rb
91
+ - spec/fixtures/bank_account.rb
92
+ - spec/spec_helper.rb
93
+ - spec/support/loader.rb