much-mixin 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 28fac94ed0a9f228774a872d2a0300825f5eb92c0e7b382af265936f21abd30b
4
+ data.tar.gz: 42992da0c84c834ac2b864fb08a02fa9dd4c252d143512052cdc9cfbde46e8e4
5
+ SHA512:
6
+ metadata.gz: c2229eca4fda26cb23d960904055554436682e86a7bf2c760042727024f65d84691d721313a67c35226af2032034ae89065cd8a7105294644ddc19e0cc592f7e
7
+ data.tar.gz: c3acfab039cea2a7de5177b46fa4e47603626f0890927960dd59bbb4c38d1292b69188b2d783a52bd4f077a8c14c576e60fc159ae00602ee5c3108f11a6dbae8
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.log
3
+ *.rbc
4
+ .rbx/
5
+ .bundle
6
+ .config
7
+ .yardoc
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ ruby "~> 2.5"
6
+
7
+ gemspec
8
+
9
+ gem "pry"
10
+
11
+ # to release, uncomment this and run `bundle exec ggem r -f`
12
+ # gem "ggem"
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015-Present Kelly Redding and Collin Redding
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.
@@ -0,0 +1,131 @@
1
+ # MuchMixin
2
+
3
+ Enhanced mix-in API.
4
+
5
+ ## Usage
6
+
7
+ ```ruby
8
+ requre "much-mixin"
9
+
10
+ module MyMuchMixin
11
+ include MuchMixin
12
+
13
+ mixin_included do
14
+ # do some stuff ...
15
+ # - will be class eval'd in the scope of the receiver of `MyMuchMixin`
16
+ # - will only be executed once per receiver, no matter how many times
17
+ # `MyMuchMixin` is included in that receiver
18
+ end
19
+ end
20
+ ```
21
+
22
+ Mix `MuchMixin` in on other mixins that act as "plugins" to other components. Define included hooks using `mixin_included` that will be class eval'd in the scope of the receiver.
23
+
24
+ This allows you to define multiple hooks separately and ensures each hook will only be executed once - even if your plugin is mixed in multiple times on the same receiver.
25
+
26
+ ### `mixin_class_methods` / `mixin_instance_methods`
27
+
28
+ MuchMixin provides convenience methods for defining instance/class methods on plugin receivers:
29
+
30
+ ```ruby
31
+ requre "much-mixin"
32
+
33
+ module MyMuchMixin
34
+ include MuchMixin
35
+
36
+ mixin_class_methods do
37
+ # define some methods ...
38
+ # - these methods will become class methods on the receiver
39
+ end
40
+
41
+ mixin_instance_methods do
42
+ # define some methods ...
43
+ # - these methods will become instance methods on the receiver
44
+ end
45
+ end
46
+ ```
47
+
48
+ ### `after_mixin_included`
49
+
50
+ These hooks work just like the `mixin_included` hooks, except they are evaluated _after_ any plugin class/instance methods have been evaluated. E.g. use this to call a class method that the plugin defines.
51
+
52
+ ```ruby
53
+ requre "much-mixin"
54
+
55
+ module MyMuchMixin
56
+ include MuchMixin
57
+
58
+ after_mixin_included do
59
+ configure_the_plugin
60
+ end
61
+
62
+ mixin_class_methods do
63
+ def configure_the_plugin
64
+ # ...
65
+ end
66
+ end
67
+ end
68
+ ```
69
+
70
+ ## Example
71
+
72
+ ```ruby
73
+ requre "much-mixin"
74
+
75
+ module AnotherMixin
76
+ def another
77
+ "another"
78
+ end
79
+ end
80
+
81
+ module MyMuchMixin
82
+ include MuchMixin
83
+
84
+ mixin_included do
85
+ include AnotherMixin
86
+ end
87
+
88
+ mixin_class_methods do
89
+ def a_class_method
90
+ "a-class-method"
91
+ end
92
+ end
93
+
94
+ mixin_instance_methods do
95
+ def an_instance_method
96
+ "an-instance-method"
97
+ end
98
+ end
99
+ end
100
+
101
+ class MyClass
102
+ include MyMuchMixin
103
+ end
104
+
105
+ my_class = MyClass.new
106
+ my_class.another # => "another"
107
+ my_class.an_instance_method # => "an-instance-method"
108
+ MyClass.a_class_method # => "a-class-method"
109
+ ```
110
+
111
+ ## Installation
112
+
113
+ Add this line to your application's Gemfile:
114
+
115
+ gem "much-mixin"
116
+
117
+ And then execute:
118
+
119
+ $ bundle
120
+
121
+ Or install it yourself as:
122
+
123
+ $ gem install much-mixin
124
+
125
+ ## Contributing
126
+
127
+ 1. Fork it
128
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
129
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
130
+ 4. Push to the branch (`git push origin my-new-feature`)
131
+ 5. Create new Pull Request
@@ -0,0 +1,27 @@
1
+ require "much-mixin"
2
+ require "benchmark"
3
+
4
+ module Methods; end
5
+
6
+ module MyMixin
7
+ def self.included(receiver)
8
+ receiver.class_eval{ include Methods }
9
+ end
10
+ end
11
+
12
+ module MyMuchMixin
13
+ include MuchMixin
14
+
15
+ mixin_included do
16
+ include Methods
17
+ end
18
+ end
19
+
20
+ Benchmark.bmbm do |x|
21
+ x.report("MyMixin") do
22
+ 10_000.times{ Class.new{ include MyMixin } }
23
+ end
24
+ x.report("MyMuchMixin") do
25
+ 10_000.times{ Class.new{ include MyMuchMixin } }
26
+ end
27
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "much-mixin/version"
4
+
5
+ module MuchMixin
6
+ def self.included(receiver)
7
+ receiver.class_eval{ extend ClassMethods }
8
+ end
9
+
10
+ module ClassMethods
11
+ # install an included block that first checks if this plugin's receiver mixin
12
+ # has already been included. If it has not been, include the receiver mixin
13
+ # and run all of the `mixin_included` blocks
14
+ def included(plugin_receiver)
15
+ return if plugin_receiver.include?(self.much_mixin_included_detector)
16
+ plugin_receiver.send(:include, self.much_mixin_included_detector)
17
+
18
+ self.much_mixin_included_blocks.each do |block|
19
+ plugin_receiver.class_eval(&block)
20
+ end
21
+
22
+ self.much_plugin_class_method_blocks.each do |block|
23
+ self.much_mixin_class_methods_module.class_eval(&block)
24
+ end
25
+ plugin_receiver.send(:extend, self.much_mixin_class_methods_module)
26
+
27
+ self.much_plugin_instance_method_blocks.each do |block|
28
+ self.much_mixin_instance_methods_module.class_eval(&block)
29
+ end
30
+ plugin_receiver.send(:include, self.much_mixin_instance_methods_module)
31
+
32
+ self.much_plugin_after_included_blocks.each do |block|
33
+ plugin_receiver.class_eval(&block)
34
+ end
35
+ end
36
+
37
+ # the included detector is an empty module that is only used to detect if
38
+ # the plugin has been included or not, it doesn't add any behavior or
39
+ # methods to the object receiving the plugin; we use `const_set` to name the
40
+ # module so if its seen in the ancestors it doesn't look like some random
41
+ # module and it can be tracked back to much-mixin
42
+ def much_mixin_included_detector
43
+ @much_mixin_included_detector ||= Module.new.tap do |m|
44
+ self.const_set("MuchMixinIncludedDetector", m)
45
+ end
46
+ end
47
+
48
+ def much_mixin_class_methods_module
49
+ @much_mixin_class_methods_module ||= Module.new.tap do |m|
50
+ self.const_set("MuchMixinClassMethods", m)
51
+ end
52
+ end
53
+
54
+ def much_mixin_instance_methods_module
55
+ @much_mixin_instance_methods_module ||= Module.new.tap do |m|
56
+ self.const_set("MuchMixinInstanceMethods", m)
57
+ end
58
+ end
59
+
60
+ def much_mixin_included_blocks
61
+ @much_mixin_included_blocks ||= []
62
+ end
63
+
64
+ def much_plugin_after_included_blocks
65
+ @much_plugin_after_included_blocks ||= []
66
+ end
67
+
68
+ def much_plugin_class_method_blocks
69
+ @much_plugin_class_method_blocks ||= []
70
+ end
71
+
72
+ def much_plugin_instance_method_blocks
73
+ @much_plugin_instance_method_blocks ||= []
74
+ end
75
+
76
+ def mixin_included(&block)
77
+ self.much_mixin_included_blocks << block
78
+ end
79
+ alias_method :plugin_included, :mixin_included
80
+
81
+ def after_mixin_included(&block)
82
+ self.much_plugin_after_included_blocks << block
83
+ end
84
+ alias_method :after_plugin_included, :after_mixin_included
85
+
86
+ def mixin_class_methods(&block)
87
+ self.much_plugin_class_method_blocks << block
88
+ end
89
+ alias_method :plugin_class_methods, :mixin_class_methods
90
+
91
+ def mixin_instance_methods(&block)
92
+ self.much_plugin_instance_method_blocks << block
93
+ end
94
+ alias_method :plugin_instance_methods, :mixin_instance_methods
95
+ end
96
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MuchMixin
4
+ VERSION = "0.0.1"
5
+ end
@@ -0,0 +1,3 @@
1
+ require "much-mixin"
2
+
3
+ MuchPlugin = MuchMixin
File without changes
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "much-mixin/version"
6
+
7
+ Gem::Specification.new do |gem|
8
+ gem.name = "much-mixin"
9
+ gem.version = MuchMixin::VERSION
10
+ gem.authors = ["Kelly Redding", "Collin Redding"]
11
+ gem.email = ["kelly@kellyredding.com", "collin.redding@me.com"]
12
+ gem.summary = %q{Enhanced mix-in API.}
13
+ gem.description = %q{Enhanced mix-in API.}
14
+ gem.homepage = "http://github.com/redding/much-mixin"
15
+ gem.license = "MIT"
16
+
17
+ gem.files = `git ls-files`.split($/)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.require_paths = ["lib"]
21
+
22
+ gem.required_ruby_version = "~> 2.5"
23
+
24
+ gem.add_development_dependency("assert", ["~> 2.19.2"])
25
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # this file is automatically required when you run `assert`
4
+ # put any test helpers here
5
+
6
+ # add the root dir to the load path
7
+ $LOAD_PATH.unshift(File.expand_path("../..", __FILE__))
8
+
9
+ # require pry for debugging (`binding.pry`)
10
+ require "pry"
11
+
12
+ require "test/support/factory"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert/factory"
4
+
5
+ module Factory
6
+ extend Assert::Factory
7
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-mixin"
5
+
6
+ module MuchMixin
7
+ class SystemTests < Assert::Context
8
+ desc "MuchMixin"
9
+ setup do
10
+ @my_class = MyClass.new
11
+ end
12
+ subject{ @my_class }
13
+
14
+ should "class eval the plugin included block on MyClass" do
15
+ assert_equal "another", subject.another
16
+ end
17
+
18
+ should "add the plugin class methods to MyClass" do
19
+ assert_equal "a-class-method", MyClass.a_class_method
20
+ end
21
+
22
+ should "add the plugin instance methods to MyClass" do
23
+ assert_equal "an-instance-method", subject.an_instance_method
24
+ end
25
+
26
+ module AnotherMixin
27
+ def another
28
+ "another"
29
+ end
30
+ end
31
+
32
+ module MyMuchMixin
33
+ include MuchMixin
34
+
35
+ mixin_included do
36
+ include AnotherMixin
37
+ end
38
+
39
+ mixin_class_methods do
40
+ def a_class_method
41
+ "a-class-method"
42
+ end
43
+ end
44
+
45
+ mixin_instance_methods do
46
+ def an_instance_method
47
+ "an-instance-method"
48
+ end
49
+ end
50
+ end
51
+
52
+ class MyClass
53
+ include MyMuchMixin
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-plugin"
5
+
6
+ module MuchMixin
7
+ class SystemTests < Assert::Context
8
+ desc "MuchMixin"
9
+ setup do
10
+ @my_class = MyClass.new
11
+ end
12
+ subject{ @my_class }
13
+
14
+ should "class eval the plugin included block on MyClass" do
15
+ assert_equal "another", subject.another
16
+ end
17
+
18
+ should "add the plugin class methods to MyClass" do
19
+ assert_equal "a-class-method", MyClass.a_class_method
20
+ end
21
+
22
+ should "add the plugin instance methods to MyClass" do
23
+ assert_equal "an-instance-method", subject.an_instance_method
24
+ end
25
+
26
+ module AnotherMixin
27
+ def another
28
+ "another"
29
+ end
30
+ end
31
+
32
+ module MyMuchMixin
33
+ include MuchMixin
34
+
35
+ mixin_included do
36
+ include AnotherMixin
37
+ end
38
+
39
+ mixin_class_methods do
40
+ def a_class_method
41
+ "a-class-method"
42
+ end
43
+ end
44
+
45
+ mixin_instance_methods do
46
+ def an_instance_method
47
+ "an-instance-method"
48
+ end
49
+ end
50
+ end
51
+
52
+ class MyClass
53
+ include MyMuchMixin
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-mixin"
5
+
6
+ module MuchMixin
7
+ class UnitTests < Assert::Context
8
+ desc "MuchMixin"
9
+ setup do
10
+ @block1 = proc{ 1 }
11
+ @block2 = proc{ 2 }
12
+
13
+ @muchmixin = Module.new{ include MuchMixin }
14
+ end
15
+ subject{ @muchmixin }
16
+
17
+ should have_imeths :much_mixin_included_detector, :much_mixin_included_blocks
18
+ should have_imeths :mixin_included, :after_mixin_included
19
+
20
+ should "know its included detector" do
21
+ mixin = subject.much_mixin_included_detector
22
+ assert_instance_of Module, mixin
23
+ assert_same mixin, subject.much_mixin_included_detector
24
+ exp = subject::MuchMixinIncludedDetector
25
+ assert_same exp, subject.much_mixin_included_detector
26
+ end
27
+
28
+ should "have no plugin included blocks by default" do
29
+ assert_empty subject.much_mixin_included_blocks
30
+ assert_empty subject.much_plugin_after_included_blocks
31
+ end
32
+
33
+ should "append blocks" do
34
+ subject.mixin_included(&@block1)
35
+ subject.mixin_included(&@block2)
36
+
37
+ assert_equal @block1, subject.much_mixin_included_blocks.first
38
+ assert_equal @block2, subject.much_mixin_included_blocks.last
39
+ end
40
+ end
41
+
42
+ class MixedInTests < UnitTests
43
+ desc "when mixed in"
44
+ setup do
45
+ @receiver = Class.new do
46
+ def self.inc_block1; @block1_count ||= 0; @block1_count += 1; end
47
+ def self.block1_count; @block1_count ||= 0; end
48
+ def self.inc_block2; @block2_count ||= 0; @block2_count += 1; end
49
+ def self.block2_count; @block2_count ||= 0; end
50
+
51
+ def self.do_something_count; @do_something_count ||= 0; end
52
+ end
53
+ end
54
+
55
+ should "call the plugin included blocks" do
56
+ assert_equal 0, @receiver.block1_count
57
+ assert_equal 0, @receiver.block2_count
58
+ assert_equal 0, @receiver.do_something_count
59
+
60
+ @receiver.send(:include, TestMuchMixin)
61
+
62
+ assert_equal 1, @receiver.block1_count
63
+ assert_equal 1, @receiver.block2_count
64
+ assert_equal 1, @receiver.do_something_count
65
+ end
66
+
67
+ should "call blocks only once no matter even if previously mixed in" do
68
+ @receiver.send(:include, TestMuchMixin)
69
+
70
+ assert_equal 1, @receiver.block1_count
71
+ assert_equal 1, @receiver.block2_count
72
+ assert_equal 1, @receiver.do_something_count
73
+
74
+ @receiver.send(:include, TestMuchMixin)
75
+
76
+ assert_equal 1, @receiver.block1_count
77
+ assert_equal 1, @receiver.block2_count
78
+ assert_equal 1, @receiver.do_something_count
79
+ end
80
+
81
+ should "call blocks only once even if mixed in by a 3rd party" do
82
+ third_party = Module.new do
83
+ def self.included(receiver)
84
+ receiver.send(:include, TestMuchMixin)
85
+ end
86
+ end
87
+ @receiver.send(:include, third_party)
88
+
89
+ assert_equal 1, @receiver.block1_count
90
+ assert_equal 1, @receiver.block2_count
91
+ assert_equal 1, @receiver.do_something_count
92
+
93
+ @receiver.send(:include, TestMuchMixin)
94
+
95
+ assert_equal 1, @receiver.block1_count
96
+ assert_equal 1, @receiver.block2_count
97
+ assert_equal 1, @receiver.do_something_count
98
+
99
+ @receiver.send(:include, third_party)
100
+
101
+ assert_equal 1, @receiver.block1_count
102
+ assert_equal 1, @receiver.block2_count
103
+ assert_equal 1, @receiver.do_something_count
104
+ end
105
+ end
106
+
107
+ TestMuchMixin =
108
+ Module.new do
109
+ include MuchMixin
110
+
111
+ mixin_included{ inc_block1 }
112
+ after_mixin_included{
113
+ inc_block2
114
+ do_something
115
+ }
116
+
117
+ mixin_class_methods do
118
+ def do_something
119
+ @do_something_count ||= 0
120
+ @do_something_count += 1
121
+ end
122
+ end
123
+ end
124
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: much-mixin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kelly Redding
8
+ - Collin Redding
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2021-01-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: assert
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 2.19.2
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 2.19.2
28
+ description: Enhanced mix-in API.
29
+ email:
30
+ - kelly@kellyredding.com
31
+ - collin.redding@me.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".gitignore"
37
+ - Gemfile
38
+ - LICENSE
39
+ - README.md
40
+ - bench/script.rb
41
+ - lib/much-mixin.rb
42
+ - lib/much-mixin/version.rb
43
+ - lib/much-plugin.rb
44
+ - log/.gitkeep
45
+ - much-mixin.gemspec
46
+ - test/helper.rb
47
+ - test/support/factory.rb
48
+ - test/system/much-mixin_tests.rb
49
+ - test/system/much-plugin_tests.rb
50
+ - test/unit/much-mixin_tests.rb
51
+ - tmp/.gitkeep
52
+ homepage: http://github.com/redding/much-mixin
53
+ licenses:
54
+ - MIT
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - "~>"
63
+ - !ruby/object:Gem::Version
64
+ version: '2.5'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubygems_version: 3.1.2
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: Enhanced mix-in API.
75
+ test_files:
76
+ - test/helper.rb
77
+ - test/support/factory.rb
78
+ - test/system/much-mixin_tests.rb
79
+ - test/system/much-plugin_tests.rb
80
+ - test/unit/much-mixin_tests.rb