maestrano-connector-rails 0.2.16 → 0.2.17
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.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/Gemfile.lock +27 -0
- data/VERSION +1 -1
- data/app/helpers/maestrano/connector/rails/session_helper.rb +5 -1
- data/app/models/maestrano/connector/rails/complex_entity.rb +14 -33
- data/app/models/maestrano/connector/rails/concerns/entity.rb +161 -73
- data/app/models/maestrano/connector/rails/sub_entity_base.rb +44 -4
- data/lib/generators/connector/install_generator.rb +20 -24
- data/lib/generators/connector/templates/home_controller.rb +33 -10
- data/lib/generators/connector/templates/home_controller_spec.rb +141 -0
- data/lib/generators/connector/templates/home_index.haml +103 -0
- data/lib/generators/connector/templates/layouts.haml +45 -0
- data/lib/generators/connector/templates/oauth_controller.rb +3 -6
- data/lib/generators/connector/templates/shared_entities_controller.rb +7 -0
- data/lib/generators/connector/templates/shared_entities_controller_spec.rb +23 -0
- data/lib/generators/connector/templates/shared_entities_index.haml +41 -0
- data/lib/generators/connector/templates/stylesheets/application.sass +24 -0
- data/lib/generators/connector/templates/stylesheets/banners.sass +59 -0
- data/lib/generators/connector/templates/stylesheets/home.sass +25 -0
- data/lib/generators/connector/templates/stylesheets/layout.sass +125 -0
- data/lib/generators/connector/templates/stylesheets/spacers.sass +46 -0
- data/lib/generators/connector/templates/stylesheets/variables.sass +57 -0
- data/lib/generators/connector/templates/sychronizations_controller_spec.rb +22 -0
- data/lib/generators/connector/templates/synchronizations_controller.rb +7 -0
- data/lib/generators/connector/templates/synchronizations_index.haml +42 -0
- data/maestrano-connector-rails.gemspec +29 -6
- data/pkg/maestrano-connector-rails-0.2.16.gem +0 -0
- data/spec/models/complex_entity_spec.rb +46 -12
- data/spec/models/entity_spec.rb +212 -113
- data/spec/models/sub_entity_base_spec.rb +59 -0
- data/template/maestrano-connector-template.rb +4 -3
- data/template/routes.rb +14 -0
- metadata +61 -5
- data/lib/generators/connector/templates/admin_controller.rb +0 -58
- data/lib/generators/connector/templates/admin_index.html.erb +0 -55
- data/lib/generators/connector/templates/home_index.html.erb +0 -48
@@ -14,19 +14,59 @@ module Maestrano::Connector::Rails
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def external_entity_name
|
17
|
-
if
|
18
|
-
|
17
|
+
if external?
|
18
|
+
entity_name
|
19
19
|
else
|
20
20
|
raise "Forbidden call"
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
def connec_entity_name
|
25
|
-
if
|
25
|
+
if external?
|
26
26
|
raise "Forbidden call"
|
27
27
|
else
|
28
|
-
|
28
|
+
entity_name
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
def names_hash
|
33
|
+
if external?
|
34
|
+
{external_entity: entity_name.downcase}
|
35
|
+
else
|
36
|
+
{connec_entity: entity_name.downcase}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_idmap_from_external_entity(entity, connec_entity_name, organization)
|
41
|
+
if external?
|
42
|
+
h = names_hash.merge({
|
43
|
+
external_id: get_id_from_external_entity_hash(entity),
|
44
|
+
name: object_name_from_external_entity_hash(entity),
|
45
|
+
connec_entity: connec_entity_name.downcase,
|
46
|
+
organization_id: organization.id
|
47
|
+
})
|
48
|
+
Maestrano::Connector::Rails::IdMap.create(h)
|
49
|
+
else
|
50
|
+
raise 'Forbidden call'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_idmap_from_connec_entity(entity, external_entity_name, organization)
|
55
|
+
if external?
|
56
|
+
raise 'Forbidden call'
|
57
|
+
else
|
58
|
+
h = names_hash.merge({
|
59
|
+
connec_id: entity['id'],
|
60
|
+
name: object_name_from_connec_entity_hash(entity),
|
61
|
+
external_entity: external_entity_name.downcase,
|
62
|
+
organization_id: organization.id
|
63
|
+
})
|
64
|
+
Maestrano::Connector::Rails::IdMap.create(h)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def map_external_entity_with_idmap(external_entity, connec_entity_name, idmap, organization)
|
69
|
+
{entity: map_to(connec_entity_name, external_entity, organization), idmap: idmap}
|
70
|
+
end
|
31
71
|
end
|
32
72
|
end
|
@@ -21,25 +21,6 @@ module Connector
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def include_routes
|
25
|
-
sentinel = 'Rails.application.routes.draw do'
|
26
|
-
code_lines = [
|
27
|
-
"mount Maestrano::Connector::Rails::Engine, at: '/'\n",
|
28
|
-
"root 'home#index'",
|
29
|
-
"get 'home/index' => 'home#index'",
|
30
|
-
"get 'admin/index' => 'admin#index'",
|
31
|
-
"put 'admin/update' => 'admin#update'",
|
32
|
-
"post 'admin/synchronize' => 'admin#synchronize'",
|
33
|
-
"put 'admin/toggle_sync' => 'admin#toggle_sync'"
|
34
|
-
]
|
35
|
-
|
36
|
-
in_root do
|
37
|
-
gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
|
38
|
-
"#{match}\n #{code_lines.join("\n ")}\n"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
24
|
def copy_entity
|
44
25
|
copy_file 'entity.rb', 'app/models/maestrano/connector/rails/entity.rb'
|
45
26
|
end
|
@@ -52,14 +33,29 @@ module Connector
|
|
52
33
|
copy_file 'example_entity.rb', 'app/models/entities/example_entitiy.rb'
|
53
34
|
end
|
54
35
|
|
55
|
-
def
|
36
|
+
def copy_controllers_and_views
|
56
37
|
copy_file 'home_controller.rb', 'app/controllers/home_controller.rb'
|
57
|
-
copy_file '
|
38
|
+
copy_file 'home_controller_spec.rb', 'spec/controllers/home_controller_spec.rb'
|
39
|
+
copy_file 'home_index.haml', 'app/views/home/index.html.haml'
|
40
|
+
|
41
|
+
copy_file 'synchronizations_controller.rb', 'app/controllers/synchronizations_controller.rb'
|
42
|
+
copy_file 'synchronizations_controller_spec.rb', 'spec/controllers/synchronizations_controller_spec.rb'
|
43
|
+
copy_file 'synchronizations_index.haml', 'app/views/synchronizations/index.html.haml'
|
44
|
+
|
45
|
+
copy_file 'shared_entities_controller.rb', 'app/controllers/shared_entities_controller.rb'
|
46
|
+
copy_file 'shared_entities_controller_spec.rb', 'spec/controllers/shared_entities_controller_spec.rb'
|
47
|
+
copy_file 'shared_entities_index.haml', 'app/views/shared_entities/index.html.haml'
|
48
|
+
|
49
|
+
copy_file 'layouts.haml', 'app/views/layouts/application.html.haml'
|
58
50
|
end
|
59
51
|
|
60
|
-
def
|
61
|
-
copy_file '
|
62
|
-
copy_file '
|
52
|
+
def copy_stylesheets
|
53
|
+
copy_file 'stylesheets/application.sass', 'app/assets/stylesheets/application.sass'
|
54
|
+
copy_file 'stylesheets/banners.sass', 'app/assets/stylesheets/banners.sass'
|
55
|
+
copy_file 'stylesheets/home.sass', 'app/assets/stylesheets/home.sass'
|
56
|
+
copy_file 'stylesheets/layout.sass', 'app/assets/stylesheets/layout.sass'
|
57
|
+
copy_file 'stylesheets/spacers.sass', 'app/assets/stylesheets/spacers.sass'
|
58
|
+
copy_file 'stylesheets/variables.sass', 'app/assets/stylesheets/variables.sass'
|
63
59
|
end
|
64
60
|
|
65
61
|
def copy_oauth_controller
|
@@ -1,17 +1,40 @@
|
|
1
|
-
# TODO
|
2
|
-
# This controller is given as an example of a possible home
|
3
|
-
# Home funcitons :
|
4
|
-
# * Display generic information about the connector (mostly for not connected users)
|
5
|
-
# * Link the connector to a Maestrano organization
|
6
|
-
# * Acces the last synchronization and a synchronization history (with their status)
|
7
1
|
class HomeController < ApplicationController
|
8
2
|
def index
|
9
|
-
|
10
|
-
|
3
|
+
@organization = current_organization
|
4
|
+
end
|
5
|
+
|
6
|
+
def update
|
7
|
+
organization = Maestrano::Connector::Rails::Organization.find_by_id(params[:id])
|
11
8
|
|
12
|
-
|
13
|
-
|
9
|
+
if organization && is_admin?(current_user, organization)
|
10
|
+
organization.synchronized_entities.keys.each do |entity|
|
11
|
+
if !!params["#{entity}"]
|
12
|
+
organization.synchronized_entities[entity] = true
|
13
|
+
else
|
14
|
+
organization.synchronized_entities[entity] = false
|
15
|
+
end
|
14
16
|
end
|
17
|
+
organization.save
|
18
|
+
end
|
19
|
+
|
20
|
+
redirect_to(:back)
|
21
|
+
end
|
22
|
+
|
23
|
+
def synchronize
|
24
|
+
if is_admin
|
25
|
+
Maestrano::Connector::Rails::SynchronizationJob.perform_later(current_organization, params['opts'] || {})
|
26
|
+
flash[:info] = 'Synchronization requested'
|
15
27
|
end
|
28
|
+
|
29
|
+
redirect_to(:back)
|
30
|
+
end
|
31
|
+
|
32
|
+
def toggle_sync
|
33
|
+
if is_admin
|
34
|
+
current_organization.update(sync_enabled: !current_organization.sync_enabled)
|
35
|
+
flash[:info] = current_organization.sync_enabled ? 'Synchronization enabled' : 'Synchronization disabled'
|
36
|
+
end
|
37
|
+
|
38
|
+
redirect_to(:back)
|
16
39
|
end
|
17
40
|
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HomeController, :type => :controller do
|
4
|
+
let(:back_path) { home_index_path }
|
5
|
+
before(:each) do
|
6
|
+
request.env["HTTP_REFERER"] = back_path
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'index' do
|
10
|
+
subject { get :index }
|
11
|
+
|
12
|
+
it { expect(subject).to be_success }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'update' do
|
16
|
+
let(:organization) { create(:organization, synchronized_entities: {'people' => false, 'item' => true}) }
|
17
|
+
subject { put :update, id: organization.id, 'people' => true, 'item' => false, 'lala' => true }
|
18
|
+
|
19
|
+
|
20
|
+
context 'when user is not admin' do
|
21
|
+
before {
|
22
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:is_admin?).and_return(false)
|
23
|
+
}
|
24
|
+
|
25
|
+
it { expect(subject).to redirect_to back_path }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when user is admin' do
|
29
|
+
before {
|
30
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:is_admin?).and_return(true)
|
31
|
+
}
|
32
|
+
|
33
|
+
it { expect(subject).to redirect_to back_path }
|
34
|
+
|
35
|
+
it 'updates organization' do
|
36
|
+
subject
|
37
|
+
organization.reload
|
38
|
+
expect(organization.synchronized_entities).to eq({'people' => true, 'item' => false})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'synchronize' do
|
44
|
+
subject { post :synchronize }
|
45
|
+
let(:organization) { create(:organization) }
|
46
|
+
before {
|
47
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
|
48
|
+
}
|
49
|
+
|
50
|
+
context 'when user is not admin' do
|
51
|
+
before {
|
52
|
+
allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(false)
|
53
|
+
}
|
54
|
+
|
55
|
+
it { expect(subject).to redirect_to back_path }
|
56
|
+
|
57
|
+
it 'does nothing' do
|
58
|
+
expect(Maestrano::Connector::Rails::SynchronizationJob).to_not receive(:perform_later)
|
59
|
+
subject
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when user is admin' do
|
64
|
+
before {
|
65
|
+
allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(true)
|
66
|
+
}
|
67
|
+
|
68
|
+
it { expect(subject).to redirect_to back_path }
|
69
|
+
|
70
|
+
context 'with opts' do
|
71
|
+
subject { post :synchronize, opts: 'opts' }
|
72
|
+
|
73
|
+
it 'calls perform_later with opts' do
|
74
|
+
expect(Maestrano::Connector::Rails::SynchronizationJob).to receive(:perform_later).with(organization, 'opts')
|
75
|
+
subject
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'without opts' do
|
80
|
+
subject { post :synchronize}
|
81
|
+
|
82
|
+
it 'calls perform_later with empty opts hash' do
|
83
|
+
expect(Maestrano::Connector::Rails::SynchronizationJob).to receive(:perform_later).with(organization, {})
|
84
|
+
subject
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'toggle_sync' do
|
90
|
+
subject { put :toggle_sync }
|
91
|
+
let(:organization) { create(:organization, sync_enabled: true) }
|
92
|
+
before {
|
93
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
|
94
|
+
}
|
95
|
+
|
96
|
+
context 'when user is not an admin' do
|
97
|
+
before {
|
98
|
+
allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(false)
|
99
|
+
}
|
100
|
+
it { expect(subject).to redirect_to back_path }
|
101
|
+
|
102
|
+
it 'does nothing' do
|
103
|
+
expect{ subject }.to_not change{ organization.sync_enabled }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'when user is admin' do
|
108
|
+
before {
|
109
|
+
allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(true)
|
110
|
+
}
|
111
|
+
it { expect(subject).to redirect_to back_path }
|
112
|
+
|
113
|
+
it 'change sync_enabled from true to false' do
|
114
|
+
expect{ subject }.to change{ organization.sync_enabled }.from(true).to(false)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'change sync_enabled from false to true' do
|
118
|
+
organization.update(sync_enabled: false)
|
119
|
+
expect{ subject }.to change{ organization.sync_enabled }.from(false).to(true)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe 'redirect_to_external' do
|
126
|
+
subject { get :redirect_to_external }
|
127
|
+
|
128
|
+
context 'when organization has a redirect url' do
|
129
|
+
let(:organization) { create(:organization, instance_url: 'url') }
|
130
|
+
before {
|
131
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
|
132
|
+
}
|
133
|
+
|
134
|
+
it {expect(subject).to redirect_to('url')}
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'otherwise' do
|
138
|
+
it {expect(subject).to redirect_to('https://login.salesforce.com')}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
/ TODO
|
2
|
+
/ Replace ApplicationName
|
3
|
+
.home
|
4
|
+
.banners
|
5
|
+
.row
|
6
|
+
.col-md-10.col-md-offset-2
|
7
|
+
%h2 ApplicationName Connector
|
8
|
+
%p
|
9
|
+
Link your Maestrano account to ApplicationName to get your business in synch. Check the status of your connection on this screen.
|
10
|
+
|
11
|
+
.container
|
12
|
+
- if current_user
|
13
|
+
- unless is_admin
|
14
|
+
.row
|
15
|
+
.col-md-12.alert.alert-warning
|
16
|
+
Only administrators can modify the application settings
|
17
|
+
|
18
|
+
.row.link-step
|
19
|
+
.col-md-1.text-center.link-step-number
|
20
|
+
%span.badge.link-step-badge
|
21
|
+
1
|
22
|
+
.col-md-3.text-center
|
23
|
+
=image_tag "logos/default-white.png", class: 'logo'
|
24
|
+
.col-md-6.link-step-description
|
25
|
+
%h
|
26
|
+
Your Maestrano company <strong>#{@organization.name} (#{@organization.uid})</strong> is currently linked
|
27
|
+
.col-md-2.text-center.link-step-action
|
28
|
+
= link_to "Disconnect", Maestrano::Connector::Rails::Engine.routes.url_helpers.maestrano_signout_path, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
|
29
|
+
|
30
|
+
.spacer1
|
31
|
+
|
32
|
+
.row.link-step
|
33
|
+
.col-md-1.text-center.link-step-number
|
34
|
+
%span.badge.link-step-badge
|
35
|
+
2
|
36
|
+
.col-md-3.text-center
|
37
|
+
=image_tag "logos/external.png", class: 'logo'
|
38
|
+
.col-md-6.link-step-description
|
39
|
+
%h
|
40
|
+
- if @organization.oauth_uid
|
41
|
+
Your ApplicationName account <strong>#{@organization.oauth_name} (#{@organization.oauth_uid})</strong> is currently linked
|
42
|
+
- else
|
43
|
+
Your ApplicationName account is not linked
|
44
|
+
.col-md-2.text-center.link-step-action
|
45
|
+
- if @organization.oauth_uid
|
46
|
+
/ TODO
|
47
|
+
= link_to "Disconnect", root_path, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
|
48
|
+
- else
|
49
|
+
/ TODO
|
50
|
+
= link_to "Link to ApplicationName", root_path, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
|
51
|
+
|
52
|
+
.spacer1
|
53
|
+
|
54
|
+
.row.link-step
|
55
|
+
.col-md-1.text-center.link-step-number
|
56
|
+
%span.badge.link-step-badge
|
57
|
+
3
|
58
|
+
.col-md-3.text-center
|
59
|
+
=image_tag "logos/sync.png", class: 'logo'
|
60
|
+
.col-md-6.link-step-description
|
61
|
+
%h
|
62
|
+
- if @organization.sync_enabled
|
63
|
+
Synchronizations are enabled
|
64
|
+
- else
|
65
|
+
Synchronizations are disabled
|
66
|
+
.col-md-2.text-center.link-step-action
|
67
|
+
= link_to "Toggle #{@organization.sync_enabled ? 'off' : 'on'}", home_toggle_sync_path, method: :put, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
|
68
|
+
|
69
|
+
- else
|
70
|
+
.row
|
71
|
+
.col-md-4.col-md-offset-4.center
|
72
|
+
= link_to "Link your Maestrano account", Maestrano::Connector::Rails::Engine.routes.url_helpers.default_maestrano_auth_saml_index_path(tenant: :default), class: 'btn btn-warning'
|
73
|
+
|
74
|
+
- if @organization
|
75
|
+
.spacer2
|
76
|
+
.row
|
77
|
+
.col-md-12
|
78
|
+
%h1
|
79
|
+
Synchronized entities
|
80
|
+
%small You can customize which entities are synchronized by the connector
|
81
|
+
|
82
|
+
.spacer1
|
83
|
+
.row
|
84
|
+
.col-md-10.col-md-offset-1
|
85
|
+
= form_tag home_update_path(id: @organization.id), method: :put do
|
86
|
+
- @organization.synchronized_entities.each do |k, v|
|
87
|
+
.row.sync-entity
|
88
|
+
.col-md-1.link-step-action
|
89
|
+
%input{type: "checkbox", id: "#{k}", name: "#{k}", checked: v}
|
90
|
+
.col-md-7{style: 'padding-top: 5px;'}
|
91
|
+
%label{:for => "#{k}"} #{k.to_s.humanize}
|
92
|
+
-if is_admin
|
93
|
+
.col-md-4.text-right
|
94
|
+
- if v && @organization.oauth_uid
|
95
|
+
- if @organization.sync_enabled
|
96
|
+
= link_to "Force a synchronization for #{k.to_s.humanize.pluralize} only", home_synchronize_path(opts: {only_entities: [k.to_s]}), method: :post, class: "btn btn-warning btn-sm #{is_admin && @organization.sync_enabled ? '' : 'disabled'}"
|
97
|
+
- else
|
98
|
+
.btn.btn-warning.btn-sm.disabled= "Force a synchronization for #{k.to_s.humanize.pluralize} only"
|
99
|
+
-if is_admin
|
100
|
+
.spacer1
|
101
|
+
.row
|
102
|
+
.col-md-4.col-md-offset-8.text-right
|
103
|
+
=submit_tag :Save, class: "btn btn-primary"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
/ TODO
|
2
|
+
/ Replace ApplicationName
|
3
|
+
!!!
|
4
|
+
%html
|
5
|
+
%head
|
6
|
+
%title ApplicationName Connector for Connec!™
|
7
|
+
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
|
8
|
+
= javascript_include_tag 'application', 'data-turbolinks-track' => true
|
9
|
+
= csrf_meta_tags
|
10
|
+
%body
|
11
|
+
.navbar.navbar-fixed-top.navbar-inverse
|
12
|
+
.container-fluid
|
13
|
+
.collapse.navbar-collapse#maestrano-navbar-collapse
|
14
|
+
%ul.nav.navbar-nav
|
15
|
+
=image_tag "logos/default.png", class: 'external-logo'
|
16
|
+
|
17
|
+
%ul.nav.navbar-nav
|
18
|
+
%li=link_to "Status", root_path
|
19
|
+
%li=link_to "History", synchronizations_index_path
|
20
|
+
-if is_admin
|
21
|
+
%li=link_to "Mapping", shared_entities_index_path
|
22
|
+
|
23
|
+
-if current_user
|
24
|
+
%ul.nav.navbar-nav.navbar-right
|
25
|
+
%li.dropdown
|
26
|
+
%a{:href=>"#", :class=>"dropdown-toggle", :'data-toggle'=>"dropdown", :role=>"button"}
|
27
|
+
Hi #{current_user.first_name} #{current_user.last_name}
|
28
|
+
|
29
|
+
.padding-navbar
|
30
|
+
|
31
|
+
.wrapper{style: 'margin-bottom: 15px; '}
|
32
|
+
- if flash.any?
|
33
|
+
- flash.each do |flash_key, message|
|
34
|
+
.alert{:class => "alert-#{flash_key}"}
|
35
|
+
%strong= flash.discard(flash_key)
|
36
|
+
|
37
|
+
= yield
|
38
|
+
|
39
|
+
.footer
|
40
|
+
.container
|
41
|
+
.row
|
42
|
+
.col-xs-12
|
43
|
+
.copyright
|
44
|
+
© Copyright #{Time.now.year} Maestrano Pty Ltd (ABN: 80 152 564 424).
|
45
|
+
All rights reserved. Various trademarks held by their respective owners.
|