fun_with_patterns 0.0.4 → 0.0.5
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.
- checksums.yaml +8 -8
- data/CHANGELOG.markdown +7 -0
- data/Gemfile +1 -1
- data/README.rdoc +54 -0
- data/VERSION +1 -1
- data/lib/fun_with/patterns/get_and_set.rb +16 -2
- data/lib/fun_with/patterns/get_and_set_api.rb +13 -4
- data/lib/fun_with/patterns/loader/class_methods.rb +182 -0
- data/lib/fun_with/patterns/loader/features/bracketwise_lookup.rb +17 -0
- data/lib/fun_with/patterns/loader/loading_styles/eval.rb +23 -0
- data/lib/fun_with/patterns/loader/loading_styles/instance_exec.rb +24 -0
- data/lib/fun_with/patterns/loader/loading_styles/yaml.rb +27 -0
- data/lib/fun_with/patterns/loader.rb +2 -155
- data/lib/fun_with_patterns.rb +2 -4
- data/test/helper.rb +2 -1
- data/test/test_get_and_set.rb +31 -2
- data/test/test_loader_pattern.rb +14 -6
- data/test/user.rb +25 -1
- data/test/users/{mary.rb → eval/mary.rb} +0 -0
- data/test/users/{more → eval/more}/gary.rb +0 -0
- data/test/users/{steve.rb → eval/steve.rb} +0 -0
- data/test/users/{wanda.rb → eval/wanda.rb} +0 -0
- data/test/users/instance_exec/mary.rb +2 -0
- data/test/users/instance_exec/more/gary.rb +2 -0
- data/test/users/instance_exec/steve.rb +2 -0
- data/test/users/instance_exec/wanda.rb +2 -0
- data/test/users/yaml/mary.yaml +3 -0
- data/test/users/yaml/more/gary.yml +3 -0
- data/test/users/yaml/steve.yaml +3 -0
- data/test/users/yaml/wanda.yml +3 -0
- data/test/yaml_obj.rb +2 -1
- metadata +25 -11
- data/lib/fun_with/patterns/hooks.rb +0 -47
- data/lib/fun_with/patterns/reloadable.rb +0 -33
- data/test/reloadable/my_reloadable.rb +0 -7
- data/test/test_hooks.rb +0 -25
- data/test/test_reloadable_pattern.rb +0 -33
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MTQwYjliNzVlNWFkNmYxMjkxZTU4NTY1NmZjZDEwMmNmMTE5NDU0MQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NDAyMWQ1ZTU4M2JiYjdjODkwMGIwM2IwMjM0YjZlMDYxZGY2M2M5Ng==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YmFkOTUzMWQ2MTViZDEzZjZjYTY2NzBmZDU5OTE2NjQ0NzgxODQ1ZGRjYjYx
|
10
|
+
MWFhMTgxOGMyMWE0MjVkYWRiNDcxOGYxODg4Y2VmMDQ2Y2JiYWY4MGQ1Mjc2
|
11
|
+
MDQxMzBiMTg4OGQ4YzM1YmJjZDcxM2MxYWY5OTZlNTdkYjg1ZWI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
N2EzZjE5OTNlN2YzZmNjMTAwNDE2YTU5NGYxMjFlN2JkMDE1OTJkMjVjNjBl
|
14
|
+
ODQ4YjY0MzI5YWFjYjgxYzg4ZDQ4ZTQ1YWM3OTljY2FlMzllYWZjZTk3ZTZi
|
15
|
+
N2RiNDBlN2EyMjY4ODM0YjU0ZDgxZThjMzM3MTk4MzdhM2VjMmY=
|
data/CHANGELOG.markdown
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
Changelog
|
2
2
|
=========
|
3
3
|
|
4
|
+
0.0.5
|
5
|
+
-----
|
6
|
+
|
7
|
+
Removed Reloadable and Hooks patterns.
|
8
|
+
Added `get_and_set_block()` to GetAndSet (declares or runs a block)
|
9
|
+
`get_and_set*()` methods can now be called in `Class` and `Module` themselves.
|
10
|
+
|
4
11
|
0.0.4
|
5
12
|
-----
|
6
13
|
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -63,9 +63,63 @@ For each directory you give it, it will:
|
|
63
63
|
|
64
64
|
You can get some interesting behavior by overwriting individual methods. For example, loading individual configurations from YAML or JSON or XML. There's an example in the test/ folder.
|
65
65
|
|
66
|
+
The default "style" of loading a file is "eval the contents and return the result." This may not always be the desired behavior. Other built in loading styles:
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
66
72
|
Note: If you want to manage your own registry by redefining loader_pattern_load_from_dir( dir ), loader_pattern_register_item( item ), etc., make sure you handle and report exceptions.
|
67
73
|
|
68
74
|
|
75
|
+
More example code!
|
76
|
+
|
77
|
+
class Klass
|
78
|
+
|
79
|
+
include FunWith::Patterns::Loader
|
80
|
+
loader_pattern_configure( :bracketwise_lookup,
|
81
|
+
:warn_on_key_change,
|
82
|
+
{ :verbose => true, :load } )
|
83
|
+
end
|
84
|
+
|
85
|
+
loader_pattern_configure is a quickie method for setting up a variety of behaviors. Options so far:
|
86
|
+
|
87
|
+
:bracketwise_lookup : instead of calling Klass.loader_pattern_registry_lookup( "string" )
|
88
|
+
you can just call Klass["string"]
|
89
|
+
|
90
|
+
{:key => :<sym>} : loader determines registry key by calling this method
|
91
|
+
|
92
|
+
:warn_on_key_changes : If a newly loaded item has an existing registry key, it prints a warning as it overwrites
|
93
|
+
|
94
|
+
:dont_warn_on_key_changes : (default)
|
95
|
+
:style => :(eval|instance_exec|yaml) : Use one of the built-in loading styles. Otherwise, set custom loading
|
96
|
+
behavior by defining your own Klass.loader_pattern_register_item
|
97
|
+
|
98
|
+
|
99
|
+
The styles need some explanation:
|
100
|
+
|
101
|
+
:eval (default) : evals the contents of the file and returns the result
|
102
|
+
|
103
|
+
:instance_exec : The file contents will run as though inside the object
|
104
|
+
|
105
|
+
Klass.new do
|
106
|
+
# ----- file contents for boris.rb starts here
|
107
|
+
self.name "Boris"
|
108
|
+
self.age 23
|
109
|
+
self.kill_count 9
|
110
|
+
# ----- file contents for boris.rb end here
|
111
|
+
end
|
112
|
+
|
113
|
+
:yaml The file contents describe a YAML hash, with the topmost keys describing
|
114
|
+
the setter to call in order to set the attribute to that value. For example,
|
115
|
+
|
116
|
+
---
|
117
|
+
- name: Boris
|
118
|
+
|
119
|
+
would end up calling klass_object.name=( "Boris" )
|
120
|
+
|
121
|
+
|
122
|
+
TODO: I feel like a default label should maybe come out of the filename (zero-config and all that), but haven't built it.
|
69
123
|
|
70
124
|
== Reloadable ==
|
71
125
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.5
|
@@ -17,8 +17,7 @@ module FunWith
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# Would also like to do a boolean version which creates
|
20
|
-
# .bool? .bool! and .not_bool!
|
21
|
-
|
20
|
+
# .bool? .bool! and .not_bool!
|
22
21
|
def get_and_set_boolean( *method_names )
|
23
22
|
for name in method_names
|
24
23
|
if self.is_a?(Class) || self.is_a?(Module)
|
@@ -45,6 +44,21 @@ module FunWith
|
|
45
44
|
end
|
46
45
|
end
|
47
46
|
end
|
47
|
+
|
48
|
+
# the name() method can be called with a block (to change the block that is to be executed)
|
49
|
+
# or called with args to get the results of the block. Uses the internal object variable @name
|
50
|
+
def get_and_set_block( name, *args, &block )
|
51
|
+
eval "define_method( :#{name} ) do |*args, &block|
|
52
|
+
if block.is_a?( Proc ) # oddly, block_given? always returns false when defined this way
|
53
|
+
raise ArgumentError.new( 'Call #{name}() with either a block or args' ) unless args.length == 0
|
54
|
+
self.instance_variable_set( :@#{name}, block )
|
55
|
+
block
|
56
|
+
else
|
57
|
+
block = self.instance_variable_get( :@#{name} )
|
58
|
+
( block || Proc.new{} ).call( *args )
|
59
|
+
end
|
60
|
+
end "
|
61
|
+
end
|
48
62
|
end
|
49
63
|
end
|
50
64
|
end
|
@@ -1,11 +1,20 @@
|
|
1
1
|
module FunWith
|
2
2
|
module Patterns
|
3
3
|
module GetAndSetAPI
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
# Can pass in an array listing the classes to activate, or just pass in arguments
|
5
|
+
def activate( *classes_to_activate )
|
6
|
+
if classes_to_activate.length == 1 && classes_to_activate.first.is_a?(Array)
|
7
|
+
classes_to_activate = classes_to_activate.first
|
8
|
+
elsif classes_to_activate.length == 0
|
9
|
+
classes_to_activate = [Class, Module] # no arguments given
|
10
|
+
end
|
11
|
+
|
7
12
|
for klass in classes_to_activate
|
8
|
-
klass
|
13
|
+
if klass == Class || klass == Module
|
14
|
+
klass.send( :include, GetAndSet ) # Because individual classes or modules are objects of class Class/Module
|
15
|
+
end
|
16
|
+
|
17
|
+
klass.send( :extend, GetAndSet )
|
9
18
|
end
|
10
19
|
end
|
11
20
|
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module FunWith
|
2
|
+
module Patterns
|
3
|
+
module Loader
|
4
|
+
module ClassMethods
|
5
|
+
# By default, looks for .rb files to evaluate
|
6
|
+
# ext => :all (or "*") to load every file from directory.
|
7
|
+
# ext => :rb (or "rb") to load all .rb files (this is default)
|
8
|
+
# call without arguments to inquire about current extension.
|
9
|
+
def loader_pattern_extension( ext = nil )
|
10
|
+
case ext
|
11
|
+
when nil
|
12
|
+
# do nothing
|
13
|
+
when "*", :all
|
14
|
+
@loader_pattern_extension = "*"
|
15
|
+
else
|
16
|
+
@loader_pattern_extension = ext
|
17
|
+
end
|
18
|
+
|
19
|
+
@loader_pattern_extension
|
20
|
+
end
|
21
|
+
|
22
|
+
def loader_pattern_verbose( verbosity = nil )
|
23
|
+
@loader_pattern_verbose = verbosity unless verbosity.nil?
|
24
|
+
@loader_pattern_verbose
|
25
|
+
end
|
26
|
+
|
27
|
+
def loader_pattern_rescue_failing_item_load( file, &block )
|
28
|
+
file = file.fwf_filepath.expand
|
29
|
+
if file.file?
|
30
|
+
obj = yield
|
31
|
+
STDOUT.puts( "Loaded file #{file}" ) if self.loader_pattern_verbose
|
32
|
+
|
33
|
+
obj
|
34
|
+
else
|
35
|
+
STDERR.puts( "(#{self.class}) Load failed, no such file: #{file}" )
|
36
|
+
end
|
37
|
+
rescue Exception => e
|
38
|
+
STDERR.puts( "Could not load file #{file}. Reason: #{e.class.name} #{e.message}" )
|
39
|
+
|
40
|
+
if self.loader_pattern_verbose
|
41
|
+
STDERR.puts( puts e.backtrace.map{|line| "\t\t#{line}"}.join("\n") )
|
42
|
+
STDERR.puts( "\n" )
|
43
|
+
end
|
44
|
+
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
# Default, may want to override how the registry behaves.
|
49
|
+
# If you don't provide a key argument, then the object needs to
|
50
|
+
# respond to .loader_pattern_registry_key()
|
51
|
+
def loader_pattern_register_item( obj, key = nil )
|
52
|
+
return nil if obj.nil?
|
53
|
+
@loader_pattern_registry ||= {}
|
54
|
+
|
55
|
+
if key.nil?
|
56
|
+
if obj.respond_to?( :loader_pattern_registry_key )
|
57
|
+
key = obj.loader_pattern_registry_key
|
58
|
+
else
|
59
|
+
raise "#{self.class} not registered. No registry key given, and object does not respond to .loader_pattern_registry_key()."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if loader_pattern_is_item_registerable?( obj )
|
64
|
+
if @loader_pattern_warn_on_key_changes && loader_pattern_registry_lookup( key )
|
65
|
+
warn( "class #{self} is replacing lookup key #{key.inspect}" )
|
66
|
+
end
|
67
|
+
|
68
|
+
return @loader_pattern_registry[ key ] = obj
|
69
|
+
else
|
70
|
+
warn( "#{obj} is not an instance of a registerable class. Registerable classes: #{self.loader_pattern_only_register_classes.inspect}" )
|
71
|
+
return nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def loader_pattern_registry_lookup( key )
|
76
|
+
@loader_pattern_registry ||= {}
|
77
|
+
@loader_pattern_registry[key]
|
78
|
+
end
|
79
|
+
|
80
|
+
def loader_pattern_registry
|
81
|
+
@loader_pattern_registry
|
82
|
+
end
|
83
|
+
|
84
|
+
def loader_pattern_only_register_classes( *args )
|
85
|
+
if args.length > 0
|
86
|
+
@loader_pattern_only_register_classes = args
|
87
|
+
end
|
88
|
+
|
89
|
+
@loader_pattern_only_register_classes || []
|
90
|
+
end
|
91
|
+
|
92
|
+
def loader_pattern_is_item_registerable?( item )
|
93
|
+
return true if loader_pattern_only_register_classes.fwf_blank?
|
94
|
+
|
95
|
+
for klass in @loader_pattern_only_register_classes
|
96
|
+
return true if item.is_a?( klass )
|
97
|
+
end
|
98
|
+
|
99
|
+
return false
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
# Assumes that every file in the directory and subdirectories contain ruby code that
|
104
|
+
# will yield an object that the loader is looking for. It also automatically
|
105
|
+
# adds the resulting object to a registry.
|
106
|
+
# You may want to override this if you're looking for different behavior.
|
107
|
+
def loader_pattern_load_from_dir( *dirs )
|
108
|
+
for dir in dirs
|
109
|
+
dir = dir.fwf_filepath
|
110
|
+
@loader_pattern_directories ||= []
|
111
|
+
@loader_pattern_directories << dir
|
112
|
+
|
113
|
+
for file in dir.glob( :ext => self.loader_pattern_extension, :recurse => true )
|
114
|
+
obj = self.loader_pattern_load_item( file )
|
115
|
+
self.loader_pattern_register_item( obj ) if self.loader_pattern_is_item_registerable?( obj )
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def loader_pattern_loaded_directories
|
121
|
+
@loader_pattern_directories ||= []
|
122
|
+
end
|
123
|
+
|
124
|
+
# Handle the initial configuration of the class.
|
125
|
+
#
|
126
|
+
# :bracketwise_lookup : Instead of looking up an object by LoaderBearingClass.loader_pattern_registry_lookup(:keyword)
|
127
|
+
# you can simply use LoaderBearingClass[:keyword]
|
128
|
+
#
|
129
|
+
# :warn_on_key_changes : Warns whenever the registry overwrites an existing key. Useful for debugging, sometimes.
|
130
|
+
#
|
131
|
+
# Some configuration directives are given as hashes. Multiple directives may be combined into
|
132
|
+
# a single hash.
|
133
|
+
#
|
134
|
+
# {:key => <METHOD_SYM>} : The method for asking the object what name it should be lookupable under.
|
135
|
+
#
|
136
|
+
# {:verbose => (true|false)} : How noisy do you want the loading to be?
|
137
|
+
#
|
138
|
+
# {:style => :eval}
|
139
|
+
#
|
140
|
+
# {:style => :instance_exec}
|
141
|
+
#
|
142
|
+
# {:style => <PROC>}
|
143
|
+
|
144
|
+
def loader_pattern_configure( *args )
|
145
|
+
for arg in args
|
146
|
+
case arg
|
147
|
+
when :bracketwise_lookup
|
148
|
+
self.extend( Features::BracketwiseLookup )
|
149
|
+
when :warn_on_key_changes
|
150
|
+
@loader_pattern_warn_on_key_changes = true
|
151
|
+
when :dont_warn_on_key_changes
|
152
|
+
@loader_pattern_warn_on_key_changes = false
|
153
|
+
when Hash
|
154
|
+
for key, val in arg
|
155
|
+
case key
|
156
|
+
when :key
|
157
|
+
self.class_eval do
|
158
|
+
eval( "alias :loader_pattern_registry_key #{val.to_sym.inspect}" )
|
159
|
+
end
|
160
|
+
when :verbose
|
161
|
+
self.loader_pattern_verbose( val )
|
162
|
+
when :style
|
163
|
+
case val # styles allowed: :eval, :instance_exec
|
164
|
+
when :eval
|
165
|
+
self.extend( LoadingStyles::Eval )
|
166
|
+
when :instance_exec
|
167
|
+
self.extend( LoadingStyles::InstanceExec )
|
168
|
+
when :yaml
|
169
|
+
self.extend( LoadingStyles::YAML )
|
170
|
+
self.loader_pattern_extension( [:yml, :yaml] )
|
171
|
+
else
|
172
|
+
raise "Unknown Loader loading style: #{val.inspect}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module FunWith
|
2
|
+
module Patterns
|
3
|
+
module Loader
|
4
|
+
module Features
|
5
|
+
module BracketwiseLookup
|
6
|
+
def []( key )
|
7
|
+
loader_pattern_registry_lookup( key )
|
8
|
+
end
|
9
|
+
|
10
|
+
def []=( key, val )
|
11
|
+
loader_pattern_register_item( val, key )
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module FunWith
|
2
|
+
module Patterns
|
3
|
+
module Loader
|
4
|
+
module LoadingStyles
|
5
|
+
module Eval
|
6
|
+
# Default behavior: read the file, evaluate it, expect a ruby object
|
7
|
+
# of the class that the loader pattern is installed on. If anything goes
|
8
|
+
# wrong (file no exist, syntax error), returns a nil.
|
9
|
+
#
|
10
|
+
# Override in your class if you need your files translated
|
11
|
+
# into objects differently.
|
12
|
+
def loader_pattern_load_item( file )
|
13
|
+
self.loader_pattern_rescue_failing_item_load( file ) do
|
14
|
+
obj = eval( file.read )
|
15
|
+
|
16
|
+
return obj
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module FunWith
|
2
|
+
module Patterns
|
3
|
+
module Loader
|
4
|
+
module LoadingStyles
|
5
|
+
# Assumes the class does not take arguments in its initialize() method.
|
6
|
+
# The contents of the file are run via instance_exec to configure the object.
|
7
|
+
module InstanceExec
|
8
|
+
def loader_pattern_load_item( file )
|
9
|
+
self.loader_pattern_rescue_failing_item_load( file ) do
|
10
|
+
obj = self.new
|
11
|
+
|
12
|
+
# obj.instance_eval( file.read )
|
13
|
+
obj.instance_exec do
|
14
|
+
eval( file.read )
|
15
|
+
end
|
16
|
+
|
17
|
+
return obj
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module FunWith
|
2
|
+
module Patterns
|
3
|
+
module Loader
|
4
|
+
module LoadingStyles
|
5
|
+
module YAML
|
6
|
+
def loader_pattern_load_item( file )
|
7
|
+
self.loader_pattern_rescue_failing_item_load( file ) do
|
8
|
+
obj = self.new
|
9
|
+
|
10
|
+
hash = Psych.load( file.read )
|
11
|
+
|
12
|
+
for method, val in hash
|
13
|
+
eq_method = :"#{method}="
|
14
|
+
|
15
|
+
if obj.respond_to?( eq_method )
|
16
|
+
obj.send( eq_method, val )
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
return obj
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -3,161 +3,8 @@ module FunWith
|
|
3
3
|
module Loader
|
4
4
|
def self.included( base )
|
5
5
|
base.extend( ClassMethods )
|
6
|
-
base.
|
7
|
-
|
8
|
-
|
9
|
-
module ClassMethods
|
10
|
-
# By default, looks for .rb files to evaluate
|
11
|
-
# ext => :all (or "*") to load every file from directory.
|
12
|
-
# ext => :rb (or "rb") to load all .rb files (this is default)
|
13
|
-
# call without arguments to inquire about current extension.
|
14
|
-
def loader_pattern_extension( ext = nil )
|
15
|
-
case ext
|
16
|
-
when nil
|
17
|
-
# do nothing
|
18
|
-
when "*", :all
|
19
|
-
@loader_pattern_extension = "*"
|
20
|
-
else
|
21
|
-
@loader_pattern_extension = "*.#{ext}"
|
22
|
-
end
|
23
|
-
|
24
|
-
@loader_pattern_extension
|
25
|
-
end
|
26
|
-
|
27
|
-
def loader_pattern_verbose( verbosity = nil )
|
28
|
-
@loader_pattern_verbose = verbosity unless verbosity.nil?
|
29
|
-
@loader_pattern_verbose
|
30
|
-
end
|
31
|
-
|
32
|
-
# Default behavior: read the file, evaluate it, expect a ruby object
|
33
|
-
# of the class that the loader pattern is installed on. If anything goes
|
34
|
-
# wrong (file no exist, syntax error), returns a nil.
|
35
|
-
#
|
36
|
-
# Override in your class if you need your files translated
|
37
|
-
# into objects differently.
|
38
|
-
def loader_pattern_load_item( file )
|
39
|
-
file = file.fwf_filepath
|
40
|
-
if file.file?
|
41
|
-
obj = eval( file.read )
|
42
|
-
|
43
|
-
STDOUT.puts( "Loaded file #{file}" ) if self.loader_pattern_verbose
|
44
|
-
return obj
|
45
|
-
else
|
46
|
-
STDERR.puts( "(#{self.class}) Load failed, no such file: #{file}" )
|
47
|
-
return nil
|
48
|
-
end
|
49
|
-
rescue Exception => e
|
50
|
-
STDERR.puts( "Could not load file #{file}. Reason: #{e.class.name} #{e.message}" )
|
51
|
-
|
52
|
-
if self.loader_pattern_verbose
|
53
|
-
STDERR.puts( puts e.backtrace.map{|line| "\t\t#{line}"}.join("\n") )
|
54
|
-
STDERR.puts( "\n" )
|
55
|
-
end
|
56
|
-
|
57
|
-
nil
|
58
|
-
end
|
59
|
-
|
60
|
-
# Default, may want to override how the registry behaves.
|
61
|
-
# If you don't provide a key argument, then the object needs to
|
62
|
-
# respond to .loader_pattern_registry_key()
|
63
|
-
def loader_pattern_register_item( obj, key = nil )
|
64
|
-
return nil if obj.nil?
|
65
|
-
@loader_pattern_registry ||= {}
|
66
|
-
|
67
|
-
key = obj.loader_pattern_registry_key if key.nil?
|
68
|
-
|
69
|
-
if loader_pattern_is_item_registerable?( obj )
|
70
|
-
if @loader_pattern_warn_on_key_changes && loader_pattern_registry_lookup( key )
|
71
|
-
warn( "class #{self} is replacing lookup key #{key.inspect}" )
|
72
|
-
end
|
73
|
-
|
74
|
-
return @loader_pattern_registry[ key ] = obj
|
75
|
-
else
|
76
|
-
warn( "#{obj} is not an instance of a registerable class. Registerable classes: #{self.loader_pattern_only_register_classes.inspect}" )
|
77
|
-
return nil
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def loader_pattern_registry_lookup( key )
|
82
|
-
@loader_pattern_registry ||= {}
|
83
|
-
@loader_pattern_registry[key]
|
84
|
-
end
|
85
|
-
|
86
|
-
def loader_pattern_registry
|
87
|
-
@loader_pattern_registry
|
88
|
-
end
|
89
|
-
|
90
|
-
def loader_pattern_only_register_classes( *args )
|
91
|
-
if args.length > 0
|
92
|
-
@loader_pattern_only_register_classes = args
|
93
|
-
end
|
94
|
-
|
95
|
-
@loader_pattern_only_register_classes || []
|
96
|
-
end
|
97
|
-
|
98
|
-
def loader_pattern_is_item_registerable?( item )
|
99
|
-
return true if loader_pattern_only_register_classes.fwf_blank?
|
100
|
-
|
101
|
-
for klass in @loader_pattern_only_register_classes
|
102
|
-
return true if item.is_a?( klass )
|
103
|
-
end
|
104
|
-
|
105
|
-
return false
|
106
|
-
end
|
107
|
-
|
108
|
-
|
109
|
-
# Assumes that every file in the directory and subdirectories contain ruby code that
|
110
|
-
# will yield an object that the loader is looking for. It also automatically
|
111
|
-
# adds the resulting object to a registry.
|
112
|
-
# You may want to override this if you're looking for different behavior.
|
113
|
-
def loader_pattern_load_from_dir( *dirs )
|
114
|
-
for dir in dirs
|
115
|
-
dir = dir.fwf_filepath
|
116
|
-
@loader_pattern_directories ||= []
|
117
|
-
@loader_pattern_directories << dir
|
118
|
-
|
119
|
-
for file in dir.glob( "**", self.loader_pattern_extension )
|
120
|
-
obj = self.loader_pattern_load_item( file )
|
121
|
-
self.loader_pattern_register_item( obj ) if self.loader_pattern_is_item_registerable?( obj )
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def loader_pattern_loaded_directories
|
127
|
-
@loader_pattern_directories ||= []
|
128
|
-
end
|
129
|
-
|
130
|
-
def loader_pattern_configure( *args )
|
131
|
-
for arg in args
|
132
|
-
case arg
|
133
|
-
when :bracketwise_lookup
|
134
|
-
self.class_eval do
|
135
|
-
def self.[]( key )
|
136
|
-
loader_pattern_registry_lookup( key )
|
137
|
-
end
|
138
|
-
|
139
|
-
def self.[]=( key, val )
|
140
|
-
loader_pattern_register_item( val, key )
|
141
|
-
end
|
142
|
-
end
|
143
|
-
when :warn_on_key_changes
|
144
|
-
@loader_pattern_warn_on_key_changes = true
|
145
|
-
when :dont_warn_on_key_changes
|
146
|
-
@loader_pattern_warn_on_key_changes = false
|
147
|
-
when Hash
|
148
|
-
for key, val in arg
|
149
|
-
case key
|
150
|
-
when :key
|
151
|
-
self.class_eval do
|
152
|
-
eval( "alias :loader_pattern_registry_key #{val.inspect}" )
|
153
|
-
end
|
154
|
-
when :verbose
|
155
|
-
self.loader_pattern_verbose( val )
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
6
|
+
base.extend( LoadingStyles::Eval ) # provides a default load_item method
|
7
|
+
base.loader_pattern_extension( :rb ) if base.loader_pattern_extension.nil?
|
161
8
|
end
|
162
9
|
end
|
163
10
|
end
|
data/lib/fun_with_patterns.rb
CHANGED
@@ -2,9 +2,7 @@ require 'fun_with_gems'
|
|
2
2
|
|
3
3
|
FunWith::Gems.make_gem_fun( "FunWith::Patterns" )
|
4
4
|
|
5
|
-
Class.send( :include, FunWith::Patterns::MakeInstancesReloadable )
|
6
|
-
Module.send( :include, FunWith::Patterns::MakeInstancesReloadable )
|
7
|
-
FunWith::Patterns::Reloadable.extend( FunWith::Patterns::ClassReloaderMethod )
|
8
5
|
|
9
6
|
# Activate Object#get_and_set / Module#get_and_set by calling GetAndSet.activate
|
10
|
-
FunWith::Patterns::GetAndSet.extend( FunWith::Patterns::GetAndSetAPI )
|
7
|
+
FunWith::Patterns::GetAndSet.extend( FunWith::Patterns::GetAndSetAPI )
|
8
|
+
|
data/test/helper.rb
CHANGED
@@ -15,8 +15,9 @@
|
|
15
15
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
16
16
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
17
17
|
|
18
|
-
require 'fun_with_testing'
|
19
18
|
require 'fun_with_patterns'
|
19
|
+
require 'fun_with_testing'
|
20
|
+
|
20
21
|
|
21
22
|
require_relative 'user'
|
22
23
|
require_relative 'yaml_obj'
|
data/test/test_get_and_set.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
FunWith::Patterns::GetAndSet.activate
|
4
|
-
|
5
3
|
class TestGetAndSet < FunWith::Patterns::TestCase
|
6
4
|
context "basics" do
|
7
5
|
should "be plumbed correctly" do
|
@@ -12,6 +10,8 @@ class TestGetAndSet < FunWith::Patterns::TestCase
|
|
12
10
|
context "trial run" do
|
13
11
|
should "get and set" do
|
14
12
|
c = Class.new
|
13
|
+
FunWith::Patterns::GetAndSet.activate( c )
|
14
|
+
|
15
15
|
c.get_and_set( :radio, :radius, :radium )
|
16
16
|
c.get_and_set( :radiate )
|
17
17
|
c.get_and_set_boolean( :stringy, :flurmish )
|
@@ -46,6 +46,8 @@ class TestGetAndSet < FunWith::Patterns::TestCase
|
|
46
46
|
m = Module.new
|
47
47
|
c = Class.new
|
48
48
|
|
49
|
+
FunWith::Patterns::GetAndSet.activate( m )
|
50
|
+
|
49
51
|
c.send( :include, m )
|
50
52
|
|
51
53
|
m.get_and_set( :radio, :radius, :radium )
|
@@ -73,5 +75,32 @@ class TestGetAndSet < FunWith::Patterns::TestCase
|
|
73
75
|
assert_equal v2, o.radium( v2 ) # ==> "Madame Curie"
|
74
76
|
assert_equal v2, o.radium() # ==> "Madame Curie"
|
75
77
|
end
|
78
|
+
|
79
|
+
should "get and set blocks" do
|
80
|
+
c = Class.new
|
81
|
+
FunWith::Patterns::GetAndSet.activate( c )
|
82
|
+
c.get_and_set_block( :string_transformation )
|
83
|
+
|
84
|
+
doubler = c.new
|
85
|
+
assert_respond_to( doubler, :string_transformation )
|
86
|
+
|
87
|
+
assert_nil doubler.string_transformation( "hello" )
|
88
|
+
|
89
|
+
doubler.string_transformation do |input|
|
90
|
+
"#{input}#{input}"
|
91
|
+
end
|
92
|
+
|
93
|
+
assert_equal "hellohello", doubler.string_transformation( "hello" )
|
94
|
+
assert_equal "55", doubler.string_transformation( "5" )
|
95
|
+
|
96
|
+
stripper = c.new
|
97
|
+
stripper.string_transformation do |input|
|
98
|
+
input.strip
|
99
|
+
end
|
100
|
+
|
101
|
+
assert_equal "stripped", stripper.string_transformation( " stripped ")
|
102
|
+
|
103
|
+
|
104
|
+
end
|
76
105
|
end
|
77
106
|
end
|
data/test/test_loader_pattern.rb
CHANGED
@@ -11,8 +11,12 @@ class TestLoaderPattern < FunWith::Patterns::TestCase
|
|
11
11
|
|
12
12
|
context "testing User" do
|
13
13
|
setup do
|
14
|
-
User.loader_pattern_load_from_dir( FunWith::Patterns.root( "test", "users" ) )
|
15
|
-
User2.loader_pattern_load_from_dir( FunWith::Patterns.root( "test", "users" ) )
|
14
|
+
User.loader_pattern_load_from_dir( FunWith::Patterns.root( "test", "users", "eval" ) )
|
15
|
+
User2.loader_pattern_load_from_dir( FunWith::Patterns.root( "test", "users", "eval" ) )
|
16
|
+
User3.loader_pattern_load_from_dir( FunWith::Patterns.root( "test", "users", "instance_exec" ) )
|
17
|
+
User4.loader_pattern_load_from_dir( FunWith::Patterns.root( "test", "users", "yaml" ) )
|
18
|
+
|
19
|
+
@user_classes = [User, User2, User3, User4]
|
16
20
|
end
|
17
21
|
|
18
22
|
should "have all the right methods" do
|
@@ -22,13 +26,17 @@ class TestLoaderPattern < FunWith::Patterns::TestCase
|
|
22
26
|
:loader_pattern_register_item,
|
23
27
|
:loader_pattern_load_from_dir,
|
24
28
|
:loader_pattern_configure ]
|
25
|
-
|
29
|
+
for klass in @user_classes
|
30
|
+
assert_respond_to( klass, method, "#{klass} should respond to ##{method}" )
|
31
|
+
end
|
26
32
|
end
|
27
33
|
end
|
28
34
|
|
29
|
-
should "load users from test/users
|
30
|
-
|
31
|
-
|
35
|
+
should "load users from test/users into various classes" do
|
36
|
+
for klass in @user_classes
|
37
|
+
assert klass.loader_pattern_registry_lookup("Gary Milhouse"), "#{klass} did not load Gary. Poor Gary."
|
38
|
+
assert_equal 54, klass.loader_pattern_registry_lookup("Gary Milhouse").age, "#{klass} did not load Gary with proper age data."
|
39
|
+
end
|
32
40
|
end
|
33
41
|
|
34
42
|
should "lookup via brackets" do
|
data/test/user.rb
CHANGED
@@ -22,4 +22,28 @@ class User2
|
|
22
22
|
@name = name
|
23
23
|
@age = age
|
24
24
|
end
|
25
|
-
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class User3
|
28
|
+
FunWith::Patterns::GetAndSet.activate( self )
|
29
|
+
|
30
|
+
get_and_set :name, :age
|
31
|
+
|
32
|
+
include FunWith::Patterns::Loader
|
33
|
+
loader_pattern_configure( :bracketwise_lookup,
|
34
|
+
{ :key => :name },
|
35
|
+
{ :style => :instance_exec } # Create object, run code in configuration file inside the object's context.
|
36
|
+
)
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
class User4
|
41
|
+
attr_accessor :name, :age
|
42
|
+
|
43
|
+
include FunWith::Patterns::Loader
|
44
|
+
loader_pattern_configure( :bracketwise_lookup,
|
45
|
+
{ :key => :name },
|
46
|
+
{ :style => :yaml }
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/test/yaml_obj.rb
CHANGED
metadata
CHANGED
@@ -1,19 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fun_with_patterns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bryce Anderson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fun_with_gems
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.2
|
17
20
|
- - ~>
|
18
21
|
- !ruby/object:Gem::Version
|
19
22
|
version: '0.0'
|
@@ -21,6 +24,9 @@ dependencies:
|
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.0.2
|
24
30
|
- - ~>
|
25
31
|
- !ruby/object:Gem::Version
|
26
32
|
version: '0.0'
|
@@ -48,21 +54,29 @@ extra_rdoc_files:
|
|
48
54
|
files:
|
49
55
|
- ./lib/fun_with/patterns/get_and_set.rb
|
50
56
|
- ./lib/fun_with/patterns/get_and_set_api.rb
|
51
|
-
- ./lib/fun_with/patterns/hooks.rb
|
52
57
|
- ./lib/fun_with/patterns/loader.rb
|
53
|
-
- ./lib/fun_with/patterns/
|
58
|
+
- ./lib/fun_with/patterns/loader/class_methods.rb
|
59
|
+
- ./lib/fun_with/patterns/loader/features/bracketwise_lookup.rb
|
60
|
+
- ./lib/fun_with/patterns/loader/loading_styles/eval.rb
|
61
|
+
- ./lib/fun_with/patterns/loader/loading_styles/instance_exec.rb
|
62
|
+
- ./lib/fun_with/patterns/loader/loading_styles/yaml.rb
|
54
63
|
- ./lib/fun_with_patterns.rb
|
55
64
|
- ./test/helper.rb
|
56
|
-
- ./test/reloadable/my_reloadable.rb
|
57
65
|
- ./test/test_get_and_set.rb
|
58
|
-
- ./test/test_hooks.rb
|
59
66
|
- ./test/test_loader_pattern.rb
|
60
|
-
- ./test/test_reloadable_pattern.rb
|
61
67
|
- ./test/user.rb
|
62
|
-
- ./test/users/mary.rb
|
63
|
-
- ./test/users/more/gary.rb
|
64
|
-
- ./test/users/steve.rb
|
65
|
-
- ./test/users/wanda.rb
|
68
|
+
- ./test/users/eval/mary.rb
|
69
|
+
- ./test/users/eval/more/gary.rb
|
70
|
+
- ./test/users/eval/steve.rb
|
71
|
+
- ./test/users/eval/wanda.rb
|
72
|
+
- ./test/users/instance_exec/mary.rb
|
73
|
+
- ./test/users/instance_exec/more/gary.rb
|
74
|
+
- ./test/users/instance_exec/steve.rb
|
75
|
+
- ./test/users/instance_exec/wanda.rb
|
76
|
+
- ./test/users/yaml/mary.yaml
|
77
|
+
- ./test/users/yaml/more/gary.yml
|
78
|
+
- ./test/users/yaml/steve.yaml
|
79
|
+
- ./test/users/yaml/wanda.yml
|
66
80
|
- ./test/yaml_obj.rb
|
67
81
|
- ./test/yamls/mike/mike_amazon.yaml
|
68
82
|
- ./test/yamls/mike/mike_gmail.yaml
|
@@ -1,47 +0,0 @@
|
|
1
|
-
module FunWith
|
2
|
-
module Patterns
|
3
|
-
module Hooks
|
4
|
-
def add_hook_before( method, &block )
|
5
|
-
hook_pattern_install_hooks_for_method( method )
|
6
|
-
end
|
7
|
-
|
8
|
-
def add_hook_after( method, &block )
|
9
|
-
hook_pattern_install_hooks_for_method( method )
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def hook_pattern_hooks_installed_for_method?( method )
|
14
|
-
self.respond_to?( :"#{method}_method_without_hooks" )
|
15
|
-
end
|
16
|
-
|
17
|
-
def hook_pattern_install_hooks_for_method( method )
|
18
|
-
return false if hook_pattern_hooks_installed_for_method?( method )
|
19
|
-
|
20
|
-
alias :"#{method}_method_without_hooks" :"#{method}"
|
21
|
-
|
22
|
-
@hook_pattern_hooks ||= {}
|
23
|
-
@hook_pattern_hooks[method] ||= {}
|
24
|
-
@hook_pattern_hooks[method][:before] ||= []
|
25
|
-
@hook_pattern_hooks[method][:after] ||= []
|
26
|
-
|
27
|
-
|
28
|
-
# http://stackoverflow.com/questions/4470108/when-monkey-patching-a-method-can-you-call-the-overridden-method-from-the-new-i
|
29
|
-
old_method = instance_method(method)
|
30
|
-
|
31
|
-
# define_method(:bar) do
|
32
|
-
# old_bar.bind(self).() + ' World'
|
33
|
-
# end
|
34
|
-
|
35
|
-
define_method( method ) do |*args, &block|
|
36
|
-
self.hook_pattern_run( @hook_pattern_hooks[method][:before], *args, &block )
|
37
|
-
old_method.bind( self ).call( *args, &block )
|
38
|
-
self.hook_pattern_run( @hook_pattern_hooks[method][:after], *args, &block )
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def hook_pattern_run( hookset, *args, &block )
|
43
|
-
puts "running hookset"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
warn( "Something wrong with the Reloadable class. Flaky. Not recommended for use." )
|
2
|
-
module FunWith
|
3
|
-
module Patterns
|
4
|
-
# A bare-bones reloading system. Useful when the entire file defines exactly one
|
5
|
-
# class or module, with no dependencies or side-effects.
|
6
|
-
module Reloadable
|
7
|
-
def reload!
|
8
|
-
FunWith::Patterns::Reloadable.reload_class( self )
|
9
|
-
end
|
10
|
-
|
11
|
-
def reloader_filepath
|
12
|
-
@reloader_filepath
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
module MakeInstancesReloadable
|
17
|
-
def reloadable!
|
18
|
-
self.extend( FunWith::Patterns::Reloadable )
|
19
|
-
kaller = caller.first.gsub(/:\d+:in.*/, '')
|
20
|
-
@reloader_filepath = kaller.fwf_filepath.expand
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
module ClassReloaderMethod
|
25
|
-
def reload_class( klass )
|
26
|
-
if file = klass.reloader_filepath
|
27
|
-
Object.send( :remove_const, klass.name.to_sym )
|
28
|
-
file.load
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
data/test/test_hooks.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
|
3
|
-
class TestHooksPattern < FunWith::Patterns::TestCase
|
4
|
-
_context "first try" do
|
5
|
-
should "set up a class with hooks" do
|
6
|
-
class A
|
7
|
-
include FunWith::Patterns::Hooks
|
8
|
-
|
9
|
-
def called_x_times()
|
10
|
-
@called_x_times ||= 0
|
11
|
-
@called_x_times
|
12
|
-
end
|
13
|
-
|
14
|
-
add_hook_before( :called_x_times ) do
|
15
|
-
@called_x_times ||= 0
|
16
|
-
@called_x_times += 1
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
a = A.new
|
21
|
-
|
22
|
-
assert_one( a.called_x_times() )
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
|
3
|
-
class TestReloadablePattern < FunWith::Patterns::TestCase
|
4
|
-
context "testing basics" do
|
5
|
-
setup do
|
6
|
-
Class.send( :include, FunWith::Patterns::Reloadable )
|
7
|
-
assert_has_instance_method( Class, :reload! )
|
8
|
-
assert_has_instance_method( Class, :reloadable! )
|
9
|
-
|
10
|
-
assert_respond_to( Object, :reload!)
|
11
|
-
assert_respond_to( Object, :reloadable!)
|
12
|
-
end
|
13
|
-
|
14
|
-
should "reload MyReloadable" do
|
15
|
-
refute defined?(MyReloadable)
|
16
|
-
FunWith::Patterns.root( "test", "reloadable", "my_reloadable.rb" ).requir
|
17
|
-
assert defined?(MyReloadable)
|
18
|
-
assert_respond_to( MyReloadable.new, :square )
|
19
|
-
|
20
|
-
MyReloadable.class_eval do
|
21
|
-
remove_method :square
|
22
|
-
end
|
23
|
-
|
24
|
-
refute_respond_to( MyReloadable.new, :square )
|
25
|
-
|
26
|
-
MyReloadable.reload!
|
27
|
-
|
28
|
-
assert defined?(MyReloadable)
|
29
|
-
|
30
|
-
assert_respond_to( MyReloadable.new, :square )
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|