diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 00000000..4a63026f --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,4 @@ +inherit_from: .rubocop_todo.yml + +Style/StringLiterals: + EnforcedStyle: 'double_quotes' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 00000000..ef991d2e --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,118 @@ +# This configuration was generated by `rubocop --auto-gen-config` +# on 2015-01-30 11:07:37 -0800 using RuboCop version 0.28.0. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 1 +Lint/AmbiguousOperator: + Enabled: false + +# Offense count: 1 +Lint/HandleExceptions: + Enabled: false + +# Offense count: 5 +Lint/ShadowingOuterLocalVariable: + Enabled: false + +# Offense count: 1 +Lint/UselessAccessModifier: + Enabled: false + +# Offense count: 7 +Lint/UselessAssignment: + Enabled: false + +# Offense count: 11 +Metrics/AbcSize: + Max: 90 + +# Offense count: 3 +Metrics/BlockNesting: + Max: 4 + +# Offense count: 2 +# Configuration parameters: CountComments. +Metrics/ClassLength: + Max: 277 + +# Offense count: 6 +Metrics/CyclomaticComplexity: + Max: 15 + +# Offense count: 345 +# Configuration parameters: AllowURI, URISchemes. +Metrics/LineLength: + Max: 274 + +# Offense count: 10 +# Configuration parameters: CountComments. +Metrics/MethodLength: + Max: 51 + +# Offense count: 5 +Metrics/PerceivedComplexity: + Max: 19 + +# Offense count: 1 +Style/AccessorMethodName: + Enabled: false + +# Offense count: 2 +# Configuration parameters: IndentWhenRelativeTo, SupportedStyles, IndentOneStep. +Style/CaseIndentation: + Enabled: false + +# Offense count: 18 +Style/Documentation: + Enabled: false + +# Offense count: 1 +Style/EachWithObject: + Enabled: false + +# Offense count: 1 +Style/EvenOdd: + Enabled: false + +# Offense count: 1 +# Configuration parameters: MinBodyLength. +Style/GuardClause: + Enabled: false + +# Offense count: 1 +# Configuration parameters: MaxLineLength. +Style/IfUnlessModifier: + Enabled: false + +# Offense count: 2 +Style/NestedTernaryOperator: + Enabled: false + +# Offense count: 1 +# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles. +Style/Next: + Enabled: false + +# Offense count: 4 +Style/RegexpLiteral: + MaxSlashes: 0 + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AllowAsExpressionSeparator. +Style/Semicolon: + Enabled: false + +# Offense count: 1 +# Configuration parameters: Methods. +Style/SingleLineBlockParams: + Enabled: false + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, Whitelist. +Style/TrivialAccessors: + Enabled: false diff --git a/Gemfile b/Gemfile index 6e33f344..14fc4182 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ -source 'https://rubygems.org' +source "https://rubygems.org" # Specify your gem's dependencies in brainstem.gemspec gemspec diff --git a/Guardfile b/Guardfile index 808a0094..f543b8a2 100644 --- a/Guardfile +++ b/Guardfile @@ -1,8 +1,8 @@ # A sample Guardfile # More info at https://github.com/guard/guard#readme -guard 'rspec' do +guard "rspec" do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } - watch('spec/spec_helper.rb') { "spec" } + watch("spec/spec_helper.rb") { "spec" } end diff --git a/Rakefile b/Rakefile index a8a8eb24..d997ede2 100755 --- a/Rakefile +++ b/Rakefile @@ -3,4 +3,4 @@ require "bundler/gem_tasks" require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec) -task :default => :spec \ No newline at end of file +task default: :spec diff --git a/brainstem.gemspec b/brainstem.gemspec index c2e507c5..ab952cf4 100644 --- a/brainstem.gemspec +++ b/brainstem.gemspec @@ -1,18 +1,18 @@ # -*- encoding: utf-8 -*- -$LOAD_PATH.unshift File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift File.expand_path("../lib", __FILE__) require "brainstem/version" Gem::Specification.new do |gem| gem.name = "brainstem" gem.authors = ["Mavenlink"] gem.email = ["opensource@mavenlink.com"] - gem.description = %q{Brainstem allows you to create rich API presenters that know how to filter, sort, and include associations.} - gem.summary = %q{ActiveRecord presenters with a rich request API} + gem.description = "Brainstem allows you to create rich API presenters that know how to filter, sort, and include associations." + gem.summary = "ActiveRecord presenters with a rich request API" gem.homepage = "http://github.com/mavenlink/brainstem" gem.license = "MIT" gem.files = Dir["**/*"] - gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } + gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ["lib"] gem.version = Brainstem::VERSION @@ -23,6 +23,7 @@ Gem::Specification.new do |gem| gem.add_development_dependency "redcarpet" # for markdown in yard gem.add_development_dependency "rr" gem.add_development_dependency "rspec" + gem.add_development_dependency "rubocop" gem.add_development_dependency "sqlite3" gem.add_development_dependency "yard" end diff --git a/lib/brainstem.rb b/lib/brainstem.rb index 5ec0549e..8cddde8b 100644 --- a/lib/brainstem.rb +++ b/lib/brainstem.rb @@ -8,8 +8,8 @@ module Brainstem # Sets {default_namespace} to a new value. # @param [String] namespace # @return [String] the new default namespace - def self.default_namespace=(namespace) - @default_namespace = namespace + class << self + attr_writer :default_namespace end # The namespace that will be used by {presenter_collection} and {add_presenter_class} if none is given or implied. @@ -55,7 +55,7 @@ def self.logger # Sets a new Brainstem logger. # @param [Logger] logger A new Brainstem logger. # @return [Logger] The new Brainstem logger. - def self.logger=(logger) - @logger = logger + class << self + attr_writer :logger end end diff --git a/lib/brainstem/association_field.rb b/lib/brainstem/association_field.rb index c1cf807c..751408be 100644 --- a/lib/brainstem/association_field.rb +++ b/lib/brainstem/association_field.rb @@ -33,13 +33,13 @@ def initialize(*args, &block) @ignore_type = options[:ignore_type] || false @restrict_to_only = options[:restrict_to_only] || false if block_given? - raise ArgumentError, "options[:json_name] is required when using a block" unless options[:json_name] - raise ArgumentError, "Method name is invalid with a block" if method_name + fail ArgumentError, "options[:json_name] is required when using a block" unless options[:json_name] + fail ArgumentError, "Method name is invalid with a block" if method_name @block = block elsif method_name @method_name = method_name else - raise ArgumentError, "Method name or block is required" + fail ArgumentError, "Method name or block is required" end end diff --git a/lib/brainstem/controller_methods.rb b/lib/brainstem/controller_methods.rb index a04f3b34..e0875442 100644 --- a/lib/brainstem/controller_methods.rb +++ b/lib/brainstem/controller_methods.rb @@ -1,10 +1,8 @@ module Brainstem - # ControllerMethods are intended to be included into controllers that will be handling requests for presented objects. # The present method will pass through +params+, so that any allowed and requested includes, filters, sort orders # will be applied to the presented data. module ControllerMethods - # Return a Ruby hash that contains models requested by the user's params and allowed # by the +name+ presenter's configuration. # @@ -16,7 +14,7 @@ module ControllerMethods # @yield (see PresenterCollection#presenting) # @return (see PresenterCollection#presenting) def present(name, options = {}, &block) - Brainstem.presenter_collection(options[:namespace]).presenting(name, options.reverse_merge(:params => params), &block) + Brainstem.presenter_collection(options[:namespace]).presenting(name, options.reverse_merge(params: params), &block) end # Similar to ControllerMethods#present, but always returns all of the given objects, not just those that match any provided @@ -27,10 +25,10 @@ def present(name, options = {}, &block) # only required if the name cannot be inferred. # @return (see PresenterCollection#presenting) def present_object(objects, options = {}) - options.merge!(:params => params, :apply_default_filters => false) + options.merge!(params: params, apply_default_filters: false) if objects.is_a?(ActiveRecord::Relation) || objects.is_a?(Array) - raise ActiveRecord::RecordNotFound if objects.empty? + fail ActiveRecord::RecordNotFound if objects.empty? klass = objects.first.class ids = objects.map(&:id) else @@ -40,7 +38,7 @@ def present_object(objects, options = {}) end options[:as] = (options[:key_map] || {})[klass.to_s] || klass.table_name - present(klass, options) { klass.where(:id => ids) } + present(klass, options) { klass.where(id: ids) } end alias_method :present_objects, :present_object end diff --git a/lib/brainstem/presenter.rb b/lib/brainstem/presenter.rb index 65202dac..5d52796c 100644 --- a/lib/brainstem/presenter.rb +++ b/lib/brainstem/presenter.rb @@ -1,11 +1,10 @@ -require 'date' -require 'brainstem/association_field' -require 'brainstem/time_classes' +require "date" +require "brainstem/association_field" +require "brainstem/time_classes" module Brainstem # @abstract Subclass and override {#present} to implement a presenter. class Presenter - # Class methods # Accepts a list of classes this presenter knows how to present. @@ -37,14 +36,14 @@ def self.default_sort_order(sort_string = nil) # Create a named sort order, either containing a string to use as ORDER in a query, or with a block that adds an order Arel predicate to a scope. # @raise [ArgumentError] if neither an order string or block is given. def self.sort_order(name, order = nil, &block) - raise ArgumentError, "A sort order must be given" unless block_given? || order + fail ArgumentError, "A sort order must be given" unless block_given? || order @sort_orders ||= HashWithIndifferentAccess.new @sort_orders[name] = (block_given? ? block : order) end # @return [Hash] All defined sort orders, keyed by their name. - def self.sort_orders - @sort_orders + class << self + attr_reader :sort_orders end # @overload filter(name, options = {}) @@ -61,16 +60,16 @@ def self.filter(name, options = {}, &block) end # @return [Hash] All defined filters, keyed by their name. - def self.filters - @filters + class << self + attr_reader :filters end def self.search(&block) @search_block = block end - def self.search_block - @search_block + class << self + attr_reader :search_block end # Declares a helper module whose methods will be available in instances of the presenter class and available inside sort and filter blocks. @@ -81,12 +80,11 @@ def self.helper(mod) extend mod end - # Instance methods # @raise [RuntimeError] if this method has not been overridden in the presenter subclass. - def present(model) - raise "Please override #present(model) in your subclass of Brainstem::Presenter" + def present(_model) + fail "Please override #present(model) in your subclass of Brainstem::Presenter" end # @api private @@ -124,7 +122,7 @@ def group_present(models, associations = []) end # Subclasses can define this if they wish. This method will be called before {#present}. - def custom_preload(models, associations = []) + def custom_preload(_models, _associations = []) end # @api private @@ -135,10 +133,10 @@ def datetimes_to_json(struct) struct.map { |value| datetimes_to_json(value) } when Hash processed = {} - struct.each { |k,v| processed[k] = datetimes_to_json(v) } + struct.each { |k, v| processed[k] = datetimes_to_json(v) } processed when Date - struct.strftime('%F') + struct.strftime("%F") when *TIME_CLASSES # Time, ActiveSupport::TimeWithZone struct.iso8601 else @@ -155,14 +153,14 @@ def load_associations!(model, struct, associations) struct.delete key id_attr = value.method_name ? "#{value.method_name}_id" : nil - if id_attr && model.class.columns_hash.has_key?(id_attr) + if id_attr && model.class.columns_hash.key?(id_attr) reflection = value.method_name && reflections[value.method_name.to_s] if reflection && reflection.options[:polymorphic] && !value.ignore_type struct["#{key.to_s.singularize}_ref".to_sym] = begin if (id_attr = model.send(id_attr)).present? { - :id => to_s_except_nil(id_attr), - :key => model.send("#{value.method_name}_type").try(:constantize).try(:table_name) + id: to_s_except_nil(id_attr), + key: model.send("#{value.method_name}_type").try(:constantize).try(:table_name) } end end @@ -172,7 +170,7 @@ def load_associations!(model, struct, associations) elsif associations.include?(key.to_s) result = value.call(model) if result.is_a?(Array) || result.is_a?(ActiveRecord::Relation) - struct["#{key.to_s.singularize}_ids".to_sym] = result.map {|a| to_s_except_nil(a.is_a?(ActiveRecord::Base) ? a.id : a) } + struct["#{key.to_s.singularize}_ids".to_sym] = result.map { |a| to_s_except_nil(a.is_a?(ActiveRecord::Base) ? a.id : a) } else if result.is_a?(ActiveRecord::Base) struct["#{key.to_s.singularize}_id".to_sym] = to_s_except_nil(result.id) diff --git a/lib/brainstem/presenter_collection.rb b/lib/brainstem/presenter_collection.rb index 9801d38c..befb67ba 100644 --- a/lib/brainstem/presenter_collection.rb +++ b/lib/brainstem/presenter_collection.rb @@ -1,9 +1,8 @@ -require 'brainstem/association_field' -require 'brainstem/search_unavailable_error' +require "brainstem/association_field" +require "brainstem/search_unavailable_error" module Brainstem class PresenterCollection - # @!attribute default_max_per_page # @return [Integer] The maximum number of objects that can be requested in a single presented hash. attr_accessor :default_max_per_page @@ -78,7 +77,7 @@ def presenting(name, options = {}, &block) # Determine if an exception should be raised on an empty result set. if options[:raise_on_empty] && records.empty? - raise options[:empty_error_class] || ActiveRecord::RecordNotFound + fail options[:empty_error_class] || ActiveRecord::RecordNotFound end records = order_for_search(records, ordered_search_ids) if searching? options @@ -94,7 +93,7 @@ def presenting(name, options = {}, &block) if models.length > 0 presenter = for!(models.first.class) - assoc = includes_hash.to_a.find { |k, v| v.json_name == json_name } + assoc = includes_hash.to_a.find { |_k, v| v.json_name == json_name } struct[json_name] = presenter.group_present(models, []) else struct[json_name] = [] @@ -104,7 +103,7 @@ def presenting(name, options = {}, &block) if primary_models.length > 0 presented_primary_models = options[:presenter].group_present(models, includes_hash.keys) struct[options[:as]] += presented_primary_models - struct[:results] = presented_primary_models.map { |model| { :key => options[:as].to_s, :id => model[:id] } } + struct[:results] = presented_primary_models.map { |model| { key: options[:as].to_s, id: model[:id] } } end rewrite_keys_as_objects!(struct) @@ -133,7 +132,7 @@ def for(klass) # @return [Brainstem::Presenter] The presenter that knows how to present the class +klass+. # @raise [ArgumentError] if there is no known presenter for +klass+. def for!(klass) - self.for(klass) || raise(ArgumentError, "Unable to find a presenter for class #{klass}") + self.for(klass) || fail(ArgumentError, "Unable to find a presenter for class #{klass}") end private @@ -185,7 +184,7 @@ def calculate_allowed_includes(presenter, presented_class, is_only_query) if association && !association.options[:polymorphic] v.json_name = association && association.table_name.to_sym if v.json_name.nil? - raise ":json_name is a required option for method-based associations (#{presented_class}##{v.method_name})" + fail ":json_name is a required option for method-based associations (#{presented_class}##{v.method_name})" end end end @@ -197,7 +196,7 @@ def calculate_allowed_includes(presenter, presented_class, is_only_query) def filter_includes(user_includes, allowed_includes) filtered_includes = {} - (user_includes || '').split(',').each do |k| + (user_includes || "").split(",").each do |k| allowed = allowed_includes[k] if allowed filtered_includes[k] = allowed @@ -207,8 +206,8 @@ def filter_includes(user_includes, allowed_includes) end def handle_only(scope, only) - ids = (only || "").split(",").select {|id| id =~ /\A\d+\Z/}.uniq - [scope.where(:id => ids), scope.where(:id => ids).count] + ids = (only || "").split(",").select { |id| id =~ /\A\d+\Z/ }.uniq + [scope.where(id: ids), scope.where(id: ids).count] end def run_filters(scope, options) @@ -250,8 +249,8 @@ def run_search(scope, includes, sort_name, direction, options) return scope unless searching? options search_options = HashWithIndifferentAccess.new( - :include => includes, - :order => { :sort_order => sort_name, :direction => direction }, + include: includes, + order: { sort_order: sort_name, direction: direction } ) if options[:params][:limit].present? && options[:params][:offset].present? @@ -266,9 +265,9 @@ def run_search(scope, includes, sort_name, direction, options) result_ids, count = options[:presenter].search_block.call(options[:params][:search], search_options) if result_ids - [scope.where(:id => result_ids ), count, result_ids] + [scope.where(id: result_ids), count, result_ids] else - raise(SearchUnavailableError, 'Search is currently unavailable') + fail(SearchUnavailableError, "Search is currently unavailable") end end @@ -321,19 +320,19 @@ def calculate_sort_name_and_direction(options) direction = default_direction end - [sort_name, direction == 'desc' ? 'desc' : 'asc'] + [sort_name, direction == "desc" ? "desc" : "asc"] end def perform_preloading(records, includes_hash) records.tap do |models| - association_names_to_preload = includes_hash.values.map {|i| i.method_name } + association_names_to_preload = includes_hash.values.map(&:method_name) if models.first reflections = Brainstem::PresenterCollection.reflections(models.first.class) - association_names_to_preload.reject! { |association| !reflections.has_key?(association.to_s) } + association_names_to_preload.reject! { |association| !reflections.key?(association.to_s) } end if association_names_to_preload.any? Brainstem::PresenterCollection.preload(models, association_names_to_preload) - Brainstem.logger.info "Eager loaded #{association_names_to_preload.join(", ")}." + Brainstem.logger.info "Eager loaded #{association_names_to_preload.join(', ')}." end end end @@ -342,14 +341,14 @@ def gather_associations(models, includes_hash) record_hash = {} primary_models = [] - includes_hash.each do |include, include_data| + includes_hash.each do |_include, include_data| record_hash[include_data.json_name] ||= [] if include_data.json_name end models.each do |model| primary_models << model - includes_hash.each do |include, include_data| + includes_hash.each do |_include, include_data| models = Array(include_data.call(model)) if include_data.json_name @@ -370,12 +369,12 @@ def gather_associations(models, includes_hash) def rewrite_keys_as_objects!(struct) (struct.keys - [:count, :results]).each do |key| - struct[key] = struct[key].inject({}) {|memo, obj| memo[obj[:id] || obj["id"] || "unknown_id"] = obj; memo } + struct[key] = struct[key].inject({}) { |memo, obj| memo[obj[:id] || obj["id"] || "unknown_id"] = obj; memo } end end def set_default_filters_option!(options) - return unless options[:params].has_key?(:apply_default_filters) + return unless options[:params].key?(:apply_default_filters) options[:apply_default_filters] = [true, "true", "TRUE", 1, "1"].include? options[:params].delete(:apply_default_filters) end @@ -388,7 +387,7 @@ def self.reflections(klass) end def self.preload(models, association_names) - if Gem.loaded_specs['activerecord'].version >= Gem::Version.create('4.1') + if Gem.loaded_specs["activerecord"].version >= Gem::Version.create("4.1") ActiveRecord::Associations::Preloader.new.preload(models, association_names) else ActiveRecord::Associations::Preloader.new(models, association_names).run diff --git a/lib/brainstem/time_classes.rb b/lib/brainstem/time_classes.rb index af5f2265..a32a92e5 100644 --- a/lib/brainstem/time_classes.rb +++ b/lib/brainstem/time_classes.rb @@ -6,9 +6,9 @@ class Presenter TIME_CLASSES = [Time] begin - require 'active_support/time_with_zone' + require "active_support/time_with_zone" TIME_CLASSES << ActiveSupport::TimeWithZone rescue LoadError end end -end \ No newline at end of file +end diff --git a/spec/brainstem/controller_methods_spec.rb b/spec/brainstem/controller_methods_spec.rb index 30da3af6..716c92b7 100644 --- a/spec/brainstem/controller_methods_spec.rb +++ b/spec/brainstem/controller_methods_spec.rb @@ -1,5 +1,5 @@ -require 'spec_helper' -require 'spec_helpers/presenters' +require "spec_helper" +require "spec_helpers/presenters" describe Brainstem::ControllerMethods do class FakeController @@ -8,7 +8,7 @@ class FakeController attr_accessor :call_results def params - { :a => :b } + { a: :b } end end @@ -27,7 +27,7 @@ def params describe "calling #present with sensible params" do before do def @controller.present(klass, options) - @call_results = { :klass => klass, :options => options, :block_result => yield } + @call_results = { klass: klass, options: options, block_result: yield } end end @@ -53,19 +53,19 @@ def @controller.present(klass, options) end it "accepts a key map" do - @controller.present_object(Workspace.find(1), :key_map => { "Workspace" => "your_workspaces" }) + @controller.present_object(Workspace.find(1), key_map: { "Workspace" => "your_workspaces" }) expect(@controller.call_results[:klass]).to eq(Workspace) expect(@controller.call_results[:options][:as]).to eq("your_workspaces") expect(@controller.call_results[:block_result].pluck(:id)).to eq([1]) end it "passes through the controller params" do - @controller.present_object(Workspace.find(1), :key_map => { "Workspace" => "your_workspaces" }) - expect(@controller.call_results[:options][:params]).to eq(@controller.params.merge(:only => '1')) + @controller.present_object(Workspace.find(1), key_map: { "Workspace" => "your_workspaces" }) + expect(@controller.call_results[:options][:params]).to eq(@controller.params.merge(only: "1")) end it "passes through supplied options" do - @controller.present_object(Workspace.find(1), :foo => :bar) + @controller.present_object(Workspace.find(1), foo: :bar) expect(@controller.call_results[:options][:foo]).to eq(:bar) end diff --git a/spec/brainstem/presenter_collection_spec.rb b/spec/brainstem/presenter_collection_spec.rb index 49850c7f..3d14753d 100644 --- a/spec/brainstem/presenter_collection_spec.rb +++ b/spec/brainstem/presenter_collection_spec.rb @@ -1,5 +1,5 @@ -require 'spec_helper' -require 'spec_helpers/presenters' +require "spec_helper" +require "spec_helpers/presenters" describe Brainstem::PresenterCollection do before do @@ -10,9 +10,9 @@ @presenter_collection = Brainstem.presenter_collection end - let(:bob) { User.where(:username => "bob").first } + let(:bob) { User.where(username: "bob").first } let(:bob_workspaces_ids) { bob.workspaces.map(&:id) } - let(:jane) { User.where(:username => "jane").first } + let(:jane) { User.where(username: "jane").first } describe "#presenting" do describe "#pagination" do @@ -22,81 +22,81 @@ end it "has a global per_page default" do - expect(@presenter_collection.presenting("workspaces") { Workspace.order('id desc') }[:workspaces].length).to eq(2) + expect(@presenter_collection.presenting("workspaces") { Workspace.order("id desc") }[:workspaces].length).to eq(2) end it "will not accept a per_page less than 1" do - expect(@presenter_collection.presenting("workspaces", :params => { :per_page => 0 }) { Workspace.order('id desc') }[:workspaces].length).to eq(2) - expect(@presenter_collection.presenting("workspaces", :per_page => 0) { Workspace.order('id desc') }[:workspaces].length).to eq(2) + expect(@presenter_collection.presenting("workspaces", params: { per_page: 0 }) { Workspace.order("id desc") }[:workspaces].length).to eq(2) + expect(@presenter_collection.presenting("workspaces", per_page: 0) { Workspace.order("id desc") }[:workspaces].length).to eq(2) end it "will accept strings" do - struct = @presenter_collection.presenting("workspaces", :params => { :per_page => "1", :page => "2" }) { Workspace.order('id desc') } - expect(struct[:results].first[:id]).to eq(Workspace.order('id desc')[1].id.to_s) + struct = @presenter_collection.presenting("workspaces", params: { per_page: "1", page: "2" }) { Workspace.order("id desc") } + expect(struct[:results].first[:id]).to eq(Workspace.order("id desc")[1].id.to_s) end it "has a global max_per_page default" do - expect(@presenter_collection.presenting("workspaces", :params => { :per_page => 5 }) { Workspace.order('id desc') }[:workspaces].length).to eq(3) + expect(@presenter_collection.presenting("workspaces", params: { per_page: 5 }) { Workspace.order("id desc") }[:workspaces].length).to eq(3) end it "takes a configurable default page size and max page size" do - expect(@presenter_collection.presenting("workspaces", :params => { :per_page => 5 }, :max_per_page => 4) { Workspace.order('id desc') }[:workspaces].length).to eq(4) + expect(@presenter_collection.presenting("workspaces", params: { per_page: 5 }, max_per_page: 4) { Workspace.order("id desc") }[:workspaces].length).to eq(4) end describe "limits and offsets" do context "when only per_page and page are present" do it "honors the user's requested page size and page and returns counts" do - result = @presenter_collection.presenting("workspaces", :params => { :per_page => 1, :page => 2 }) { Workspace.order('id desc') }[:results] + result = @presenter_collection.presenting("workspaces", params: { per_page: 1, page: 2 }) { Workspace.order("id desc") }[:results] expect(result.length).to eq(1) - expect(result.first[:id]).to eq(Workspace.order('id desc')[1].id.to_s) + expect(result.first[:id]).to eq(Workspace.order("id desc")[1].id.to_s) - result = @presenter_collection.presenting("workspaces", :params => { :per_page => 2, :page => 2 }) { Workspace.order('id desc') }[:results] + result = @presenter_collection.presenting("workspaces", params: { per_page: 2, page: 2 }) { Workspace.order("id desc") }[:results] expect(result.length).to eq(2) - expect(result.map { |m| m[:id] }).to eq(Workspace.order('id desc')[2..3].map(&:id).map(&:to_s)) + expect(result.map { |m| m[:id] }).to eq(Workspace.order("id desc")[2..3].map(&:id).map(&:to_s)) end it "defaults to 1 if the page number is less than 1" do - result = @presenter_collection.presenting("workspaces", :params => { :per_page => 1, :page => 0 }) { Workspace.order('id desc') }[:results] + result = @presenter_collection.presenting("workspaces", params: { per_page: 1, page: 0 }) { Workspace.order("id desc") }[:results] expect(result.length).to eq(1) - expect(result.first[:id]).to eq(Workspace.order('id desc')[0].id.to_s) + expect(result.first[:id]).to eq(Workspace.order("id desc")[0].id.to_s) end end context "when only limit and offset are present" do it "honors the user's requested limit and offset and returns counts" do - result = @presenter_collection.presenting("workspaces", :params => { :limit => 1, :offset => 2 }) { Workspace.order('id desc') }[:results] + result = @presenter_collection.presenting("workspaces", params: { limit: 1, offset: 2 }) { Workspace.order("id desc") }[:results] expect(result.length).to eq(1) - expect(result.first[:id]).to eq(Workspace.order('id desc')[2].id.to_s) + expect(result.first[:id]).to eq(Workspace.order("id desc")[2].id.to_s) - result = @presenter_collection.presenting("workspaces", :params => { :limit => 2, :offset => 2 }) { Workspace.order('id desc') }[:results] + result = @presenter_collection.presenting("workspaces", params: { limit: 2, offset: 2 }) { Workspace.order("id desc") }[:results] expect(result.length).to eq(2) - expect(result.map { |m| m[:id] }).to eq(Workspace.order('id desc')[2..3].map(&:id).map(&:to_s)) + expect(result.map { |m| m[:id] }).to eq(Workspace.order("id desc")[2..3].map(&:id).map(&:to_s)) end it "defaults to offset 0 if the passed offset is less than 0 and limit to 1 if the passed limit is less than 1" do stub.proxy(@presenter_collection).calculate_offset(anything).times(1) stub.proxy(@presenter_collection).calculate_limit(anything).times(1) - result = @presenter_collection.presenting("workspaces", :params => { :limit => -1, :offset => -1 }) { Workspace.order('id desc') }[:results] + result = @presenter_collection.presenting("workspaces", params: { limit: -1, offset: -1 }) { Workspace.order("id desc") }[:results] expect(result.length).to eq(1) - expect(result.first[:id]).to eq(Workspace.order('id desc')[0].id.to_s) + expect(result.first[:id]).to eq(Workspace.order("id desc")[0].id.to_s) end end context "when both sets of params are present" do it "prefers limit and offset over per_page and page" do - result = @presenter_collection.presenting("workspaces", :params => { :limit => 1, :offset => 0, :per_page => 2, :page => 2 }) { Workspace.order('id desc') }[:results] + result = @presenter_collection.presenting("workspaces", params: { limit: 1, offset: 0, per_page: 2, page: 2 }) { Workspace.order("id desc") }[:results] expect(result.length).to eq(1) - expect(result.first[:id]).to eq(Workspace.order('id desc')[0].id.to_s) + expect(result.first[:id]).to eq(Workspace.order("id desc")[0].id.to_s) end it "uses per_page and page if limit and offset are not complete" do - result = @presenter_collection.presenting("workspaces", :params => { :limit => 5, :per_page => 1, :page => 0 }) { Workspace.order('id desc') }[:results] + result = @presenter_collection.presenting("workspaces", params: { limit: 5, per_page: 1, page: 0 }) { Workspace.order("id desc") }[:results] expect(result.length).to eq(1) - expect(result.first[:id]).to eq(Workspace.order('id desc')[0].id.to_s) + expect(result.first[:id]).to eq(Workspace.order("id desc")[0].id.to_s) - result = @presenter_collection.presenting("workspaces", :params => { :offset => 5, :per_page => 1, :page => 0 }) { Workspace.order('id desc') }[:results] + result = @presenter_collection.presenting("workspaces", params: { offset: 5, per_page: 1, page: 0 }) { Workspace.order("id desc") }[:results] expect(result.length).to eq(1) - expect(result.first[:id]).to eq(Workspace.order('id desc')[0].id.to_s) + expect(result.first[:id]).to eq(Workspace.order("id desc")[0].id.to_s) end end end @@ -107,15 +107,15 @@ it "should raise the provided error class when the empty_error_class option is provided" do class MyException < Exception; end - expect { - @presenter_collection.presenting("workspaces", :raise_on_empty => true, :empty_error_class => MyException) { Workspace.where(:id => nil) } - }.to raise_error(MyException) + expect do + @presenter_collection.presenting("workspaces", raise_on_empty: true, empty_error_class: MyException) { Workspace.where(id: nil) } + end.to raise_error(MyException) end it "should raise ActiveRecord::RecordNotFound when the empty_error_class option is not provided" do - expect { - @presenter_collection.presenting("workspaces", :raise_on_empty => true) { Workspace.where(:id => nil) } - }.to raise_error(ActiveRecord::RecordNotFound) + expect do + @presenter_collection.presenting("workspaces", raise_on_empty: true) { Workspace.where(id: nil) } + end.to raise_error(ActiveRecord::RecordNotFound) end end @@ -123,18 +123,18 @@ class MyException < Exception; end it "should not raise an exception" do expect(Workspace.count).to be > 0 - expect { - @presenter_collection.presenting("workspaces", :raise_on_empty => true) { Workspace.order('id desc') } - }.not_to raise_error + expect do + @presenter_collection.presenting("workspaces", raise_on_empty: true) { Workspace.order("id desc") } + end.not_to raise_error end end end context "raise_on_empty is false" do it "should not raise an exception when the results are empty" do - expect { - @presenter_collection.presenting("workspaces") { Workspace.where(:id => nil) } - }.not_to raise_error + expect do + @presenter_collection.presenting("workspaces") { Workspace.where(id: nil) } + end.not_to raise_error end end end @@ -146,7 +146,7 @@ class MyException < Exception; end end it "returns the unique count by model id" do - result = @presenter_collection.presenting("workspaces", :params => { :per_page => 2, :page => 1 }) { Workspace.order('id desc') } + result = @presenter_collection.presenting("workspaces", params: { per_page: 2, page: 1 }) { Workspace.order("id desc") } expect(result[:count]).to eq(Workspace.count) end end @@ -154,22 +154,22 @@ class MyException < Exception; end describe "uses presenters" do it "finds presenter by table name string" do - result = @presenter_collection.presenting("workspaces") { Workspace.order('id desc') } + result = @presenter_collection.presenting("workspaces") { Workspace.order("id desc") } expect(result[:workspaces].length).to eq(Workspace.count) end it "finds presenter by model name string" do - result = @presenter_collection.presenting("Workspace") { order('id desc') } + result = @presenter_collection.presenting("Workspace") { order("id desc") } expect(result[:workspaces].length).to eq(Workspace.count) end it "finds presenter by model" do - result = @presenter_collection.presenting(Workspace) { order('id desc') } + result = @presenter_collection.presenting(Workspace) { order("id desc") } expect(result[:workspaces].length).to eq(Workspace.count) end it "infers the table name from the model" do - result = @presenter_collection.presenting("not_workspaces", :model => "Workspace", :params => { :per_page => 2, :page => 1 }) { Workspace.order('id desc') } + result = @presenter_collection.presenting("not_workspaces", model: "Workspace", params: { per_page: 2, page: 1 }) { Workspace.order("id desc") } expect(result[:not_workspaces]).not_to be_empty expect(result[:count]).to eq(Workspace.count) end @@ -177,67 +177,67 @@ class MyException < Exception; end describe "the 'results' top level key" do it "comes back with an explicit list of the matching results" do - structure = @presenter_collection.presenting("workspaces", :params => { :include => "tasks" }, :max_per_page => 2) { Workspace.where(:id => 1) } + structure = @presenter_collection.presenting("workspaces", params: { include: "tasks" }, max_per_page: 2) { Workspace.where(id: 1) } expect(structure.keys).to match_array([:workspaces, :tasks, :count, :results]) - expect(structure[:results]).to eq(Workspace.where(:id => 1).limit(2).map {|w| { :key => "workspaces", :id => w.id.to_s } }) - expect(structure[:workspaces].keys).to eq(%w[1]) + expect(structure[:results]).to eq(Workspace.where(id: 1).limit(2).map { |w| { key: "workspaces", id: w.id.to_s } }) + expect(structure[:workspaces].keys).to eq(%w(1)) end end describe "includes" do it "reads allowed includes from the presenter" do - result = @presenter_collection.presenting("workspaces", :params => { :include => "drop table,tasks,users" }) { Workspace.order('id desc') } + result = @presenter_collection.presenting("workspaces", params: { include: "drop table,tasks,users" }) { Workspace.order("id desc") } expect(result.keys).to match_array([:count, :workspaces, :tasks, :results]) - result = @presenter_collection.presenting("workspaces", :params => { :include => "foo,tasks,lead_user" }) { Workspace.order('id desc') } + result = @presenter_collection.presenting("workspaces", params: { include: "foo,tasks,lead_user" }) { Workspace.order("id desc") } expect(result.keys).to match_array([:count, :workspaces, :tasks, :users, :results]) end it "allows the allowed includes list to have different json names and association names" do result = @presenter_collection.presenting("tasks", - :params => { :include => "other_tasks" }) { Task.order('id desc') } + params: { include: "other_tasks" }) { Task.order("id desc") } expect(result[:tasks]).to be_present expect(result[:other_tasks]).to be_present end it "defaults to not include any allowed includes" do tasked_workspace = Task.first - result = @presenter_collection.presenting("workspaces", :max_per_page => 2) { Workspace.where(:id => tasked_workspace.workspace_id) } - expect(result[:workspaces].keys).to eq([ tasked_workspace.workspace_id.to_s ]) + result = @presenter_collection.presenting("workspaces", max_per_page: 2) { Workspace.where(id: tasked_workspace.workspace_id) } + expect(result[:workspaces].keys).to eq([tasked_workspace.workspace_id.to_s]) expect(result[:tasks]).to be_nil end it "loads has_many associations and returns them when requested" do - result = @presenter_collection.presenting("workspaces", :params => { :include => "tasks" }, :max_per_page => 2) { Workspace.where(:id => 1) } + result = @presenter_collection.presenting("workspaces", params: { include: "tasks" }, max_per_page: 2) { Workspace.where(id: 1) } expect(result[:tasks].keys).to match_array(Workspace.first.tasks.map(&:id).map(&:to_s)) expect(result[:workspaces]["1"][:task_ids]).to match_array(Workspace.first.tasks.map(&:id).map(&:to_s)) end it "returns appropriate fields" do result = @presenter_collection.presenting("workspaces", - :params => { :include => "tasks" }, - :max_per_page => 2) { Workspace.where(:id => 1) } + params: { include: "tasks" }, + max_per_page: 2) { Workspace.where(id: 1) } expect(result[:workspaces].values.first).to have_key(:description) expect(result[:tasks].values.first).to have_key(:name) end it "loads belongs_tos and returns them when requested" do - result = @presenter_collection.presenting("tasks", :params => { :include => "workspace" }, :max_per_page => 2) { Task.where(:id => 1) } - expect(result[:workspaces].keys).to eq(%w[1]) + result = @presenter_collection.presenting("tasks", params: { include: "workspace" }, max_per_page: 2) { Task.where(id: 1) } + expect(result[:workspaces].keys).to eq(%w(1)) end it "doesn't return nils when belong_tos are missing" do t = Task.first t.update_attribute :workspace, nil expect(t.reload.workspace).to be_nil - result = @presenter_collection.presenting("tasks", :params => { :include => "workspace" }, :max_per_page => 2) { Task.where(:id => t.id) } - expect(result[:tasks].keys).to eq([ t.id.to_s ]) + result = @presenter_collection.presenting("tasks", params: { include: "workspace" }, max_per_page: 2) { Task.where(id: t.id) } + expect(result[:tasks].keys).to eq([t.id.to_s]) expect(result[:workspaces]).to eq({}) expect(result.keys).to match_array([:tasks, :workspaces, :count, :results]) end it "returns sensible data when including something of the same type as the primary model" do - result = @presenter_collection.presenting("tasks", :params => { :include => "sub_tasks" }) { Task.where(:id => 2) } + result = @presenter_collection.presenting("tasks", params: { include: "sub_tasks" }) { Task.where(id: 2) } sub_task_ids = Task.find(2).sub_tasks.map(&:id).map(&:to_s) expect(result[:tasks].keys).to match_array(sub_task_ids + ["2"]) expect(result[:tasks]["2"][:sub_task_ids]).to eq(sub_task_ids) # The primary should have a sub_story_ids array. @@ -245,14 +245,14 @@ class MyException < Exception; end end it "includes requested includes even when all records are filtered" do - result = @presenter_collection.presenting("workspaces", :params => { :only => "not an id", :include => "not an include,tasks" }) { Workspace.order("id desc") } + result = @presenter_collection.presenting("workspaces", params: { only: "not an id", include: "not an include,tasks" }) { Workspace.order("id desc") } expect(result[:workspaces].length).to eq(0) expect(result[:tasks].length).to eq(0) end it "includes requested includes even when the scope has no records" do - expect(Workspace.where(:id => 123456789)).to be_empty - result = @presenter_collection.presenting("workspaces", :params => { :include => "not an include,tasks" }) { Workspace.where(:id => 123456789) } + expect(Workspace.where(id: 123_456_789)).to be_empty + result = @presenter_collection.presenting("workspaces", params: { include: "not an include,tasks" }) { Workspace.where(id: 123_456_789) } expect(result[:workspaces].length).to eq(0) expect(result[:tasks].length).to eq(0) end @@ -260,18 +260,18 @@ class MyException < Exception; end it "preloads associations when they are full model-level associations" do # Here, primary_maven is a method on Workspace, not a true association. mock(Brainstem::PresenterCollection).preload(anything, [:tasks]) - result = @presenter_collection.presenting("workspaces", :params => { :include => "tasks" }) { Workspace.order('id desc') } + result = @presenter_collection.presenting("workspaces", params: { include: "tasks" }) { Workspace.order("id desc") } expect(result[:tasks].length).to be > 0 end it "works with model methods that load records (but without preloading)" do - result = @presenter_collection.presenting("workspaces", :params => { :include => "lead_user" }) { Workspace.order('id desc') } + result = @presenter_collection.presenting("workspaces", params: { include: "lead_user" }) { Workspace.order("id desc") } expect(result[:workspaces][Workspace.first.id.to_s]).to be_present expect(result[:users][Workspace.first.lead_user.id.to_s]).to be_present end it "can accept a lambda for the association and uses that when present" do - result = @presenter_collection.presenting("users", :params => { :include => "odd_workspaces" }) { User.where(:id => 1) } + result = @presenter_collection.presenting("users", params: { include: "odd_workspaces" }) { User.where(id: 1) } expect(result[:odd_workspaces][Workspace.first.id.to_s]).to be_present expect(result[:users][Workspace.first.lead_user.id.to_s]).to be_present end @@ -279,14 +279,14 @@ class MyException < Exception; end describe "restricted associations" do it "does apply includes that are restricted to only queries in an only query" do t = Task.first - result = @presenter_collection.presenting("tasks", :params => { :include => "restricted", :only => t.id.to_s }, :max_per_page => 2) { Task.where(:id => t.id) } + result = @presenter_collection.presenting("tasks", params: { include: "restricted", only: t.id.to_s }, max_per_page: 2) { Task.where(id: t.id) } expect(result[:tasks][t.id.to_s].keys).to include(:restricted_id) expect(result.keys).to include(:restricted_associations) end it "does not apply includes that are restricted to only queries in a non-only query" do t = Task.first - result = @presenter_collection.presenting("tasks", :params => { :include => "restricted" }, :max_per_page => 2) { Task.where(:id => t.id) } + result = @presenter_collection.presenting("tasks", params: { include: "restricted" }, max_per_page: 2) { Task.where(id: t.id) } expect(result[:tasks][t.id.to_s].keys).not_to include(:restricted_id) expect(result.keys).not_to include(:restricted_associations) @@ -295,14 +295,14 @@ class MyException < Exception; end describe "polymorphic associations" do it "works with polymorphic associations" do - result = @presenter_collection.presenting("posts", :params => { :include => "subject" }) { Post.order('id desc') } + result = @presenter_collection.presenting("posts", params: { include: "subject" }) { Post.order("id desc") } expect(result[:posts][Post.first.id.to_s]).to be_present expect(result[:workspaces][Workspace.first.id.to_s]).to be_present expect(result[:tasks][Task.first.id.to_s]).to be_present end it "does not return an empty hash when none are found" do - result = @presenter_collection.presenting("posts", :params => { :include => "subject" }) { Post.where(:id => nil) } + result = @presenter_collection.presenting("posts", params: { include: "subject" }) { Post.where(id: nil) } expect(result).to have_key(:posts) expect(result).not_to have_key(:workspaces) expect(result).not_to have_key(:tasks) @@ -312,25 +312,25 @@ class MyException < Exception; end describe "handling of only" do it "accepts params[:only] as a list of ids to limit to" do - result = @presenter_collection.presenting("workspaces", :params => { :only => Workspace.limit(2).pluck(:id).join(",") }) { Workspace.order("id desc") } + result = @presenter_collection.presenting("workspaces", params: { only: Workspace.limit(2).pluck(:id).join(",") }) { Workspace.order("id desc") } expect(result[:workspaces].keys).to match_array(Workspace.limit(2).pluck(:id).map(&:to_s)) end it "does not paginate only requests" do dont_allow(@presenter_collection).paginate - @presenter_collection.presenting("workspaces", :params => { :only => Workspace.limit(2).pluck(:id).join(",") }) { Workspace.order("id desc") } + @presenter_collection.presenting("workspaces", params: { only: Workspace.limit(2).pluck(:id).join(",") }) { Workspace.order("id desc") } end it "escapes ids" do - result = @presenter_collection.presenting("workspaces", :params => { :only => "#{Workspace.first.id}foo,;drop tables;,#{Workspace.first.id}" }) { Workspace.order("id desc") } + result = @presenter_collection.presenting("workspaces", params: { only: "#{Workspace.first.id}foo,;drop tables;,#{Workspace.first.id}" }) { Workspace.order("id desc") } expect(result[:workspaces].length).to eq(1) end it "only runs when it receives ids" do - result = @presenter_collection.presenting("workspaces", :params => { :only => "" }) { Workspace.order("id desc") } + result = @presenter_collection.presenting("workspaces", params: { only: "" }) { Workspace.order("id desc") } expect(result[:workspaces].length).to be > 1 - result = @presenter_collection.presenting("workspaces", :params => { :only => "1" }) { Workspace.order("id desc") } + result = @presenter_collection.presenting("workspaces", params: { only: "1" }) { Workspace.order("id desc") } expect(result[:workspaces].length).to be <= 1 end end @@ -338,33 +338,33 @@ class MyException < Exception; end describe "filters" do before do WorkspacePresenter.filter(:owned_by) { |scope, user_id| scope.owned_by(user_id.to_i) } - WorkspacePresenter.filter(:title) { |scope, title| scope.where(:title => title) } + WorkspacePresenter.filter(:title) { |scope, title| scope.where(title: title) } end it "limits records to those matching given filters" do - result = @presenter_collection.presenting("workspaces", :params => { :owned_by => bob.id.to_s }) { Workspace.order("id desc") } # hit the API, filtering on owned_by:bob + result = @presenter_collection.presenting("workspaces", params: { owned_by: bob.id.to_s }) { Workspace.order("id desc") } # hit the API, filtering on owned_by:bob expect(result[:workspaces]).to be_present - expect(result[:workspaces].keys.all? {|id| bob_workspaces_ids.map(&:to_s).include?(id) }).to be_truthy # all of the returned workspaces should contain bob + expect(result[:workspaces].keys.all? { |id| bob_workspaces_ids.map(&:to_s).include?(id) }).to be_truthy # all of the returned workspaces should contain bob end it "returns all records if filters are not given" do result = @presenter_collection.presenting("workspaces") { Workspace.order("id desc") } # hit the API again, this time not filtering on anything - expect(result[:workspaces].keys.all? {|id| bob_workspaces_ids.map(&:to_s).include?(id) }).to be_falsey # the returned workspaces no longer all contain bob + expect(result[:workspaces].keys.all? { |id| bob_workspaces_ids.map(&:to_s).include?(id) }).to be_falsey # the returned workspaces no longer all contain bob end it "ignores unknown filters" do - result = @presenter_collection.presenting("workspaces", :params => { :wut => "is this?" }) { Workspace.order("id desc") } - expect(result[:workspaces].keys.all? {|id| bob_workspaces_ids.map(&:to_s).include?(id) }).to be_falsey + result = @presenter_collection.presenting("workspaces", params: { wut: "is this?" }) { Workspace.order("id desc") } + expect(result[:workspaces].keys.all? { |id| bob_workspaces_ids.map(&:to_s).include?(id) }).to be_falsey end it "limits records to those matching all given filters" do - result = @presenter_collection.presenting("workspaces", :params => { :owned_by => bob.id.to_s, :title => "bob workspace 1" }) { Workspace.order("id desc") } # try two filters - expect(result[:results].first[:id]).to eq(Workspace.where(:title => "bob workspace 1").first.id.to_s) + result = @presenter_collection.presenting("workspaces", params: { owned_by: bob.id.to_s, title: "bob workspace 1" }) { Workspace.order("id desc") } # try two filters + expect(result[:results].first[:id]).to eq(Workspace.where(title: "bob workspace 1").first.id.to_s) end it "converts boolean parameters from strings to booleans" do - WorkspacePresenter.filter(:owned_by_bob) { |scope, boolean| boolean ? scope.where(:user_id => bob.id) : scope.where(:user_id => jane.id) } - result = @presenter_collection.presenting("workspaces", :params => { :owned_by_bob => "false" }) { Workspace.where(nil) } + WorkspacePresenter.filter(:owned_by_bob) { |scope, boolean| boolean ? scope.where(user_id: bob.id) : scope.where(user_id: jane.id) } + result = @presenter_collection.presenting("workspaces", params: { owned_by_bob: "false" }) { Workspace.where(nil) } expect(result[:workspaces].values.find { |workspace| workspace[:title].include?("jane") }).to be expect(result[:workspaces].values.find { |workspace| workspace[:title].include?("bob") }).not_to be end @@ -376,7 +376,7 @@ class MyException < Exception; end expect(string).to be_a(String) scope end - @presenter_collection.presenting("workspaces", :params => { :owned_by_bob => { :wut => "is this?" } }) { Workspace.where(nil) } + @presenter_collection.presenting("workspaces", params: { owned_by_bob: { wut: "is this?" } }) { Workspace.where(nil) } expect(filter_was_run).to be_truthy end @@ -387,93 +387,93 @@ class MyException < Exception; end expect(array).to be_a(Array) scope end - @presenter_collection.presenting("workspaces", :params => { :owned_by_bob => [1, 2] }) { Workspace.where(nil) } + @presenter_collection.presenting("workspaces", params: { owned_by_bob: [1, 2] }) { Workspace.where(nil) } expect(filter_was_run).to be_truthy end it "allows filters to be called with false as an argument" do - WorkspacePresenter.filter(:nothing) { |scope, bool| bool ? scope.where(:id => nil) : scope } - result = @presenter_collection.presenting("workspaces", :params => { :nothing => "true" }) { Workspace.where(nil) } + WorkspacePresenter.filter(:nothing) { |scope, bool| bool ? scope.where(id: nil) : scope } + result = @presenter_collection.presenting("workspaces", params: { nothing: "true" }) { Workspace.where(nil) } expect(result[:workspaces].length).to eq(0) - result = @presenter_collection.presenting("workspaces", :params => { :nothing => "false" }) { Workspace.where(nil) } + result = @presenter_collection.presenting("workspaces", params: { nothing: "false" }) { Workspace.where(nil) } expect(result[:workspaces].length).not_to eq(0) end it "passes colon separated params through as a string" do - WorkspacePresenter.filter(:between) { |scope, a_and_b| - a, b = a_and_b.split(':') + WorkspacePresenter.filter(:between) do |scope, a_and_b| + a, b = a_and_b.split(":") expect(a).to eq("1") expect(b).to eq("10") scope - } + end - @presenter_collection.presenting("workspaces", :params => { :between => "1:10" }) { Workspace.where(nil) } + @presenter_collection.presenting("workspaces", params: { between: "1:10" }) { Workspace.where(nil) } end context "with defaults" do before do - WorkspacePresenter.filter(:owner, :default => bob.id) { |scope, id| scope.owned_by(id) } + WorkspacePresenter.filter(:owner, default: bob.id) { |scope, id| scope.owned_by(id) } end - let(:jane) { User.where(:username => "jane").first } + let(:jane) { User.where(username: "jane").first } it "applies the filter when it is not requested" do - result = @presenter_collection.presenting("workspaces") { Workspace.order('id desc') } + result = @presenter_collection.presenting("workspaces") { Workspace.order("id desc") } expect(result[:workspaces].keys).to match_array(bob.workspaces.map(&:id).map(&:to_s)) end it "allows falsy defaults" do - WorkspacePresenter.filter(:include_early_workspaces, :default => false) { |scope, bool| bool ? scope : scope.where("id > 3") } + WorkspacePresenter.filter(:include_early_workspaces, default: false) { |scope, bool| bool ? scope : scope.where("id > 3") } result = @presenter_collection.presenting("workspaces") { Workspace.unscoped } expect(result[:workspaces]["2"]).not_to be_present - result = @presenter_collection.presenting("workspaces", :params => { :include_early_workspaces => "true" }) { Workspace.unscoped } + result = @presenter_collection.presenting("workspaces", params: { include_early_workspaces: "true" }) { Workspace.unscoped } expect(result[:workspaces]["2"]).to be_present end it "allows defaults to be skipped if :apply_default_filters is false" do - WorkspacePresenter.filter(:include_early_workspaces, :default => false) { |scope, bool| bool ? scope : scope.where("id > 3") } - result = @presenter_collection.presenting("workspaces", :apply_default_filters => true) { Workspace.unscoped } + WorkspacePresenter.filter(:include_early_workspaces, default: false) { |scope, bool| bool ? scope : scope.where("id > 3") } + result = @presenter_collection.presenting("workspaces", apply_default_filters: true) { Workspace.unscoped } expect(result[:workspaces]["2"]).not_to be_present - result = @presenter_collection.presenting("workspaces", :apply_default_filters => false) { Workspace.unscoped } + result = @presenter_collection.presenting("workspaces", apply_default_filters: false) { Workspace.unscoped } expect(result[:workspaces]["2"]).to be_present end it "allows defaults set to false to be skipped if params contain :apply_default_filters with a false value" do - WorkspacePresenter.filter(:include_early_workspaces, :default => false) { |scope, bool| bool ? scope : scope.where("id > 3") } + WorkspacePresenter.filter(:include_early_workspaces, default: false) { |scope, bool| bool ? scope : scope.where("id > 3") } - result = @presenter_collection.presenting("workspaces", :params => { :apply_default_filters => "true" }) { Workspace.unscoped } + result = @presenter_collection.presenting("workspaces", params: { apply_default_filters: "true" }) { Workspace.unscoped } expect(result[:workspaces]["2"]).not_to be_present - result = @presenter_collection.presenting("workspaces", :params => { :apply_default_filters => true }) { Workspace.unscoped } + result = @presenter_collection.presenting("workspaces", params: { apply_default_filters: true }) { Workspace.unscoped } expect(result[:workspaces]["2"]).not_to be_present end it "allows defaults set to true to be skipped if params contain :apply_default_filters with a false value" do - WorkspacePresenter.filter(:include_early_workspaces, :default => true) { |scope, bool| bool ? scope : scope.where("id > 3") } + WorkspacePresenter.filter(:include_early_workspaces, default: true) { |scope, bool| bool ? scope : scope.where("id > 3") } - result = @presenter_collection.presenting("workspaces", :params => { :apply_default_filters => "false" }) { Workspace.unscoped } + result = @presenter_collection.presenting("workspaces", params: { apply_default_filters: "false" }) { Workspace.unscoped } expect(result[:workspaces]["2"]).to be_present - result = @presenter_collection.presenting("workspaces", :params => { :apply_default_filters => false }) { Workspace.unscoped } + result = @presenter_collection.presenting("workspaces", params: { apply_default_filters: false }) { Workspace.unscoped } expect(result[:workspaces]["2"]).to be_present end it "allows the default value to be overridden" do - result = @presenter_collection.presenting("workspaces", :params => { :owner => jane.id.to_s }) { Workspace.order('id desc') } + result = @presenter_collection.presenting("workspaces", params: { owner: jane.id.to_s }) { Workspace.order("id desc") } expect(result[:workspaces].keys).to match_array(jane.workspaces.map(&:id).map(&:to_s)) - WorkspacePresenter.filter(:include_early_workspaces, :default => true) { |scope, bool| bool ? scope : scope.where("id > 3") } - result = @presenter_collection.presenting("workspaces", :params => { :include_early_workspaces => "false" }) { Workspace.unscoped } + WorkspacePresenter.filter(:include_early_workspaces, default: true) { |scope, bool| bool ? scope : scope.where("id > 3") } + result = @presenter_collection.presenting("workspaces", params: { include_early_workspaces: "false" }) { Workspace.unscoped } expect(result[:workspaces]["2"]).not_to be_present end end context "without blocks" do - let(:bob) { User.where(:username => "bob").first } - let(:jane) { User.where(:username => "jane").first } + let(:bob) { User.where(username: "bob").first } + let(:jane) { User.where(username: "jane").first } before do - WorkspacePresenter.filter(:owned_by, :default => bob.id) + WorkspacePresenter.filter(:owned_by, default: bob.id) WorkspacePresenter.presents("Workspace") end @@ -483,7 +483,7 @@ class MyException < Exception; end end it "calls the named scope with given arguments" do - result = @presenter_collection.presenting("workspaces", :params => { :owned_by => jane.id.to_s }) { Workspace.where(nil) } + result = @presenter_collection.presenting("workspaces", params: { owned_by: jane.id.to_s }) { Workspace.where(nil) } expect(result[:workspaces].keys).to eq(jane.workspaces.pluck(:id).map(&:to_s)) end @@ -491,15 +491,15 @@ class MyException < Exception; end WorkspacePresenter.filter(:numeric_description) result = @presenter_collection.presenting("workspaces") { Workspace.where(nil) } - expect(result[:workspaces].keys).to eq(%w[1 2 3 4]) + expect(result[:workspaces].keys).to eq(%w(1 2 3 4)) - result = @presenter_collection.presenting("workspaces", :params => { :numeric_description => "true" }) { Workspace.where(nil) } - expect(result[:workspaces].keys).to eq(%w[2 4]) + result = @presenter_collection.presenting("workspaces", params: { numeric_description: "true" }) { Workspace.where(nil) } + expect(result[:workspaces].keys).to eq(%w(2 4)) # This is probably not the behavior that the developer or user intends. You should always use a one-argument lambda in your # model scope declaration! - result = @presenter_collection.presenting("workspaces", :params => { :numeric_description => "false" }) { Workspace.where(nil) } - expect(result[:workspaces].keys).to eq(%w[2 4]) + result = @presenter_collection.presenting("workspaces", params: { numeric_description: "false" }) { Workspace.where(nil) } + expect(result[:workspaces].keys).to eq(%w(2 4)) end end end @@ -508,51 +508,50 @@ class MyException < Exception; end context "with search method defined" do before do WorkspacePresenter.sort_order(:description, "workspaces.description") - WorkspacePresenter.search do |string| + WorkspacePresenter.search do |_string| [[5, 3], 2] end end context "and a search request is made" do it "calls the search method and maintains the resulting order" do - result = @presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.order("id asc") } - expect(result[:workspaces].keys).to eq(%w[5 3]) + result = @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.order("id asc") } + expect(result[:workspaces].keys).to eq(%w(5 3)) expect(result[:count]).to eq(2) end it "does not apply filters" do mock(@presenter_collection).run_filters(anything, anything).times(0) - result = @presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.order("id asc") } + result = @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.order("id asc") } end it "does not apply ordering" do mock(@presenter_collection).handle_ordering(anything, anything).times(0) - result = @presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.order("id asc") } + result = @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.order("id asc") } end it "does not try to handle only's" do mock(@presenter_collection).handle_only(anything, anything).times(0) - result = @presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.order("id asc") } + result = @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.order("id asc") } end it "does not apply pagination" do mock(@presenter_collection).paginate(anything, anything).times(0) - result = @presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.order("id asc") } + result = @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.order("id asc") } end it "keeps the records in the order returned by search" do - result = @presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.unscoped } - + result = @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.unscoped } end it "throws a SearchUnavailableError if the search block returns false" do - WorkspacePresenter.search do |string| + WorkspacePresenter.search do |_string| false end - expect { - @presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.unscoped } - }.to raise_error(Brainstem::SearchUnavailableError) + expect do + @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.unscoped } + end.to raise_error(Brainstem::SearchUnavailableError) end describe "passing options to the search block" do @@ -560,7 +559,7 @@ class MyException < Exception; end WorkspacePresenter.filter(:owned_by) { |scope| scope } WorkspacePresenter.search do |string, options| expect(string).to eq("blah") - expect(options[:include]).to eq(["tasks", "lead_user"]) + expect(options[:include]).to eq(%w(tasks lead_user)) expect(options[:owned_by]).to eq(false) expect(options[:order][:sort_order]).to eq("description") expect(options[:order][:direction]).to eq("desc") @@ -569,97 +568,97 @@ class MyException < Exception; end [[1], 1] # returned ids, count - not testing this in this set of specs end - @presenter_collection.presenting("workspaces", :params => { :search => "blah", :include => "tasks,lead_user", :owned_by => "false", :order => "description:desc", :page => 2, :per_page => 5 }) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah", include: "tasks,lead_user", owned_by: "false", order: "description:desc", page: 2, per_page: 5 }) { Workspace.order("id asc") } end describe "includes" do it "throws out requested inlcudes that the presenter does not have associations for" do - WorkspacePresenter.search do |string, options| + WorkspacePresenter.search do |_string, options| expect(options[:include]).to eq([]) [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah", :include => "users"}) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah", include: "users" }) { Workspace.order("id asc") } end end describe "filters" do it "passes through the default filters if no filter is requested" do - WorkspacePresenter.filter(:owned_by, :default => true) { |scope| scope } - WorkspacePresenter.search do |string, options| + WorkspacePresenter.filter(:owned_by, default: true) { |scope| scope } + WorkspacePresenter.search do |_string, options| expect(options[:owned_by]).to eq(true) [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.order("id asc") } end it "throws out requested filters that the presenter does not have" do - WorkspacePresenter.search do |string, options| + WorkspacePresenter.search do |_string, options| expect(options[:highest_rated]).to be_nil [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah", :highest_rated => true}) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah", highest_rated: true }) { Workspace.order("id asc") } end it "does not pass through existing non-default filters that are not requested" do WorkspacePresenter.filter(:owned_by) { |scope| scope } - WorkspacePresenter.search do |string, options| - expect(options.has_key?(:owned_by)).to eq(false) + WorkspacePresenter.search do |_string, options| + expect(options.key?(:owned_by)).to eq(false) [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah"}) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.order("id asc") } end end describe "orders" do it "passes through the default sort order if no order is requested" do WorkspacePresenter.default_sort_order("description:desc") - WorkspacePresenter.search do |string, options| + WorkspacePresenter.search do |_string, options| expect(options[:order][:sort_order]).to eq("description") expect(options[:order][:direction]).to eq("desc") [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah"}) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.order("id asc") } end it "makes the sort order 'updated_at:desc' if the requested order doesn't match an existing sort order and there is no default" do - WorkspacePresenter.search do |string, options| + WorkspacePresenter.search do |_string, options| expect(options[:order][:sort_order]).to eq("updated_at") expect(options[:order][:direction]).to eq("desc") [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah", :order => "created_at:asc"}) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah", order: "created_at:asc" }) { Workspace.order("id asc") } end it "sanitizes sort orders" do - WorkspacePresenter.search do |string, options| + WorkspacePresenter.search do |_string, options| expect(options[:order][:sort_order]).to eq("description") expect(options[:order][:direction]).to eq("asc") [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah", :order => "description:owned"}) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah", order: "description:owned" }) { Workspace.order("id asc") } end end describe "pagination" do it "passes through limit and offset if they are requested" do - WorkspacePresenter.search do |string, options| + WorkspacePresenter.search do |_string, options| expect(options[:limit]).to eq(1) expect(options[:offset]).to eq(2) [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah", :limit => 1, :offset => 2}) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah", limit: 1, offset: 2 }) { Workspace.order("id asc") } end it "passes through only limit and offset if all pagination options are requested" do - WorkspacePresenter.search do |string, options| + WorkspacePresenter.search do |_string, options| expect(options[:limit]).to eq(1) expect(options[:offset]).to eq(2) expect(options[:per_page]).to eq(nil) @@ -667,11 +666,11 @@ class MyException < Exception; end [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah", :limit => 1, :offset => 2, :per_page => 3, :page => 4}) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah", limit: 1, offset: 2, per_page: 3, page: 4 }) { Workspace.order("id asc") } end it "passes through page and per_page when limit not present" do - WorkspacePresenter.search do |string, options| + WorkspacePresenter.search do |_string, options| expect(options[:limit]).to eq(nil) expect(options[:offset]).to eq(nil) expect(options[:per_page]).to eq(3) @@ -679,11 +678,11 @@ class MyException < Exception; end [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah", :offset => 2, :per_page => 3, :page => 4}) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah", offset: 2, per_page: 3, page: 4 }) { Workspace.order("id asc") } end it "passes through page and per_page when offset not present" do - WorkspacePresenter.search do |string, options| + WorkspacePresenter.search do |_string, options| expect(options[:limit]).to eq(nil) expect(options[:offset]).to eq(nil) expect(options[:per_page]).to eq(3) @@ -691,11 +690,11 @@ class MyException < Exception; end [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah", :limit => 1, :per_page => 3, :page => 4}) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah", limit: 1, per_page: 3, page: 4 }) { Workspace.order("id asc") } end it "passes through page and per_page by default" do - WorkspacePresenter.search do |string, options| + WorkspacePresenter.search do |_string, options| expect(options[:limit]).to eq(nil) expect(options[:offset]).to eq(nil) expect(options[:per_page]).to eq(20) @@ -703,7 +702,7 @@ class MyException < Exception; end [[1], 1] end - @presenter_collection.presenting("workspaces", :params => { :search => "blah"}) { Workspace.order("id asc") } + @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.order("id asc") } end end end @@ -720,7 +719,7 @@ class MyException < Exception; end context "without search method defined" do context "and a search request is made" do it "returns as if there was no search" do - result = @presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.order("id asc") } + result = @presenter_collection.presenting("workspaces", params: { search: "blah" }) { Workspace.order("id asc") } expect(result[:workspaces].keys).to eq(Workspace.pluck(:id).map(&:to_s)) end end @@ -730,13 +729,13 @@ class MyException < Exception; end describe "sorting and ordering" do context "when there is no sort provided" do it "returns an empty array when there are no objects" do - result = @presenter_collection.presenting("workspaces") { Workspace.where(:id => nil) } - expect(result).to eq(:count => 0, :workspaces => {}, :results => []) + result = @presenter_collection.presenting("workspaces") { Workspace.where(id: nil) } + expect(result).to eq(count: 0, workspaces: {}, results: []) end it "falls back to the object's sort order when nothing is provided" do - result = @presenter_collection.presenting("workspaces") { Workspace.where(:id => [1, 3]) } - expect(result[:workspaces].keys).to eq(%w[1 3]) + result = @presenter_collection.presenting("workspaces") { Workspace.where(id: [1, 3]) } + expect(result[:workspaces].keys).to eq(%w(1 3)) end end @@ -744,28 +743,28 @@ class MyException < Exception; end WorkspacePresenter.sort_order(:description, "workspaces.description") WorkspacePresenter.default_sort_order("description:desc") result = @presenter_collection.presenting("workspaces") { Workspace.where("id is not null") } - expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(c b a 3 2 1)) + expect(result[:results].map { |i| result[:workspaces][i[:id]][:description] }).to eq(%w(c b a 3 2 1)) end it "allows default ordering ascending" do WorkspacePresenter.sort_order(:description, "workspaces.description") WorkspacePresenter.default_sort_order("description:asc") result = @presenter_collection.presenting("workspaces") { Workspace.where("id is not null") } - expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(1 2 3 a b c)) + expect(result[:results].map { |i| result[:workspaces][i[:id]][:description] }).to eq(%w(1 2 3 a b c)) end it "applies orders that match the default order" do WorkspacePresenter.sort_order(:description, "workspaces.description") WorkspacePresenter.default_sort_order("description:desc") - result = @presenter_collection.presenting("workspaces", :params => { :order => "description:desc"} ) { Workspace.where("id is not null") } - expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(c b a 3 2 1)) + result = @presenter_collection.presenting("workspaces", params: { order: "description:desc" }) { Workspace.where("id is not null") } + expect(result[:results].map { |i| result[:workspaces][i[:id]][:description] }).to eq(%w(c b a 3 2 1)) end it "applies orders that conflict with the default order" do WorkspacePresenter.sort_order(:description, "workspaces.description") WorkspacePresenter.default_sort_order("description:desc") - result = @presenter_collection.presenting("workspaces", :params => { :order => "description:asc"} ) { Workspace.where("id is not null") } - expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(1 2 3 a b c)) + result = @presenter_collection.presenting("workspaces", params: { order: "description:asc" }) { Workspace.where("id is not null") } + expect(result[:results].map { |i| result[:workspaces][i[:id]][:description] }).to eq(%w(1 2 3 a b c)) end it "cleans the params" do @@ -777,30 +776,30 @@ class MyException < Exception; end WorkspacePresenter.sort_order(:title, "workspaces.title") WorkspacePresenter.default_sort_order("description:desc") - result = @presenter_collection.presenting("workspaces", :params => { :order => "description:drop table" }) { Workspace.where("id is not null") } - expect(last_direction).to eq('asc') + result = @presenter_collection.presenting("workspaces", params: { order: "description:drop table" }) { Workspace.where("id is not null") } + expect(last_direction).to eq("asc") expect(result.keys).to match_array([:count, :workspaces, :results]) - result = @presenter_collection.presenting("workspaces", :params => { :order => "description:;;hacker;;" }) { Workspace.where("id is not null") } - expect(last_direction).to eq('asc') + result = @presenter_collection.presenting("workspaces", params: { order: "description:;;hacker;;" }) { Workspace.where("id is not null") } + expect(last_direction).to eq("asc") - result = @presenter_collection.presenting("workspaces", :params => { :order => "description:desc" }) { Workspace.where("id is not null") } - expect(last_direction).to eq('desc') + result = @presenter_collection.presenting("workspaces", params: { order: "description:desc" }) { Workspace.where("id is not null") } + expect(last_direction).to eq("desc") - result = @presenter_collection.presenting("workspaces", :params => { :order => "description:asc" }) { Workspace.where("id is not null") } - expect(last_direction).to eq('asc') + result = @presenter_collection.presenting("workspaces", params: { order: "description:asc" }) { Workspace.where("id is not null") } + expect(last_direction).to eq("asc") - result = @presenter_collection.presenting("workspaces", :params => { :order => "drop table:desc" }) { Workspace.where("id is not null") } - expect(last_direction).to eq('desc') + result = @presenter_collection.presenting("workspaces", params: { order: "drop table:desc" }) { Workspace.where("id is not null") } + expect(last_direction).to eq("desc") - result = @presenter_collection.presenting("workspaces", :params => { :order => "title:desc" }) { Workspace.where("id is not null") } - expect(result[:results].map {|i| result[:workspaces][i[:id]][:title] }).to eq(["jane workspace 2", "jane workspace 1", "bob workspace 4", "bob workspace 3", "bob workspace 2", "bob workspace 1"]) + result = @presenter_collection.presenting("workspaces", params: { order: "title:desc" }) { Workspace.where("id is not null") } + expect(result[:results].map { |i| result[:workspaces][i[:id]][:title] }).to eq(["jane workspace 2", "jane workspace 1", "bob workspace 4", "bob workspace 3", "bob workspace 2", "bob workspace 1"]) - result = @presenter_collection.presenting("workspaces", :params => { :order => "title:hacker" }) { Workspace.where("id is not null") } - expect(result[:results].map {|i| result[:workspaces][i[:id]][:title] }).to eq(["bob workspace 1", "bob workspace 2", "bob workspace 3", "bob workspace 4", "jane workspace 1", "jane workspace 2"]) + result = @presenter_collection.presenting("workspaces", params: { order: "title:hacker" }) { Workspace.where("id is not null") } + expect(result[:results].map { |i| result[:workspaces][i[:id]][:title] }).to eq(["bob workspace 1", "bob workspace 2", "bob workspace 3", "bob workspace 4", "jane workspace 1", "jane workspace 2"]) - result = @presenter_collection.presenting("workspaces", :params => { :order => "title:;;;drop table;;" }) { Workspace.where("id is not null") } - expect(result[:results].map {|i| result[:workspaces][i[:id]][:title] }).to eq(["bob workspace 1", "bob workspace 2", "bob workspace 3", "bob workspace 4", "jane workspace 1", "jane workspace 2"]) + result = @presenter_collection.presenting("workspaces", params: { order: "title:;;;drop table;;" }) { Workspace.where("id is not null") } + expect(result[:results].map { |i| result[:workspaces][i[:id]][:title] }).to eq(["bob workspace 1", "bob workspace 2", "bob workspace 3", "bob workspace 4", "jane workspace 1", "jane workspace 2"]) end it "can take a proc" do @@ -809,21 +808,21 @@ class MyException < Exception; end # Default result = @presenter_collection.presenting("workspaces") { Workspace.where("id is not null") } - expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(a 1 b 2 c 3)) + expect(result[:results].map { |i| result[:workspaces][i[:id]][:description] }).to eq(%w(a 1 b 2 c 3)) # Asc - result = @presenter_collection.presenting("workspaces", :params => { :order => "id:asc" }) { Workspace.where("id is not null") } - expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(a 1 b 2 c 3)) + result = @presenter_collection.presenting("workspaces", params: { order: "id:asc" }) { Workspace.where("id is not null") } + expect(result[:results].map { |i| result[:workspaces][i[:id]][:description] }).to eq(%w(a 1 b 2 c 3)) # Desc - result = @presenter_collection.presenting("workspaces", :params => { :order => "id:desc" }) { Workspace.where("id is not null") } - expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(3 c 2 b 1 a)) + result = @presenter_collection.presenting("workspaces", params: { order: "id:desc" }) { Workspace.where("id is not null") } + expect(result[:results].map { |i| result[:workspaces][i[:id]][:description] }).to eq(%w(3 c 2 b 1 a)) end end describe "the :as param" do it "determines the chosen top-level key name" do - result = @presenter_collection.presenting("workspaces", :as => :my_workspaces) { Workspace.where(:id => 1) } + result = @presenter_collection.presenting("workspaces", as: :my_workspaces) { Workspace.where(id: 1) } expect(result.keys).to eq([:count, :my_workspaces, :results]) end end @@ -832,16 +831,16 @@ class MyException < Exception; end it "should return the total number of matched records" do WorkspacePresenter.filter(:owned_by) { |scope, user_id| scope.owned_by(user_id.to_i) } - result = @presenter_collection.presenting("workspaces") { Workspace.where(:id => 1) } + result = @presenter_collection.presenting("workspaces") { Workspace.where(id: 1) } expect(result[:count]).to eq(1) result = @presenter_collection.presenting("workspaces") { Workspace.unscoped } expect(result[:count]).to eq(Workspace.count) - result = @presenter_collection.presenting("workspaces", :params => { :owned_by => bob.to_param }) { Workspace.unscoped } + result = @presenter_collection.presenting("workspaces", params: { owned_by: bob.to_param }) { Workspace.unscoped } expect(result[:count]).to eq(Workspace.owned_by(bob.to_param).count) - result = @presenter_collection.presenting("workspaces", :params => { :owned_by => bob.to_param }) { Workspace.group(:id) } + result = @presenter_collection.presenting("workspaces", params: { owned_by: bob.to_param }) { Workspace.group(:id) } expect(result[:count]).to eq(Workspace.owned_by(bob.to_param).count) end end @@ -877,7 +876,7 @@ class ArrayPresenter < Brainstem::Presenter describe "for! method" do it "raises if there is no presenter for the given class" do - expect{ Brainstem.presenter_collection("v1").for!(String) }.to raise_error(ArgumentError) + expect { Brainstem.presenter_collection("v1").for!(String) }.to raise_error(ArgumentError) end end end diff --git a/spec/brainstem/presenter_spec.rb b/spec/brainstem/presenter_spec.rb index d1ac96ee..299085a5 100644 --- a/spec/brainstem/presenter_spec.rb +++ b/spec/brainstem/presenter_spec.rb @@ -1,8 +1,7 @@ -require 'spec_helper' +require "spec_helper" describe Brainstem::Presenter do describe "class methods" do - describe "presents method" do before do @klass = Class.new(Brainstem::Presenter) @@ -70,8 +69,8 @@ def foo end it "creates an entry in the filters class ivar" do - @klass.filter(:foo, :default => true) { 1 } - expect(@klass.filters[:foo][0]).to eq({"default" => true}) + @klass.filter(:foo, default: true) { 1 } + expect(@klass.filters[:foo][0]).to eq("default" => true) expect(@klass.filters[:foo][1]).to be_a(Proc) end @@ -87,7 +86,7 @@ def foo end it "creates an entry in the search class ivar" do - @klass.search do end + @klass.search {} expect(@klass.search_block).to be_a(Proc) end end @@ -101,7 +100,7 @@ def foo def present(model) { - :body => model.body, + body: model.body } end end @@ -120,14 +119,14 @@ def present(model) describe "converting dates and times" do it "should convert all Time-and-date-like objects to iso8601" do class TimePresenter < Brainstem::Presenter - def present(model) + def present(_model) { - :time => Time.now, - :date => Date.new, - :recursion => { - :time => Time.now, - :something => [Time.now, :else], - :foo => :bar + time: Time.now, + date: Date.new, + recursion: { + time: Time.now, + something: [Time.now, :else], + foo: :bar } } end @@ -153,10 +152,10 @@ def present(model) def present(model) { - :body => model.body, - :subject => association(:subject), - :another_subject => association(:subject), - :something_else => association(:subject, :ignore_type => true) + body: model.body, + subject: association(:subject), + another_subject: association(:subject), + something_else: association(:subject, ignore_type: true) } end end @@ -169,15 +168,14 @@ def present(model) context "when polymorphic association exists" do let(:post) { Post.find(1) } - it "outputs the object as a hash with the id & class table name" do - expect(presented_data[:subject_ref]).to eq({ :id => post.subject.id.to_s, - :key => post.subject.class.table_name }) + expect(presented_data[:subject_ref]).to eq(id: post.subject.id.to_s, + key: post.subject.class.table_name) end it "outputs custom names for the object as a hash with the id & class table name" do - expect(presented_data[:another_subject_ref]).to eq({ :id => post.subject.id.to_s, - :key => post.subject.class.table_name }) + expect(presented_data[:another_subject_ref]).to eq(id: post.subject.id.to_s, + key: post.subject.class.table_name) end it "skips the polymorphic handling when ignore_type is true" do @@ -207,14 +205,14 @@ def present(model) def present(model) { - :updated_at => model.updated_at, - :tasks => association(:tasks), - :user => association(:user), - :something => association(:user), - :lead_user => association(:lead_user), - :lead_user_with_lambda => association(:json_name => "users") { |model| model.user }, - :tasks_with_lambda => association(:json_name => "tasks") { |model| Task.where(:workspace_id => model) }, - :synthetic => association(:synthetic) + updated_at: model.updated_at, + tasks: association(:tasks), + user: association(:user), + something: association(:user), + lead_user: association(:lead_user), + lead_user_with_lambda: association(json_name: "users") { |model| model.user }, + tasks_with_lambda: association(json_name: "tasks") { |model| Task.where(workspace_id: model) }, + synthetic: association(:synthetic) } end end @@ -267,7 +265,7 @@ def present(model) context "when the model has an _id method but no column" do it "does not include the _id field" do def @workspace.synthetic_id - raise "this explodes because it's not an association" + fail "this explodes because it's not an association" end expect(@presenter.present_and_post_process(@workspace, [])).not_to have_key(:synthetic_id) end diff --git a/spec/brainstem_spec.rb b/spec/brainstem_spec.rb index 3324aca1..4401e143 100644 --- a/spec/brainstem_spec.rb +++ b/spec/brainstem_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require "spec_helper" describe Brainstem do describe "default_namespace attribute" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b9e2a644..88b64743 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,12 +1,12 @@ -require 'active_record' -require 'logger' -require 'rr' -require 'rspec' -require 'sqlite3' +require "active_record" +require "logger" +require "rr" +require "rspec" +require "sqlite3" -require 'brainstem' -require_relative 'spec_helpers/db' -require_relative 'spec_helpers/cleanup' +require "brainstem" +require_relative "spec_helpers/db" +require_relative "spec_helpers/cleanup" RSpec.configure do |config| config.mock_with :rr diff --git a/spec/spec_helpers/cleanup.rb b/spec/spec_helpers/cleanup.rb index 1f2bf7fd..e4b26bf7 100644 --- a/spec/spec_helpers/cleanup.rb +++ b/spec/spec_helpers/cleanup.rb @@ -1,7 +1,6 @@ module Brainstem - def self.clear_collections! - presenter_collection.presenters.each do |klass, presenter| + presenter_collection.presenters.each do |_klass, presenter| presenter.clear_options! end @presenter_collection = {} @@ -19,5 +18,4 @@ def clear_options! self.class.clear_options! end end - -end \ No newline at end of file +end diff --git a/spec/spec_helpers/db.rb b/spec/spec_helpers/db.rb index 38d9b129..856e0f21 100644 --- a/spec/spec_helpers/db.rb +++ b/spec/spec_helpers/db.rb @@ -1,27 +1,27 @@ -ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:') +ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Schema.define do self.verbose = false - create_table :users, :force => true do |t| + create_table :users, force: true do |t| t.string :username t.timestamps null: true end - create_table :workspaces, :force => true do |t| + create_table :workspaces, force: true do |t| t.string :title t.string :description t.belongs_to :user t.timestamps null: true end - create_table :tasks, :force => true do |t| + create_table :tasks, force: true do |t| t.string :name t.integer :parent_id t.belongs_to :workspace t.timestamps null: true end - create_table :posts, :force => true do |t| + create_table :posts, force: true do |t| t.string :body t.integer :subject_id t.string :subject_type @@ -35,11 +35,11 @@ class User < ActiveRecord::Base class Task < ActiveRecord::Base belongs_to :workspace - has_many :sub_tasks, :foreign_key => :parent_id, :class_name => "Task" + has_many :sub_tasks, foreign_key: :parent_id, class_name: "Task" has_many :posts def tags - %w[some tags] + %w(some tags) end end @@ -48,8 +48,8 @@ class Workspace < ActiveRecord::Base has_many :tasks has_many :posts - scope :owned_by, -> id { where(:user_id => id) } - scope :numeric_description, -> description { where(:description => ["1", "2", "3"]) } + scope :owned_by, -> id { where(user_id: id) } + scope :numeric_description, -> _description { where(description: %w(1 2 3)) } def lead_user user @@ -57,24 +57,24 @@ def lead_user end class Post < ActiveRecord::Base - belongs_to :subject, :polymorphic => true + belongs_to :subject, polymorphic: true end -User.create!(:id => 1, :username => "bob") -User.create!(:id => 2, :username => "jane") +User.create!(id: 1, username: "bob") +User.create!(id: 2, username: "jane") -Workspace.create!(:id => 1, :user_id => 1, :title => "bob workspace 1", :description => "a") -Workspace.create!(:id => 2, :user_id => 1, :title => "bob workspace 2", :description => "1") -Workspace.create!(:id => 3, :user_id => 1, :title => "bob workspace 3", :description => "b") -Workspace.create!(:id => 4, :user_id => 1, :title => "bob workspace 4", :description => "2") -Workspace.create!(:id => 5, :user_id => 2, :title => "jane workspace 1", :description => "c") -Workspace.create!(:id => 6, :user_id => 2, :title => "jane workspace 2", :description => "3") +Workspace.create!(id: 1, user_id: 1, title: "bob workspace 1", description: "a") +Workspace.create!(id: 2, user_id: 1, title: "bob workspace 2", description: "1") +Workspace.create!(id: 3, user_id: 1, title: "bob workspace 3", description: "b") +Workspace.create!(id: 4, user_id: 1, title: "bob workspace 4", description: "2") +Workspace.create!(id: 5, user_id: 2, title: "jane workspace 1", description: "c") +Workspace.create!(id: 6, user_id: 2, title: "jane workspace 2", description: "3") -Task.create!(:id => 1, :workspace_id => 1, :name => "Buy milk") -Task.create!(:id => 2, :workspace_id => 1, :name => "Buy bananas") -Task.create!(:id => 3, :workspace_id => 1, :parent_id => 2, :name => "Green preferred") -Task.create!(:id => 4, :workspace_id => 1, :parent_id => 2, :name => "One bunch") +Task.create!(id: 1, workspace_id: 1, name: "Buy milk") +Task.create!(id: 2, workspace_id: 1, name: "Buy bananas") +Task.create!(id: 3, workspace_id: 1, parent_id: 2, name: "Green preferred") +Task.create!(id: 4, workspace_id: 1, parent_id: 2, name: "One bunch") -Post.create!(:id => 1, :subject => Workspace.first, :body => "first post!") -Post.create!(:id => 2, :subject => Task.first, :body => "this is important. get on it!") -Post.create!(:id => 3, :body => "Post without subject") \ No newline at end of file +Post.create!(id: 1, subject: Workspace.first, body: "first post!") +Post.create!(id: 2, subject: Task.first, body: "this is important. get on it!") +Post.create!(id: 3, body: "Post without subject") diff --git a/spec/spec_helpers/presenters.rb b/spec/spec_helpers/presenters.rb index 87f191ce..bd3c7055 100644 --- a/spec/spec_helpers/presenters.rb +++ b/spec/spec_helpers/presenters.rb @@ -1,11 +1,11 @@ class WorkspacePresenter < Brainstem::Presenter def present(model) { - :title => model.title, - :description => model.description, - :updated_at => model.updated_at, - :tasks => association(:tasks), - :lead_user => association(:lead_user, :json_name => "users") + title: model.title, + description: model.description, + updated_at: model.updated_at, + tasks: association(:tasks), + lead_user: association(:lead_user, json_name: "users") } end end @@ -13,11 +13,11 @@ def present(model) class TaskPresenter < Brainstem::Presenter def present(model) { - :name => model.name, - :sub_tasks => association(:sub_tasks), - :other_tasks => association(:sub_tasks, :json_name => "other_tasks"), - :workspace => association(:workspace), - :restricted => association(:json_name => "restricted_association", :restrict_to_only => true) { |model| model } + name: model.name, + sub_tasks: association(:sub_tasks), + other_tasks: association(:sub_tasks, json_name: "other_tasks"), + workspace: association(:workspace), + restricted: association(json_name: "restricted_association", restrict_to_only: true) { |model| model } } end end @@ -25,19 +25,19 @@ def present(model) class UserPresenter < Brainstem::Presenter def present(model) { - :username => model.username, - :odd_workspaces => association(:json_name => "odd_workspaces") { |user| + username: model.username, + odd_workspaces: association(json_name: "odd_workspaces") do |user| user.workspaces.select { |workspace| workspace.id % 2 == 1 } - } + end } end end - + class PostPresenter < Brainstem::Presenter def present(model) { - :body => model.body, - :subject => association(:subject) + body: model.body, + subject: association(:subject) } end end