iron-extensions 1.1.5 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/History.txt +7 -0
- data/README.rdoc +11 -50
- data/Version.txt +1 -1
- data/lib/iron/extensions/array.rb +0 -12
- data/lib/iron/extensions/enumerable.rb +4 -0
- data/lib/iron/extensions/fixnum.rb +0 -4
- data/lib/iron/extensions/object.rb +4 -0
- data/lib/iron/extensions/string.rb +1 -1
- data/lib/iron/extensions/symbol.rb +0 -5
- data/spec/extensions/enumerable_spec.rb +7 -0
- data/spec/extensions/object_spec.rb +4 -0
- data/spec/extensions/string_spec.rb +6 -0
- metadata +15 -24
- data/lib/iron/extensions/class.rb +0 -46
- data/lib/iron/extensions/dsl_builder.rb +0 -14
- data/lib/iron/extensions/dsl_proxy.rb +0 -176
- data/spec/extensions/dsl_builder_spec.rb +0 -29
- data/spec/extensions/dsl_proxy_spec.rb +0 -110
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 25e58b7dab82ea9ffef58c5d04ad58ba2d9777c9
|
4
|
+
data.tar.gz: 915e17967decb24e1526328b927f1e562357a01b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 63b0cbb8e2702a7645818e80e26557221b919998152f2dd0a9724672b1fa94f3d8839f64e932b31d1784d162d88e3fa379dcb7648979930339c2d150b035ab6e
|
7
|
+
data.tar.gz: 916089267edf2c7735e357f2a9a0535202f6f50f1e4e0a7a2fcc6f1fc2c3f40cf4d1b5128de8e4d0909228ff3facbbe5fb87b7be19a859f1ac5ad3c4d20db004
|
data/History.txt
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== 1.2.0 / 2015-01-27
|
2
|
+
|
3
|
+
* BREAKING CHANGE: removed DslProxy/DslBuilder and the dsl_* accessor helpers and moved them into the iron-dsl gem
|
4
|
+
* BREAKING CHANGE: removed deprecated Array#rand and Array#rand! - use #shuffle instead
|
5
|
+
* Improved blank? to work on strings containing only whitespace
|
6
|
+
* Improved documentation & specs a bit
|
7
|
+
|
1
8
|
== 1.1.5 / 2014-03-31
|
2
9
|
|
3
10
|
* Fix bug in Enumerable#convert_to_hash where default values (eg hashes) weren't being duplicated
|
data/README.rdoc
CHANGED
@@ -4,7 +4,9 @@ Written by Rob Morris @ Irongaze Consulting LLC (http://irongaze.com)
|
|
4
4
|
|
5
5
|
== DESCRIPTION
|
6
6
|
|
7
|
-
Helpful extensions to core Ruby classes, plus a little sugar for common patterns
|
7
|
+
Helpful extensions to core Ruby classes, plus a little sugar for common patterns. For users of
|
8
|
+
version 1.1.x and lower, please note that as of version 1.2.0 the DSL-specific extensions
|
9
|
+
have been moved to the iron-dsl gem!
|
8
10
|
|
9
11
|
== ADDED EXTENSIONS
|
10
12
|
|
@@ -12,48 +14,23 @@ Helpful extensions to core Ruby classes, plus a little sugar for common patterns
|
|
12
14
|
|
13
15
|
[1, 2, nil, '', 'count'].list_join # => '1, 2, count'
|
14
16
|
|
15
|
-
*
|
17
|
+
* Enumerable#convert_to_hash - convert an array or other enumerable to a hash using a block or constant
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
builder = MyBuilder.new
|
21
|
-
builder.name 'ProjectX'
|
22
|
-
builder.name # => 'ProjectX'
|
23
|
-
DslProxy.exec(builder) do
|
24
|
-
name 'Project Omega'
|
25
|
-
end
|
26
|
-
builder.name # => 'Project Omega'
|
27
|
-
|
28
|
-
* Class#dsl_flag - same as above, but designed for boolean values
|
29
|
-
|
30
|
-
class AnotherBuilder
|
31
|
-
dsl_flag :awesome
|
32
|
-
end
|
33
|
-
|
34
|
-
builder = AnotherBuilder
|
35
|
-
builder.awesome? # => false on uninitialized value
|
36
|
-
builder.awesome! # => sets @awesome to true
|
37
|
-
builder.awesome true # ditto, same style as dsl_accessor above
|
38
|
-
|
39
|
-
* Enumerable#to_hash - convert an array or other enumerable to a hash using a block or constant
|
40
|
-
|
41
|
-
[:frog, :pig].to_hash {|n| n.to_s.capitalize} # => {:frog => 'Frog', :pig => 'Pig'}
|
42
|
-
[:frog, :pig].to_hash(nil) # => {:frog => nil, :pig => nil}
|
19
|
+
[:frog, :pig].convert_to_hash {|n| n.to_s.capitalize} # => {:frog => 'Frog', :pig => 'Pig'}
|
20
|
+
[:frog, :pig].convert_to_hash(nil) # => {:frog => nil, :pig => nil}
|
43
21
|
|
44
22
|
* Enumerable#delete_unless - equivalent to delete_if but with inverted test
|
45
23
|
|
46
24
|
[1,2,3,4,5].delete_unless(&:odd?) # => [1,3,5]
|
47
25
|
|
48
|
-
* File.
|
26
|
+
* File.safe_replace - atomic replacement of a file given a block to generate it
|
49
27
|
|
50
28
|
# Defers deleting old file until block completes successfully (ie no exceptions), then
|
51
29
|
# moves the new file into the old file's location
|
52
|
-
File.
|
30
|
+
File.safe_replace('./config') do |file|
|
53
31
|
file.write("PRODUCTION: true")
|
54
32
|
end
|
55
33
|
|
56
|
-
* Fixnum#blank? - always false
|
57
34
|
* Fixnum#to_human_size - size to MB/GB/whatever, adapted from Rails
|
58
35
|
|
59
36
|
123456.to_human_size # => "120.5 KB"
|
@@ -75,7 +52,7 @@ Helpful extensions to core Ruby classes, plus a little sugar for common patterns
|
|
75
52
|
|
76
53
|
* Nil#blank? - always true
|
77
54
|
|
78
|
-
* Numeric#to_display - pretty display of numbers with options for number of decimal places and inclusion of thousands separator
|
55
|
+
* Numeric#to_display - pretty display of numbers with options for number of decimal places and inclusion of thousands separator (US-only, sorry!)
|
79
56
|
|
80
57
|
5000.to_display # => 5,000
|
81
58
|
100.to_display(2) # => 100.00
|
@@ -97,8 +74,8 @@ Helpful extensions to core Ruby classes, plus a little sugar for common patterns
|
|
97
74
|
* Regexp::IP_ADDRESS, Regexp::EMAIL_ADDRESS, Regexp::DOMAIN - commonly useful regexen
|
98
75
|
|
99
76
|
* String#blank? - true if empty?
|
100
|
-
* String#append / String#prepend -
|
101
|
-
* String#to_date - "better" date parser than Date.parse
|
77
|
+
* String#append / String#prepend - these should be in Ruby core
|
78
|
+
* String#to_date - "better" date parser than Date.parse (US-centric)
|
102
79
|
* String#to_dashcase - perfect for permalinks!
|
103
80
|
* String#smart_truncate - truncate honoring word boundaries
|
104
81
|
* String#integer? - true when string represents an integer
|
@@ -110,22 +87,6 @@ Helpful extensions to core Ruby classes, plus a little sugar for common patterns
|
|
110
87
|
* Symbol#blank? - always false
|
111
88
|
* Symbol#to_dashcase - same as for String
|
112
89
|
|
113
|
-
== ADDED CLASSES/MODULES
|
114
|
-
|
115
|
-
* DslProxy - a cool and sexy way to make powerful DSLs (domain-specific languages) look easy - see the docs for details
|
116
|
-
|
117
|
-
# DslProxy makes this code possible:
|
118
|
-
@items = ['one', 'two']
|
119
|
-
Console.out do
|
120
|
-
# No explicit receiver for DSL method calls
|
121
|
-
p 'Item List'
|
122
|
-
hr
|
123
|
-
indent do
|
124
|
-
# Even nested, local variables are still available
|
125
|
-
@items.each {|item| p item }
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
90
|
== SYNOPSIS
|
130
91
|
|
131
92
|
To use:
|
data/Version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0
|
@@ -1,17 +1,5 @@
|
|
1
1
|
class Array
|
2
2
|
|
3
|
-
# DEPRECATED: as of Ruby 1.9, you should use Array#sample instead
|
4
|
-
def rand(count = 1)
|
5
|
-
warn("[DEPRECATION] Array#rand is deprecated - use Array#sample instead")
|
6
|
-
shuffle[0...count]
|
7
|
-
end
|
8
|
-
|
9
|
-
# DEPRECATED: as of Ruby 1.9, you should use Array#sample instead
|
10
|
-
def rand!(count = 1)
|
11
|
-
warn("[DEPRECATION] Array#rand is deprecated - use Array#sample instead")
|
12
|
-
self.replace rand(count)
|
13
|
-
end
|
14
|
-
|
15
3
|
# Join an array's values ignoring blank entries
|
16
4
|
def list_join(sep = ', ')
|
17
5
|
self.select{|e| !e.blank?}.join(sep)
|
@@ -78,5 +78,11 @@ describe String do
|
|
78
78
|
it 'should support natural ordering' do
|
79
79
|
['a0', 'a90', 'a10', 'a9'].sort_by {|a| a.natural_order}.should == ['a0', 'a9', 'a10', 'a90']
|
80
80
|
end
|
81
|
+
|
82
|
+
it 'should be appropriately blank' do
|
83
|
+
[nil, '', ' ', "\t", "\n"].each do |test|
|
84
|
+
test.should be_blank
|
85
|
+
end
|
86
|
+
end
|
81
87
|
|
82
88
|
end
|
metadata
CHANGED
@@ -1,30 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iron-extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
5
|
-
prerelease:
|
4
|
+
version: 1.2.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Rob Morris
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2015-01-28 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rspec
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- - ~>
|
17
|
+
- - "~>"
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '2.6'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- - ~>
|
24
|
+
- - "~>"
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '2.6'
|
30
27
|
description: Adds common extensions to core Ruby classes
|
@@ -34,10 +31,13 @@ executables: []
|
|
34
31
|
extensions: []
|
35
32
|
extra_rdoc_files: []
|
36
33
|
files:
|
34
|
+
- ".rspec"
|
35
|
+
- History.txt
|
36
|
+
- LICENSE
|
37
|
+
- README.rdoc
|
38
|
+
- Version.txt
|
39
|
+
- lib/iron/extensions.rb
|
37
40
|
- lib/iron/extensions/array.rb
|
38
|
-
- lib/iron/extensions/class.rb
|
39
|
-
- lib/iron/extensions/dsl_builder.rb
|
40
|
-
- lib/iron/extensions/dsl_proxy.rb
|
41
41
|
- lib/iron/extensions/enumerable.rb
|
42
42
|
- lib/iron/extensions/file.rb
|
43
43
|
- lib/iron/extensions/fixnum.rb
|
@@ -50,10 +50,7 @@ files:
|
|
50
50
|
- lib/iron/extensions/regexp.rb
|
51
51
|
- lib/iron/extensions/string.rb
|
52
52
|
- lib/iron/extensions/symbol.rb
|
53
|
-
- lib/iron/extensions.rb
|
54
53
|
- spec/extensions/array_spec.rb
|
55
|
-
- spec/extensions/dsl_builder_spec.rb
|
56
|
-
- spec/extensions/dsl_proxy_spec.rb
|
57
54
|
- spec/extensions/enumerable_spec.rb
|
58
55
|
- spec/extensions/kernel_spec.rb
|
59
56
|
- spec/extensions/numeric_spec.rb
|
@@ -61,34 +58,28 @@ files:
|
|
61
58
|
- spec/extensions/string_spec.rb
|
62
59
|
- spec/extensions/symbol_spec.rb
|
63
60
|
- spec/spec_helper.rb
|
64
|
-
- LICENSE
|
65
|
-
- History.txt
|
66
|
-
- Version.txt
|
67
|
-
- README.rdoc
|
68
|
-
- .rspec
|
69
61
|
homepage: https://github.com/irongaze/iron-extensions
|
70
62
|
licenses:
|
71
63
|
- MIT
|
64
|
+
metadata: {}
|
72
65
|
post_install_message:
|
73
66
|
rdoc_options: []
|
74
67
|
require_paths:
|
75
68
|
- lib
|
76
69
|
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
-
none: false
|
78
70
|
requirements:
|
79
|
-
- -
|
71
|
+
- - ">="
|
80
72
|
- !ruby/object:Gem::Version
|
81
73
|
version: 1.9.2
|
82
74
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
-
none: false
|
84
75
|
requirements:
|
85
|
-
- -
|
76
|
+
- - ">="
|
86
77
|
- !ruby/object:Gem::Version
|
87
78
|
version: '0'
|
88
79
|
requirements: []
|
89
80
|
rubyforge_project:
|
90
|
-
rubygems_version:
|
81
|
+
rubygems_version: 2.4.3
|
91
82
|
signing_key:
|
92
|
-
specification_version:
|
83
|
+
specification_version: 4
|
93
84
|
summary: Extensions to core classes
|
94
85
|
test_files: []
|
@@ -1,46 +0,0 @@
|
|
1
|
-
class Class
|
2
|
-
|
3
|
-
# Provides a DSL-friendly way to set values. Similar to attr_accessor, but
|
4
|
-
# supports setting values like so:
|
5
|
-
#
|
6
|
-
# class Widget
|
7
|
-
# dsl_accessor :size
|
8
|
-
# end
|
9
|
-
# @w = Widget.new
|
10
|
-
# @w.size = 5 # normal setter, same as...
|
11
|
-
# @w.size 5 # note the lack of explicit = sign
|
12
|
-
# puts @w.size # still get reader access
|
13
|
-
#
|
14
|
-
# Useful in DslProxy blocks:
|
15
|
-
#
|
16
|
-
# DslProxy.exec(Widget.new) do
|
17
|
-
# size 10 # sets size to 10, as expected
|
18
|
-
# size = 10 # fails, creates local variable 'size' instead of invoking Widget#size
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
def dsl_accessor(*keys)
|
22
|
-
keys.each do |key|
|
23
|
-
class_eval "def #{key}(val = :__UNDEFINED); @#{key} = val unless val == :__UNDEFINED; @#{key}; end"
|
24
|
-
class_eval "def #{key}=(val); @#{key} = val; end"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# Like #dsl_accessor, but adds imperative and query versions of the keys as well to set the
|
29
|
-
# flag to true and to query the true-ness of the flag.
|
30
|
-
#
|
31
|
-
# class Widget
|
32
|
-
# dsl_flag :heavy
|
33
|
-
# end
|
34
|
-
# @w = Widget.new
|
35
|
-
# @w.heavy? # => false
|
36
|
-
# @w.heavy! # now is true
|
37
|
-
#
|
38
|
-
def dsl_flag(*keys)
|
39
|
-
dsl_accessor(*keys)
|
40
|
-
keys.each do |key|
|
41
|
-
class_eval "def #{key}!; @#{key} = true; end"
|
42
|
-
class_eval "def #{key}?; @#{key} === true; end"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# Provides a base class for building DSL (domain specific language) builder
|
2
|
-
# classes, ie classes that define a minimal subset of methods and act as aggregators
|
3
|
-
# of settings or functionality. Similar to BasicObject in the standard library, but
|
4
|
-
# has methods such as respond_to? and send that are required for any real DSL building
|
5
|
-
# effort.
|
6
|
-
class DslBuilder < Object
|
7
|
-
|
8
|
-
# Remove all methods not explicitly desired
|
9
|
-
instance_methods.each do |m|
|
10
|
-
keepers = [:inspect, :send]
|
11
|
-
undef_method m if m =~ /^[a-z]+[0-9]?$/ && !keepers.include?(m)
|
12
|
-
end
|
13
|
-
|
14
|
-
end
|
@@ -1,176 +0,0 @@
|
|
1
|
-
# Specialty helper class for building elegant DSLs (domain-specific languages)
|
2
|
-
# The purpose of the class is to allow seamless DSL's by allowing execution
|
3
|
-
# of blocks with the instance variables of the calling context preserved, but
|
4
|
-
# all method calls proxied to a given receiver. This sounds pretty abstract,
|
5
|
-
# so here's an example:
|
6
|
-
#
|
7
|
-
# class ControlBuilder
|
8
|
-
# def initialize; @controls = []; end
|
9
|
-
# def control_list; @controls; end
|
10
|
-
# def knob; @controls << :knob; end
|
11
|
-
# def button; @controls << :button; end
|
12
|
-
# def switch; @controls << :switch; end
|
13
|
-
# def self.define(&block)
|
14
|
-
# @builder = self.new
|
15
|
-
# DslProxy.exec(@builder, &block)
|
16
|
-
# # Do something here with the builder's list of controls
|
17
|
-
# @builder.control_list
|
18
|
-
# end
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# @knob_count = 5
|
22
|
-
# new_list = ControlBuilder.define do
|
23
|
-
# switch
|
24
|
-
# @knob_count.times { knob }
|
25
|
-
# button
|
26
|
-
# end
|
27
|
-
#
|
28
|
-
# Notice the lack of explicit builder receiver to the calls to #switch, #knob and #button.
|
29
|
-
# Those calls are automatically proxied to the receiver we passed to the DslProxy.
|
30
|
-
#
|
31
|
-
# In quick and dirty DSLs, like Rails' migrations, you end up with a lot of
|
32
|
-
# pointless receiver declarations for each method call, like so:
|
33
|
-
#
|
34
|
-
# def change
|
35
|
-
# create_table do |t|
|
36
|
-
# t.integer :counter
|
37
|
-
# t.text :title
|
38
|
-
# t.text :desc
|
39
|
-
# # ... tired of typing "t." yet? ...
|
40
|
-
# end
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# This is not a big deal if you're using a simple DSL, but when you have multiple nested
|
44
|
-
# builders going on at once, it is ugly, pointless, and can cause bugs when
|
45
|
-
# the throwaway arg names you choose (eg 't' above) overlap in scope.
|
46
|
-
#
|
47
|
-
# In addition, simply using a yield statment loses the instance variables set in the calling
|
48
|
-
# context. This is a major pain in eg Rails views, where most of the interesting
|
49
|
-
# data resides in instance variables. You can get around this when #yield-ing by
|
50
|
-
# explicitly creating a local variable to be picked up by the closure created in the
|
51
|
-
# block, but it kind of sucks.
|
52
|
-
#
|
53
|
-
# In summary, DslProxy allows you to keep all the local and instance variable context
|
54
|
-
# from your block declarations, while proxying all method calls to a given
|
55
|
-
# receiver. If you're not building DSLs, this class is not for you, but if you are,
|
56
|
-
# I hope it helps!
|
57
|
-
class DslProxy < BasicObject
|
58
|
-
|
59
|
-
# Pass in a builder-style class, or other receiver you want set as "self" within the
|
60
|
-
# block, and off you go. The passed block will be executed with all
|
61
|
-
# block-context local and instance variables available, but with all
|
62
|
-
# method calls sent to the receiver you pass in. The block's result will
|
63
|
-
# be returned.
|
64
|
-
#
|
65
|
-
# If the receiver doesn't respond_to? a method, any missing methods
|
66
|
-
# will be proxied to the enclosing context.
|
67
|
-
def self.exec(receiver, *to_yield, &block) # :yields: receiver
|
68
|
-
# Find the context within which the block was defined
|
69
|
-
context = ::Kernel.eval('self', block.binding)
|
70
|
-
|
71
|
-
# Create or re-use our proxy object
|
72
|
-
if context.respond_to?(:_to_dsl_proxy)
|
73
|
-
# If we're nested, we don't want/need a new dsl proxy, just re-use the existing one
|
74
|
-
proxy = context._to_dsl_proxy
|
75
|
-
else
|
76
|
-
# Not nested, create a new proxy for our use
|
77
|
-
proxy = DslProxy.new(context)
|
78
|
-
end
|
79
|
-
|
80
|
-
# Exec the block and return the result
|
81
|
-
proxy._proxy(receiver, *to_yield, &block)
|
82
|
-
end
|
83
|
-
|
84
|
-
# Simple state setup
|
85
|
-
def initialize(context)
|
86
|
-
@_receivers = []
|
87
|
-
@_instance_original_values = {}
|
88
|
-
@_context = context
|
89
|
-
end
|
90
|
-
|
91
|
-
def _proxy(receiver, *to_yield, &block) # :yields: receiver
|
92
|
-
# Sanity!
|
93
|
-
raise 'Cannot proxy with a DslProxy as receiver!' if receiver.respond_to?(:_to_dsl_proxy)
|
94
|
-
|
95
|
-
if @_receivers.empty?
|
96
|
-
# On first proxy call, run each context instance variable,
|
97
|
-
# and set it to ourselves so we can proxy it
|
98
|
-
@_context.instance_variables.each do |var|
|
99
|
-
unless var.starts_with?('@_')
|
100
|
-
value = @_context.instance_variable_get(var.to_s)
|
101
|
-
@_instance_original_values[var] = value
|
102
|
-
instance_eval "#{var} = value"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
# Save the dsl target as our receiver for proxying
|
108
|
-
_push_receiver(receiver)
|
109
|
-
|
110
|
-
# Run the block with ourselves as the new "self", passing the given yieldable(s) or
|
111
|
-
# the receiver in case the code wants to disambiguate for some reason
|
112
|
-
to_yield = [receiver] if to_yield.empty?
|
113
|
-
to_yield = to_yield.first(block.arity)
|
114
|
-
result = instance_exec(*to_yield, &block)
|
115
|
-
|
116
|
-
# Pop the last receiver off the stack
|
117
|
-
_pop_receiver
|
118
|
-
|
119
|
-
if @_receivers.empty?
|
120
|
-
# Run each local instance variable and re-set it back to the context if it has changed during execution
|
121
|
-
#instance_variables.each do |var|
|
122
|
-
@_context.instance_variables.each do |var|
|
123
|
-
unless var.starts_with?('@_')
|
124
|
-
value = instance_eval("#{var}")
|
125
|
-
if @_instance_original_values[var] != value
|
126
|
-
@_context.instance_variable_set(var.to_s, value)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
return result
|
133
|
-
end
|
134
|
-
|
135
|
-
# For nesting multiple proxies
|
136
|
-
def _to_dsl_proxy
|
137
|
-
self
|
138
|
-
end
|
139
|
-
|
140
|
-
# Set the currently active receiver
|
141
|
-
def _push_receiver(receiver)
|
142
|
-
@_receivers.push receiver
|
143
|
-
end
|
144
|
-
|
145
|
-
# Remove the currently active receiver, restore old receiver if nested
|
146
|
-
def _pop_receiver
|
147
|
-
@_receivers.pop
|
148
|
-
end
|
149
|
-
|
150
|
-
# Proxies all calls to our receiver, or to the block's context
|
151
|
-
# if the receiver doesn't respond_to? it.
|
152
|
-
def method_missing(method, *args, &block)
|
153
|
-
#$stderr.puts "Method missing: #{method}"
|
154
|
-
if @_receivers.last.respond_to?(method)
|
155
|
-
#$stderr.puts "Proxy [#{method}] to receiver"
|
156
|
-
@_receivers.last.__send__(method, *args, &block)
|
157
|
-
else
|
158
|
-
#$stderr.puts "Proxy [#{method}] to context"
|
159
|
-
@_context.__send__(method, *args, &block)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
# Let anyone who's interested know what our proxied objects will accept
|
164
|
-
def respond_to?(method, include_private = false)
|
165
|
-
return true if method == :_to_dsl_proxy
|
166
|
-
@_receivers.last.respond_to?(method, include_private) || @_context.respond_to?(method, include_private)
|
167
|
-
end
|
168
|
-
|
169
|
-
# Proxies searching for constants to the context, so that eg Kernel::foo can actually
|
170
|
-
# find Kernel - BasicObject does not partake in the global scope!
|
171
|
-
def self.const_missing(name)
|
172
|
-
#$stderr.puts "Constant missing: #{name} - proxy to context"
|
173
|
-
@_context.class.const_get(name)
|
174
|
-
end
|
175
|
-
|
176
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
describe DslBuilder do
|
2
|
-
|
3
|
-
# TODO: break this out a bit...
|
4
|
-
it 'should allow using DSL-style accessors' do
|
5
|
-
class MyBuilder < DslBuilder
|
6
|
-
dsl_accessor :name
|
7
|
-
dsl_flag :flagged
|
8
|
-
end
|
9
|
-
builder = MyBuilder.new
|
10
|
-
|
11
|
-
# Test standalone
|
12
|
-
builder.name 'ProjectX'
|
13
|
-
builder.name.should == 'ProjectX'
|
14
|
-
|
15
|
-
builder.flagged?.should be_false
|
16
|
-
builder.flagged = true
|
17
|
-
builder.flagged?.should be_true
|
18
|
-
builder.flagged = false
|
19
|
-
|
20
|
-
# Test as part of DslProxy usage (common case)
|
21
|
-
DslProxy.exec(builder) do
|
22
|
-
name 'Project Omega'
|
23
|
-
flagged!
|
24
|
-
end
|
25
|
-
builder.name.should == 'Project Omega'
|
26
|
-
builder.flagged?.should be_true
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
@@ -1,110 +0,0 @@
|
|
1
|
-
describe DslProxy do
|
2
|
-
|
3
|
-
# Sample DSL builder class for use in testing
|
4
|
-
class ControlBuilder
|
5
|
-
def initialize; @controls = []; end
|
6
|
-
def controls; @controls; end
|
7
|
-
def knob; @controls << :knob; end
|
8
|
-
def button; @controls << :button; end
|
9
|
-
def switch; @controls << :switch; end
|
10
|
-
|
11
|
-
def self.define(&block)
|
12
|
-
@builder = self.new
|
13
|
-
DslProxy.exec(@builder, &block)
|
14
|
-
@builder.controls
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'should proxy calls to the receiver' do
|
19
|
-
receiver = Object.new
|
20
|
-
DslProxy.exec(receiver) do
|
21
|
-
self.class.name.should == 'Object'
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'should proxy respond_to? to the receiver' do
|
26
|
-
receiver = ControlBuilder.new
|
27
|
-
DslProxy.exec(receiver) do
|
28
|
-
respond_to?(:garbaz).should == false
|
29
|
-
respond_to?(:button).should == true
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'should proxy local variables from the binding context' do
|
34
|
-
@foo = 'bar'
|
35
|
-
DslProxy.exec(Object.new) do
|
36
|
-
@foo.should == 'bar'
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'should propagate local variable changes back to the binding context' do
|
41
|
-
@foo = 'bar'
|
42
|
-
DslProxy.exec(Object.new) do
|
43
|
-
@foo = 'no bar!'
|
44
|
-
end
|
45
|
-
@foo.should == 'no bar!'
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'should proxy missing methods on the receiver to the calling context' do
|
49
|
-
class TestContext
|
50
|
-
def bar
|
51
|
-
'something'
|
52
|
-
end
|
53
|
-
|
54
|
-
def test
|
55
|
-
DslProxy.exec(Object.new) do
|
56
|
-
bar
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
TestContext.new.test.should == 'something'
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'should return the result of the block' do
|
65
|
-
res = DslProxy.exec(Object.new) do
|
66
|
-
'foo'
|
67
|
-
end
|
68
|
-
res.should == 'foo'
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'should allow access to global constants' do
|
72
|
-
DslProxy.exec(self) do # Use self here, so #be_a is defined. :-)
|
73
|
-
Object.new.should be_a(Object)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'should proxy correctly even when nested' do
|
78
|
-
def outerfunc
|
79
|
-
5
|
80
|
-
end
|
81
|
-
@instance_var = nil
|
82
|
-
local_var = nil
|
83
|
-
DslProxy.exec(self) do
|
84
|
-
DslProxy.exec(Object.new) do
|
85
|
-
outerfunc.should == 5
|
86
|
-
@instance_var.should be_nil
|
87
|
-
local_var.should be_nil
|
88
|
-
@instance_var = 10
|
89
|
-
local_var = 11
|
90
|
-
end
|
91
|
-
end
|
92
|
-
@instance_var.should == 10
|
93
|
-
local_var.should == 11
|
94
|
-
end
|
95
|
-
|
96
|
-
it 'should pass additional args to block as argument' do
|
97
|
-
l = lambda {|arg1, arg2| arg1 + arg2}
|
98
|
-
DslProxy.exec(Object.new, 5, 1, &l).should == 6
|
99
|
-
end
|
100
|
-
|
101
|
-
it 'should put it all together' do
|
102
|
-
@knob_count = 5
|
103
|
-
controls = ControlBuilder.define do
|
104
|
-
switch
|
105
|
-
@knob_count.times { knob }
|
106
|
-
end
|
107
|
-
controls.count.should == 6
|
108
|
-
end
|
109
|
-
|
110
|
-
end
|