persistence 0.0.1.alpha → 0.0.1

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.
Files changed (138) hide show
  1. data/CHANGELOG.md +4 -0
  2. data/README.md +260 -17
  3. data/lib/namespaces.rb +55 -0
  4. data/lib/persistence.rb +38 -3
  5. data/lib/persistence/adapter/abstract.rb +22 -0
  6. data/lib/persistence/adapter/abstract/enable_disable.rb +107 -0
  7. data/lib/persistence/adapter/abstract/primary_key/id_property_string.rb +33 -0
  8. data/lib/persistence/adapter/abstract/primary_key/simple.rb +29 -0
  9. data/lib/persistence/adapter/mock.rb +9 -0
  10. data/lib/persistence/adapter/mock/adapter_interface.rb +102 -0
  11. data/lib/persistence/adapter/mock/bucket.rb +9 -0
  12. data/lib/persistence/adapter/mock/bucket/bucket_interface.rb +260 -0
  13. data/lib/persistence/adapter/mock/bucket/index.rb +9 -0
  14. data/lib/persistence/adapter/mock/bucket/index/index_interface.rb +155 -0
  15. data/lib/persistence/adapter/mock/cursor.rb +9 -0
  16. data/lib/persistence/adapter/mock/cursor/cursor_interface.rb +238 -0
  17. data/lib/persistence/cursor.rb +11 -0
  18. data/lib/persistence/cursor/atomic.rb +110 -0
  19. data/lib/persistence/cursor/cursor_interface.rb +337 -0
  20. data/lib/persistence/exception/block_required.rb +7 -0
  21. data/lib/persistence/exception/conflicting_index_already_declared.rb +7 -0
  22. data/lib/persistence/exception/duplicate_violates_unique_index.rb +7 -0
  23. data/lib/persistence/exception/explicit_index_required.rb +7 -0
  24. data/lib/persistence/exception/indexing_block_failed_to_generate_keys.rb +7 -0
  25. data/lib/persistence/exception/indexing_object_requires_keys.rb +7 -0
  26. data/lib/persistence/exception/key_value_required.rb +7 -0
  27. data/lib/persistence/exception/no_port_enabled.rb +7 -0
  28. data/lib/persistence/object.rb +21 -0
  29. data/lib/persistence/object/autodetermine.rb +74 -0
  30. data/lib/persistence/object/class_instance.rb +1884 -0
  31. data/lib/persistence/object/complex.rb +17 -0
  32. data/lib/persistence/object/complex/array.rb +14 -0
  33. data/lib/persistence/object/complex/array/class_instance.rb +37 -0
  34. data/lib/persistence/object/complex/array/object_instance.rb +54 -0
  35. data/lib/persistence/object/complex/attributes.rb +1808 -0
  36. data/lib/persistence/object/complex/attributes/attributes_array.rb +32 -0
  37. data/lib/persistence/object/complex/attributes/attributes_hash.rb +187 -0
  38. data/lib/persistence/object/complex/attributes/default_atomic_non_atomic.rb +102 -0
  39. data/lib/persistence/object/complex/attributes/hash_to_port.rb +40 -0
  40. data/lib/persistence/object/complex/class_and_object_instance.rb +132 -0
  41. data/lib/persistence/object/complex/class_instance.rb +267 -0
  42. data/lib/persistence/object/complex/complex_object.rb +111 -0
  43. data/lib/persistence/object/complex/hash.rb +14 -0
  44. data/lib/persistence/object/complex/hash/class_instance.rb +40 -0
  45. data/lib/persistence/object/complex/hash/object_instance.rb +63 -0
  46. data/lib/persistence/object/complex/index/attribute_index.rb +10 -0
  47. data/lib/persistence/object/complex/index/attribute_index/attribute_index_interface.rb +43 -0
  48. data/lib/persistence/object/complex/object_instance.rb +469 -0
  49. data/lib/persistence/object/flat.rb +17 -0
  50. data/lib/persistence/object/flat/class_instance.rb +34 -0
  51. data/lib/persistence/object/flat/file.rb +14 -0
  52. data/lib/persistence/object/flat/file/class_instance.rb +122 -0
  53. data/lib/persistence/object/flat/file/contents.rb +7 -0
  54. data/lib/persistence/object/flat/file/file_persistence.rb +147 -0
  55. data/lib/persistence/object/flat/file/object_instance.rb +116 -0
  56. data/lib/persistence/object/flat/file/path.rb +9 -0
  57. data/lib/persistence/object/flat/object_instance.rb +24 -0
  58. data/lib/persistence/object/index.rb +479 -0
  59. data/lib/persistence/object/index/block_index.rb +10 -0
  60. data/lib/persistence/object/index/block_index/block_index_interface.rb +110 -0
  61. data/lib/persistence/object/index/explicit_index.rb +10 -0
  62. data/lib/persistence/object/index/explicit_index/explicit_index_interface.rb +57 -0
  63. data/lib/persistence/object/index_hash.rb +40 -0
  64. data/lib/persistence/object/object_instance.rb +322 -0
  65. data/lib/persistence/object/parse_persistence_args.rb +145 -0
  66. data/lib/persistence/port.rb +9 -0
  67. data/lib/persistence/port/bucket.rb +9 -0
  68. data/lib/persistence/port/bucket/bucket_index.rb +9 -0
  69. data/lib/persistence/port/bucket/bucket_interface.rb +685 -0
  70. data/lib/persistence/port/controller.rb +263 -0
  71. data/lib/persistence/port/port_interface.rb +417 -0
  72. data/lib/requires.rb +146 -0
  73. data/spec/Integration_spec.rb +53 -0
  74. data/spec/Persistence_spec.rb +175 -0
  75. data/spec/example_objects.rb +6 -0
  76. data/spec/example_objects/complex_object.rb +7 -0
  77. data/spec/example_objects/complex_object/array_object.rb +7 -0
  78. data/spec/example_objects/complex_object/hash_object.rb +7 -0
  79. data/spec/example_objects/flat_object.rb +7 -0
  80. data/spec/example_objects/flat_object/file_object.rb +7 -0
  81. data/spec/persistence/adapter/enable_disable_spec.rb +29 -0
  82. data/spec/persistence/adapter/mock/cursor_spec.rb +64 -0
  83. data/spec/persistence/adapter/mock_helpers.rb +27 -0
  84. data/spec/persistence/adapter/mock_helpers/bucket.rb +10 -0
  85. data/spec/persistence/adapter/mock_helpers/integration/dictionary_hash.rb +4 -0
  86. data/spec/persistence/adapter/mock_helpers/integration/note.rb +18 -0
  87. data/spec/persistence/adapter/mock_helpers/integration/notes_array.rb +4 -0
  88. data/spec/persistence/adapter/mock_helpers/integration/user.rb +44 -0
  89. data/spec/persistence/adapter/mock_helpers/integration/user/address.rb +18 -0
  90. data/spec/persistence/adapter/mock_helpers/integration/user/dictionary_entry.rb +12 -0
  91. data/spec/persistence/adapter/mock_helpers/integration/user/sub_account.rb +15 -0
  92. data/spec/persistence/adapter/mock_helpers/object.rb +87 -0
  93. data/spec/persistence/adapter/mock_helpers/port.rb +21 -0
  94. data/spec/persistence/adapter/mock_spec.rb +211 -0
  95. data/spec/persistence/adapter/primary_key/id_property_string_spec.rb +27 -0
  96. data/spec/persistence/adapter/primary_key/simple_spec.rb +19 -0
  97. data/spec/persistence/adapter/spec_abstract/adapter_spec.rb +223 -0
  98. data/spec/persistence/adapter/spec_abstract/cursor_spec.rb +116 -0
  99. data/spec/persistence/cursor/atomic_spec.rb +86 -0
  100. data/spec/persistence/cursor/object_and_class_instance_spec.rb +73 -0
  101. data/spec/persistence/cursor_spec.rb +128 -0
  102. data/spec/persistence/object/complex/attributes/persistence_hash/array_instance_spec.rb +51 -0
  103. data/spec/persistence/object/complex/attributes/persistence_hash/hash_instance_spec.rb +56 -0
  104. data/spec/persistence/object/complex/attributes_spec.rb +1717 -0
  105. data/spec/persistence/object/complex/complex_spec.rb +922 -0
  106. data/spec/persistence/object/complex/index/attribute_index_spec.rb +76 -0
  107. data/spec/persistence/object/flat/bignum_spec.rb +33 -0
  108. data/spec/persistence/object/flat/class_instance_spec.rb +30 -0
  109. data/spec/persistence/object/flat/class_spec.rb +38 -0
  110. data/spec/persistence/object/flat/complex_spec.rb +36 -0
  111. data/spec/persistence/object/flat/false_class_spec.rb +34 -0
  112. data/spec/persistence/object/flat/file/class_instance_spec.rb +54 -0
  113. data/spec/persistence/object/flat/file/object_instance_spec.rb +143 -0
  114. data/spec/persistence/object/flat/file_spec.rb +64 -0
  115. data/spec/persistence/object/flat/fixnum_spec.rb +32 -0
  116. data/spec/persistence/object/flat/float_spec.rb +32 -0
  117. data/spec/persistence/object/flat/indexing_spec.rb +38 -0
  118. data/spec/persistence/object/flat/rational_spec.rb +33 -0
  119. data/spec/persistence/object/flat/regexp_spec.rb +32 -0
  120. data/spec/persistence/object/flat/string_spec.rb +34 -0
  121. data/spec/persistence/object/flat/symbol_spec.rb +32 -0
  122. data/spec/persistence/object/flat/true_class_spec.rb +32 -0
  123. data/spec/persistence/object/indexes/block_index_spec.rb +119 -0
  124. data/spec/persistence/object/indexes/explicit_index_spec.rb +112 -0
  125. data/spec/persistence/object/parse_persistence_args_spec.rb +65 -0
  126. data/spec/persistence/object_spec.rb +310 -0
  127. data/spec/persistence/port/bucket/bucket_interface_spec.rb +146 -0
  128. data/spec/persistence/port/bucket/index/bucket_index_spec.rb +67 -0
  129. data/spec/persistence/port/bucket_spec.rb +20 -0
  130. data/spec/persistence/port/controller_spec.rb +60 -0
  131. data/spec/persistence/port/port_interface_spec.rb +105 -0
  132. metadata +178 -21
  133. data/.gitignore +0 -17
  134. data/Gemfile +0 -4
  135. data/LICENSE +0 -22
  136. data/Rakefile +0 -2
  137. data/lib/persistence/version.rb +0 -3
  138. data/persistence.gemspec +0 -17
@@ -0,0 +1,17 @@
1
+
2
+ ###
3
+ # Convenience module for extending flat object types (singletons and instances) with persistence capabilities.
4
+ # Flat object types include: Bignum, Fixnum, Complex, Rational, TrueClass, FalseClass,
5
+ # String, Symbol, Regexp, File, NilClass.
6
+ #
7
+ module ::Persistence::Object::Flat
8
+
9
+ extend ModuleCluster::Define::ClusterCascades
10
+
11
+ include ::Persistence::Object::ObjectInstance
12
+ include_or_extend_cascades_prepend_extends ::Persistence::Object::ClassInstance
13
+
14
+ include ::Persistence::Object::Flat::ObjectInstance
15
+ include_or_extend_cascades_prepend_extends ::Persistence::Object::Flat::ClassInstance
16
+
17
+ end
@@ -0,0 +1,34 @@
1
+
2
+ ###
3
+ # Class methods for flat objects enabled with persistence capabilities.
4
+ #
5
+ module ::Persistence::Object::Flat::ClassInstance
6
+
7
+ #############
8
+ # persist #
9
+ #############
10
+
11
+ def persist( *args )
12
+
13
+ persistence_value = nil
14
+
15
+ index_instance, key, no_key = parse_args_for_index_value_no_value( args )
16
+
17
+ # if no key, open a cursor for a list
18
+ if no_key
19
+
20
+ persistence_value = ::Persistence::Cursor.new( instance_persistence_bucket, index_instance )
21
+
22
+ else
23
+
24
+ global_id = index_instance ? index_instance.get_object_id( key ) : key
25
+
26
+ persistence_value = instance_persistence_port.get_flat_object( global_id )
27
+
28
+ end
29
+
30
+ return persistence_value
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,14 @@
1
+
2
+ ###
3
+ # Convenience module for extending File with persistence capabilities.
4
+ #
5
+ module ::Persistence::Object::Flat::File
6
+
7
+ extend ModuleCluster::Define::ClusterCascades
8
+
9
+ include ::Persistence::Object::Flat
10
+
11
+ include ::Persistence::Object::Flat::File::ObjectInstance
12
+ include_or_extend_cascades_prepend_extends ::Persistence::Object::Flat::File::ClassInstance
13
+
14
+ end
@@ -0,0 +1,122 @@
1
+
2
+ ###
3
+ # Class methods for File objects enabled with persistence capabilities.
4
+ #
5
+ module ::Persistence::Object::Flat::File::ClassInstance
6
+
7
+ include ::Persistence::Object::Flat::File::FilePersistence
8
+
9
+ #############
10
+ # persist #
11
+ #############
12
+
13
+ def persist( *args )
14
+
15
+ persistence_value = super
16
+
17
+ if persists_file_paths_as_objects? and
18
+ persistence_value.is_a?( ::Persistence::Object::Flat::File::Path )
19
+
20
+ persistence_value = File.open( persistence_value,
21
+ persistence_value.mode || 'r' )
22
+
23
+ end
24
+
25
+ return persistence_value
26
+
27
+ end
28
+
29
+ ################################
30
+ # persists_files_by_content? #
31
+ ################################
32
+
33
+ def persists_files_by_content?
34
+
35
+ persists_files_by_content = nil
36
+
37
+ persists_files_by_content = super
38
+
39
+ if persists_files_by_content.nil?
40
+ persists_files_by_content = instance_persistence_bucket.persists_files_by_content?
41
+ end
42
+
43
+ return persists_files_by_content
44
+
45
+ end
46
+
47
+ #############################
48
+ # persists_files_by_path? #
49
+ #############################
50
+
51
+ ###
52
+ # Query whether File instances should be persisted by path (rather than by content).
53
+ # Lookup chain is: File instance, File class, Persistence::Port::Bucket instance, Persistence::Port instance,
54
+ # Persistence singleton.
55
+ #
56
+ # @return [true,false] Whether files should be persisted by path rather than by content.
57
+ #
58
+ def persists_files_by_path?
59
+
60
+ persists_files_by_path = nil
61
+
62
+ persists_files_by_path = super
63
+
64
+ if persists_files_by_path.nil?
65
+ persists_files_by_path = instance_persistence_bucket.persists_files_by_path?
66
+ end
67
+
68
+ return persists_files_by_path
69
+
70
+ end
71
+
72
+ #####################################
73
+ # persists_file_paths_as_objects? #
74
+ #####################################
75
+
76
+ ###
77
+ # Query whether File paths should be persisted as objects (rather than by strings).
78
+ # Lookup chain is: File instance, File class, Persistence::Port::Bucket instance, Persistence::Port instance,
79
+ # Persistence singleton.
80
+ #
81
+ # @return [true,false] Whether files should be persisted as objects rather than as strings.
82
+ #
83
+ def persists_file_paths_as_objects?
84
+
85
+ persists_file_paths_as_objects = nil
86
+
87
+ persists_file_paths_as_objects = super
88
+
89
+ if persists_file_paths_as_objects.nil?
90
+ persists_file_paths_as_objects = instance_persistence_bucket.persists_file_paths_as_objects?
91
+ end
92
+
93
+ return persists_file_paths_as_objects
94
+
95
+ end
96
+
97
+ #####################################
98
+ # persists_file_paths_as_strings? #
99
+ #####################################
100
+
101
+ ###
102
+ # Query whether File paths should be persisted as strings (rather than by objects).
103
+ # Lookup chain is: File instance, File class, Persistence::Port::Bucket instance, Persistence::Port instance,
104
+ # Persistence singleton.
105
+ #
106
+ # @return [true,false] Whether files should be persisted as strings rather than as objects.
107
+ #
108
+ def persists_file_paths_as_strings?
109
+
110
+ persists_file_paths_as_strings = nil
111
+
112
+ persists_file_paths_as_strings = super
113
+
114
+ if persists_file_paths_as_strings.nil?
115
+ persists_file_paths_as_strings = instance_persistence_bucket.persists_file_paths_as_strings?
116
+ end
117
+
118
+ return persists_file_paths_as_strings
119
+
120
+ end
121
+
122
+ end
@@ -0,0 +1,7 @@
1
+
2
+ ###
3
+ # Subclass of {::String} used for persisting file contents.
4
+ #
5
+ class ::Persistence::Object::Flat::File::Contents < ::String
6
+
7
+ end
@@ -0,0 +1,147 @@
1
+
2
+ ###
3
+ # Common module used for look-up chain for file persistence configuration.
4
+ # Lookup chain is: File instance, File class, Persistence::Port::Bucket instance, Persistence::Port instance,
5
+ # Persistence singleton.
6
+ #
7
+ module ::Persistence::Object::Flat::File::FilePersistence
8
+
9
+ include ::CascadingConfiguration::Setting
10
+
11
+ extend self
12
+
13
+ ################################
14
+ # persists_files_by_content? #
15
+ ################################
16
+
17
+ ###
18
+ #
19
+ # @method persists_files_by_content?
20
+ #
21
+ # Query whether File instances should be persisted by content (rather than by path).
22
+ # Lookup chain is: File instance, File class, Persistence::Port::Bucket instance, Persistence::Port instance,
23
+ # Persistence singleton.
24
+ #
25
+ # @return [true,false] Whether files should be persisted by content rather than by path.
26
+ #
27
+ attr_setting :persists_files_by_content? => :persist_files_by_content=
28
+
29
+ #############################
30
+ # persists_files_by_path? #
31
+ #############################
32
+
33
+ ###
34
+ #
35
+ # @method persists_files_by_path?
36
+ #
37
+ # Query whether File instances should be persisted by path (rather than by content).
38
+ # Lookup chain is: File instance, File class, Persistence::Port::Bucket instance, Persistence::Port instance,
39
+ # Persistence singleton.
40
+ #
41
+ # @return [true,false] Whether files should be persisted by path rather than by content.
42
+ #
43
+ attr_setting :persists_files_by_path? => :persist_files_by_path=
44
+
45
+ #####################################
46
+ # persists_file_paths_as_objects? #
47
+ #####################################
48
+
49
+ ###
50
+ #
51
+ # @method persists_file_paths_as_objects?
52
+ #
53
+ # Query whether File paths should be persisted as objects (rather than by strings).
54
+ # Lookup chain is: File instance, File class, Persistence::Port::Bucket instance, Persistence::Port instance,
55
+ # Persistence singleton.
56
+ #
57
+ # @return [true,false] Whether files should be persisted as objects rather than as strings.
58
+ #
59
+ attr_setting :persists_file_paths_as_objects? => :persist_file_paths_as_objects=
60
+
61
+ #####################################
62
+ # persists_file_paths_as_strings? #
63
+ #####################################
64
+
65
+ ###
66
+ #
67
+ # @method persists_file_paths_as_strings?
68
+ #
69
+ # Query whether File paths should be persisted as strings (rather than by objects).
70
+ # Lookup chain is: File instance, File class, Persistence::Port::Bucket instance, Persistence::Port instance,
71
+ # Persistence singleton.
72
+ #
73
+ # @return [true,false] Whether files should be persisted as strings rather than as objects.
74
+ #
75
+ attr_setting :persists_file_paths_as_strings? => :persist_file_paths_as_strings=
76
+
77
+ ############################
78
+ # persist_files_by_path! #
79
+ ############################
80
+
81
+ ###
82
+ # Declare that files should be persisted by path (rather than by contents).
83
+ #
84
+ def persist_files_by_path!
85
+
86
+ self.persist_files_by_path = true
87
+ self.persist_files_by_content = false
88
+
89
+ return self
90
+
91
+ end
92
+
93
+ ###############################
94
+ # persist_files_by_content! #
95
+ ###############################
96
+
97
+ ###
98
+ # Declare that files should be persisted by contents (rather than by path).
99
+ #
100
+ def persist_files_by_content!
101
+
102
+ self.persist_files_by_content = true
103
+ self.persist_files_by_path = false
104
+
105
+ return self
106
+
107
+ end
108
+
109
+ alias_method( :persists_files_by_content!, :persist_files_by_content! )
110
+
111
+ ####################################
112
+ # persist_file_paths_as_objects! #
113
+ ####################################
114
+
115
+ ###
116
+ # Declare that file paths should be persisted as objects (rather than strings).
117
+ #
118
+ def persist_file_paths_as_objects!
119
+
120
+ self.persist_file_paths_as_objects = true
121
+ self.persist_file_paths_as_strings = false
122
+
123
+ return self
124
+
125
+ end
126
+
127
+ alias_method( :persists_file_paths_as_objects!, :persist_file_paths_as_objects! )
128
+
129
+ ####################################
130
+ # persist_file_paths_as_strings! #
131
+ ####################################
132
+
133
+ ###
134
+ # Declare that file paths should be persisted as strings (rather than objects).
135
+ #
136
+ def persist_file_paths_as_strings!
137
+
138
+ self.persist_file_paths_as_strings = true
139
+ self.persist_file_paths_as_objects = false
140
+
141
+ return self
142
+
143
+ end
144
+
145
+ alias_method( :persists_file_paths_as_strings!, :persist_file_paths_as_strings! )
146
+
147
+ end
@@ -0,0 +1,116 @@
1
+
2
+ ###
3
+ # Instance methods for File objects enabled with persistence capabilities.
4
+ #
5
+ module ::Persistence::Object::Flat::File::ObjectInstance
6
+
7
+ include ::Persistence::Object::Flat::File::FilePersistence
8
+
9
+ ##############
10
+ # persist! #
11
+ ##############
12
+
13
+ def persist!( *args )
14
+
15
+ starting_pos = self.pos
16
+
17
+ # if we are persisting by file path
18
+ persistence_instance = nil
19
+ if persists_files_by_path?
20
+ persistence_instance = ::Persistence::Object::Flat::File::Path.new( path )
21
+ # if we are persisting by file contents
22
+ else
23
+ persistence_instance = ::Persistence::Object::Flat::File::Contents.new( self.readlines.join )
24
+ end
25
+
26
+ persistence_instance.persistence_port = persistence_port
27
+ persistence_instance.persistence_bucket = persistence_bucket
28
+
29
+ # set new instance ID to current ID
30
+ persistence_instance.persistence_id = persistence_id
31
+
32
+ persistence_instance.persist!( *args )
33
+
34
+ # if we got ID from persist! on our instance set our own ID to it
35
+ self.persistence_id = persistence_instance.persistence_id
36
+
37
+ # rewind file to wherever we started
38
+ self.pos = starting_pos
39
+
40
+ return self
41
+
42
+ end
43
+
44
+ ################################
45
+ # persists_files_by_content? #
46
+ ################################
47
+
48
+ def persists_files_by_content?
49
+
50
+ persists_files_by_content = nil
51
+
52
+ persists_files_by_content = super
53
+
54
+ if persists_files_by_content.nil?
55
+ persists_files_by_content = self.class.persists_files_by_content?
56
+ end
57
+
58
+ return persists_files_by_content
59
+
60
+ end
61
+
62
+ #############################
63
+ # persists_files_by_path? #
64
+ #############################
65
+
66
+ def persists_files_by_path?
67
+
68
+ persists_files_by_path = nil
69
+
70
+ persists_files_by_path = super
71
+
72
+ if persists_files_by_path.nil?
73
+ persists_files_by_path = self.class.persists_files_by_path?
74
+ end
75
+
76
+ return persists_files_by_path
77
+
78
+ end
79
+
80
+ #####################################
81
+ # persists_file_paths_as_objects? #
82
+ #####################################
83
+
84
+ def persists_file_paths_as_objects?
85
+
86
+ persists_file_paths_as_objects = nil
87
+
88
+ persists_file_paths_as_objects = super
89
+
90
+ if persists_file_paths_as_objects.nil?
91
+ persists_file_paths_as_objects = self.class.persists_file_paths_as_objects?
92
+ end
93
+
94
+ return persists_file_paths_as_objects
95
+
96
+ end
97
+
98
+ #####################################
99
+ # persists_file_paths_as_strings? #
100
+ #####################################
101
+
102
+ def persists_file_paths_as_strings?
103
+
104
+ persists_file_paths_as_strings = nil
105
+
106
+ persists_file_paths_as_strings = super
107
+
108
+ if persists_file_paths_as_strings.nil?
109
+ persists_file_paths_as_strings = self.class.persists_file_paths_as_strings?
110
+ end
111
+
112
+ return persists_file_paths_as_strings
113
+
114
+ end
115
+
116
+ end