api_maker 0.0.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 (103) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +476 -0
  4. data/Rakefile +27 -0
  5. data/app/channels/api_maker/subscriptions_channel.rb +80 -0
  6. data/app/controllers/api_maker/base_controller.rb +32 -0
  7. data/app/controllers/api_maker/commands_controller.rb +26 -0
  8. data/app/controllers/api_maker/devise_controller.rb +60 -0
  9. data/app/controllers/api_maker/session_statuses_controller.rb +33 -0
  10. data/app/services/api_maker/application_service.rb +7 -0
  11. data/app/services/api_maker/collection_command_service.rb +24 -0
  12. data/app/services/api_maker/command_response.rb +67 -0
  13. data/app/services/api_maker/command_service.rb +31 -0
  14. data/app/services/api_maker/create_command.rb +62 -0
  15. data/app/services/api_maker/create_command_service.rb +18 -0
  16. data/app/services/api_maker/destroy_command.rb +39 -0
  17. data/app/services/api_maker/destroy_command_service.rb +22 -0
  18. data/app/services/api_maker/generate_react_native_api_service.rb +61 -0
  19. data/app/services/api_maker/index_command.rb +96 -0
  20. data/app/services/api_maker/index_command_service.rb +22 -0
  21. data/app/services/api_maker/js_method_namer_service.rb +11 -0
  22. data/app/services/api_maker/member_command_service.rb +25 -0
  23. data/app/services/api_maker/model_content_generator_service.rb +108 -0
  24. data/app/services/api_maker/models_finder_service.rb +22 -0
  25. data/app/services/api_maker/models_generator_service.rb +104 -0
  26. data/app/services/api_maker/update_command.rb +43 -0
  27. data/app/services/api_maker/update_command_service.rb +21 -0
  28. data/app/services/api_maker/valid_command.rb +35 -0
  29. data/app/services/api_maker/valid_command_service.rb +21 -0
  30. data/app/views/api_maker/_data.html.erb +15 -0
  31. data/config/rails_best_practices.yml +55 -0
  32. data/config/routes.rb +7 -0
  33. data/lib/api_maker.rb +36 -0
  34. data/lib/api_maker/ability.rb +39 -0
  35. data/lib/api_maker/ability_loader.rb +21 -0
  36. data/lib/api_maker/action_controller_base_extensions.rb +5 -0
  37. data/lib/api_maker/base_command.rb +81 -0
  38. data/lib/api_maker/base_resource.rb +78 -0
  39. data/lib/api_maker/collection_serializer.rb +69 -0
  40. data/lib/api_maker/command_spec_helper.rb +57 -0
  41. data/lib/api_maker/configuration.rb +34 -0
  42. data/lib/api_maker/engine.rb +5 -0
  43. data/lib/api_maker/individual_command.rb +37 -0
  44. data/lib/api_maker/javascript/api.js +92 -0
  45. data/lib/api_maker/javascript/base-model.js +543 -0
  46. data/lib/api_maker/javascript/bootstrap/attribute-row.jsx +16 -0
  47. data/lib/api_maker/javascript/bootstrap/attribute-rows.jsx +47 -0
  48. data/lib/api_maker/javascript/bootstrap/card.jsx +79 -0
  49. data/lib/api_maker/javascript/bootstrap/checkbox.jsx +127 -0
  50. data/lib/api_maker/javascript/bootstrap/checkboxes.jsx +105 -0
  51. data/lib/api_maker/javascript/bootstrap/live-table.jsx +168 -0
  52. data/lib/api_maker/javascript/bootstrap/money-input.jsx +136 -0
  53. data/lib/api_maker/javascript/bootstrap/radio-buttons.jsx +80 -0
  54. data/lib/api_maker/javascript/bootstrap/select.jsx +168 -0
  55. data/lib/api_maker/javascript/bootstrap/string-input.jsx +203 -0
  56. data/lib/api_maker/javascript/cable-connection-pool.js +169 -0
  57. data/lib/api_maker/javascript/cable-subscription-pool.js +111 -0
  58. data/lib/api_maker/javascript/cable-subscription.js +33 -0
  59. data/lib/api_maker/javascript/collection.js +186 -0
  60. data/lib/api_maker/javascript/commands-pool.js +123 -0
  61. data/lib/api_maker/javascript/custom-error.js +14 -0
  62. data/lib/api_maker/javascript/deserializer.js +35 -0
  63. data/lib/api_maker/javascript/devise.js.erb +113 -0
  64. data/lib/api_maker/javascript/error-logger.js +119 -0
  65. data/lib/api_maker/javascript/event-connection.jsx +24 -0
  66. data/lib/api_maker/javascript/event-created.jsx +26 -0
  67. data/lib/api_maker/javascript/event-destroyed.jsx +26 -0
  68. data/lib/api_maker/javascript/event-emitter-listener.jsx +32 -0
  69. data/lib/api_maker/javascript/event-listener.jsx +41 -0
  70. data/lib/api_maker/javascript/event-updated.jsx +26 -0
  71. data/lib/api_maker/javascript/form-data-to-object.js +70 -0
  72. data/lib/api_maker/javascript/included.js +39 -0
  73. data/lib/api_maker/javascript/key-value-store.js +47 -0
  74. data/lib/api_maker/javascript/logger.js +23 -0
  75. data/lib/api_maker/javascript/model-name.js +21 -0
  76. data/lib/api_maker/javascript/model-template.js.erb +110 -0
  77. data/lib/api_maker/javascript/models-response-reader.js +43 -0
  78. data/lib/api_maker/javascript/paginate.jsx +128 -0
  79. data/lib/api_maker/javascript/params.js +68 -0
  80. data/lib/api_maker/javascript/resource-route.jsx +75 -0
  81. data/lib/api_maker/javascript/resource-routes.jsx +36 -0
  82. data/lib/api_maker/javascript/result.js +25 -0
  83. data/lib/api_maker/javascript/session-status-updater.js +113 -0
  84. data/lib/api_maker/javascript/sort-link.jsx +88 -0
  85. data/lib/api_maker/javascript/updated-attribute.jsx +60 -0
  86. data/lib/api_maker/loader.rb +14 -0
  87. data/lib/api_maker/memory_storage.rb +65 -0
  88. data/lib/api_maker/model_extensions.rb +96 -0
  89. data/lib/api_maker/permitted_params_argument.rb +12 -0
  90. data/lib/api_maker/preloader.rb +91 -0
  91. data/lib/api_maker/preloader_belongs_to.rb +58 -0
  92. data/lib/api_maker/preloader_has_many.rb +69 -0
  93. data/lib/api_maker/preloader_has_one.rb +70 -0
  94. data/lib/api_maker/preloader_through.rb +101 -0
  95. data/lib/api_maker/railtie.rb +14 -0
  96. data/lib/api_maker/relationship_includer.rb +42 -0
  97. data/lib/api_maker/resource_routing.rb +8 -0
  98. data/lib/api_maker/result_parser.rb +50 -0
  99. data/lib/api_maker/serializer.rb +86 -0
  100. data/lib/api_maker/spec_helper.rb +100 -0
  101. data/lib/api_maker/version.rb +3 -0
  102. data/lib/tasks/api_maker_tasks.rake +5 -0
  103. metadata +581 -0
@@ -0,0 +1,136 @@
1
+ import Api from "api-maker/api"
2
+ import formatNumber from "format-number"
3
+ import PropTypes from "prop-types"
4
+ import React from "react"
5
+
6
+ const inflection = require("inflection")
7
+
8
+ export default class BootstrapMoneyInput extends React.Component {
9
+ constructor(props) {
10
+ super(props)
11
+ this.state = {}
12
+
13
+ let formatOptions = {
14
+ decimal: I18n.t("number.currency.format.separator"),
15
+ integerSeparator: I18n.t("number.currency.format.delimiter")
16
+ }
17
+ this.formatter = formatNumber(formatOptions)
18
+ }
19
+
20
+ render() {
21
+ return (
22
+ <div className="component-api-maker-bootstrap-money-input">
23
+ <input defaultValue={this.inputDefaultCentsValue()} id={this.inputCentsId()} name={this.inputCentsName()} ref="input" type="hidden" />
24
+
25
+ <div className="input-group">
26
+ <input
27
+ className={this.props.className}
28
+ defaultValue={this.inputDefaultValue()}
29
+ id={this.inputId()}
30
+ onBlur={() => this.setAmount()}
31
+ onChange={() => this.setCents()}
32
+ onKeyUp={() => this.setCents()}
33
+ placeholder={this.props.placeholder}
34
+ ref="whole"
35
+ type="text"
36
+ />
37
+ <select className="component-bootstrap-money-input" defaultValue={this.inputCurrencyValue()} id={this.inputCurrencyId()} name={this.inputCurrencyName()} onChange={() => { this.onCurrencyChanged() }} ref="currency">
38
+ <option></option>
39
+ {this.props.currenciesCollection.map(option => (
40
+ <option key={`select-option-${option[1]}`} value={option[1]}>
41
+ {this.props.small && option[1]}
42
+ {!this.props.small && option[0]}
43
+ </option>
44
+ ))}
45
+ </select>
46
+ </div>
47
+ </div>
48
+ )
49
+ }
50
+
51
+ inputCurrencyId() {
52
+ return `${this.inputId()}_currency`
53
+ }
54
+
55
+ inputCurrencyName() {
56
+ if (this.props.currencyName)
57
+ return this.props.currencyName
58
+
59
+ return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}_currency]`
60
+ }
61
+
62
+ inputCurrencyValue() {
63
+ let value = this.props.model[this.props.attribute]()
64
+
65
+ if (value) {
66
+ return MoneyFormatter.currencyFromMoney(value).code
67
+ } else {
68
+ return "DKK"
69
+ }
70
+ }
71
+
72
+ inputDefaultValue() {
73
+ let value = this.props.model[this.props.attribute]()
74
+
75
+ if (value) {
76
+ return MoneyFormatter.fromMoney({amount: value.amount, currency: this.inputCurrencyValue()}, {decimals: 2, excludeCurrency: true}).toString()
77
+ } else {
78
+ return ""
79
+ }
80
+ }
81
+
82
+ inputDefaultCentsValue() {
83
+ let value = this.props.model[this.props.attribute]()
84
+
85
+ if (this.refs.input)
86
+ return this.refs.input.value
87
+
88
+ if (value)
89
+ return MoneyFormatter.amountFromMoney(value)
90
+ }
91
+
92
+ inputCentsId() {
93
+ return `${this.inputId()}_cents`
94
+ }
95
+
96
+ inputCentsName() {
97
+ if (this.props.name)
98
+ return this.props.name
99
+
100
+ return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}_cents]`
101
+ }
102
+
103
+ inputId() {
104
+ return `${this.props.model.modelClassData().paramKey}_${inflection.underscore(this.props.attribute)}`
105
+ }
106
+
107
+ onCurrencyChanged() {
108
+ if (this.props.onChange)
109
+ this.props.onChange()
110
+ }
111
+
112
+ setAmount() {
113
+ if (!this.refs.input.value && this.refs.input.value == "") {
114
+ this.refs.whole.value = ""
115
+ } else {
116
+ let cents = parseFloat(this.refs.input.value)
117
+ let formatted = MoneyFormatter.fromMoney({amount: cents, currency: this.inputCurrencyValue()}, {decimals: 2, excludeCurrency: true}).toString()
118
+
119
+ this.refs.whole.value = formatted
120
+ }
121
+ }
122
+
123
+ setCents() {
124
+ let whole = MoneyFormatter.stringToFloat(this.refs.whole.value)
125
+ let cents = parseInt(whole * 100)
126
+ let oldCents = parseInt(this.refs.input.value)
127
+ this.refs.input.value = cents
128
+
129
+ if (this.props.onChange && oldCents != cents)
130
+ this.props.onChange()
131
+ }
132
+ }
133
+
134
+ BootstrapMoneyInput.propTypes = {
135
+ currenciesCollection: PropTypes.array.isRequired
136
+ }
@@ -0,0 +1,80 @@
1
+ import PropTypesExact from "prop-types-exact"
2
+ import React from "react"
3
+
4
+ const inflection = require("inflection")
5
+
6
+ export default class BootstrapRadioButtons extends React.Component {
7
+ static propTypes = PropTypesExact({
8
+ attribute: PropTypes.string,
9
+ collection: PropTypes.array.isRequired,
10
+ defaultValue: PropTypes.oneOfType([
11
+ PropTypes.number,
12
+ PropTypes.string
13
+ ]),
14
+ id: PropTypes.string,
15
+ name: PropTypes.string,
16
+ model: PropTypes.object,
17
+ wrapperClassName: PropTypes.string
18
+ })
19
+
20
+ render() {
21
+ return (
22
+ <div className={this.wrapperClassName()}>
23
+ <input name={this.inputName()} type="hidden" value="" />
24
+ {this.props.collection.map(option => this.optionElement(option))}
25
+ </div>
26
+ )
27
+ }
28
+
29
+ inputDefaultValue() {
30
+ if (this.props.defaultValue) {
31
+ return this.props.defaultValue
32
+ } else if (this.props.model) {
33
+ if (!this.props.model[this.props.attribute])
34
+ throw new Error(`No such attribute: ${this.props.attribute}`)
35
+
36
+ return this.props.model[this.props.attribute]()
37
+ }
38
+ }
39
+
40
+ inputName() {
41
+ if (this.props.name) {
42
+ return this.props.name
43
+ } else if (this.props.model) {
44
+ return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}]`
45
+ }
46
+ }
47
+
48
+ generatedId() {
49
+ return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
50
+ }
51
+
52
+ optionElement(option) {
53
+ let id = this.generatedId()
54
+
55
+ return (
56
+ <div key={`option-${option[1]}`}>
57
+ <input
58
+ data-option-value={option[1]}
59
+ defaultChecked={option[1] == this.inputDefaultValue()}
60
+ id={id}
61
+ name={this.inputName()}
62
+ type="radio"
63
+ value={option[1]} />
64
+
65
+ <label className="ml-1" htmlFor={id}>
66
+ {option[0]}
67
+ </label>
68
+ </div>
69
+ )
70
+ }
71
+
72
+ wrapperClassName() {
73
+ var classNames = ["component-bootstrap-radio-buttons"]
74
+
75
+ if (this.props.wrapperClassName)
76
+ classNames.push(this.props.wrapperClassName)
77
+
78
+ return classNames.join(" ")
79
+ }
80
+ }
@@ -0,0 +1,168 @@
1
+ import PropTypes from "prop-types"
2
+ import PropTypesExact from "prop-types-exact"
3
+ import React from "react"
4
+
5
+ const inflection = require("inflection")
6
+
7
+ export default class BootstrapSelect extends React.Component {
8
+ static propTypes = PropTypesExact({
9
+ attribute: PropTypes.string,
10
+ children: PropTypes.node,
11
+ className: PropTypes.string,
12
+ "data-controller": PropTypes.string,
13
+ defaultValue: PropTypes.oneOfType([PropTypes.array, PropTypes.number, PropTypes.string]),
14
+ description: PropTypes.node,
15
+ disabled: PropTypes.bool,
16
+ id: PropTypes.string,
17
+ includeBlank: PropTypes.bool,
18
+ hideSearch: PropTypes.bool,
19
+ hint: PropTypes.node,
20
+ hintBottom: PropTypes.node,
21
+ label: PropTypes.node,
22
+ labelContainerClassName: PropTypes.string,
23
+ model: PropTypes.object,
24
+ multiple: PropTypes.bool,
25
+ name: PropTypes.string,
26
+ placeholder: PropTypes.string,
27
+ onChange: PropTypes.func,
28
+ options: PropTypes.array,
29
+ select2: PropTypes.bool,
30
+ wrapperClassName: PropTypes.string,
31
+ })
32
+
33
+ componentDidMount() {
34
+ if (this.props.select2 && this.props.onChange)
35
+ $(this.refs.select).on("change", this.props.onChange)
36
+
37
+ // Set default value to nothing when multiple
38
+ if (this.props.select2 && this.props.multiple && !this.inputDefaultValue())
39
+ $(this.refs.select).val("")
40
+ }
41
+
42
+ componentWillUnmount() {
43
+ if (this.props.select2 && this.props.onChange)
44
+ $(this.refs.select).off("change", this.props.onChange)
45
+ }
46
+
47
+ render() {
48
+ return (
49
+ <div className={this.wrapperClassName()}>
50
+ {this.label() &&
51
+ <div className={this.props.labelContainerClassName ? this.props.labelContainerClassName : null}>
52
+ <label className={this.labelClassName()} htmlFor={this.inputId()}>
53
+ {this.label()}
54
+ </label>
55
+ </div>
56
+ }
57
+ {this.props.description &&
58
+ <div className="mb-4">
59
+ {this.props.description}
60
+ </div>
61
+ }
62
+ {this.props.hint &&
63
+ <span className="form-text text-muted font-smoothing font-xs">
64
+ {this.props.hint}
65
+ </span>
66
+ }
67
+ <select
68
+ data-controller={this.dataController()}
69
+ data-hide-search={this.props.hideSearch}
70
+ data-placeholder={this.props.placeholder}
71
+ defaultValue={this.inputDefaultValue()}
72
+ className={`form-control ${this.props.className}`}
73
+ disabled={this.props.disabled}
74
+ id={this.inputId()}
75
+ multiple={this.props.multiple}
76
+ name={this.inputName()}
77
+ onChange={this.props.onChange}
78
+ ref="select"
79
+ >
80
+ {this.includeBlank() &&
81
+ <option />
82
+ }
83
+ {this.props.options && this.props.options.map(option => (
84
+ <option key={`select-option-${option[1]}`} value={option[1]}>{option[0]}</option>
85
+ ))}
86
+ {this.props.children}
87
+ </select>
88
+ {this.props.hintBottom &&
89
+ <span className="form-text text-muted font-smoothing font-xs">
90
+ {this.props.hintBottom}
91
+ </span>
92
+ }
93
+ </div>
94
+ )
95
+ }
96
+
97
+ dataController() {
98
+ if ("data-controller" in this.props) {
99
+ return this.props["data-controller"]
100
+ } else if (this.props.select2) {
101
+ return "select2--default"
102
+ }
103
+ }
104
+
105
+ includeBlank() {
106
+ if (this.props.includeBlank || (this.props.placeholder && !this.props.multiple)) {
107
+ return true
108
+ } else {
109
+ return false
110
+ }
111
+ }
112
+
113
+ inputDefaultValue() {
114
+ if ("defaultValue" in this.props) {
115
+ return this.props.defaultValue
116
+ } else if (this.props.selected) {
117
+ return this.props.selected
118
+ } else if (this.props.model) {
119
+ if (!this.props.model[this.props.attribute])
120
+ throw new Error(`No attribute by that name: ${this.props.attribute}`)
121
+
122
+ return this.props.model[this.props.attribute]()
123
+ }
124
+ }
125
+
126
+ inputId() {
127
+ if ("id" in this.props) {
128
+ return this.props.id
129
+ } else if (this.props.model) {
130
+ return `${this.props.model.modelClassData().paramKey}_${inflection.underscore(this.props.attribute)}`
131
+ }
132
+ }
133
+
134
+ inputName() {
135
+ if ("name" in this.props) {
136
+ return this.props.name
137
+ } else if (this.props.model) {
138
+ return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}]`
139
+ }
140
+ }
141
+
142
+ label() {
143
+ if ("label" in this.props) {
144
+ return this.props.label
145
+ } else if (this.props.model) {
146
+ let attributeMethodName = inflection.camelize(this.props.attribute.replace(/_id$/, ""), true)
147
+ return this.props.model.modelClass().humanAttributeName(attributeMethodName)
148
+ }
149
+ }
150
+
151
+ labelClassName() {
152
+ let classNames = ["form-group-label"]
153
+
154
+ if (this.props.labelClassName)
155
+ classNames.push(this.props.labelClassName)
156
+
157
+ return classNames.join(" ")
158
+ }
159
+
160
+ wrapperClassName() {
161
+ let classNames = ["form-group", "component-bootstrap-select"]
162
+
163
+ if (this.props.wrapperClassName)
164
+ classNames.push(this.props.wrapperClassName)
165
+
166
+ return classNames.join(" ")
167
+ }
168
+ }
@@ -0,0 +1,203 @@
1
+ import MoneyInput from "./money-input"
2
+ import PropTypes from "prop-types"
3
+ import PropTypesExact from "prop-types-exact"
4
+ import React from "react"
5
+
6
+ const inflection = require("inflection")
7
+
8
+ export default class BootstrapStringInput extends React.Component {
9
+ static propTypes = PropTypesExact({
10
+ append: PropTypes.node,
11
+ attribute: PropTypes.string,
12
+ autoComplete: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
13
+ className: PropTypes.string,
14
+ currenciesCollection: PropTypes.array,
15
+ currencyName: PropTypes.string,
16
+ "data-controller": PropTypes.string,
17
+ defaultValue: PropTypes.node,
18
+ disabled: PropTypes.bool,
19
+ hint: PropTypes.node,
20
+ hintBottom: PropTypes.node,
21
+ id: PropTypes.string,
22
+ label: PropTypes.node,
23
+ labelClassName: PropTypes.string,
24
+ maxLength: PropTypes.number,
25
+ model: PropTypes.object,
26
+ name: PropTypes.string,
27
+ onChange: PropTypes.func,
28
+ onKeyUp: PropTypes.func,
29
+ placeholder: PropTypes.node,
30
+ rows: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
31
+ step: PropTypes.number,
32
+ small: PropTypes.bool,
33
+ type: PropTypes.string,
34
+ wrapperClassName: PropTypes.string
35
+ })
36
+
37
+ render() {
38
+ return (
39
+ <div className={this.wrapperClassName()} ref="wrapper">
40
+ {this.label() &&
41
+ <label className={this.labelClassName()} htmlFor={this.inputId()}>
42
+ {this.label()}
43
+ </label>
44
+ }
45
+ {this.props.hint &&
46
+ <span className="form-text text-muted font-smoothing font-xs">
47
+ {this.props.hint}
48
+ </span>
49
+ }
50
+ {this.inputType() == "textarea" &&
51
+ <textarea
52
+ className={this.inputClassName()}
53
+ data-controller={this.props["data-controller"]}
54
+ defaultValue={this.inputDefaultValue()}
55
+ id={this.inputId()}
56
+ maxLength={this.props.maxLength}
57
+ name={this.inputName()}
58
+ onChange={this.props.onChange}
59
+ onKeyUp={this.props.onKeyUp}
60
+ placeholder={this.props.placeholder}
61
+ ref="input"
62
+ rows={this.props.rows}
63
+ />
64
+ }
65
+ {this.inputType() == "money" &&
66
+ <MoneyInput
67
+ attribute={this.props.attribute}
68
+ currenciesCollection={this.props.currenciesCollection}
69
+ currencyName={this.props.currencyName}
70
+ model={this.props.model}
71
+ name={this.props.name}
72
+ className={this.inputClassName()}
73
+ onChange={this.props.onChange}
74
+ placeholder={this.props.placeholder}
75
+ small={this.props.small}
76
+ ref="money"
77
+ />
78
+ }
79
+ {this.inputType() != "textarea" && this.inputType() != "money" &&
80
+ <div className="input-group">
81
+ {this.props.prepend &&
82
+ <div className="input-group-prepend">
83
+ <span className="input-group-text">
84
+ {this.props.prepend}
85
+ </span>
86
+ </div>
87
+ }
88
+ <input
89
+ autoComplete={this.props.autoComplete}
90
+ className={this.inputClassName()}
91
+ data-controller={this.props["data-controller"]}
92
+ defaultValue={this.inputDefaultValue()}
93
+ disabled={this.props.disabled}
94
+ id={this.inputId()}
95
+ name={this.inputName()}
96
+ onChange={this.props.onChange}
97
+ onKeyUp={this.props.onKeyUp}
98
+ placeholder={this.props.placeholder}
99
+ ref="input"
100
+ step={this.props.step}
101
+ type={this.inputType()}
102
+ />
103
+ {this.props.append &&
104
+ <div className="input-group-append">
105
+ <span className="input-group-text">
106
+ {this.props.append}
107
+ </span>
108
+ </div>
109
+ }
110
+ </div>
111
+ }
112
+ {this.props.hintBottom &&
113
+ <span className="form-text text-muted font-smoothing font-xs">
114
+ {this.props.hintBottom}
115
+ </span>
116
+ }
117
+ </div>
118
+ )
119
+ }
120
+
121
+ inputClassName() {
122
+ var classNames = ["form-control"]
123
+
124
+ if (this.props.className)
125
+ classNames.push(this.props.className)
126
+
127
+ return classNames.join(" ")
128
+ }
129
+
130
+ inputDefaultValue() {
131
+ if ("defaultValue" in this.props) {
132
+ return this.formatValue(this.props.defaultValue)
133
+ } else if (this.props.model) {
134
+ if (!this.props.model[this.props.attribute])
135
+ throw new Error(`No such attribute: ${this.props.model.modelClassData().name}#${this.props.attribute}`)
136
+
137
+ return this.formatValue(this.props.model[this.props.attribute]())
138
+ }
139
+ }
140
+
141
+ formatValue(value) {
142
+ // We need to use a certain format for datetime-local
143
+ if (this.inputType() == "datetime-local" && value instanceof Date) {
144
+ return I18n.strftime(value, "%Y-%m-%dT%H:%M:%S")
145
+ } else if (this.inputType() == "date" && value instanceof Date) {
146
+ return I18n.strftime(value, "%Y-%m-%d")
147
+ }
148
+
149
+ return value
150
+ }
151
+
152
+ inputId() {
153
+ if ("id" in this.props) {
154
+ return this.props.id
155
+ } else if (this.props.model) {
156
+ return `${this.props.model.modelClassData().paramKey}_${inflection.underscore(this.props.attribute)}`
157
+ }
158
+ }
159
+
160
+ inputName() {
161
+ if ("name" in this.props) {
162
+ return this.props.name
163
+ } else if (this.props.model) {
164
+ return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}]`
165
+ }
166
+ }
167
+
168
+ inputType() {
169
+ if (this.props.type) {
170
+ return this.props.type
171
+ } else {
172
+ return "text"
173
+ }
174
+ }
175
+
176
+ label() {
177
+ if (this.props.label === false) {
178
+ return null
179
+ } else if (this.props.label) {
180
+ return this.props.label
181
+ } else if (this.props.model) {
182
+ return this.props.model.modelClass().humanAttributeName(this.props.attribute)
183
+ }
184
+ }
185
+
186
+ labelClassName() {
187
+ var classNames = []
188
+
189
+ if (this.props.labelClassName)
190
+ classNames.push(this.props.labelClassName)
191
+
192
+ return classNames.join(" ")
193
+ }
194
+
195
+ wrapperClassName() {
196
+ var classNames = ["form-group", "component-bootstrap-string-input"]
197
+
198
+ if (this.props.wrapperClassName)
199
+ classNames.push(this.props.wrapperClassName)
200
+
201
+ return classNames.join(" ")
202
+ }
203
+ }