office_clerk 0.8 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -2
  3. data/Gemfile.lock +159 -100
  4. data/Guardfile +3 -0
  5. data/README.md +7 -2
  6. data/Rakefile +9 -0
  7. data/app/assets/images/missing_list.png +0 -0
  8. data/app/assets/images/missing_product.png +0 -0
  9. data/app/assets/images/missing_thumb.png +0 -0
  10. data/app/assets/javascripts/office_clerk.js +7 -5
  11. data/app/assets/stylesheets/office_clerk.css.scss +89 -14
  12. data/app/controllers/baskets_controller.rb +37 -26
  13. data/app/controllers/orders_controller.rb +44 -29
  14. data/app/controllers/products_controller.rb +6 -6
  15. data/app/controllers/purchases_controller.rb +18 -8
  16. data/app/helpers/admin_helper.rb +16 -21
  17. data/app/helpers/baskets_helper.rb +31 -0
  18. data/app/helpers/categories_helper.rb +3 -0
  19. data/app/helpers/office_helper.rb +34 -7
  20. data/app/helpers/orders_helper.rb +2 -3
  21. data/app/models/basket.rb +46 -20
  22. data/app/models/clerk.rb +6 -1
  23. data/app/models/item.rb +7 -0
  24. data/app/models/order.rb +18 -6
  25. data/app/models/product.rb +23 -8
  26. data/app/views/addresses/show.html.haml +2 -2
  27. data/app/views/baskets/_small.html.haml +2 -2
  28. data/app/views/baskets/edit.html.haml +19 -16
  29. data/app/views/baskets/index.html.haml +13 -5
  30. data/app/views/baskets/show.html.haml +3 -3
  31. data/app/views/categories/_treeview.html.haml +1 -2
  32. data/app/views/categories/edit.html.haml +4 -5
  33. data/app/views/categories/index.html.haml +7 -7
  34. data/app/views/categories/show.html.haml +3 -4
  35. data/app/views/clerks/edit.html.haml +4 -3
  36. data/app/views/clerks/index.html.haml +6 -6
  37. data/app/views/clerks/show.html.haml +3 -3
  38. data/app/views/layouts/_admin_menu.html.haml +8 -8
  39. data/app/views/layouts/_messages.html.haml +2 -2
  40. data/app/views/layouts/office_clerk.haml +10 -27
  41. data/app/views/order_mailer/confirm.text.erb +1 -2
  42. data/app/views/order_mailer/shipped.text.erb +2 -1
  43. data/app/views/orders/index.csv.erb +6 -6
  44. data/app/views/orders/index.html.haml +25 -20
  45. data/app/views/orders/shipment.haml +57 -0
  46. data/app/views/orders/show.html.haml +106 -39
  47. data/app/views/products/_head.haml +1 -2
  48. data/app/views/products/_line.html.haml +1 -1
  49. data/app/views/products/_preview_box.haml +14 -10
  50. data/app/views/products/_triple.html.haml +1 -1
  51. data/app/views/products/edit.html.haml +4 -4
  52. data/app/views/products/index.html.haml +24 -13
  53. data/app/views/products/show.html.haml +15 -12
  54. data/app/views/purchases/index.html.haml +11 -7
  55. data/app/views/purchases/show.html.haml +42 -25
  56. data/app/views/sessions/sign_up.haml +10 -10
  57. data/app/views/suppliers/edit.html.haml +2 -2
  58. data/app/views/suppliers/index.html.haml +6 -7
  59. data/app/views/suppliers/show.html.haml +2 -3
  60. data/config/i18n-tasks.yml +97 -0
  61. data/config/initializers/migrate.rb +11 -1
  62. data/config/initializers/paperclip.rb +17 -0
  63. data/config/locales/config.yml +0 -1
  64. data/config/locales/en.yml +99 -116
  65. data/config/locales/fi.yml +101 -117
  66. data/config/routes.rb +18 -15
  67. data/db/migrate/20131226143612_categories.rb +2 -2
  68. data/db/migrate/20131226151332_products.rb +3 -3
  69. data/lib/office_clerk/engine.rb +5 -8
  70. data/lib/office_clerk/shipping_method.rb +12 -1
  71. data/lib/office_clerk/version.rb +1 -1
  72. data/office_clerk.gemspec +5 -5
  73. data/spec/controllers/baskets_controller_spec.rb +2 -2
  74. data/spec/controllers/orders_controller_spec.rb +0 -29
  75. data/spec/controllers/products_controller_spec.rb +4 -3
  76. data/spec/controllers/purchases_controller_spec.rb +3 -4
  77. data/spec/factories/orders.rb +6 -1
  78. data/spec/factories/products.rb +3 -0
  79. data/spec/features/baskets/edit_spec.rb +6 -1
  80. data/spec/features/baskets/index_spec.rb +1 -0
  81. data/spec/features/baskets/search_spec.rb +1 -1
  82. data/spec/features/orders/filter_spec.rb +19 -0
  83. data/spec/features/orders/inventory_spec.rb +19 -0
  84. data/spec/features/orders/ordering_spec.rb +84 -0
  85. data/spec/features/paginate_spec.rb +11 -0
  86. data/spec/features/sessions_spec.rb +2 -1
  87. data/spec/{models/locale_spec.rb → i18n_spec.rb} +19 -1
  88. data/spec/mailers/order_mailer_spec.rb +2 -1
  89. data/spec/models/baskets/inventory_spec.rb +9 -2
  90. data/spec/models/baskets/items_spec.rb +6 -1
  91. data/spec/models/order_spec.rb +20 -1
  92. data/spec/routing/baskets_routing_spec.rb +5 -1
  93. data/spec/routing/orders_routing_spec.rb +0 -12
  94. data/spec/spec_helper.rb +16 -3
  95. data/test_app/bin/setup +29 -0
  96. data/test_app/config/application.rb +3 -0
  97. data/test_app/config/boot.rb +1 -2
  98. data/test_app/config/environment.rb +1 -1
  99. data/test_app/config/environments/development.rb +1 -1
  100. data/test_app/config/environments/production.rb +20 -21
  101. data/test_app/config/environments/test.rb +7 -4
  102. data/test_app/config/initializers/assets.rb +11 -0
  103. data/test_app/config/initializers/cookies_serializer.rb +3 -0
  104. data/test_app/config/initializers/mime_types.rb +0 -1
  105. data/test_app/config/initializers/session_store.rb +1 -1
  106. data/test_app/config/routes.rb +1 -2
  107. data/test_app/config/secrets.yml +22 -0
  108. data/test_app/db/migrate/20141114205526_suppliers.office.rb +1 -1
  109. data/test_app/db/migrate/20141114205527_categories.office.rb +3 -3
  110. data/test_app/db/migrate/20141114205528_items.office.rb +1 -1
  111. data/test_app/db/migrate/20141114205529_orders.office.rb +1 -1
  112. data/test_app/db/migrate/20141114205530_baskets.office.rb +1 -1
  113. data/test_app/db/migrate/20141114205531_purchases.office.rb +1 -1
  114. data/test_app/db/migrate/20141114205532_products.office.rb +4 -4
  115. data/test_app/db/schema.rb +26 -26
  116. metadata +35 -28
  117. data/app/views/orders/edit.html.haml +0 -25
  118. data/app/views/orders/ship.haml +0 -91
  119. data/app/views/purchases/edit.html.haml +0 -9
  120. data/spec/features/orders_spec.rb +0 -37
data/config/routes.rb CHANGED
@@ -2,12 +2,12 @@ OfficeClerk::Engine.routes.draw do
2
2
 
3
3
  get "sign_out" => "sessions#sign_out"
4
4
  get "sign_in" => "sessions#sign_in"
5
- post "create_session" => "sessions#create"
5
+ post "create_session" => "sessions#create"
6
6
  match "sign_up" => "sessions#sign_up" , :via => [:get ,:post]
7
7
 
8
8
  resources :purchases do
9
9
  collection do
10
- match "search" => "purchases#index", :via => [:get, :post]
10
+ get "search" => "purchases#index"
11
11
  end
12
12
  member do
13
13
  get :order
@@ -18,7 +18,7 @@ OfficeClerk::Engine.routes.draw do
18
18
 
19
19
  resources :baskets do
20
20
  collection do
21
- match "search" => "baskets#index", :via => [:get, :post]
21
+ get "search" => "baskets#index"
22
22
  end
23
23
  member do
24
24
  get :discount
@@ -26,24 +26,27 @@ OfficeClerk::Engine.routes.draw do
26
26
  get :order
27
27
  get :purchase
28
28
  get :checkout
29
+ get :zero
29
30
  end
30
31
  end
31
32
 
32
- resources :orders do
33
+ resources :orders , :except => [:edit] do
33
34
  collection do
34
- match "search" => "orders#index", :via => [:get, :post]
35
+ get "search" => "orders#index"
35
36
  end
36
37
  member do
37
38
  get "mail/:act" , :action => :mail , :as => :mail
38
39
  get :pay
40
+ get :shipment
39
41
  get :ship
40
- patch :ship
42
+ get :cancel
43
+ patch :shipment
41
44
  end
42
45
  end
43
46
 
44
47
  resources :items do
45
48
  collection do
46
- match "search" => "items#index", :via => [:get, :post]
49
+ get "search" => "items#index"
47
50
  end
48
51
  member do
49
52
  end
@@ -51,7 +54,7 @@ OfficeClerk::Engine.routes.draw do
51
54
 
52
55
  resources :categories do
53
56
  collection do
54
- match "search" => "categories#index", :via => [:get, :post]
57
+ get "search" => "categories#index"
55
58
  end
56
59
  member do
57
60
  end
@@ -59,7 +62,7 @@ OfficeClerk::Engine.routes.draw do
59
62
 
60
63
  resources :products do
61
64
  collection do
62
- match "search" => "products#index", :via => [:get, :post]
65
+ get "search" => "products#index"
63
66
  end
64
67
  member do
65
68
  get :delete
@@ -68,7 +71,7 @@ OfficeClerk::Engine.routes.draw do
68
71
 
69
72
  resources :clerks do
70
73
  collection do
71
- match "search" => "clerks#index", :via => [:get, :post]
74
+ get "search" => "clerks#index"
72
75
  end
73
76
  member do
74
77
  end
@@ -76,18 +79,18 @@ OfficeClerk::Engine.routes.draw do
76
79
 
77
80
  resources :addresses do
78
81
  collection do
79
- match "search" => "addresses#index", :via => [:get, :post]
82
+ get "search" => "addresses#index"
80
83
  end
81
84
  end
82
85
 
83
86
  resources :suppliers do
84
87
  collection do
85
- match "search" => "suppliers#index", :via => [:get, :post]
88
+ get "search" => "suppliers#index"
86
89
  end
87
90
  member do
88
91
  end
89
92
  end
90
93
 
91
- match "manage/all" => "manage#all", :via => [:get, :post]
92
-
93
- end
94
+ get "manage/all" => "manage#all"
95
+
96
+ end
@@ -4,8 +4,8 @@ class Categories < ActiveRecord::Migration
4
4
  t.references :category
5
5
  t.boolean :online, :default => false
6
6
  t.string :name
7
- t.text :description , :default => ""
8
- t.text :summary , :default => ""
7
+ t.text :description
8
+ t.text :summary
9
9
  t.integer :position , :default => 1
10
10
  t.string :link
11
11
  t.attachment :main_picture
@@ -3,9 +3,9 @@ class Products < ActiveRecord::Migration
3
3
  create_table :products do |t|
4
4
  t.float :price , :null => false
5
5
  t.string :name , :null => false
6
- t.string :link
7
- t.text :description , :default => ""
8
- t.text :summary , :default => ""
6
+ t.string :link
7
+ t.text :description
8
+ t.text :summary
9
9
  t.attachment :main_picture
10
10
  t.attachment :extra_picture
11
11
  t.boolean :online , :default => false
@@ -5,28 +5,25 @@ module OfficeClerk
5
5
  engine_name "office"
6
6
 
7
7
  config.autoload_paths += %W(#{config.root}/lib)
8
- # config.assets.paths +=
8
+ # config.assets.paths +=
9
9
  config.exceptions_app = self.routes
10
10
  initializer "office_clerk.assets.precompile" do |app|
11
11
  app.config.assets.precompile += %w(office_clerk.css office_clerk.js)
12
12
  end
13
13
 
14
- I18n.enforce_available_locales = false
15
- I18n.default_locale = :fi
14
+ config.i18n.enforce_available_locales = false
15
+ config.i18n.available_locales = [:fi , :en , :config]
16
+ config.i18n.default_locale = :fi
16
17
  # for testing this makes engine factories available
17
18
  initializer "model_core.factories", :after => "factory_girl.set_factory_paths" do
18
19
  FactoryGirl.definition_file_paths << File.expand_path('../../../spec/factories', __FILE__) if defined?(FactoryGirl)
19
20
  end
20
21
 
21
22
  # have to init the BestInPlace first to be able to include helpers
22
- config.railties_order = [BestInPlace::Railtie , OfficeClerk::Engine , :all ]
23
+ config.railties_order = [BestInPlace::Railtie , OfficeClerk::Engine , :all ]
23
24
  config.after_initialize do
24
25
  BestInPlace::ViewHelpers.extend OfficeHelper
25
26
  end
26
27
 
27
- config.paperclip_defaults = { :styles => {:thumb => '48x48>', :list => '150x150>', :product => '600x600>' },
28
- :default_style => :list,
29
- :url => "/images/:id/:style/:basename.:extension",
30
- :default_url => "/images/missing/:style.png" }
31
28
  end
32
29
  end
@@ -8,9 +8,17 @@ module OfficeClerk
8
8
  end
9
9
  attr_reader :data , :name , :type , :description
10
10
 
11
+ # the relevant interface for a shipping method is
12
+ # a) whether it applies to the order (basket) : available?
13
+ # b) how much sendin costs, price_for(basket)
11
14
  def price_for(basket)
12
15
  raise "Not implemented in #{self}"
13
16
  end
17
+ def available?(basket)
18
+ true
19
+ end
20
+
21
+ ## class stuff after here, global list etc
14
22
  @@methods = nil
15
23
  def self.all
16
24
  return @@methods if @@methods
@@ -20,10 +28,10 @@ module OfficeClerk
20
28
  begin
21
29
  clas_name = method[:class]
22
30
  clas = clas_name.constantize
23
- @@methods[key] = clas.new( method.merge(:type => key) )
24
31
  rescue
25
32
  puts "No such Class #{method[:class]}, check config.yml"
26
33
  end
34
+ @@methods[key] = clas.new( method.merge(:type => key) )
27
35
  end
28
36
  @@methods
29
37
  end
@@ -40,5 +48,8 @@ module OfficeClerk
40
48
  def price_for(basket)
41
49
  0.0
42
50
  end
51
+ def available?(basket)
52
+ true
53
+ end
43
54
  end
44
55
  end
@@ -1,3 +1,3 @@
1
1
  module OfficeClerk
2
- VERSION = "0.8"
2
+ VERSION = "0.9"
3
3
  end
data/office_clerk.gemspec CHANGED
@@ -23,22 +23,22 @@ Gem::Specification.new do |s|
23
23
 
24
24
  s.has_rdoc = false
25
25
 
26
- s.add_runtime_dependency 'rails', '~> 4.1', '< 4.2'
27
- s.add_runtime_dependency 'ransack', '~> 1.5', '>= 1.5.1'
26
+ s.add_runtime_dependency 'rails', '~> 4.2', '< 5.0'
27
+ s.add_runtime_dependency 'ransack', '~> 1.7'
28
28
  s.add_runtime_dependency "valid_email" , '~> 0.0', '>= 0.0.10'
29
- s.add_runtime_dependency 'sass-rails' , '~> 4.0'
29
+ s.add_runtime_dependency 'sass-rails' , '~> 5.0'
30
30
  s.add_runtime_dependency "haml" , '~> 4.0'
31
31
  s.add_runtime_dependency "jquery-rails" , '~> 3.1'
32
32
  s.add_runtime_dependency 'coffee-rails' , '~> 4.0'
33
33
  s.add_runtime_dependency 'gon' , '~> 5.2'
34
34
  s.add_runtime_dependency 'jquery-ui-rails' , '~> 5.0'
35
35
  s.add_runtime_dependency "kramdown" , '~> 1.5'
36
- s.add_runtime_dependency "best_in_place" , '~> 0'
36
+ s.add_runtime_dependency "best_in_place" , '~> 3.0'
37
37
 
38
38
  s.add_runtime_dependency 'will_paginate-bootstrap' , '~> 1.0'
39
39
  s.add_runtime_dependency 'bootstrap-sass', '~> 3.1'
40
40
  s.add_runtime_dependency 'bootstrap_form' , '~> 2.2'
41
- s.add_runtime_dependency "paperclip" , '~> 4.1'
41
+ s.add_runtime_dependency "paperclip" , '~> 4.0'
42
42
  s.add_runtime_dependency 'rails-i18n' , '~> 4.0'
43
43
 
44
44
  s.add_runtime_dependency "bcrypt-ruby" , '~> 3.1'
@@ -12,7 +12,7 @@ describe BasketsController do
12
12
  # BasketsController. Be sure to keep this updated.
13
13
  before :all do
14
14
  create :admin unless Clerk.where(:admin => true).first
15
- end
15
+ end
16
16
  let(:valid_session) { { :clerk_email => Clerk.where(:admin => true).first.email } }
17
17
 
18
18
  describe "GET index" do
@@ -80,7 +80,7 @@ describe BasketsController do
80
80
  it "redirects to the basket" do
81
81
  basket = create :basket
82
82
  put :update, {:id => basket.to_param, :basket => attributes_for(:basket)}, valid_session
83
- expect(response).to redirect_to( :action => :edit , :id => basket.id)
83
+ expect(response).to redirect_to( edit_basket_path(basket))
84
84
  end
85
85
  end
86
86
 
@@ -34,35 +34,6 @@ describe OrdersController do
34
34
  it "assigns a new order as @order" do
35
35
  get :new, {}, valid_session
36
36
  expect(assigns(:order)).to be_kind_of(Order)
37
- expect(assigns(:order)).to be_new_record
38
- end
39
- end
40
-
41
- describe "GET edit" do
42
- it "assigns the requested order as @order" do
43
- order = create :order
44
- get :edit, {:id => order.to_param}, valid_session
45
- expect(assigns(:order)).to eq(order)
46
- end
47
- end
48
-
49
- describe "POST create" do
50
-
51
- describe "with invalid params" do
52
- it "assigns a newly created but unsaved order as @order" do
53
- # Trigger the behavior that occurs when invalid params are submitted
54
- allow_any_instance_of(Order).to receive(:save).and_return(false)
55
- post :create, {:order => { :paid_on => ""}}, valid_session
56
- expect(assigns(:order)).to be_kind_of(Order)
57
- expect(assigns(:order)).to be_new_record
58
- end
59
-
60
- it "re-renders the 'new' template" do
61
- # Trigger the behavior that occurs when invalid params are submitted
62
- allow_any_instance_of(Order).to receive(:save).and_return(false)
63
- post :create, {:order => { :paid_on => ""}}, valid_session
64
- expect(response).to render_template(:edit)
65
- end
66
37
  end
67
38
  end
68
39
 
@@ -10,7 +10,7 @@ describe ProductsController do
10
10
 
11
11
  before :all do
12
12
  create :admin unless Clerk.where(:admin => true).first
13
- end
13
+ end
14
14
 
15
15
  # This should return the minimal set of values that should be in the session
16
16
  # in order to pass any filters (e.g. authentication) defined in
@@ -122,16 +122,17 @@ describe ProductsController do
122
122
  end
123
123
 
124
124
  describe "DELETE destroy" do
125
+ routes { OfficeClerk::Engine.routes }
125
126
  it "deletes the requested product" do
126
127
  product = create :product_without_inventory
127
128
  before = Product.count
128
- delete :delete, {:id => product.to_param}, valid_session
129
+ delete :destroy, {:id => product.to_param}, valid_session
129
130
  expect(Product.count).to be before - 1
130
131
  end
131
132
 
132
133
  it "does not delete with inventory" do
133
134
  product = Product.create! valid_attributes
134
- delete :delete, {:id => product.to_param}, valid_session
135
+ delete :destroy, {:id => product.to_param}, valid_session
135
136
  expect(response).to redirect_to(product_path(product))
136
137
  end
137
138
  end
@@ -38,14 +38,13 @@ describe PurchasesController do
38
38
  it "assigns a new purchase as @purchase" do
39
39
  get :new, {}, valid_session
40
40
  expect(assigns(:purchase)).to be_kind_of(Purchase)
41
- expect(assigns(:purchase)).to be_new_record
42
41
  end
43
42
  end
44
43
 
45
44
  describe "GET edit" do
46
45
  it "assigns the requested purchase as @purchase" do
47
46
  purchase = Purchase.create! valid_attributes
48
- get :edit, {:id => purchase.to_param}, valid_session
47
+ get :show, {:id => purchase.to_param}, valid_session
49
48
  expect(assigns(:purchase)).to eq(purchase)
50
49
  end
51
50
  end
@@ -83,7 +82,7 @@ describe PurchasesController do
83
82
  # Trigger the behavior that occurs when invalid params are submitted
84
83
  allow_any_instance_of(Purchase).to receive(:save).and_return(false)
85
84
  post :create, {:purchase => { :supplier_id => nil }}, valid_session
86
- expect(response).to render_template(:edit)
85
+ expect(response).to render_template(:show)
87
86
  end
88
87
  end
89
88
  end
@@ -117,7 +116,7 @@ describe PurchasesController do
117
116
  # Trigger the behavior that occurs when invalid params are submitted
118
117
  allow_any_instance_of(Purchase).to receive(:save).and_return(false)
119
118
  put :update, {:id => purchase.to_param, :purchase => { :supplier_id => nil }}, valid_session
120
- expect(response).to render_template(:edit)
119
+ expect(response).to render_template(:show)
121
120
  end
122
121
  end
123
122
  end
@@ -3,6 +3,7 @@
3
3
  FactoryGirl.define do
4
4
  factory :order do
5
5
  sequence( :email) { |n| "test#{n}@test.com" }
6
+ ordered_on Date.today
6
7
  shipment_tax 0
7
8
  basket { create :basket_with_item }
8
9
  factory :order_ordered do
@@ -10,10 +11,14 @@ FactoryGirl.define do
10
11
  factory :order_paid do
11
12
  paid_on "2013-12-26"
12
13
  end
13
- factory :order_shippped do
14
+ factory :order_shipped do
14
15
  shipped_on "2013-12-26"
15
16
  shipment_price 10.0
16
17
  shipment_tax 20.0
18
+ after(:create) do |o|
19
+ o.basket.locked = Date.today
20
+ o.save
21
+ end
17
22
  end
18
23
  end
19
24
  end
@@ -14,6 +14,9 @@ FactoryGirl.define do
14
14
  prod.category = cat
15
15
  prod.save!
16
16
  end
17
+ factory :shop_product_without_inventory do
18
+ inventory 0
19
+ end
17
20
  end
18
21
  factory :product_line do
19
22
  after(:create) do |prod|
@@ -35,9 +35,14 @@ describe "edit baskets" do
35
35
  it "discounts basket" do
36
36
  basket = create :basket_with_item
37
37
  total = basket.total_price
38
- visit discount_basket_path(basket , :discount => "10")
38
+ visit office.discount_basket_path(basket , :discount => "10")
39
39
  expect_basket_total total * 0.9
40
40
  end
41
+ it "zeros basket" do
42
+ basket = create :basket_with_item
43
+ visit zero_basket_path(basket)
44
+ expect_basket_total 0.0
45
+ end
41
46
  it "discounts item" do
42
47
  basket = create :basket_2_items
43
48
  total = basket.total_price - basket.items.first.price*0.1
@@ -13,6 +13,7 @@ describe "Basket index/search page" do
13
13
  create :purchase_received
14
14
  create :order_ordered
15
15
  create :order_paid
16
+ create :order_shipped
16
17
  visit_path baskets_path
17
18
  end
18
19
  end
@@ -30,4 +30,4 @@ describe "Basket search functionality" do
30
30
  expect(td).to have_content(p.name) # added the product, yes!
31
31
  end
32
32
 
33
- end
33
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Order index" do
4
+ before(:each) do
5
+ sign_in
6
+ end
7
+ it "lists orders" do
8
+ visit_path orders_path
9
+ end
10
+ it "creates new order" do
11
+ visit_path orders_path
12
+ find(".new_order").click
13
+ end
14
+ it "filters by email" do
15
+ order_ab :email =>[ "torsten@villataika.fi", "raisa@villataika.fi"]
16
+ fill_in("q[email_cont]" , :with => "torsten")
17
+ expect(order_count).to eq 1
18
+ end
19
+ end