lanes 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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.jshintrc +3 -0
- data/Gemfile +9 -0
- data/Guardfile +15 -0
- data/LICENSE-MIT.txt +21 -0
- data/README.md +15 -0
- data/Rakefile +74 -0
- data/bin/lanes +5 -0
- data/client/images/ajax-loader.gif +0 -0
- data/client/images/dataTables/Sorting icons.psd +0 -0
- data/client/images/dataTables/back_disabled.png +0 -0
- data/client/images/dataTables/back_enabled.png +0 -0
- data/client/images/dataTables/back_enabled_hover.png +0 -0
- data/client/images/dataTables/favicon.ico +0 -0
- data/client/images/dataTables/forward_disabled.png +0 -0
- data/client/images/dataTables/forward_enabled.png +0 -0
- data/client/images/dataTables/forward_enabled_hover.png +0 -0
- data/client/images/dataTables/loading-background.png +0 -0
- data/client/images/dataTables/sort_asc.png +0 -0
- data/client/images/dataTables/sort_asc_disabled.png +0 -0
- data/client/images/dataTables/sort_both.png +0 -0
- data/client/images/dataTables/sort_desc.png +0 -0
- data/client/images/dataTables/sort_desc_disabled.png +0 -0
- data/client/images/logo-sm.png +0 -0
- data/client/javascripts/component/Base.coffee +39 -0
- data/client/javascripts/component/ChoicesInput.coffee +47 -0
- data/client/javascripts/component/Grid.coffee +199 -0
- data/client/javascripts/component/ModalDialog.coffee +44 -0
- data/client/javascripts/component/PopOver.coffee +52 -0
- data/client/javascripts/component/RadioGroup.coffee +59 -0
- data/client/javascripts/component/RecordFinder.coffee +143 -0
- data/client/javascripts/component/SelectField.coffee +43 -0
- data/client/javascripts/component/TaggedField.coffee +27 -0
- data/client/javascripts/component/grid/Editor.coffee +65 -0
- data/client/javascripts/component/grid/PopOverEditor.coffee +29 -0
- data/client/javascripts/component/grid/RowEditor.coffee +31 -0
- data/client/javascripts/component/grid/popover-editor.html +18 -0
- data/client/javascripts/component/grid/row-editor.html +16 -0
- data/client/javascripts/component/grid.html +4 -0
- data/client/javascripts/component/index.js +5 -0
- data/client/javascripts/component/modal.html +17 -0
- data/client/javascripts/component/popover.html +5 -0
- data/client/javascripts/component/record-finder/clause.skr +35 -0
- data/client/javascripts/component/record-finder/dialog.skr +4 -0
- data/client/javascripts/component/record-finder/field.skr +8 -0
- data/client/javascripts/data/Bootstrap.coffee +8 -0
- data/client/javascripts/data/ChangeSet.coffee +50 -0
- data/client/javascripts/data/Collection.coffee +111 -0
- data/client/javascripts/data/Config.coffee +15 -0
- data/client/javascripts/data/Model.coffee +269 -0
- data/client/javascripts/data/PubSub.coffee +68 -0
- data/client/javascripts/data/Query.coffee +184 -0
- data/client/javascripts/data/Roles.coffee +91 -0
- data/client/javascripts/data/Screens.coffee +157 -0
- data/client/javascripts/data/Sync.coffee +62 -0
- data/client/javascripts/data/User.coffee +70 -0
- data/client/javascripts/data/index.js +7 -0
- data/client/javascripts/data/mixins/HasCodeField.coffee +13 -0
- data/client/javascripts/extension/Base.coffee +9 -0
- data/client/javascripts/extension/Extensions.coffee +17 -0
- data/client/javascripts/extension/GlAccounts.coffee +9 -0
- data/client/javascripts/extension/index.js +6 -0
- data/client/javascripts/extension/load.js.erb +3 -0
- data/client/javascripts/lanes-complete.js +3 -0
- data/client/javascripts/lanes-workspace.js +1 -0
- data/client/javascripts/lib/MakeBaseClass.coffee +55 -0
- data/client/javascripts/lib/ModuleSupport.coffee +22 -0
- data/client/javascripts/lib/Templates.coffee +47 -0
- data/client/javascripts/lib/create-namespace.js +0 -0
- data/client/javascripts/lib/debounce.coffee +15 -0
- data/client/javascripts/lib/defer.coffee +7 -0
- data/client/javascripts/lib/el.js +115 -0
- data/client/javascripts/lib/index.js +9 -0
- data/client/javascripts/lib/loader.coffee +95 -0
- data/client/javascripts/lib/namespace.coffee +9 -0
- data/client/javascripts/lib/noConflict.coffee +14 -0
- data/client/javascripts/lib/promise_helpers.coffee +4 -0
- data/client/javascripts/lib/results.coffee +15 -0
- data/client/javascripts/lib/underscore.inflection.js +210 -0
- data/client/javascripts/lib/utilFunctions.coffee +51 -0
- data/client/javascripts/plugins/ResizeSensor.js +144 -0
- data/client/javascripts/plugins/index.js +4 -0
- data/client/javascripts/plugins/overlay.coffee +41 -0
- data/client/javascripts/plugins/trigger.coffee +15 -0
- data/client/javascripts/vendor/bootstrap/affix.js +142 -0
- data/client/javascripts/vendor/bootstrap/alert.js +92 -0
- data/client/javascripts/vendor/bootstrap/button.js +110 -0
- data/client/javascripts/vendor/bootstrap/carousel.js +223 -0
- data/client/javascripts/vendor/bootstrap/collapse.js +170 -0
- data/client/javascripts/vendor/bootstrap/dropdown.js +151 -0
- data/client/javascripts/vendor/bootstrap/modal.js +280 -0
- data/client/javascripts/vendor/bootstrap/popover.js +113 -0
- data/client/javascripts/vendor/bootstrap/scrollspy.js +170 -0
- data/client/javascripts/vendor/bootstrap/tab.js +128 -0
- data/client/javascripts/vendor/bootstrap/tooltip.js +457 -0
- data/client/javascripts/vendor/bootstrap/transition.js +59 -0
- data/client/javascripts/vendor/dataTables/dataTables.bootstrap.js +156 -0
- data/client/javascripts/vendor/dataTables/dataTables.scroller.js +1185 -0
- data/client/javascripts/vendor/dataTables/datatables.responsive.js +666 -0
- data/client/javascripts/vendor/dataTables/index.js +2 -0
- data/client/javascripts/vendor/dataTables/jquery.dataTables.js +14380 -0
- data/client/javascripts/vendor/jquery-2.js +9190 -0
- data/client/javascripts/vendor/jquery.tap.js +401 -0
- data/client/javascripts/vendor/magicsuggest.js +1565 -0
- data/client/javascripts/vendor/message-bus.js +285 -0
- data/client/javascripts/vendor/modern-stack.js +14 -0
- data/client/javascripts/vendor/packaged.js +13769 -0
- data/client/javascripts/view/Assets.coffee +9 -0
- data/client/javascripts/view/Base.coffee +231 -0
- data/client/javascripts/view/FormBindings.coffee +98 -0
- data/client/javascripts/view/Functions.coffee +13 -0
- data/client/javascripts/view/Helpers.coffee +77 -0
- data/client/javascripts/view/InterfaceState.coffee +88 -0
- data/client/javascripts/view/Keys.coffee +59 -0
- data/client/javascripts/view/ModelObserver.coffee +31 -0
- data/client/javascripts/view/ModelUpdate.coffee +8 -0
- data/client/javascripts/view/PubSub.coffee +29 -0
- data/client/javascripts/view/RenderContext.coffee +32 -0
- data/client/javascripts/view/SaveNotify.coffee +30 -0
- data/client/javascripts/view/Screen.coffee +30 -0
- data/client/javascripts/view/TimedHighlight.coffee +38 -0
- data/client/javascripts/view/TimedMask.coffee +65 -0
- data/client/javascripts/view/_button.html +3 -0
- data/client/javascripts/view/_toolbar.html +27 -0
- data/client/javascripts/view/index.js +10 -0
- data/client/javascripts/view/mixins/ScreenChangeListener.coffee +43 -0
- data/client/javascripts/view/model-update.html +13 -0
- data/client/javascripts/view/screen-definitions.js.erb +7 -0
- data/client/javascripts/workspace/ActiveScreensSwitcher.coffee +117 -0
- data/client/javascripts/workspace/Instance.es6 +60 -0
- data/client/javascripts/workspace/Layout.coffee +18 -0
- data/client/javascripts/workspace/LoginDialog.coffee +33 -0
- data/client/javascripts/workspace/Navbar.coffee +44 -0
- data/client/javascripts/workspace/Pages.coffee +46 -0
- data/client/javascripts/workspace/ScreensMenu.coffee +126 -0
- data/client/javascripts/workspace/index.js +12 -0
- data/client/javascripts/workspace/layout.html +4 -0
- data/client/javascripts/workspace/login-dialog.html +16 -0
- data/client/javascripts/workspace/menu.html +356 -0
- data/client/javascripts/workspace/menu_toggle.html +9 -0
- data/client/javascripts/workspace/navbar.html +19 -0
- data/client/javascripts/workspace/pages.html +6 -0
- data/client/javascripts/workspace/screens-menu.html +11 -0
- data/client/javascripts/workspace/screens-switcher.html +7 -0
- data/client/javascripts/workspace/tab.html +0 -0
- data/client/screens/user-management/UserEditScreen.coffee +21 -0
- data/client/screens/user-management/UserManagement.coffee +24 -0
- data/client/screens/user-management/grid-popover-editor.html +33 -0
- data/client/screens/user-management/index.css +4 -0
- data/client/screens/user-management/index.js +2 -0
- data/client/screens/user-management/user-management-styles.scss +7 -0
- data/client/screens/user-management/user-management.html +8 -0
- data/client/stylesheets/compoonents/all.scss +6 -0
- data/client/stylesheets/compoonents/changes-notification.scss +44 -0
- data/client/stylesheets/compoonents/grid-editors.scss +65 -0
- data/client/stylesheets/compoonents/grid.scss +301 -0
- data/client/stylesheets/compoonents/modal-dialog.scss +23 -0
- data/client/stylesheets/compoonents/record-finder.scss +71 -0
- data/client/stylesheets/compoonents/suggest.scss +266 -0
- data/client/stylesheets/fonts/icomoon.eot +0 -0
- data/client/stylesheets/fonts/icomoon.svg +160 -0
- data/client/stylesheets/fonts/icomoon.ttf +0 -0
- data/client/stylesheets/fonts/icomoon.woff +0 -0
- data/client/stylesheets/fonts/selection.json +3565 -0
- data/client/stylesheets/fonts/style.css +451 -0
- data/client/stylesheets/fonts.scss +38 -0
- data/client/stylesheets/forms.scss +75 -0
- data/client/stylesheets/index.css +4 -0
- data/client/stylesheets/keybindings.scss +6 -0
- data/client/stylesheets/lanes-workspace.scss +17 -0
- data/client/stylesheets/layout.scss +272 -0
- data/client/stylesheets/plugins/all.scss +2 -0
- data/client/stylesheets/plugins/overlay.scss +63 -0
- data/client/stylesheets/plugins/resize-sensor.scss +24 -0
- data/client/stylesheets/screens.scss +66 -0
- data/client/stylesheets/tabs.scss +148 -0
- data/client/stylesheets/vendor/bootstrap/_alerts.scss +68 -0
- data/client/stylesheets/vendor/bootstrap/_badges.scss +57 -0
- data/client/stylesheets/vendor/bootstrap/_breadcrumbs.scss +26 -0
- data/client/stylesheets/vendor/bootstrap/_button-groups.scss +240 -0
- data/client/stylesheets/vendor/bootstrap/_buttons.scss +157 -0
- data/client/stylesheets/vendor/bootstrap/_carousel.scss +243 -0
- data/client/stylesheets/vendor/bootstrap/_close.scss +35 -0
- data/client/stylesheets/vendor/bootstrap/_code.scss +68 -0
- data/client/stylesheets/vendor/bootstrap/_component-animations.scss +35 -0
- data/client/stylesheets/vendor/bootstrap/_dropdowns.scss +215 -0
- data/client/stylesheets/vendor/bootstrap/_forms.scss +538 -0
- data/client/stylesheets/vendor/bootstrap/_glyphicons.scss +237 -0
- data/client/stylesheets/vendor/bootstrap/_grid.scss +84 -0
- data/client/stylesheets/vendor/bootstrap/_input-groups.scss +166 -0
- data/client/stylesheets/vendor/bootstrap/_jumbotron.scss +48 -0
- data/client/stylesheets/vendor/bootstrap/_labels.scss +66 -0
- data/client/stylesheets/vendor/bootstrap/_list-group.scss +132 -0
- data/client/stylesheets/vendor/bootstrap/_media.scss +56 -0
- data/client/stylesheets/vendor/bootstrap/_mixins.scss +39 -0
- data/client/stylesheets/vendor/bootstrap/_modals.scss +150 -0
- data/client/stylesheets/vendor/bootstrap/_navbar.scss +659 -0
- data/client/stylesheets/vendor/bootstrap/_navs.scss +242 -0
- data/client/stylesheets/vendor/bootstrap/_normalize.scss +425 -0
- data/client/stylesheets/vendor/bootstrap/_pager.scss +55 -0
- data/client/stylesheets/vendor/bootstrap/_pagination.scss +88 -0
- data/client/stylesheets/vendor/bootstrap/_panels.scss +243 -0
- data/client/stylesheets/vendor/bootstrap/_popovers.scss +133 -0
- data/client/stylesheets/vendor/bootstrap/_print.scss +101 -0
- data/client/stylesheets/vendor/bootstrap/_progress-bars.scss +105 -0
- data/client/stylesheets/vendor/bootstrap/_responsive-embed.scss +34 -0
- data/client/stylesheets/vendor/bootstrap/_responsive-utilities.scss +174 -0
- data/client/stylesheets/vendor/bootstrap/_scaffolding.scss +150 -0
- data/client/stylesheets/vendor/bootstrap/_tables.scss +233 -0
- data/client/stylesheets/vendor/bootstrap/_theme.scss +258 -0
- data/client/stylesheets/vendor/bootstrap/_thumbnails.scss +38 -0
- data/client/stylesheets/vendor/bootstrap/_tooltip.scss +95 -0
- data/client/stylesheets/vendor/bootstrap/_type.scss +304 -0
- data/client/stylesheets/vendor/bootstrap/_utilities.scss +57 -0
- data/client/stylesheets/vendor/bootstrap/_variables.scss +850 -0
- data/client/stylesheets/vendor/bootstrap/_wells.scss +29 -0
- data/client/stylesheets/vendor/bootstrap/bootstrap.scss +50 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_alerts.scss +14 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_background-variant.scss +11 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_border-radius.scss +18 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_buttons.scss +50 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_center-block.scss +7 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_clearfix.scss +22 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_forms.scss +84 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_gradients.scss +58 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_grid-framework.scss +81 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_grid.scss +122 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_hide-text.scss +21 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_image.scss +34 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_labels.scss +12 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_list-group.scss +31 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_nav-divider.scss +10 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_nav-vertical-align.scss +9 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_opacity.scss +8 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_pagination.scss +23 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_panels.scss +24 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_progress-bar.scss +10 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_reset-filter.scss +8 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_resize.scss +6 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_responsive-visibility.scss +21 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_size.scss +10 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_tab-focus.scss +9 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_table-row.scss +28 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_text-emphasis.scss +11 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_text-overflow.scss +8 -0
- data/client/stylesheets/vendor/bootstrap/mixins/_vendor-prefixes.scss +219 -0
- data/client/stylesheets/vendor/bootstrap-custom-grid.scss +85 -0
- data/client/stylesheets/vendor/bootstrap-custom-modals.scss +150 -0
- data/client/stylesheets/vendor/bootstrap.scss +69 -0
- data/client/stylesheets/vendor/dataTables.scss +4 -0
- data/config/database.yml +9 -0
- data/config/puma.rb +7 -0
- data/config.ru +4 -0
- data/db/migrate/20140615031600_create_hip_users.rb +17 -0
- data/db/seed.rb +37 -0
- data/foo/Gemfile +5 -0
- data/foo/Guardfile +13 -0
- data/foo/foo/Gemfile +5 -0
- data/foo/foo/lib/foo.rb +7 -0
- data/foo/lib/foo/version.rb +3 -0
- data/foo/lib/foo.rb +8 -0
- data/lanes.gemspec +54 -0
- data/lib/generators/lanes/migrations/install_generator.rb +42 -0
- data/lib/lanes/access/locked_fields.rb +43 -0
- data/lib/lanes/access/role.rb +58 -0
- data/lib/lanes/access/role_collection.rb +75 -0
- data/lib/lanes/access/roles/administrator.rb +25 -0
- data/lib/lanes/access/roles/support.rb +13 -0
- data/lib/lanes/access/user_maint_screen.rb +32 -0
- data/lib/lanes/access.rb +50 -0
- data/lib/lanes/api/asset_pipeline.rb +59 -0
- data/lib/lanes/api/authentication_helper.rb +21 -0
- data/lib/lanes/api/authentication_provider.rb +45 -0
- data/lib/lanes/api/controller.rb +290 -0
- data/lib/lanes/api/default_routes.rb +35 -0
- data/lib/lanes/api/eco.js +516 -0
- data/lib/lanes/api/error_formatter.rb +37 -0
- data/lib/lanes/api/helper_methods.rb +32 -0
- data/lib/lanes/api/javascript_processor.rb +116 -0
- data/lib/lanes/api/pub_sub.rb +33 -0
- data/lib/lanes/api/request_wrapper.rb +42 -0
- data/lib/lanes/api/root.rb +103 -0
- data/lib/lanes/api/skr_templates.rb +60 -0
- data/lib/lanes/api/test_specs.rb +59 -0
- data/lib/lanes/api/updates.rb +38 -0
- data/lib/lanes/api.rb +27 -0
- data/lib/lanes/cli.rb +13 -0
- data/lib/lanes/concerns/all.rb +16 -0
- data/lib/lanes/concerns/api_path.rb +21 -0
- data/lib/lanes/concerns/association_extensions.rb +85 -0
- data/lib/lanes/concerns/attr_accessor_with_default.rb +62 -0
- data/lib/lanes/concerns/code_identifier.rb +43 -0
- data/lib/lanes/concerns/export_associations.rb +52 -0
- data/lib/lanes/concerns/export_join_tables.rb +39 -0
- data/lib/lanes/concerns/export_methods.rb +104 -0
- data/lib/lanes/concerns/export_scope.rb +66 -0
- data/lib/lanes/concerns/exported_limit_evaluator.rb +17 -0
- data/lib/lanes/concerns/immutable_model.rb +32 -0
- data/lib/lanes/concerns/locked_fields.rb +84 -0
- data/lib/lanes/concerns/pub_sub.rb +105 -0
- data/lib/lanes/concerns/queries.rb +20 -0
- data/lib/lanes/concerns/random_hash_code.rb +40 -0
- data/lib/lanes/concerns/sanitize_api_data.rb +15 -0
- data/lib/lanes/concerns/set_attribute_data.rb +154 -0
- data/lib/lanes/concerns/track_modifications.rb +51 -0
- data/lib/lanes/concerns/visible_id_identifier.rb +53 -0
- data/lib/lanes/configuration.rb +85 -0
- data/lib/lanes/db/migration_helpers.rb +178 -0
- data/lib/lanes/db/migrations.rb +13 -0
- data/lib/lanes/db/seed.rb +27 -0
- data/lib/lanes/db.rb +86 -0
- data/lib/lanes/environment.rb +19 -0
- data/lib/lanes/extension.rb +72 -0
- data/lib/lanes/generators/app/Gemfile +5 -0
- data/lib/lanes/generators/app/Guardfile +13 -0
- data/lib/lanes/generators/app/Rakefile +9 -0
- data/lib/lanes/generators/app/config/database.yml +9 -0
- data/lib/lanes/generators/app/config.ru +4 -0
- data/lib/lanes/generators/app/lib/main_class/version.rb +3 -0
- data/lib/lanes/generators/app/lib/main_class.rb +8 -0
- data/lib/lanes/generators/app.rb +36 -0
- data/lib/lanes/guard_tasks.rb +44 -0
- data/lib/lanes/logger.rb +37 -0
- data/lib/lanes/model.rb +26 -0
- data/lib/lanes/numbers.rb +72 -0
- data/lib/lanes/rails_engine.rb +5 -0
- data/lib/lanes/screens.rb +126 -0
- data/lib/lanes/spec_asset_expander.rb +43 -0
- data/lib/lanes/strings.rb +56 -0
- data/lib/lanes/user.rb +127 -0
- data/lib/lanes/validators/all.rb +2 -0
- data/lib/lanes/validators/email.rb +17 -0
- data/lib/lanes/validators/set.rb +18 -0
- data/lib/lanes/version.rb +5 -0
- data/lib/lanes.rb +22 -0
- data/npm-build/README +1 -0
- data/npm-build/compile.coffee +15 -0
- data/npm-build/package.json +59 -0
- data/npm-build/shims/underscore.js +1416 -0
- data/npm-build/template.js +33 -0
- data/public/javascripts/jasmine_examples/Player.js +22 -0
- data/public/javascripts/jasmine_examples/Song.js +7 -0
- data/spec/api/javascript_processor_spec.rb +107 -0
- data/spec/api/user_spec.rb +52 -0
- data/spec/client/component/ChoicesInputSpec.coffee +12 -0
- data/spec/client/component/foo_spec.coffee +4 -0
- data/spec/client/foo_spec.js +0 -0
- data/spec/client/jasmine_examples/PlayerSpec.js +0 -0
- data/spec/client/support/jasmine.yml +128 -0
- data/spec/client/support/jasmine_helper.rb +15 -0
- data/spec/concerns/api_path_spec.rb +14 -0
- data/spec/concerns/association_extensions_spec.rb +30 -0
- data/spec/concerns/attr_accessor_with_default_spec.rb +57 -0
- data/spec/concerns/code_identifier_spec.rb +45 -0
- data/spec/concerns/export_associations_spec.rb +7 -0
- data/spec/concerns/export_methods_spec.rb +43 -0
- data/spec/concerns/export_scope_spec.rb +15 -0
- data/spec/concerns/exported_limits_spec.rb +47 -0
- data/spec/concerns/pub_sub_spec.rb +83 -0
- data/spec/concerns/set_attribute_data_spec.rb +66 -0
- data/spec/configuration_spec.rb +26 -0
- data/spec/fixtures/lanes/users.yml +13 -0
- data/spec/helpers/.gitkeep +0 -0
- data/spec/helpers/SpecHelper.js +18 -0
- data/spec/locked_fields_spec.rb +27 -0
- data/spec/numbers_spec.rb +26 -0
- data/spec/role_collection_spec.rb +19 -0
- data/spec/spec_helper.rb +163 -0
- data/spec/strings_spec.rb +41 -0
- data/spec/testing_models.rb +54 -0
- data/spec/user_role_spec.rb +7 -0
- data/spec/user_spec.rb +53 -0
- data/tasks/migrations.rake +22 -0
- data/tasks/publish.rake +8 -0
- data/views/index.erb +29 -0
- data/views/specs.erb +25 -0
- data/yard_ext/all.rb +9 -0
- data/yard_ext/code_identifier_handler.rb +33 -0
- data/yard_ext/concern_meta_methods.rb +60 -0
- data/yard_ext/config_options.rb +27 -0
- data/yard_ext/exported_scope.rb +4 -0
- data/yard_ext/immutable_handler.rb +17 -0
- data/yard_ext/json_attr_accessor.rb +22 -0
- data/yard_ext/locked_fields_handler.rb +21 -0
- data/yard_ext/templates/default/layout/html/layout.erb +20 -0
- data/yard_ext/templates/default/method_details/html/github_link.erb +1 -0
- data/yard_ext/templates/default/method_details/setup.rb +3 -0
- data/yard_ext/validators.rb +1 -0
- data/yard_ext/visible_id_handler.rb +38 -0
- metadata +772 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require "oj"
|
|
2
|
+
|
|
3
|
+
require_relative "updates"
|
|
4
|
+
|
|
5
|
+
require 'message_bus'
|
|
6
|
+
|
|
7
|
+
module Lanes
|
|
8
|
+
module API
|
|
9
|
+
|
|
10
|
+
class PubSub
|
|
11
|
+
|
|
12
|
+
def self.publish(channel, data)
|
|
13
|
+
MessageBus.publish channel, data #Oj.dump(data, mode: :compat)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.initialize
|
|
17
|
+
# Use OJ - it encodes dates properly as ISO 8601
|
|
18
|
+
# https://github.com/moment/moment/issues/1407
|
|
19
|
+
Oj.mimic_JSON()
|
|
20
|
+
# # Requiring json here seems to stop conflicts when requiring json in other files.
|
|
21
|
+
begin
|
|
22
|
+
require 'json'
|
|
23
|
+
rescue Exception
|
|
24
|
+
# ignore
|
|
25
|
+
end
|
|
26
|
+
::MessageBus.redis_config = Lanes.config.redis
|
|
27
|
+
Updates.relay!
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Sinatra
|
|
2
|
+
|
|
3
|
+
module RequestWrapper
|
|
4
|
+
|
|
5
|
+
def wrap_request(model, params, parent_attribute)
|
|
6
|
+
|
|
7
|
+
authentication = Lanes::API::AuthenticationProvider.new(
|
|
8
|
+
request_type: request.request_method,
|
|
9
|
+
session: session,
|
|
10
|
+
params: params
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
unless authentication.allowed_access_to?(model)
|
|
14
|
+
halt( 401, Oj.dump({
|
|
15
|
+
success:false, errors: {user: "Access Denied"}, message: authentication.error_message
|
|
16
|
+
}))
|
|
17
|
+
end
|
|
18
|
+
::Lanes::User.scoped_to(authentication.current_user) do | user |
|
|
19
|
+
Lanes.logger.debug "User : #{user.id} (#{user.login})"
|
|
20
|
+
Lanes.logger.debug "Params : #{request.params}"
|
|
21
|
+
|
|
22
|
+
params[:nested_attribute] = Hash[ parent_attribute, params[parent_attribute] ] if parent_attribute
|
|
23
|
+
|
|
24
|
+
wrap_json_reply do
|
|
25
|
+
yield authentication
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def wrap_json_reply
|
|
32
|
+
response = yield
|
|
33
|
+
if false == response[:success]
|
|
34
|
+
status(406)
|
|
35
|
+
end
|
|
36
|
+
Oj.dump(response, mode: :compat)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require 'sinatra'
|
|
2
|
+
require 'oj'
|
|
3
|
+
require 'rack/csrf'
|
|
4
|
+
require 'sprockets-helpers'
|
|
5
|
+
require 'message_bus'
|
|
6
|
+
require_relative 'asset_pipeline'
|
|
7
|
+
require_relative 'helper_methods'
|
|
8
|
+
require_relative 'pub_sub'
|
|
9
|
+
|
|
10
|
+
module Lanes
|
|
11
|
+
module API
|
|
12
|
+
class Root < Sinatra::Application
|
|
13
|
+
set :environment, Lanes.config.environment
|
|
14
|
+
Lanes.config.get(:environment) do | env |
|
|
15
|
+
set :environment, env
|
|
16
|
+
end
|
|
17
|
+
register AssetPipeline
|
|
18
|
+
helpers Sinatra::RequestWrapper
|
|
19
|
+
helpers HelperMethods
|
|
20
|
+
not_found do
|
|
21
|
+
Oj.dump({ message: "endpoint not found", success: false })
|
|
22
|
+
end
|
|
23
|
+
before do
|
|
24
|
+
content_type 'application/json'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
use Rack::Session::Cookie, :key => 'lanes.session', :secret => Lanes.config.session_secret_key_base
|
|
28
|
+
use ActiveRecord::ConnectionAdapters::ConnectionManagement
|
|
29
|
+
use MessageBus::Rack::Middleware
|
|
30
|
+
error do
|
|
31
|
+
Lanes.logger.warn request.env['sinatra.error']#.backtrace
|
|
32
|
+
Oj.dump({
|
|
33
|
+
success: false,
|
|
34
|
+
errors: { exception: request.env['sinatra.error'].message },
|
|
35
|
+
message: request.env['sinatra.error'].message
|
|
36
|
+
})
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
configure do
|
|
40
|
+
set :views, Pathname.new(__FILE__).dirname.join("../../../views")
|
|
41
|
+
set :show_exceptions, false
|
|
42
|
+
DB.establish_connection
|
|
43
|
+
PubSub.initialize
|
|
44
|
+
# use Rack::Csrf, :skip=>['GET:/'], :raise => true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.build_route(model, options = {})
|
|
48
|
+
|
|
49
|
+
path = options[:path] || model.api_path
|
|
50
|
+
controller = options[:controller] || Lanes::API::Controller
|
|
51
|
+
|
|
52
|
+
parent_attribute = false
|
|
53
|
+
prefix = if options[:under]
|
|
54
|
+
parent_attribute = options[:parent_attribute] || options[:under].underscore.singularize+'_id'
|
|
55
|
+
else
|
|
56
|
+
''
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# index
|
|
61
|
+
get "#{prefix}/#{path}/?:id?.json" do
|
|
62
|
+
wrap_request(model, params, parent_attribute) do |authentication|
|
|
63
|
+
controller.new(model, authentication, params).perform_retrieval
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# create
|
|
68
|
+
post "#{prefix}/#{path}.json" do
|
|
69
|
+
wrap_request(model, params, parent_attribute) do |authentication|
|
|
70
|
+
controller.new(model, authentication, params, data).perform_creation
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
unless options[:immutable]
|
|
75
|
+
patch "#{prefix}/#{path}/:id.json" do
|
|
76
|
+
perform_request(model, params, parent_attribute) do |authentication|
|
|
77
|
+
controller.new(model, authentication, params, data).perform_update
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# update
|
|
82
|
+
put "#{prefix}/#{path}/:id.json" do
|
|
83
|
+
wrap_request(model, params, parent_attribute) do |authentication|
|
|
84
|
+
controller.new(model, authentication, params, data).perform_update
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
unless options[:indestructible]
|
|
89
|
+
# destroy
|
|
90
|
+
delete "#{prefix}/#{path}/:id.json" do
|
|
91
|
+
wrap_request(model, params, parent_attribute) do |authentication|
|
|
92
|
+
controller.new(model, authentication, params).perform_destroy
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Stockor templates compiler
|
|
2
|
+
#
|
|
3
|
+
# This is a hacked together version of Sam Stephenson's Eco
|
|
4
|
+
# project for embedded coffeescript templates.
|
|
5
|
+
#
|
|
6
|
+
# Our version has the various helper functions pre-loaded
|
|
7
|
+
# so the templates themselves are smaller in size.
|
|
8
|
+
# and also includes a "h" helper namespace
|
|
9
|
+
|
|
10
|
+
require 'pathname'
|
|
11
|
+
require 'tilt'
|
|
12
|
+
module Skr
|
|
13
|
+
module Workspace
|
|
14
|
+
|
|
15
|
+
module EJSSource
|
|
16
|
+
class << self
|
|
17
|
+
def path
|
|
18
|
+
@path ||= Pathname.new(__FILE__).dirname.join('eco.js')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def contents
|
|
22
|
+
@contents ||= path.read
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def combined_contents
|
|
26
|
+
[CoffeeScript::Source.contents, contents].join(";\n")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def context
|
|
30
|
+
@context ||= ExecJS.compile(combined_contents)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def compile(template)
|
|
34
|
+
template = template.read if template.respond_to?(:read)
|
|
35
|
+
context.call("eco.precompile", template, 'Skr.TemplateWrapper','Skr.View.Helpers')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SkrTemplates < Tilt::Template
|
|
43
|
+
|
|
44
|
+
self.default_mime_type = 'application/javascript'
|
|
45
|
+
|
|
46
|
+
def evaluate(scope, locals, &block)
|
|
47
|
+
"Skr.Templates['#{scope.logical_path}']=" + EJSSource.compile(data)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def prepare;
|
|
51
|
+
# NOOP
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.register(app)
|
|
55
|
+
app.assets.register_engine(".skr", self )
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'jasmine-core'
|
|
2
|
+
|
|
3
|
+
module Lanes
|
|
4
|
+
module API
|
|
5
|
+
|
|
6
|
+
class TestSpecs
|
|
7
|
+
cattr_accessor :current
|
|
8
|
+
|
|
9
|
+
def initialize(root)
|
|
10
|
+
@root = root
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def css_files
|
|
14
|
+
urlpath Jasmine::Core.css_files
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def js_files
|
|
18
|
+
urlpath(Jasmine::Core.js_files) +
|
|
19
|
+
urlpath(Jasmine::Core.boot_files) +
|
|
20
|
+
urlpath(spec_files("helpers")) +
|
|
21
|
+
urlpath(spec_files("client"))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def spec_files(path)
|
|
27
|
+
dir = @root.join("spec")
|
|
28
|
+
regex = /^#{dir}\//
|
|
29
|
+
Dir.glob( dir.join(path,"**/*.{coffee,js}") ).map do |file|
|
|
30
|
+
file.sub(regex,'').sub(/coffee$/,'js')
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def urlpath(files)
|
|
35
|
+
files.map{ |file| "/spec/"+file }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Lanes.config.get(:specs_root) do | root |
|
|
40
|
+
TestSpecs.current = TestSpecs.new(root)
|
|
41
|
+
Root.sprockets.append_path(root.join("spec"))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Root.sprockets.append_path(Jasmine::Core.path)
|
|
45
|
+
|
|
46
|
+
Root.get '/spec' do
|
|
47
|
+
content_type 'text/html'
|
|
48
|
+
erb :specs, locals: { specs: TestSpecs.current }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
Root.get "/spec/*" do |path|
|
|
52
|
+
env_sprockets = request.env.dup
|
|
53
|
+
env_sprockets['PATH_INFO'] = path
|
|
54
|
+
settings.sprockets.call env_sprockets
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Lanes
|
|
2
|
+
|
|
3
|
+
module API
|
|
4
|
+
|
|
5
|
+
module Updates
|
|
6
|
+
@relaying_messages = false
|
|
7
|
+
|
|
8
|
+
def self.user_info_for_change(model)
|
|
9
|
+
if model.has_attribute?(:updated_by_id)
|
|
10
|
+
model.updated_by.as_json(only:[:id,:name,:email])
|
|
11
|
+
else
|
|
12
|
+
{}
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.relay!
|
|
17
|
+
if @relaying_messages
|
|
18
|
+
raise "Already relaying messages, calling relay! twice will lead to message duplication"
|
|
19
|
+
end
|
|
20
|
+
@relaying_messages = true
|
|
21
|
+
Lanes::Model.observe(:save) do |model|
|
|
22
|
+
if model.changes.any?
|
|
23
|
+
path = "/#{model.class.api_path}/#{model.id}"
|
|
24
|
+
|
|
25
|
+
Lanes::API::PubSub.publish(path, {
|
|
26
|
+
by: self.user_info_for_change(model),
|
|
27
|
+
update: model.changes
|
|
28
|
+
})
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/lanes/api.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require_relative 'api/authentication_helper'
|
|
2
|
+
require_relative 'api/authentication_provider'
|
|
3
|
+
|
|
4
|
+
require_relative 'api/request_wrapper'
|
|
5
|
+
require_relative 'api/error_formatter'
|
|
6
|
+
require_relative 'api/controller'
|
|
7
|
+
require_relative 'api/root'
|
|
8
|
+
require_relative 'api/default_routes'
|
|
9
|
+
# require_relative 'api/login_sessions_controller'
|
|
10
|
+
# require_relative 'api/users_controller'
|
|
11
|
+
|
|
12
|
+
require_relative "api/pub_sub"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Lanes.config.get(:environment) do
|
|
16
|
+
require_relative("api/test_specs") if Lanes.env.test?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Lanes::API::Root.routes['GET'].each do |route|
|
|
20
|
+
# puts route[0]
|
|
21
|
+
# end
|
|
22
|
+
|
|
23
|
+
# require_relative "api/skr_templates"
|
|
24
|
+
# if defined?(::Rails)
|
|
25
|
+
# require_relative "api/engine"
|
|
26
|
+
# require_relative "api/route_helper"
|
|
27
|
+
# end
|
data/lib/lanes/cli.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require_relative "api_path"
|
|
2
|
+
require_relative "association_extensions"
|
|
3
|
+
require_relative "attr_accessor_with_default"
|
|
4
|
+
require_relative "code_identifier"
|
|
5
|
+
require_relative "export_associations"
|
|
6
|
+
require_relative "export_join_tables"
|
|
7
|
+
require_relative "export_methods"
|
|
8
|
+
require_relative "export_scope"
|
|
9
|
+
require_relative "immutable_model"
|
|
10
|
+
require_relative "set_attribute_data"
|
|
11
|
+
require_relative "locked_fields"
|
|
12
|
+
require_relative "pub_sub"
|
|
13
|
+
require_relative "queries"
|
|
14
|
+
require_relative "random_hash_code"
|
|
15
|
+
require_relative "sanitize_api_data"
|
|
16
|
+
require_relative "visible_id_identifier"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Lanes::Concerns
|
|
2
|
+
|
|
3
|
+
# @see ClassMethods
|
|
4
|
+
module ApiPath
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
|
|
9
|
+
def api_path
|
|
10
|
+
self.to_s.demodulize.pluralize.underscore.dasherize
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def from_api_path(path)
|
|
14
|
+
name = path.underscore.camelize.singularize
|
|
15
|
+
name = "Lanes::#{name}" unless name=~/^Lanes/
|
|
16
|
+
name.safe_constantize
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module Lanes::Concerns
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# Custom implentation of has_many and belongs_to
|
|
5
|
+
#
|
|
6
|
+
module AssociationExtensions
|
|
7
|
+
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
|
|
12
|
+
def has_one(name, scope = nil, options = {})
|
|
13
|
+
opts = extract_private_options( scope, options )
|
|
14
|
+
associations = super
|
|
15
|
+
handle_private_options( name, opts ) unless opts.empty?
|
|
16
|
+
associations
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def belongs_to(name, scope = nil, options = {} )
|
|
20
|
+
opts = extract_private_options( scope, options )
|
|
21
|
+
associations = super
|
|
22
|
+
handle_private_options( name, opts ) unless opts.empty?
|
|
23
|
+
associations
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def has_many(name, scope=nil,options={}, &extension )
|
|
27
|
+
opts = extract_private_options( scope, options )
|
|
28
|
+
associations = super
|
|
29
|
+
handle_private_options( name, opts ) unless opts.empty?
|
|
30
|
+
associations
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def handle_private_options( name, opts )
|
|
36
|
+
association = reflect_on_association( name.to_sym )
|
|
37
|
+
setup_listeners( association, opts[:listen] ) if opts[:listen]
|
|
38
|
+
setup_association_export( association, opts[:export] ) if opts[:export]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def extract_private_options( scope, options )
|
|
42
|
+
if scope.is_a?(Hash)
|
|
43
|
+
scope.extract!( :export, :listen )
|
|
44
|
+
else
|
|
45
|
+
options.extract!( :export, :listen )
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# This gets complex; We
|
|
50
|
+
# First setup proc's for each listener,
|
|
51
|
+
# then attempt to load the associations's class
|
|
52
|
+
# If that succeds, we're done, otherwise we add it
|
|
53
|
+
# to PubSub's pending queue
|
|
54
|
+
def setup_listeners( association, listeners )
|
|
55
|
+
targets = {}
|
|
56
|
+
if listeners.any? && association.options[:inverse_of].nil?
|
|
57
|
+
raise ArgumentError.new "Setting listener on #{name}##{association.name} " +
|
|
58
|
+
"but the association does not have " +
|
|
59
|
+
"an inverse_of specified."
|
|
60
|
+
end
|
|
61
|
+
listeners.each do | name, target |
|
|
62
|
+
targets[ name ] = Proc.new{ | record, *args |
|
|
63
|
+
record = record.send( association.inverse_of.name )
|
|
64
|
+
record.send( target, *( [record] + args ) ) if record
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
begin
|
|
68
|
+
klass = association.klass # This will throw if the class hasn't been loaded yet
|
|
69
|
+
targets.each{ | name, proc | klass._add_event_listener( name, proc ) }
|
|
70
|
+
rescue NameError
|
|
71
|
+
pending = PendingEventListeners.all[association.class_name.demodulize]
|
|
72
|
+
targets.each do | name, proc |
|
|
73
|
+
pending[name] << proc
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def setup_association_export( association, options )
|
|
79
|
+
export_associations( association.name, options == true ? {} : options )
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Lanes::Concerns
|
|
2
|
+
|
|
3
|
+
# @see ClassMethods
|
|
4
|
+
module AttrAccessorWithDefault
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
|
|
9
|
+
# defines a attr_accessor with a default value
|
|
10
|
+
# @param name [ Symbol ] name of the attribute
|
|
11
|
+
# @param default [ Object, lambda, Proc ] if a value is given, be aware that it will be shared between instances
|
|
12
|
+
# @example
|
|
13
|
+
#
|
|
14
|
+
# Shared = Struct.new(:str)
|
|
15
|
+
# class AttrTestClass
|
|
16
|
+
# include AttrAccessorWithDefault
|
|
17
|
+
# attr_accessor_with_default :non_copying, ->{ Shared.new("a default string") }
|
|
18
|
+
# attr_accessor_with_default :shared, Shared.new("a default string")
|
|
19
|
+
# end
|
|
20
|
+
# a = AttrTestClass.new
|
|
21
|
+
# b = AttrTestClass.new
|
|
22
|
+
# a.non_copying.str #=> "a default string"
|
|
23
|
+
# a.non_copying.str = "new_string" #=> "new string"
|
|
24
|
+
# b.non_copying.str #=> "a default string"
|
|
25
|
+
#
|
|
26
|
+
# a.shared.str #=> "a default string"
|
|
27
|
+
# b.shared.str #=> "a default string"
|
|
28
|
+
# a.shared.str = "new string" #=> "new string"
|
|
29
|
+
# b.shared.str #=> "new string"
|
|
30
|
+
|
|
31
|
+
def attr_accessor_with_default( name, default=nil )
|
|
32
|
+
attr_writer name
|
|
33
|
+
attr_add_default_setting_method(name)
|
|
34
|
+
attr_reader_with_default( name, default )
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def attr_add_default_setting_method(name)
|
|
38
|
+
module_eval do
|
|
39
|
+
define_singleton_method(name) do | value |
|
|
40
|
+
attr_reader_with_default( name, value )
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
def attr_reader_with_default( name, default )
|
|
45
|
+
module_eval do
|
|
46
|
+
define_method( name ) do
|
|
47
|
+
class << self; self; end.class_eval do
|
|
48
|
+
attr_reader( name )
|
|
49
|
+
end
|
|
50
|
+
if instance_variables.include? "@#{name}"
|
|
51
|
+
instance_variable_get( "@#{name}" )
|
|
52
|
+
else
|
|
53
|
+
instance_variable_set( "@#{name}", default.is_a?(Proc) ? default.call : default )
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Lanes
|
|
2
|
+
module Concerns
|
|
3
|
+
|
|
4
|
+
# @see ClassMethods
|
|
5
|
+
module CodeIdentifier
|
|
6
|
+
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
# ### Code Identifier Concern
|
|
10
|
+
# This adds the {#has_code_identifier} class method
|
|
11
|
+
module ClassMethods
|
|
12
|
+
|
|
13
|
+
# A 2-10 character string that identifies an entity, such as a Customer, Vendor, or SKU.
|
|
14
|
+
# The code is often assigned by the user, but may also
|
|
15
|
+
# be auto-generated by {Lanes::Strings.code_identifier}
|
|
16
|
+
#
|
|
17
|
+
# @param max_length [Integer] how long the code is allowed to be
|
|
18
|
+
# @param from [Symbol], method to call for a string to base the code upon
|
|
19
|
+
def has_code_identifier( from: nil, max_length: 10 )
|
|
20
|
+
|
|
21
|
+
validates :code, :length=>2..max_length, :presence=>true, :uniqueness=>true
|
|
22
|
+
|
|
23
|
+
alias_attribute :record_identifier, :code
|
|
24
|
+
|
|
25
|
+
if from
|
|
26
|
+
before_validation(:on=>:create) do
|
|
27
|
+
source = self[from]
|
|
28
|
+
unless source.blank?
|
|
29
|
+
self.code ||= Lanes::Strings.code_identifier( source, length:max_length )
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
before_validation do
|
|
35
|
+
self.code = self.code.upcase unless self.code.blank?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Lanes::Concerns
|
|
2
|
+
|
|
3
|
+
# @see ClassMethods
|
|
4
|
+
module ExportAssociations
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
class_attribute :exported_associations
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
# Mark associations as exported, meaning they can be quered against and optionally written to
|
|
13
|
+
# @param associations [list of Symbols]
|
|
14
|
+
# @option options [Boolean] :writable should the associations accept nested attributes
|
|
15
|
+
# @option options [Boolean] :mandatory should the association be included with it's parent at all times?
|
|
16
|
+
# @option options [Symbol referring to a method, lambda] :limit restrict to Users for whom true is returned
|
|
17
|
+
def export_associations( *associations )
|
|
18
|
+
self.exported_associations ||= {}
|
|
19
|
+
associations.flatten!
|
|
20
|
+
options = associations.extract_options!
|
|
21
|
+
associations.each do | assoc_name |
|
|
22
|
+
self.exported_associations[ assoc_name.to_sym ] = options
|
|
23
|
+
if options[:writable]
|
|
24
|
+
accepts_nested_attributes_for( assoc_name, options.except(:writable, :optional) )
|
|
25
|
+
end
|
|
26
|
+
if false == options[:optional]
|
|
27
|
+
self.export_methods( *assoc_name, { :optional=>false })
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# If the association is exported it may be queried against and it's data included
|
|
33
|
+
# along with the parent record. It may not be written to unless the :writable flag
|
|
34
|
+
# was set, in which case it'll be allowed by has_exported_association?
|
|
35
|
+
def has_exported_association?( association, user )
|
|
36
|
+
self.exported_associations &&
|
|
37
|
+
( options = self.exported_associations[ association.to_sym ] ) &&
|
|
38
|
+
evaluate_export_limit( user, :association, association, options[:limit] )
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Does the association allow writes? True if the association has been exported
|
|
42
|
+
# and it accepts nested attributes
|
|
43
|
+
def has_exported_nested_attribute?( association, user )
|
|
44
|
+
self.nested_attributes_options[ association.to_sym ] &&
|
|
45
|
+
self.has_exported_association?( association, user )
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|