ampex 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +18 -1
- data/lib/ampex.rb +63 -12
- metadata +4 -4
data/README.markdown
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
The Ampex (`&X`) library provides a Metavariable X that can be used in conjunction with the unary ampersand to create anonymous blocks in a slightly more readable way than the default. It was inspired by the clever `Symbol#to_proc` method which handles the most common case very elegantly, and discussion with Sam Stokes who created an earlier version.
|
2
2
|
|
3
|
+
Usage
|
4
|
+
-----
|
5
|
+
|
3
6
|
At its simplest, `&X` can be used as a drop-in replacement for `Symbol#to_proc`:
|
4
7
|
|
5
8
|
[1,2,3].map &X.to_s
|
@@ -21,12 +24,18 @@ And, as everything in ruby is a method, create readable expressions without the
|
|
21
24
|
["a", "b", "c"].map &(X * 2)
|
22
25
|
# => ["aa", "bb", "cc"]
|
23
26
|
|
27
|
+
[{}].each &X[1] = 2
|
28
|
+
# => [{1 => 2}]
|
29
|
+
|
24
30
|
As an added bonus, the effect is transitive — you can chain method calls:
|
25
31
|
|
26
32
|
[1, 2, 3].map &X.to_f.to_s
|
27
33
|
# => ["1.0", "2.0", "3.0"]
|
28
34
|
|
29
|
-
|
35
|
+
Gotchas
|
36
|
+
-------
|
37
|
+
|
38
|
+
There are a few things to watch out for:
|
30
39
|
|
31
40
|
Firstly, `&X` can only appear on the left:
|
32
41
|
|
@@ -52,6 +61,14 @@ Secondly, other arguments or operands will only be evaluated once, and not every
|
|
52
61
|
[1, 2].map{ |x| x + (i += 1) }
|
53
62
|
# => [2, 4]
|
54
63
|
|
64
|
+
Bugs
|
65
|
+
----
|
66
|
+
|
67
|
+
If you create an assigning callable (e.g. `X[a] = b`, `X.a = b` ) without an immediate preceding `&`, then `b.class#to_proc` will return the assigning callable the first time, and only the first time, you call it. If you want to get access to an assigning callable that you've defined using `&X`, you must do: `lambda &X[a] = b` instead.
|
68
|
+
|
69
|
+
|
70
|
+
Epilogue
|
71
|
+
--------
|
55
72
|
|
56
73
|
For bug-fixes or enhancements, please contact the author: Conrad Irwin <conrad.irwin@gmail.com>
|
57
74
|
|
data/lib/ampex.rb
CHANGED
@@ -4,23 +4,74 @@ require 'blankslate'
|
|
4
4
|
#
|
5
5
|
# For detailed usage notes, please see README.markdown
|
6
6
|
#
|
7
|
-
class Metavariable < BlankSlate
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
class Metavariable < BlankSlate
|
8
|
+
|
9
|
+
# When you pass an argument with & in ruby, you're actually calling #to_proc
|
10
|
+
# on the object. So it's Symbol#to_proc that makes the &:to_s trick work,
|
11
|
+
# and Metavariable#to_proc that makes &X work.
|
12
|
+
attr_reader :to_proc
|
13
|
+
|
14
|
+
def initialize(&block)
|
15
|
+
@to_proc = block || lambda{|x| x}
|
11
16
|
end
|
12
17
|
|
18
|
+
# Each time a method is called on a Metavariable, we want to create a new
|
19
|
+
# Metavariable that is like the last but does something more.
|
20
|
+
#
|
21
|
+
# The end result of calling X.one.two will be like:
|
22
|
+
#
|
23
|
+
# lambda{|x|
|
24
|
+
# (lambda{|x|
|
25
|
+
# (lambda{|x| x}).call(x).one
|
26
|
+
# }).call(x).two
|
27
|
+
# }
|
28
|
+
#
|
13
29
|
def method_missing(name, *args, &block)
|
14
|
-
Metavariable.new
|
30
|
+
mv = Metavariable.new { |x| @to_proc.call(x).send(name, *args, &block) }
|
31
|
+
Metavariable.temporarily_monkeypatch(args.last.class, mv) if name.to_s =~ /=$/
|
32
|
+
mv
|
15
33
|
end
|
16
34
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
35
|
+
private
|
36
|
+
|
37
|
+
# In order to support assignment via &X (expressions of the form &X['one'] = 2),
|
38
|
+
# we need to add 2.to_proc (because assignment in ruby always returns the operand)
|
39
|
+
#
|
40
|
+
# Luckily, we only need to do this for a very short time.
|
41
|
+
#
|
42
|
+
# When given an expression such as:
|
43
|
+
#
|
44
|
+
# ary.map(&X[args(a)] = :two)
|
45
|
+
#
|
46
|
+
# the order of execution is:
|
47
|
+
# args(a)
|
48
|
+
# X[_] = :two \_ need to patch here
|
49
|
+
# :two.to_proc _/ and un-patch here
|
50
|
+
# ary.map &_
|
51
|
+
#
|
52
|
+
# There is a risk if someone uses an amp-less X, and assigns something with to_proc
|
53
|
+
# (most likely a symbol), and then uses .map(&:to_i), as &:to_i will return the
|
54
|
+
# behaviour of their metavariable.
|
55
|
+
#
|
56
|
+
# There are other things that might notice us doing this, if people are listening
|
57
|
+
# on various method_added hooks, or have overridden class_eval, etc. But I'm not
|
58
|
+
# too worried.
|
59
|
+
#
|
60
|
+
def self.temporarily_monkeypatch(klass, mv)
|
61
|
+
klass.send :class_variable_set, :'@@metavariable', mv
|
62
|
+
klass.class_eval do
|
63
|
+
|
64
|
+
alias_method(:to_proc_without_metavariable, :to_proc) rescue nil
|
65
|
+
def to_proc
|
66
|
+
self.class.class_eval do
|
67
|
+
|
68
|
+
undef to_proc
|
69
|
+
alias_method(:to_proc, :to_proc_without_metavariable) rescue nil
|
70
|
+
undef to_proc_without_metavariable rescue nil
|
71
|
+
|
72
|
+
# Remove the metavariable from the class and return its proc
|
73
|
+
remove_class_variable(:'@@metavariable').to_proc
|
74
|
+
end
|
24
75
|
end
|
25
76
|
end
|
26
77
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ampex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 1.0.0
|
10
|
+
version: 1.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Conrad Irwin
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-11-
|
18
|
+
date: 2010-11-28 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|