mzl 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/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format progress
3
+ --drb
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.3@mzl --create
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake'
4
+ gem 'rspec'
5
+ gem 'pry'
6
+ gem 'spork'
7
+ gem 'autotest-standalone'
8
+
9
+ # Specify your gem's dependencies in mzl.gemspec
10
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Kyle Brett
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,29 @@
1
+ # Mzl
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'mzl'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install mzl
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'bundler/setup'
4
+ require 'rspec/core/rake_task'
5
+ require 'pry'
6
+
7
+ desc 'run console'
8
+ task :console do
9
+ exec('bundle exec pry -I lib -r mzl')
10
+ end
11
+
12
+ desc 'run specs'
13
+ RSpec::Core::RakeTask.new
@@ -0,0 +1,23 @@
1
+ module Examples
2
+ class Calculate
3
+ def initialize
4
+ @total = 0
5
+ end
6
+
7
+ mzl.def :total, persist: true do
8
+ @total
9
+ end
10
+
11
+ mzl.def :add do |*numbers|
12
+ @total += numbers.inject(:+)
13
+ end
14
+
15
+ mzl.def :sub do |*numbers|
16
+ @total -= numbers.inject(:+)
17
+ end
18
+
19
+ mzl.def :exp do |exp|
20
+ @total = total ** exp
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ require 'examples/calculate'
2
+
3
+ module Examples
4
+ class Scopes
5
+ mzl.child :calculate, Calculate
6
+ end
7
+ end
8
+
9
+
data/lib/mzl.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'mzl/version'
2
+
3
+ module Mzl
4
+ autoload :Thing, 'mzl/thing'
5
+ autoload :DSLProxy, 'mzl/dsl_proxy'
6
+
7
+ module Class
8
+ def mzl(&block)
9
+ @mzl ||= Mzl::Thing.for(self)
10
+
11
+ block_given? ? @mzl.new(&block) : @mzl
12
+ end
13
+ end
14
+ end
15
+
16
+ Class.send(:include, Mzl::Class)
@@ -0,0 +1,56 @@
1
+ module Mzl
2
+ class DSLProxy
3
+ def initialize
4
+ @defs = {}
5
+ end
6
+
7
+ # define a method that will be available on objects created with
8
+ # the mzl object that created this object
9
+ def def(m, opts, &block)
10
+ raise ArgumentError if defs.include?(m)
11
+ @defs[m] = [block, opts]
12
+ end
13
+
14
+ # a list of our methods
15
+ def defs
16
+ @defs.keys
17
+ end
18
+
19
+ # leave this def on the instance? (so it is accessible without mzl)
20
+ def persist?(m)
21
+ @defs[m][1][:persist]
22
+ end
23
+
24
+ # the proc for a def is the first element of the array in @defs[m]
25
+ def proc_for(m)
26
+ @defs[m][0]
27
+ end
28
+
29
+ # define our DSL methods on the instance's metaclass
30
+ def insert_mzl(instance, filter = {})
31
+ @defs.each do |m, ary|
32
+ blk, opts = ary
33
+ next unless filter.empty? || opts == filter
34
+ instance.singleton_class.send(:define_method, m, &blk)
35
+ end
36
+ end
37
+
38
+ # remove all our methods
39
+ def extract_mzl(instance)
40
+ defs.each do |m|
41
+ instance.singleton_class.send(:remove_method, m) unless persist?(m)
42
+ end
43
+ end
44
+
45
+ # execute the block against the instance with our methods
46
+ # available. afterwards, remove the :persist => false ones
47
+ def exec_for(instance, &block)
48
+ insert_mzl(instance)
49
+ instance.instance_exec(&block)
50
+ extract_mzl(instance)
51
+
52
+ # return the instance
53
+ instance
54
+ end
55
+ end
56
+ end
data/lib/mzl/thing.rb ADDED
@@ -0,0 +1,110 @@
1
+ module Mzl
2
+ class Thing
3
+ attr_reader :subject, :defaults, :options
4
+
5
+ def self.for(klass)
6
+ @things_by_class ||= {}
7
+ @things_by_class[klass] ||= new(klass)
8
+ end
9
+
10
+ def initialize(subject)
11
+ raise ArgumentError unless subject.is_a?(Class)
12
+
13
+ # the class we will be instantiating
14
+ @subject = subject
15
+
16
+ # this object will hold our DSL methods so we don't make a mess
17
+ @dsl_proxy = DSLProxy.new
18
+
19
+ # default parameters for things
20
+ @defaults = Hash.new({})
21
+ end
22
+
23
+ # this is stupid and probably only here for that test I wrote earlier
24
+ def dsl_methods
25
+ @dsl_proxy.defs
26
+ end
27
+
28
+ def override_new(bool = true)
29
+ if bool
30
+ @subject.singleton_class.class_exec do
31
+ alias_method(:mzl_orig_new, :new) if method_defined?(:new)
32
+ def new(*args, &block)
33
+ mzl.new(*args, &block)
34
+ end
35
+ @__mzl_new_overridden = true
36
+ end
37
+ elsif @subject.singleton_class.instance_variable_get(:@__mzl_new_overridden)
38
+ @subject.singleton_class.class_exec do
39
+ @__mzl_new_overridden = false
40
+ remove_method(:new) # this is the shim new we defined above
41
+
42
+ if respond_to?(:mzl_orig_new)
43
+ alias_method :mzl_orig_new, :new
44
+ remove_method(:mzl_orig_new)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ # define a DSL method
51
+ def def(sym, opts = {}, &block)
52
+ raise ArgumentError unless block_given?
53
+ raise ArgumentError if @dsl_proxy.defs.include?(sym)
54
+ @dsl_proxy.def(sym, @defaults[:def].merge(opts), &block)
55
+ end
56
+
57
+ def child(sym, klass, opts = {persist: true})
58
+ self.def(sym) do |&block|
59
+ # be a attr_reader for a new instance of the child class
60
+ child = instance_variable_get(:"@#{sym}")
61
+ child = instance_variable_set(:"@#{sym}", klass.mzl.new) unless child
62
+
63
+ # mzl an optional block in the child
64
+ child.mzl(&block) if block.is_a?(Proc)
65
+
66
+ # and return it, of course
67
+ child
68
+ end
69
+
70
+ if opts[:persist]
71
+ @subject.send(:define_method, sym) do
72
+ if val = instance_variable_get(:"@#{sym}")
73
+ val
74
+ else
75
+ val = instance_variable_set(:"@#{sym}", klass.mzl.new)
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ # instance method not class method!
82
+ def new(&block)
83
+ # we will need ourselves later
84
+ _self = self
85
+
86
+ # create an instance of subject
87
+ instance = subject.respond_to?(:mzl_orig_new) ? subject.mzl_orig_new : subject.new
88
+ instance = block_given? ? exec(instance, &block) : instance
89
+
90
+ # Give the instance a mzl thing (_self)
91
+ instance.singleton_class.send(:define_method, :mzl) do |&blk|
92
+ _self.exec(self, &blk) if blk.is_a?(Proc)
93
+ _self
94
+ end
95
+
96
+ # put the permanent methods on (in case they never call mzl with a block)
97
+ @dsl_proxy.insert_mzl(instance, persist: true)
98
+
99
+ # and return it
100
+ instance
101
+ end
102
+
103
+ def exec(instance, &block)
104
+ return instance unless block_given?
105
+
106
+ # have the dsl proxy execute the block on behalf of that instance
107
+ @dsl_proxy.exec_for(instance, &block)
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,3 @@
1
+ module Mzl
2
+ VERSION = "0.0.1"
3
+ end
data/mzl.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/mzl/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Kyle Brett"]
6
+ gem.email = ["kyle@kylebrett.com"]
7
+ gem.description = %q{Ridiculous metaprogramming for almost no reason.}
8
+ gem.summary = %q{Metaprogramming library for DSLs}
9
+ gem.homepage = "http://github.com/dashkb/mzl"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "mzl"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Mzl::VERSION
17
+ end
data/spec/mzl_spec.rb ADDED
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+ require 'examples/calculate'
3
+
4
+ describe 'Class' do
5
+ let(:klass) do
6
+ klass = Class.new
7
+ klass.mzl.def :call_block do |*args, &block|
8
+ block.call
9
+ end
10
+
11
+ klass.mzl.def :properties, persist: true do
12
+ @properties ||= {}
13
+ end
14
+
15
+ klass.mzl.def :throw_the_instance do
16
+ throw :the_instance, self
17
+ end
18
+
19
+ klass
20
+ end
21
+
22
+ describe '.mzl' do
23
+ it 'responds with a Mzl::Thing' do
24
+ Class.mzl.should be_a(Mzl::Thing)
25
+ end
26
+
27
+ specify 'subject is the calling class' do
28
+ Class.mzl.subject.should == Class
29
+ klass.mzl.subject.should == klass
30
+ klass.mzl.new.mzl.subject.should == klass
31
+ end
32
+
33
+ specify 'with a block is the same as .mzl.new' do
34
+ instance = klass.mzl do
35
+ properties[:foo] = :bar
36
+ end
37
+
38
+ instance.instance_variable_get(:@properties)[:foo].should == :bar
39
+ end
40
+
41
+ describe '.def' do
42
+ it 'defines a DSL method for the subject' do
43
+ klass.mzl.dsl_methods.should == [:call_block, :properties, :throw_the_instance]
44
+ end
45
+
46
+ it 'can define methods which take blocks' do
47
+ catch(:block) do
48
+ klass.mzl.new do
49
+ call_block do
50
+ throw :block, self
51
+ end
52
+ end
53
+ end.should be_a(klass)
54
+ end
55
+
56
+ it 'executes the methods in the correct context' do
57
+ catch(:the_instance) do
58
+ klass.mzl.new do
59
+ throw_the_instance
60
+ end
61
+ end.should be_a(klass)
62
+ end
63
+
64
+ it 'takes option :persist => true to permanently define the method on the instance' do
65
+ klass.mzl do
66
+ properties[:foo] = 'whatever'
67
+ end.properties[:foo].should == 'whatever'
68
+ end
69
+
70
+ it 'by default defines methods only available during mzl' do
71
+ expect {
72
+ klass.mzl.new
73
+ }
74
+ end
75
+ end
76
+
77
+ describe '.new' do
78
+ it 'returns an instance of of the subject class' do
79
+ klass.mzl.new.should be_a(klass)
80
+ end
81
+
82
+ it 'sets self to the instance' do
83
+ catch(:instance) do
84
+ klass.mzl.new do
85
+ throw :instance, self
86
+ end.should be_a(klass)
87
+ end
88
+ end
89
+
90
+ it 'instance_execs a block against the instance with mzl methods available' do
91
+ instance_a = klass.mzl.new do
92
+ properties[:foo] = :bar
93
+ end
94
+
95
+ instance_b = klass.mzl.new do
96
+ properties[:foo] = :baz
97
+ end
98
+
99
+ [[instance_a, :bar], [instance_b, :baz]].each do |pair|
100
+ instance, val = pair
101
+ props = instance.instance_variable_get(:@properties)
102
+ props.should be_a(Hash)
103
+ props[:foo].should == val
104
+ end
105
+ end
106
+
107
+ it 'can override klass.new' do
108
+ klass.mzl.new.should respond_to(:properties)
109
+ klass.new.should_not respond_to(:properties)
110
+
111
+ klass.mzl.override_new
112
+ klass.new.should respond_to(:properties)
113
+ klass.mzl.override_new(false)
114
+ klass.new.should_not respond_to(:properties)
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ describe 'Mzl instances' do
121
+ let(:klass) { Examples::Calculate }
122
+
123
+ it 'should not respond to DSL methods' do
124
+ klass.mzl.new.should_not respond_to(:add)
125
+ end
126
+
127
+ it 'should respond to normal instance methods' do
128
+ klass.mzl.new.total.should == 0
129
+
130
+ catch(:total) do
131
+ klass.mzl.new do
132
+ throw(:total, total)
133
+ end
134
+ end.should == 0
135
+ end
136
+
137
+ it 'should put two and two together' do
138
+ klass.mzl.new do
139
+ add 2, 2
140
+ end.total.should == 4
141
+ end
142
+
143
+ it 'should have a mzl thing' do
144
+ instance = klass.mzl.new
145
+ instance.should respond_to(:mzl)
146
+ instance.mzl.should be_a(Mzl::Thing)
147
+ end
148
+
149
+ it 'can use the mzl thing to call dsl methods later' do
150
+ instance = klass.mzl.new
151
+ expect {
152
+ instance.mzl do
153
+ add 1, 1, 1, 1
154
+ end
155
+ }.to change { instance.total }.by(4)
156
+ end
157
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'examples/scopes'
3
+
4
+ describe Examples::Scopes do
5
+ subject { Examples::Scopes }
6
+ let(:instance) { subject.mzl.new }
7
+
8
+ it 'has a calculator child' do
9
+ instance.should respond_to(:calculate)
10
+ instance.calculate.should be_a(Examples::Calculate)
11
+
12
+ catch(:calculate) do
13
+ instance.mzl do
14
+ throw :calculate, calculate
15
+ end
16
+ end.should be_a(Examples::Calculate)
17
+ end
18
+
19
+ it 'can do math with the calculator child' do
20
+ expect {
21
+ instance.mzl do
22
+ calculate.mzl { add 1, 1 }
23
+ end
24
+ }.to change { instance.calculate.total }.by(2)
25
+ end
26
+
27
+ it 'can do math with the calculator child without calling mzl explicitly' do
28
+ expect {
29
+ instance.mzl { calculate { add 1, 4 } }
30
+ }.to change { instance.calculate.total }.by(5)
31
+
32
+ expect {
33
+ instance.calculate { add 1, 1 }
34
+ }.to_not raise_exception
35
+
36
+ expect {
37
+ instance.calculate.mzl { calculate { add 2, 3 } }
38
+ }
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ require 'spork'
3
+
4
+ #uncomment the following line to use spork with the debugger
5
+ #require 'spork/ext/ruby-debug'
6
+
7
+ Spork.prefork do
8
+ require 'pry'
9
+
10
+ RSpec.configure do |config|
11
+ config.treat_symbols_as_metadata_keys_with_true_values = true
12
+ config.run_all_when_everything_filtered = true
13
+ config.filter_run :focus
14
+
15
+ # Run specs in random order to surface order dependencies. If you find an
16
+ # order dependency and want to debug it, you can fix the order by providing
17
+ # the seed, which is printed after each run.
18
+ # --seed 1234
19
+ config.order = 'random'
20
+ end
21
+ end
22
+
23
+ Spork.each_run do
24
+ require 'mzl'
25
+ end
26
+
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mzl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kyle Brett
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-17 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Ridiculous metaprogramming for almost no reason.
15
+ email:
16
+ - kyle@kylebrett.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - .rspec
23
+ - .rvmrc
24
+ - Gemfile
25
+ - LICENSE
26
+ - README.md
27
+ - Rakefile
28
+ - lib/examples/calculate.rb
29
+ - lib/examples/scopes.rb
30
+ - lib/mzl.rb
31
+ - lib/mzl/dsl_proxy.rb
32
+ - lib/mzl/thing.rb
33
+ - lib/mzl/version.rb
34
+ - mzl.gemspec
35
+ - spec/mzl_spec.rb
36
+ - spec/scopes_spec.rb
37
+ - spec/spec_helper.rb
38
+ homepage: http://github.com/dashkb/mzl
39
+ licenses: []
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ segments:
51
+ - 0
52
+ hash: -1818933194322953242
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ segments:
60
+ - 0
61
+ hash: -1818933194322953242
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 1.8.24
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Metaprogramming library for DSLs
68
+ test_files:
69
+ - spec/mzl_spec.rb
70
+ - spec/scopes_spec.rb
71
+ - spec/spec_helper.rb