mzl 0.0.1

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