ampex 1.0.0 → 1.1.0
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/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
|