contextr 0.1.0 → 0.1.1

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.txt CHANGED
@@ -1,3 +1,16 @@
1
- README for contextr
2
- ===================
1
+ = ContextR
3
2
 
3
+ A context-oriented programming library for Ruby.
4
+
5
+ Inspired by ContextL (Pascal Costanza) and ContextS (Robert Hirschfeld) with
6
+ thanks to Christian Neukirchen for giving the name and lots of ideas.
7
+
8
+ For more information see
9
+ - http://contextr.rubyforge.org/
10
+ - http://www.contextr.org/ or
11
+ - http://www.swa.hpi.uni-potsdam.de/cop/
12
+
13
+ This code is published under the same license as Ruby. See LICENSE.txt for more
14
+ information.
15
+
16
+ (c) 2007 - Gregor Schmidt - Berlin, Germany
data/examples/README ADDED
@@ -0,0 +1,9 @@
1
+ All documentation may be found in /test and /spec. The first contains written
2
+ english and some embedded usage examples, the latter a specification written in
3
+ RSpec.
4
+
5
+ This allows the documentation to be executable and testable. Therefore it is
6
+ more likely that is in sync with the current development of the libary. This
7
+ is all for your best.
8
+
9
+ So just have a look at the files in test and you will see what I mean.
@@ -1,5 +1,7 @@
1
- module ContextR
2
- module ClassMethods
1
+ module ContextR # :nodoc:
2
+ module ClassMethods # :nodoc:
3
+ include MutexCode
4
+
3
5
  def const_missing(const_name)
4
6
  if const_name.to_s =~ /.*Layer$/
5
7
  self.const_set(const_name, Class.new(ContextR::Layer))
@@ -14,8 +16,22 @@ module ContextR
14
16
  end
15
17
  end
16
18
 
19
+ def active_layers_as_classes
20
+ Dynamic[:layers]
21
+ end
22
+
23
+ def layered_do(layers, block)
24
+ Dynamic.let({:layers => layers}, &block)
25
+ end
26
+
27
+ def layers_as_classes
28
+ constants.select { |l| l =~ /.+Layer$/ }.collect { |l|
29
+ l.scan(/(.+)Layer/).first.first.underscore.to_sym
30
+ }
31
+ end
32
+
17
33
  def symbol_by_layer(lay)
18
- lay.to_s.gsub( /^ContextR::(.*)Layer$/, '\1' ).underscore
34
+ lay.to_s.gsub( /^ContextR::(.*)Layer$/, '\1' ).underscore.to_sym
19
35
  end
20
36
 
21
37
  def layer_by_symbol(sym)
@@ -45,7 +61,7 @@ module ContextR
45
61
  def on_core_method_called(receiver, contextified_class,
46
62
  method_name, arguments, block)
47
63
  proxies = []
48
- layers.each do |layer|
64
+ active_layers_as_classes.each do |layer|
49
65
  proxies += layer.context_proxies(contextified_class, method_name)
50
66
  end.compact
51
67
 
@@ -62,6 +78,9 @@ module ContextR
62
78
  def observe_core_method(klass, method_name, version)
63
79
  only_once do
64
80
  klass.class_eval(%Q{
81
+ if self.instance_methods.include?("#{method_name}")
82
+ undef_method("#{method_name}")
83
+ end
65
84
  def #{method_name}(*arguments, &block)
66
85
  ContextR::on_core_method_called(
67
86
  self,
@@ -1,18 +1,57 @@
1
- class Module
2
- alias_method :include_without_layers, :include
3
- def include_with_layers(associations)
4
- if associations.delete(:class_side)
5
- klass = class << self; self; end
6
- else
7
- klass = self
8
- end
1
+ #--
2
+ # The aliasing of these methods is done in a class_eval block to avoid code
3
+ # documentation by RDoc.
4
+ #++
5
+ Module.class_eval do
6
+ alias_method :include_without_layers, :include
7
+ end
8
+
9
+ class Module
10
+ protected
11
+ def include_with_layers(associations) # :nodoc:
9
12
  associations.each do | modul, layer |
10
- ContextR::layer_by_symbol(layer).add_method_collection(klass, modul)
13
+ ContextR::layer_by_symbol(layer).add_method_collection(self, modul)
11
14
  end
15
+ self
12
16
  end
13
17
 
18
+ # call-seq:
19
+ # include(module, ...) => self
20
+ # include(module => layer_qualifier, ...) => self
21
+ #
22
+ # Invokes <code>Module.append_features</code> on each parameter in turn.
23
+ #
24
+ # If called with a hash, adds the module to the given layer. The behaviour
25
+ # is associated with the class side of the object.
26
+ #
27
+ # module Mod
28
+ # def name
29
+ # "Hello from #{yield(:next)}.\n"
30
+ # end
31
+ # end
32
+ #
33
+ # class Klass
34
+ # def name
35
+ # "Klass"
36
+ # end
37
+ #
38
+ # include Mod => :hello
39
+ # end
40
+ #
41
+ # k = Klass.new
42
+ # k.name #=> "Klass.\n"
43
+ # ContextR::with_layer :hello do
44
+ # k.name #=> "Hello from Klass.\n"
45
+ # end
46
+ # k.name #=> "Klass.\n"
47
+ #
14
48
  def include(*args)
15
49
  args.first.is_a?(Module) ? include_without_layers(*args) :
16
50
  include_with_layers(*args)
17
51
  end
18
52
  end
53
+
54
+ Module.class_eval do
55
+ private :include
56
+ private :include_with_layers
57
+ end
@@ -1,5 +1,72 @@
1
+ #--
2
+ # The aliasing of these methods is done in a class_eval block to avoid code
3
+ # documentation by RDoc.
4
+ #++
5
+ Object.class_eval do
6
+ alias_method :extend_without_layers, :extend
7
+ end
8
+
1
9
  class Object
2
- def behavioural_class
10
+ def extend_with_layers(associations) # :nodoc:
11
+ klass = class << self; self; end
12
+ associations.each do | modul, layer |
13
+ ContextR::layer_by_symbol(layer).add_method_collection(klass, modul)
14
+ end
15
+ self
16
+ end
17
+
18
+ # call-seq:
19
+ # obj.extend(module, ...) => obj
20
+ # obj.extend(module => layer_qualifier, ...) => obj
21
+ #
22
+ # Adds to _obj_ the instance methods from each module given as a
23
+ # parameter.
24
+ #
25
+ # module Mod
26
+ # def hello
27
+ # "Hello from Mod.\n"
28
+ # end
29
+ # end
30
+ #
31
+ # class Klass
32
+ # def hello
33
+ # "Hello from Klass.\n"
34
+ # end
35
+ # end
36
+ #
37
+ # k = Klass.new
38
+ # k.hello #=> "Hello from Klass.\n"
39
+ # k.extend(Mod) #=> #<Klass:0x401b3bc8>
40
+ # k.hello #=> "Hello from Mod.\n"
41
+ #
42
+ # If called with a hash, adds the module to the given layer. The behaviour
43
+ # is associated with the class side of the object.
44
+ #
45
+ # module Mod
46
+ # def name
47
+ # "Hello from #{yield(:next)}.\n"
48
+ # end
49
+ # end
50
+ #
51
+ # class Klass
52
+ # def name
53
+ # "Klass"
54
+ # end
55
+ # end
56
+ #
57
+ # k = Klass.new
58
+ # k.extend(Mod => :hello) #=> #<Klass:0x401b3bc8>
59
+ # k.name #=> "Klass.\n"
60
+ # ContextR::with_layer :hello do
61
+ # k.name #=> "Hello from Klass.\n"
62
+ # end
63
+ # k.name #=> "Klass.\n"
64
+ def extend(*args)
65
+ args.first.is_a?(Module) ? extend_without_layers(*args) :
66
+ extend_with_layers(*args)
67
+ end
68
+
69
+ def behavioural_class #:nodoc:
3
70
  if self.kind_of?(Module)
4
71
  class << self; self; end
5
72
  else
@@ -1,5 +1,5 @@
1
1
  module ContextR
2
- class EventMachine
2
+ class EventMachine # :nodoc: all
3
3
  module ClassMethods
4
4
  include UniqueId
5
5
 
@@ -1,5 +1,5 @@
1
- module ContextR
2
- class Layer
1
+ module ContextR # :nodoc:
2
+ class Layer # :nodoc: all
3
3
  module ClassMethods
4
4
  def definitions
5
5
  @definitions ||= {}
@@ -10,7 +10,9 @@ module ContextR
10
10
 
11
11
  def add_method_collection(contextified_class, methods_module)
12
12
  definitions[contextified_class] ||= []
13
- definitions[contextified_class] |= [methods_module]
13
+ definitions[contextified_class].delete(methods_module)
14
+ definitions[contextified_class].push(methods_module)
15
+
14
16
  (methods_module.instance_methods &
15
17
  contextified_class.instance_methods).each do | method_name |
16
18
  replace_core_method(contextified_class, method_name, 0)
@@ -1,5 +1,5 @@
1
1
  require "thread"
2
- module MutexCode
2
+ module MutexCode #:nodoc:
3
3
  def semaphore
4
4
  @semaphore ||= Mutex.new
5
5
  end
@@ -1,4 +1,4 @@
1
- module UniqueId
1
+ module UniqueId #:nodoc:
2
2
  def new_unique_id
3
3
  $id_semaphore ||= Mutex.new
4
4
  $id_semaphore.synchronize do
@@ -1,14 +1,12 @@
1
1
  module ContextR
2
- module ClassMethods
3
- include MutexCode
4
-
2
+ module PublicApi
5
3
  # allows the explicit activation of layers within a block context
6
4
  #
7
5
  # ContextR::with_layers(:foo, :bar) do
8
- # ContextR::current_layers # => [:default, :foo, :bar]
6
+ # ContextR::active_layers # => [:default, :foo, :bar]
9
7
  #
10
8
  # ContextR::with_layers(:baz) do
11
- # ContextR::current_layers # => [:default, :foo, :bar, :baz]
9
+ # ContextR::active_layers # => [:default, :foo, :bar, :baz]
12
10
  # end
13
11
  #
14
12
  # end
@@ -17,20 +15,20 @@ module ContextR
17
15
  # with_layers(layer_name, ...) { ... }
18
16
  #
19
17
  def with_layers(*layer_symbols, &block)
20
- layers = layer_symbols.collect do | layer_symbol |
18
+ layers = layer_symbols.collect do |layer_symbol|
21
19
  layer_by_symbol(layer_symbol)
22
20
  end
23
- Dynamic.let({ :layers => Dynamic[:layers] | layers }, &block)
21
+ layered_do(active_layers_as_classes - layers + layers, block)
24
22
  end
25
23
  alias with_layer with_layers
26
24
 
27
25
  # allows the explicit deactivation of layers within a block context
28
26
  #
29
27
  # ContextR::with_layers(:foo, :bar) do
30
- # ContextR::current_layers # => [:default, :foo, :bar]
28
+ # ContextR::active_layers # => [:default, :foo, :bar]
31
29
  #
32
30
  # ContextR::without_layers(:foo) do
33
- # ContextR::current_layers # => [:default, :bar]
31
+ # ContextR::active_layers # => [:default, :bar]
34
32
  # end
35
33
  #
36
34
  # end
@@ -39,20 +37,24 @@ module ContextR
39
37
  # without_layers(layer_name, ...) { ... }
40
38
  #
41
39
  def without_layers(*layer_symbols, &block)
42
- layers = layer_symbols.collect do | layer_symbol |
40
+ layers = layer_symbols.collect do |layer_symbol|
43
41
  layer_by_symbol(layer_symbol)
44
42
  end
45
- Dynamic.let({ :layers => Dynamic[:layers] - layers }, &block)
43
+ layered_do(active_layers_as_classes - layers, block)
46
44
  end
47
45
  alias without_layer without_layers
48
46
 
49
- def layers
50
- Dynamic[:layers]
47
+ # returns all currently active layers in their activation order
48
+ def active_layers
49
+ active_layers_as_classes.collect { |layer| symbol_by_layer(layer) }
51
50
  end
52
51
 
53
- def layer_symbols
54
- layers.collect { | layer | symbol_by_layer(layer) }
52
+ # returns all layers that where defined, but are not neccessarily active
53
+ def layers
54
+ layers_as_classes.collect { |layer| symbol_by_layer(layer) }
55
55
  end
56
+
56
57
  end
58
+ self.extend(PublicApi)
57
59
  end
58
60
 
@@ -2,7 +2,7 @@ module ContextR #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 1
5
- TINY = 0
5
+ TINY = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,5 +1,26 @@
1
1
  unless Object.const_defined? "ActiveSupport"
2
2
  class Module
3
+ # Encapsulates the common pattern of:
4
+ #
5
+ # alias_method :foo_without_feature, :foo
6
+ # alias_method :foo, :foo_with_feature
7
+ #
8
+ # With this, you simply do:
9
+ #
10
+ # alias_method_chain :foo, :feature
11
+ #
12
+ # And both aliases are set up for you.
13
+ #
14
+ # Query and bang methods (foo?, foo!) keep the same punctuation:
15
+ #
16
+ # alias_method_chain :foo?, :feature
17
+ #
18
+ # is equivalent to
19
+ #
20
+ # alias_method :foo_without_feature?, :foo?
21
+ # alias_method :foo?, :foo_with_feature?
22
+ #
23
+ # so you can safely chain foo, foo?, and foo! with the same feature.
3
24
  def alias_method_chain(target, feature)
4
25
  # Strip out punctuation on predicates or bang methods since
5
26
  # e.g. target?_without_feature is not a valid method name.
@@ -23,9 +44,24 @@ unless Object.const_defined? "ActiveSupport"
23
44
  end
24
45
  end
25
46
 
26
- module Inflector
47
+ # The Inflector transforms words from singular to plural, class names to
48
+ # table names, modularized class names to ones without, and class names to
49
+ # foreign keys. The default inflections for pluralization, singularization,
50
+ # and uncountable words are kept in inflections.rb.
51
+ module Inflector #:nodoc:
27
52
  extend self
28
53
 
54
+ # By default, camelize converts strings to UpperCamelCase. If the argument
55
+ # to camelize is set to ":lower" then camelize produces lowerCamelCase.
56
+ #
57
+ # camelize will also convert '/' to '::' which is useful for converting
58
+ # paths to namespaces
59
+ #
60
+ # Examples
61
+ # "active_record".camelize #=> "ActiveRecord"
62
+ # "active_record".camelize(:lower) #=> "activeRecord"
63
+ # "active_record/errors".camelize #=> "ActiveRecord::Errors"
64
+ # "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
29
65
  def camelize(lower_case_and_underscored_word,
30
66
  first_letter_in_uppercase = true)
31
67
  if first_letter_in_uppercase
@@ -37,6 +73,14 @@ unless Object.const_defined? "ActiveSupport"
37
73
  end
38
74
  end
39
75
 
76
+ # The reverse of +camelize+. Makes an underscored form from the expression
77
+ # in the string.
78
+ #
79
+ # Changes '::' to '/' to convert namespaces to paths.
80
+ #
81
+ # Examples
82
+ # "ActiveRecord".underscore #=> "active_record"
83
+ # "ActiveRecord::Errors".underscore #=> active_record/errors
40
84
  def underscore(camel_cased_word)
41
85
  camel_cased_word.to_s.gsub(/::/, '/').
42
86
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
@@ -45,6 +89,13 @@ unless Object.const_defined? "ActiveSupport"
45
89
  downcase
46
90
  end
47
91
 
92
+ # Constantize tries to find a declared constant with the name specified
93
+ # in the string. It raises a NameError when the name is not in CamelCase
94
+ # or is not initialized.
95
+ #
96
+ # Examples
97
+ # "Module".constantize #=> Module
98
+ # "Class".constantize #=> Class
48
99
  def constantize(camel_cased_word)
49
100
  unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
50
101
  raise NameError,
@@ -55,10 +106,26 @@ unless Object.const_defined? "ActiveSupport"
55
106
  end
56
107
  end
57
108
 
58
- module ActiveSupport
59
- module CoreExtensions
60
- module String
109
+ module ActiveSupport #:nodoc:
110
+ module CoreExtensions #:nodoc:
111
+ module String #:nodoc:
112
+ # String inflections define new methods on the String class to
113
+ # transform names for different purposes. For instance, you can figure
114
+ # out the name of a database from the name of a class.
115
+ # "ScaleScore".tableize => "scale_scores"
61
116
  module Inflections
117
+ # By default, camelize converts strings to UpperCamelCase. If the
118
+ # argument to camelize is set to ":lower" then camelize produces
119
+ # lowerCamelCase.
120
+ #
121
+ # camelize will also convert '/' to '::' which is useful for
122
+ # converting paths to namespaces
123
+ #
124
+ # Examples
125
+ # "active_record".camelize #=> "ActiveRecord"
126
+ # "active_record".camelize(:lower) #=> "activeRecord"
127
+ # "active_record/errors".camelize #=> "ActiveRecord::Errors"
128
+ # "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
62
129
  def camelize(first_letter = :upper)
63
130
  case first_letter
64
131
  when :upper then Inflector.camelize(self, true)
@@ -67,10 +134,25 @@ unless Object.const_defined? "ActiveSupport"
67
134
  end
68
135
  alias_method :camelcase, :camelize
69
136
 
137
+ # The reverse of +camelize+. Makes an underscored form from the
138
+ # expression in the string.
139
+ #
140
+ # Changes '::' to '/' to convert namespaces to paths.
141
+ #
142
+ # Examples
143
+ # "ActiveRecord".underscore #=> "active_record"
144
+ # "ActiveRecord::Errors".underscore #=> active_record/errors
70
145
  def underscore
71
146
  Inflector.underscore(self)
72
147
  end
73
148
 
149
+ # Create a class name from a table name like Rails does for table
150
+ # names to models. Note that this returns a string and not a Class.
151
+ # (To convert to an actual class follow classify with constantize.)
152
+ #
153
+ # Examples
154
+ # "egg_and_hams".classify #=> "EggAndHam"
155
+ # "post".classify #=> "Post"
74
156
  def constantize
75
157
  Inflector.constantize(self)
76
158
  end