morph 0.1.3 → 0.1.4
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/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
|
|