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,26 @@
1
+ import PropTypes from "prop-types"
2
+ import PropTypesExact from "prop-types-exact"
3
+ import React from "react"
4
+
5
+ export default class ApiMakerEventDestroyed extends React.Component {
6
+ static propTypes = PropTypesExact({
7
+ model: PropTypes.object.isRequired,
8
+ onDestroyed: PropTypes.func.isRequired
9
+ })
10
+
11
+ componentDidMount() {
12
+ this.connect()
13
+ }
14
+
15
+ componentWillUnmount() {
16
+ this.connectDestroyed.unsubscribe()
17
+ }
18
+
19
+ connect() {
20
+ this.connectDestroyed = this.props.model.connectDestroyed(this.props.onDestroyed)
21
+ }
22
+
23
+ render() {
24
+ return ""
25
+ }
26
+ }
@@ -0,0 +1,32 @@
1
+ import EventEmitter from "events"
2
+ import PropTypes from "prop-types"
3
+ import React from "react"
4
+
5
+ export default class ApiMakerEventEmitterListener extends React.Component {
6
+ static propTypes = {
7
+ events: PropTypes.instanceOf(EventEmitter).isRequired,
8
+ event: PropTypes.string.isRequired,
9
+ onCalled: PropTypes.func.isRequired
10
+ }
11
+
12
+ constructor(props) {
13
+ super(props)
14
+ this.onCalled = this.onCalled.bind(this)
15
+ }
16
+
17
+ componentDidMount() {
18
+ this.props.events.addListener(this.props.event, this.onCalled)
19
+ }
20
+
21
+ componentWillUnmount() {
22
+ this.props.events.removeListener(this.props.event, this.onCalled)
23
+ }
24
+
25
+ onCalled(...args) {
26
+ this.props.onCalled.apply(null, ...args)
27
+ }
28
+
29
+ render() {
30
+ return ""
31
+ }
32
+ }
@@ -0,0 +1,41 @@
1
+ import PropTypes from "prop-types"
2
+ import PropTypesExact from "prop-types-exact"
3
+ import React from "react"
4
+
5
+ export default class ApiMakerEventListener extends React.Component {
6
+ static callEvent(target, eventName, args = []) {
7
+ let event = document.createEvent("Event")
8
+ event.initEvent(eventName, false, true)
9
+ target.dispatchEvent(event, args)
10
+ }
11
+
12
+ static propTypes = PropTypesExact({
13
+ event: PropTypes.string.isRequired,
14
+ onCalled: PropTypes.func.isRequired,
15
+ target: PropTypes.object
16
+ })
17
+
18
+ constructor(props) {
19
+ super(props)
20
+ this.onCalled = this.onCalled.bind(this)
21
+
22
+ if (!this.props.target)
23
+ throw new Error("'target' was expected but not given")
24
+ }
25
+
26
+ componentDidMount() {
27
+ this.props.target.addEventListener(this.props.event, this.onCalled)
28
+ }
29
+
30
+ componentWillUnmount() {
31
+ this.props.target.removeEventListener(this.props.event, this.onCalled)
32
+ }
33
+
34
+ onCalled(...args) {
35
+ this.props.onCalled.apply(null, args)
36
+ }
37
+
38
+ render() {
39
+ return ""
40
+ }
41
+ }
@@ -0,0 +1,26 @@
1
+ import PropTypes from "prop-types"
2
+ import PropTypesExact from "prop-types-exact"
3
+ import React from "react"
4
+
5
+ export default class ApiMakerEventUpdated extends React.Component {
6
+ static propTypes = PropTypesExact({
7
+ model: PropTypes.object.isRequired,
8
+ onUpdated: PropTypes.func.isRequired
9
+ })
10
+
11
+ componentDidMount() {
12
+ this.connect()
13
+ }
14
+
15
+ componentWillUnmount() {
16
+ this.connectUpdated.unsubscribe()
17
+ }
18
+
19
+ connect() {
20
+ this.connectUpdated = this.props.model.connectUpdated(this.props.onUpdated)
21
+ }
22
+
23
+ render() {
24
+ return ""
25
+ }
26
+ }
@@ -0,0 +1,70 @@
1
+ export default class FormDataToObject {
2
+ static toObject(formData) {
3
+ var formDataToObject = new FormDataToObject(formData)
4
+ return formDataToObject.toObject()
5
+ }
6
+
7
+ constructor(formData) {
8
+ this.formData = formData
9
+ }
10
+
11
+ toObject() {
12
+ var result = {}
13
+
14
+ for(var entry of this.formData.entries()) {
15
+ var key = entry[0]
16
+ var value = entry[1]
17
+
18
+ this.treatInitial(key, value, result)
19
+ }
20
+
21
+ return result
22
+ }
23
+
24
+ treatInitial(key, value, result) {
25
+ var firstMatch = key.match(/^(.+?)(\[([\s\S]+$))/)
26
+
27
+ if (firstMatch) {
28
+ var inputName = firstMatch[1]
29
+ var rest = firstMatch[2]
30
+
31
+ if (inputName in result) {
32
+ var newResult = result[inputName]
33
+ } else if (rest == "[]") {
34
+ var newResult = []
35
+ result[inputName] = newResult
36
+ } else {
37
+ var newResult = {}
38
+ result[inputName] = newResult
39
+ }
40
+
41
+ this.treatSecond(value, rest, newResult)
42
+ } else {
43
+ result[key] = value
44
+ }
45
+ }
46
+
47
+ treatSecond(value, rest, result) {
48
+ var secondMatch = rest.match(/^\[(.*?)\]([\s\S]*)$/)
49
+ var key = secondMatch[1]
50
+ var newRest = secondMatch[2]
51
+
52
+ if (rest == "[]") {
53
+ result.push(value)
54
+ } else if (newRest == "") {
55
+ result[key] = value
56
+ } else {
57
+ if (key in result) {
58
+ var newResult = result[key]
59
+ } else if (newRest == "[]") {
60
+ var newResult = []
61
+ result[key] = newResult
62
+ } else {
63
+ var newResult = {}
64
+ result[key] = newResult
65
+ }
66
+
67
+ this.treatSecond(value, newRest, newResult)
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,39 @@
1
+ const inflection = require("inflection")
2
+
3
+ export default class ApiMakerIncluded {
4
+ constructor(response) {
5
+ this.response = response
6
+ this.loadIncludedModels()
7
+ }
8
+
9
+ loadIncludedModels() {
10
+ this.included = {}
11
+
12
+ for(var includedType in this.response.included) {
13
+ for(var includedId in this.response.included[includedType]) {
14
+ var includedData = this.response.included[includedType][includedId]
15
+ var modelClassName = inflection.dasherize(inflection.singularize(includedType))
16
+ var modelClass = require(`api-maker/models/${modelClassName}`).default
17
+ var model = new modelClass({data: includedData, isNewRecord: false})
18
+
19
+ if (!this.included[includedType])
20
+ this.included[includedType] = {}
21
+
22
+ this.included[includedType][includedId] = model
23
+ }
24
+ }
25
+
26
+ for(var modelType in this.included) {
27
+ for(var modelId in this.included[modelType]) {
28
+ this.included[modelType][modelId]._readIncludedRelationships(this)
29
+ }
30
+ }
31
+ }
32
+
33
+ getModel(type, id) {
34
+ if (!this.included[type] || !this.included[type][id])
35
+ throw new Error(`Could not find a ${type} by that ID: ${id}`)
36
+
37
+ return this.included[type][id]
38
+ }
39
+ }
@@ -0,0 +1,47 @@
1
+ import Dexie from "dexie"
2
+
3
+ export default class KeyValueStore {
4
+ static current() {
5
+ if (!window.currentKeyValueStore)
6
+ window.currentKeyValueStore = new KeyValueStore()
7
+
8
+ return window.currentKeyValueStore
9
+ }
10
+
11
+ static async get(key) {
12
+ return await KeyValueStore.current().get(key)
13
+ }
14
+
15
+ static async set(key, value) {
16
+ return await KeyValueStore.current().set(key, value)
17
+ }
18
+
19
+ constructor() {
20
+ this.database = new Dexie("KeyValueStoreDatabase")
21
+ this.database.version(1).stores({
22
+ keyValues: "++id, key, value"
23
+ })
24
+ }
25
+
26
+ async get(key) {
27
+ var row = await this.database.keyValues.where("key").equals(key).first()
28
+
29
+ if (row)
30
+ return row.value
31
+ }
32
+
33
+ async set(key, value) {
34
+ var row = await this.database.keyValues.where("key").equals(key).first()
35
+
36
+ if (row) {
37
+ await this.database.keyValues.update(row.id, {value: value})
38
+ } else {
39
+ await this.database.keyValues.add({
40
+ key: key,
41
+ value: value
42
+ })
43
+ }
44
+
45
+ return true
46
+ }
47
+ }
@@ -0,0 +1,23 @@
1
+ export default class ApiMakerLogger {
2
+ static current() {
3
+ if (!window.apiMakerLogger) {
4
+ window.apiMakerLogger = new ApiMakerLogger()
5
+ // window.apiMakerLogger.setDebug(true)
6
+ }
7
+
8
+ return window.apiMakerLogger
9
+ }
10
+
11
+ static log(message) {
12
+ ApiMakerLogger.current().log(message)
13
+ }
14
+
15
+ log(message) {
16
+ if (this.debug)
17
+ console.log("ApiMaker", message)
18
+ }
19
+
20
+ setDebug(value) {
21
+ this.debug = value
22
+ }
23
+ }
@@ -0,0 +1,21 @@
1
+ export default class ModelName {
2
+ constructor(data) {
3
+ this.data = data
4
+ }
5
+
6
+ human(args) {
7
+ if (!args)
8
+ args = {count: 1}
9
+
10
+ var countKey
11
+
12
+ if (args.count > 1 || args.count < 0) {
13
+ countKey = "other"
14
+ } else {
15
+ countKey = "one"
16
+ }
17
+
18
+ var key = `activerecord.models.${this.data.modelClassData.i18nKey}.${countKey}`
19
+ return I18n.t(key)
20
+ }
21
+ }
@@ -0,0 +1,110 @@
1
+ import BaseModel from "../base-model"
2
+ import Collection from "../collection"
3
+
4
+ export default class <%= model.name.gsub("::", "") %> extends BaseModel {
5
+ static modelClassData() {
6
+ return <%= {
7
+ attributes: attributes,
8
+ collectionKey: model.model_name.collection,
9
+ collectionName: resource.collection_name,
10
+ i18nKey: model.model_name.i18n_key,
11
+ name: resource.short_name,
12
+ pluralName: model.model_name.plural,
13
+ relationships: reflections_for_model_class_data,
14
+ paramKey: model.model_name.param_key,
15
+ path: "/api_maker/#{model.model_name.route_key}",
16
+ primaryKey: model.primary_key
17
+ }.to_json %>
18
+ }
19
+
20
+ <% reflections.each do |reflection| %>
21
+ <% if reflection.macro == :belongs_to %>
22
+ <%= ApiMaker::JsMethodNamerService.execute!(name: "load_#{reflection.name}") %>() {
23
+ let id = this.<%= ApiMaker::JsMethodNamerService.execute!(name: reflection.foreign_key) %>()
24
+ let modelClass = require(`api-maker/models/<%= reflection.class_name.underscore.dasherize %>`).default
25
+ return this._loadBelongsToReflection(<%= api_maker_json("reflectionName" => reflection.name, "model" => "{{this}}", "modelClass" => "{{modelClass}}") %>, <%= api_maker_json("ransack" => {"#{reflection.options[:primary_key] || reflection.klass.primary_key}_eq" => "{{id}}"}) %>)
26
+ }
27
+
28
+ <%= ApiMaker::JsMethodNamerService.execute!(name: reflection.name) %>() {
29
+ let modelClass = require(`api-maker/models/<%= reflection.class_name.underscore.dasherize %>`).default
30
+ return this._readBelongsToReflection(<%= api_maker_json("reflectionName" => reflection.name, "model" => "{{this}}", "modelClass" => "{{modelClass}}") %>)
31
+ }
32
+ <% elsif reflection.macro == :has_many %>
33
+ <%= ApiMaker::JsMethodNamerService.execute!(name: reflection.name) %>() {
34
+ let id = this.<%= ApiMaker::JsMethodNamerService.execute!(name: reflection.options[:primary_key] || reflection.active_record.primary_key) %>()
35
+ let modelClass = require(`api-maker/models/<%= reflection.class_name.underscore.dasherize %>`).default
36
+ return new Collection(<%= api_maker_json(reflection_has_many_parameters(reflection)) %>, <%= api_maker_json(reflection_has_many_parameters_query(reflection)) %>)
37
+ }
38
+ <% elsif reflection.macro == :has_one && reflection.options[:through] %>
39
+ <%= ApiMaker::JsMethodNamerService.execute!(name: "load_#{reflection.name}") %>() {
40
+ let id = this.<%= reflection.active_record.primary_key %>()
41
+ let modelClass = require(`api-maker/models/<%= reflection.class_name.underscore.dasherize %>`).default
42
+ return this._loadHasOneReflection(<%= api_maker_json("reflectionName" => reflection.name, "model" => "{{this}}", "modelClass" => "{{modelClass}}") %>, <%= api_maker_json("params": {"through" => {"model" => model.model_name, "id" => "{{id}}", "reflection" => reflection.name}}) %>)
43
+ }
44
+
45
+ <%= ApiMaker::JsMethodNamerService.execute!(name: reflection.name) %>() {
46
+ let modelClass = require(`api-maker/models/<%= reflection.class_name.underscore.dasherize %>`).default
47
+ return this._readHasOneReflection(<%= api_maker_json("reflectionName" => reflection.name, "model" => "{{this}}", "modelClass" => "{{modelClass}}") %>)
48
+ }
49
+ <% elsif reflection.macro == :has_one %>
50
+ <%= ApiMaker::JsMethodNamerService.execute!(name: "load_#{reflection.name}") %>() {
51
+ let id = this.<%= reflection.active_record.primary_key %>()
52
+ let modelClass = require(`api-maker/models/<%= reflection.class_name.underscore.dasherize %>`).default
53
+ return this._loadHasOneReflection(<%= api_maker_json("reflectionName" => reflection.name, "model" => "{{this}}", "modelClass" => "{{modelClass}}") %>, <%= api_maker_json("ransack" => {"#{reflection.foreign_key}_eq" => "{{id}}"}) %>)
54
+ }
55
+
56
+ <%= ApiMaker::JsMethodNamerService.execute!(name: reflection.name) %>() {
57
+ let modelClass = require(`api-maker/models/<%= reflection.class_name.underscore.dasherize %>`).default
58
+ return this._readHasOneReflection(<%= api_maker_json("reflectionName" => reflection.name, "model" => "{{this}}", "modelClass" => "{{modelClass}}") %>)
59
+ }
60
+ <% end %>
61
+ <% end %>
62
+
63
+ <% attributes.each do |attribute_data| %>
64
+ <% methodName = ApiMaker::JsMethodNamerService.execute!(name: attribute_data.fetch(:name)) %>
65
+ <%= methodName %>() {
66
+ // <%= attribute_data.fetch(:type) %>
67
+ <% if attribute_data.fetch(:type) == :datetime || attribute_data.fetch(:type) == :date %>
68
+ return this._getAttributeDateTime("<%= attribute_data.fetch(:name) %>")
69
+ <% elsif attribute_data.fetch(:type) == :money %>
70
+ return this._getAttributeMoney("<%= attribute_data.fetch(:name) %>")
71
+ <% else %>
72
+ return this._getAttribute("<%= attribute_data.fetch(:name) %>")
73
+ <% end %>
74
+ }
75
+
76
+ <%= ApiMaker::JsMethodNamerService.execute!(name: "has_#{attribute_data.fetch(:name)}") %>() {
77
+ let value = this.<%= methodName %>()
78
+ return this._isPresent(value)
79
+ }
80
+ <% end %>
81
+
82
+ <% collection_commands.each do |collection_command, data| %>
83
+ static <%= ApiMaker::JsMethodNamerService.execute!(name: collection_command) %>(args, commandArgs = {}) {
84
+ return this._callCollectionCommand(
85
+ {
86
+ args: args,
87
+ command: "<%= collection_command %>",
88
+ collectionName: this.modelClassData().collectionName,
89
+ type: "collection"
90
+ },
91
+ commandArgs
92
+ )
93
+ }
94
+ <% end %>
95
+
96
+ <% member_commands.each do |member_command, data| %>
97
+ <%= ApiMaker::JsMethodNamerService.execute!(name: member_command) %>(args, commandArgs = {}) {
98
+ return this._callMemberCommand(
99
+ {
100
+ args: args,
101
+ command: "<%= member_command %>",
102
+ primaryKey: this._primaryKey(),
103
+ collectionName: this.modelClassData().collectionName,
104
+ type: "member"
105
+ },
106
+ commandArgs
107
+ )
108
+ }
109
+ <% end %>
110
+ }