api_maker 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }