schmetterling 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Michał Łomnicki
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,48 @@
1
+ # Schmetterling
2
+
3
+ Simple aspects implemented on Module#prepend.
4
+
5
+ ## Requirements
6
+
7
+ Ruby 2.0
8
+
9
+ ## Highlights
10
+
11
+ * Support before/after/around advices
12
+ * Separate cross-cutting concerns
13
+
14
+ ## How it works
15
+
16
+ Every time you add an advice a new anonymous module is created
17
+ and prepended to the adviced class.
18
+
19
+ The similar effect can be achived with alias method chaining.
20
+
21
+ ## Usage
22
+
23
+ class User
24
+ def recommend_book(book)
25
+ @recommended_books << book
26
+ end
27
+ end
28
+
29
+ class Twitter
30
+ def publish(token, message)
31
+ # ...
32
+ end
33
+ end
34
+
35
+ require 'schmetterling/dsl'
36
+ class AppConfiguration
37
+ include Schmetterling::DSL
38
+
39
+ def initialize(twitter)
40
+ @twitter = twitter
41
+ end
42
+
43
+ def enable
44
+ After(User, :recommend_book) do |user, book|
45
+ twitter.publish(user.twitter_token, "Hey, I just read #{book.title}")
46
+ end
47
+ end
48
+ end
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "lib"
7
+ t.libs << "test"
8
+ t.test_files = FileList["test/*_test.rb"]
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,7 @@
1
+ require "schemetterling/version"
2
+ require "schemetterling/before"
3
+ require "schemetterling/after"
4
+ require "schemetterling/around"
5
+
6
+ module Schmetterling
7
+ end
@@ -0,0 +1,19 @@
1
+ module Schmetterling
2
+ class After
3
+
4
+ def initialize(klass, method, advice=nil, &block)
5
+ advice ||= block
6
+ klass.send(:prepend, build_module_for_advice(method, advice))
7
+ end
8
+
9
+ def build_module_for_advice(adviced_method, advice)
10
+ Module.new do
11
+ define_method adviced_method do |*args|
12
+ super(*args)
13
+ advice.call(self, *args)
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module Schmetterling
2
+ class Around
3
+
4
+ def initialize(klass, method, advice=nil, &block)
5
+ advice ||= block
6
+ klass.send(:prepend, build_module_for_advice(method, advice))
7
+ end
8
+
9
+ def build_module_for_advice(adviced_method, advice)
10
+ Module.new do
11
+ define_method adviced_method do |*args|
12
+ original_method = ->(args_for_original=args) { super(args_for_original) }
13
+ advice.call(original_method, self, *args)
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module Schmetterling
2
+ class Before
3
+
4
+ def initialize(klass, method, advice=nil, &block)
5
+ advice ||= block
6
+ klass.send(:prepend, build_module_for_advice(method, advice))
7
+ end
8
+
9
+ def build_module_for_advice(adviced_method, advice)
10
+ Module.new do
11
+ define_method adviced_method do |*args|
12
+ advice.call(self, *args)
13
+ super(*args)
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ require 'schmetterling/before'
2
+ require 'schmetterling/after'
3
+ require 'schmetterling/around'
4
+
5
+ module Schmetterling
6
+ module DSL
7
+
8
+ def Before(*args, &block)
9
+ Schmetterling::Before.new(*args, &block)
10
+ end
11
+
12
+ def After(*args, &block)
13
+ Schmetterling::After.new(*args, &block)
14
+ end
15
+
16
+ def Around(*args, &block)
17
+ Schmetterling::Around.new(*args, &block)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Schmetterling
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'schmetterling/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "schmetterling"
8
+ spec.version = Schmetterling::VERSION
9
+ spec.authors = ["Michał Łomnicki"]
10
+ spec.email = ["michal.lomnicki@gmail.com"]
11
+ spec.description = %q{Aspects implemented on Ruby2.0 Class#prepend}
12
+ spec.summary = %q{Aspects implemented on Ruby2.0 Class#prepend}
13
+ spec.homepage = "https://github.com/x8/schmetterling"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "mocha", "~> 0.13"
24
+ end
@@ -0,0 +1,53 @@
1
+ require 'test_helper'
2
+ require 'schmetterling/after'
3
+
4
+ class AfterTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @adviced_class = Class.new do
8
+ def bar(calls)
9
+ calls << :method_called
10
+ end
11
+ end
12
+ @calls = []
13
+ end
14
+
15
+ def test_executes_the_advice_after_adviced_method
16
+ Schmetterling::After.new(@adviced_class, :bar, -> (object, calls) { calls << :after_called })
17
+ @adviced_class.new.bar(@calls)
18
+ assert_equal [:method_called, :after_called], @calls
19
+ end
20
+
21
+ def test_executes_multiple_advices
22
+ Schmetterling::After.new(@adviced_class, :bar, -> (object, calls) { calls << :after1_called })
23
+ Schmetterling::After.new(@adviced_class, :bar, -> (object, calls) { calls << :after2_called })
24
+ @adviced_class.new.bar(@calls)
25
+ assert_equal [:method_called, :after1_called, :after2_called], @calls
26
+ end
27
+
28
+ def test_works_on_child_classes
29
+ @child_class = Class.new(@adviced_class)
30
+ Schmetterling::After.new(@adviced_class, :bar, -> (object, calls) { calls << :after_called })
31
+ @child_class.new.bar(@calls)
32
+ assert_equal [:method_called, :after_called], @calls
33
+ end
34
+
35
+ def test_works_with_blocks
36
+ Schmetterling::After.new(@adviced_class, :bar) do |object, calls|
37
+ calls << :after_called
38
+ end
39
+ @adviced_class.new.bar(@calls)
40
+ assert_equal [:method_called, :after_called], @calls
41
+ end
42
+
43
+ def test_advice_receives_the_adviced_object
44
+ @adviced_object = @adviced_class.new
45
+ Schmetterling::After.new(@adviced_class, :bar) do |object, calls|
46
+ assert_equal @adviced_object, object
47
+ calls << :after_called
48
+ end
49
+ @adviced_object.bar(@calls)
50
+ assert_equal [:method_called, :after_called], @calls
51
+ end
52
+
53
+ end
@@ -0,0 +1,68 @@
1
+ require 'test_helper'
2
+ require 'schmetterling/around'
3
+
4
+ class AroundTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @calls = []
8
+ @adviced_class = Class.new do
9
+ def bar(calls)
10
+ calls << :method_called
11
+ end
12
+ end
13
+ end
14
+
15
+ def test_executes_the_advice_around_the_adviced_method
16
+ advice = -> (original_method, object, calls) {
17
+ calls << :before_called
18
+ original_method.call(calls)
19
+ calls << :after_called
20
+ }
21
+ Schmetterling::Around.new(@adviced_class, :bar, advice)
22
+ @adviced_class.new.bar(@calls)
23
+ assert_equal [:before_called, :method_called, :after_called], @calls
24
+ end
25
+
26
+ def test_works_with_blocks
27
+ Schmetterling::Around.new(@adviced_class, :bar) do |original_method, object, calls|
28
+ calls << :before_called
29
+ original_method.call(calls)
30
+ calls << :after_called
31
+ end
32
+ @adviced_class.new.bar(@calls)
33
+ assert_equal [:before_called, :method_called, :after_called], @calls
34
+ end
35
+
36
+ def test_changing_arguments
37
+ @changed_argument = []
38
+ advice = -> (original_method, object, calls) {
39
+ calls << :before_called
40
+ original_method.call(@changed_argument)
41
+ calls << :after_called
42
+ }
43
+ Schmetterling::Around.new(@adviced_class, :bar, advice)
44
+ @adviced_class.new.bar(@calls)
45
+ assert_equal [:before_called, :after_called], @calls
46
+ assert_equal [:method_called], @changed_argument
47
+ end
48
+
49
+ def test_skipping_original_method
50
+ advice = -> (original_method, object, calls) {
51
+ calls << :evil_called
52
+ }
53
+ Schmetterling::Around.new(@adviced_class, :bar, advice)
54
+ @adviced_class.new.bar(@calls)
55
+ assert_equal [:evil_called], @calls
56
+ end
57
+
58
+ def test_advice_receives_the_adviced_object
59
+ @adviced_object = @adviced_class.new
60
+ Schmetterling::Around.new(@adviced_class, :bar) do |original_method, object, calls|
61
+ assert_equal @adviced_object, object
62
+ original_method.call(calls)
63
+ end
64
+ @adviced_object.bar(@calls)
65
+ assert_equal [:method_called], @calls
66
+ end
67
+
68
+ end
@@ -0,0 +1,63 @@
1
+ require 'test_helper'
2
+ require 'schmetterling/before'
3
+ require 'schmetterling/after'
4
+
5
+ class BeforeTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @calls = []
9
+ @adviced_class = Class.new do
10
+ def bar(calls)
11
+ calls << :method_called
12
+ end
13
+ end
14
+ end
15
+
16
+ def test_executes_the_advice_before_adviced_method
17
+ Schmetterling::Before.new(@adviced_class, :bar, -> (object, calls) { calls << :before_called })
18
+ @adviced_class.new.bar(@calls)
19
+ assert_equal [:before_called, :method_called], @calls
20
+ end
21
+
22
+ def test_executes_multiple_advices
23
+ Schmetterling::Before.new(@adviced_class, :bar, -> (object, calls) { calls << :before1_called })
24
+ Schmetterling::Before.new(@adviced_class, :bar, -> (object, calls) { calls << :before2_called })
25
+ @adviced_class.new.bar(@calls)
26
+ assert_equal [:before2_called, :before1_called, :method_called], @calls
27
+ end
28
+
29
+ def test_works_on_child_classes
30
+ @child_class = Class.new(@adviced_class)
31
+ Schmetterling::Before.new(@adviced_class, :bar, -> (object, calls) { calls << :before_called })
32
+ @child_class.new.bar(@calls)
33
+ assert_equal [:before_called, :method_called], @calls
34
+ end
35
+
36
+ def test_works_with_blocks
37
+ Schmetterling::After.new(@adviced_class, :bar) do |object, calls|
38
+ calls << :after_called
39
+ end
40
+ @adviced_class.new.bar(@calls)
41
+ assert_equal [:method_called, :after_called], @calls
42
+ end
43
+
44
+ def test_is_always_called_before_the_after_aspects
45
+ Schmetterling::After.new(@adviced_class, :bar, -> (_, calls) { calls << :after1_called })
46
+ Schmetterling::Before.new(@adviced_class, :bar, -> (_, calls) { calls << :before1_called })
47
+ Schmetterling::After.new(@adviced_class, :bar, -> (_, calls) { calls << :after2_called })
48
+ Schmetterling::Before.new(@adviced_class, :bar, -> (_, calls) { calls << :before2_called })
49
+ @adviced_class.new.bar(@calls)
50
+ assert_equal [:before2_called, :before1_called, :method_called, :after1_called, :after2_called], @calls
51
+ end
52
+
53
+ def test_advice_receives_the_adviced_object
54
+ @adviced_object = @adviced_class.new
55
+ Schmetterling::Before.new(@adviced_class, :bar) do |object, calls|
56
+ assert_equal @adviced_object, object
57
+ calls << :before_called
58
+ end
59
+ @adviced_object.bar(@calls)
60
+ assert_equal [:before_called, :method_called], @calls
61
+ end
62
+
63
+ end
data/test/dsl_test.rb ADDED
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+ require 'schmetterling/dsl'
3
+
4
+ class DslTest < Test::Unit::TestCase
5
+
6
+ class User
7
+ def foo; end
8
+ end
9
+
10
+ class Configuration
11
+ include Schmetterling::DSL
12
+
13
+ def initialize(twitter)
14
+ @twitter = twitter
15
+ end
16
+
17
+ def enable
18
+ After(User, :foo) do |user|
19
+ @twitter.publish(user, "some text")
20
+ end
21
+ end
22
+ end
23
+
24
+ def setup
25
+ @twitter = mock("Twitter")
26
+ @configuration = Configuration.new(@twitter)
27
+ @configuration.enable
28
+ end
29
+
30
+ def test_invokes_the_advice
31
+ @user = User.new
32
+ @twitter.expects(:publish).with(@user, "some text")
33
+ @user.foo
34
+ end
35
+
36
+ end
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
2
+ require 'test/unit'
3
+ require 'mocha/setup'
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: schmetterling
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Michał Łomnicki
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ type: :development
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ version: '1.3'
29
+ name: bundler
30
+ - !ruby/object:Gem::Dependency
31
+ type: :development
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ name: rake
46
+ - !ruby/object:Gem::Dependency
47
+ type: :development
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '0.13'
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ version: '0.13'
61
+ name: mocha
62
+ description: Aspects implemented on Ruby2.0 Class#prepend
63
+ email:
64
+ - michal.lomnicki@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - LICENSE.txt
72
+ - README.md
73
+ - Rakefile
74
+ - lib/schmetterling.rb
75
+ - lib/schmetterling/after.rb
76
+ - lib/schmetterling/around.rb
77
+ - lib/schmetterling/before.rb
78
+ - lib/schmetterling/dsl.rb
79
+ - lib/schmetterling/version.rb
80
+ - schmetterling.gemspec
81
+ - test/after_test.rb
82
+ - test/around_test.rb
83
+ - test/before_test.rb
84
+ - test/dsl_test.rb
85
+ - test/test_helper.rb
86
+ homepage: https://github.com/x8/schmetterling
87
+ licenses:
88
+ - MIT
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 1.8.24
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: Aspects implemented on Ruby2.0 Class#prepend
111
+ test_files:
112
+ - test/after_test.rb
113
+ - test/around_test.rb
114
+ - test/before_test.rb
115
+ - test/dsl_test.rb
116
+ - test/test_helper.rb