ampex 1.1.1 → 1.1.2

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.
Files changed (3) hide show
  1. data/README.markdown +19 -7
  2. data/lib/ampex.rb +47 -18
  3. metadata +4 -4
data/README.markdown CHANGED
@@ -13,7 +13,12 @@ However the real strength in the library comes from allowing you to call methods
13
13
  [1,"2",3].select &X.is_a?(String)
14
14
  # => ["2"]
15
15
 
16
- And, as everything in ruby is a method, create readable expressions without the noise of a one-argument block:
16
+ And to chain method calls:
17
+
18
+ [1, 2, 3].map &X.to_f.to_s
19
+ # => ["1.0", "2.0", "3.0"]
20
+
21
+ As everything in Ruby is a method call, you can create readable expressions without the noise of a one-argument block:
17
22
 
18
23
  [{1 => 2}, {1 => 3}].map &X[1]
19
24
  # => [2, 3]
@@ -27,10 +32,11 @@ And, as everything in ruby is a method, create readable expressions without the
27
32
  [{}].each &X[1] = 2
28
33
  # => [{1 => 2}]
29
34
 
30
- As an added bonus, the effect is transitive you can chain method calls:
35
+ You can use this in any place a block is expected, for example to create a lambda:
31
36
 
32
- [1, 2, 3].map &X.to_f.to_s
33
- # => ["1.0", "2.0", "3.0"]
37
+ normalizer = lambda &X.to_s.downcase
38
+ normalizer.call :HelloWorld
39
+ # => "helloworld"
34
40
 
35
41
  Gotchas
36
42
  -------
@@ -64,14 +70,20 @@ Secondly, other arguments or operands will only be evaluated once, and not every
64
70
  Bugs
65
71
  ----
66
72
 
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
-
73
+ In normal usage there are no known bugs. That said, if you accidentally miss the `&` from in front of the `X`, in an expression that ends in an assignment (e.g. `X.formatter = :inspect`); then the `#to_proc` method of the object assigned will respond with the expression generated by that `X` the next time you call it from anywhere else in the same thread.
69
74
 
70
75
  Epilogue
71
76
  --------
72
77
 
78
+ `&X` has been tested on MRI ruby 1.8.6, 1.8.7 and 1.9.2 and jruby 1.5.3. It is thread-safe.
79
+
73
80
  For bug-fixes or enhancements, please contact the author: Conrad Irwin <conrad.irwin@gmail.com>
74
81
 
75
- For an up-to-date version, try http://github.com/rapportive-oss/ampex
82
+ For an up-to-date version, try <https://github.com/rapportive-oss/ampex>
76
83
 
77
84
  This library is copyrighted under the MIT license, see LICENSE.MIT for details.
85
+
86
+ See also
87
+ --------
88
+
89
+ * <https://github.com/danielribeiro/RubyUnderscore> — which uses an underscore in place of `&X` and works by rewriting the syntax tree.
data/lib/ampex.rb CHANGED
@@ -28,7 +28,7 @@ class Metavariable < BlankSlate
28
28
  #
29
29
  def method_missing(name, *args, &block)
30
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 =~ /[^!=]=$/
31
+ Metavariable.temporarily_monkeypatch(args.last, :to_proc) { mv.to_proc } if name.to_s =~ /[^!=<>]=$/
32
32
  mv
33
33
  end
34
34
 
@@ -49,28 +49,57 @@ class Metavariable < BlankSlate
49
49
  # :two.to_proc _/ and un-patch here
50
50
  # ary.map &_
51
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.
52
+ # We go to some lengths to ensure that, providing the & and the X are adjacent,
53
+ # it's not possible to get different behaviour in the rest of the program; despite
54
+ # the temporary mutation of potentially global state.
55
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.
56
+ # We can't really do anything if the & has been split from the X, consider:
59
57
  #
60
- def self.temporarily_monkeypatch(klass, mv)
61
- klass.send :class_variable_set, :'@@metavariable', mv
62
- klass.class_eval do
58
+ # assigner = (X[0] = :to_i)
59
+ # assigner == :to_i
60
+ # # => true
61
+ # [1,2,3].map(&:to_i)
62
+ # # => NoMethodError: undefined method `[]=' for 1:Fixnum
63
+ #
64
+ # Just strongly encourage use of:
65
+ # assigner = lambda &X = :to_i
66
+ # assigner == :to_i
67
+ # # => false
68
+ # [1,2,3].map(&:to_i)
69
+ # # => [1,2,3]
70
+ #
71
+ def self.temporarily_monkeypatch(instance, method_name, &block)
72
+
73
+ Thread.exclusive do
74
+ @monkey_patch_count = @monkey_patch_count ? @monkey_patch_count + 1 : 0
75
+ stashed_method_name = :"#{method_name}_without_metavariable_#{@monkey_patch_count}"
76
+ thread = Thread.current
77
+
78
+ # Try to get a handle on the object's singleton class, but fall back to using
79
+ # its actual class where that is not possible (i.e. for numbers and symbols)
80
+ klass = (class << instance; self; end) rescue instance.class
81
+ klass.class_eval do
82
+
83
+ alias_method(stashed_method_name, method_name) rescue nil
84
+ define_method(method_name) do
85
+
86
+ todo = block
87
+
88
+ Thread.exclusive do
89
+ if self.equal?(instance) && thread.equal?(Thread.current)
63
90
 
64
- alias_method(:to_proc_without_metavariable, :to_proc) rescue nil
65
- def to_proc
66
- self.class.class_eval do
91
+ klass.class_eval do
92
+ undef_method(method_name)
93
+ alias_method(method_name, stashed_method_name) rescue nil
94
+ undef_method(stashed_method_name) rescue nil
95
+ end
67
96
 
68
- undef to_proc
69
- alias_method(:to_proc, :to_proc_without_metavariable) rescue nil
70
- undef to_proc_without_metavariable rescue nil
97
+ else
98
+ todo = method(stashed_method_name)
99
+ end
100
+ end
71
101
 
72
- # Remove the metavariable from the class and return its proc
73
- remove_class_variable(:'@@metavariable').to_proc
102
+ todo.call
74
103
  end
75
104
  end
76
105
  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: 17
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 1
9
- - 1
10
- version: 1.1.1
9
+ - 2
10
+ version: 1.1.2
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-12-02 00:00:00 -08:00
18
+ date: 2010-12-04 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency