morph 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/README +53 -21
- data/lib/morph.rb +50 -44
- data/morph.gemspec +6 -6
- data/spec/morph_spec.rb +32 -22
- metadata +5 -5
data/CHANGELOG
CHANGED
data/README
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
Morph allows you to emerge class definitions via calling assignment methods.
|
2
|
-
Mix with Hpricot for screen scrapping fun.
|
1
|
+
Morph allows you to emerge class definitions via calling assignment methods; mix with Hpricot for screen scrapping fun.
|
3
2
|
|
4
|
-
|
3
|
+
|
4
|
+
|
5
|
+
== Morph example with Hpricot
|
5
6
|
|
6
7
|
Here's example code showing Morph playing with Hpricot:
|
7
8
|
|
@@ -17,7 +18,7 @@ Here's example code showing Morph playing with Hpricot:
|
|
17
18
|
label = node.inner_text
|
18
19
|
value = node.next_sibling.inner_text.strip
|
19
20
|
|
20
|
-
morph(label, value)
|
21
|
+
morph(label, value) # morph magic happening here!
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
@@ -60,32 +61,63 @@ So, a new company method has appeared:
|
|
60
61
|
dhh.company #=> "37signals"
|
61
62
|
|
62
63
|
|
64
|
+
|
65
|
+
== Morph example from Hash
|
66
|
+
|
63
67
|
How about adding a hash of attribute values?
|
64
68
|
|
65
|
-
|
69
|
+
class Order; include Morph; end
|
70
|
+
order = Order.new
|
71
|
+
order.morph :drink => 'tea', :spoons_of_sugar => 2, :milk => 'prefer soya thanks'
|
72
|
+
|
73
|
+
order.drink # => "tea"
|
74
|
+
order.spoons_of_sugar # => 2
|
75
|
+
order.milk # => "prefer soya thanks"
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
== Morph example hijacking method creation
|
80
|
+
|
81
|
+
class VegieFoxes
|
82
|
+
include Morph
|
83
|
+
|
84
|
+
def method_missing symbol, *args
|
85
|
+
if (is_writer = symbol.to_s =~ /=\Z/)
|
86
|
+
morph_method_missing(symbol, *args) do |base, attribute|
|
87
|
+
create_accessors base, attribute
|
88
|
+
end
|
89
|
+
send(symbol, *args)
|
90
|
+
else
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def create_accessors base, attribute
|
97
|
+
base.class_eval "attr_reader :#{attribute}"
|
66
98
|
|
67
|
-
|
68
|
-
|
69
|
-
|
99
|
+
base.class_def("#{attribute}=") do |value|
|
100
|
+
is_meat = %w[bacon ham].include? value
|
101
|
+
value = is_meat ? 'spinach' : value
|
102
|
+
instance_variable_set "@#{attribute}".to_sym, value
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
70
106
|
|
107
|
+
foxes = VegieFoxes.new
|
71
108
|
|
72
|
-
|
109
|
+
foxes.chunky = 'bacon'
|
110
|
+
foxes.inspect # => #<VegieFoxes @chunky="spinach">
|
73
111
|
|
74
|
-
|
112
|
+
foxes.chunky = 'cheese'
|
113
|
+
puts foxes.inspect # => #<VegieFoxes @chunky="cheese">
|
75
114
|
|
76
|
-
|
77
|
-
|
78
|
-
attr_accessor :drink
|
79
|
-
attr_accessor :email
|
80
|
-
attr_accessor :location
|
81
|
-
attr_accessor :milk
|
82
|
-
attr_accessor :name
|
83
|
-
attr_accessor :projects
|
84
|
-
attr_accessor :spoons_of_sugar
|
85
|
-
=> nil
|
115
|
+
foxes.pet = 'ham'
|
116
|
+
foxes.inspect # => #<VegieFoxes @pet="spinach", @chunky="cheese">
|
86
117
|
|
118
|
+
== Last bits
|
87
119
|
|
88
|
-
See examples/ directory for
|
120
|
+
See examples/ directory for some example code.
|
89
121
|
See LICENSE for the terms of this software.
|
90
122
|
|
91
123
|
. ,
|
data/lib/morph.rb
CHANGED
@@ -1,58 +1,50 @@
|
|
1
1
|
module Morph
|
2
|
-
VERSION = "0.1.
|
2
|
+
VERSION = "0.1.4"
|
3
3
|
|
4
4
|
def self.included(base)
|
5
5
|
base.extend ClassMethods
|
6
6
|
base.send(:include, InstanceMethods)
|
7
|
+
base.send(:include, MethodMissing)
|
7
8
|
end
|
8
9
|
|
9
10
|
module ClassMethods
|
10
11
|
|
12
|
+
@@adding_morph_method = false
|
11
13
|
@@morph_methods = {}
|
12
14
|
|
13
|
-
def
|
14
|
-
|
15
|
-
name = '_'+name if name =~ /^\d/
|
16
|
-
name
|
15
|
+
def morph_methods
|
16
|
+
@@morph_methods.keys.sort
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
|
21
|
-
@@morph_methods[attribute] = true
|
22
|
-
@@morph_methods[attribute+'='] = true
|
23
|
-
class_eval "attr_accessor :#{attribute}"
|
19
|
+
def adding_morph_method= true_or_false
|
20
|
+
@@adding_morph_method = true_or_false
|
24
21
|
end
|
25
22
|
|
26
|
-
def
|
27
|
-
|
23
|
+
def class_def name, &block
|
24
|
+
class_eval { define_method name, &block }
|
28
25
|
end
|
29
26
|
|
30
|
-
|
31
|
-
@@morph_methods.delete symbol.to_s
|
32
|
-
super
|
33
|
-
end
|
27
|
+
protected
|
34
28
|
|
35
|
-
|
36
|
-
|
37
|
-
writers.each do |writer|
|
38
|
-
class_eval "remove_method :#{writer}"
|
29
|
+
def method_added symbol
|
30
|
+
@@morph_methods[symbol.to_s] = true if @@adding_morph_method
|
39
31
|
end
|
40
|
-
end
|
41
32
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
readers = methods.reject { |m| m =~ /=\Z/ }
|
33
|
+
def method_removed symbol
|
34
|
+
@@morph_methods.delete symbol.to_s if @@morph_methods.has_key? symbol.to_s
|
35
|
+
end
|
46
36
|
|
47
|
-
|
48
|
-
readers = readers.reject { |m| accessors.include? m }
|
49
|
-
writers = writers.reject { |m| accessors.include? m.chomp('=') }
|
37
|
+
end
|
50
38
|
|
51
|
-
|
52
|
-
|
53
|
-
|
39
|
+
module MethodMissing
|
40
|
+
def method_missing symbol, *args
|
41
|
+
is_writer = symbol.to_s =~ /=\Z/
|
54
42
|
|
55
|
-
|
43
|
+
if is_writer
|
44
|
+
morph_method_missing symbol, *args
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
56
48
|
end
|
57
49
|
end
|
58
50
|
|
@@ -82,29 +74,43 @@ module Morph
|
|
82
74
|
attributes.each { |a, v| morph(a, v) }
|
83
75
|
else
|
84
76
|
label = attributes
|
85
|
-
attribute = label.is_a?(String) ?
|
77
|
+
attribute = label.is_a?(String) ? convert_to_morph_method_name(label) : label
|
86
78
|
send("#{attribute}=".to_sym, value)
|
87
79
|
end
|
88
80
|
end
|
89
81
|
|
90
|
-
|
91
|
-
is_writer = symbol.to_s =~ /=\Z/
|
82
|
+
protected
|
92
83
|
|
93
|
-
|
84
|
+
def morph_method_missing symbol, *args
|
94
85
|
attribute = symbol.to_s.chomp '='
|
86
|
+
|
95
87
|
if Object.instance_methods.include?(attribute)
|
96
88
|
raise "'#{attribute}' is an instance_method on Object, cannot create accessor methods for '#{attribute}'"
|
97
|
-
elsif args
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
89
|
+
elsif argument_provided? args
|
90
|
+
base = self.class
|
91
|
+
base.adding_morph_method= true
|
92
|
+
|
93
|
+
if block_given?
|
94
|
+
yield base, attribute
|
95
|
+
else
|
96
|
+
base.class_eval "attr_accessor :#{attribute}"
|
102
97
|
send(symbol, *args)
|
103
98
|
end
|
99
|
+
base.adding_morph_method= false
|
104
100
|
end
|
105
|
-
else
|
106
|
-
super
|
107
101
|
end
|
108
|
-
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def argument_provided? args
|
106
|
+
args.size > 0 && !args[0].nil? && !(args[0].is_a?(String) && args[0].strip.size == 0)
|
107
|
+
end
|
108
|
+
|
109
|
+
def convert_to_morph_method_name label
|
110
|
+
name = label.downcase.tr('()*',' ').gsub('%','percentage').strip.chomp(':').strip.gsub(/\s/,'_').squeeze('_')
|
111
|
+
name = '_'+name if name =~ /^\d/
|
112
|
+
name
|
113
|
+
end
|
114
|
+
|
109
115
|
end
|
110
116
|
end
|
data/morph.gemspec
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Morph-0.1.
|
2
|
+
# Gem::Specification for Morph-0.1.4
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = %q{morph}
|
7
|
-
s.version = "0.1.
|
7
|
+
s.version = "0.1.4"
|
8
8
|
|
9
9
|
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.authors = ["Rob McKinnon"]
|
13
|
-
s.date = %q{2008-04-
|
14
|
-
s.description = %q{Morph allows you to emerge class definitions via calling assignment methods.}
|
13
|
+
s.date = %q{2008-04-10}
|
14
|
+
s.description = %q{Morph allows you to emerge class definitions via calling assignment methods; mix with Hpricot for screen scrapping fun.}
|
15
15
|
s.email = ["rob ~@nospam@~ rubyforge.org"]
|
16
16
|
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README"]
|
17
17
|
s.files = ["CHANGELOG", "examples/forger.rb", "examples/hubbit.rb", "lib/morph.rb", "LICENSE", "README", "spec/morph_spec.rb", "spec/spec.opts", "Manifest", "morph.gemspec"]
|
@@ -20,8 +20,8 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Morph", "--main", "README", "--inline-source"]
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
s.rubyforge_project = %q{morph}
|
23
|
-
s.rubygems_version = %q{1.0
|
24
|
-
s.summary = %q{Morph allows you to emerge class definitions via calling assignment methods.}
|
23
|
+
s.rubygems_version = %q{1.1.0}
|
24
|
+
s.summary = %q{Morph allows you to emerge class definitions via calling assignment methods; mix with Hpricot for screen scrapping fun.}
|
25
25
|
end
|
26
26
|
|
27
27
|
|
data/spec/morph_spec.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../lib/morph'
|
2
2
|
require File.dirname(__FILE__) + '/morph_spec_helper'
|
3
3
|
|
4
|
-
|
5
4
|
describe Morph, "when writer method that didn't exist before is called with non-nil value" do
|
6
5
|
before :each do
|
7
6
|
remove_morph_methods
|
@@ -28,6 +27,20 @@ describe Morph, "when writer method that didn't exist before is called with nil
|
|
28
27
|
it_should_behave_like "class without generated accessor methods added"
|
29
28
|
end
|
30
29
|
|
30
|
+
describe Morph, "when class definition contains methods and morph is included" do
|
31
|
+
include MorphSpecHelperMethods
|
32
|
+
|
33
|
+
after :all do
|
34
|
+
remove_morph_methods
|
35
|
+
@morphed_class.class_eval "remove_method :happy"
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should not return methods defined in class in morph_methods list' do
|
39
|
+
initialize_morph "class ExampleMorph\n include Morph\n def happy\n 'happy, joy, joy'\n end\n end"
|
40
|
+
morph_methods.should be_empty
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
31
44
|
describe Morph, "when writer method that didn't exist before is called with blank space attribute value" do
|
32
45
|
before :each do
|
33
46
|
remove_morph_methods
|
@@ -38,7 +51,6 @@ describe Morph, "when writer method that didn't exist before is called with blan
|
|
38
51
|
it_should_behave_like "class without generated accessor methods added"
|
39
52
|
end
|
40
53
|
|
41
|
-
|
42
54
|
describe Morph, 'when morph method used to set attribute value' do
|
43
55
|
|
44
56
|
before :each do
|
@@ -168,36 +180,34 @@ describe Morph, "when class= is called" do
|
|
168
180
|
end
|
169
181
|
end
|
170
182
|
|
171
|
-
|
172
|
-
describe Morph, 'when remove_morph_writers is called after a generated method has been added' do
|
183
|
+
describe Morph, 'when passing block to morph_method_missing' do
|
173
184
|
|
174
185
|
include MorphSpecHelperMethods
|
175
186
|
before :all do initialize_morph; end
|
176
|
-
after :
|
177
|
-
|
178
|
-
before :each do
|
179
|
-
remove_morph_methods
|
180
|
-
@morph.noise= 'quack'
|
181
|
-
@attribute = 'noise'
|
182
|
-
@morphed_class.remove_morph_writers
|
183
|
-
end
|
187
|
+
after :each do remove_morph_methods; end
|
184
188
|
|
185
|
-
it 'should
|
186
|
-
|
187
|
-
|
189
|
+
it 'should class_eval the block' do
|
190
|
+
@morph.morph_method_missing(:chunky, 'bacon') do |base, attribute|
|
191
|
+
base.class_eval "def #{attribute}; 'spinach'; end"
|
192
|
+
end
|
193
|
+
@morph.respond_to?(:chunky).should == true
|
194
|
+
@morph.chunky.should == 'spinach'
|
195
|
+
@morphed_class.class_eval "remove_method :chunky"
|
196
|
+
lambda { @morph.chunky }.should raise_error
|
188
197
|
end
|
189
198
|
|
190
|
-
it 'should
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
@
|
199
|
+
it 'should class_eval the block' do
|
200
|
+
@morph.morph_method_missing :chunky, 'bacon' do |base, attribute|
|
201
|
+
base.class_def(attribute) { 'spinach' }
|
202
|
+
end
|
203
|
+
@morph.respond_to?(:chunky).should == true
|
204
|
+
@morph.chunky.should == 'spinach'
|
205
|
+
@morphed_class.class_eval "remove_method :chunky"
|
206
|
+
lambda { @morph.chunky }.should raise_error
|
196
207
|
end
|
197
208
|
|
198
209
|
end
|
199
210
|
|
200
|
-
|
201
211
|
describe Morph, "when converting label text to morph method name" do
|
202
212
|
|
203
213
|
include MorphSpecHelperMethods
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: morph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob McKinnon
|
@@ -9,11 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-04-
|
12
|
+
date: 2008-04-10 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description: Morph allows you to emerge class definitions via calling assignment methods.
|
16
|
+
description: Morph allows you to emerge class definitions via calling assignment methods; mix with Hpricot for screen scrapping fun.
|
17
17
|
email:
|
18
18
|
- rob ~@nospam@~ rubyforge.org
|
19
19
|
executables: []
|
@@ -63,9 +63,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
63
|
requirements: []
|
64
64
|
|
65
65
|
rubyforge_project: morph
|
66
|
-
rubygems_version: 1.0
|
66
|
+
rubygems_version: 1.1.0
|
67
67
|
signing_key:
|
68
68
|
specification_version: 2
|
69
|
-
summary: Morph allows you to emerge class definitions via calling assignment methods.
|
69
|
+
summary: Morph allows you to emerge class definitions via calling assignment methods; mix with Hpricot for screen scrapping fun.
|
70
70
|
test_files: []
|
71
71
|
|