granule 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +21 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +8 -7
- data/Gemfile +2 -0
- data/README.md +112 -13
- data/Rakefile +2 -0
- data/bin/console +1 -0
- data/exe/granule +1 -2
- data/granule.gemspec +2 -0
- data/lib/granule/cli.rb +19 -4
- data/lib/granule/feature/devise_semantic/devise.en.yml +65 -0
- data/lib/granule/feature/devise_semantic/devise.rb +303 -0
- data/lib/granule/feature/devise_semantic/devise_create_users.rb +43 -0
- data/lib/granule/feature/devise_semantic/user.rb +8 -0
- data/lib/granule/feature/devise_semantic/views/users/confirmations/new.html.erb +16 -0
- data/lib/granule/feature/devise_semantic/views/users/mailer/confirmation_instructions.html.erb +5 -0
- data/lib/granule/feature/devise_semantic/views/users/mailer/email_changed.html.erb +7 -0
- data/lib/granule/feature/devise_semantic/views/users/mailer/password_change.html.erb +3 -0
- data/lib/granule/feature/devise_semantic/views/users/mailer/reset_password_instructions.html.erb +8 -0
- data/lib/granule/feature/devise_semantic/views/users/mailer/unlock_instructions.html.erb +7 -0
- data/lib/granule/feature/devise_semantic/views/users/passwords/edit.html.erb +25 -0
- data/lib/granule/feature/devise_semantic/views/users/passwords/new.html.erb +18 -0
- data/lib/granule/feature/devise_semantic/views/users/registrations/edit.html.erb +45 -0
- data/lib/granule/feature/devise_semantic/views/users/registrations/new.html.erb +31 -0
- data/lib/granule/feature/devise_semantic/views/users/sessions/new.html.erb +37 -0
- data/lib/granule/feature/devise_semantic/views/users/shared/_error_messages.html.erb +14 -0
- data/lib/granule/feature/devise_semantic/views/users/shared/_links.html.erb +25 -0
- data/lib/granule/feature/devise_semantic/views/users/show.html.erb +22 -0
- data/lib/granule/feature/devise_semantic/views/users/unlocks/new.html.erb +16 -0
- data/lib/granule/feature/devise_semantic.rb +58 -0
- data/lib/granule/feature/semantic_react/hello_react.jsx +340 -0
- data/lib/granule/feature/semantic_react/homes_controller.rb +5 -0
- data/lib/granule/feature/semantic_react/index.html.erb +0 -0
- data/lib/granule/feature/semantic_react.rb +50 -0
- data/lib/granule/new/base/.dockerignore.tt +5 -0
- data/lib/granule/new/base/.gitignore.tt +33 -0
- data/lib/granule/{templates → new/base}/.rubocop.yml.tt +0 -0
- data/lib/granule/new/base/Dockerfile.dev.tt +34 -0
- data/lib/granule/{templates → new/base}/Gemfile.tt +5 -8
- data/lib/granule/new/base/README.md.tt +21 -0
- data/lib/granule/new/base/database.yml.tt +21 -0
- data/lib/granule/new/base/docker-compose.yml.tt +83 -0
- data/lib/granule/new/base.rb +19 -0
- data/lib/granule/version.rb +4 -2
- data/lib/granule.rb +2 -1
- metadata +38 -8
- data/Gemfile.lock +0 -169
- data/lib/granule/template.rb +0 -10
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Granule
|
4
|
+
module Feature
|
5
|
+
class DeviseSemantic < Thor
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
APPLICATION_HTML_ERB_PATH = './app/views/layouts/application.html.erb'
|
9
|
+
APPLICATION_PATH = './config/application.rb'
|
10
|
+
DEVELOPMENT_PATH = './config/environments/development.rb'
|
11
|
+
ROUTES_PATH = './config/routes.rb'
|
12
|
+
|
13
|
+
no_commands do
|
14
|
+
def call
|
15
|
+
check_files
|
16
|
+
|
17
|
+
copy_file 'devise.rb', './config/initializers/devise.rb'
|
18
|
+
copy_file 'user.rb', './app/models/user.rb'
|
19
|
+
copy_file 'devise.en.yml', './config/locales/devise.en.yml'
|
20
|
+
copy_file 'devise_create_users.rb', "./db/migrate/#{Time.now.strftime('%Y%m%d%H%m%S')}_devise_create_users.rb"
|
21
|
+
|
22
|
+
uncomment_lines APPLICATION_PATH, %r{sprockets/railtie}
|
23
|
+
directory 'views', './app/views'
|
24
|
+
insert_into_file ROUTES_PATH, "\tdevise_for :users\n",
|
25
|
+
after: "Rails.application.routes.draw do\n"
|
26
|
+
insert_into_file APPLICATION_HTML_ERB_PATH,
|
27
|
+
"\t\t<% if current_user %>\n"\
|
28
|
+
"\t\t\t<div class=\"ui pointing secondary menu\">\n"\
|
29
|
+
"\t\t\t\t<a class=\"active item\" href=\"/\">Home</a>\n"\
|
30
|
+
"\t\t\t\t<div class=\"right menu\">\n"\
|
31
|
+
"\t\t\t\t\t<%= link_to 'Logout', destroy_user_session_url, "\
|
32
|
+
"method: :delete, class: 'item' %>\n"\
|
33
|
+
"\t\t\t\t</div>\n"\
|
34
|
+
"\t\t\t</div>\n"\
|
35
|
+
"\t\t<% end %>\n",
|
36
|
+
after: "<body>\n"
|
37
|
+
|
38
|
+
insert_into_file DEVELOPMENT_PATH,
|
39
|
+
"\tconfig.action_mailer.default_url_options = { host: 'localhost', port: 3000 }\n"\
|
40
|
+
"\tconfig.action_mailer.delivery_method = :letter_opener_web\n",
|
41
|
+
after: "Rails.application.configure do\n"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.source_root
|
46
|
+
"#{File.dirname(__FILE__)}/devise_semantic"
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def check_files
|
52
|
+
[APPLICATION_HTML_ERB_PATH, APPLICATION_PATH, DEVELOPMENT_PATH, ROUTES_PATH].each do |file_path|
|
53
|
+
raise Granule::Error, "#{file_path} doesn't exist" unless File.file?(file_path)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,340 @@
|
|
1
|
+
// Run this example by adding <%= javascript_pack_tag 'hello_react' %> to the head of your layout file,
|
2
|
+
// like app/views/layouts/application.html.erb. All it does is render <div>Hello React</div> at the bottom
|
3
|
+
// of the page.
|
4
|
+
|
5
|
+
import ReactDOM from 'react-dom'
|
6
|
+
import React, { Component } from 'react'
|
7
|
+
import PropTypes from 'prop-types'
|
8
|
+
import {
|
9
|
+
Button,
|
10
|
+
Container,
|
11
|
+
Divider,
|
12
|
+
Grid,
|
13
|
+
Header,
|
14
|
+
Icon,
|
15
|
+
Image,
|
16
|
+
List,
|
17
|
+
Menu,
|
18
|
+
Responsive,
|
19
|
+
Segment,
|
20
|
+
Sidebar,
|
21
|
+
Visibility,
|
22
|
+
} from 'semantic-ui-react'
|
23
|
+
|
24
|
+
const getWidth = () => {
|
25
|
+
const isSSR = typeof window === 'undefined'
|
26
|
+
|
27
|
+
return isSSR ? Responsive.onlyTablet.minWidth : window.innerWidth
|
28
|
+
}
|
29
|
+
|
30
|
+
const HomepageHeading = ({ mobile }) => (
|
31
|
+
<Container text>
|
32
|
+
<Header
|
33
|
+
as='h1'
|
34
|
+
content='Imagine-a-Company'
|
35
|
+
inverted
|
36
|
+
style={{
|
37
|
+
fontSize: mobile ? '2em' : '4em',
|
38
|
+
fontWeight: 'normal',
|
39
|
+
marginBottom: 0,
|
40
|
+
marginTop: mobile ? '1.5em' : '3em',
|
41
|
+
}}
|
42
|
+
/>
|
43
|
+
<Header
|
44
|
+
as='h2'
|
45
|
+
content='Do whatever you want when you want to.'
|
46
|
+
inverted
|
47
|
+
style={{
|
48
|
+
fontSize: mobile ? '1.5em' : '1.7em',
|
49
|
+
fontWeight: 'normal',
|
50
|
+
marginTop: mobile ? '0.5em' : '1.5em',
|
51
|
+
}}
|
52
|
+
/>
|
53
|
+
<Button primary size='huge'>
|
54
|
+
Get Started
|
55
|
+
<Icon name='right arrow' />
|
56
|
+
</Button>
|
57
|
+
</Container>
|
58
|
+
)
|
59
|
+
|
60
|
+
HomepageHeading.propTypes = {
|
61
|
+
mobile: PropTypes.bool,
|
62
|
+
}
|
63
|
+
|
64
|
+
/* Heads up!
|
65
|
+
* Neither Semantic UI nor Semantic UI React offer a responsive navbar, however, it can be implemented easily.
|
66
|
+
* It can be more complicated, but you can create really flexible markup.
|
67
|
+
*/
|
68
|
+
class DesktopContainer extends Component {
|
69
|
+
state = {}
|
70
|
+
|
71
|
+
hideFixedMenu = () => this.setState({ fixed: false })
|
72
|
+
showFixedMenu = () => this.setState({ fixed: true })
|
73
|
+
|
74
|
+
render() {
|
75
|
+
const { children } = this.props
|
76
|
+
const { fixed } = this.state
|
77
|
+
|
78
|
+
return (
|
79
|
+
<Responsive getWidth={getWidth} minWidth={Responsive.onlyTablet.minWidth}>
|
80
|
+
<Visibility
|
81
|
+
once={false}
|
82
|
+
onBottomPassed={this.showFixedMenu}
|
83
|
+
onBottomPassedReverse={this.hideFixedMenu}
|
84
|
+
>
|
85
|
+
<Segment
|
86
|
+
inverted
|
87
|
+
textAlign='center'
|
88
|
+
style={{ minHeight: 700, padding: '1em 0em' }}
|
89
|
+
vertical
|
90
|
+
>
|
91
|
+
<Menu
|
92
|
+
fixed={fixed ? 'top' : null}
|
93
|
+
inverted={!fixed}
|
94
|
+
pointing={!fixed}
|
95
|
+
secondary={!fixed}
|
96
|
+
size='large'
|
97
|
+
>
|
98
|
+
<Container>
|
99
|
+
<Menu.Item as='a' active>
|
100
|
+
Home
|
101
|
+
</Menu.Item>
|
102
|
+
<Menu.Item as='a'>Work</Menu.Item>
|
103
|
+
<Menu.Item as='a'>Company</Menu.Item>
|
104
|
+
<Menu.Item as='a'>Careers</Menu.Item>
|
105
|
+
<Menu.Item position='right'>
|
106
|
+
<Button as='a' inverted={!fixed}>
|
107
|
+
Log in
|
108
|
+
</Button>
|
109
|
+
<Button as='a' inverted={!fixed} primary={fixed} style={{ marginLeft: '0.5em' }}>
|
110
|
+
Sign Up
|
111
|
+
</Button>
|
112
|
+
</Menu.Item>
|
113
|
+
</Container>
|
114
|
+
</Menu>
|
115
|
+
<HomepageHeading />
|
116
|
+
</Segment>
|
117
|
+
</Visibility>
|
118
|
+
|
119
|
+
{children}
|
120
|
+
</Responsive>
|
121
|
+
)
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
DesktopContainer.propTypes = {
|
126
|
+
children: PropTypes.node,
|
127
|
+
}
|
128
|
+
|
129
|
+
class MobileContainer extends Component {
|
130
|
+
state = {}
|
131
|
+
|
132
|
+
handleSidebarHide = () => this.setState({ sidebarOpened: false })
|
133
|
+
|
134
|
+
handleToggle = () => this.setState({ sidebarOpened: true })
|
135
|
+
|
136
|
+
render() {
|
137
|
+
const { children } = this.props
|
138
|
+
const { sidebarOpened } = this.state
|
139
|
+
|
140
|
+
return (
|
141
|
+
<Responsive
|
142
|
+
as={Sidebar.Pushable}
|
143
|
+
getWidth={getWidth}
|
144
|
+
maxWidth={Responsive.onlyMobile.maxWidth}
|
145
|
+
>
|
146
|
+
<Sidebar
|
147
|
+
as={Menu}
|
148
|
+
animation='push'
|
149
|
+
inverted
|
150
|
+
onHide={this.handleSidebarHide}
|
151
|
+
vertical
|
152
|
+
visible={sidebarOpened}
|
153
|
+
>
|
154
|
+
<Menu.Item as='a' active>
|
155
|
+
Home
|
156
|
+
</Menu.Item>
|
157
|
+
<Menu.Item as='a'>Work</Menu.Item>
|
158
|
+
<Menu.Item as='a'>Company</Menu.Item>
|
159
|
+
<Menu.Item as='a'>Careers</Menu.Item>
|
160
|
+
<Menu.Item as='a'>Log in</Menu.Item>
|
161
|
+
<Menu.Item as='a'>Sign Up</Menu.Item>
|
162
|
+
</Sidebar>
|
163
|
+
|
164
|
+
<Sidebar.Pusher dimmed={sidebarOpened}>
|
165
|
+
<Segment
|
166
|
+
inverted
|
167
|
+
textAlign='center'
|
168
|
+
style={{ minHeight: 350, padding: '1em 0em' }}
|
169
|
+
vertical
|
170
|
+
>
|
171
|
+
<Container>
|
172
|
+
<Menu inverted pointing secondary size='large'>
|
173
|
+
<Menu.Item onClick={this.handleToggle}>
|
174
|
+
<Icon name='sidebar' />
|
175
|
+
</Menu.Item>
|
176
|
+
<Menu.Item position='right'>
|
177
|
+
<Button as='a' inverted>
|
178
|
+
Log in
|
179
|
+
</Button>
|
180
|
+
<Button as='a' inverted style={{ marginLeft: '0.5em' }}>
|
181
|
+
Sign Up
|
182
|
+
</Button>
|
183
|
+
</Menu.Item>
|
184
|
+
</Menu>
|
185
|
+
</Container>
|
186
|
+
<HomepageHeading mobile />
|
187
|
+
</Segment>
|
188
|
+
|
189
|
+
{children}
|
190
|
+
</Sidebar.Pusher>
|
191
|
+
</Responsive>
|
192
|
+
)
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
MobileContainer.propTypes = {
|
197
|
+
children: PropTypes.node,
|
198
|
+
}
|
199
|
+
|
200
|
+
const ResponsiveContainer = ({ children }) => (
|
201
|
+
<div>
|
202
|
+
<DesktopContainer>{children}</DesktopContainer>
|
203
|
+
<MobileContainer>{children}</MobileContainer>
|
204
|
+
</div>
|
205
|
+
)
|
206
|
+
|
207
|
+
ResponsiveContainer.propTypes = {
|
208
|
+
children: PropTypes.node,
|
209
|
+
}
|
210
|
+
|
211
|
+
const HomepageLayout = () => (
|
212
|
+
<ResponsiveContainer>
|
213
|
+
<Segment style={{ padding: '8em 0em' }} vertical>
|
214
|
+
<Grid container stackable verticalAlign='middle'>
|
215
|
+
<Grid.Row>
|
216
|
+
<Grid.Column width={8}>
|
217
|
+
<Header as='h3' style={{ fontSize: '2em' }}>
|
218
|
+
We Help Companies and Companions
|
219
|
+
</Header>
|
220
|
+
<p style={{ fontSize: '1.33em' }}>
|
221
|
+
We can give your company superpowers to do things that they never thought possible.
|
222
|
+
Let us delight your customers and empower your needs... through pure data analytics.
|
223
|
+
</p>
|
224
|
+
<Header as='h3' style={{ fontSize: '2em' }}>
|
225
|
+
We Make Bananas That Can Dance
|
226
|
+
</Header>
|
227
|
+
<p style={{ fontSize: '1.33em' }}>
|
228
|
+
Yes that's right, you thought it was the stuff of dreams, but even bananas can be
|
229
|
+
bioengineered.
|
230
|
+
</p>
|
231
|
+
</Grid.Column>
|
232
|
+
<Grid.Column floated='right' width={6}>
|
233
|
+
<Image bordered rounded size='large' src='https://via.placeholder.com/500x300.png' />
|
234
|
+
</Grid.Column>
|
235
|
+
</Grid.Row>
|
236
|
+
<Grid.Row>
|
237
|
+
<Grid.Column textAlign='center'>
|
238
|
+
<Button size='huge'>Check Them Out</Button>
|
239
|
+
</Grid.Column>
|
240
|
+
</Grid.Row>
|
241
|
+
</Grid>
|
242
|
+
</Segment>
|
243
|
+
<Segment style={{ padding: '0em' }} vertical>
|
244
|
+
<Grid celled='internally' columns='equal' stackable>
|
245
|
+
<Grid.Row textAlign='center'>
|
246
|
+
<Grid.Column style={{ paddingBottom: '5em', paddingTop: '5em' }}>
|
247
|
+
<Header as='h3' style={{ fontSize: '2em' }}>
|
248
|
+
"What a Company"
|
249
|
+
</Header>
|
250
|
+
<p style={{ fontSize: '1.33em' }}>That is what they all say about us</p>
|
251
|
+
</Grid.Column>
|
252
|
+
<Grid.Column style={{ paddingBottom: '5em', paddingTop: '5em' }}>
|
253
|
+
<Header as='h3' style={{ fontSize: '2em' }}>
|
254
|
+
"I shouldn't have gone with their competitor."
|
255
|
+
</Header>
|
256
|
+
<p style={{ fontSize: '1.33em' }}>
|
257
|
+
<Image avatar src='https://via.placeholder.com/500x300.png' />
|
258
|
+
<b>Nan</b> Chief Fun Officer Acme Toys
|
259
|
+
</p>
|
260
|
+
</Grid.Column>
|
261
|
+
</Grid.Row>
|
262
|
+
</Grid>
|
263
|
+
</Segment>
|
264
|
+
<Segment style={{ padding: '8em 0em' }} vertical>
|
265
|
+
<Container text>
|
266
|
+
<Header as='h3' style={{ fontSize: '2em' }}>
|
267
|
+
Breaking The Grid, Grabs Your Attention
|
268
|
+
</Header>
|
269
|
+
<p style={{ fontSize: '1.33em' }}>
|
270
|
+
Instead of focusing on content creation and hard work, we have learned how to master the
|
271
|
+
art of doing nothing by providing massive amounts of whitespace and generic content that
|
272
|
+
can seem massive, monolithic and worth your attention.
|
273
|
+
</p>
|
274
|
+
<Button as='a' size='large'>
|
275
|
+
Read More
|
276
|
+
</Button>
|
277
|
+
<Divider
|
278
|
+
as='h4'
|
279
|
+
className='header'
|
280
|
+
horizontal
|
281
|
+
style={{ margin: '3em 0em', textTransform: 'uppercase' }}
|
282
|
+
>
|
283
|
+
<a href='#'>Case Studies</a>
|
284
|
+
</Divider>
|
285
|
+
<Header as='h3' style={{ fontSize: '2em' }}>
|
286
|
+
Did We Tell You About Our Bananas?
|
287
|
+
</Header>
|
288
|
+
<p style={{ fontSize: '1.33em' }}>
|
289
|
+
Yes I know you probably disregarded the earlier boasts as non-sequitur filler content, but
|
290
|
+
it's really true. It took years of gene splicing and combinatory DNA research, but our
|
291
|
+
bananas can really dance.
|
292
|
+
</p>
|
293
|
+
<Button as='a' size='large'>
|
294
|
+
I'm Still Quite Interested
|
295
|
+
</Button>
|
296
|
+
</Container>
|
297
|
+
</Segment>
|
298
|
+
<Segment inverted vertical style={{ padding: '5em 0em' }}>
|
299
|
+
<Container>
|
300
|
+
<Grid divided inverted stackable>
|
301
|
+
<Grid.Row>
|
302
|
+
<Grid.Column width={3}>
|
303
|
+
<Header inverted as='h4' content='About' />
|
304
|
+
<List link inverted>
|
305
|
+
<List.Item as='a'>Sitemap</List.Item>
|
306
|
+
<List.Item as='a'>Contact Us</List.Item>
|
307
|
+
<List.Item as='a'>Religious Ceremonies</List.Item>
|
308
|
+
<List.Item as='a'>Gazebo Plans</List.Item>
|
309
|
+
</List>
|
310
|
+
</Grid.Column>
|
311
|
+
<Grid.Column width={3}>
|
312
|
+
<Header inverted as='h4' content='Services' />
|
313
|
+
<List link inverted>
|
314
|
+
<List.Item as='a'>Banana Pre-Order</List.Item>
|
315
|
+
<List.Item as='a'>DNA FAQ</List.Item>
|
316
|
+
<List.Item as='a'>How To Access</List.Item>
|
317
|
+
<List.Item as='a'>Favorite X-Men</List.Item>
|
318
|
+
</List>
|
319
|
+
</Grid.Column>
|
320
|
+
<Grid.Column width={7}>
|
321
|
+
<Header as='h4' inverted>
|
322
|
+
Footer Header
|
323
|
+
</Header>
|
324
|
+
<p>
|
325
|
+
Extra space for a call to action inside the footer that could help re-engage users.
|
326
|
+
</p>
|
327
|
+
</Grid.Column>
|
328
|
+
</Grid.Row>
|
329
|
+
</Grid>
|
330
|
+
</Container>
|
331
|
+
</Segment>
|
332
|
+
</ResponsiveContainer>
|
333
|
+
)
|
334
|
+
|
335
|
+
document.addEventListener('DOMContentLoaded', () => {
|
336
|
+
ReactDOM.render(
|
337
|
+
<HomepageLayout />,
|
338
|
+
document.body.appendChild(document.createElement('div')),
|
339
|
+
)
|
340
|
+
})
|
File without changes
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Granule
|
4
|
+
module Feature
|
5
|
+
class SemanticReact < Thor
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
APPLICATION_JS_PATH = './app/javascript/packs/application.js'
|
9
|
+
APPLICATION_HTML_ERB_PATH = './app/views/layouts/application.html.erb'
|
10
|
+
PACKAGE_JSON_PATH = './package.json'
|
11
|
+
ROUTES_PATH = './config/routes.rb'
|
12
|
+
|
13
|
+
no_commands do
|
14
|
+
def call
|
15
|
+
check_files
|
16
|
+
|
17
|
+
copy_file 'homes_controller.rb', './app/controllers/homes_controller.rb'
|
18
|
+
copy_file 'hello_react.jsx', './app/javascript/packs/hello_react.jsx', force: true
|
19
|
+
copy_file 'index.html.erb', './app/views/homes/index.html.erb'
|
20
|
+
|
21
|
+
insert_into_file APPLICATION_JS_PATH,
|
22
|
+
"import 'semantic-ui-css/semantic.min.css';\n\n",
|
23
|
+
before: 'require("@rails/ujs").start()'
|
24
|
+
|
25
|
+
insert_into_file APPLICATION_HTML_ERB_PATH,
|
26
|
+
"\t\t<%= javascript_pack_tag 'hello_react' %>",
|
27
|
+
after: "<%= javascript_pack_tag 'application' %>\n"
|
28
|
+
|
29
|
+
insert_into_file ROUTES_PATH, "\troot 'homes#index'\n", after: "Rails.application.routes.draw do\n"
|
30
|
+
|
31
|
+
insert_into_file PACKAGE_JSON_PATH,
|
32
|
+
"\t\t\"semantic-ui-css\": \"^2.4.1\",\n\t\t\"semantic-ui-react\": \"^0.88.1\",\n",
|
33
|
+
after: "\"dependencies\": {\n"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.source_root
|
38
|
+
"#{File.dirname(__FILE__)}/semantic_react"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def check_files
|
44
|
+
[APPLICATION_JS_PATH, ROUTES_PATH, APPLICATION_HTML_ERB_PATH, PACKAGE_JSON_PATH].each do |file_path|
|
45
|
+
raise Granule::Error, "#{file_path} doesn't exist" unless File.file?(file_path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
2
|
+
#
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
5
|
+
# git config --global core.excludesfile '~/.gitignore_global'
|
6
|
+
|
7
|
+
# Ignore bundler config.
|
8
|
+
/.bundle
|
9
|
+
|
10
|
+
# Ignore all logfiles and tempfiles.
|
11
|
+
/log/*
|
12
|
+
/tmp/*
|
13
|
+
!/log/.keep
|
14
|
+
!/tmp/.keep
|
15
|
+
|
16
|
+
# Ignore uploaded files in development.
|
17
|
+
/storage/*
|
18
|
+
!/storage/.keep
|
19
|
+
|
20
|
+
/public/assets
|
21
|
+
.byebug_history
|
22
|
+
|
23
|
+
# Ignore master key for decrypting credentials and more.
|
24
|
+
/config/master.key
|
25
|
+
|
26
|
+
/public/packs
|
27
|
+
/public/packs-test
|
28
|
+
/node_modules
|
29
|
+
/yarn-error.log
|
30
|
+
yarn-debug.log*
|
31
|
+
.yarn-integrity
|
32
|
+
|
33
|
+
.psqlrc
|
File without changes
|
@@ -0,0 +1,34 @@
|
|
1
|
+
ARG RUBY_VERSION
|
2
|
+
ARG ALPINE_VERSION
|
3
|
+
FROM ruby:$RUBY_VERSION-alpine$ALPINE_VERSION
|
4
|
+
|
5
|
+
ARG BUNDLER_VERSION
|
6
|
+
|
7
|
+
RUN apk add --update --no-cache \
|
8
|
+
bash \
|
9
|
+
build-base \
|
10
|
+
postgresql-client \
|
11
|
+
postgresql-dev \
|
12
|
+
nodejs-current \
|
13
|
+
git \
|
14
|
+
imagemagick \
|
15
|
+
tzdata
|
16
|
+
|
17
|
+
RUN apk add --no-cache yarn --repository="http://dl-cdn.alpinelinux.org/alpine/edge/community"
|
18
|
+
|
19
|
+
# Configure bundler and PATH
|
20
|
+
ENV LANG=C.UTF-8 \
|
21
|
+
GEM_HOME=/bundle \
|
22
|
+
BUNDLE_JOBS=4 \
|
23
|
+
BUNDLE_RETRY=3
|
24
|
+
ENV BUNDLE_PATH $GEM_HOME
|
25
|
+
ENV BUNDLE_APP_CONFIG=$BUNDLE_PATH \
|
26
|
+
BUNDLE_BIN=$BUNDLE_PATH/bin
|
27
|
+
ENV PATH /app/bin:$BUNDLE_BIN:$PATH
|
28
|
+
|
29
|
+
RUN gem update --system && \
|
30
|
+
gem install bundler:$BUNDLER_VERSION
|
31
|
+
|
32
|
+
RUN mkdir -p /app
|
33
|
+
|
34
|
+
WORKDIR /app
|
@@ -14,7 +14,7 @@ gem 'dry-matcher'
|
|
14
14
|
gem 'dry-monads'
|
15
15
|
|
16
16
|
# Validation
|
17
|
-
gem 'dry-validation'
|
17
|
+
gem 'dry-validation'
|
18
18
|
|
19
19
|
# I18n for js
|
20
20
|
gem 'i18n-js'
|
@@ -25,15 +25,15 @@ gem 'mini_magick'
|
|
25
25
|
# Database adapter
|
26
26
|
gem 'pg'
|
27
27
|
|
28
|
+
# Data migrator
|
29
|
+
gem 'data_migrate'
|
30
|
+
|
28
31
|
# Ruby Server
|
29
32
|
gem 'puma'
|
30
33
|
|
31
34
|
# Framework
|
32
35
|
gem 'rails'
|
33
36
|
|
34
|
-
# React rails monolith
|
35
|
-
gem 'react-rails'
|
36
|
-
|
37
37
|
# User roles management
|
38
38
|
gem 'rolify'
|
39
39
|
|
@@ -42,7 +42,7 @@ gem 'webpacker'
|
|
42
42
|
|
43
43
|
group :development do
|
44
44
|
gem 'better_errors'
|
45
|
-
gem '
|
45
|
+
gem 'letter_opener_web'
|
46
46
|
gem 'listen'
|
47
47
|
gem 'spring'
|
48
48
|
gem 'spring-watcher-listen'
|
@@ -51,7 +51,6 @@ end
|
|
51
51
|
|
52
52
|
group :test do
|
53
53
|
gem 'capybara'
|
54
|
-
gem 'fuubar'
|
55
54
|
gem 'rspec-rails'
|
56
55
|
gem 'selenium-webdriver'
|
57
56
|
gem 'shoulda-matchers'
|
@@ -60,7 +59,6 @@ group :test do
|
|
60
59
|
end
|
61
60
|
|
62
61
|
group :development, :test do
|
63
|
-
gem 'awesome_print'
|
64
62
|
gem 'brakeman', require: false
|
65
63
|
gem 'bullet'
|
66
64
|
gem 'bundler-audit', require: false
|
@@ -69,7 +67,6 @@ group :development, :test do
|
|
69
67
|
gem 'ffaker'
|
70
68
|
gem 'i18n-tasks'
|
71
69
|
gem 'lol_dba'
|
72
|
-
gem 'pry-byebug'
|
73
70
|
gem 'pry-rails'
|
74
71
|
gem 'pry-rescue'
|
75
72
|
gem 'pry-stack_explorer'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Docker for development
|
2
|
+
|
3
|
+
## Provisioning
|
4
|
+
|
5
|
+
Install dependencies and create/migrate db schema with seeds
|
6
|
+
|
7
|
+
```
|
8
|
+
$ docker-compose run --rm runner
|
9
|
+
|
10
|
+
> bundle install
|
11
|
+
> yarn install
|
12
|
+
> rails db:create
|
13
|
+
> rails db:migrate
|
14
|
+
> rails data:migrate
|
15
|
+
```
|
16
|
+
|
17
|
+
## Run server
|
18
|
+
|
19
|
+
```
|
20
|
+
$ docker-compose run --rm --service-ports rails
|
21
|
+
```
|
@@ -0,0 +1,21 @@
|
|
1
|
+
---
|
2
|
+
default: &default
|
3
|
+
adapter: postgresql
|
4
|
+
encoding: unicode
|
5
|
+
pool: <%= ENV.fetch('RAILS_MAX_THREADS') { 5 } %>
|
6
|
+
|
7
|
+
development: &development
|
8
|
+
<<: *default
|
9
|
+
host: postgres
|
10
|
+
port: 5432
|
11
|
+
username: postgres
|
12
|
+
password: postgres
|
13
|
+
database: '<%= "#{app_name}_development" %>'
|
14
|
+
|
15
|
+
test:
|
16
|
+
<<: *development
|
17
|
+
database: '<%= "#{app_name}_test" %>'
|
18
|
+
|
19
|
+
production:
|
20
|
+
<<: *default
|
21
|
+
url: <%= ENV['DATABASE_URL'] %>
|