lightmodels 0.1.2-java → 0.2.1-java
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/.gitignore +19 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +8 -0
- data/Rakefile +9 -0
- data/lib/lightmodels/info_extraction.rb +133 -0
- data/lib/lightmodels/jsonser_nav.rb +12 -12
- data/lib/lightmodels/model_building.rb +66 -1
- data/lib/lightmodels/monkey_patching.rb +30 -0
- data/lib/lightmodels/parsing.rb +186 -0
- data/lib/lightmodels/query_serialized.rb +81 -0
- data/lib/lightmodels/rgen_ext.rb +153 -0
- data/lib/lightmodels/serialization.rb +11 -28
- data/lib/lightmodels/version.rb +3 -0
- data/lightmodels.gemspec +29 -0
- data/test/data/node_setCompleted.json +443 -0
- data/test/test_helper.rb +8 -0
- data/test/test_info_extraction.rb +90 -0
- data/test/test_queryserialized.rb +40 -0
- data/test/test_serialization.rb +64 -0
- metadata +122 -32
- checksums.yaml +0 -7
- data/lib/lightmodels/model.rb +0 -11
- data/lib/lightmodels/query.rb +0 -36
- data/lib/lightmodels/stats.rb +0 -69
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 ftomassetti
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
lightmodels
|
2
|
+
===========
|
3
|
+
|
4
|
+
lightmodels is a library to manipulate models and save them in a lightweight format (JSON is used for serialization).
|
5
|
+
|
6
|
+
EMF models and RGen models can be converted to lightmodels.
|
7
|
+
|
8
|
+
Other projects permit to obtain lightmodels representation of Ruby (ruby_to_emf_parser) and Java (java_model_jrb) code, Properties (properties_lightmodels) and XML (xml_lightmodels) files.
|
data/Rakefile
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
module LightModels
|
2
|
+
|
3
|
+
module InfoExtraction
|
4
|
+
|
5
|
+
class TermsBreaker
|
6
|
+
|
7
|
+
attr_accessor :sequences, :inv_sequences
|
8
|
+
|
9
|
+
def initialize(language_specific_logic)
|
10
|
+
@language_specific_logic = language_specific_logic
|
11
|
+
@sequences = Hash.new {|h,k|
|
12
|
+
h[k] = Hash.new {|h,k|
|
13
|
+
h[k]=0
|
14
|
+
}
|
15
|
+
}
|
16
|
+
@inv_sequences = Hash.new {|h,k|
|
17
|
+
h[k] = Hash.new {|h2,k2|
|
18
|
+
h2[k2]=0
|
19
|
+
}
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.from_context(language_specific_logic,context)
|
24
|
+
ser_context = LightModels::Serialization.jsonize_obj(context)
|
25
|
+
values_map = LightModels::QuerySerialized.collect_values_with_count(ser_context)
|
26
|
+
instance = new(language_specific_logic)
|
27
|
+
values_map.each do |value,c|
|
28
|
+
value = value.to_s.strip
|
29
|
+
if language_specific_logic.terms_containing_value?(value)
|
30
|
+
words = language_specific_logic.to_words(value)
|
31
|
+
first_words = words[0...-1]
|
32
|
+
instance.inv_sequences[words[0].downcase][:start] += c
|
33
|
+
first_words.each_with_index do |w,i|
|
34
|
+
instance.sequences[w.downcase][words[i+1].downcase] += c
|
35
|
+
instance.inv_sequences[words[i+1].downcase][w.downcase] += c
|
36
|
+
end
|
37
|
+
last_word = words.last
|
38
|
+
instance.sequences[last_word.downcase][:end] += c
|
39
|
+
else
|
40
|
+
# who cares, it will be never considered for composed names...
|
41
|
+
end
|
42
|
+
end
|
43
|
+
instance
|
44
|
+
end
|
45
|
+
|
46
|
+
def terms_in_value(value)
|
47
|
+
value = value.to_s.strip
|
48
|
+
if @language_specific_logic.terms_containing_value?(value)
|
49
|
+
words = @language_specific_logic.to_words(value)
|
50
|
+
group_words_in_terms(words).map{|w| w.downcase}
|
51
|
+
else
|
52
|
+
[value]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def frequent_straight_sequence?(w1,w2)
|
59
|
+
w1 = w1.downcase
|
60
|
+
w2 = w2.downcase
|
61
|
+
all_sequences_of_w1 = 0
|
62
|
+
@sequences[w1].each do |k,v|
|
63
|
+
all_sequences_of_w1 += v
|
64
|
+
end
|
65
|
+
sequences_w1_w2 = @sequences[w1][w2]
|
66
|
+
(sequences_w1_w2.to_f/all_sequences_of_w1.to_f)>0.5
|
67
|
+
end
|
68
|
+
|
69
|
+
def frequent_inverse_sequence?(w1,w2)
|
70
|
+
w1 = w1.downcase
|
71
|
+
w2 = w2.downcase
|
72
|
+
all_inv_sequences_of_w1 = 0
|
73
|
+
@inv_sequences[w1].each do |k,v|
|
74
|
+
all_inv_sequences_of_w1 += v
|
75
|
+
end
|
76
|
+
inv_sequences_w1_w2 = @inv_sequences[w1][w2]
|
77
|
+
(inv_sequences_w1_w2.to_f/all_inv_sequences_of_w1.to_f)>0.5
|
78
|
+
end
|
79
|
+
|
80
|
+
def frequent_sequence?(w1,w2)
|
81
|
+
return false unless w2
|
82
|
+
frequent_straight_sequence?(w1,w2) && frequent_inverse_sequence?(w2,w1)
|
83
|
+
end
|
84
|
+
|
85
|
+
def group_words_in_terms(words)
|
86
|
+
# getNotSoGoodFieldName is not a term because
|
87
|
+
# notSoGoodFieldName is more frequently alone that preceded by get
|
88
|
+
|
89
|
+
return words if words.count==1
|
90
|
+
start_term = 0
|
91
|
+
end_term = 0
|
92
|
+
term = words[0]
|
93
|
+
while end_term < words.count && frequent_sequence?(words[end_term],words[end_term+1])
|
94
|
+
end_term += 1
|
95
|
+
term = @language_specific_logic.concat(term,words[end_term])
|
96
|
+
end
|
97
|
+
return [term] if end_term==(words.count-1)
|
98
|
+
[term] + group_words_in_terms(words[(end_term+1)..-1])
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.values_map(model_node)
|
104
|
+
ser_model_node = LightModels::Serialization.jsonize_obj(model_node)
|
105
|
+
LightModels::QuerySerialized.collect_values_with_count(ser_model_node)
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.terms_map(language_specific_logic,model_node,context=nil)
|
109
|
+
# context default to root
|
110
|
+
unless context
|
111
|
+
context = model_node
|
112
|
+
while context.eContainer
|
113
|
+
context = context.eContainer
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# look into context to see how frequent are certain series of words,
|
118
|
+
# frequent series are recognized as composed terms
|
119
|
+
terms_breaker = TermsBreaker.from_context(language_specific_logic,context)
|
120
|
+
|
121
|
+
values_map = values_map(model_node)
|
122
|
+
terms_map = Hash.new {|h,k| h[k]=0}
|
123
|
+
values_map.each do |v,n|
|
124
|
+
terms_breaker.terms_in_value(v).each do |t|
|
125
|
+
terms_map[t] += n
|
126
|
+
end
|
127
|
+
end
|
128
|
+
terms_map
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
@@ -3,20 +3,30 @@
|
|
3
3
|
|
4
4
|
module LightModels
|
5
5
|
|
6
|
-
module
|
6
|
+
module QuerySerialized
|
7
7
|
|
8
8
|
def self.rel_conts(root)
|
9
9
|
root.keys.select {|k| k.start_with? 'relcont_'}
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.rel_non_conts(root)
|
13
|
-
root.keys.select {|k| k.start_with? '
|
13
|
+
root.keys.select {|k| k.start_with? 'relnoncont_'}
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.attrs(root)
|
17
17
|
root.keys.select {|k| k.start_with? 'attr_'}
|
18
18
|
end
|
19
19
|
|
20
|
+
def self.print_tree(root,depth=0)
|
21
|
+
traverse(root) do |n,d|
|
22
|
+
s = ""
|
23
|
+
d.times { s = s + " " }
|
24
|
+
s = s + n['type'] if n
|
25
|
+
s = s + '<NIL>' unless n
|
26
|
+
puts s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
20
30
|
def self.values(root,feat)
|
21
31
|
raw = root[feat]
|
22
32
|
return [] if raw==nil
|
@@ -40,16 +50,6 @@ module Query
|
|
40
50
|
end
|
41
51
|
end
|
42
52
|
|
43
|
-
def self.print_tree(root,depth=0)
|
44
|
-
traverse(root) do |n,d|
|
45
|
-
s = ""
|
46
|
-
d.times { s = s + " " }
|
47
|
-
s = s + n['type'] if n
|
48
|
-
s = s + '<NIL>' unless n
|
49
|
-
puts s
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
53
|
end
|
54
54
|
|
55
55
|
end
|
@@ -8,6 +8,34 @@ class << self
|
|
8
8
|
attr_accessor :verbose
|
9
9
|
end
|
10
10
|
|
11
|
+
def self.handle_serialized_models_in_dir(src,src_extension,error_handler=nil,model_handler,&model_creator)
|
12
|
+
puts "== #{src} ==" if LightModels::ModelBuilding.verbose
|
13
|
+
Dir["#{src}/*"].each do |fd|
|
14
|
+
if File.directory? fd
|
15
|
+
basename = File.basename(fd)
|
16
|
+
handle_serialized_models_in_dir("#{src}/#{basename}",src_extension,error_handler,model_handler,&model_creator)
|
17
|
+
else
|
18
|
+
if File.extname(fd)==".#{src_extension}"
|
19
|
+
handle_serialized_model_per_file(fd,error_handler,model_handler,&model_creator)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.handle_models_in_dir(src,src_extension,error_handler=nil,model_handler,&model_creator)
|
26
|
+
puts "== #{src} ==" if LightModels::ModelBuilding.verbose
|
27
|
+
Dir["#{src}/*"].each do |fd|
|
28
|
+
if File.directory? fd
|
29
|
+
basename = File.basename(fd)
|
30
|
+
handle_models_in_dir("#{src}/#{basename}",src_extension,error_handler,model_handler,&model_creator)
|
31
|
+
else
|
32
|
+
if File.extname(fd)==".#{src_extension}"
|
33
|
+
handle_model_per_file(fd,error_handler,model_handler,&model_creator)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
11
39
|
def self.generate_models_in_dir(src,dest,src_extension,dest_extension,max_nesting=500,error_handler=nil,&model_creator)
|
12
40
|
puts "== #{src} -> #{dest} ==" if LightModels::ModelBuilding.verbose
|
13
41
|
Dir["#{src}/*"].each do |fd|
|
@@ -25,9 +53,46 @@ def self.generate_models_in_dir(src,dest,src_extension,dest_extension,max_nestin
|
|
25
53
|
end
|
26
54
|
end
|
27
55
|
|
56
|
+
def self.handle_model_per_file(src,error_handler=nil,model_handler,&models_generator)
|
57
|
+
puts "<Model from #{src}>" if LightModels::ModelBuilding.verbose
|
58
|
+
|
59
|
+
if error_handler
|
60
|
+
begin
|
61
|
+
m = models_generator.call(src)
|
62
|
+
model_handler.call(src,m)
|
63
|
+
rescue Exception => e
|
64
|
+
error_handler.call(src,e)
|
65
|
+
rescue
|
66
|
+
error_handler.call(src,nil)
|
67
|
+
end
|
68
|
+
else
|
69
|
+
m = models_generator.call(src)
|
70
|
+
model_handler.call(src,m)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.handle_serialized_model_per_file(src,error_handler=nil,model_handler,&models_generator)
|
75
|
+
puts "<Model from #{src}>" if LightModels::ModelBuilding.verbose
|
76
|
+
|
77
|
+
if error_handler
|
78
|
+
begin
|
79
|
+
m = models_generator.call(src)
|
80
|
+
model_handler.call(src,m)
|
81
|
+
rescue Exception => e
|
82
|
+
error_handler.call(src,e)
|
83
|
+
rescue
|
84
|
+
error_handler.call(src,nil)
|
85
|
+
end
|
86
|
+
else
|
87
|
+
m = models_generator.call(src)
|
88
|
+
model_handler.call(src,m)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
28
93
|
def self.generate_model_per_file(src,dest,max_nesting=500,error_handler=nil,&models_generator)
|
29
94
|
if not File.exist? dest
|
30
|
-
puts "<Model from #{src}>"
|
95
|
+
puts "<Model from #{src}>" if LightModels::ModelBuilding.verbose
|
31
96
|
|
32
97
|
if error_handler
|
33
98
|
begin
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Module
|
2
|
+
|
3
|
+
def simple_name
|
4
|
+
if (i = (r = name).rindex(':')) then r[0..i] = '' end
|
5
|
+
r
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
class String
|
11
|
+
|
12
|
+
def remove_postfix(postfix)
|
13
|
+
raise "'#{self}'' have not the right postfix '#{postfix}'" unless end_with?(postfix)
|
14
|
+
self[0..-(1+postfix.length)]
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove_prefix(prefix)
|
18
|
+
raise "'#{self}'' have not the right prefix '#{prefix}'" unless start_with?(prefix)
|
19
|
+
self[prefix.length..-1]
|
20
|
+
end
|
21
|
+
|
22
|
+
def proper_capitalize
|
23
|
+
self[0, 1].upcase + self[1..-1]
|
24
|
+
end
|
25
|
+
|
26
|
+
def proper_uncapitalize
|
27
|
+
self[0, 1].downcase + self[1..-1]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'lightmodels/monkey_patching'
|
2
|
+
|
3
|
+
module LightModels
|
4
|
+
|
5
|
+
class ParsingError < Exception
|
6
|
+
attr_reader :node
|
7
|
+
attr_reader :line
|
8
|
+
|
9
|
+
def initialize(node,msg,line=nil)
|
10
|
+
@node = node
|
11
|
+
@msg = msg
|
12
|
+
@line = lin
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"#{@msg}, start line: #{@line}"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
class UnknownNodeType < ParsingError
|
22
|
+
|
23
|
+
def initialize(node,line=nil,node_type=nil,where=nil)
|
24
|
+
super(node,"UnknownNodeType: type=#{node_type} , where: #{where}")
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
module ParserWrapper
|
30
|
+
|
31
|
+
@@verbose = false
|
32
|
+
|
33
|
+
JavaCollection = ::Java::JavaClass.for_name("java.util.Collection")
|
34
|
+
|
35
|
+
def adapter(model_class,ref)
|
36
|
+
if adapter_specific_class(model_class,ref)
|
37
|
+
adapter_specific_class(model_class,ref)
|
38
|
+
else
|
39
|
+
if model_class.superclass!=Object
|
40
|
+
adapter(model_class.superclass,ref)
|
41
|
+
else
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def reference_to_method(model_class,ref)
|
48
|
+
s = ref.name
|
49
|
+
#s = 'value' if s=='body'
|
50
|
+
adapted = adapter(model_class,ref)
|
51
|
+
s = adapted if adapted
|
52
|
+
s.to_sym
|
53
|
+
end
|
54
|
+
|
55
|
+
def attribute_to_method(model_class,att)
|
56
|
+
s = att.name
|
57
|
+
adapted = adapter(model_class,att)
|
58
|
+
s = adapted if adapted
|
59
|
+
s.to_sym
|
60
|
+
end
|
61
|
+
|
62
|
+
def assign_ref_to_model(model,ref,value)
|
63
|
+
return unless value==nil # we do not need to assign a nil...
|
64
|
+
if ref.many
|
65
|
+
adder_method = :"add#{ref.name.capitalize}"
|
66
|
+
value.each {|el| model.send(adder_method,node_to_model(el))}
|
67
|
+
else
|
68
|
+
setter_method = :"#{ref.name}="
|
69
|
+
raise "Trying to assign an array to a single property. Class #{model.class}, property #{ref.name}" if value.is_a?(::Array)
|
70
|
+
model.send(setter_method,node_to_model(value))
|
71
|
+
end
|
72
|
+
rescue Object => e
|
73
|
+
puts "Problem while assigning ref #{ref.name} (many? #{ref.many}) to #{model.class}. Value: #{value.class}"
|
74
|
+
puts "\t<<#{e}>>"
|
75
|
+
raise e
|
76
|
+
end
|
77
|
+
|
78
|
+
def assign_att_to_model(model,att,value)
|
79
|
+
if att.many
|
80
|
+
adder_method = :"add#{att.name.capitalize}"
|
81
|
+
value.each {|el| model.send(adder_method,el)}
|
82
|
+
else
|
83
|
+
setter_method = :"#{att.name}="
|
84
|
+
raise "Trying to assign an array to a single property. Class #{model.class}, property #{att.name}" if value.is_a?(::Array)
|
85
|
+
model.send(setter_method,value)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def populate_attr(node,att,model)
|
90
|
+
value = get_feature_value(node,att)
|
91
|
+
#puts "Value got for #{node.class} #{att} : #{value.class}"
|
92
|
+
# nil are ignored
|
93
|
+
model.send(:"#{att.name}=",value) if value!=nil
|
94
|
+
rescue Object => e
|
95
|
+
puts "Problem while populating attribute #{att.name} of #{model} from #{node}. Value: #{value}"
|
96
|
+
raise e
|
97
|
+
end
|
98
|
+
|
99
|
+
def populate_ref(node,ref,model)
|
100
|
+
log("populate ref #{ref.name}, node: #{node.class}, model: #{model.class}")
|
101
|
+
value = get_feature_value(node,ref)
|
102
|
+
log("\tvalue=#{value.class}")
|
103
|
+
if value!=nil
|
104
|
+
if value==node
|
105
|
+
puts "avoiding loop... #{ref.name}, class #{node.class}"
|
106
|
+
return
|
107
|
+
end
|
108
|
+
if JavaCollection.assignable_from?(value.java_class)
|
109
|
+
log("\tvalue is a collection")
|
110
|
+
capitalized_name = ref.name.proper_capitalize
|
111
|
+
value.each do |el|
|
112
|
+
unless el.respond_to?(:parent)
|
113
|
+
class << el
|
114
|
+
attr_accessor :parent
|
115
|
+
end
|
116
|
+
end
|
117
|
+
el.parent = node
|
118
|
+
model.send(:"add#{capitalized_name}",node_to_model(el))
|
119
|
+
end
|
120
|
+
else
|
121
|
+
log("\tvalue is not a collection")
|
122
|
+
unless value.respond_to?(:parent)
|
123
|
+
class << value
|
124
|
+
attr_accessor :parent
|
125
|
+
end
|
126
|
+
end
|
127
|
+
value.parent = node
|
128
|
+
model.send(:"#{ref.name}=",node_to_model(value))
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def log(msg)
|
134
|
+
puts msg if @@verbose
|
135
|
+
end
|
136
|
+
|
137
|
+
def node_to_model(node)
|
138
|
+
log("node_to_model #{node.class}")
|
139
|
+
metaclass = get_corresponding_metaclass(node)
|
140
|
+
instance = metaclass.new
|
141
|
+
metaclass.ecore.eAllAttributes.each do |attr|
|
142
|
+
populate_attr(node,attr,instance)
|
143
|
+
end
|
144
|
+
metaclass.ecore.eAllReferences.each do |ref|
|
145
|
+
populate_ref(node,ref,instance)
|
146
|
+
end
|
147
|
+
instance
|
148
|
+
end
|
149
|
+
|
150
|
+
def transform_enum_values(value)
|
151
|
+
if value.respond_to?(:java_class) && value.java_class.enum?
|
152
|
+
value.name
|
153
|
+
else
|
154
|
+
value
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def get_feature_value_through_getter(node,feat_name)
|
159
|
+
capitalized_name = feat_name.proper_capitalize
|
160
|
+
methods = [:"get#{capitalized_name}",:"is#{capitalized_name}"]
|
161
|
+
|
162
|
+
methods.each do |m|
|
163
|
+
if node.respond_to?(m)
|
164
|
+
begin
|
165
|
+
return transform_enum_values(node.send(m))
|
166
|
+
rescue Object => e
|
167
|
+
raise "Problem invoking #{m} on #{node.class}: #{e}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
raise "how should I get this... #{feat_name} on #{node.class}. It does not respond to #{methods}"
|
172
|
+
end
|
173
|
+
|
174
|
+
def get_feature_value(node,feat)
|
175
|
+
adapter = adapter(node.class,feat)
|
176
|
+
if adapter
|
177
|
+
#puts "Using adapter for #{node.class} #{feat_name}"
|
178
|
+
adapter[:adapter].call(node)
|
179
|
+
else
|
180
|
+
get_feature_value_through_getter(node,feat.name)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|