morph 0.1.0
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 +1 -0
- data/LICENSE +18 -0
- data/Manifest +9 -0
- data/README +155 -0
- data/examples/forger.rb +31 -0
- data/examples/hubbit.rb +30 -0
- data/lib/morph.rb +82 -0
- data/morph.gemspec +56 -0
- data/spec/morph_spec.rb +308 -0
- data/spec/spec.opts +7 -0
- metadata +63 -0
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v0.1.0. initial release
|
data/LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright 2008 Rob McKinnon
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal in the Software without restriction, including without limitation the
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest
ADDED
data/README
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
Morph allows you to emerge class definitions via calling assignment methods.
|
2
|
+
Mix with Hpricot for screen scrapping fun.
|
3
|
+
|
4
|
+
Here's example code showing Morph playing with Hpricot:
|
5
|
+
|
6
|
+
require 'hpricot'; require 'open-uri'; require 'morph'
|
7
|
+
|
8
|
+
class Hubbit
|
9
|
+
include Morph # allows class to morph
|
10
|
+
|
11
|
+
def initialize name
|
12
|
+
doc = Hpricot open("http://github.com/#{name}")
|
13
|
+
|
14
|
+
(doc/'label').collect do |node|
|
15
|
+
label = node.inner_text
|
16
|
+
value = node.next_sibling.inner_text.strip
|
17
|
+
|
18
|
+
morph(label, value) # morph magic happening here!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def Hubbit name; Hubbit.new name; end
|
24
|
+
|
25
|
+
|
26
|
+
The model emerges from the data. Let's start by looking up 'why':
|
27
|
+
|
28
|
+
why = Hubbit 'why'
|
29
|
+
|
30
|
+
What new methods do we have?
|
31
|
+
|
32
|
+
Hubbit.morph_methods # => ["email", "email=", "name", "name="]
|
33
|
+
|
34
|
+
Ah-ha, so we have a name attribute now:
|
35
|
+
|
36
|
+
why.name #=> "why the lucky stiff"
|
37
|
+
|
38
|
+
|
39
|
+
Let's add some of why's projects:
|
40
|
+
|
41
|
+
why.projects = %w[shoes hacketyhack camping hoodwinkd hpricot markaby
|
42
|
+
mousehole parkplace poignant sandbox]
|
43
|
+
|
44
|
+
That why's a productive fellow! Note new accessor methods have been added:
|
45
|
+
|
46
|
+
Hubbit.morph_methods # => ["email", "email=", "name", "name=",
|
47
|
+
"projects", "projects="]
|
48
|
+
|
49
|
+
|
50
|
+
Let's do some more morphing:
|
51
|
+
|
52
|
+
dhh = Hubbit 'dhh'
|
53
|
+
|
54
|
+
Do we have more methods now?
|
55
|
+
|
56
|
+
Hubbit.morph_methods # => ["blog", "blog=", "company", "company=",
|
57
|
+
"email", "email=", "location", "location="
|
58
|
+
"name", "name=", "projects", "projects="]
|
59
|
+
|
60
|
+
So, a new company method has appeared:
|
61
|
+
|
62
|
+
dhh.company #=> "37signals"
|
63
|
+
|
64
|
+
|
65
|
+
Time to print the nascent attribute definitions:
|
66
|
+
|
67
|
+
puts Hubbit.print_morph_methods
|
68
|
+
|
69
|
+
# attr_accessor :name
|
70
|
+
# attr_accessor :email
|
71
|
+
# attr_accessor :blog
|
72
|
+
# attr_accessor :company
|
73
|
+
# attr_accessor :location
|
74
|
+
# attr_accessor :projects
|
75
|
+
|
76
|
+
|
77
|
+
See examples/ directory for more example code.
|
78
|
+
See LICENSE for the terms of this software.
|
79
|
+
|
80
|
+
,
|
81
|
+
?7+~::+II~
|
82
|
+
?7: ,:+7
|
83
|
+
777IIII777? 7: :?7
|
84
|
+
=I= I: 7? ,+7
|
85
|
+
I? ,, 77 7: :I
|
86
|
+
= ?7777 77 7 7 7+, :7
|
87
|
+
7 777777 ~77+=77 I+ I? ,7
|
88
|
+
:7 77 ~77 I I7 7 ?: ?
|
89
|
+
I 77 7, 7 7 :I I ?
|
90
|
+
7 ?77=7~ 77777 7 ~+ ,+
|
91
|
+
7~ 7 :I7?~ 7
|
92
|
+
=? 7 ?I ~I77= I=
|
93
|
+
7 ? :, 7 I7777, 7 7
|
94
|
+
? 777?~~7777+ 7 7~ 7
|
95
|
+
?7 ,777777=, ,7 7 ,7
|
96
|
+
7= , =7 7: 7
|
97
|
+
+7 :7 7 ,I
|
98
|
+
:7 ?~ 7? 7
|
99
|
+
7 7 ~II7~, 7
|
100
|
+
7 7 , =7777777?+,,, I=
|
101
|
+
:7, ~==, 7
|
102
|
+
II~,, 77~
|
103
|
+
,I? +777
|
104
|
+
7+, ~7777:
|
105
|
+
== :77
|
106
|
+
:7: ,7I
|
107
|
+
7I 7
|
108
|
+
I ,7, 7
|
109
|
+
=7 77=7 7
|
110
|
+
,7 7I 7 7
|
111
|
+
I, I7 7 7
|
112
|
+
?, ,7 7, 7
|
113
|
+
7 7~ 7, 7
|
114
|
+
7 ,7I 7 7
|
115
|
+
=+ =7 7 ~=
|
116
|
+
=7 7, 7 7
|
117
|
+
,7, ~7IIII7+, 7
|
118
|
+
+: II I
|
119
|
+
?7 I? +~
|
120
|
+
II, +I 7
|
121
|
+
~7 ,I 7
|
122
|
+
7= ~7 7
|
123
|
+
?7, ~7+ ?~
|
124
|
+
~7777I= ,7
|
125
|
+
7: 7
|
126
|
+
I 7
|
127
|
+
I ,:77I 7
|
128
|
+
I :7 I
|
129
|
+
I =~
|
130
|
+
7 , ,7
|
131
|
+
+, 7 : ,7
|
132
|
+
+ 7 + 7
|
133
|
+
+ 7 + ,7
|
134
|
+
7 I ? ,7
|
135
|
+
7 +: 7 ,7
|
136
|
+
7 =+ 7 ,7
|
137
|
+
7 :I I ,7
|
138
|
+
7 :I 7 7
|
139
|
+
7 :I I 7
|
140
|
+
I, ,7 I: 7
|
141
|
+
=+ ,7 ? 7
|
142
|
+
:?, ,7 7, 7
|
143
|
+
I: ,7 7, ?
|
144
|
+
:7 ,7 7, ,
|
145
|
+
+I, : ? ,=
|
146
|
+
+= ~ =~ 7
|
147
|
+
:II,, = I ?
|
148
|
+
=I= ? 7, :7
|
149
|
+
II~ I 7, ,II
|
150
|
+
7~ ~7 7 ,=7
|
151
|
+
= =7 I, ::
|
152
|
+
77II?==?II777777777777777 7~ 7
|
153
|
+
77+,, 7:
|
154
|
+
777777+:,~777
|
155
|
+
|
data/examples/forger.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'hpricot'; require 'open-uri'; require 'morph'
|
2
|
+
|
3
|
+
# An example of Morph playing with Hpricot
|
4
|
+
class Forger
|
5
|
+
|
6
|
+
include Morph
|
7
|
+
|
8
|
+
def initialize name
|
9
|
+
begin
|
10
|
+
doc = Hpricot open("http://rubyforge.org/users/#{name}")
|
11
|
+
|
12
|
+
table = doc.at('td[text() = "Personal Information"]').parent.parent
|
13
|
+
values = table/'tr/td/strong'
|
14
|
+
|
15
|
+
values.collect do |node|
|
16
|
+
value = node.inner_text.strip
|
17
|
+
label = node.at('../../td[1]').inner_text
|
18
|
+
morph(label, value)
|
19
|
+
end
|
20
|
+
rescue
|
21
|
+
raise "Couldn't find forger with name: #{name}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def Forger name
|
27
|
+
Forger.new name
|
28
|
+
end
|
29
|
+
|
30
|
+
#> why = Forger 'why'
|
31
|
+
#> dhh = Forger 'webster123'
|
data/examples/hubbit.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'hpricot'; require 'open-uri'; require 'morph'
|
2
|
+
|
3
|
+
# An example of Morph playing with Hpricot
|
4
|
+
class Hubbit
|
5
|
+
|
6
|
+
include Morph
|
7
|
+
|
8
|
+
def initialize name
|
9
|
+
begin
|
10
|
+
doc = Hpricot open("http://github.com/#{name}")
|
11
|
+
|
12
|
+
(doc/'label').collect do |node|
|
13
|
+
label = node.inner_text
|
14
|
+
value = node.next_sibling.inner_text.strip
|
15
|
+
|
16
|
+
morph(label, value) # magic morphing happening here!
|
17
|
+
end
|
18
|
+
rescue
|
19
|
+
raise "Couldn't find hubbit with name: #{name}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def Hubbit name
|
25
|
+
Hubbit.new name
|
26
|
+
end
|
27
|
+
|
28
|
+
# why = Hubbit 'why'
|
29
|
+
# dhh = Hubbit 'dhh'
|
30
|
+
|
data/lib/morph.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module Morph
|
2
|
+
VERSION = "0.1.0"
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
base.send(:include, InstanceMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
@@morph_methods = {}
|
12
|
+
|
13
|
+
def convert_to_morph_method_name label
|
14
|
+
name = label.downcase.tr('()*',' ').gsub('%','percentage').strip.chomp(':').strip.gsub(/\s/,'_').squeeze('_')
|
15
|
+
name = '_'+name if name =~ /^\d/
|
16
|
+
name
|
17
|
+
end
|
18
|
+
|
19
|
+
def morph_accessor symbol
|
20
|
+
attribute = symbol.to_s
|
21
|
+
@@morph_methods[attribute] = true
|
22
|
+
@@morph_methods[attribute+'='] = true
|
23
|
+
class_eval "attr_accessor :#{attribute}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def morph_methods
|
27
|
+
@@morph_methods.keys.sort
|
28
|
+
end
|
29
|
+
|
30
|
+
def remove_method symbol
|
31
|
+
@@morph_methods.delete symbol.to_s
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def remove_morph_writers
|
36
|
+
writers = morph_methods.select { |m| m =~ /=\Z/ }
|
37
|
+
writers.each do |writer|
|
38
|
+
class_eval "remove_method :#{writer}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def print_morph_methods
|
43
|
+
methods = morph_methods
|
44
|
+
writers = methods.select { |m| m =~ /=\Z/ }
|
45
|
+
readers = methods.reject { |m| m =~ /=\Z/ }
|
46
|
+
|
47
|
+
accessors = readers.select { |m| writers.include? "#{m}=" }
|
48
|
+
readers = readers.reject { |m| accessors.include? m }
|
49
|
+
writers = writers.reject { |m| accessors.include? m.chomp('=') }
|
50
|
+
|
51
|
+
attributes = accessors.collect { |attribute| "attr_accessor :#{attribute}\n" }
|
52
|
+
attributes += readers.collect { |attribute| "attr_reader :#{attribute}\n" }
|
53
|
+
attributes += writers.collect { |attribute| "attr_writer :#{attribute}\n" }
|
54
|
+
|
55
|
+
attributes.join.chop
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
module InstanceMethods
|
60
|
+
|
61
|
+
def morph label, value
|
62
|
+
attribute = self.class.convert_to_morph_method_name label
|
63
|
+
send("#{attribute}=".to_sym, value)
|
64
|
+
end
|
65
|
+
|
66
|
+
def method_missing symbol, *args
|
67
|
+
attribute = symbol.to_s.chomp '='
|
68
|
+
if Object.instance_methods.include?(attribute)
|
69
|
+
raise "'#{attribute}' is an instance_method on Object, cannot create accessor methods for '#{attribute}'"
|
70
|
+
else
|
71
|
+
is_writer = symbol.to_s =~ /=\Z/
|
72
|
+
if is_writer
|
73
|
+
value = args[0]
|
74
|
+
empty_value = (value.nil? or (value.is_a?(String) && value.strip.size == 0))
|
75
|
+
return if empty_value
|
76
|
+
end
|
77
|
+
self.class.morph_accessor attribute.to_sym
|
78
|
+
send(symbol, *args)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/morph.gemspec
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
# Gem::Specification for Morph-0.1.0
|
3
|
+
# Originally generated by Echoe
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{morph}
|
7
|
+
s.version = "0.1.0"
|
8
|
+
|
9
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.authors = ["Rob McKinnon"]
|
13
|
+
s.date = %q{2008-03-24}
|
14
|
+
s.description = %q{Morph allows you to emerge class definitions via calling assignment methods.}
|
15
|
+
s.email = ["rob ~@nospam@~ rubyforge.org"]
|
16
|
+
s.files = ["CHANGELOG", "examples/forger.rb", "examples/hubbit.rb", "lib/morph.rb", "LICENSE", "README", "spec/morph_spec.rb", "spec/spec.opts", "Manifest", "morph.gemspec"]
|
17
|
+
s.has_rdoc = true
|
18
|
+
s.homepage = %q{}
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.rubyforge_project = %q{morph}
|
21
|
+
s.rubygems_version = %q{1.0.1}
|
22
|
+
s.summary = %q{Morph allows you to emerge class definitions via calling assignment methods.}
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# # Original Rakefile source (requires the Echoe gem):
|
27
|
+
#
|
28
|
+
# require 'rubygems'
|
29
|
+
# require 'spec'
|
30
|
+
# require 'lib/morph'
|
31
|
+
#
|
32
|
+
# begin
|
33
|
+
# require 'echoe'
|
34
|
+
#
|
35
|
+
# Echoe.new("morph", Morph::VERSION) do |morph|
|
36
|
+
# morph.author = ["Rob McKinnon"]
|
37
|
+
# morph.email = ["rob ~@nospam@~ rubyforge.org"]
|
38
|
+
# morph.description = File.readlines("README").first
|
39
|
+
# morph.rubyforge_name = "morph"
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# rescue LoadError
|
43
|
+
# puts "You need to install the echoe gem to perform meta operations on this gem"
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# desc "Open an irb session preloaded with this library"
|
47
|
+
# task :console do
|
48
|
+
# sh "irb -rubygems -r ./lib/morph.rb"
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# desc "Run spec runner"
|
52
|
+
# task(:test) do
|
53
|
+
# files = FileList['spec/**/*_spec.rb']
|
54
|
+
# Spec::Runner::CommandLine.run(rspec_options)
|
55
|
+
# system "ruby spec/spec_runner.rb #{files} --format specdoc"
|
56
|
+
# end
|
data/spec/morph_spec.rb
ADDED
@@ -0,0 +1,308 @@
|
|
1
|
+
$KCODE = "u"
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../lib/morph'
|
4
|
+
|
5
|
+
module MorphSpecHelperMethods
|
6
|
+
|
7
|
+
def initialize_morph_class
|
8
|
+
@morphed_class = eval 'class ExampleMorph; include Morph; end'
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize_morph
|
12
|
+
initialize_morph_class
|
13
|
+
@original_instance_methods = @morphed_class.instance_methods
|
14
|
+
@morph = @morphed_class.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove_morph_methods
|
18
|
+
@morphed_class.instance_methods.each do |method|
|
19
|
+
@morphed_class.class_eval "remove_method :#{method}" unless @original_instance_methods.include?(method)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def instance_methods
|
24
|
+
@morphed_class.instance_methods
|
25
|
+
end
|
26
|
+
|
27
|
+
def morph_methods
|
28
|
+
@morphed_class.morph_methods
|
29
|
+
end
|
30
|
+
|
31
|
+
def check_convert_to_morph_method_name label, method_name
|
32
|
+
initialize_morph_class
|
33
|
+
@morphed_class.convert_to_morph_method_name(label).should == method_name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "class with generated accessor methods added", :shared => true do
|
38
|
+
|
39
|
+
include MorphSpecHelperMethods
|
40
|
+
before :all do initialize_morph; end
|
41
|
+
after :all do remove_morph_methods; end
|
42
|
+
|
43
|
+
it 'should add reader method to class instance_methods list' do
|
44
|
+
instance_methods.include?(@attribute).should == true
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should add writer method to class instance_methods list' do
|
48
|
+
instance_methods.include?("#{@attribute}=").should == true
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should add reader method to class morph_methods list' do
|
52
|
+
morph_methods.include?(@attribute).should == true
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should add writer method to class morph_methods list' do
|
56
|
+
morph_methods.include?("#{@attribute}=").should == true
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should only have generated accessor methods in morph_methods list' do
|
60
|
+
morph_methods.size.should == @expected_morph_methods_count
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should be able to print morph method declarations' do
|
64
|
+
@morphed_class.print_morph_methods.should == %Q|attr_accessor :#{@attribute}|
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "class without generated accessor methods added", :shared => true do
|
69
|
+
include MorphSpecHelperMethods
|
70
|
+
|
71
|
+
before :all do
|
72
|
+
initialize_morph
|
73
|
+
end
|
74
|
+
|
75
|
+
after :all do
|
76
|
+
remove_morph_methods
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should not add reader method to class instance_methods list' do
|
80
|
+
instance_methods.include?(@attribute).should == false
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should not add writer method to class instance_methods list' do
|
84
|
+
instance_methods.include?("#{@attribute}=").should == false
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should not add reader method to class morph_methods list' do
|
88
|
+
morph_methods.include?(@attribute).should == false
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should not add writer method to class morph_methods list' do
|
92
|
+
morph_methods.include?("#{@attribute}=").should == false
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should have empty morph_methods list' do
|
96
|
+
morph_methods.size.should == 0
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe Morph, "when reader method that didn't exist before is called" do
|
101
|
+
before :each do
|
102
|
+
remove_morph_methods
|
103
|
+
@morph.noise
|
104
|
+
@attribute = 'noise'
|
105
|
+
@expected_morph_methods_count = 2
|
106
|
+
end
|
107
|
+
|
108
|
+
it_should_behave_like "class with generated accessor methods added"
|
109
|
+
|
110
|
+
it 'should return nil if reader is called' do
|
111
|
+
@morph.noise.should == nil
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe Morph, "when writer method that didn't exist before is called with non-nil value" do
|
116
|
+
before :each do
|
117
|
+
remove_morph_methods
|
118
|
+
@quack = 'quack'
|
119
|
+
@morph.noise= @quack
|
120
|
+
@attribute = 'noise'
|
121
|
+
@expected_morph_methods_count = 2
|
122
|
+
end
|
123
|
+
|
124
|
+
it_should_behave_like "class with generated accessor methods added"
|
125
|
+
|
126
|
+
it 'should return assigned value when reader method called' do
|
127
|
+
@morph.noise.should == @quack
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe Morph, "when writer method that didn't exist before is in unicode" do
|
132
|
+
before :each do
|
133
|
+
remove_morph_methods
|
134
|
+
@age = 19
|
135
|
+
@attribute = "年龄"
|
136
|
+
@morph.morph(@attribute, @age)
|
137
|
+
@expected_morph_methods_count = 2
|
138
|
+
end
|
139
|
+
|
140
|
+
it_should_behave_like "class with generated accessor methods added"
|
141
|
+
|
142
|
+
it 'should return assigned value when reader method called' do
|
143
|
+
@morph.send(@attribute.to_sym) == @age
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe Morph, "when writer method that didn't exist before is called with nil value" do
|
148
|
+
before :each do
|
149
|
+
remove_morph_methods
|
150
|
+
@morph.morph('Pizza', nil)
|
151
|
+
@attribute = 'pizza'
|
152
|
+
end
|
153
|
+
|
154
|
+
it_should_behave_like "class without generated accessor methods added"
|
155
|
+
end
|
156
|
+
|
157
|
+
describe Morph, 'when morph method used to set blank space attribute value' do
|
158
|
+
before :each do
|
159
|
+
remove_morph_methods
|
160
|
+
@morph.morph('Pizza', ' ')
|
161
|
+
@attribute = 'pizza'
|
162
|
+
end
|
163
|
+
|
164
|
+
it_should_behave_like "class without generated accessor methods added"
|
165
|
+
end
|
166
|
+
|
167
|
+
describe Morph, 'when morph method used to set nil attribute value' do
|
168
|
+
before :each do
|
169
|
+
remove_morph_methods
|
170
|
+
@morph.morph('Pizza', nil)
|
171
|
+
@attribute = 'pizza'
|
172
|
+
end
|
173
|
+
|
174
|
+
it_should_behave_like "class without generated accessor methods added"
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
describe Morph, 'when remove_morph_writers is called after a generated method has been added' do
|
179
|
+
|
180
|
+
include MorphSpecHelperMethods
|
181
|
+
before :all do initialize_morph; end
|
182
|
+
after :all do remove_morph_methods; end
|
183
|
+
|
184
|
+
before :each do
|
185
|
+
remove_morph_methods
|
186
|
+
@morph.noise= 'quack'
|
187
|
+
@attribute = 'noise'
|
188
|
+
@morphed_class.remove_morph_writers
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should remove a morph generated writer method from morph_methods list' do
|
192
|
+
morph_methods.include?('noise=').should == false
|
193
|
+
morph_methods.size.should == 1
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should remove a morph generated writer method from class instance_methods list' do
|
197
|
+
instance_methods.include?('noise=').should == false
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'should be able to print morph method declarations' do
|
201
|
+
@morphed_class.print_morph_methods.should == %Q|attr_reader :#{@attribute}|
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
describe Morph, "when reader method called is a class method" do
|
207
|
+
|
208
|
+
before :each do
|
209
|
+
remove_morph_methods
|
210
|
+
@morph.name
|
211
|
+
@attribute = 'name'
|
212
|
+
@expected_morph_methods_count = 2
|
213
|
+
end
|
214
|
+
|
215
|
+
it_should_behave_like "class with generated accessor methods added"
|
216
|
+
end
|
217
|
+
|
218
|
+
describe Morph, "when writer method called is a class method" do
|
219
|
+
|
220
|
+
before :each do
|
221
|
+
remove_morph_methods
|
222
|
+
@value = 'Morph'
|
223
|
+
@morph.name = @value
|
224
|
+
@attribute = 'name'
|
225
|
+
@expected_morph_methods_count = 2
|
226
|
+
end
|
227
|
+
|
228
|
+
it_should_behave_like "class with generated accessor methods added"
|
229
|
+
|
230
|
+
it 'should return assigned value when reader method called' do
|
231
|
+
@morph.name.should == @value
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
describe Morph, "when class= is called" do
|
236
|
+
|
237
|
+
include MorphSpecHelperMethods
|
238
|
+
before :all do initialize_morph; end
|
239
|
+
after :all do remove_morph_methods; end
|
240
|
+
|
241
|
+
it 'should throw exception if non nil object is passed' do
|
242
|
+
lambda { @morph.class = 'Red' }.should raise_error(/cannot create accessor methods/)
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'should throw exception if nil object is passed' do
|
246
|
+
lambda { @morph.class = nil }.should raise_error(/cannot create accessor methods/)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
describe Morph, "when converting label text to morph method name" do
|
251
|
+
|
252
|
+
include MorphSpecHelperMethods
|
253
|
+
|
254
|
+
it 'should upper case to lower case' do
|
255
|
+
check_convert_to_morph_method_name 'CaSe', 'case'
|
256
|
+
end
|
257
|
+
it 'should convert single space to underscorce' do
|
258
|
+
check_convert_to_morph_method_name 'First reading', 'first_reading'
|
259
|
+
end
|
260
|
+
it 'should convert multiple spaces to single underscorce' do
|
261
|
+
check_convert_to_morph_method_name "First reading", 'first_reading'
|
262
|
+
end
|
263
|
+
it 'should convert tabs to single underscorce' do
|
264
|
+
check_convert_to_morph_method_name "First\t\treading", 'first_reading'
|
265
|
+
end
|
266
|
+
it 'should convert new line chars to single underscorce' do
|
267
|
+
check_convert_to_morph_method_name "First\r\nreading", 'first_reading'
|
268
|
+
end
|
269
|
+
it 'should remove leading and trailing whitespace new line chars to single underscorce' do
|
270
|
+
check_convert_to_morph_method_name " \t\r\nFirst reading \t\r\n", 'first_reading'
|
271
|
+
end
|
272
|
+
it 'should remove trailing colon surrounded by whitespace' do
|
273
|
+
check_convert_to_morph_method_name "First reading : ", 'first_reading'
|
274
|
+
end
|
275
|
+
it 'should remove parenthesis' do
|
276
|
+
check_convert_to_morph_method_name 'Nav(GBX)', 'nav_gbx'
|
277
|
+
end
|
278
|
+
it 'should remove *' do
|
279
|
+
check_convert_to_morph_method_name 'Change**', 'change'
|
280
|
+
end
|
281
|
+
it 'should convert % character to the text "percentage"' do
|
282
|
+
check_convert_to_morph_method_name '% Change', 'percentage_change'
|
283
|
+
end
|
284
|
+
it 'should precede leading digit with an underscore character' do
|
285
|
+
check_convert_to_morph_method_name '52w_high', '_52w_high'
|
286
|
+
end
|
287
|
+
it 'should handle unicode name' do
|
288
|
+
check_convert_to_morph_method_name '年龄', '年龄'
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
describe Morph, 'when morph method used to set attribute value and attribute name ends with colon' do
|
293
|
+
|
294
|
+
before :each do
|
295
|
+
remove_morph_methods
|
296
|
+
@value = '20 Mar 2008'
|
297
|
+
@morph.morph('First reading : ', @value)
|
298
|
+
@attribute = 'first_reading'
|
299
|
+
@expected_morph_methods_count = 2
|
300
|
+
end
|
301
|
+
|
302
|
+
it_should_behave_like "class with generated accessor methods added"
|
303
|
+
|
304
|
+
it 'should return assigned value when reader method called' do
|
305
|
+
@morph.first_reading.should == @value
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
data/spec/spec.opts
ADDED
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: morph
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rob McKinnon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-03-24 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Morph allows you to emerge class definitions via calling assignment methods.
|
17
|
+
email:
|
18
|
+
- rob ~@nospam@~ rubyforge.org
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- CHANGELOG
|
27
|
+
- examples/forger.rb
|
28
|
+
- examples/hubbit.rb
|
29
|
+
- lib/morph.rb
|
30
|
+
- LICENSE
|
31
|
+
- README
|
32
|
+
- spec/morph_spec.rb
|
33
|
+
- spec/spec.opts
|
34
|
+
- Manifest
|
35
|
+
- morph.gemspec
|
36
|
+
has_rdoc: true
|
37
|
+
homepage: ""
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
48
|
+
version:
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
rubyforge_project: morph
|
58
|
+
rubygems_version: 1.0.1
|
59
|
+
signing_key:
|
60
|
+
specification_version: 2
|
61
|
+
summary: Morph allows you to emerge class definitions via calling assignment methods.
|
62
|
+
test_files: []
|
63
|
+
|