breezy 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/breezy.rb +11 -6
  3. data/lib/breezy/render.rb +8 -1
  4. data/lib/breezy/version.rb +1 -1
  5. data/lib/breezy/xhr_headers.rb +9 -22
  6. data/lib/generators/breezy/view/templates/{view.jsx → view.jsx.mobile} +0 -0
  7. data/lib/generators/breezy/view/templates/{view.js → view.jsx.web} +0 -0
  8. data/lib/generators/breezy/view/view_generator.rb +3 -3
  9. data/lib/generators/rails/breezy_generator.rb +103 -0
  10. data/lib/generators/rails/scaffold_controller_generator.rb +13 -0
  11. data/lib/generators/rails/templates/controller.rb.tt +85 -0
  12. data/lib/generators/rails/templates/edit.js.props +18 -0
  13. data/lib/generators/rails/templates/index.js.props +20 -0
  14. data/lib/generators/rails/templates/mobile/edit.jsx +76 -0
  15. data/lib/generators/rails/templates/mobile/elements.js +88 -0
  16. data/lib/generators/rails/templates/mobile/form.jsx +34 -0
  17. data/lib/generators/rails/templates/mobile/index.jsx +103 -0
  18. data/lib/generators/rails/templates/mobile/new.jsx +75 -0
  19. data/lib/generators/rails/templates/mobile/show.jsx +32 -0
  20. data/lib/generators/rails/templates/new.js.props +7 -0
  21. data/lib/generators/rails/templates/show.js.props +12 -0
  22. data/lib/generators/rails/templates/web/base.jsx +68 -0
  23. data/lib/generators/rails/templates/web/edit.jsx +34 -0
  24. data/lib/generators/rails/templates/web/form.jsx +37 -0
  25. data/lib/generators/rails/templates/web/index.jsx +56 -0
  26. data/lib/generators/rails/templates/web/new.jsx +31 -0
  27. data/lib/generators/rails/templates/web/show.jsx +28 -0
  28. data/lib/install/templates/mobile/app.js +31 -38
  29. data/lib/install/templates/mobile/package.json +5 -2
  30. data/lib/install/templates/web/application.js +7 -3
  31. data/lib/install/web.rb +2 -4
  32. metadata +23 -6
  33. data/lib/assets/javascript/breezy.js +0 -6067
  34. data/lib/breezy/xhr_redirect.rb +0 -13
@@ -0,0 +1,88 @@
1
+ import React from 'react'
2
+ import {View, Text, ScrollView, TouchableOpacity, Alert} from 'react-native'
3
+ import {Header, Card, ListItem, Icon, Button} from 'react-native-elements'
4
+
5
+ export function Container(props) {
6
+ return <ScrollView contentContainerStyle={{paddingBottom: 100, flexDirection: 'column'}}>
7
+ {props.children}
8
+ </ScrollView>
9
+ }
10
+
11
+ export function BarSaveAndGoBack({onPress}) {
12
+ return (
13
+ <TouchableOpacity onPress={onPress} style={{flex: 1, flexDirection: 'row', alignItems: 'flex-end'}}>
14
+ <Icon
15
+ name='chevron-left'
16
+ color='#fff'
17
+ size={18}
18
+ underlayColor='rgba(0, 0, 0, 0)'
19
+ />
20
+ <Text style={{color: '#fff'}}>SAVE AND GO BACK</Text>
21
+ </TouchableOpacity>
22
+ )
23
+ }
24
+
25
+ export function BarGoBack({onPress}) {
26
+ return (
27
+ <TouchableOpacity onPress={onPress} style={{flex: 1, flexDirection: 'row', alignItems: 'flex-end'}}>
28
+ <Icon
29
+ name='chevron-left'
30
+ color='#fff'
31
+ size={18}
32
+ underlayColor='rgba(0, 0, 0, 0)'
33
+ />
34
+ <Text style={{color: '#fff'}}>BACK</Text>
35
+ </TouchableOpacity>
36
+ )
37
+ }
38
+
39
+ export function BarCancel({onPress}) {
40
+ return (
41
+ <TouchableOpacity onPress={onPress} style={{flex: 1, flexDirection: 'row', alignItems: 'flex-end'}}>
42
+ <Text style={{color: '#fff'}}>CANCEL</Text>
43
+ </TouchableOpacity>
44
+ )
45
+ }
46
+
47
+ export function BarNew ({onPress}) {
48
+ return (
49
+ <TouchableOpacity
50
+ onPress={onPress}
51
+ style={{flex: 1, flexDirection: 'row', alignItems: 'flex-end'}}>
52
+ <Icon
53
+ name='note-add'
54
+ color='#fff'
55
+ size={18}
56
+ underlayColor='rgba(0, 0, 0, 0)'
57
+ />
58
+ <Text style={{color: '#fff'}}>NEW</Text>
59
+ </TouchableOpacity>
60
+ )
61
+ }
62
+
63
+ export function IconDelete({onPress}) {
64
+ return <Icon
65
+ raised
66
+ name='close'
67
+ color='#f50'
68
+ size={13}
69
+ onPress={onPress}/>
70
+ }
71
+ export function IconEdit({onPress}) {
72
+ return <Icon
73
+ raised
74
+ name='mode-edit'
75
+ color='#f50'
76
+ size={13}
77
+ onPress={onPress}/>
78
+
79
+ }
80
+ export function IconShow({onPress}) {
81
+ return <Icon
82
+ raised
83
+ name='more-horiz'
84
+ color='#f50'
85
+ size={13}
86
+ onPress={onPress}/>
87
+ }
88
+
@@ -0,0 +1,34 @@
1
+ import React from 'react'
2
+ import { Field, reduxForm, stopSubmit, touch} from 'redux-form'
3
+ import {View, Text} from 'react-native'
4
+ import { FormLabel, FormInput, FormValidationMessage } from 'react-native-elements'
5
+
6
+ const RenderField = ({ input, label, type, meta: { touched, error } }) => {
7
+ return (
8
+ <View>
9
+ <FormLabel><Text>{label}</Text></FormLabel>
10
+ <FormInput onChangeText={input.onChange} value={input.value} placeholder={`Please enter ${label}`}/>
11
+ {touched && error && <FormValidationMessage><Text>{error}</Text></FormValidationMessage>}
12
+ </View>
13
+ )
14
+ }
15
+ const SimpleForm = props => {
16
+ const { error, handleSubmit, initialValue } = props
17
+
18
+ return (
19
+ <View><% attributes_list.select{|attr| attr != :id }.each do |attr| %>
20
+ <Field
21
+ name="<%=attr.to_s.camelize(:lower)%>"
22
+ label="<%=attr.to_s.humanize%>"
23
+ component={RenderField}
24
+ type="text"
25
+ />
26
+ <%end%></View>
27
+ )
28
+ }
29
+
30
+ export default reduxForm({
31
+ form: '<%=plural_table_name%>_form' // a unique identifier for this form
32
+ })(SimpleForm)
33
+
34
+
@@ -0,0 +1,103 @@
1
+ import React from 'react'
2
+ import {mapDispatchToProps, mapStateToProps} from '@jho406/breezy'
3
+ import {connect} from 'react-redux'
4
+ import {View, Text, TouchableOpacity, Alert} from 'react-native'
5
+ import {Header, Card} from 'react-native-elements'
6
+ import DropdownAlert from 'react-native-dropdownalert';
7
+ import {Container, IconDelete, IconEdit, IconShow, BarNew} from 'components/elements'
8
+
9
+ class <%= plural_table_name.camelize %>Index extends React.Component {
10
+ static defaultProps = {
11
+ <%= plural_table_name %>: []
12
+ }
13
+
14
+ constructor (props) {
15
+ super(props)
16
+ this.handleClick = this.handleClick.bind(this)
17
+ this.handleDeleteClick = this.handleDeleteClick.bind(this)
18
+ }
19
+
20
+ handleDeleteClick(path) {
21
+ const method = 'DELETE'
22
+
23
+ Alert.alert(
24
+ 'Wait!',
25
+ 'Are you sure you want to delete this <%=singular_table_name%>?',
26
+ [
27
+ {
28
+ text: 'Delete',
29
+ onPress: e => {this.props.visit(path, {method})},
30
+ style: 'destructive'
31
+ },
32
+ { text: 'Cancel', style: 'cancel' },
33
+ ],
34
+ { cancelable: false }
35
+ )
36
+ }
37
+
38
+ handleClick(path, method='GET') {
39
+ const nav = this.props.navigation
40
+ this.props.visit(path, {method}).then((rsp)=>{
41
+ if (rsp.canNavigate) {
42
+ return nav.navigate(rsp.screen, {pathQuery: rsp.pathQuery})
43
+ } else {
44
+ // There can only be one visit at a time, if `canNavigate`
45
+ // is false, then this request is being ignored for a more
46
+ // recent visit. Do Nothing.
47
+ return
48
+ }
49
+ })
50
+ }
51
+
52
+ showNotice (message) {
53
+ if (message) {
54
+ setImmediate(() => {
55
+ this.dropdown.alertWithType('success', 'Success', message);
56
+ })
57
+
58
+ return <DropdownAlert ref={ref => this.dropdown = ref} translucent={true}/>
59
+ } else {
60
+ return null
61
+ }
62
+ }
63
+
64
+ render () {
65
+ const headerOptions = {
66
+ centerComponent: { text: 'POSTS', style: { color: '#fff' } },
67
+ rightComponent: (<BarNew
68
+ onPress={e => this.handleClick('/<%=plural_table_name%>/new')}
69
+ />)
70
+ }
71
+
72
+ return (
73
+ <View>
74
+ <Header {...headerOptions}/>
75
+ <Container>
76
+ {this.props.<%=plural_table_name%>.map((<%=singular_table_name%>) => {
77
+ const {show_path, edit_path, delete_path} = <%=singular_table_name%>.meta
78
+ return (
79
+ <Card key={<%=singular_table_name%>.id}>
80
+ <TouchableOpacity>
81
+ <% attributes_list.select{|attr| attr != :id }.each do |attr| %><Text style={{marginBottom: 10}}>{<%=singular_table_name%>.<%=attr%>}</Text>
82
+ <% end %>
83
+ </TouchableOpacity>
84
+ <View style={{flex: 1, justifyContent: 'flex-end', flexDirection: 'row'}}>
85
+ <IconDelete onPress={e => this.handleDeleteClick(delete_path)}/>
86
+ <IconEdit onPress={e => this.handleClick(edit_path)}/>
87
+ <IconShow onPress={e => this.handleClick(show_path)}/>
88
+ </View>
89
+ </Card>
90
+ )
91
+ })}
92
+ </Container>
93
+ {this.showNotice(this.props.notice)}
94
+ </View>
95
+ )
96
+ }
97
+ }
98
+
99
+ export default connect(
100
+ mapStateToProps,
101
+ mapDispatchToProps
102
+ )(<%= plural_table_name.camelize %>Index)
103
+
@@ -0,0 +1,75 @@
1
+ import React from 'react'
2
+ import {submit} from 'redux-form'
3
+ import {Header} from 'react-native-elements'
4
+ import {SubmissionError} from 'redux-form'
5
+ import {View} from 'react-native'
6
+ import {connect} from 'react-redux'
7
+ import {delInPage} from '@jho406/breezy/dist/action_creators'
8
+ import {mapStateToProps, mapDispatchToProps} from '@jho406/breezy'
9
+ import <%= plural_table_name.camelize %>Form from 'components/<%= plural_table_name.camelize %>Form'
10
+ import {Container, BarSaveAndGoBack, BarCancel} from 'components/elements'
11
+
12
+ class <%= plural_table_name.camelize %>New extends React.Component {
13
+ constructor (props) {
14
+ super(props)
15
+ this.submit = this.submit.bind(this)
16
+ this.remoteSubmit = this.remoteSubmit.bind(this)
17
+ }
18
+
19
+ submit (body) {
20
+ const options = {
21
+ method: 'POST',
22
+ body: JSON.stringify(body),
23
+ contentType: 'application/json'
24
+ }
25
+
26
+ this.props.delInPage({pathQuery: this.props.pathQuery, keypath: 'errors'})
27
+ return this.props.visit('/<%= plural_table_name %>', options).then((rsp) => {
28
+ if (this.props.errors) {
29
+ throw new SubmissionError({
30
+ ...this.props.errors
31
+ })
32
+ }
33
+ if (rsp.canNavigate) {
34
+ return this.props.navigation.goBack()
35
+ } else {
36
+ // There can only ve one visit at a time, if `canNavigate`
37
+ // is false, then this request is being ignored for a more
38
+ // recent visit. Do Nothing.
39
+ return
40
+ }
41
+ }).catch((err) => {
42
+ if (err instanceof SubmissionError) {
43
+ throw err
44
+ } else {
45
+ //handle other errors here
46
+ }
47
+ })
48
+ }
49
+
50
+ remoteSubmit () {
51
+ this.props.remoteSubmit('<%=plural_table_name%>_form')
52
+ }
53
+
54
+ render () {
55
+ const headerOptions = {
56
+ leftComponent: (<BarSaveAndGoBack onPress={this.remoteSubmit} />),
57
+ rightComponent: (<BarCancel onPress={e => this.props.navigation.goBack()} />)
58
+ }
59
+
60
+ return (
61
+ <View>
62
+ <Header {...headerOptions}/>
63
+ <Container>
64
+ <<%= plural_table_name.camelize %>Form error={this.props.error} onSubmit={this.submit}/>
65
+ </Container>
66
+ </View>
67
+ )
68
+ }
69
+ }
70
+
71
+ export default connect(
72
+ mapStateToProps,
73
+ {...mapDispatchToProps, remoteSubmit: submit, delInPage}
74
+ )(<%= plural_table_name.camelize %>New)
75
+
@@ -0,0 +1,32 @@
1
+ import React from 'react'
2
+ import {mapStateToProps, mapDispatchToProps} from '@jho406/breezy'
3
+ import {connect} from 'react-redux'
4
+ import {Text, View} from 'react-native'
5
+ import {Header, Card} from 'react-native-elements'
6
+ import {Container, BarGoBack} from 'components/elements'
7
+
8
+ class <%= plural_table_name.camelize %>Show extends React.Component {
9
+ render() {
10
+ const headerOptions = {
11
+ leftComponent: (<BarGoBack onPress={ e => this.props.navigation.goBack()} />)
12
+ }
13
+
14
+ return (
15
+ <View>
16
+ <Header {...headerOptions}/>
17
+ <Container>
18
+ <Card><% attributes_list_with_timestamps.select{|attr| attr != :id }.each do |attr| %>
19
+ <Text style={{fontWeight: 'bold'}}><%=attr%></Text>
20
+ <Text>{this.props.<%=attr%>}</Text><% end %>
21
+ </Card>
22
+ </Container>
23
+ </View>
24
+ )
25
+ }
26
+ }
27
+
28
+ export default connect(
29
+ mapStateToProps,
30
+ mapDispatchToProps
31
+ )(<%= plural_table_name.camelize %>Show)
32
+
@@ -0,0 +1,7 @@
1
+ if @<%= singular_table_name %>.errors.any?
2
+ json.errors @<%= singular_table_name %>.errors.to_h
3
+ end
4
+
5
+ json.meta do
6
+ json.index_path <%= plural_table_name %>_path
7
+ end
@@ -0,0 +1,12 @@
1
+ <%- attributes_list_with_timestamps.each do |attr|-%>
2
+ json.<%=attr%> @<%= singular_table_name %>.<%=attr%>
3
+ <%- end -%>
4
+
5
+ if notice
6
+ json.notice notice
7
+ end
8
+
9
+ json.meta do
10
+ json.index_path <%= plural_table_name %>_path
11
+ json.edit_path edit_<%= singular_table_name %>_path(@<%= singular_table_name %>)
12
+ end
@@ -0,0 +1,68 @@
1
+ import React from 'react'
2
+ import {SubmissionError} from 'redux-form'
3
+
4
+ export default class extends React.Component {
5
+ constructor (props) {
6
+ super(props)
7
+ this.handleSubmit = this.handleSubmit.bind(this)
8
+ this.handleClick = this.handleClick.bind(this)
9
+ }
10
+
11
+ handleClick(path, method='GET') {
12
+ this.props.visit(path, {method}).then((rsp)=>{
13
+ // the window needs a full reload when asset fingerprint changes
14
+ if (rsp.needsRefresh) {
15
+ return window.location = rsp.url
16
+ }
17
+
18
+ if (rsp.canNavigate) {
19
+ return this.props.navigateTo(rsp.screen, rsp.pathQuery)
20
+ } else {
21
+ // There can only be one visit at a time, if `canNavigate`
22
+ // is false, then this request is being ignored for a more
23
+ // recent visit. Do Nothing.
24
+ return
25
+ }
26
+ })
27
+ }
28
+
29
+ handleSubmit (url, body, method='POST') {
30
+ const options = {
31
+ method,
32
+ body: JSON.stringify(body),
33
+ contentType: 'application/json'
34
+ }
35
+
36
+ return this.props.visit(url, options).then((rsp) => {
37
+ // the window needs a full reload when asset fingerprint changes
38
+ if (rsp.needsRefresh) {
39
+ return window.location = rsp.url
40
+ }
41
+
42
+ if (this.props.errors) {
43
+ throw new SubmissionError({
44
+ ...this.props.errors
45
+ })
46
+ }
47
+
48
+ if (rsp.canNavigate) {
49
+ //Uncomment this if you want full-page reloads
50
+ // window.location = rsp.url
51
+
52
+ return this.props.navigateTo(rsp.screen, rsp.pathQuery)
53
+ } else {
54
+ // There can only ve one visit at a time, if `canNavigate`
55
+ // is false, then this request is being ignored for a more
56
+ // recent visit. Do Nothing.
57
+ return
58
+ }
59
+ }).catch((err) => {
60
+ if (err.name === 'SubmissionError') {
61
+ throw err
62
+ } else {
63
+ window.location = err.url
64
+ }
65
+ })
66
+ }
67
+ }
68
+
@@ -0,0 +1,34 @@
1
+ import React from 'react'
2
+ import {mapStateToProps, mapDispatchToProps} from '@jho406/breezy'
3
+ import {delInPage} from '@jho406/breezy/dist/action_creators'
4
+ import {connect} from 'react-redux'
5
+ import BaseScreen from 'components/BaseScreen'
6
+ import <%= plural_table_name.camelize %>Form from 'components/<%= plural_table_name.camelize %>Form'
7
+
8
+ class <%= plural_table_name.camelize %>Edit extends BaseScreen {
9
+ handleSubmit (body) {
10
+ this.props.delInPage({pathQuery: this.props.pathQuery, keypath: 'errors'})
11
+ return super.handleSubmit('/<%= plural_table_name %>/' + this.props.id, body, 'PATCH')
12
+ }
13
+
14
+ render () {
15
+ return (
16
+ <div>
17
+ <<%= plural_table_name.camelize %>Form
18
+ error={this.props.error}
19
+ onSubmit={this.handleSubmit}
20
+ initialValues={this.props.attributes_for_form}
21
+ />
22
+ <a onClick={ e => this.handleClick(this.props.meta.show_path)}>Show</a>
23
+ <a onClick={ e => this.handleClick(this.props.meta.index_path)}>Back</a>
24
+ </div>
25
+ )
26
+ }
27
+ }
28
+
29
+ export default connect(
30
+ mapStateToProps,
31
+ {...mapDispatchToProps, delInPage}
32
+ )(<%= plural_table_name.camelize %>Edit)
33
+
34
+