commit 659da24af38b7d72aa983c7abd0ce3ff6aabfa51 Author: Benjamin ter Kuile Date: Thu Dec 21 11:36:52 2017 +0100 initial copy commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aae7f11 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.bundle/ +log/*.log +pkg/ +spec/dummy/db/*.sqlite3 +spec/dummy/db/*.sqlite3-journal +spec/dummy/log/*.log +spec/dummy/tmp/ diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..a35c44f --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--require rails_helper diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..bc174f8 --- /dev/null +++ b/Gemfile @@ -0,0 +1,32 @@ +source 'https://rubygems.org' + +# Declare your gem's dependencies in dunlop-ember.gemspec. +# Bundler will treat runtime dependencies like base dependencies, and +# development dependencies will be added by default to the :development group. +gemspec + +gem 'coffee-rails' +gem 'kaminari' + +group :development, :test do + gem 'rack-cors' + gem 'pry-rails' + gem 'pry-doc' + gem 'spring' + gem 'spring-commands-rspec' + gem 'rspec-rails' + gem 'cancancan' + gem 'slim-rails' + gem 'dunlop-core', github: 'companytools/dunlop-core', branch: 'master' + #gem 'dunlop-core', path: '~/development/rails/components/dunlop-core' + gem 'factory_girl_rails' + gem 'active_model_serializers' + gem 'database_cleaner' +end +# Declare any dependencies that are still in development here instead of in +# your gemspec. These might include edge Rails or gems from your path or +# Git. Remember to move these dependencies to your gemspec before releasing +# your gem to rubygems.org. + +# To use a debugger +# gem 'byebug', group: [:development, :test] diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..23f5365 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,332 @@ +GIT + remote: git://github.com/companytools/dunlop-core.git + revision: a7b86d3c17309d9de29bc2b4aa9a7dff842d619f + branch: master + specs: + dunlop-core (1.4.0) + cancancan + carrierwave + coffee-rails (~> 4.1) + devise + devise-token_authenticatable + exception_notification + facets + font-awesome-rails + js-routes + memoist + momentjs-rails + pickadate-rails (~> 3.5.5.0) + rails (>= 4.2.6) + ransack + record_collection (>= 0.10.4) + redcarpet + request_store + responders + sass-rails (>= 5.0.4) + simple_form + slim-rails + state_machines-activerecord + +PATH + remote: . + specs: + dunlop-ember (0.1.0) + active_model_serializers + devise + rails (>= 4.2.0) + ransack + +GEM + remote: https://rubygems.org/ + specs: + actioncable (5.1.4) + actionpack (= 5.1.4) + nio4r (~> 2.0) + websocket-driver (~> 0.6.1) + actionmailer (5.1.4) + actionpack (= 5.1.4) + actionview (= 5.1.4) + activejob (= 5.1.4) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.1.4) + actionview (= 5.1.4) + activesupport (= 5.1.4) + rack (~> 2.0) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.1.4) + activesupport (= 5.1.4) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + active_attr (0.10.2) + activemodel (>= 3.0.2, < 5.2) + activesupport (>= 3.0.2, < 5.2) + active_model_serializers (0.10.6) + actionpack (>= 4.1, < 6) + activemodel (>= 4.1, < 6) + case_transform (>= 0.2) + jsonapi-renderer (>= 0.1.1.beta1, < 0.2) + activejob (5.1.4) + activesupport (= 5.1.4) + globalid (>= 0.3.6) + activemodel (5.1.4) + activesupport (= 5.1.4) + activerecord (5.1.4) + activemodel (= 5.1.4) + activesupport (= 5.1.4) + arel (~> 8.0) + activesupport (5.1.4) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) + arel (8.0.0) + bcrypt (3.1.11) + builder (3.2.3) + cancancan (2.0.0) + capybara (2.15.2) + addressable + mini_mime (>= 0.1.3) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) + carrierwave (1.2.1) + activemodel (>= 4.0.0) + activesupport (>= 4.0.0) + mime-types (>= 1.16) + case_transform (0.2) + activesupport + coderay (1.1.2) + coffee-rails (4.2.2) + coffee-script (>= 2.2.0) + railties (>= 4.0.0) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.12.2) + concurrent-ruby (1.0.5) + crass (1.0.2) + database_cleaner (1.6.1) + devise (4.3.0) + bcrypt (~> 3.0) + orm_adapter (~> 0.1) + railties (>= 4.1.0, < 5.2) + responders + warden (~> 1.2.3) + devise-token_authenticatable (1.0.0) + devise (>= 4.0.0, < 4.4.0) + diff-lcs (1.3) + erubi (1.6.1) + exception_notification (4.2.2) + actionmailer (>= 4.0, < 6) + activesupport (>= 4.0, < 6) + execjs (2.7.0) + facets (3.1.0) + factory_girl (4.8.1) + activesupport (>= 3.0.0) + factory_girl_rails (4.8.0) + factory_girl (~> 4.8.0) + railties (>= 3.0.0) + ffi (1.9.18) + font-awesome-rails (4.7.0.2) + railties (>= 3.2, < 5.2) + globalid (0.4.0) + activesupport (>= 4.2.0) + i18n (0.8.6) + js-routes (1.4.2) + railties (>= 3.2) + sprockets-rails + jsonapi-renderer (0.1.3) + kaminari (1.0.1) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.0.1) + kaminari-activerecord (= 1.0.1) + kaminari-core (= 1.0.1) + kaminari-actionview (1.0.1) + actionview + kaminari-core (= 1.0.1) + kaminari-activerecord (1.0.1) + activerecord + kaminari-core (= 1.0.1) + kaminari-core (1.0.1) + loofah (2.1.1) + crass (~> 1.0.2) + nokogiri (>= 1.5.9) + mail (2.6.6) + mime-types (>= 1.16, < 4) + memoist (0.16.0) + method_source (0.9.0) + mime-types (3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0521) + mini_mime (0.1.4) + mini_portile2 (2.3.0) + minitest (5.10.3) + momentjs-rails (2.17.1) + railties (>= 3.1) + nio4r (2.1.0) + nokogiri (1.8.1) + mini_portile2 (~> 2.3.0) + orm_adapter (0.5.0) + pickadate-rails (3.5.5.0) + railties (>= 3.1.0) + polyamorous (1.3.1) + activerecord (>= 3.0) + pry (0.11.1) + coderay (~> 1.1.0) + method_source (~> 0.9.0) + pry-doc (0.11.1) + pry (~> 0.9) + yard (~> 0.9) + pry-rails (0.3.6) + pry (>= 0.10.4) + public_suffix (3.0.0) + rack (2.0.3) + rack-cors (1.0.1) + rack-test (0.7.0) + rack (>= 1.0, < 3) + rails (5.1.4) + actioncable (= 5.1.4) + actionmailer (= 5.1.4) + actionpack (= 5.1.4) + actionview (= 5.1.4) + activejob (= 5.1.4) + activemodel (= 5.1.4) + activerecord (= 5.1.4) + activesupport (= 5.1.4) + bundler (>= 1.3.0) + railties (= 5.1.4) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-html-sanitizer (1.0.3) + loofah (~> 2.0) + railties (5.1.4) + actionpack (= 5.1.4) + activesupport (= 5.1.4) + method_source + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + rake (12.1.0) + ransack (1.8.3) + actionpack (>= 3.0) + activerecord (>= 3.0) + activesupport (>= 3.0) + i18n + polyamorous (~> 1.3) + rb-fsevent (0.10.2) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + record_collection (0.10.4) + active_attr (>= 0.8) + activemodel (>= 4.1) + railties (>= 3.1) + redcarpet (3.4.0) + request_store (1.3.2) + responders (2.4.0) + actionpack (>= 4.2.0, < 5.3) + railties (>= 4.2.0, < 5.3) + rspec-core (3.6.0) + rspec-support (~> 3.6.0) + rspec-expectations (3.6.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.6.0) + rspec-mocks (3.6.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.6.0) + rspec-rails (3.6.1) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) + rspec-support (~> 3.6.0) + rspec-support (3.6.0) + sass (3.5.3) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sass-rails (5.0.7) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) + simple_form (3.5.0) + actionpack (> 4, < 5.2) + activemodel (> 4, < 5.2) + slim (3.0.8) + temple (>= 0.7.6, < 0.9) + tilt (>= 1.3.3, < 2.1) + slim-rails (3.1.2) + actionpack (>= 3.1) + railties (>= 3.1) + slim (~> 3.0) + spring (2.0.2) + activesupport (>= 4.2) + spring-commands-rspec (1.0.4) + spring (>= 0.9.1) + sprockets (3.7.1) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-rails (3.2.1) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + sqlite3 (1.3.13) + state_machines (0.5.0) + state_machines-activemodel (0.5.0) + activemodel (>= 4.1, < 5.2) + state_machines (>= 0.5.0) + state_machines-activerecord (0.5.0) + activerecord (>= 4.1, < 5.2) + state_machines-activemodel (>= 0.5.0) + temple (0.8.0) + thor (0.20.0) + thread_safe (0.3.6) + tilt (2.0.8) + tzinfo (1.2.3) + thread_safe (~> 0.1) + warden (1.2.7) + rack (>= 1.0) + websocket-driver (0.6.5) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) + xpath (2.1.0) + nokogiri (~> 1.3) + yard (0.9.9) + +PLATFORMS + ruby + +DEPENDENCIES + active_model_serializers + cancancan + capybara + coffee-rails + database_cleaner + dunlop-core! + dunlop-ember! + factory_girl_rails + kaminari + pry-doc + pry-rails + rack-cors + rspec-rails + slim-rails + spring + spring-commands-rspec + sqlite3 + +BUNDLED WITH + 1.16.0 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9912c5b --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +Copyright 2016 Companytools diff --git a/README.md b/README.md new file mode 100644 index 0000000..57cf0f5 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# Dunlop::Ember +This gem adds some context and support for connection ember applications +## baic assumptions (for now (infrastructure is growing)) +* The model structure is non namespaced with `api` +* The controllers have namespace `Api`, not versioned (controller level) api (yet): + ```ruby + namespace :api, path: 'api/v1' do + # ...api controller definitions... + end + ``` +* The inheritance column for STI models is `sti_type` in stead of the + inconvenient chosen `type`: + ```ruby + class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + self.inheritance_column = :sti_type + end + ``` +* There is a UserSerializer in the main_app, can be same as `Api::User` + serializer `app/serializers/user_serializer.rb`: + ```ruby + class UserSerializer < Api::UserSerializer + # attribute :email + # attribute :nickname + end + ``` + + +## Installation +Add this line to your application's Gemfile: + +```ruby +gem 'dunlop-ember' +``` + +And then execute: +```bash +$ bundle +``` + +Add the engine to your routes file +```ruby + mount Dunlop::Ember::Engine => '/app' +``` diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..78952ef --- /dev/null +++ b/Rakefile @@ -0,0 +1,26 @@ +begin + require 'bundler/setup' +rescue LoadError + puts 'You must `gem install bundler` and `bundle install` to run rake tasks' +end + +require 'rdoc/task' + +RDoc::Task.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'Dunlop::Ember' + rdoc.options << '--line-numbers' + rdoc.rdoc_files.include('README.md') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__) +load 'rails/tasks/engine.rake' + + +load 'rails/tasks/statistics.rake' + + + +require 'bundler/gem_tasks' + diff --git a/app/assets/config/dunlop_ember_manifest.js b/app/assets/config/dunlop_ember_manifest.js new file mode 100644 index 0000000..57a6fd1 --- /dev/null +++ b/app/assets/config/dunlop_ember_manifest.js @@ -0,0 +1,2 @@ +//= link_directory ../javascripts/dunlop/ember .js +//= link_directory ../stylesheets/dunlop/ember .css diff --git a/app/assets/images/dunlop/ember/.keep b/app/assets/images/dunlop/ember/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/assets/javascripts/dunlop/ember/application.js b/app/assets/javascripts/dunlop/ember/application.js new file mode 100644 index 0000000..e54c646 --- /dev/null +++ b/app/assets/javascripts/dunlop/ember/application.js @@ -0,0 +1,13 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// +//= require_tree . diff --git a/app/assets/stylesheets/dunlop/ember/application.css b/app/assets/stylesheets/dunlop/ember/application.css new file mode 100644 index 0000000..0ebd7fe --- /dev/null +++ b/app/assets/stylesheets/dunlop/ember/application.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/app/controllers/dunlop/ember/application_controller.rb b/app/controllers/dunlop/ember/application_controller.rb new file mode 100644 index 0000000..2f222db --- /dev/null +++ b/app/controllers/dunlop/ember/application_controller.rb @@ -0,0 +1,54 @@ +module Dunlop + module Ember + class ApplicationController < ActionController::Base + protect_from_forgery with: :exception + before_action :authenticate_user_from_token! + before_action :authenticate_user! + + def ember_path + app_globals = {} + if adapter_name = params[:adapter].to_s.scan(/\w+/).first.presence + app_index = Rails.root.join("public/adapters/#{adapter_name}/app/index.html") + return render text: 'unauthorized, no adapter engine', status: 403 unless adapter = Panda.adapters[adapter_name] + #app_globals = adapter.settings # load through request instead. More stable then changing environments + unless Rails.application.config.try(:loose_ember_app_authorization) + return render text: 'unauthorized, no adapter engine', status: 403 unless current_user.admin? or authorize!(:read, adapter.engine) + end + else + app_index = Rails.root.join('public/app/index.html') + end + Rails.logger.info "Loading ember app for path '#/#{params[:rest]}' adapter: '#{params[:adapter].presence || 'main_app'}'" + if app_index.exist? + index_html = app_index.read.to_s + app_globals[:flash] = flash.to_h if flash.present? + index_html.sub! 'app_globals={}', "app_globals=#{app_globals.to_json}" if app_globals.present? + render html: index_html.html_safe + else + render html: "App file #{app_index} not found".html_safe + end + end + + rescue_from CanCan::AccessDenied do + if request.format.json? + head 403 + else + render 'application/403', status: 403 + end + end + + private + + def authenticate_user_from_token! + # https://romulomachado.github.io/2015/09/28/using-ember-simple-auth-with-devise.html + authenticate_with_http_token do |token, options| + user_email = options[:email].presence + user = user_email && User.find_by_email(user_email) + + if user && Devise.secure_compare(user.authentication_token, token) + sign_in user, store: false + end + end + end + end + end +end diff --git a/app/controllers/dunlop/ember/sessions_controller.rb b/app/controllers/dunlop/ember/sessions_controller.rb new file mode 100644 index 0000000..7c3aeae --- /dev/null +++ b/app/controllers/dunlop/ember/sessions_controller.rb @@ -0,0 +1,27 @@ +class Dunlop::Ember::SessionsController < Devise::SessionsController + #respond_to :json + skip_before_action :verify_authenticity_token, if: :json_request?, raise: false + + #https://www.devmynd.com/blog/2014-7-rails-ember-js-with-the-ember-cli-redux-part-1-the-api-and-cms-with-ruby-on-rails/ + def create + if json_request? + super do |user| + data = { + token: user.authentication_token, + email: user.email, + user_id: user.id + } + render json: data, status: 201 and return + end + else + super + end + end + + protected + + def json_request? + request.format.json? + end +end + diff --git a/app/controllers/dunlop/ember/users_controller.rb b/app/controllers/dunlop/ember/users_controller.rb new file mode 100644 index 0000000..c7a9e04 --- /dev/null +++ b/app/controllers/dunlop/ember/users_controller.rb @@ -0,0 +1,16 @@ +module Dunlop::Ember + class UsersController < Dunlop::Ember::ApplicationController + def me + if respond_to?(:current_user) and current_user.present? + user = current_user + sign_out user # no more current_user, needed to allow sign_in using store: true to work (devise v4.3.0) + sign_in user, store: true # create session if not exist, only needed for hybrid rails/api integrations like ePOTS. Otherwise api only without store is sufficient + #sign_in current_user, store: true # create session if not exist, only needed for hybrid rails/api integrations like ePOTS. Otherwise api only without store is sufficient + #warden.set_user current_user, store: true + render json: current_user, serializer: UserSerializer + else + head 404 + end + end + end +end diff --git a/app/helpers/dunlop/ember/application_helper.rb b/app/helpers/dunlop/ember/application_helper.rb new file mode 100644 index 0000000..d15ac06 --- /dev/null +++ b/app/helpers/dunlop/ember/application_helper.rb @@ -0,0 +1,6 @@ +module Dunlop + module Ember + module ApplicationHelper + end + end +end diff --git a/app/jobs/dunlop/ember/application_job.rb b/app/jobs/dunlop/ember/application_job.rb new file mode 100644 index 0000000..8d7423a --- /dev/null +++ b/app/jobs/dunlop/ember/application_job.rb @@ -0,0 +1,6 @@ +module Dunlop + module Ember + class ApplicationJob < ActiveJob::Base + end + end +end diff --git a/app/mailers/dunlop/ember/application_mailer.rb b/app/mailers/dunlop/ember/application_mailer.rb new file mode 100644 index 0000000..a32cab7 --- /dev/null +++ b/app/mailers/dunlop/ember/application_mailer.rb @@ -0,0 +1,8 @@ +module Dunlop + module Ember + class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' + end + end +end diff --git a/app/services/flat_keys.rb b/app/services/flat_keys.rb new file mode 100644 index 0000000..1b91bb1 --- /dev/null +++ b/app/services/flat_keys.rb @@ -0,0 +1,51 @@ +# Transform a nested tructure indicated by dots to a structure as used by the ActiveRecord includes statement +# - dslams +# - ppls.distribution_cables +# => +# [:dslams, {ppls: :distribution_cables}] +class FlatKeys + class << self + + def as_nested_structure(ary) + return ary unless ary.first.is_a?(String) + nested_keys, flat_keys = ary.partition{|spec| spec['.']} + result = flat_keys.map(&:to_sym) + if nested_keys.any? + obj = {} + nested_keys.map{ |key| key.split('.').map(&:to_sym) }.each do |parts| + traverse_nest_structure(parts, obj) + end + result.push obj + end + result + end + + def traverse_nest_structure(parts, obj) + key = parts.shift + if parts.size == 1 + case obj[key] + when nil then obj[key] = parts[0] + when Array then obj[key] |= [parts[0]] + when Hash + raise "Colliding keys for nesting of #{key} -> #{parts[0]}" unless obj[key].has_key?(parts[0]) + else # expect symbol + obj[key] = Array.wrap(obj[key]).push parts[0] unless obj[key] == parts[0] # no duplicate + end + else # parts.size > 2 + case obj[key] + when nil + obj[key] = {} + traverse_nest_structure(parts, obj[key]) + when Array + raise "Cannot traverse #{key} -> #{parts.join('.')} because existing array value #{obj[key].inspect}" + when Hash + traverse_nest_structure(parts, obj[key]) + else # expect symbol + raise "Cannot add deeper nesting for endpoing with different name #{key} -> #{obj[key]} => #{parts.join('.')}" unless obj[key] = parts[0] # same name, allows deeper nesting + obj[key] = {obj[key] => nil} # prepare for nesting + traverse_nest_structure(parts, obj[key]) + end + end + end + end +end diff --git a/app/views/dunlop/ember/application/ember_path.html.slim b/app/views/dunlop/ember/application/ember_path.html.slim new file mode 100644 index 0000000..a12ef33 --- /dev/null +++ b/app/views/dunlop/ember/application/ember_path.html.slim @@ -0,0 +1,4 @@ +h3 + span Could not find + tt= '/app/index.html' + span file diff --git a/app/views/layouts/dunlop/ember/application.html.erb b/app/views/layouts/dunlop/ember/application.html.erb new file mode 100644 index 0000000..880df0d --- /dev/null +++ b/app/views/layouts/dunlop/ember/application.html.erb @@ -0,0 +1,14 @@ + + + + Dunlop ember + <%= stylesheet_link_tag "dunlop/ember/application", media: "all" %> + <%= javascript_include_tag "dunlop/ember/application" %> + <%= csrf_meta_tags %> + + + +<%= yield %> + + + diff --git a/bin/rails b/bin/rails new file mode 100755 index 0000000..a4bf0ba --- /dev/null +++ b/bin/rails @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby +# This command will automatically be run when you run "rails" with Rails gems +# installed from the root of your application. + +ENGINE_ROOT = File.expand_path('../..', __FILE__) +ENGINE_PATH = File.expand_path('../../lib/dunlop/ember/engine', __FILE__) + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) + +require 'rails/all' +require 'rails/engine/commands' diff --git a/config/initializers/active_model_serializers_config.rb b/config/initializers/active_model_serializers_config.rb new file mode 100644 index 0000000..c622aef --- /dev/null +++ b/config/initializers/active_model_serializers_config.rb @@ -0,0 +1,7 @@ +if defined?(ActiveModelSerializers) + ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes` + #ActiveModelSerializers.config.key_transform = :unaltered # explicitly set because json api moves to dash + ActiveModelSerializers.config.jsonapi_pagination_links_enabled = false # manual meta setting + ActiveModelSerializers.config.jsonapi_namespace_separator = '/' # Default: `:attributes` + ActiveModelSerializers.config.jsonapi_resource_type = :singular +end diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..ee73810 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,8 @@ +Dunlop::Ember::Engine.routes.draw do + get 'users/me' + devise_scope :user do + post 'users/sign_in', to: 'sessions#create' + end + root to: 'application#ember_path' + get '*rest', controller: 'application', action: 'ember_path' +end diff --git a/config/spring.rb b/config/spring.rb new file mode 100644 index 0000000..6b95e4d --- /dev/null +++ b/config/spring.rb @@ -0,0 +1 @@ +Spring.application_root = './spec/dummy' diff --git a/dunlop-ember.gemspec b/dunlop-ember.gemspec new file mode 100644 index 0000000..2b74499 --- /dev/null +++ b/dunlop-ember.gemspec @@ -0,0 +1,29 @@ +$:.push File.expand_path("../lib", __FILE__) + +# Maintain your gem's version: +require "dunlop/ember/version" + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = "dunlop-ember" + s.version = Dunlop::Ember::VERSION + s.authors = ["Companytools"] + s.email = ["develop@companytools.nl"] + s.homepage = "http://companytools.nl" + s.summary = "Summary of Dunlop::Ember." + s.description = "Description of Dunlop::Ember." + s.license = "COMPANYTOOLS" + + s.files = Dir["{app,config,db,lib}/**/*", "LICENSE", "Rakefile", "README.md"] + s.test_files = Dir["spec/**/*"] + + s.add_dependency "rails", ">= 4.2.0" + s.add_dependency "ransack" + s.add_dependency "devise" + s.add_dependency "active_model_serializers" + + s.add_development_dependency "sqlite3" + s.add_development_dependency 'rspec-rails' + s.add_development_dependency 'capybara' + s.add_development_dependency 'factory_girl_rails' +end diff --git a/lib/dunlop/ember.rb b/lib/dunlop/ember.rb new file mode 100644 index 0000000..1f9d5ac --- /dev/null +++ b/lib/dunlop/ember.rb @@ -0,0 +1,9 @@ +require "dunlop/ember/engine" +require "dunlop/ember/cancan_safeguard" +require "dunlop/ember/api_base_controller" + +module Dunlop + module Ember + # Your code goes here... + end +end diff --git a/lib/dunlop/ember/api_base_controller.rb b/lib/dunlop/ember/api_base_controller.rb new file mode 100644 index 0000000..bc6193a --- /dev/null +++ b/lib/dunlop/ember/api_base_controller.rb @@ -0,0 +1,252 @@ +module Dunlop::Ember::ApiBaseController + extend ActiveSupport::Concern + + included do + before_action :authenticate_user_from_token! + before_action :authenticate_user! + before_action :set_current_user + before_action :find_record, only: %i[show update destroy] + respond_to :json + rescue_from CanCan::AccessDenied do + render json: {errors: [{status: 403, detail: 'unauthorized'}]}, status: 403 + end + end + + def index + index_safeguard record_class + result = record_scope.search(query).result + result = result.includes(*FlatKeys.as_nested_structure(index_include_relations)) if index_include_relations.present? + result = result.uniq if params[:uniq].present? + if index_pagination? + paginated = result.page(params[:page]).per(params[:per_page]) + render json: paginated, meta: {total_pages: paginated.total_pages, total_count: paginated.total_count} + else + render json: result + end + end + + def show + show_safeguard @record + render json: @record + end + + def create + @record ||= record_class.new(record_params) + create_safeguard @record + return if performed? + if @record.save + render json: @record + else + render json: {errors: @record.errors.map{|attr, errors| {attr => errors.to_s}}}, status: :unprocessable_entity + end + end + + def update + update_safeguard @record + return if performed? + @record.update_attributes record_params + render json: @record + end + + def destroy + destroy_safeguard @record + @record.destroy + head :no_content + end + + private + + # used to include relations on database lookup + # has form: + # ['comments.replies', 'authors.orginizations'] + # + def include_relations + return [] unless includes = params[:include].presence + includes.split(',') + end + + %i[index show].each do |action| + define_method "#{action}_include_relations" do + include_relations + end + end + + def publish(event, data = {}) + return + data = data.merge(event: event) + data[:client_id] = request.headers['HTTP_CLIENT_ID'] if request.present? + FayeRails.client(nil).publish('/activity', data) + end + + def index_safeguard(model) + # can be overloaded + end + + def show_safeguard(record) + # can be overloaded + record_safeguard(record) + end + + def create_safeguard(record) + # can be overloaded + record_safeguard(record) + end + + def update_safeguard(record) + # can be overloaded + record_safeguard(record) + end + + def destroy_safeguard(record) + # can be overloaded + record_safeguard(record) + end + + def record_safeguard(record) + # can be overloaded + return + render json: {}, status: :unauthorized unless authorized_ids(record).include?(current_user.id) + end + + def authorized_ids(*) + # can be overloaded + [] + end + + def authenticate_user_from_token! + # https://romulomachado.github.io/2015/09/28/using-ember-simple-auth-with-devise.html + authenticate_with_http_token do |token, options| + user_email = options[:email].presence + user = user_email && User.find_by_email(user_email) + + if user && Devise.secure_compare(user.authentication_token, token) + sign_in user, store: false + end + end + end + + public def record_class + deducted_class_name = controller_path.sub(/api\//,'').classify + deducted_class_name.safe_constantize || deducted_class_name.demodulize.constantize + end + + # basic scope for find and index. Can be overloaded to add extra security restrictions + def record_scope + record_class.all + end + + def find_record(options = {}) + scope = record_scope + scope = scope.includes(*Array.wrap(options[:includes])) if options[:includes] + @record = scope.find(params[:id]) + end + + def record_params + #params.require(controller_path.sub(/^api\//, '').singularize).permit(*permitted_params) + params.require(controller_path.sub(/api\//, '').singularize).permit(*permitted_params) + end + + def permitted_params + [] + end + + # used for serialization + def serialize_options + return {} unless includes = params[:include].presence + {include: includes.split(',')} + end + + # map action specific serialize options to general serialize options + %i[index show create update].each do |action| + define_method "#{action}_serialize_options" do + serialize_options + end + end + + # /(_eq|_eq_any|_eq_all|_not_eq|_not_eq_any|_not_eq_all|_matches|_matches_any|_matches_all| + # _does_not_match|_does_not_match_any|_does_not_match_all|_lt|_lt_any|_lt_all|_lteq|_lteq_any| + # _lteq_all|_gt|_gt_any|_gt_all|_gteq|_gteq_any|_gteq_all|_in|_in_any|_in_all|_not_in|_not_in_any| + # _not_in_all|_cont|_cont_any|_cont_all|_i_cont|_i_cont_any|_i_cont_all|_not_cont|_not_cont_any| + # _not_cont_all|_i_not_cont|_i_not_cont_any|_i_not_cont_all|_start|_start_any|_start_all|_not_start| + # _not_start_any|_not_start_all|_end|_end_any|_end_all|_not_end|_not_end_any|_not_end_all| + # _true|_not_true|_false|_not_false|_present|_blank|_null|_not_null)$/ + PREDICATE_END_MATCHER = Regexp.new("(_#{Ransack.predicates.keys.join('|_')})$") + + #TODO: make me pretty, specs are in place + def query + mapped_query = {} + (params[:q] || {}).each do |key, value| + key = "#{key}_eq" unless %w[s sorts].include?(key) or record_class.try(:ransackable_scopes).try(:include?, key.to_sym) or key =~ PREDICATE_END_MATCHER # expect equality when no predicate is given + if key =~ /^type_/ + # map type to sti_type + mapped_value = case value + when Array then value.map{|v| "#{record_class.name}::#{v.to_s.gsub('-', '_').classify}"} + else + "#{record_class.name}::#{value.to_s.gsub('-', '_').classify}" + end + mapped_query["sti_#{key}"] = mapped_value + else + mapped_query[key] = value + end + end + mapped_query + end + + def _render_with_renderer_json(resource, options = {}) + return resource.to_json if resource.is_a?(Hash) + return resource if resource.is_a?(String) + + action_serialize_options_method = :"#{action_name}_serialize_options" + #action_serialize_options = respond_to?(action_serialize_options_method) ? __send__(action_serialize_options_method) : {} + action_serialize_options = __send__(action_serialize_options_method) rescue {} + action_serialize_options[:include] = options[:include] if options.has_key?(:include) + action_serialize_options[:meta] = options[:meta] if options.has_key?(:meta) + + serializer_class = if options.has_key?(:serializer) + options.delete(:serializer) + elsif action_serialize_options[:serializer].present? + action_serialize_options[:serializer] + elsif resource.is_a?(ActiveRecord::Base) + #infer based on controller path replacing actual controller part with resouce part /workflow_step_definitions/:id/table + "#{self.class.name.deconstantize}::#{resource.class.name.demodulize}Serializer".constantize + elsif resource.is_a?(Array) and resource.first.is_a?(ActiveRecord::Base) + # infer based in first element of collection + "#{self.class.name.deconstantize}::#{resource.first.class.name.demodulize}Serializer".constantize + else + # infer based on controller path + "#{controller_path.underscore.classify}Serializer".constantize + end + + serializer = if resource.respond_to?(:each) + ActiveModel::Serializer::CollectionSerializer.new(resource, action_serialize_options.merge(serializer: serializer_class)) #TODO: fix index includes + else + serializer_class.new(resource, action_serialize_options) + end + adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, action_serialize_options) + res = {} + time = Benchmark.ms { res = adapter.as_json } + logger.info "Generated serialized output in #{time.round} ms" + #binding.pry + #return res + return res.is_a?(String) ? res : res.to_json + end + + def has_permission_of_type(type, user = current_user) + Dunlop::Ability.has_permission_of_type(type, user) + end + + def any_permission_starts_with(expression, user = current_user) + Dunlop::Ability.any_permission_starts_with(expression, user) + end + + def index_pagination? + true + end + + def set_current_user + request.env["exception_notifier.exception_data"] = { + current_user: current_user.try(:inspect) + } + User.current = current_user + end +end diff --git a/lib/dunlop/ember/cancan_safeguard.rb b/lib/dunlop/ember/cancan_safeguard.rb new file mode 100644 index 0000000..8744767 --- /dev/null +++ b/lib/dunlop/ember/cancan_safeguard.rb @@ -0,0 +1,22 @@ +module Dunlop::Ember::CanCanSafeguard + def index_safeguard(model) + authorize! :index, model + end + + def show_safeguard(record) + authorize! :show, record + end + + def create_safeguard(record) + authorize! :create, record + end + + def update_safeguard(record) + authorize! :update, record + end + + def destroy_safeguard(record) + authorize! :destroy, record + end + +end diff --git a/lib/dunlop/ember/engine.rb b/lib/dunlop/ember/engine.rb new file mode 100644 index 0000000..10ce68d --- /dev/null +++ b/lib/dunlop/ember/engine.rb @@ -0,0 +1,14 @@ +require 'devise' +require 'ransack' +require 'active_model_serializers' + +module Dunlop + module Ember + class Engine < ::Rails::Engine + isolate_namespace Dunlop::Ember + config.generators do |g| + g.test_framework :rspec + end + end + end +end diff --git a/lib/dunlop/ember/version.rb b/lib/dunlop/ember/version.rb new file mode 100644 index 0000000..7c54301 --- /dev/null +++ b/lib/dunlop/ember/version.rb @@ -0,0 +1,5 @@ +module Dunlop + module Ember + VERSION = '0.1.0' + end +end diff --git a/lib/generators/dunlop/ember/install/USAGE b/lib/generators/dunlop/ember/install/USAGE new file mode 100644 index 0000000..c9fb09f --- /dev/null +++ b/lib/generators/dunlop/ember/install/USAGE @@ -0,0 +1,8 @@ +Description: + Explain the generator + +Example: + rails generate install Thing + + This will create: + what/will/it/create diff --git a/lib/generators/dunlop/ember/install/install_generator.rb b/lib/generators/dunlop/ember/install/install_generator.rb new file mode 100644 index 0000000..8448b96 --- /dev/null +++ b/lib/generators/dunlop/ember/install/install_generator.rb @@ -0,0 +1,10 @@ +class Dunlop::Ember::InstallGenerator < Rails::Generators::Base + source_root File.expand_path('../templates', __FILE__) + + def add_initializers + directory 'initializers', 'config/initializers' + end + def upgrade_devise + gsub_file Rails.root.join('config/routes.rb'), /devise_for :users$/, "devise_for :users, controllers: {sessions: 'dunlop/ember/sessions'}" + end +end diff --git a/lib/generators/dunlop/ember/install/templates/initializers/active_model_serializers_config.rb b/lib/generators/dunlop/ember/install/templates/initializers/active_model_serializers_config.rb new file mode 100644 index 0000000..49959ca --- /dev/null +++ b/lib/generators/dunlop/ember/install/templates/initializers/active_model_serializers_config.rb @@ -0,0 +1,5 @@ +ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes` +#ActiveModelSerializers.config.key_transform = :unaltered # explicitly set because json api moves to dash +ActiveModelSerializers.config.jsonapi_pagination_links_enabled = false # manual meta setting +ActiveModelSerializers.config.jsonapi_namespace_separator = '/' # Default: `:attributes` +ActiveModelSerializers.config.jsonapi_resource_type = :singular diff --git a/lib/generators/dunlop/ember/install/templates/initializers/jsonapi_mime_type.rb b/lib/generators/dunlop/ember/install/templates/initializers/jsonapi_mime_type.rb new file mode 100644 index 0000000..980ff91 --- /dev/null +++ b/lib/generators/dunlop/ember/install/templates/initializers/jsonapi_mime_type.rb @@ -0,0 +1,8 @@ +api_mime_types = %W[ + application/vnd.api+json + text/x-json + application/json +] + +Mime::Type.unregister :json +Mime::Type.register 'application/json', :json, api_mime_types diff --git a/lib/tasks/dunlop/ember_tasks.rake b/lib/tasks/dunlop/ember_tasks.rake new file mode 100644 index 0000000..d2f84d8 --- /dev/null +++ b/lib/tasks/dunlop/ember_tasks.rake @@ -0,0 +1,4 @@ +# desc "Explaining what the task does" +# task :dunlop_ember do +# # Task goes here +# end diff --git a/spec/controllers/api/posts_controller/index_spec.rb b/spec/controllers/api/posts_controller/index_spec.rb new file mode 100644 index 0000000..a9294c4 --- /dev/null +++ b/spec/controllers/api/posts_controller/index_spec.rb @@ -0,0 +1,110 @@ +require 'rails_helper' + +RSpec.describe Api::PostsController, type: :controller do + describe "index" do + describe 'unauthorized' do + it 'redirects to login page' do + get :index + response.redirect_url.should end_with('/users/sign_in') + end + end + + describe 'authorized' do + before { sign_in create :user } + it "no default include, default pagination" do + post1 = create :post, title: 'post-1', body: 'post-body-1' + comment = create :comment, post: post1 + post2 = create :post, title: 'post-2', body: 'post-body-2' + get :index + assert_json( + data: [ + { + id: post1.id.to_s, + type: "post", + attributes: {"title"=>"post-1", "body"=>"post-body-1"}, + relationships: { + comments: {data: [{id: comment.id.to_s , type: "comment" }]} + } + }, + { + id: post2.id.to_s, + type: "post", + attributes: {"title"=>"post-2", "body"=>"post-body-2"}, + relationships: { + comments: {data: []} + } + }, + ], + meta: {"total-pages"=>1, "total-count"=>2} + ) + end + + it "does not paginate when index_pagination? is false" do + expect( subject ).to receive(:index_pagination?).and_return false + post1 = create :post, title: 'post-1', body: 'post-body-1' + comment = create :comment, post: post1 + post2 = create :post, title: 'post-2', body: 'post-body-2' + get :index + assert_json( + data: [ + { + id: post1.id.to_s, + type: "post", + attributes: {"title"=>"post-1", "body"=>"post-body-1"}, + relationships: { + comments: {data: [{id: comment.id.to_s , type: "comment" }]} + } + }, + { + id: post2.id.to_s, + type: "post", + attributes: {"title"=>"post-2", "body"=>"post-body-2"}, + relationships: { + comments: {data: []} + } + }, + ] + ) + end + + it "includes associations if configured" do + expect( subject ).to receive(:index_serialize_options).and_return(include: 'comments') + post1 = create :post, title: 'post-1', body: 'post-body-1' + comment = create :comment, post: post1, body: 'post-1-comment-1' + post2 = create :post, title: 'post-2', body: 'post-body-2' + get :index + assert_json( + data: [ + { + id: post1.id.to_s, + type: "post", + attributes: {"title"=>"post-1", "body"=>"post-body-1"}, + relationships: { + comments: {data: [{id: comment.id.to_s , type: "comment" }]} + } + }, + { + id: post2.id.to_s, + type: "post", + attributes: {"title"=>"post-2", "body"=>"post-body-2"}, + relationships: { + comments: {data: []} + } + }, + ], + included: [ + { + id: comment.id.to_s, + type: "comment", + attributes: {body: "post-1-comment-1"}, + relationships: { + post: {data: {id: post1.id.to_s, type: "posts"}} + } + } + ], + meta: {"total-pages"=>1, "total-count"=>2} + ) + end + end + end +end diff --git a/spec/controllers/api/posts_controller/update_spec.rb b/spec/controllers/api/posts_controller/update_spec.rb new file mode 100644 index 0000000..fc5a259 --- /dev/null +++ b/spec/controllers/api/posts_controller/update_spec.rb @@ -0,0 +1,42 @@ +require 'rails_helper' + +RSpec.describe Api::PostsController, type: :controller do + describe "update" do + describe 'unauthorized' do + it 'redirects to login page' do + patch :update, params: {id: 9} + response.redirect_url.should end_with('/users/sign_in') + end + end + + describe 'authorized' do + before { sign_in create :user } + it "includes associations if configured" do + expect( subject ).to receive(:update_serialize_options).and_return(include: 'comments') + post1 = create :post, title: 'post-1', body: 'post-body-1' + comment = create :comment, post: post1, body: 'post-1-comment-1' + patch :update, params: {id: post1.id, post: {title: 'post-1-updated', body: 'post-body-1-updated'}} + assert_json( + data: { + id: post1.id.to_s, + type: "post", + attributes: {"title"=>"post-1", "body"=>"post-body-1-updated"}, # title not permitted, could be boolean admin + relationships: { + comments: {data: [{id: comment.id.to_s , type: "comment" }]} + } + }, + included: [ + { + id: comment.id.to_s, + type: "comment", + attributes: {body: "post-1-comment-1"}, + relationships: { + post: {data: {id: post1.id.to_s, type: "posts"}} + } + } + ] + ) + end + end + end +end diff --git a/spec/dummy/Rakefile b/spec/dummy/Rakefile new file mode 100644 index 0000000..e85f913 --- /dev/null +++ b/spec/dummy/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative 'config/application' + +Rails.application.load_tasks diff --git a/spec/dummy/app/assets/config/manifest.js b/spec/dummy/app/assets/config/manifest.js new file mode 100644 index 0000000..e065e1f --- /dev/null +++ b/spec/dummy/app/assets/config/manifest.js @@ -0,0 +1,5 @@ + +//= link_tree ../images +//= link_directory ../javascripts .js +//= link_directory ../stylesheets .css +//= link dunlop_ember_manifest.js diff --git a/spec/dummy/app/assets/images/.keep b/spec/dummy/app/assets/images/.keep new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/app/assets/javascripts/application.js b/spec/dummy/app/assets/javascripts/application.js new file mode 100644 index 0000000..e54c646 --- /dev/null +++ b/spec/dummy/app/assets/javascripts/application.js @@ -0,0 +1,13 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// +//= require_tree . diff --git a/spec/dummy/app/assets/javascripts/cable.js b/spec/dummy/app/assets/javascripts/cable.js new file mode 100644 index 0000000..71ee1e6 --- /dev/null +++ b/spec/dummy/app/assets/javascripts/cable.js @@ -0,0 +1,13 @@ +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the rails generate channel command. +// +//= require action_cable +//= require_self +//= require_tree ./channels + +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); diff --git a/spec/dummy/app/assets/javascripts/channels/.keep b/spec/dummy/app/assets/javascripts/channels/.keep new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/app/assets/javascripts/comments.coffee b/spec/dummy/app/assets/javascripts/comments.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/spec/dummy/app/assets/javascripts/comments.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/spec/dummy/app/assets/stylesheets/application.css b/spec/dummy/app/assets/stylesheets/application.css new file mode 100644 index 0000000..0ebd7fe --- /dev/null +++ b/spec/dummy/app/assets/stylesheets/application.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/spec/dummy/app/channels/application_cable/channel.rb b/spec/dummy/app/channels/application_cable/channel.rb new file mode 100644 index 0000000..d672697 --- /dev/null +++ b/spec/dummy/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/spec/dummy/app/channels/application_cable/connection.rb b/spec/dummy/app/channels/application_cable/connection.rb new file mode 100644 index 0000000..0ff5442 --- /dev/null +++ b/spec/dummy/app/channels/application_cable/connection.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/spec/dummy/app/controllers/api/base_controller.rb b/spec/dummy/app/controllers/api/base_controller.rb new file mode 100644 index 0000000..0eeb269 --- /dev/null +++ b/spec/dummy/app/controllers/api/base_controller.rb @@ -0,0 +1,5 @@ +module Api + class BaseController < ActionController::Base + include Dunlop::Ember::ApiBaseController + end +end diff --git a/spec/dummy/app/controllers/api/posts_controller.rb b/spec/dummy/app/controllers/api/posts_controller.rb new file mode 100644 index 0000000..4b577b8 --- /dev/null +++ b/spec/dummy/app/controllers/api/posts_controller.rb @@ -0,0 +1,7 @@ +module Api + class PostsController < Api::BaseController + def permitted_params + [:body] # title not permitted + end + end +end diff --git a/spec/dummy/app/controllers/application_controller.rb b/spec/dummy/app/controllers/application_controller.rb new file mode 100644 index 0000000..8f5c146 --- /dev/null +++ b/spec/dummy/app/controllers/application_controller.rb @@ -0,0 +1,32 @@ +class ApplicationController < ActionController::Base + # Prevent CSRF attacks by raising an exception. + # For APIs, you may want to use :null_session instead. + protect_from_forgery with: :exception + + before_action :authenticate_user! + + #http://rails-bestpractices.com/posts/2010/08/23/fetch-current-user-in-models + before_action :set_current_user + + rescue_from CanCan::AccessDenied do + render 'application/403', status: 403 + end + + + private + + def set_current_user + request.env["exception_notifier.exception_data"] = { + current_user: current_user.try(:inspect) + } + User.current = current_user + end + + # Return an array of ids given as a param or nil if nothing is supplied + def ids_param + return nil unless ids = params[:ids].presence + @ids_param ||= (ids.is_a?(String) ? ids.split(RecordCollection.ids_separator) : Array.wrap(ids)) + end + helper_method :ids_param + +end diff --git a/spec/dummy/app/controllers/comments_controller.rb b/spec/dummy/app/controllers/comments_controller.rb new file mode 100644 index 0000000..2cfbf95 --- /dev/null +++ b/spec/dummy/app/controllers/comments_controller.rb @@ -0,0 +1,58 @@ +class CommentsController < ApplicationController + before_action :set_comment, only: [:show, :edit, :update, :destroy] + + # GET /comments + def index + @comments = Comment.all + end + + # GET /comments/1 + def show + end + + # GET /comments/new + def new + @comment = Comment.new + end + + # GET /comments/1/edit + def edit + end + + # POST /comments + def create + @comment = Comment.new(comment_params) + + if @comment.save + redirect_to @comment, notice: 'Comment was successfully created.' + else + render :new + end + end + + # PATCH/PUT /comments/1 + def update + if @comment.update(comment_params) + redirect_to @comment, notice: 'Comment was successfully updated.' + else + render :edit + end + end + + # DELETE /comments/1 + def destroy + @comment.destroy + redirect_to comments_url, notice: 'Comment was successfully destroyed.' + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_comment + @comment = Comment.find(params[:id]) + end + + # Only allow a trusted parameter "white list" through. + def comment_params + params.require(:comment).permit(:user_id, :post_id, :body) + end +end diff --git a/spec/dummy/app/controllers/concerns/.keep b/spec/dummy/app/controllers/concerns/.keep new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/app/controllers/posts_controller.rb b/spec/dummy/app/controllers/posts_controller.rb new file mode 100644 index 0000000..a9af96b --- /dev/null +++ b/spec/dummy/app/controllers/posts_controller.rb @@ -0,0 +1,58 @@ +class PostsController < ApplicationController + before_action :set_post, only: [:show, :edit, :update, :destroy] + + # GET /posts + def index + @posts = Post.all + end + + # GET /posts/1 + def show + end + + # GET /posts/new + def new + @post = Post.new + end + + # GET /posts/1/edit + def edit + end + + # POST /posts + def create + @post = Post.new(post_params) + + if @post.save + redirect_to @post, notice: 'Post was successfully created.' + else + render :new + end + end + + # PATCH/PUT /posts/1 + def update + if @post.update(post_params) + redirect_to @post, notice: 'Post was successfully updated.' + else + render :edit + end + end + + # DELETE /posts/1 + def destroy + @post.destroy + redirect_to posts_url, notice: 'Post was successfully destroyed.' + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_post + @post = Post.find(params[:id]) + end + + # Only allow a trusted parameter "white list" through. + def post_params + params.require(:post).permit(:title, :body) + end +end diff --git a/spec/dummy/app/helpers/application_helper.rb b/spec/dummy/app/helpers/application_helper.rb new file mode 100644 index 0000000..de6be79 --- /dev/null +++ b/spec/dummy/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/spec/dummy/app/helpers/comments_helper.rb b/spec/dummy/app/helpers/comments_helper.rb new file mode 100644 index 0000000..0ec9ca5 --- /dev/null +++ b/spec/dummy/app/helpers/comments_helper.rb @@ -0,0 +1,2 @@ +module CommentsHelper +end diff --git a/spec/dummy/app/helpers/posts_helper.rb b/spec/dummy/app/helpers/posts_helper.rb new file mode 100644 index 0000000..a7b8cec --- /dev/null +++ b/spec/dummy/app/helpers/posts_helper.rb @@ -0,0 +1,2 @@ +module PostsHelper +end diff --git a/spec/dummy/app/jobs/application_job.rb b/spec/dummy/app/jobs/application_job.rb new file mode 100644 index 0000000..a009ace --- /dev/null +++ b/spec/dummy/app/jobs/application_job.rb @@ -0,0 +1,2 @@ +class ApplicationJob < ActiveJob::Base +end diff --git a/spec/dummy/app/mailers/application_mailer.rb b/spec/dummy/app/mailers/application_mailer.rb new file mode 100644 index 0000000..286b223 --- /dev/null +++ b/spec/dummy/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' +end diff --git a/spec/dummy/app/models/ability.rb b/spec/dummy/app/models/ability.rb new file mode 100644 index 0000000..3351628 --- /dev/null +++ b/spec/dummy/app/models/ability.rb @@ -0,0 +1,7 @@ +class Ability + include Dunlop::Ability + + def setup_abilities + setup_dunlop_abilities + end +end diff --git a/spec/dummy/app/models/application_record.rb b/spec/dummy/app/models/application_record.rb new file mode 100644 index 0000000..ee7b6d6 --- /dev/null +++ b/spec/dummy/app/models/application_record.rb @@ -0,0 +1,18 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + self.inheritance_column = :sti_type + + if Rails::VERSION::MAJOR < 5 + def saved_changes + changes + end + end + + class << self + def truncate + #connection_pool.with_connection { |c| c.truncate table_name } + delete_all + end + end +end + diff --git a/spec/dummy/app/models/comment.rb b/spec/dummy/app/models/comment.rb new file mode 100644 index 0000000..8b86c56 --- /dev/null +++ b/spec/dummy/app/models/comment.rb @@ -0,0 +1,3 @@ +class Comment < ApplicationRecord + belongs_to :post +end diff --git a/spec/dummy/app/models/concerns/.keep b/spec/dummy/app/models/concerns/.keep new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/app/models/execution_batch.rb b/spec/dummy/app/models/execution_batch.rb new file mode 100644 index 0000000..6805c19 --- /dev/null +++ b/spec/dummy/app/models/execution_batch.rb @@ -0,0 +1,3 @@ +class ExecutionBatch < ApplicationRecord + include Dunlop::ExecutionBatchModel +end diff --git a/spec/dummy/app/models/post.rb b/spec/dummy/app/models/post.rb new file mode 100644 index 0000000..4a0df21 --- /dev/null +++ b/spec/dummy/app/models/post.rb @@ -0,0 +1,3 @@ +class Post < ApplicationRecord + has_many :comments +end diff --git a/spec/dummy/app/models/user.rb b/spec/dummy/app/models/user.rb new file mode 100644 index 0000000..7006d87 --- /dev/null +++ b/spec/dummy/app/models/user.rb @@ -0,0 +1,11 @@ +class User < ApplicationRecord + include Dunlop::UserModel + + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable and :omniauthable + devise :database_authenticatable, #:token_authenticatable, #:registerable, + :recoverable, :rememberable, :trackable, :validatable + + # To be overridden, with application logic + scope :active, ->{ all } +end diff --git a/spec/dummy/app/serializers/api/comment_serializer.rb b/spec/dummy/app/serializers/api/comment_serializer.rb new file mode 100644 index 0000000..a6385ab --- /dev/null +++ b/spec/dummy/app/serializers/api/comment_serializer.rb @@ -0,0 +1,4 @@ +class Api::CommentSerializer < ActiveModel::Serializer + attribute :body + belongs_to :post +end diff --git a/spec/dummy/app/serializers/api/post_serializer.rb b/spec/dummy/app/serializers/api/post_serializer.rb new file mode 100644 index 0000000..e3adf41 --- /dev/null +++ b/spec/dummy/app/serializers/api/post_serializer.rb @@ -0,0 +1,5 @@ +class Api::PostSerializer < ActiveModel::Serializer + attribute :title + attribute :body + has_many :comments +end diff --git a/spec/dummy/app/views/comments/_form.html.slim b/spec/dummy/app/views/comments/_form.html.slim new file mode 100644 index 0000000..08153bd --- /dev/null +++ b/spec/dummy/app/views/comments/_form.html.slim @@ -0,0 +1,18 @@ += form_for @comment do |f| + - if @comment.errors.any? + #error_explanation + h2 = "#{pluralize(@comment.errors.count, "error")} prohibited this comment from being saved:" + ul + - @comment.errors.full_messages.each do |message| + li = message + + .field + = f.label :user_id + = f.number_field :user_id + .field + = f.label :post_id + = f.number_field :post_id + .field + = f.label :body + = f.text_area :body + .actions = f.submit diff --git a/spec/dummy/app/views/comments/edit.html.slim b/spec/dummy/app/views/comments/edit.html.slim new file mode 100644 index 0000000..7385128 --- /dev/null +++ b/spec/dummy/app/views/comments/edit.html.slim @@ -0,0 +1,8 @@ +h1 Editing comment + +== render 'form' + += link_to 'Show', @comment +' | += link_to 'Back', comments_path + diff --git a/spec/dummy/app/views/comments/index.html.slim b/spec/dummy/app/views/comments/index.html.slim new file mode 100644 index 0000000..68b36ed --- /dev/null +++ b/spec/dummy/app/views/comments/index.html.slim @@ -0,0 +1,25 @@ +h1 Listing comments + +table + thead + tr + th User + th Post + th Body + th + th + th + + tbody + - @comments.each do |comment| + tr + td = comment.user_id + td = comment.post_id + td = comment.body + td = link_to 'Show', comment + td = link_to 'Edit', edit_comment_path(comment) + td = link_to 'Destroy', comment, data: { confirm: 'Are you sure?' }, method: :delete + +br + += link_to 'New Comment', new_comment_path diff --git a/spec/dummy/app/views/comments/new.html.slim b/spec/dummy/app/views/comments/new.html.slim new file mode 100644 index 0000000..b529d2c --- /dev/null +++ b/spec/dummy/app/views/comments/new.html.slim @@ -0,0 +1,5 @@ +h1 New comment + +== render 'form' + += link_to 'Back', comments_path diff --git a/spec/dummy/app/views/comments/show.html.slim b/spec/dummy/app/views/comments/show.html.slim new file mode 100644 index 0000000..6f4640e --- /dev/null +++ b/spec/dummy/app/views/comments/show.html.slim @@ -0,0 +1,15 @@ +p#notice = notice + +p + strong User: + = @comment.user_id +p + strong Post: + = @comment.post_id +p + strong Body: + = @comment.body + += link_to 'Edit', edit_comment_path(@comment) +' | += link_to 'Back', comments_path diff --git a/spec/dummy/app/views/layouts/_messages.html.slim b/spec/dummy/app/views/layouts/_messages.html.slim new file mode 100644 index 0000000..5a6c1ae --- /dev/null +++ b/spec/dummy/app/views/layouts/_messages.html.slim @@ -0,0 +1,5 @@ +- flash.each do |name, msg| + - if msg.is_a?(String) + div.alert-box.round class="#{name.to_s == 'notice' ? 'success' : 'alert'}" data-alert="" + = content_tag :div, msg + a.close href="#" × diff --git a/spec/dummy/app/views/layouts/_navigation.html.slim b/spec/dummy/app/views/layouts/_navigation.html.slim new file mode 100644 index 0000000..220cdfb --- /dev/null +++ b/spec/dummy/app/views/layouts/_navigation.html.slim @@ -0,0 +1,5 @@ +nav.top-bar data-topbar=true + ul.title-area + li.name= link_to application_name, main_app.root_path + li.toggle-topbar.menu-icon: a href="#": span Menu + .top-bar-section= render 'layouts/navigation_links' diff --git a/spec/dummy/app/views/layouts/_navigation_links.html.slim b/spec/dummy/app/views/layouts/_navigation_links.html.slim new file mode 100644 index 0000000..5a91447 --- /dev/null +++ b/spec/dummy/app/views/layouts/_navigation_links.html.slim @@ -0,0 +1,39 @@ +- if signed_in? + ul.right + li class=active_class('dunlop#changelog') + = link_to Rails.application.config.git_version, dunlop.changelog_path, title: 'Current version, click for changelog' + li class=active_class('dunlop/users#profile', 'dunlop/users#edit_profile') = link_to current_user.email.to_s.sub(/\@.*/, ''), dunlop.profile_users_path + li= link_to 'Sign out', main_app.destroy_user_session_path, method: :delete + ul.left + - if can? :index, WorkflowInstance + li.has-dropdown class=active_class('workflow_instance') + = link_to WorkflowInstance.model_name.human_plural, '#' + ul.dropdown + - WorkflowInstance.scenario_classes.each do |scenario| + - if can? :read, scenario + li class=active_class("workflow_instance/#{scenario.process_name.pluralize}") = link_to scenario.model_name.human_plural, [main_app, scenario] + - if can? :index, WorkflowInstanceBatch + li.has-dropdown class=active_class('workflow_instance_batch') + = link_to WorkflowInstanceBatch.model_name.human_plural, '#' + ul.dropdown + - WorkflowInstance.scenario_classes.each do |scenario| + - if can? :read, scenario.batch_class + li class=active_class("workflow_instance_batch/#{scenario.batch_class.process_name.pluralize}") = link_to scenario.batch_class.model_name.human_plural, [main_app, scenario.batch_class] + /- if can? :index, SourceFile or can? :index, TargetFile + li.has-dropdown class=active_class('dunlop/source_files', 'dunlop/target_files') + = link_to 'Files', '#' + ul.dropdown + - if can? :index, SourceFile + li class=active_class('dunlop/source_files') = link_to SourceFile.model_name.human_plural, dunlop.source_files_path + - if can? :index, TargetFile + li class=active_class('dunlop/target_files') = link_to TargetFile.model_name.human_plural, dunlop.target_files_path + - if can? :index, User + li.has-dropdown class=active_class("dunlop/users", except: /profile$/) + = link_to 'Admin', '#' + ul.dropdown + li class=active_class('dunlop/users', except: /profile$/) = link_to User.model_name.human_plural, dunlop.users_path + /li{ class: active_class('manual') }= link_to 'Manual', manual_root_path +- else + ul.right + li= link_to 'Sign In', main_app.new_user_session_path + diff --git a/spec/dummy/app/views/layouts/application.html.slim b/spec/dummy/app/views/layouts/application.html.slim new file mode 100644 index 0000000..c9ab11f --- /dev/null +++ b/spec/dummy/app/views/layouts/application.html.slim @@ -0,0 +1,27 @@ +doctype html +html + head + meta name="viewport" content="width=device-width, initial-scale=1.0" + title= content_for?(:title) ? yield(:title) : application_name + meta name="description" content="#{content_for?(:description) ? yield(:description) : 'Migration Dashboard'}" + = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true + = javascript_include_tag 'vendor/modernizr' + = javascript_include_tag 'application', 'data-turbolinks-track' => true + = csrf_meta_tags + body class=body_classes + #workflow-step-modal.reveal-modal data-reveal="" + .content + a.close-reveal-modal × + header= render 'layouts/navigation' + main role="main" + .environment-ribbon class=Rails.env + span= Rails.env + = render 'layouts/messages' + = yield + footer + .right + Powered by + = link_to 'FourStack B.V.', 'http://fourstack.nl/', target: :_blank + #display-badge-info-popup.reveal-modal data-reveal="" + .display-badge-info-content + a.close-reveal-modal x diff --git a/spec/dummy/app/views/layouts/mailer.html.erb b/spec/dummy/app/views/layouts/mailer.html.erb new file mode 100644 index 0000000..cbd34d2 --- /dev/null +++ b/spec/dummy/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/spec/dummy/app/views/layouts/mailer.text.erb b/spec/dummy/app/views/layouts/mailer.text.erb new file mode 100644 index 0000000..37f0bdd --- /dev/null +++ b/spec/dummy/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/spec/dummy/app/views/posts/_form.html.slim b/spec/dummy/app/views/posts/_form.html.slim new file mode 100644 index 0000000..733669e --- /dev/null +++ b/spec/dummy/app/views/posts/_form.html.slim @@ -0,0 +1,15 @@ += form_for @post do |f| + - if @post.errors.any? + #error_explanation + h2 = "#{pluralize(@post.errors.count, "error")} prohibited this post from being saved:" + ul + - @post.errors.full_messages.each do |message| + li = message + + .field + = f.label :title + = f.text_field :title + .field + = f.label :body + = f.text_area :body + .actions = f.submit diff --git a/spec/dummy/app/views/posts/edit.html.slim b/spec/dummy/app/views/posts/edit.html.slim new file mode 100644 index 0000000..745d8cc --- /dev/null +++ b/spec/dummy/app/views/posts/edit.html.slim @@ -0,0 +1,8 @@ +h1 Editing post + +== render 'form' + += link_to 'Show', @post +' | += link_to 'Back', posts_path + diff --git a/spec/dummy/app/views/posts/index.html.slim b/spec/dummy/app/views/posts/index.html.slim new file mode 100644 index 0000000..dcc6f20 --- /dev/null +++ b/spec/dummy/app/views/posts/index.html.slim @@ -0,0 +1,23 @@ +h1 Listing posts + +table + thead + tr + th Title + th Body + th + th + th + + tbody + - @posts.each do |post| + tr + td = post.title + td = post.body + td = link_to 'Show', post + td = link_to 'Edit', edit_post_path(post) + td = link_to 'Destroy', post, data: { confirm: 'Are you sure?' }, method: :delete + +br + += link_to 'New Post', new_post_path diff --git a/spec/dummy/app/views/posts/new.html.slim b/spec/dummy/app/views/posts/new.html.slim new file mode 100644 index 0000000..eb7c43f --- /dev/null +++ b/spec/dummy/app/views/posts/new.html.slim @@ -0,0 +1,5 @@ +h1 New post + +== render 'form' + += link_to 'Back', posts_path diff --git a/spec/dummy/app/views/posts/show.html.slim b/spec/dummy/app/views/posts/show.html.slim new file mode 100644 index 0000000..868a1f8 --- /dev/null +++ b/spec/dummy/app/views/posts/show.html.slim @@ -0,0 +1,12 @@ +p#notice = notice + +p + strong Title: + = @post.title +p + strong Body: + = @post.body + += link_to 'Edit', edit_post_path(@post) +' | += link_to 'Back', posts_path diff --git a/spec/dummy/bin/bundle b/spec/dummy/bin/bundle new file mode 100755 index 0000000..66e9889 --- /dev/null +++ b/spec/dummy/bin/bundle @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +load Gem.bin_path('bundler', 'bundle') diff --git a/spec/dummy/bin/rails b/spec/dummy/bin/rails new file mode 100755 index 0000000..0739660 --- /dev/null +++ b/spec/dummy/bin/rails @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +APP_PATH = File.expand_path('../config/application', __dir__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/spec/dummy/bin/rake b/spec/dummy/bin/rake new file mode 100755 index 0000000..1724048 --- /dev/null +++ b/spec/dummy/bin/rake @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/spec/dummy/bin/setup b/spec/dummy/bin/setup new file mode 100755 index 0000000..e620b4d --- /dev/null +++ b/spec/dummy/bin/setup @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + # puts "\n== Copying sample files ==" + # unless File.exist?('config/database.yml') + # cp 'config/database.yml.sample', 'config/database.yml' + # end + + puts "\n== Preparing database ==" + system! 'bin/rails db:setup' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/spec/dummy/bin/update b/spec/dummy/bin/update new file mode 100755 index 0000000..a8e4462 --- /dev/null +++ b/spec/dummy/bin/update @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a way to update your development environment automatically. + # Add necessary update steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + puts "\n== Updating database ==" + system! 'bin/rails db:migrate' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/spec/dummy/config.ru b/spec/dummy/config.ru new file mode 100644 index 0000000..f7ba0b5 --- /dev/null +++ b/spec/dummy/config.ru @@ -0,0 +1,5 @@ +# This file is used by Rack-based servers to start the application. + +require_relative 'config/environment' + +run Rails.application diff --git a/spec/dummy/config/application.rb b/spec/dummy/config/application.rb new file mode 100644 index 0000000..5b132a5 --- /dev/null +++ b/spec/dummy/config/application.rb @@ -0,0 +1,37 @@ +require_relative 'boot' + +# Pick the frameworks you want: +require "active_record/railtie" +require "action_controller/railtie" +require "action_view/railtie" +require "action_mailer/railtie" +require "active_job/railtie" +require "action_cable/engine" +# require "rails/test_unit/railtie" +require "sprockets/railtie" + +Bundler.require(*Rails.groups) +require "dunlop/ember" + +module Dummy + class Application < Rails::Application + + config.application_name = "Dummy" + + config.git_version = Rails.env + config.git_version = File.read('REVISION').strip if File.exists?('REVISION') + config.git_version = File.read('OFFLINE_REVISION').strip if File.exists?('OFFLINE_REVISION') + config.load_defaults 5.1 + + config.middleware.insert_before 0, Rack::Cors do + allow do + origins '*' + resource '*', headers: :any, methods: [:get, :post, :options, :delete, :put, :patch] + end + end + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + end +end + diff --git a/spec/dummy/config/boot.rb b/spec/dummy/config/boot.rb new file mode 100644 index 0000000..c9aef85 --- /dev/null +++ b/spec/dummy/config/boot.rb @@ -0,0 +1,5 @@ +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) + +require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) +$LOAD_PATH.unshift File.expand_path('../../../lib', __dir__) diff --git a/spec/dummy/config/cable.yml b/spec/dummy/config/cable.yml new file mode 100644 index 0000000..0bbde6f --- /dev/null +++ b/spec/dummy/config/cable.yml @@ -0,0 +1,9 @@ +development: + adapter: async + +test: + adapter: async + +production: + adapter: redis + url: redis://localhost:6379/1 diff --git a/spec/dummy/config/database.yml b/spec/dummy/config/database.yml new file mode 100644 index 0000000..1c1a37c --- /dev/null +++ b/spec/dummy/config/database.yml @@ -0,0 +1,25 @@ +# SQLite version 3.x +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +# +default: &default + adapter: sqlite3 + pool: 5 + timeout: 5000 + +development: + <<: *default + database: db/development.sqlite3 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: db/test.sqlite3 + +production: + <<: *default + database: db/production.sqlite3 diff --git a/spec/dummy/config/environment.rb b/spec/dummy/config/environment.rb new file mode 100644 index 0000000..426333b --- /dev/null +++ b/spec/dummy/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative 'application' + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/spec/dummy/config/environments/development.rb b/spec/dummy/config/environments/development.rb new file mode 100644 index 0000000..a29bb0e --- /dev/null +++ b/spec/dummy/config/environments/development.rb @@ -0,0 +1,58 @@ +Rails.application.configure do + + config.time_zone = 'Moscow' + config.git_version = 'develop' + + # Settings specified here will take precedence over those in config/application.rb. + + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable/disable caching. By default caching is disabled. + if Rails.root.join('tmp/caching-dev.txt').exist? + config.action_controller.perform_caching = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=172800' + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Don't care if the mailer can't send. + config.action_mailer.raise_delivery_errors = false + + config.action_mailer.perform_caching = false + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + # Debug mode disables concatenation and preprocessing of assets. + # This option may cause significant delays in view rendering with a large + # number of complex assets. + config.assets.debug = true + + # Suppress logger output for asset requests. + config.assets.quiet = true + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true + + # Use an evented file watcher to asynchronously detect changes in source code, + # routes, locales, etc. This feature depends on the listen gem. + # config.file_watcher = ActiveSupport::EventedFileUpdateChecker +end diff --git a/spec/dummy/config/environments/production.rb b/spec/dummy/config/environments/production.rb new file mode 100644 index 0000000..739e81c --- /dev/null +++ b/spec/dummy/config/environments/production.rb @@ -0,0 +1,86 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + + # Compress JavaScripts and CSS. + config.assets.js_compressor = :uglifier + # config.assets.css_compressor = :sass + + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Mount Action Cable outside main process or domain + # config.action_cable.mount_path = nil + # config.action_cable.url = 'wss://example.com/cable' + # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "dummy_#{Rails.env}" + config.action_mailer.perform_caching = false + + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners. + config.active_support.deprecation = :notify + + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new + + # Use a different logger for distributed setups. + # require 'syslog/logger' + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + + if ENV["RAILS_LOG_TO_STDOUT"].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false +end diff --git a/spec/dummy/config/environments/test.rb b/spec/dummy/config/environments/test.rb new file mode 100644 index 0000000..8058284 --- /dev/null +++ b/spec/dummy/config/environments/test.rb @@ -0,0 +1,46 @@ +Rails.application.configure do + + config.time_zone = 'Moscow' + config.git_version = 'test' + + # Settings specified here will take precedence over those in config/application.rb. + + # The test environment is used exclusively to run your application's + # test suite. You never need to work with it otherwise. Remember that + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true + + # Do not eager load code on boot. This avoids loading your whole application + # just for the purpose of running a single test. If you are using a tool that + # preloads Rails for running tests, you may have to set it to true. + config.eager_load = false + + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=3600' + } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Raise exceptions instead of rendering exception templates. + config.action_dispatch.show_exceptions = false + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + config.action_mailer.perform_caching = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true +end diff --git a/spec/dummy/config/initializers/application_controller_renderer.rb b/spec/dummy/config/initializers/application_controller_renderer.rb new file mode 100644 index 0000000..51639b6 --- /dev/null +++ b/spec/dummy/config/initializers/application_controller_renderer.rb @@ -0,0 +1,6 @@ +# Be sure to restart your server when you modify this file. + +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) diff --git a/spec/dummy/config/initializers/assets.rb b/spec/dummy/config/initializers/assets.rb new file mode 100644 index 0000000..01ef3e6 --- /dev/null +++ b/spec/dummy/config/initializers/assets.rb @@ -0,0 +1,11 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = '1.0' + +# Add additional assets to the asset load path +# Rails.application.config.assets.paths << Emoji.images_path + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in app/assets folder are already added. +# Rails.application.config.assets.precompile += %w( search.js ) diff --git a/spec/dummy/config/initializers/backtrace_silencers.rb b/spec/dummy/config/initializers/backtrace_silencers.rb new file mode 100644 index 0000000..59385cd --- /dev/null +++ b/spec/dummy/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/spec/dummy/config/initializers/cookies_serializer.rb b/spec/dummy/config/initializers/cookies_serializer.rb new file mode 100644 index 0000000..5a6a32d --- /dev/null +++ b/spec/dummy/config/initializers/cookies_serializer.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Specify a serializer for the signed and encrypted cookie jars. +# Valid options are :json, :marshal, and :hybrid. +Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/spec/dummy/config/initializers/devise.rb b/spec/dummy/config/initializers/devise.rb new file mode 100644 index 0000000..d52d2c6 --- /dev/null +++ b/spec/dummy/config/initializers/devise.rb @@ -0,0 +1,277 @@ +# Use this hook to configure devise mailer, warden hooks and so forth. +# Many of these configuration options can be set straight in your model. +Devise.setup do |config| + # The secret key used by Devise. Devise uses this key to generate + # random tokens. Changing this key will render invalid all existing + # confirmation, reset password and unlock tokens in the database. + # Devise will use the `secret_key_base` as its `secret_key` + # by default. You can change it below and use your own secret key. + config.secret_key = '3538d800ea7a5fe1dba29d51c998fc44ee9728cb1d88ab536549e8a4d1ed224dc49ba6675d7a31e2fe3fe4fcd4ec677411c673ad862b2e853f1cf3ad0c31ee7f' + + # ==> Mailer Configuration + # Configure the e-mail address which will be shown in Devise::Mailer, + # note that it will be overwritten if you use your own mailer class + # with default "from" parameter. + config.mailer_sender = 'dummy-test@example.com' + + # Configure the class responsible to send e-mails. + # config.mailer = 'Devise::Mailer' + + # Configure the parent class responsible to send e-mails. + # config.parent_mailer = 'ActionMailer::Base' + + # ==> ORM configuration + # Load and configure the ORM. Supports :active_record (default) and + # :mongoid (bson_ext recommended) by default. Other ORMs may be + # available as additional gems. + require 'devise/orm/active_record' + + # ==> Configuration for any authentication mechanism + # Configure which keys are used when authenticating a user. The default is + # just :email. You can configure it to use [:username, :subdomain], so for + # authenticating a user, both parameters are required. Remember that those + # parameters are used only when authenticating and not when retrieving from + # session. If you need permissions, you should implement that in a before filter. + # You can also supply a hash where the value is a boolean determining whether + # or not authentication should be aborted when the value is not present. + # config.authentication_keys = [:email] + + # Configure parameters from the request object used for authentication. Each entry + # given should be a request method and it will automatically be passed to the + # find_for_authentication method and considered in your model lookup. For instance, + # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. + # The same considerations mentioned for authentication_keys also apply to request_keys. + # config.request_keys = [] + + # Configure which authentication keys should be case-insensitive. + # These keys will be downcased upon creating or modifying a user and when used + # to authenticate or find a user. Default is :email. + config.case_insensitive_keys = [:email] + + # Configure which authentication keys should have whitespace stripped. + # These keys will have whitespace before and after removed upon creating or + # modifying a user and when used to authenticate or find a user. Default is :email. + config.strip_whitespace_keys = [:email] + + # Tell if authentication through request.params is enabled. True by default. + # It can be set to an array that will enable params authentication only for the + # given strategies, for example, `config.params_authenticatable = [:database]` will + # enable it only for database (email + password) authentication. + # config.params_authenticatable = true + + # Tell if authentication through HTTP Auth is enabled. False by default. + # It can be set to an array that will enable http authentication only for the + # given strategies, for example, `config.http_authenticatable = [:database]` will + # enable it only for database authentication. The supported strategies are: + # :database = Support basic authentication with authentication key + password + # config.http_authenticatable = false + + # If 401 status code should be returned for AJAX requests. True by default. + # config.http_authenticatable_on_xhr = true + + # The realm used in Http Basic Authentication. 'Application' by default. + # config.http_authentication_realm = 'Application' + + # It will change confirmation, password recovery and other workflows + # to behave the same regardless if the e-mail provided was right or wrong. + # Does not affect registerable. + # config.paranoid = true + + # By default Devise will store the user in session. You can skip storage for + # particular strategies by setting this option. + # Notice that if you are skipping storage for all authentication paths, you + # may want to disable generating routes to Devise's sessions controller by + # passing skip: :sessions to `devise_for` in your config/routes.rb + config.skip_session_storage = [:http_auth] + + # By default, Devise cleans up the CSRF token on authentication to + # avoid CSRF token fixation attacks. This means that, when using AJAX + # requests for sign in and sign up, you need to get a new CSRF token + # from the server. You can disable this option at your own risk. + # config.clean_up_csrf_token_on_authentication = true + + # When false, Devise will not attempt to reload routes on eager load. + # This can reduce the time taken to boot the app but if your application + # requires the Devise mappings to be loaded during boot time the application + # won't boot properly. + # config.reload_routes = true + + # ==> Configuration for :database_authenticatable + # For bcrypt, this is the cost for hashing the password and defaults to 11. If + # using other algorithms, it sets how many times you want the password to be hashed. + # + # Limiting the stretches to just one in testing will increase the performance of + # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use + # a value less than 10 in other environments. Note that, for bcrypt (the default + # algorithm), the cost increases exponentially with the number of stretches (e.g. + # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). + config.stretches = Rails.env.test? ? 1 : 11 + + # Set up a pepper to generate the hashed password. + # config.pepper = '49df74b77985daa55af0cbbaa610e93dbffd32d2d3c211e9a2f46c8fc0b8555359077fce9803b8ed7e9dae4a7e5d6d728b94c23a0e2c654b29da2b1c901cff80' + + # Send a notification to the original email when the user's email is changed. + # config.send_email_changed_notification = false + + # Send a notification email when the user's password is changed. + # config.send_password_change_notification = false + + # ==> Configuration for :confirmable + # A period that the user is allowed to access the website even without + # confirming their account. For instance, if set to 2.days, the user will be + # able to access the website for two days without confirming their account, + # access will be blocked just in the third day. Default is 0.days, meaning + # the user cannot access the website without confirming their account. + # config.allow_unconfirmed_access_for = 2.days + + # A period that the user is allowed to confirm their account before their + # token becomes invalid. For example, if set to 3.days, the user can confirm + # their account within 3 days after the mail was sent, but on the fourth day + # their account can't be confirmed with the token any more. + # Default is nil, meaning there is no restriction on how long a user can take + # before confirming their account. + # config.confirm_within = 3.days + + # If true, requires any email changes to be confirmed (exactly the same way as + # initial account confirmation) to be applied. Requires additional unconfirmed_email + # db field (see migrations). Until confirmed, new email is stored in + # unconfirmed_email column, and copied to email column on successful confirmation. + config.reconfirmable = true + + # Defines which key will be used when confirming an account + # config.confirmation_keys = [:email] + + # ==> Configuration for :rememberable + # The time the user will be remembered without asking for credentials again. + # config.remember_for = 2.weeks + + # Invalidates all the remember me tokens when the user signs out. + config.expire_all_remember_me_on_sign_out = true + + # If true, extends the user's remember period when remembered via cookie. + # config.extend_remember_period = false + + # Options to be passed to the created cookie. For instance, you can set + # secure: true in order to force SSL only cookies. + # config.rememberable_options = {} + + # ==> Configuration for :validatable + # Range for password length. + config.password_length = 6..128 + + # Email regex used to validate email formats. It simply asserts that + # one (and only one) @ exists in the given string. This is mainly + # to give user feedback and not to assert the e-mail validity. + config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ + + # ==> Configuration for :timeoutable + # The time you want to timeout the user session without activity. After this + # time the user will be asked for credentials again. Default is 30 minutes. + # config.timeout_in = 30.minutes + + # ==> Configuration for :lockable + # Defines which strategy will be used to lock an account. + # :failed_attempts = Locks an account after a number of failed attempts to sign in. + # :none = No lock strategy. You should handle locking by yourself. + # config.lock_strategy = :failed_attempts + + # Defines which key will be used when locking and unlocking an account + # config.unlock_keys = [:email] + + # Defines which strategy will be used to unlock an account. + # :email = Sends an unlock link to the user email + # :time = Re-enables login after a certain amount of time (see :unlock_in below) + # :both = Enables both strategies + # :none = No unlock strategy. You should handle unlocking by yourself. + # config.unlock_strategy = :both + + # Number of authentication tries before locking an account if lock_strategy + # is failed attempts. + # config.maximum_attempts = 20 + + # Time interval to unlock the account if :time is enabled as unlock_strategy. + # config.unlock_in = 1.hour + + # Warn on the last attempt before the account is locked. + # config.last_attempt_warning = true + + # ==> Configuration for :recoverable + # + # Defines which key will be used when recovering the password for an account + # config.reset_password_keys = [:email] + + # Time interval you can reset your password with a reset password key. + # Don't put a too small interval or your users won't have the time to + # change their passwords. + config.reset_password_within = 6.hours + + # When set to false, does not sign a user in automatically after their password is + # reset. Defaults to true, so a user is signed in automatically after a reset. + # config.sign_in_after_reset_password = true + + # ==> Configuration for :encryptable + # Allow you to use another hashing or encryption algorithm besides bcrypt (default). + # You can use :sha1, :sha512 or algorithms from others authentication tools as + # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20 + # for default behavior) and :restful_authentication_sha1 (then you should set + # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper). + # + # Require the `devise-encryptable` gem when using anything other than bcrypt + # config.encryptor = :sha512 + + # ==> Scopes configuration + # Turn scoped views on. Before rendering "sessions/new", it will first check for + # "users/sessions/new". It's turned off by default because it's slower if you + # are using only default views. + # config.scoped_views = false + + # Configure the default scope given to Warden. By default it's the first + # devise role declared in your routes (usually :user). + # config.default_scope = :user + + # Set this configuration to false if you want /users/sign_out to sign out + # only the current scope. By default, Devise signs out all scopes. + # config.sign_out_all_scopes = true + + # ==> Navigation configuration + # Lists the formats that should be treated as navigational. Formats like + # :html, should redirect to the sign in page when the user does not have + # access, but formats like :xml or :json, should return 401. + # + # If you have any extra navigational formats, like :iphone or :mobile, you + # should add them to the navigational formats lists. + # + # The "*/*" below is required to match Internet Explorer requests. + # config.navigational_formats = ['*/*', :html] + + # The default HTTP method used to sign out a resource. Default is :delete. + config.sign_out_via = :delete + + # ==> OmniAuth + # Add a new OmniAuth provider. Check the wiki for more information on setting + # up on your models and hooks. + # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' + + # ==> Warden configuration + # If you want to use other strategies, that are not supported by Devise, or + # change the failure app, you can configure them inside the config.warden block. + # + # config.warden do |manager| + # manager.intercept_401 = false + # manager.default_strategies(scope: :user).unshift :some_external_strategy + # end + + # ==> Mountable engine configurations + # When using Devise inside an engine, let's call it `MyEngine`, and this engine + # is mountable, there are some extra configurations to be taken into account. + # The following options are available, assuming the engine is mounted as: + # + # mount MyEngine, at: '/my_engine' + # + # The router that invoked `devise_for`, in the example above, would be: + # config.router_name = :my_engine + # + # When using OmniAuth, Devise cannot automatically set OmniAuth path, + # so you need to do it manually. For the users scope, it would be: + # config.omniauth_path_prefix = '/my_engine/users/auth' +end diff --git a/spec/dummy/config/initializers/filter_parameter_logging.rb b/spec/dummy/config/initializers/filter_parameter_logging.rb new file mode 100644 index 0000000..4a994e1 --- /dev/null +++ b/spec/dummy/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Configure sensitive parameters which will be filtered from the log file. +Rails.application.config.filter_parameters += [:password] diff --git a/spec/dummy/config/initializers/inflections.rb b/spec/dummy/config/initializers/inflections.rb new file mode 100644 index 0000000..ac033bf --- /dev/null +++ b/spec/dummy/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym 'RESTful' +# end diff --git a/spec/dummy/config/initializers/mime_types.rb b/spec/dummy/config/initializers/mime_types.rb new file mode 100644 index 0000000..dc18996 --- /dev/null +++ b/spec/dummy/config/initializers/mime_types.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf diff --git a/spec/dummy/config/initializers/new_framework_defaults.rb b/spec/dummy/config/initializers/new_framework_defaults.rb new file mode 100644 index 0000000..671abb6 --- /dev/null +++ b/spec/dummy/config/initializers/new_framework_defaults.rb @@ -0,0 +1,24 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 5.0 upgrade. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. + +# Enable per-form CSRF tokens. Previous versions had false. +Rails.application.config.action_controller.per_form_csrf_tokens = true + +# Enable origin-checking CSRF mitigation. Previous versions had false. +Rails.application.config.action_controller.forgery_protection_origin_check = true + +# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. +# Previous versions had false. +ActiveSupport.to_time_preserves_timezone = true + +# Require `belongs_to` associations by default. Previous versions had false. +Rails.application.config.active_record.belongs_to_required_by_default = true + +# Do not halt callback chains when a callback returns false. Previous versions had true. +ActiveSupport.halt_callback_chains_on_return_false = false + +# Configure SSL options to enable HSTS with subdomains. Previous versions had false. +Rails.application.config.ssl_options = { hsts: { subdomains: true } } diff --git a/spec/dummy/config/initializers/session_store.rb b/spec/dummy/config/initializers/session_store.rb new file mode 100644 index 0000000..e766b67 --- /dev/null +++ b/spec/dummy/config/initializers/session_store.rb @@ -0,0 +1,3 @@ +# Be sure to restart your server when you modify this file. + +Rails.application.config.session_store :cookie_store, key: '_dummy_session' diff --git a/spec/dummy/config/initializers/wrap_parameters.rb b/spec/dummy/config/initializers/wrap_parameters.rb new file mode 100644 index 0000000..bbfc396 --- /dev/null +++ b/spec/dummy/config/initializers/wrap_parameters.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. + +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters format: [:json] +end + +# To enable root element in JSON for ActiveRecord objects. +# ActiveSupport.on_load(:active_record) do +# self.include_root_in_json = true +# end diff --git a/spec/dummy/config/locales/devise.en.yml b/spec/dummy/config/locales/devise.en.yml new file mode 100644 index 0000000..0b8f130 --- /dev/null +++ b/spec/dummy/config/locales/devise.en.yml @@ -0,0 +1,64 @@ +# Additional translations at https://github.com/plataformatec/devise/wiki/I18n + +en: + devise: + confirmations: + confirmed: "Your email address has been successfully confirmed." + send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." + send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." + failure: + already_authenticated: "You are already signed in." + inactive: "Your account is not activated yet." + invalid: "Invalid %{authentication_keys} or password." + locked: "Your account is locked." + last_attempt: "You have one more attempt before your account is locked." + not_found_in_database: "Invalid %{authentication_keys} or password." + timeout: "Your session expired. Please sign in again to continue." + unauthenticated: "You need to sign in or sign up before continuing." + unconfirmed: "You have to confirm your email address before continuing." + mailer: + confirmation_instructions: + subject: "Confirmation instructions" + reset_password_instructions: + subject: "Reset password instructions" + unlock_instructions: + subject: "Unlock instructions" + email_changed: + subject: "Email Changed" + password_change: + subject: "Password Changed" + omniauth_callbacks: + failure: "Could not authenticate you from %{kind} because \"%{reason}\"." + success: "Successfully authenticated from %{kind} account." + passwords: + no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." + send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." + send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." + updated: "Your password has been changed successfully. You are now signed in." + updated_not_active: "Your password has been changed successfully." + registrations: + destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." + signed_up: "Welcome! You have signed up successfully." + signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." + signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." + signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." + update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." + updated: "Your account has been updated successfully." + sessions: + signed_in: "Signed in successfully." + signed_out: "Signed out successfully." + already_signed_out: "Signed out successfully." + unlocks: + send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." + send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." + unlocked: "Your account has been unlocked successfully. Please sign in to continue." + errors: + messages: + already_confirmed: "was already confirmed, please try signing in" + confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" + expired: "has expired, please request a new one" + not_found: "not found" + not_locked: "was not locked" + not_saved: + one: "1 error prohibited this %{resource} from being saved:" + other: "%{count} errors prohibited this %{resource} from being saved:" diff --git a/spec/dummy/config/locales/en.yml b/spec/dummy/config/locales/en.yml new file mode 100644 index 0000000..0653957 --- /dev/null +++ b/spec/dummy/config/locales/en.yml @@ -0,0 +1,23 @@ +# Files in the config/locales directory are used for internationalization +# and are automatically loaded by Rails. If you want to use locales other +# than English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t 'hello' +# +# In views, this is aliased to just `t`: +# +# <%= t('hello') %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# To learn more, please read the Rails Internationalization guide +# available at http://guides.rubyonrails.org/i18n.html. + +en: + hello: "Hello world" diff --git a/spec/dummy/config/puma.rb b/spec/dummy/config/puma.rb new file mode 100644 index 0000000..c7f311f --- /dev/null +++ b/spec/dummy/config/puma.rb @@ -0,0 +1,47 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum, this matches the default thread size of Active Record. +# +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests, default is 3000. +# +port ENV.fetch("PORT") { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked webserver processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. If you use this option +# you need to make sure to reconnect any threads in the `on_worker_boot` +# block. +# +# preload_app! + +# The code in the `on_worker_boot` will be called if you are using +# clustered mode by specifying a number of `workers`. After each worker +# process is booted this block will be run, if you are using `preload_app!` +# option you will want to use this block to reconnect to any threads +# or connections that may have been created at application boot, Ruby +# cannot share connections between processes. +# +# on_worker_boot do +# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) +# end + +# Allow puma to be restarted by `rails restart` command. +plugin :tmp_restart diff --git a/spec/dummy/config/routes.rb b/spec/dummy/config/routes.rb new file mode 100644 index 0000000..bd291d2 --- /dev/null +++ b/spec/dummy/config/routes.rb @@ -0,0 +1,11 @@ +Rails.application.routes.draw do + mount Dunlop::Engine => "/dunlop" + resources :comments + resources :posts + devise_for :users + mount Dunlop::Ember::Engine => "/app", as: :app + namespace :api do + resources :comments + resources :posts + end +end diff --git a/spec/dummy/config/secrets.yml b/spec/dummy/config/secrets.yml new file mode 100644 index 0000000..166b869 --- /dev/null +++ b/spec/dummy/config/secrets.yml @@ -0,0 +1,22 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key is used for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! + +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +# You can use `rails secret` to generate a secure secret key. + +# Make sure the secrets in this file are kept private +# if you're sharing your code publicly. + +development: + secret_key_base: 0c559e89f99778575a9fac1cb44f0aefedeac486b1c7c1614a9f5c1467acca856b7bae0ca89b1c6975f81857619dcdd04f2947e0b07a759f67503450f46046da + +test: + secret_key_base: 6ea9df1b658a31fd40e3caf40733753351964092ceddcb573c7515a33606c8d795166fa3c4df1bf89eb74a8383ad9eb6278498719b8806ab72e9816ca69f0dc3 + +# Do not keep production secrets in the repository, +# instead read values from the environment. +production: + secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> diff --git a/spec/dummy/config/spring.rb b/spec/dummy/config/spring.rb new file mode 100644 index 0000000..c9119b4 --- /dev/null +++ b/spec/dummy/config/spring.rb @@ -0,0 +1,6 @@ +%w( + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +).each { |path| Spring.watch(path) } diff --git a/spec/dummy/db/migrate/20170926045124_create_posts.rb b/spec/dummy/db/migrate/20170926045124_create_posts.rb new file mode 100644 index 0000000..b788575 --- /dev/null +++ b/spec/dummy/db/migrate/20170926045124_create_posts.rb @@ -0,0 +1,10 @@ +class CreatePosts < ActiveRecord::Migration[5.1] + def change + create_table :posts do |t| + t.string :title + t.text :body + + t.timestamps + end + end +end diff --git a/spec/dummy/db/migrate/20170926045157_create_comments.rb b/spec/dummy/db/migrate/20170926045157_create_comments.rb new file mode 100644 index 0000000..4ac277e --- /dev/null +++ b/spec/dummy/db/migrate/20170926045157_create_comments.rb @@ -0,0 +1,11 @@ +class CreateComments < ActiveRecord::Migration[5.1] + def change + create_table :comments do |t| + t.integer :user_id + t.integer :post_id + t.text :body + + t.timestamps + end + end +end diff --git a/spec/dummy/db/migrate/20170926045305_devise_create_users.rb b/spec/dummy/db/migrate/20170926045305_devise_create_users.rb new file mode 100644 index 0000000..060217e --- /dev/null +++ b/spec/dummy/db/migrate/20170926045305_devise_create_users.rb @@ -0,0 +1,42 @@ +class DeviseCreateUsers < ActiveRecord::Migration[5.1] + def change + create_table :users do |t| + ## Database authenticatable + t.string :email, null: false, default: "" + t.string :encrypted_password, null: false, default: "" + + ## Recoverable + t.string :reset_password_token + t.datetime :reset_password_sent_at + + ## Rememberable + t.datetime :remember_created_at + + ## Trackable + t.integer :sign_in_count, default: 0, null: false + t.datetime :current_sign_in_at + t.datetime :last_sign_in_at + t.string :current_sign_in_ip + t.string :last_sign_in_ip + + ## Confirmable + # t.string :confirmation_token + # t.datetime :confirmed_at + # t.datetime :confirmation_sent_at + # t.string :unconfirmed_email # Only if using reconfirmable + + ## Lockable + # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts + # t.string :unlock_token # Only if unlock strategy is :email or :both + # t.datetime :locked_at + + + t.timestamps null: false + end + + add_index :users, :email, unique: true + add_index :users, :reset_password_token, unique: true + # add_index :users, :confirmation_token, unique: true + # add_index :users, :unlock_token, unique: true + end +end diff --git a/spec/dummy/db/migrate/20170926050413_add_admin_and_role_names_to_users.rb b/spec/dummy/db/migrate/20170926050413_add_admin_and_role_names_to_users.rb new file mode 100644 index 0000000..fc5116d --- /dev/null +++ b/spec/dummy/db/migrate/20170926050413_add_admin_and_role_names_to_users.rb @@ -0,0 +1,9 @@ +class AddAdminAndRoleNamesToUsers < ActiveRecord::Migration[5.1] + def change + add_column :users, :admin, :boolean, default: false, null: false + add_column :users, :role_names, :text + add_column :users, :role_names_admin, :text + add_column :users, :settings_storage, :text + end +end + diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb new file mode 100644 index 0000000..a447bb3 --- /dev/null +++ b/spec/dummy/db/schema.rb @@ -0,0 +1,51 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20170926050413) do + + create_table "comments", force: :cascade do |t| + t.integer "user_id" + t.integer "post_id" + t.text "body" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "posts", force: :cascade do |t| + t.string "title" + t.text "body" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "users", force: :cascade do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.integer "sign_in_count", default: 0, null: false + t.datetime "current_sign_in_at" + t.datetime "last_sign_in_at" + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "admin", default: false, null: false + t.text "role_names" + t.text "role_names_admin" + t.text "settings_storage" + t.index ["email"], name: "index_users_on_email", unique: true + t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true + end + +end diff --git a/spec/dummy/lib/assets/.keep b/spec/dummy/lib/assets/.keep new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/log/.keep b/spec/dummy/log/.keep new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/public/404.html b/spec/dummy/public/404.html new file mode 100644 index 0000000..b612547 --- /dev/null +++ b/spec/dummy/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

You may have mistyped the address or the page may have moved.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/spec/dummy/public/422.html b/spec/dummy/public/422.html new file mode 100644 index 0000000..a21f82b --- /dev/null +++ b/spec/dummy/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

Maybe you tried to change something you didn't have access to.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/spec/dummy/public/500.html b/spec/dummy/public/500.html new file mode 100644 index 0000000..061abc5 --- /dev/null +++ b/spec/dummy/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/spec/dummy/public/apple-touch-icon-precomposed.png b/spec/dummy/public/apple-touch-icon-precomposed.png new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/public/apple-touch-icon.png b/spec/dummy/public/apple-touch-icon.png new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/public/favicon.ico b/spec/dummy/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/spec/factories/comments.rb b/spec/factories/comments.rb new file mode 100644 index 0000000..9e0265a --- /dev/null +++ b/spec/factories/comments.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :comment do + user_id 1 + post_id 1 + body "MyText" + end +end diff --git a/spec/factories/posts.rb b/spec/factories/posts.rb new file mode 100644 index 0000000..d905de2 --- /dev/null +++ b/spec/factories/posts.rb @@ -0,0 +1,6 @@ +FactoryGirl.define do + factory :post do + sequence(:title) {|i| "post-#{i}"} + sequence(:body) {|i| "post-body-#{i}"} + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb new file mode 100644 index 0000000..152e380 --- /dev/null +++ b/spec/factories/users.rb @@ -0,0 +1,9 @@ +FactoryGirl.define do + factory :user do + sequence(:email) { |i| "user#{i}@example.com" } + password 'secret' + end + factory :admin, parent: :user do + admin true + end +end diff --git a/spec/lib/dunlop/ember/api_base_controller_spec.rb b/spec/lib/dunlop/ember/api_base_controller_spec.rb new file mode 100644 index 0000000..93fd740 --- /dev/null +++ b/spec/lib/dunlop/ember/api_base_controller_spec.rb @@ -0,0 +1,62 @@ +# root application situation +class Test1 +end +module Api + class Test1sController < ApplicationController + include Dunlop::Ember::ApiBaseController + end +end + +# namespaced engine situation +module Adapter1 + class Test2 + end + module Api + class Test2sController < ApplicationController + include Dunlop::Ember::ApiBaseController + end + class RootFallBackModelsController < ApplicationController + include Dunlop::Ember::ApiBaseController + end + end +end + +class RootFallBackModel + # Root model, requested by namespaced controller +end + +describe Dunlop::Ember::ApiBaseController do + describe '#record_class private method, but for now because of unit control' do + it "finds the proper class on Api:: namespace" do + Api::Test1sController.new.record_class.should eq Test1 + end + + it "finds the proper class on Adapter::Api:: namespace" do + Adapter1::Api::Test2sController.new.record_class.should eq Adapter1::Test2 + end + + it 'falls back to demodularized version if namespaced version is not found' do + controller = Adapter1::Api::RootFallBackModelsController.new + controller.record_class.should eq RootFallBackModel + end + end + + describe '#record_params' do + it "finds the proper class on Api:: namespace" do + controller = Api::Test1sController.new + expect(controller).to receive(:params).and_return ActionController::Parameters.new(test1: {a: 1, b: 2}) + expect(controller).to receive(:permitted_params).and_return [:a] + controller.__send__(:record_params).to_hash.should eq('a' => 1) + end + + it "finds the proper class on Adapter::Api:: namespace" do + controller = Adapter1::Api::Test2sController.new + expect(controller).to receive(:params).and_return ActionController::Parameters.new('adapter1/test2' => {a: 1, b: 2}) + expect(controller).to receive(:permitted_params).and_return [:b] + controller.__send__(:record_params).to_hash.should eq('b' => 2) + end + + end +end + + diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb new file mode 100644 index 0000000..10b4c2b --- /dev/null +++ b/spec/rails_helper.rb @@ -0,0 +1,97 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +require 'spec_helper' +ENV['RAILS_ENV'] ||= 'test' +test_log = File.expand_path('../dummy/log/test.log', __FILE__) +File.delete(test_log) if File.exist?(test_log) +require File.expand_path("../dummy/config/environment.rb", __FILE__) + +# Prevent database truncation if the environment is production +abort("The Rails environment is running in production mode!") if Rails.env.production? +require 'rspec/rails' +require 'pry' +require 'rspec/rails' +require 'devise' +require 'rspec/dunlop' +#require 'capybara/poltergeist' + +ENGINE_RAILS_ROOT=File.join(File.dirname(__FILE__), '../') +# Requires supporting ruby files with custom matchers and macros, etc, +# in spec/support/ and its subdirectories. +Dir[File.join(ENGINE_RAILS_ROOT, "spec/support/**/*.rb")].each {|f| require f } +Dir[File.join(ENGINE_RAILS_ROOT, "spec/factories/**/*.rb")].each {|f| require f } +#FactoryGirl.definition_file_paths = [File.join(ENGINE_RAILS_ROOT, "spec/factories/**/*.rb")] # not needed here +Dir.glob("spec/acceptance/steps/**/*steps.rb") { |f| load f, true } + +# Add additional requires below this line. Rails is not loaded until this point! + +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } + +# Checks for pending migration and applies them before tests are run. +# If you are not using ActiveRecord, you can remove this line. +ActiveRecord::Migration.maintain_test_schema! +module ControllerJuice + def assert_json(obj) + response.body.should eq obj.to_json + end +end +DatabaseCleaner.strategy = :truncation +RSpec::Support::ObjectFormatter.default_instance.max_formatted_output_length = 10_000 # do not trash string like: expected: "abc .... def" +RSpec.configure do |config| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + config.expect_with(:rspec) { |c| c.syntax = [:should, :expect] } + config.include FactoryGirl::Syntax::Methods + config.include Rspec::Dunlop::GeneralHelpers + config.include Rspec::Dunlop::FeatureHelpers, type: :feature + config.include Devise::Test::ControllerHelpers, type: :controller + config.include ControllerJuice, type: :controller + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = false + config.before :suite do + #FactoryGirl.reload # to add the adapter factories + #DatabaseCleaner.clean_with(:truncation) + DatabaseCleaner.clean_with(:truncation) + Dunlop::NestedLogger.options[:yaml_options][:line_width] = 2000 + Warden.test_mode! + end + + config.before :each do + DatabaseCleaner.clean + end + + # RSpec Rails can automatically mix in different behaviours to your tests + # based on their file location, for example enabling you to call `get` and + # `post` in specs under `spec/controllers`. + # + # You can disable this behaviour by removing the line below, and instead + # explicitly tag your specs with their type, e.g.: + # + # RSpec.describe UsersController, :type => :controller do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://relishapp.com/rspec/rspec-rails/docs + config.infer_spec_type_from_file_location! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") +end diff --git a/spec/routing/app_routing_spec.rb b/spec/routing/app_routing_spec.rb new file mode 100644 index 0000000..2f04849 --- /dev/null +++ b/spec/routing/app_routing_spec.rb @@ -0,0 +1,17 @@ +describe "app routing" do + routes { Dunlop::Ember::Engine.routes } + it 'has a root' do + expect(get: "/").to route_to( + controller: "dunlop/ember/application", + action: "ember_path" + ) + end + + it "works with dashes", type: :routing do + expect(get: "abc-def").to route_to( + controller: "dunlop/ember/application", + action: "ember_path", + rest: 'abc-def' + ) + end +end diff --git a/spec/services/flat_keys_spec.rb b/spec/services/flat_keys_spec.rb new file mode 100644 index 0000000..5c0668c --- /dev/null +++ b/spec/services/flat_keys_spec.rb @@ -0,0 +1,58 @@ +RSpec.describe FlatKeys do + describe '#as_nested_structure' do + it "works" do + result = FlatKeys.as_nested_structure([ + 'root_one', + 'inventories.location.site', + 'ppls.distribution_cables', + 'dslams.port_spec', + 'root_two', + 'dslams.other_spec', + 'root_three', + #'dslams.other_spec.and_then.some', # should raise + 'very.deep.nested.structure.just.because.we.can', + 'very.deep.nested.structure.with.other.tail', + 'start.with.more.than.what.comes.after', + 'start.with.more.than.what.comes', + 'start.with.less.than.what.comes', + 'start.with.less.than.what.comes2.after', + 'start.with.less.than.what.comes.after', + 'start.with.less.than.what.comes2.after', # duplicate, should be no problem + ]) + + expect( result ).to eq [ + :root_one, + :root_two, + :root_three, + { + inventories: {location: :site}, + ppls: :distribution_cables, + dslams: [:port_spec, :other_spec], + very: { + deep: { + nested: { + structure: { + just: { + because: {we: :can} + }, + with: {other: :tail} + } + } + } + }, + start: { + with: { + more: { + than: {what: {comes: :after}} + }, + less: { + than: {what: {comes: :after, comes2: :after}} + } + } + } + } + ] + end + + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..ce33d66 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,96 @@ +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + config.disable_monkey_patching! + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end