lanes 0.1.9.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (304) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/Rakefile +1 -1
  4. data/client/lanes/Boot.cjsx +3 -3
  5. data/client/lanes/Config.coffee +38 -3
  6. data/client/lanes/access/Extension.coffee +1 -1
  7. data/client/lanes/access/LoginDialog.cjsx +57 -47
  8. data/client/lanes/access/Roles.coffee +2 -2
  9. data/client/lanes/access/User.coffee +4 -3
  10. data/client/lanes/access/screens/user-management/UserManagement.cjsx +12 -11
  11. data/client/lanes/components/calendar/Calendar.cjsx +16 -0
  12. data/client/lanes/components/calendar/index.js +3 -0
  13. data/client/lanes/components/calendar/styles.scss +3 -0
  14. data/client/lanes/components/grid/Body.cjsx +86 -0
  15. data/client/lanes/components/grid/CellStyles.coffee +20 -0
  16. data/client/lanes/components/grid/EditingMixin.cjsx +84 -24
  17. data/client/lanes/components/grid/Editor.cjsx +45 -0
  18. data/client/lanes/components/grid/Grid.cjsx +62 -104
  19. data/client/lanes/components/grid/Header.cjsx +35 -0
  20. data/client/lanes/components/grid/PopOverMixin.cjsx +19 -9
  21. data/client/lanes/components/grid/PopoverEditor.cjsx +7 -1
  22. data/client/lanes/components/grid/RowEditor.cjsx +1 -1
  23. data/client/lanes/components/grid/Selections.cjsx +39 -0
  24. data/client/lanes/components/grid/Toolbar.cjsx +24 -5
  25. data/client/lanes/components/grid/editors.scss +22 -50
  26. data/client/lanes/components/grid/index.js +0 -1
  27. data/client/lanes/components/grid/row-editor.scss +68 -0
  28. data/client/lanes/components/grid/styles.scss +79 -3
  29. data/client/lanes/components/modal/Modal.cjsx +64 -24
  30. data/client/lanes/components/modal/styles.scss +12 -0
  31. data/client/lanes/components/record-finder/Clause.cjsx +11 -4
  32. data/client/lanes/components/record-finder/Dialog.cjsx +23 -24
  33. data/client/lanes/components/record-finder/RecordFinder.cjsx +45 -14
  34. data/client/lanes/components/record-finder/styles.scss +9 -6
  35. data/client/lanes/components/select-field/SelectField.cjsx +108 -53
  36. data/client/lanes/components/select-field/styles.scss +19 -0
  37. data/client/lanes/components/shared/ControlLabel.cjsx +45 -0
  38. data/client/lanes/components/shared/DateTime.cjsx +48 -0
  39. data/client/lanes/components/shared/DisplayValue.cjsx +16 -0
  40. data/client/lanes/components/shared/FieldMixin.cjsx +54 -23
  41. data/client/lanes/components/shared/FieldSet.cjsx +12 -35
  42. data/client/lanes/components/shared/FieldWrapper.cjsx +13 -0
  43. data/client/lanes/components/shared/FormGroup.cjsx +37 -0
  44. data/client/lanes/components/shared/Icon.cjsx +20 -0
  45. data/client/lanes/components/shared/ImageSaver.cjsx +33 -0
  46. data/client/lanes/components/shared/Input.cjsx +19 -0
  47. data/client/lanes/components/shared/InputFieldMixin.cjsx +48 -0
  48. data/client/lanes/components/shared/JobProgress.cjsx +27 -0
  49. data/client/lanes/components/shared/NetworkActivityOverlay.cjsx +58 -0
  50. data/client/lanes/components/shared/NumberInput.cjsx +29 -0
  51. data/client/lanes/components/shared/ResizeSensor.cjsx +11 -0
  52. data/client/lanes/components/shared/ScreenWrapper.cjsx +13 -0
  53. data/client/lanes/components/shared/Throbber.cjsx +3 -0
  54. data/client/lanes/components/shared/ToggleField.cjsx +33 -0
  55. data/client/lanes/components/shared/Tooltip.cjsx +2 -2
  56. data/client/lanes/components/shared/fields.scss +75 -13
  57. data/client/lanes/components/shared/fieldset.scss +3 -5
  58. data/client/lanes/components/shared/image-saver.scss +38 -0
  59. data/client/lanes/components/shared/index.js +2 -0
  60. data/client/lanes/{styles/plugins → components/shared}/overlay.scss +17 -4
  61. data/client/lanes/components/shared/resize-sensor.scss +30 -0
  62. data/client/lanes/components/shared/styles.scss +13 -0
  63. data/client/lanes/components/shared/throbber.scss +53 -0
  64. data/client/lanes/components/toolbar/RemoteChangeSets.cjsx +21 -48
  65. data/client/lanes/components/toolbar/SaveButton.cjsx +24 -0
  66. data/client/lanes/components/toolbar/Toolbar.cjsx +24 -37
  67. data/client/lanes/components/toolbar/changes-notification.scss +10 -6
  68. data/client/lanes/components/toolbar/styles.scss +29 -9
  69. data/client/lanes/extension/Base.coffee +4 -5
  70. data/client/lanes/index.js +0 -1
  71. data/client/lanes/index.scss.erb +10 -1
  72. data/client/lanes/lib/HotReload.coffee +13 -15
  73. data/client/lanes/lib/MakeBaseClass.coffee +6 -1
  74. data/client/lanes/lib/development.coffee +2 -0
  75. data/client/lanes/lib/dom-polyfills.coffee +5 -0
  76. data/client/lanes/lib/dom.coffee +38 -9
  77. data/client/lanes/lib/format.coffee +11 -0
  78. data/client/lanes/lib/index.js.erb +2 -0
  79. data/client/lanes/lib/production.coffee +6 -0
  80. data/client/lanes/lib/utilFunctions.coffee +50 -15
  81. data/client/lanes/models/AssociationMap.coffee +122 -46
  82. data/client/lanes/models/AssociationProxy.coffee +147 -0
  83. data/client/lanes/models/Base.coffee +97 -85
  84. data/client/lanes/models/ChangeMonitor.coffee +7 -3
  85. data/client/lanes/models/ChangeSet.coffee +2 -2
  86. data/client/lanes/models/Collection.coffee +49 -6
  87. data/client/lanes/models/JobStatus.coffee +32 -0
  88. data/client/lanes/models/PubSub.coffee +7 -5
  89. data/client/lanes/models/Query.coffee +115 -34
  90. data/client/lanes/models/ServerCache.coffee +67 -52
  91. data/client/lanes/models/State.coffee +97 -0
  92. data/client/lanes/models/Sync.coffee +18 -13
  93. data/client/lanes/models/SystemSettings.coffee +0 -0
  94. data/client/lanes/models/index.js +1 -0
  95. data/client/lanes/models/mixins/FileSupport.coffee +60 -0
  96. data/client/lanes/models/mixins/HasCodeField.coffee +13 -6
  97. data/client/lanes/models/query/ArrayResult.coffee +188 -0
  98. data/client/lanes/models/query/CollectionResult.coffee +71 -0
  99. data/client/lanes/models/query/Result.coffee +9 -0
  100. data/client/lanes/react/Component.coffee +7 -3
  101. data/client/lanes/react/PubSub.coffee +7 -7
  102. data/client/lanes/react/Root.cjsx +1 -4
  103. data/client/lanes/react/Screen.coffee +1 -0
  104. data/client/lanes/react/TypeValidators.coffee +3 -3
  105. data/client/lanes/react/Viewport.coffee +41 -7
  106. data/client/lanes/react/index.js +0 -1
  107. data/client/lanes/react/mixins/Access.coffee +4 -1
  108. data/client/lanes/react/mixins/Data.coffee +40 -25
  109. data/client/lanes/react/mixins/FieldErrors.coffee +27 -0
  110. data/client/lanes/react/mixins/RelayEditingState.coffee +4 -0
  111. data/client/lanes/react/mixins/Screen.coffee +14 -0
  112. data/client/lanes/react/mixins/Viewport.coffee +9 -3
  113. data/client/lanes/screens/ChangeListener.coffee +3 -3
  114. data/client/lanes/screens/Commands.coffee +14 -7
  115. data/client/lanes/screens/CommonComponents.cjsx +20 -0
  116. data/client/lanes/screens/Definitions.coffee +64 -20
  117. data/client/lanes/screens/SystemSettings.cjsx +56 -0
  118. data/client/lanes/screens/UserPreferences.cjsx +38 -0
  119. data/client/lanes/screens/index.js +3 -0
  120. data/client/lanes/screens/styles.scss +2 -1
  121. data/client/lanes/styles/fonts.scss +1 -0
  122. data/client/lanes/styles/global.scss +2 -1
  123. data/client/lanes/styles/global/flexbox.scss +16 -0
  124. data/client/lanes/styles/global/styles.scss +1 -0
  125. data/client/lanes/styles/mixins/_dropdown.scss +21 -0
  126. data/client/lanes/styles/mixins/_flexbox.scss +394 -0
  127. data/client/lanes/styles/mixins/all.scss +2 -0
  128. data/client/lanes/styles/variables.scss +28 -0
  129. data/client/lanes/testing/BeforeEach.coffee +15 -14
  130. data/client/lanes/testing/Helpers.coffee +14 -5
  131. data/client/lanes/testing/TestObjects.coffee +10 -2
  132. data/client/lanes/testing/index.js +1 -0
  133. data/client/lanes/testing/jasmine-react.js +125 -0
  134. data/client/lanes/vendor/base.js +56049 -74987
  135. data/client/lanes/vendor/calendar.js +17301 -0
  136. data/client/lanes/vendor/calendar.scss +303 -0
  137. data/client/lanes/vendor/commons.js +14990 -15847
  138. data/client/lanes/vendor/development.js +4912 -1952
  139. data/client/lanes/vendor/grid.js +14246 -5551
  140. data/client/lanes/vendor/grid.scss +876 -335
  141. data/client/lanes/vendor/index.js +1 -0
  142. data/client/lanes/vendor/message-bus-ajax.js +44 -0
  143. data/client/lanes/vendor/message-bus.js +414 -0
  144. data/client/lanes/vendor/rw-widgets.eot +0 -0
  145. data/client/lanes/vendor/rw-widgets.svg +18 -0
  146. data/client/lanes/vendor/rw-widgets.ttf +0 -0
  147. data/client/lanes/vendor/rw-widgets.woff +0 -0
  148. data/client/lanes/vendor/toggle.js +345 -0
  149. data/client/lanes/vendor/toggle.scss +138 -0
  150. data/client/lanes/vendor/widgets.js +21245 -6839
  151. data/client/lanes/vendor/widgets.scss +83 -67
  152. data/client/lanes/workspace/Layout.cjsx +18 -8
  153. data/client/lanes/workspace/Modal.cjsx +47 -0
  154. data/client/lanes/workspace/Navbar.cjsx +16 -2
  155. data/client/lanes/workspace/ScreenView.cjsx +10 -3
  156. data/client/lanes/workspace/ScreensMenu.cjsx +23 -7
  157. data/client/lanes/workspace/Tabs.cjsx +55 -0
  158. data/client/lanes/workspace/UIState.coffee +7 -8
  159. data/client/lanes/workspace/index.js +2 -1
  160. data/client/lanes/workspace/mixin.coffee +11 -0
  161. data/client/lanes/workspace/mixins/UIState.coffee +8 -0
  162. data/client/lanes/workspace/mixins/index.js +3 -0
  163. data/client/lanes/workspace/styles.scss +2 -1
  164. data/client/lanes/workspace/styles/header.scss +23 -1
  165. data/client/lanes/workspace/styles/layout.scss +26 -24
  166. data/client/lanes/workspace/styles/screens.scss +0 -4
  167. data/client/lanes/workspace/styles/tabs.scss +3 -10
  168. data/config/routes.rb +10 -4
  169. data/config/screens.rb +25 -0
  170. data/db/migrate/01_create_system_settings.rb +10 -0
  171. data/docs/todo-example-part-1.md +18 -20
  172. data/lanes.gemspec +15 -6
  173. data/lib/lanes.rb +4 -2
  174. data/lib/lanes/access/config/routes.rb +5 -3
  175. data/lib/lanes/access/config/screens.rb +1 -0
  176. data/lib/lanes/access/db/migrate/20140615031600_create_lanes_users.rb +1 -1
  177. data/lib/lanes/access/user.rb +1 -1
  178. data/lib/lanes/api.rb +2 -1
  179. data/lib/lanes/api/controller.rb +32 -71
  180. data/lib/lanes/api/default_routes.rb +10 -8
  181. data/lib/lanes/api/formatted_reply.rb +53 -0
  182. data/lib/lanes/api/handlers/file.rb +26 -0
  183. data/lib/lanes/api/helper_methods.rb +29 -5
  184. data/lib/lanes/api/javascript_processor.rb +36 -17
  185. data/lib/lanes/api/pub_sub.rb +6 -9
  186. data/lib/lanes/api/request_wrapper.rb +1 -2
  187. data/lib/lanes/api/root.rb +11 -43
  188. data/lib/lanes/api/routing.rb +63 -0
  189. data/lib/lanes/api/sprockets_extension.rb +15 -7
  190. data/lib/lanes/api/updates.rb +1 -2
  191. data/lib/lanes/command.rb +0 -1
  192. data/lib/lanes/command/app.rb +6 -5
  193. data/lib/lanes/command/console.rb +1 -0
  194. data/lib/lanes/command/generate.rb +3 -0
  195. data/lib/lanes/command/generate_migration.rb +33 -0
  196. data/lib/lanes/command/generate_model.rb +4 -26
  197. data/lib/lanes/command/migration_support.rb +29 -0
  198. data/lib/lanes/command/update_model.rb +14 -5
  199. data/lib/lanes/concerns/all.rb +2 -0
  200. data/lib/lanes/concerns/api_path.rb +4 -2
  201. data/lib/lanes/concerns/association_extensions.rb +1 -1
  202. data/lib/lanes/concerns/attr_accessor_with_default.rb +3 -1
  203. data/lib/lanes/concerns/code_identifier.rb +1 -1
  204. data/lib/lanes/concerns/image_uploader.rb +42 -0
  205. data/lib/lanes/concerns/pub_sub.rb +0 -1
  206. data/lib/lanes/concerns/queries.rb +2 -2
  207. data/lib/lanes/concerns/set_attribute_data.rb +4 -13
  208. data/lib/lanes/concerns/sorting_expressions.rb +34 -0
  209. data/lib/lanes/configuration.rb +48 -9
  210. data/lib/lanes/extension.rb +16 -7
  211. data/lib/lanes/extension/definition.rb +8 -2
  212. data/lib/lanes/job.rb +78 -0
  213. data/lib/lanes/job/failure_logger.rb +33 -0
  214. data/lib/lanes/model.rb +4 -0
  215. data/lib/lanes/rake_tasks.rb +6 -0
  216. data/lib/lanes/redis.rb +13 -0
  217. data/lib/lanes/screen.rb +34 -18
  218. data/lib/lanes/system_settings.rb +66 -0
  219. data/lib/lanes/version.rb +1 -1
  220. data/lib/lanes/workspace/extension.rb +1 -1
  221. data/npm-build/base.js +10 -3
  222. data/npm-build/calendar.js +6 -0
  223. data/npm-build/development.js +4 -5
  224. data/npm-build/grid.js +3 -5
  225. data/npm-build/package.json +40 -29
  226. data/npm-build/react-toggle.js +5 -0
  227. data/npm-build/react-widgets.js +6 -0
  228. data/npm-build/update-dayz +14 -0
  229. data/npm-build/webpack.config.js +5 -2
  230. data/spec/command-reference-files/initial/Gemfile +1 -1
  231. data/spec/command-reference-files/initial/config/routes.rb +2 -0
  232. data/spec/command-reference-files/initial/lib/appy-app.rb +4 -0
  233. data/spec/command-reference-files/initial/lib/appy-app/extension.rb +2 -0
  234. data/spec/command-reference-files/initial/spec/server/{spec_helpers.rb → spec_helper.rb} +0 -0
  235. data/spec/command-reference-files/model/config/routes.rb +2 -0
  236. data/spec/command-reference-files/model/spec/server/test_test_spec.rb +1 -1
  237. data/spec/command-reference-files/screen/client/appy-app/screens/ready-set-go/ReadySetGo.cjsx +6 -4
  238. data/spec/command-reference-files/screen/client/appy-app/screens/ready-set-go/index.scss +4 -3
  239. data/spec/command-reference-files/screen/config/screens.rb +4 -2
  240. data/spec/command-reference-files/screen/spec/appy-app/screens/ready-set-go/ReadySetGoSpec.coffee +1 -1
  241. data/spec/fixtures/system_settings.yml +1 -0
  242. data/spec/lanes/components/grid/GridSpec.coffee +56 -31
  243. data/spec/lanes/components/grid/RowEditorSpec.coffee +96 -0
  244. data/spec/lanes/components/select-field/SelectFieldSpec.coffee +99 -0
  245. data/spec/lanes/components/shared/NetworkActivityOverlaySpec.coffee +34 -0
  246. data/spec/lanes/models/AssociationMapSpec.coffee +36 -2
  247. data/spec/lanes/models/AssociationProxySpec.coffee +77 -0
  248. data/spec/lanes/models/BaseSpec.coffee +37 -4
  249. data/spec/lanes/models/CollectionSpec.coffee +11 -17
  250. data/spec/lanes/models/PubSubSpec.coffee +1 -1
  251. data/spec/lanes/models/ServerCacheSpec.coffee +65 -0
  252. data/spec/server/api/coffeescript_processor_spec.rb +1 -1
  253. data/spec/server/concerns/pub_sub_spec.rb +9 -10
  254. data/spec/server/concerns/sorting_expressions_spec.rb +34 -0
  255. data/spec/server/configuration_spec.rb +3 -3
  256. data/spec/server/job_spec.rb +54 -0
  257. data/spec/server/spec_helper.rb +0 -5
  258. data/spec/server/system_settings_spec.rb +23 -0
  259. data/templates/client/screens/Screen.cjsx +6 -4
  260. data/templates/client/screens/styles.scss +4 -3
  261. data/templates/config/routes.rb +2 -0
  262. data/templates/config/screen.rb +4 -2
  263. data/templates/lib/namespace.rb +4 -0
  264. data/templates/lib/namespace/extension.rb +2 -0
  265. data/templates/spec/client/Screen.coffee +1 -1
  266. data/templates/spec/server/model_spec.rb +1 -1
  267. data/templates/spec/server/{spec_helpers.rb → spec_helper.rb} +0 -0
  268. data/views/lanes_root_view.erb +70 -0
  269. data/views/specs.erb +2 -2
  270. metadata +207 -68
  271. data/client/images/lanes/dataTables/Sorting icons.psd +0 -0
  272. data/client/images/lanes/dataTables/back_disabled.png +0 -0
  273. data/client/images/lanes/dataTables/back_enabled.png +0 -0
  274. data/client/images/lanes/dataTables/back_enabled_hover.png +0 -0
  275. data/client/images/lanes/dataTables/favicon.ico +0 -0
  276. data/client/images/lanes/dataTables/forward_disabled.png +0 -0
  277. data/client/images/lanes/dataTables/forward_enabled.png +0 -0
  278. data/client/images/lanes/dataTables/forward_enabled_hover.png +0 -0
  279. data/client/images/lanes/dataTables/loading-background.png +0 -0
  280. data/client/images/lanes/dataTables/sort_asc.png +0 -0
  281. data/client/images/lanes/dataTables/sort_asc_disabled.png +0 -0
  282. data/client/images/lanes/dataTables/sort_both.png +0 -0
  283. data/client/images/lanes/dataTables/sort_desc.png +0 -0
  284. data/client/images/lanes/dataTables/sort_desc_disabled.png +0 -0
  285. data/client/lanes/components/shared/Resize.cjsx +0 -152
  286. data/client/lanes/components/shared/TextArea.cjsx +0 -19
  287. data/client/lanes/components/shared/TextField.cjsx +0 -25
  288. data/client/lanes/models/Bootstrap.coffee +0 -5
  289. data/client/lanes/models/QueryResults.coffee +0 -93
  290. data/client/lanes/react/FormBindings.coffee +0 -103
  291. data/client/lanes/react/Router.cjsx +0 -18
  292. data/client/lanes/styles/dataTables.scss +0 -4
  293. data/client/lanes/styles/plugins/all.scss +0 -2
  294. data/client/lanes/styles/plugins/resize-sensor.scss +0 -24
  295. data/client/lanes/vendor/jquery-2.js +0 -9190
  296. data/client/lanes/vendor/jquery.tap.js +0 -401
  297. data/client/lanes/vendor/magicsuggest.js +0 -1565
  298. data/client/lanes/vendor/message-bus.coffee +0 -264
  299. data/client/lanes/workspace/ActiveScreenSwitcher.cjsx +0 -38
  300. data/client/lanes/workspace/styles/toolbar.scss +0 -4
  301. data/lib/lanes/api/eco.js +0 -516
  302. data/lib/lanes/api/sprockets_compressor.rb +0 -39
  303. data/spec/command-reference-files/model/lib/appy-app.rb +0 -11
  304. data/views/index.erb +0 -19
@@ -6,20 +6,24 @@
6
6
  class Lanes.Models.ChangeMonitor
7
7
  constructor: (model) ->
8
8
  model.on('change', this.onChange, this)
9
+ @_unsaved = {}
9
10
 
10
11
  onChange: (record, options) ->
11
12
  attrs = record.changedAttributes()
12
13
  return if _.isEmpty(attrs)
14
+ if attrs[record.idAttribute] and record.associations
15
+ record.associations.onIdChange(record)
13
16
  if attrs.isDirty == false
14
- delete @_unsaved
17
+ @_unsaved = {}
15
18
  else
16
- @_unsaved ||= {}
17
19
  this.recordChanges(record, _.keys(attrs))
18
20
 
21
+ reset: ->
22
+ @_unsaved = {}
19
23
 
20
24
  recordChanges: (record, names) ->
21
25
  # console.log "Change: #{names}"
22
- #console.log record.getAttributes(props:true, session: true)
26
+ # console.log record.getAttributes(props:true, session: true)
23
27
  for name in names
24
28
  if name != record.idAttribute && record._definition[name] && !record._definition[name].session
25
29
  record.isDirty = true
@@ -11,7 +11,7 @@ class Lanes.Models.ChangeSet extends Lanes.Models.Base
11
11
 
12
12
  derived:
13
13
  record: { deps: ['collection'], fn: -> @collection.parent }
14
- record_name: { deps: ['record'], fn: -> _.field2title @record.api_path() }
14
+ record_name: { deps: ['record'], fn: -> _.field2title @record.modelTypeIdentifier() }
15
15
  fields: { deps: ['update'], fn: -> _.keys(@update) }
16
16
  displayed_fields: { deps:['fields'], fn: -> _.without(@fields, 'updated_by_id', 'updated_at') }
17
17
  displayed_changes:
@@ -23,7 +23,7 @@ class Lanes.Models.ChangeSet extends Lanes.Models.Base
23
23
  value: ->
24
24
  set = {}
25
25
  for field, change of @update
26
- set[field] = change[1]
26
+ set[field] = if _.isArray(change) then _.last(change) else change
27
27
  set
28
28
 
29
29
  class ChangeSetCollection extends Lanes.Models.BasicCollection
@@ -1,5 +1,5 @@
1
1
  CommonMethods = {
2
- findIndexWhere: (attrs)->
2
+ findIndexWhere: (attrs) ->
3
3
  found = -1
4
4
  for index, model of @models
5
5
  found = parseInt(index)
@@ -10,6 +10,14 @@ CommonMethods = {
10
10
  break if found != -1
11
11
  found
12
12
 
13
+ pick: (keys...) ->
14
+ _.map(@models, _.partialRight(_.pick, keys...))
15
+
16
+ sum: ->
17
+ args = _.toArray(arguments)
18
+ args.unshift(this.models)
19
+ return _.sum.apply(_, args)
20
+
13
21
  # convenience method to create a new subCollection
14
22
  subcollection: (options) ->
15
23
  new Lanes.Models.SubCollection(this, options)
@@ -26,6 +34,20 @@ CommonMethods = {
26
34
  return this
27
35
  )
28
36
 
37
+ clone: ->
38
+ new @constructor( @invoke( 'clone' ), @options )
39
+
40
+ serialize: (options = {depth: 1}) ->
41
+ depth = options.depth
42
+ depth += 1
43
+ options = _.extend({}, options, {depth})
44
+ this.map (model) ->
45
+ if model.serialize
46
+ model.serialize(options)
47
+ else
48
+ out = _.extend({}, model)
49
+ delete out.collection
50
+ out
29
51
  }
30
52
 
31
53
  class ModelsCollection
@@ -54,6 +76,17 @@ class ModelsCollection
54
76
  else
55
77
  this.sync('read', this, options)
56
78
 
79
+ getOrFetch: (id, options) ->
80
+ @get(id) || @fetchId(id, options)
81
+
82
+ # Fetch a single model with the given id
83
+ fetchId: (id, options = {}) ->
84
+ return if id == @requestInProgress?.query?[@model::idAttribute]
85
+ this.sync('read', this, _.extend({
86
+ limit: 1, query: {"#{@model::idAttribute}": id}
87
+ }, options))
88
+
89
+ # Load the collection unless it's already loaded
57
90
  ensureLoaded: (options = {}) ->
58
91
  if options.force || (!@_isLoaded && !this.length )
59
92
  this.fetch(options)
@@ -80,7 +113,10 @@ class ModelsCollection
80
113
  )
81
114
  this.remove(models)
82
115
  else
83
- update = if options.reset then 'reset' else 'set'
116
+ if options.reset
117
+ update = 'reset'
118
+ else
119
+ update = 'set'
84
120
  this[update](data, options)
85
121
  if this._loaded_callbacks
86
122
  cb(this) for cb in this._loaded_callbacks
@@ -146,6 +182,7 @@ Lanes.Models.SubCollection = Lanes.lib.MakeBaseClass( Lanes.Vendor.Ampersand.Sub
146
182
 
147
183
  Lanes.Models.BasicCollection = Lanes.lib.MakeBaseClass(
148
184
  Lanes.Vendor.Ampersand.Collection.extend(Lanes.Vendor.Ampersand.USCollection), BasicCollection
185
+
149
186
  )
150
187
 
151
188
  Lanes.Models.Collection = Lanes.lib.MakeBaseClass( Lanes.Vendor.Ampersand.RestCollection, ModelsCollection )
@@ -155,17 +192,23 @@ Lanes.Models.Collection = Lanes.lib.MakeBaseClass( Lanes.Vendor.Ampersand.RestCo
155
192
  ## Override a few methods on the standard collection to ensure that
156
193
  # models are fetched correctly and have the fk set when they're created
157
194
  class Lanes.Models.AssociationCollection extends Lanes.Models.Collection
158
- constructor: (models, options) ->
159
- @model = options.model
160
- @associationFilter = options.filter
195
+ constructor: (models, @options) ->
196
+ @model = @options.model
197
+ @associationFilter = @options.filter
161
198
  super
162
199
 
163
200
  _prepareModel: (attrs, options = {}) ->
201
+ if @associationFilter
202
+ _.extend(attrs, @associationFilter)
164
203
  model = super
165
- model.set(@associationFilter)
204
+ if @options.inverse
205
+ parent = this.parent.clone()
206
+ parent[@options.inverse.without].reset() if @options.inverse.without
207
+ model.set(@options.inverse.name, parent, options)
166
208
  model
167
209
 
168
210
  fetch: (options) ->
169
211
  options.query ||= {}
170
212
  _.extend(options.query, @associationFilter)
213
+ _.extend(options, @options)
171
214
  super(options)
@@ -0,0 +1,32 @@
1
+ class Lanes.Models.JobStatus extends Lanes.Models.Base
2
+
3
+ props:
4
+ id: 'string'
5
+ job_name: 'string'
6
+ progress: 'number'
7
+ attempt: 'integer'
8
+ state: 'string'
9
+ recorded_at: 'date'
10
+ queued_at: 'date'
11
+ errors: 'any'
12
+ data: 'object'
13
+
14
+ events:
15
+ 'remote-update': 'onUpdate'
16
+
17
+ derived:
18
+ isSubmitted:
19
+ deps: ['id'], fn: -> !@isNew()
20
+ isExecuting:
21
+ deps: ['state'], fn: ->
22
+ _.contains(['unqueued', 'queued', 'started'], @state)
23
+ isActive:
24
+ deps: ['isSubmitted', 'isExecuting'], fn: ->
25
+ !(@isSubmitted or @isExecuting)
26
+ stepsCompleted:
27
+ deps: ['data'], fn: ->
28
+ _.map(_.keys(@data?.output), _.titleize)
29
+
30
+ onUpdate: ->
31
+ @trigger('update', @)
32
+ @parent?.trigger('update', @)
@@ -11,7 +11,8 @@ class ModelType extends Lanes.Models.State
11
11
  subscribe: (config) ->
12
12
  model = config.models[0]
13
13
  channel = "/#{_.result(model, 'api_path')}/#{model.id}"
14
- Lanes.Vendor.MessageBus.subscribe(channel, (changes) ->
14
+ Lanes.log.info "Subscribe to: #{channel}"
15
+ Lanes.Models.PubSub.mb.subscribe(channel, (changes) ->
15
16
  for model in config.models
16
17
  model.addChangeSet(changes)
17
18
  )
@@ -29,7 +30,8 @@ class ModelType extends Lanes.Models.State
29
30
  if ( config = @records[model.id] )
30
31
  _.remove(config.models, (m) -> m == model)
31
32
  if _.isEmpty(config.models)
32
- Lanes.Vendor.MessageBus.unsubscribe( config.channel )
33
+ Lanes.log.info "Unsubscribe from: #{config.channel}"
34
+ Lanes.Models.PubSub.mb.unsubscribe( config.channel )
33
35
  delete @records[model.id]
34
36
 
35
37
 
@@ -65,9 +67,9 @@ Lanes.Models.PubSub = {
65
67
  @types = new ModelTypesCollection
66
68
 
67
69
  initialize: ->
68
- Lanes.Vendor.MessageBus.start()
69
- Lanes.Vendor.MessageBus.callbackInterval = 2000
70
- Lanes.Vendor.MessageBus.subscribe("/file-change", (changes) ->
70
+ @mb = MessageBus.noConflict()
71
+ @mb.start()
72
+ @mb.subscribe("/file-change", (changes) ->
71
73
  Lanes.lib.HotReload.initiate(changes)
72
74
  )
73
75
  }
@@ -1,17 +1,30 @@
1
- class Field extends Lanes.Models.Base
1
+ ##= require_self
2
+ ##= require ./query/Result
3
+ ##= require ./query/ArrayResult
4
+ ##= require ./query/CollectionResult
2
5
 
6
+ class Field extends Lanes.Models.Base
7
+ pubsub: false
3
8
  constructor: (attributes) ->
4
9
  super( _.defaults( attributes, {
5
10
  title: _.titleize(_.humanize(attributes.id))
6
11
  }))
7
12
 
8
13
  session:
9
- id: 'string'
10
- title: 'string'
11
- visible: type: 'boolean', default: true
12
- selected: 'boolean'
13
- flex: type: 'number', default: 1
14
- textAlign: type: 'string', default: 'left'
14
+ id: type: 'string'
15
+ title: type: 'string'
16
+ selected: type: 'boolean'
17
+ visible: type: 'boolean', default: true
18
+ query: type: 'boolean', default: true
19
+ editable: type: 'boolean', default: true
20
+ sortable: type: 'boolean', default: true
21
+ format: type: 'function'
22
+ flex: type: 'number', default: 1
23
+ fixedWidth: type: 'number'
24
+ textAlign: type: 'string', default: 'left'
25
+ sortBy: type: 'any'
26
+ component: type: 'function'
27
+ onColumnClick: type: 'function'
15
28
 
16
29
  derived:
17
30
  default_value: deps: ['id'], fn: ->
@@ -20,11 +33,17 @@ class Field extends Lanes.Models.Base
20
33
  else ''
21
34
  model_field:
22
35
  deps: ['id'], fn: ->
23
- this.collection.query.modelClass::_definition[@id]
36
+ this.collection.query.model::_definition[@id]
24
37
  type:
25
38
  deps: ['model_field'], fn: ->
26
39
  type = @model_field?.type || 'string'
27
40
  if type == "code" then "string" else type
41
+ fetchIndex:
42
+ fn: ->
43
+ fetchIndex = 0
44
+ for field, index in this.collection.models
45
+ return fetchIndex if this == field
46
+ fetchIndex += 1 if field.query
28
47
 
29
48
  validValue: (value) ->
30
49
  if this.type == 'n'
@@ -49,6 +68,7 @@ class AvailableFields extends Lanes.Models.Collection
49
68
 
50
69
 
51
70
  class Operator extends Lanes.Models.Base
71
+ pubsub: false
52
72
 
53
73
  session:
54
74
  id: 'string'
@@ -70,10 +90,10 @@ class Operators extends Lanes.Models.Collection
70
90
  constructor: ->
71
91
  super
72
92
  this.add([
73
- { id: 'like', name: 'Starts With', types:['string'] }
93
+ { id: 'like', name: 'Starts With', types: Lanes.Models.Query.LIKE_QUERY_TYPES }
74
94
  { id: 'eq', name: 'Equals' }
75
- { id: 'lt', name: 'Less Than', types:['integer', 'bigdec', 'number'] }
76
- { id: 'gt', name: 'More Than', types:['integer', 'bigdec', 'number'] }
95
+ { id: 'lt', name: 'Less Than', types: Lanes.Models.Query.LESS_THAN_QUERY_TYPES }
96
+ { id: 'gt', name: 'More Than', types: Lanes.Models.Query.LESS_THAN_QUERY_TYPES }
77
97
  ])
78
98
  this.on('change', (changing) ->
79
99
  return unless changing.selected
@@ -90,6 +110,7 @@ class Operators extends Lanes.Models.Collection
90
110
 
91
111
 
92
112
  class Clause extends Lanes.Models.Base
113
+ pubsub: false
93
114
 
94
115
  session:
95
116
  value : { type: 'string', default: '' }
@@ -120,9 +141,12 @@ class Clause extends Lanes.Models.Base
120
141
  @operators.field = @fields.first()
121
142
  @fields.on('change:selected', this.setField, this)
122
143
  @operators.on('change:selected', this.setOperator, this)
123
-
124
- @fields.findWhere(visible: true)?.selected = true
125
- @operators.at(0).selected = true
144
+ field = @fields.findWhere(visible: true)
145
+ if field
146
+ field.selected = true
147
+ @setField(field)
148
+ operator = @operators.find (o) -> o.valid
149
+ operator?.selected = true
126
150
 
127
151
  setFromView: (type, val) ->
128
152
  if type == "fields" || type == "operators"
@@ -161,67 +185,124 @@ class Clauses extends Lanes.Models.Collection
161
185
  @query = options.query
162
186
  @fields = options.query.fields
163
187
 
188
+ # needs to inherit from Base so network events will be listened to
164
189
  class Lanes.Models.Query extends Lanes.Models.Base
190
+ pubsub: false
191
+
192
+ @LIKE_QUERY_TYPES: ['string', 'code']
193
+ @LESS_THAN_QUERY_TYPES: ['integer', 'bigdec', 'number']
194
+ @GREATER_THAN_QUERY_TYPES: ['integer', 'bigdec', 'number']
195
+
196
+ @mergedSyncOptions: (args...) ->
197
+ _.merge {}, args..., (a, b) ->
198
+ return a.concat(b) if _.isArray(a)
165
199
 
166
200
  session:
201
+ src: 'any'
167
202
  fields: 'collection'
168
203
  clauses: 'collection'
169
- modelClass: 'function'
170
204
  initialField: 'state'
171
205
  idIndex: 'number'
172
206
  initialFieldIndex: 'number'
173
- pageSize: 'number'
207
+ pageSize: { type: 'number', default: 100 }
208
+ syncOptions: 'any'
209
+ autoRetrieve: {type:'boolean', default: false, required: true}
210
+ title: { type: 'string', default: 'Find Record' }
211
+ sortField: 'object'
212
+ defaultSort: 'any'
213
+ changeCount: {type: 'integer', default: 0}
214
+ sortAscending: ['boolean', true, true]
174
215
 
175
216
  derived:
217
+ isCollection:
218
+ deps: ['src'], fn: -> Lanes.u.isCollection(@src)
219
+
220
+ model:
221
+ deps: ['src'], fn: ->
222
+ return null unless @src
223
+ if @isCollection then @src.model else @src
224
+
225
+ idAttribute:
226
+ deps: ['model'], fn: -> @model::idAttribute
227
+
176
228
  results:
177
- fn: -> new Lanes.Models.QueryResults(this, pageSize: @pageSize)
178
- collection_class:
179
- deps:['modelClass'], fn: -> @modelClass?.Collection
180
- url:
181
- deps:['modelClass'], fn: -> @modelClass?::urlRoot()
229
+ deps: ['src'], fn: ->
230
+ if @isCollection
231
+ new Lanes.Models.Query.CollectionResult(this)
232
+ else
233
+ new Lanes.Models.Query.ArrayResult(this, pageSize: @pageSize)
234
+
235
+ events:
236
+ 'change:sortField': '_updateSort'
237
+ 'change:sortAscending': '_updateSort'
182
238
 
183
239
  constructor: (options = {}) ->
184
240
  super
185
- idName = @modelClass::idAttribute
186
- @loadAssociations = options.loadAssociations
187
241
  @fields = new AvailableFields([], query: this)
188
242
  for col, i in options.fields
189
243
  rec = if _.isObject(col) then col else { id: col }
190
244
  @fields.add rec
191
- @idIndex = i if rec.id == idName
245
+ @idIndex = i if rec.id == @idAttribute
192
246
  unless @idIndex?
193
247
  @idIndex = @fields.length
194
- @fields.add(id: idName)
248
+ @fields.add(id: @idAttribute)
195
249
  @clauses = new Clauses([], query: this )
196
- this.listenTo(@clauses, 'change remove reset', ->
250
+ this.listenTo(@clauses, 'change remove reset', =>
197
251
  @results.reset()
198
- this.trigger('change', arguments...)
252
+ @results.ensureLoaded() if this.autoRetrieve
253
+ )
254
+ this.on('change:src change:results', ->
255
+ this.trigger('load')
256
+ )
257
+ this.on('load', ->
258
+ @changeCount += 1
199
259
  )
260
+
200
261
  if @initialFieldIndex
201
262
  @initialField = this.fields.at(@initialFieldIndex)
263
+
202
264
  @initialField ||= this.fields.findWhere(id: "code") ||
203
265
  this.fields.findWhere(id: "visibleId") ||
204
266
  this.fields.first()
205
- this.addNewClause()
267
+
268
+ @reset()
206
269
  this
207
270
 
208
- execute: ->
209
- this.trigger('execute')
271
+ reset: ->
272
+ unless @defaultSort is false
273
+ sort = @defaultSort or @fields.findWhere(visible: true).id
274
+ @setSortField( @fields.findWhere(id: sort), sortAscending: true, silent: true )
275
+
276
+ @clauses.reset([
277
+ {query: this, available_fields: @fields, field: @initialField}
278
+ ])
279
+
280
+ setSortField: (field, options = {silent: false}) ->
281
+ @set({
282
+ sortAscending: options.sortAscending || (if @sortField is field then !@sortAscending else true)
283
+ sortField: field
284
+ }, options)
285
+
286
+ ensureLoaded: -> @results.ensureLoaded()
210
287
 
211
288
  isValid: ->
212
289
  ! @clauses.findWhere( isValid: false )
213
290
 
214
- loadModelWithAssociations: (model) ->
215
- model.withAssociations(@loadAssociations...)
291
+ loadModel: (model, options = {}) ->
292
+ _.extend(options, _.result(this, 'syncOptions'))
293
+ model.withAssociations(options.include || [], options)
216
294
 
217
295
  loadSingle: (code, options = {}) ->
218
296
  options.query = {}
219
297
  options.query[ @initialField.id ] = code
220
- options.include = @loadAssociations
221
- @modelClass.fetch(options)
298
+ _.extend(options, _.result(this, 'syncOptions'))
299
+ @src.fetch(options)
222
300
 
223
301
  defaultField: ->
224
302
  @fields.findWhere( field: @initialField )
225
303
 
226
304
  addNewClause: ->
227
305
  @clauses.add(query: this, available_fields: @fields, field: @initialField)
306
+
307
+ _updateSort: _.debounce ->
308
+ @results._updateSort()