neo4j 1.0.0.beta.20 → 3.0.0.alpha.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +243 -0
- data/CONTRIBUTORS +12 -0
- data/Gemfile +10 -11
- data/README.md +23 -0
- data/bin/neo4j-jars +33 -0
- data/config/locales/en.yml +5 -0
- data/config/neo4j/config.yml +98 -0
- data/lib/neo4j.rb +28 -68
- data/lib/neo4j/active_node.rb +60 -0
- data/lib/neo4j/active_node/callbacks.rb +41 -0
- data/lib/neo4j/active_node/has_n.rb +138 -0
- data/lib/neo4j/active_node/has_n/decl_rel.rb +236 -0
- data/lib/neo4j/active_node/has_n/nodes.rb +82 -0
- data/lib/neo4j/active_node/identity.rb +28 -0
- data/lib/neo4j/active_node/initialize.rb +24 -0
- data/lib/neo4j/active_node/labels.rb +142 -0
- data/lib/neo4j/active_node/persistence.rb +193 -0
- data/lib/neo4j/active_node/property.rb +41 -0
- data/lib/neo4j/active_node/rels.rb +11 -0
- data/lib/neo4j/active_node/validations.rb +51 -0
- data/lib/neo4j/railtie.rb +40 -0
- data/lib/neo4j/version.rb +1 -1
- data/lib/neo4j/wrapper.rb +25 -0
- data/neo4j.gemspec +25 -15
- metadata +136 -149
- data/README.rdoc +0 -135
- data/lib/generators/neo4j.rb +0 -65
- data/lib/generators/neo4j/model/model_generator.rb +0 -39
- data/lib/generators/neo4j/model/templates/model.erb +0 -7
- data/lib/neo4j/config.rb +0 -153
- data/lib/neo4j/database.rb +0 -56
- data/lib/neo4j/equal.rb +0 -21
- data/lib/neo4j/event_handler.rb +0 -116
- data/lib/neo4j/index/class_methods.rb +0 -62
- data/lib/neo4j/index/index.rb +0 -33
- data/lib/neo4j/index/indexer.rb +0 -312
- data/lib/neo4j/index/indexer_registry.rb +0 -68
- data/lib/neo4j/index/lucene_query.rb +0 -191
- data/lib/neo4j/jars/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
- data/lib/neo4j/jars/lucene-core-3.0.2.jar +0 -0
- data/lib/neo4j/jars/neo4j-index-1.2-1.2.M03.jar +0 -0
- data/lib/neo4j/jars/neo4j-kernel-1.2-1.2.M03.jar +0 -0
- data/lib/neo4j/jars/neo4j-lucene-index-0.2-1.2.M03.jar +0 -0
- data/lib/neo4j/load.rb +0 -21
- data/lib/neo4j/mapping/class_methods/init_node.rb +0 -50
- data/lib/neo4j/mapping/class_methods/init_rel.rb +0 -35
- data/lib/neo4j/mapping/class_methods/property.rb +0 -80
- data/lib/neo4j/mapping/class_methods/relationship.rb +0 -90
- data/lib/neo4j/mapping/class_methods/root.rb +0 -31
- data/lib/neo4j/mapping/class_methods/rule.rb +0 -295
- data/lib/neo4j/mapping/decl_relationship_dsl.rb +0 -214
- data/lib/neo4j/mapping/has_n.rb +0 -83
- data/lib/neo4j/mapping/node_mixin.rb +0 -97
- data/lib/neo4j/mapping/relationship_mixin.rb +0 -117
- data/lib/neo4j/model.rb +0 -4
- data/lib/neo4j/neo4j.rb +0 -95
- data/lib/neo4j/node.rb +0 -131
- data/lib/neo4j/node_mixin.rb +0 -4
- data/lib/neo4j/node_relationship.rb +0 -149
- data/lib/neo4j/node_traverser.rb +0 -157
- data/lib/neo4j/property.rb +0 -111
- data/lib/neo4j/rails/finders.rb +0 -121
- data/lib/neo4j/rails/lucene_connection_closer.rb +0 -19
- data/lib/neo4j/rails/mapping/property.rb +0 -35
- data/lib/neo4j/rails/model.rb +0 -324
- data/lib/neo4j/rails/railtie.rb +0 -16
- data/lib/neo4j/rails/transaction.rb +0 -67
- data/lib/neo4j/rails/tx_methods.rb +0 -15
- data/lib/neo4j/rails/validations/non_nil.rb +0 -11
- data/lib/neo4j/rails/validations/uniqueness.rb +0 -31
- data/lib/neo4j/rails/value.rb +0 -124
- data/lib/neo4j/rails/value_properties.rb +0 -29
- data/lib/neo4j/relationship.rb +0 -169
- data/lib/neo4j/relationship_mixin.rb +0 -4
- data/lib/neo4j/relationship_traverser.rb +0 -92
- data/lib/neo4j/to_java.rb +0 -31
- data/lib/neo4j/transaction.rb +0 -68
- data/lib/neo4j/type_converters.rb +0 -98
@@ -0,0 +1,82 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module ActiveNode
|
3
|
+
module HasN
|
4
|
+
|
5
|
+
# The object created by a has_n or has_one Neo4j::NodeMixin class method which enables creating and traversal of nodes.
|
6
|
+
#
|
7
|
+
# @see Neo4j::ActiveNode::HasN::ClassMethods
|
8
|
+
class Nodes
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
def initialize(node, decl_rel) # :nodoc:
|
12
|
+
@node = node
|
13
|
+
@decl_rel = decl_rel
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"HasN::Nodes [#{@decl_rel.dir}, id: #{@node.neo_id} type: #{@decl_rel.rel_type} decl_rel:#{@decl_rel}]"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Traverse the relationship till the index position
|
21
|
+
# @return [Neo4j::ActiveMixin,Neo4j::Node,nil] the node at the given position
|
22
|
+
def [](index)
|
23
|
+
i = 0
|
24
|
+
each { |x| return x if i == index; i += 1 }
|
25
|
+
nil # out of index
|
26
|
+
end
|
27
|
+
|
28
|
+
# Pretend we are an array - this is necessarily for Rails actionpack/actionview/formhelper to work with this
|
29
|
+
def is_a?(type)
|
30
|
+
# ActionView requires this for nested attributes to work
|
31
|
+
return true if Array == type
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
# Required by the Enumerable mixin.
|
36
|
+
def each
|
37
|
+
@decl_rel.each_node(@node) { |n| yield n } # Should use yield here as passing &block through doesn't always work (why?)
|
38
|
+
end
|
39
|
+
|
40
|
+
# returns none wrapped nodes, you may get better performance using this method
|
41
|
+
def _each
|
42
|
+
@decl_rel._each_node(@node) { |n| yield n }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns an real ruby array.
|
46
|
+
def to_ary
|
47
|
+
self.to_a
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns true if there are no node in this type of relationship
|
51
|
+
def empty?
|
52
|
+
first == nil
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# Creates a relationship instance between this and the other node.
|
57
|
+
# Returns the relationship object
|
58
|
+
def create(other)
|
59
|
+
@decl_rel.create_relationship_to(@node, other)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# Creates a relationship between this and the other node.
|
64
|
+
#
|
65
|
+
# @example Person includes the Neo4j::NodeMixin and declares a has_n :friends
|
66
|
+
#
|
67
|
+
# p = Person.new # Node has declared having a friend type of relationship
|
68
|
+
# n1 = Node.new
|
69
|
+
# n2 = Node.new
|
70
|
+
#
|
71
|
+
# p.friends << n2 << n3
|
72
|
+
#
|
73
|
+
# @return self
|
74
|
+
def <<(other)
|
75
|
+
@decl_rel.create_relationship_to(@node, other)
|
76
|
+
self
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Neo4j::ActiveNode
|
2
|
+
module Identity
|
3
|
+
|
4
|
+
def ==(o)
|
5
|
+
o.class == self.class && o.id == id
|
6
|
+
end
|
7
|
+
alias_method :eql?, :==
|
8
|
+
|
9
|
+
# Returns an Enumerable of all (primary) key attributes
|
10
|
+
# or nil if model.persisted? is false
|
11
|
+
def to_key
|
12
|
+
persisted? ? [id] : nil
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Fixnum, nil] the neo4j id of the node if persisted or nil
|
16
|
+
def neo_id
|
17
|
+
_persisted_node ? _persisted_node.neo_id : nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String, nil] same as #neo_id
|
21
|
+
def id
|
22
|
+
persisted? ? neo_id.to_s : nil
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Neo4j::ActiveNode::Initialize
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
attr_reader :_persisted_node
|
5
|
+
|
6
|
+
# called when loading the node from the database
|
7
|
+
# @param [Neo4j::Node] persisted_node the node this class wraps
|
8
|
+
# @param [Hash] properties properties of the persisted node.
|
9
|
+
def init_on_load(persisted_node, properties)
|
10
|
+
@_persisted_node = persisted_node
|
11
|
+
@changed_attributes && @changed_attributes.clear
|
12
|
+
@attributes = attributes.merge(properties.stringify_keys)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Implements the Neo4j::Node#wrapper and Neo4j::Relationship#wrapper method
|
16
|
+
# so that we don't have to care if the node is wrapped or not.
|
17
|
+
# @return self
|
18
|
+
def wrapper
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module ActiveNode
|
3
|
+
|
4
|
+
|
5
|
+
# Provides a mapping between neo4j labels and Ruby classes
|
6
|
+
module Labels
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
WRAPPED_CLASSES = []
|
10
|
+
|
11
|
+
def labels
|
12
|
+
@_persisted_node.labels
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(klass)
|
16
|
+
add_wrapped_class(klass)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.add_wrapped_class(klass)
|
20
|
+
_wrapped_classes << klass
|
21
|
+
@_wrapped_labels = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def self._wrapped_classes
|
26
|
+
Neo4j::ActiveNode::Labels::WRAPPED_CLASSES
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
# Only for testing purpose
|
32
|
+
# @private
|
33
|
+
def self._wrapped_labels=(wl)
|
34
|
+
@_wrapped_labels=(wl)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self._wrapped_labels
|
38
|
+
@_wrapped_labels ||= _wrapped_classes.inject({}) do |ack, clazz|
|
39
|
+
ack.tap do |a|
|
40
|
+
a[clazz.mapped_label_name.to_sym] = clazz if clazz.respond_to?(:mapped_label_name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module ClassMethods
|
46
|
+
|
47
|
+
# Find all nodes/objects of this class, with given search criteria
|
48
|
+
# @param [Hash, nil] args the search critera or nil if finding all
|
49
|
+
# @param [Neo4j::Session] session defaults to the current
|
50
|
+
def all(args = nil, session = Neo4j::Session.current)
|
51
|
+
if (args)
|
52
|
+
find_by_hash(args, session)
|
53
|
+
else
|
54
|
+
Neo4j::Label.find_all_nodes(mapped_label_name, session)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [Fixnum] number of nodes of this class
|
59
|
+
def count(session = Neo4j::Session.current)
|
60
|
+
q = session.query("MATCH (n:`#{mapped_label_name}`) RETURN count(n) AS count")
|
61
|
+
q.to_a[0][:count]
|
62
|
+
end
|
63
|
+
|
64
|
+
# Same as #all but return only one object
|
65
|
+
# If given a String or Fixnum it will return the object with that neo4j id.
|
66
|
+
# @param [Hash,String,Fixnum] args search criteria
|
67
|
+
def find(args, session = Neo4j::Session.current)
|
68
|
+
case args
|
69
|
+
when Hash
|
70
|
+
find_by_hash(args, session).first
|
71
|
+
when String, Fixnum
|
72
|
+
Neo4j::Node.load(args)
|
73
|
+
else
|
74
|
+
raise "Unknown argument #{args.class} in find method"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# Destroy all nodes an connected relationships
|
80
|
+
def destroy_all
|
81
|
+
Neo4j::Session.current._query("MATCH (n:`#{mapped_label_name}`)-[r]-() DELETE n,r")
|
82
|
+
Neo4j::Session.current._query("MATCH (n:`#{mapped_label_name}`) DELETE n")
|
83
|
+
end
|
84
|
+
|
85
|
+
# Creates a Neo4j index on given property
|
86
|
+
# @param [Symbol] property the property we want a Neo4j index on
|
87
|
+
def index(property)
|
88
|
+
if Neo4j::Session.current
|
89
|
+
_index(property)
|
90
|
+
else
|
91
|
+
Neo4j::Session.add_listener do |event, _|
|
92
|
+
_index(property) if event == :session_available
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# @return [Array{Symbol}] all the labels that this class has
|
99
|
+
def mapped_label_names
|
100
|
+
self.ancestors.find_all { |a| a.respond_to?(:mapped_label_name) }.map { |a| a.mapped_label_name.to_sym }
|
101
|
+
end
|
102
|
+
|
103
|
+
# @return [Symbol] the label that this class has which corresponds to a Ruby class
|
104
|
+
def mapped_label_name
|
105
|
+
@_label_name || self.to_s.to_sym
|
106
|
+
end
|
107
|
+
|
108
|
+
def indexed_labels
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
|
114
|
+
def find_by_hash(hash, session)
|
115
|
+
Neo4j::Label.query(mapped_label_name, {conditions: hash}, session)
|
116
|
+
end
|
117
|
+
|
118
|
+
def _index(property)
|
119
|
+
mapped_labels.each do |label|
|
120
|
+
# make sure the property is not indexed twice
|
121
|
+
existing = label.indexes[:property_keys]
|
122
|
+
label.create_index(property) unless existing.flatten.include?(property)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def mapped_labels
|
127
|
+
mapped_label_names.map{|label_name| Neo4j::Label.create(label_name)}
|
128
|
+
end
|
129
|
+
|
130
|
+
def mapped_label
|
131
|
+
Neo4j::Label.create(mapped_label_name)
|
132
|
+
end
|
133
|
+
|
134
|
+
def set_mapped_label_name(name)
|
135
|
+
@_label_name = name.to_sym
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module Neo4j::ActiveNode
|
2
|
+
module Persistence
|
3
|
+
class RecordInvalidError < RuntimeError
|
4
|
+
attr_reader :record
|
5
|
+
|
6
|
+
def initialize(record)
|
7
|
+
@record = record
|
8
|
+
super(@record.errors.full_messages.join(", "))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
# Saves the model.
|
15
|
+
#
|
16
|
+
# If the model is new a record gets created in the database, otherwise the existing record gets updated.
|
17
|
+
# If perform_validation is true validations run.
|
18
|
+
# If any of them fail the action is cancelled and save returns false. If the flag is false validations are bypassed altogether. See ActiveRecord::Validations for more information.
|
19
|
+
# There’s a series of callbacks associated with save. If any of the before_* callbacks return false the action is cancelled and save returns false.
|
20
|
+
def save(*)
|
21
|
+
create_or_update
|
22
|
+
end
|
23
|
+
|
24
|
+
# Creates a model with values matching those of the instance attributes and returns its id.
|
25
|
+
# @private
|
26
|
+
# @return true
|
27
|
+
def create_model(*)
|
28
|
+
node = _create_node(props)
|
29
|
+
init_on_load(node, node.props)
|
30
|
+
# Neo4j::IdentityMap.add(node, self)
|
31
|
+
# write_changed_relationships
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
# Persist the object to the database. Validations and Callbacks are included
|
36
|
+
# by default but validation can be disabled by passing :validate => false
|
37
|
+
# to #save! Creates a new transaction.
|
38
|
+
#
|
39
|
+
# @raise a RecordInvalidError if there is a problem during save.
|
40
|
+
# @param (see Neo4j::Rails::Validations#save)
|
41
|
+
# @return nil
|
42
|
+
# @see #save
|
43
|
+
# @see Neo4j::Rails::Validations Neo4j::Rails::Validations - for the :validate parameter
|
44
|
+
# @see Neo4j::Rails::Callbacks Neo4j::Rails::Callbacks - for callbacks
|
45
|
+
def save!(*args)
|
46
|
+
unless save(*args)
|
47
|
+
raise RecordInvalidError.new(self)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_or_update
|
52
|
+
# since the same model can be created or updated twice from a relationship we have to have this guard
|
53
|
+
@_create_or_updating = true
|
54
|
+
result = persisted? ? update_model : create_model
|
55
|
+
unless result != false
|
56
|
+
Neo4j::Transaction.current.fail if Neo4j::Transaction.current
|
57
|
+
false
|
58
|
+
else
|
59
|
+
true
|
60
|
+
end
|
61
|
+
rescue => e
|
62
|
+
Neo4j::Transaction.current.fail if Neo4j::Transaction.current
|
63
|
+
raise e
|
64
|
+
ensure
|
65
|
+
@_create_or_updating = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def exist?
|
69
|
+
_persisted_node && _persisted_node.exist?
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns +true+ if the object was destroyed.
|
73
|
+
def destroyed?
|
74
|
+
@_deleted || (!new_record? && !exist?)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns +true+ if the record is persisted, i.e. it’s not a new record and it was not destroyed
|
78
|
+
def persisted?
|
79
|
+
!new_record? && !destroyed?
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns +true+ if the record hasn't been saved to Neo4j yet.
|
83
|
+
def new_record?
|
84
|
+
! _persisted_node
|
85
|
+
end
|
86
|
+
|
87
|
+
alias :new? :new_record?
|
88
|
+
|
89
|
+
def destroy
|
90
|
+
_persisted_node && _persisted_node.del
|
91
|
+
@_deleted = true
|
92
|
+
end
|
93
|
+
|
94
|
+
def update_model
|
95
|
+
if @changed_attributes && !@changed_attributes.empty?
|
96
|
+
changed_props = attributes.select{|k,v| @changed_attributes.include?(k)}
|
97
|
+
_persisted_node.props = changed_props
|
98
|
+
@changed_attributes.clear
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def _create_node(*args)
|
103
|
+
session = Neo4j::Session.current
|
104
|
+
props = args[0] if args[0].is_a?(Hash)
|
105
|
+
labels = self.class.mapped_label_names
|
106
|
+
session.create_node(props, labels)
|
107
|
+
end
|
108
|
+
|
109
|
+
# @return [Hash] all defined and none nil properties
|
110
|
+
def props
|
111
|
+
attributes.reject{|k,v| v.nil?}.symbolize_keys
|
112
|
+
end
|
113
|
+
|
114
|
+
# @return true if the attributes hash has been frozen
|
115
|
+
def frozen?
|
116
|
+
freeze_if_deleted
|
117
|
+
@attributes.frozen?
|
118
|
+
end
|
119
|
+
|
120
|
+
def freeze
|
121
|
+
@attributes.freeze
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
def freeze_if_deleted
|
126
|
+
unless new_record?
|
127
|
+
# TODO - Neo4j::IdentityMap.remove_node_by_id(neo_id)
|
128
|
+
unless self.class.load_entity(neo_id)
|
129
|
+
@_deleted = true
|
130
|
+
freeze
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def reload
|
136
|
+
return self if new_record?
|
137
|
+
@changed_attributes && @changed_attributes.clear
|
138
|
+
unless reload_from_database
|
139
|
+
@_deleted = true
|
140
|
+
freeze
|
141
|
+
end
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
145
|
+
def reload_from_database
|
146
|
+
# TODO - Neo4j::IdentityMap.remove_node_by_id(neo_id)
|
147
|
+
if reloaded = self.class.load_entity(neo_id)
|
148
|
+
send(:attributes=, reloaded.attributes)
|
149
|
+
end
|
150
|
+
reloaded
|
151
|
+
end
|
152
|
+
|
153
|
+
# Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved.
|
154
|
+
# If saving fails because the resource is invalid then false will be returned.
|
155
|
+
def update(attributes)
|
156
|
+
self.attributes = attributes
|
157
|
+
save
|
158
|
+
end
|
159
|
+
alias_method :update_attributes, :update
|
160
|
+
|
161
|
+
# Same as {#update_attributes}, but raises an exception if saving fails.
|
162
|
+
def update!(attributes)
|
163
|
+
self.attributes = attributes
|
164
|
+
save!
|
165
|
+
end
|
166
|
+
alias_method :update_attributes!, :update!
|
167
|
+
|
168
|
+
module ClassMethods
|
169
|
+
# Creates a saves a new node
|
170
|
+
# @param [Hash] props the properties the new node should have
|
171
|
+
def create(props = {})
|
172
|
+
new(props).tap do |obj|
|
173
|
+
obj.save
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Same as #create, but raises an error if there is a problem during save.
|
178
|
+
def create!(*args)
|
179
|
+
new(*args).tap do |o|
|
180
|
+
yield o if block_given?
|
181
|
+
o.save!
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def load_entity(id)
|
186
|
+
Neo4j::Node.load(id)
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|