basgren / railways

A plugin for RubyMine and IntelliJ IDEA Ultimate that adds a panel with routes of Ruby on Rails application.
MIT License
46 stars 18 forks source link

PSI and index do not match error in RM 2019.3 EAP #42

Closed thestelz closed 4 years ago

thestelz commented 4 years ago

I am now getting an error when first launching railways in RM. This doesn't seem to affect anything, but figured that I should put in an issue just in case it does cause a problem further down the road.

Not sure if this is an issue with RM itself or railways.

Thanks!

RubyMine 2019.3 EAP
Build #RM-193.4099.3, built on October 1, 2019
Runtime version: 11.0.4+10-b485.1 x86_64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
macOS 10.13
GC: G1 Young Generation, G1 Old Generation
Memory: 2000M
Cores: 4
Registry: debugger.watches.in.variables=false, ide.tooltip.initialDelay=702
Non-Bundled Plugins: Railways, com.intellij.plugins.webcomponents
com.intellij.psi.stubs.UpToDateStubIndexMismatch: PSI and index do not match.
Please report the problem to JetBrains with the files attached
INDEXED VERSION IS THE CURRENT ONE file=Ruby file, file.class=class org.jetbrains.plugins.ruby.ruby.lang.psi.impl.RFileImpl, file.lang=Language: ruby, modStamp=0
 tree consistent
 stub debugInfo=created in getStubTree(), with AST = false; with backReference
latestIndexedStub=StubTree{myDebugInfo='created from index; with backReference', myRoot=PsiFileStubImpl}2046590630
   same size=true
   debugInfo=created from index; with backReference
 viewProvider=com.intellij.psi.SingleRootFileViewProvider{myVirtualFile=file:///Users/user/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/aws-sdk-v1-1.67.0/lib/aws/record/model.rb, content=VirtualFileContent{size=13513}}
 viewProvider stamp: 0; file stamp: 0; file modCount: 1546881312000; file length: 13513
 doc saved: true; doc stamp: 0; doc size: 13513; committed: true
indexing info: indexed at 1546881312000 with document  byte size = 13513, char size = 13513
    at com.intellij.psi.stubs.StubTreeLoader.handleUpToDateMismatch(StubTreeLoader.java:134)
    at com.intellij.psi.stubs.StubTreeLoader.stubTreeAndIndexDoNotMatch(StubTreeLoader.java:127)
    at com.intellij.psi.stubs.StubProcessingHelperBase.inconsistencyDetected(StubProcessingHelperBase.java:147)
    at com.intellij.psi.stubs.StubProcessingHelperBase.checkType(StubProcessingHelperBase.java:89)
    at com.intellij.psi.stubs.StubProcessingHelperBase.processStubsInFile(StubProcessingHelperBase.java:68)
    at com.intellij.psi.stubs.StubIndexImpl.processElements(StubIndexImpl.java:393)
    at com.intellij.psi.stubs.StubIndex.getElements(StubIndex.java:107)
    at com.intellij.psi.stubs.StubIndex.getElements(StubIndex.java:95)
    at net.bitpot.railways.utils.RailwaysPsiUtils.findClassesAndModules(Unknown Source)
    at net.bitpot.railways.utils.RailwaysPsiUtils.findClassOrModule(Unknown Source)
    at net.bitpot.railways.utils.RailwaysPsiUtils.findControllerClass(Unknown Source)
    at net.bitpot.railways.utils.RailwaysPsiUtils.findControllerMethod(Unknown Source)
    at net.bitpot.railways.models.RailsActionInfo.update(Unknown Source)
    at net.bitpot.railways.models.routes.SimpleRoute.updateActionStatus(Unknown Source)
    at net.bitpot.railways.utils.RailwaysUtils.updateActionsStatus(Unknown Source)
    at net.bitpot.railways.routesView.RoutesManager.parseRakeRoutesOutput(Unknown Source)
    at net.bitpot.railways.routesView.RoutesManager.access$500(Unknown Source)
    at net.bitpot.railways.routesView.RoutesManager$UpdateRoutesTask.onSuccess(Unknown Source)
    at com.intellij.openapi.progress.impl.CoreProgressManager.finishTask(CoreProgressManager.java:501)
    at com.intellij.openapi.progress.impl.CoreProgressManager$4.lambda$run$1(CoreProgressManager.java:420)
    at com.intellij.openapi.application.TransactionGuardImpl$2.run(TransactionGuardImpl.java:309)
    at com.intellij.openapi.application.impl.LaterInvocator$FlushQueue.doRun(LaterInvocator.java:441)
    at com.intellij.openapi.application.impl.LaterInvocator$FlushQueue.runNextEvent(LaterInvocator.java:424)
    at com.intellij.openapi.application.impl.LaterInvocator$FlushQueue.run(LaterInvocator.java:407)
    at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:776)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:746)
    at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:906)
    at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:779)
    at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$8(IdeEventQueue.java:422)
    at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:698)
    at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:421)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

The following a files that were marked for "attachment" in the fatal error dialog in RM, so not sure if they are needed or not.

model.rb_file.txt

# Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
#     http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

# todo move these to included modules (like validations and naming)

module AWS
  module Record

    # An ActiveRecord-like interface built ontop of Amazon SimpleDB.
    #
    #     class Book < AWS::Record::Model
    #
    #       string_attr :title
    #       string_attr :author
    #       integer_attr :number_of_pages
    #
    #       timestamps # adds a :created_at and :updated_at pair of timestamps
    #
    #     end
    #
    #     b = Book.new(:title => 'My Book', :author => 'Me', :pages => 1)
    #     b.save
    #
    # # Attribute Macros
    #
    # When extending AWS::Record::Model you should first consider what
    # attributes your class should have.  Unlike ActiveRecord, AWS::Record
    # models are not backed by a database table/schema.  You must choose what
    # attributes (and what types) you need.
    #
    # * `string_attr`
    # * `boolean_attr`
    # * `integer_attr`
    # * `float_attr`
    # * `datetime_attr`
    # * `date_attr`
    #
    # ### Usage
    #
    # Normally you just call these methods inside your model class definition:
    #
    #     class Book < AWS::Record::Model
    #       string_attr :title
    #       boolean_attr :has_been_read
    #       integer_attr :number_of_pages
    #       float_attr :weight_in_pounds
    #       datetime_attr :published_at
    #     end
    #
    # For each attribute macro a pair of setter/getter methods are added #
    # to your class (and a few other useful methods).
    #
    #     b = Book.new
    #     b.title = "My Book"
    #     b.has_been_read = true
    #     b.number_of_pages = 1000
    #     b.weight_in_pounds = 1.1
    #     b.published_at = Time.now
    #     b.save
    #
    #     b.id #=> "0aa894ca-8223-4d34-831e-e5134b2bb71c"
    #     b.attributes
    #     #=> { 'title' => 'My Book', 'has_been_read' => true, ... }
    #
    # ### Default Values
    #
    # All attribute macros accept the `:default_value` option.  This sets
    # a value that is populated onto all new instnaces of the class.
    #
    #     class Book < AWS::Record::Model
    #       string_attr :author, :default_value => 'Me'
    #     end
    #
    #     Book.new.author #=> 'Me'
    #
    # ### Multi-Valued (Set) Attributes
    #
    # AWS::Record permits storing multiple values with a single attribute.
    #
    #     class Book < AWS::Record::Model
    #       string_attr :tags, :set => true
    #     end
    #
    #     b = Book.new
    #     b.tags #=> #<Set: {}>
    #
    #     b.tags = ['fiction', 'fantasy']
    #     b.tags #=> #<Set: {'fiction', 'fantasy'}>
    #
    # These multi-valued attributes are treated as sets, not arrays.  This
    # means:
    #
    # * values are unordered
    # * duplicate values are automatically omitted
    #
    # Please consider these limitations when you choose to use the `:set`
    # option with the attribute macros.
    #
    # # Validations
    #
    # It's important to validate models before there are persisted to keep
    # your data clean.  AWS::Record supports most of the ActiveRecord style
    # validators.
    #
    #     class Book < AWS::Record::Model
    #       string_attr :title
    #       validates_presence_of :title
    #     end
    #
    #     b = Book.new
    #     b.valid? #=> false
    #     b.errors.full_messages #=> ['Title may not be blank']
    #
    # Validations are checked before saving a record.  If any of the validators
    # adds an error, the the save will fail.
    #
    # For more information about the available validation methods see
    # {Validations}.
    #
    # # Finder Methods
    #
    # You can find records by their ID.  Each record gets a UUID when it
    # is saved for the first time.  You can use this ID to fetch the record
    # at a latter time:
    #
    #     b = Book["0aa894ca-8223-4d34-831e-e5134b2bb71c"]
    #
    #     b = Book.find("0aa894ca-8223-4d34-831e-e5134b2bb71c")
    #
    # If you try to find a record by ID that has no data an error will
    # be raised.
    #
    # ### All
    #
    # You can enumerate all of your records using `all`.
    #
    #     Book.all.each do |book|
    #       puts book.id
    #     end
    #
    #     Book.find(:all) do |book|
    #       puts book.id
    #     end
    #
    # Be careful when enumerating all.  Depending on the number of records
    # and number of attributes each record has, this can take a while,
    # causing quite a few requests.
    #
    # ### First
    #
    # If you only want a single record, you should use `first`.
    #
    #     b = Book.first
    #
    # ### Modifiers
    #
    # Frequently you do not want ALL records or the very first record.  You
    # can pass options to `find`, `all` and `first`.
    #
    #     my_books = Book.find(:all, :where => 'owner = "Me"')
    #
    #     book = Book.first(:where => { :has_been_read => false })
    #
    # You can pass as find options:
    #
    # * `:where` - Conditions that must be met to be returned
    # * `:order` - The order to sort matched records by
    # * `:limit` - The maximum number of records to return
    #
    # # Scopes
    #
    # More useful than writing query fragments all over the place is to
    # name your most common conditions for reuse.
    #
    #     class Book < AWS::Record::Model
    #
    #       scope :mine, where(:owner => 'Me')
    #
    #       scope :unread, where(:has_been_read => false)
    #
    #       scope :by_popularity, order(:score, :desc)
    #
    #       scope :top_10, by_popularity.limit(10)
    #
    #     end
    #
    #     # The following expression returns 10 books that belong
    #     # to me, that are unread sorted by popularity.
    #     next_good_reads = Book.mine.unread.top_10
    #
    # There are 3 standard scope methods:
    #
    # * `where`
    # * `order`
    # * `limit`
    #
    # ### Conditions (where)
    #
    # Where accepts aruments in a number of forms:
    #
    # 1. As an sql-like fragment. If you need to escape values this form is
    #    not suggested.
    #
    #        Book.where('title = "My Book"')
    #
    # 2. An sql-like fragment, with placeholders.  This escapes quoted
    #    arguments properly to avoid injection.
    #
    #        Book.where('title = ?', 'My Book')
    #
    # 3. A hash of key-value pairs. This is the simplest form, but also the
    #    least flexible.  You can not use this form if you need more complex
    #    expressions that use or.
    #
    #        Book.where(:title => 'My Book')
    #
    # ### Order
    #
    # This orders the records as returned by AWS.  Default ordering is ascending.
    # Pass the value :desc as a second argument to sort in reverse ordering.
    #
    #     Book.order(:title)        # alphabetical ordering
    #     Book.order(:title, :desc) # reverse alphabetical ordering
    #
    # You may only order by a single attribute. If you call order twice in the
    # chain, the last call gets presedence:
    #
    #     Book.order(:title).order(:price)
    #
    # In this example the books will be ordered by :price and the order(:title)
    # is lost.
    #
    # ### Limit
    #
    # Just call `limit` with an integer argument.  This sets the maximum
    # number of records to retrieve:
    #
    #     Book.limit(2)
    #
    # ### Delayed Execution
    #
    # It should be noted that all finds are lazy (except `first`).  This
    # means the value returned is not an array of records, rather a handle
    # to a {Scope} object that will return records when you enumerate over them.
    #
    # This allows you to build an expression without making unecessary requests.
    # In the following example no request is made until the call to
    # each_with_index.
    #
    #     all_books = Books.all
    #     ten_books = all_books.limit(10)
    #
    #     ten_books.each_with_index do |book,n|
    #       puts "#{n + 1} : #{book.title}"
    #     end
    #
    class Model

      require 'aws/record/model/attributes'
      require 'aws/record/model/finder_methods'
      require 'aws/record/model/scope'

      extend AbstractBase

      # The id for each record is auto-generated.  The default strategy
      # generates uuid strings.
      # @return [String] Returns the id string (uuid) for this record.  Retuns
      #   nil if this is a new record that has not been persisted yet.
      def id
        @_id
      end

      # @return [Hash] A hash with attribute names as hash keys (strings) and
      #   attribute values (of mixed types) as hash values.
      def attributes
        attributes = super
        attributes['id'] = id if persisted?
        attributes
      end

      class << self

        # Creates the SimpleDB domain that is configured for this class.
        #
        #     class Product < AWS::Record::Model
        #     end
        #
        #     Product.create_table #=> 'Product'
        #
        # If you share a single AWS account with multiple applications, you
        # can provide a domain prefix for your model classes.
        #
        #     AWS::Record.domain_prefix = 'myapp-'
        #
        #     Product.create_table #=> 'myapp-Product'
        #
        # If you have set a model shard name, this is used in place of the
        # class name.
        #
        #     AWS::Record.domain_prefix = 'prod-'
        #     class Product < AWS::Record::Model
        #       set_shard_name 'products'
        #     end
        #
        #     Product.create_table #=> 'prod-products'
        #
        # If you shard you data across multiple domains, you can specify the
        # shard name:
        #
        #     # create two domains, with the given names
        #     Product.create_domain 'products-1'
        #     Product.create_domain 'products-2'
        #
        # @param [optional,String] shard_name Defaults to the class name.
        #
        # @return [SimpleDB::Domain]
        #
        def create_domain shard_name = nil
          sdb.domains.create(sdb_domain_name(shard_name))
        end

        # @return [AWS::SimpleDB::Domain]
        # @api private
        def sdb_domain shard_name = nil
          sdb.domains[sdb_domain_name(shard_name)]
        end

        protected
        def sdb_domain_name shard_name = nil
          "#{AWS::Record.domain_prefix}#{self.shard_name(shard_name)}"
        end

        protected
        def sdb
          AWS::SimpleDB.new
        end

      end

      # @return [SimpleDB::Item] Returns a reference to the item as stored in
      #   simple db.
      # @api private
      private
      def sdb_item
        sdb_domain.items[id]
      end

      # @return [SimpleDB::Domain] Returns the domain this record is
      #   persisted to or will be persisted to.
      private
      def sdb_domain
        self.class.sdb_domain(shard)
      end

      # This function accepts a hash of item data (as returned from
      # AttributeCollection#to_h or ItemData#attributes) and returns only
      # the key/value pairs that are configured attribues for this class.
      # @api private
      private
      def deserialize_item_data item_data

        marked_for_deletion = item_data['_delete_'] || []

        data = {}
        item_data.each_pair do |attr_name,values|

          attribute = self.class.attributes[attr_name]

          next unless attribute
          next if marked_for_deletion.include?(attr_name)

          if attribute.set?
            data[attr_name] = values.map{|v| attribute.deserialize(v) }
          else
            data[attr_name] = attribute.deserialize(values.first)
          end

        end
        data
      end

      def hydrate(id, data)
        @_id = id
        super
      end

      # @api private
      def populate_id
        @_id = SecureRandom.uuid
      end

      # @api private
      protected
      def create_storage
        to_add = serialize_attributes
        sdb_item.attributes.add(to_add.merge(opt_lock_conditions))
      end

      # @api private
      private
      def update_storage

        to_update = {}
        to_delete = []

        # serialized_attributes will raise error if the entire record is blank
        attribute_values = serialize_attributes

        changed.each do |attr_name|
          if values = attribute_values[attr_name]
            to_update[attr_name] = values
          else
            to_delete << attr_name
          end
        end

        to_update.merge!(opt_lock_conditions)

        if to_delete.empty?
          sdb_item.attributes.replace(to_update)
        else
          sdb_item.attributes.replace(to_update.merge('_delete_' => to_delete))
          sdb_item.attributes.delete(to_delete + ['_delete_'])
        end

      end

      # @return [true]
      # @api private
      private
      def delete_storage
        sdb_item.delete(opt_lock_conditions)
        @_deleted = true
      end

    end

    # for backwards compatability with the old AWS::Record::Base
    Base = Model

  end
end

stubTree.txt

PsiFileStubImpl
  RModuleStub:AWS
    RModuleStub:Record
      RClassStub:Model
        RCallStubImpl
        RCallStubImpl
        RCallStubImpl
        RCallStubImpl
        RMethodStub:id
          RInstanceVariableStubImpl
        RMethodStub:attributes
        RObjectClassStubImpl
          RMethodStub:create_domain
          RMethodStub:sdb_domain
          RIdentifierStub
          RMethodStub:sdb_domain_name
          RIdentifierStub
          RMethodStub:sdb
        RIdentifierStub
        RMethodStub:sdb_item
        RIdentifierStub
        RMethodStub:sdb_domain
        RIdentifierStub
        RMethodStub:deserialize_item_data
        RMethodStub:hydrate
          RInstanceVariableStubImpl
        RMethodStub:populate_id
          RInstanceVariableStubImpl
        RIdentifierStub
        RMethodStub:create_storage
        RIdentifierStub
        RMethodStub:update_storage
        RIdentifierStub
        RMethodStub:delete_storage
          RInstanceVariableStubImpl
      RConstantStubImpl
thestelz commented 4 years ago

This doesn't just happen on first launch, but also when refreshing.

basgren commented 4 years ago

Thank you for such a detailed information! I've found the reason of this exception and already made a fix, but it needs to be tested. I will provide new build this weekend. Thanks again!

basgren commented 4 years ago

Hi. The issue is fixed in new 0.8.16 version. Please check and let me know if everything is ok.

thestelz commented 4 years ago

@basgren I have been using all day and haven't had any issues. Thanks for the fix!