mongomodel 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 (108) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +34 -0
  3. data/Rakefile +47 -0
  4. data/bin/console +45 -0
  5. data/lib/mongomodel.rb +92 -0
  6. data/lib/mongomodel/attributes/mongo.rb +40 -0
  7. data/lib/mongomodel/attributes/store.rb +30 -0
  8. data/lib/mongomodel/attributes/typecasting.rb +51 -0
  9. data/lib/mongomodel/concerns/abstract_class.rb +17 -0
  10. data/lib/mongomodel/concerns/activemodel.rb +11 -0
  11. data/lib/mongomodel/concerns/associations.rb +103 -0
  12. data/lib/mongomodel/concerns/associations/base/association.rb +33 -0
  13. data/lib/mongomodel/concerns/associations/base/definition.rb +56 -0
  14. data/lib/mongomodel/concerns/associations/base/proxy.rb +58 -0
  15. data/lib/mongomodel/concerns/associations/belongs_to.rb +68 -0
  16. data/lib/mongomodel/concerns/associations/has_many_by_foreign_key.rb +159 -0
  17. data/lib/mongomodel/concerns/associations/has_many_by_ids.rb +175 -0
  18. data/lib/mongomodel/concerns/attribute_methods.rb +55 -0
  19. data/lib/mongomodel/concerns/attribute_methods/before_type_cast.rb +29 -0
  20. data/lib/mongomodel/concerns/attribute_methods/dirty.rb +35 -0
  21. data/lib/mongomodel/concerns/attribute_methods/protected.rb +127 -0
  22. data/lib/mongomodel/concerns/attribute_methods/query.rb +22 -0
  23. data/lib/mongomodel/concerns/attribute_methods/read.rb +29 -0
  24. data/lib/mongomodel/concerns/attribute_methods/write.rb +29 -0
  25. data/lib/mongomodel/concerns/attributes.rb +85 -0
  26. data/lib/mongomodel/concerns/callbacks.rb +294 -0
  27. data/lib/mongomodel/concerns/logging.rb +15 -0
  28. data/lib/mongomodel/concerns/pretty_inspect.rb +29 -0
  29. data/lib/mongomodel/concerns/properties.rb +69 -0
  30. data/lib/mongomodel/concerns/record_status.rb +42 -0
  31. data/lib/mongomodel/concerns/timestamps.rb +32 -0
  32. data/lib/mongomodel/concerns/validations.rb +38 -0
  33. data/lib/mongomodel/concerns/validations/associated.rb +46 -0
  34. data/lib/mongomodel/document.rb +20 -0
  35. data/lib/mongomodel/document/callbacks.rb +46 -0
  36. data/lib/mongomodel/document/dynamic_finders.rb +88 -0
  37. data/lib/mongomodel/document/finders.rb +82 -0
  38. data/lib/mongomodel/document/indexes.rb +91 -0
  39. data/lib/mongomodel/document/optimistic_locking.rb +48 -0
  40. data/lib/mongomodel/document/persistence.rb +143 -0
  41. data/lib/mongomodel/document/scopes.rb +161 -0
  42. data/lib/mongomodel/document/validations.rb +68 -0
  43. data/lib/mongomodel/document/validations/uniqueness.rb +78 -0
  44. data/lib/mongomodel/embedded_document.rb +42 -0
  45. data/lib/mongomodel/locale/en.yml +55 -0
  46. data/lib/mongomodel/support/collection.rb +109 -0
  47. data/lib/mongomodel/support/configuration.rb +35 -0
  48. data/lib/mongomodel/support/core_extensions.rb +10 -0
  49. data/lib/mongomodel/support/exceptions.rb +25 -0
  50. data/lib/mongomodel/support/mongo_options.rb +177 -0
  51. data/lib/mongomodel/support/types.rb +35 -0
  52. data/lib/mongomodel/support/types/array.rb +11 -0
  53. data/lib/mongomodel/support/types/boolean.rb +25 -0
  54. data/lib/mongomodel/support/types/custom.rb +38 -0
  55. data/lib/mongomodel/support/types/date.rb +20 -0
  56. data/lib/mongomodel/support/types/float.rb +13 -0
  57. data/lib/mongomodel/support/types/hash.rb +18 -0
  58. data/lib/mongomodel/support/types/integer.rb +13 -0
  59. data/lib/mongomodel/support/types/object.rb +21 -0
  60. data/lib/mongomodel/support/types/string.rb +9 -0
  61. data/lib/mongomodel/support/types/symbol.rb +9 -0
  62. data/lib/mongomodel/support/types/time.rb +12 -0
  63. data/lib/mongomodel/version.rb +3 -0
  64. data/spec/mongomodel/attributes/store_spec.rb +273 -0
  65. data/spec/mongomodel/concerns/activemodel_spec.rb +61 -0
  66. data/spec/mongomodel/concerns/associations/belongs_to_spec.rb +153 -0
  67. data/spec/mongomodel/concerns/associations/has_many_by_foreign_key_spec.rb +165 -0
  68. data/spec/mongomodel/concerns/associations/has_many_by_ids_spec.rb +192 -0
  69. data/spec/mongomodel/concerns/attribute_methods/before_type_cast_spec.rb +46 -0
  70. data/spec/mongomodel/concerns/attribute_methods/dirty_spec.rb +131 -0
  71. data/spec/mongomodel/concerns/attribute_methods/protected_spec.rb +86 -0
  72. data/spec/mongomodel/concerns/attribute_methods/query_spec.rb +27 -0
  73. data/spec/mongomodel/concerns/attribute_methods/read_spec.rb +52 -0
  74. data/spec/mongomodel/concerns/attribute_methods/write_spec.rb +43 -0
  75. data/spec/mongomodel/concerns/attributes_spec.rb +152 -0
  76. data/spec/mongomodel/concerns/callbacks_spec.rb +90 -0
  77. data/spec/mongomodel/concerns/logging_spec.rb +20 -0
  78. data/spec/mongomodel/concerns/pretty_inspect_spec.rb +68 -0
  79. data/spec/mongomodel/concerns/properties_spec.rb +29 -0
  80. data/spec/mongomodel/concerns/timestamps_spec.rb +170 -0
  81. data/spec/mongomodel/concerns/validations_spec.rb +159 -0
  82. data/spec/mongomodel/document/callbacks_spec.rb +80 -0
  83. data/spec/mongomodel/document/dynamic_finders_spec.rb +183 -0
  84. data/spec/mongomodel/document/finders_spec.rb +231 -0
  85. data/spec/mongomodel/document/indexes_spec.rb +121 -0
  86. data/spec/mongomodel/document/optimistic_locking_spec.rb +57 -0
  87. data/spec/mongomodel/document/persistence_spec.rb +319 -0
  88. data/spec/mongomodel/document/scopes_spec.rb +204 -0
  89. data/spec/mongomodel/document/validations/uniqueness_spec.rb +217 -0
  90. data/spec/mongomodel/document/validations_spec.rb +132 -0
  91. data/spec/mongomodel/document_spec.rb +74 -0
  92. data/spec/mongomodel/embedded_document_spec.rb +66 -0
  93. data/spec/mongomodel/mongomodel_spec.rb +33 -0
  94. data/spec/mongomodel/support/collection_spec.rb +248 -0
  95. data/spec/mongomodel/support/mongo_options_spec.rb +295 -0
  96. data/spec/mongomodel/support/property_spec.rb +83 -0
  97. data/spec/spec.opts +6 -0
  98. data/spec/spec_helper.rb +21 -0
  99. data/spec/specdoc.opts +6 -0
  100. data/spec/support/callbacks.rb +44 -0
  101. data/spec/support/helpers/define_class.rb +24 -0
  102. data/spec/support/helpers/specs_for.rb +11 -0
  103. data/spec/support/matchers/be_a_subclass_of.rb +5 -0
  104. data/spec/support/matchers/respond_to_boolean.rb +17 -0
  105. data/spec/support/matchers/run_callbacks.rb +20 -0
  106. data/spec/support/models.rb +23 -0
  107. data/spec/support/time.rb +6 -0
  108. metadata +232 -0
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ module MongoModel
4
+ specs_for(EmbeddedDocument) do
5
+ describe "callbacks" do
6
+ define_class(:ChildDocument, EmbeddedDocument) do
7
+ include MongoModel::CallbackHelpers
8
+ end
9
+
10
+ define_class(:ParentDocument, Document) do
11
+ property :child, ChildDocument
12
+ end
13
+
14
+ let(:child) { ChildDocument.new }
15
+ let(:parent) { ParentDocument.create!(:child => child) }
16
+
17
+ it "should run each type of callback when initializing" do
18
+ instance = ChildDocument.new
19
+ instance.should run_callbacks(:after_initialize)
20
+ end
21
+
22
+ it "should run each type of callback on find" do
23
+ instance = ParentDocument.find(parent.id).child
24
+ instance.should run_callbacks(:after_initialize, :after_find)
25
+ end
26
+
27
+ it "should run each type of callback when validating a new document" do
28
+ instance = ChildDocument.new
29
+ instance.valid?
30
+ instance.should run_callbacks(:after_initialize, :before_validation, :after_validation)
31
+ end
32
+
33
+ it "should run each type of callback when validating an existing document" do
34
+ instance = ParentDocument.find(parent.id).child
35
+ instance.valid?
36
+ instance.should run_callbacks(:after_initialize, :after_find, :before_validation, :after_validation)
37
+ end
38
+
39
+ it "should run each type of callback when creating a document" do
40
+ instance = ParentDocument.create!(:child => ChildDocument.new)
41
+ instance.child.should run_callbacks(:after_initialize, :before_validation, :after_validation, :before_save, :before_create, :after_create, :after_save)
42
+ end
43
+
44
+ it "should run each type of callback when saving an existing document" do
45
+ instance = ParentDocument.find(parent.id)
46
+ instance.save
47
+ instance.child.should run_callbacks(:after_initialize, :after_find, :before_validation, :after_validation, :before_save, :before_update, :after_update, :after_save)
48
+ end
49
+
50
+ it "should run each type of callback when destroying a document" do
51
+ instance = ParentDocument.find(parent.id)
52
+ instance.destroy
53
+ instance.child.should run_callbacks(:after_initialize, :after_find, :before_destroy, :after_destroy)
54
+ end
55
+
56
+ it "should not run destroy callbacks when deleting a document" do
57
+ instance = ParentDocument.find(parent.id)
58
+ instance.delete
59
+ instance.child.should run_callbacks(:after_initialize, :after_find)
60
+ end
61
+ end
62
+
63
+ [ :before_save, :before_create ].each do |callback|
64
+ context "#{callback} callback return false" do
65
+ define_class(:ChildDocument, EmbeddedDocument) do
66
+ send(callback) { false }
67
+ end
68
+
69
+ define_class(:ParentDocument, Document) do
70
+ property :child, ChildDocument
71
+ end
72
+
73
+ let(:child) { ChildDocument.new }
74
+ let(:parent) { ParentDocument.new(:child => child) }
75
+
76
+ describe "#save" do
77
+ it "should return false" do
78
+ parent.save.should be_false
79
+ end
80
+ end
81
+
82
+ describe "#save!" do
83
+ it "should raise a MongoModel::DocumentNotSaved exception" do
84
+ lambda { parent.save! }.should raise_error(MongoModel::DocumentNotSaved)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ module MongoModel
4
+ specs_for(Document, EmbeddedDocument) do
5
+ describe "logging" do
6
+ define_class(:TestDocument, described_class)
7
+
8
+ let(:logger) { mock('logger') }
9
+ before(:all) { MongoModel.logger = logger }
10
+
11
+ it "should have a logger reader on the class" do
12
+ TestDocument.logger.should == logger
13
+ end
14
+
15
+ it "should have a logger reader on the instance" do
16
+ TestDocument.new.logger.should == logger
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ module MongoModel
4
+ specs_for(Document, EmbeddedDocument) do
5
+ describe "#inspect" do
6
+ context "on base class" do
7
+ it "should return class name" do
8
+ described_class.inspect.should == described_class.name
9
+ end
10
+ end
11
+
12
+ context "on subclasses" do
13
+ context "without properties" do
14
+ define_class(:TestDocument, described_class)
15
+
16
+ it "should return class name" do
17
+ TestDocument.inspect.should == 'TestDocument()'
18
+ end
19
+ end
20
+
21
+ context "with properties" do
22
+ define_class(:TestDocument, described_class) do
23
+ property :name, String
24
+ property :age, Integer
25
+ end
26
+
27
+ it "should return class name and property definitions" do
28
+ TestDocument.inspect.should == 'TestDocument(name: String, age: Integer)'
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ specs_for(Document) do
36
+ describe "#inspect" do
37
+ context "on subclass instances" do
38
+ define_class(:TestDocument, Document) do
39
+ property :name, String
40
+ property :age, Integer
41
+ end
42
+
43
+ subject { TestDocument.new(:id => 'abc-123', :name => 'Doc name', :age => 54) }
44
+
45
+ it "should return class name and property values" do
46
+ subject.inspect.should == '#<TestDocument id: abc-123, name: "Doc name", age: 54>'
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ specs_for(EmbeddedDocument) do
53
+ describe "#inspect" do
54
+ context "on subclass instances" do
55
+ define_class(:TestDocument, EmbeddedDocument) do
56
+ property :name, String
57
+ property :age, Integer
58
+ end
59
+
60
+ subject { TestDocument.new(:name => 'Doc name', :age => 54) }
61
+
62
+ it "should return class name and property values" do
63
+ subject.inspect.should == '#<TestDocument name: "Doc name", age: 54>'
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ module MongoModel
4
+ specs_for(Document, EmbeddedDocument) do
5
+ define_class(:Person, described_class) do
6
+ property :name, String
7
+ property :age, Integer, :default => 21
8
+ end
9
+
10
+ define_class(:SkilledPerson, :Person) do
11
+ property :skill, String
12
+ end
13
+
14
+ it "should have a populated hash of properties" do
15
+ Person.properties.should include({
16
+ :name => Properties::Property.new(:name, String),
17
+ :age => Properties::Property.new(:age, Integer, :default => 21)
18
+ })
19
+ end
20
+
21
+ it "should extend properties in subclasses" do
22
+ SkilledPerson.properties.should include({
23
+ :name => Properties::Property.new(:name, String),
24
+ :age => Properties::Property.new(:age, Integer, :default => 21),
25
+ :skill => Properties::Property.new(:skill, String)
26
+ })
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,170 @@
1
+ require 'spec_helper'
2
+
3
+ module MongoModel
4
+ specs_for(Document, EmbeddedDocument) do
5
+ describe "#timestamps!" do
6
+ define_class(:TestDocument, described_class) do
7
+ timestamps!
8
+ end
9
+
10
+ it "should define a Time property updated_at" do
11
+ TestDocument.properties.should include(:updated_at)
12
+ TestDocument.properties[:updated_at].type.should == Time
13
+ end
14
+
15
+ it "should define a Time property created_at" do
16
+ TestDocument.properties.should include(:created_at)
17
+ TestDocument.properties[:created_at].type.should == Time
18
+ end
19
+ end
20
+
21
+ context "with updated_at property" do
22
+ define_class(:TestDocument, described_class) do
23
+ property :updated_at, Time
24
+ end
25
+
26
+ if specing?(EmbeddedDocument)
27
+ define_class(:ParentDocument, Document) do
28
+ property :child, TestDocument
29
+ end
30
+
31
+ let(:parent) { ParentDocument.new(:child => subject) }
32
+ let(:doc) { parent }
33
+ else
34
+ let(:doc) { subject }
35
+ end
36
+
37
+ subject { TestDocument.new }
38
+
39
+ before(:each) do
40
+ @now = Time.now.utc
41
+ Time.stub!(:now).and_return(@now)
42
+ end
43
+
44
+ it "should set the updated_at property to the current time when saved" do
45
+ doc.save
46
+ subject.updated_at.should == @now
47
+ end
48
+ end
49
+
50
+ context "with updated_on property" do
51
+ define_class(:TestDocument, described_class) do
52
+ property :updated_on, Date
53
+ end
54
+
55
+ if specing?(EmbeddedDocument)
56
+ define_class(:ParentDocument, Document) do
57
+ property :child, TestDocument
58
+ end
59
+
60
+ let(:parent) { ParentDocument.new(:child => subject) }
61
+ let(:doc) { parent }
62
+ else
63
+ let(:doc) { subject }
64
+ end
65
+
66
+ subject { TestDocument.new }
67
+
68
+ it "should set the updated_on property to the current date when saved" do
69
+ doc.save
70
+ subject.updated_on.should == Date.today
71
+ end
72
+ end
73
+
74
+ context "with created_at property" do
75
+ define_class(:TestDocument, described_class) do
76
+ property :created_at, Time
77
+ end
78
+
79
+ if specing?(EmbeddedDocument)
80
+ define_class(:ParentDocument, Document) do
81
+ property :child, TestDocument
82
+ end
83
+
84
+ let(:parent) { ParentDocument.new(:child => subject) }
85
+ let(:doc) { parent }
86
+ else
87
+ let(:doc) { subject }
88
+ end
89
+
90
+ subject { TestDocument.new }
91
+
92
+ before(:each) do
93
+ @now = Time.now.utc
94
+ Time.stub!(:now).and_return(@now)
95
+ end
96
+
97
+ it "should set the created_at property to the current time when created" do
98
+ doc.save
99
+ subject.created_at.should == @now
100
+ end
101
+
102
+ it "should not change the created_at property when updated" do
103
+ @next = 1.day.from_now
104
+
105
+ Time.stub!(:now).and_return(@now)
106
+
107
+ doc.save
108
+
109
+ Time.stub!(:now).and_return(@next)
110
+
111
+ doc.save
112
+ subject.created_at.should == @now
113
+ end
114
+
115
+ it "should preserve created_at attribute when set explicitly" do
116
+ @a_year_ago = 1.year.ago
117
+
118
+ subject.created_at = @a_year_ago
119
+ doc.save
120
+
121
+ subject.created_at.should == @a_year_ago
122
+ end
123
+ end
124
+
125
+ context "with created_on property" do
126
+ define_class(:TestDocument, described_class) do
127
+ property :created_on, Date
128
+ end
129
+
130
+ if specing?(EmbeddedDocument)
131
+ define_class(:ParentDocument, Document) do
132
+ property :child, TestDocument
133
+ end
134
+
135
+ let(:parent) { ParentDocument.new(:child => subject) }
136
+ let(:doc) { parent }
137
+ else
138
+ let(:doc) { subject }
139
+ end
140
+
141
+ subject { TestDocument.new }
142
+
143
+ it "should set the created_on property to the current date when created" do
144
+ doc.save
145
+ subject.created_on.should == Date.today
146
+ end
147
+
148
+ it "should not change the created_on property when updated" do
149
+ doc.save
150
+
151
+ @today = Date.today
152
+ @tomorrow = 1.day.from_now
153
+
154
+ Time.stub!(:now).and_return(@tomorrow)
155
+
156
+ doc.save
157
+ subject.created_on.should == @today
158
+ end
159
+
160
+ it "should preserve created_on attribute when set explicitly" do
161
+ @a_year_ago = 1.year.ago.to_date
162
+
163
+ subject.created_on = @a_year_ago
164
+ doc.save
165
+
166
+ subject.created_on.should == @a_year_ago
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,159 @@
1
+ require 'spec_helper'
2
+
3
+ module MongoModel
4
+ module ValidationHelpers
5
+ def clear_validations!
6
+ reset_callbacks(:validate)
7
+ end
8
+ end
9
+
10
+ specs_for(Document, EmbeddedDocument) do
11
+ describe "validations" do
12
+ define_class(:TestDocument, described_class) do
13
+ property :title, String
14
+ validates_presence_of :title
15
+
16
+ extend MongoModel::ValidationHelpers
17
+ end
18
+
19
+ if specing?(EmbeddedDocument)
20
+ define_class(:ParentDocument, Document) do
21
+ property :child, TestDocument
22
+ end
23
+
24
+ let(:child) { TestDocument.new }
25
+ let(:parent) { ParentDocument.new(:child => child) }
26
+ let(:doc) { parent }
27
+
28
+ subject { parent.child }
29
+ else
30
+ let(:doc) { TestDocument.new }
31
+
32
+ subject { doc }
33
+ end
34
+
35
+ it "should have an errors collection" do
36
+ subject.errors.should be_an_instance_of(ActiveModel::Errors)
37
+ end
38
+
39
+ context "when validations are not met" do
40
+ before(:each) { subject.valid? }
41
+
42
+ it { should_not be_valid }
43
+ it { should have(1).error }
44
+ end
45
+
46
+ describe "validation on create" do
47
+ before(:each) do
48
+ TestDocument.clear_validations!
49
+ TestDocument.validates_presence_of :title, :on => :create
50
+ end
51
+
52
+ context "new document" do
53
+ it { should_not be_valid }
54
+ end
55
+
56
+ context "existing document" do
57
+ before(:each) do
58
+ subject.title = 'Valid title'
59
+ doc.save!
60
+ subject.title = nil
61
+ end
62
+
63
+ it { should be_valid }
64
+ end
65
+ end
66
+
67
+ describe "validation on update" do
68
+ before(:each) do
69
+ TestDocument.clear_validations!
70
+ TestDocument.validates_presence_of :title, :on => :update
71
+ end
72
+
73
+ context "new document" do
74
+ it { should be_valid }
75
+ end
76
+
77
+ context "existing document" do
78
+ before(:each) do
79
+ subject.title = 'Valid title'
80
+ doc.save!
81
+ subject.title = nil
82
+ end
83
+
84
+ it { should_not be_valid }
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "validation shortcuts" do
90
+ define_class(:TestDocument, described_class)
91
+
92
+ describe ":required => true" do
93
+ it "should add a validates_presence_of validation" do
94
+ TestDocument.should_receive(:validates_presence_of).with(:title)
95
+ TestDocument.property :title, String, :required => true
96
+ end
97
+ end
98
+
99
+ describe ":format => /regex/" do
100
+ it "should add a validates_format_of validation" do
101
+ TestDocument.should_receive(:validates_format_of).with(:email, /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i)
102
+ TestDocument.property :email, String, :format => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ specs_for(EmbeddedDocument) do
109
+ describe "validations" do
110
+ define_class(:ChildDocument, EmbeddedDocument) do
111
+ property :title, String
112
+ validates_presence_of :title
113
+
114
+ extend MongoModel::ValidationHelpers
115
+ end
116
+
117
+ define_class(:ParentDocument, Document) do
118
+ property :child, ChildDocument
119
+ end
120
+
121
+ let(:child) { ChildDocument.new }
122
+ let(:parent) { ParentDocument.new(:child => child) }
123
+
124
+ subject { parent.child }
125
+
126
+ context "child document is invalid" do
127
+ before(:each) { parent.valid? }
128
+
129
+ specify "parent should be invalid" do
130
+ parent.should_not be_valid
131
+ end
132
+
133
+ specify "parent should have error on child" do
134
+ parent.should have(1).error
135
+ parent.errors[:child].should_not be_nil
136
+ end
137
+
138
+ specify "child should be invalid" do
139
+ child.should_not be_valid
140
+ end
141
+
142
+ specify "child should have error" do
143
+ child.errors[:title].should_not be_nil
144
+ end
145
+ end
146
+
147
+ context "parent has no child" do
148
+ before(:each) do
149
+ parent.child = nil
150
+ parent.valid?
151
+ end
152
+
153
+ specify "parent should be valid" do
154
+ parent.should be_valid
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end