breezy 0.3.0 → 0.3.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 (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
+