require 'spec_helper' module RailsBestPractices module Prepares describe ModelPrepare do let(:runner) { Core::Runner.new(prepares: ModelPrepare.new) } context 'models' do it 'class_name with modules ::' do content =<<-EOF class Blog::Post < ActiveRecord::Base end EOF runner.prepare('app/models/admin/post.rb', content) models = Prepares.models expect(models.map(&:to_s)).to eq(['Blog::Post']) end it 'class_name with modules' do content =<<-EOF module Blog class Post < ActiveRecord::Base end end EOF runner.prepare('app/models/admin/post.rb', content) models = Prepares.models expect(models.map(&:to_s)).to eq(['Blog::Post']) end end context 'associations' do it 'should parse model associations' do content =<<-EOF class Project < ActiveRecord::Base belongs_to :portfolio has_one :project_manager has_many :milestones has_and_belongs_to_many :categories end EOF runner.prepare('app/models/project.rb', content) model_associations = Prepares.model_associations expect(model_associations.get_association('Project', 'portfolio')).to eq({'meta' => 'belongs_to', 'class_name' => 'Portfolio'}) expect(model_associations.get_association('Project', 'project_manager')).to eq({'meta' => 'has_one', 'class_name' => 'ProjectManager'}) expect(model_associations.get_association('Project', 'milestones')).to eq({'meta' => 'has_many', 'class_name' => 'Milestone'}) expect(model_associations.get_association('Project', 'categories')).to eq({'meta' => 'has_and_belongs_to_many', 'class_name' => 'Category'}) end context 'with class_name option' do it 'should parse belongs_to' do content =<<-EOF class Post < ActiveRecord::Base belongs_to :author, "class_name" => "Person" end EOF runner.prepare('app/models/post.rb', content) model_associations = Prepares.model_associations expect(model_associations.get_association('Post', 'author')).to eq({'meta' => 'belongs_to', 'class_name' => 'Person'}) end it 'should parse has_one' do content =<<-EOF class Project < ActiveRecord::Base has_one :project_manager, "class_name" => "Person" end EOF runner.prepare('app/models/post.rb', content) model_associations = Prepares.model_associations expect(model_associations.get_association('Project', 'project_manager')).to eq({'meta' => 'has_one', 'class_name' => 'Person'}) end it 'should parse has_many' do content =<<-EOF class Project < ActiveRecord::Base has_many :people, "class_name" => "Person" end EOF runner.prepare('app/models/project.rb', content) model_associations = Prepares.model_associations expect(model_associations.get_association('Project', 'people')).to eq({'meta' => 'has_many', 'class_name' => 'Person'}) end it 'should parse has_and_belongs_to_many' do content =<<-EOF class Citizen < ActiveRecord::Base has_and_belongs_to_many :nations, "class_name" => "Country" end EOF runner.prepare('app/models/citizen.rb', content) model_associations = Prepares.model_associations expect(model_associations.get_association('Citizen', 'nations')).to eq({'meta' => 'has_and_belongs_to_many', 'class_name' => 'Country'}) end context 'namespace' do it 'should parse with namespace' do content =<<-EOF class Community < ActiveRecord::Base has_many :members end EOF runner.prepare('app/models/community.rb', content) content =<<-EOF class Community::Member < ActiveRecord::Base belongs_to :community end EOF runner.prepare('app/models/community/member.rb', content) runner.after_prepare model_associations = Prepares.model_associations expect(model_associations.get_association('Community', 'members')).to eq({'meta' => 'has_many', 'class_name' => 'Community::Member'}) expect(model_associations.get_association('Community::Member', 'community')).to eq({'meta' => 'belongs_to', 'class_name' => 'Community'}) end it 'should parse without namespace' do content =<<-EOF class Community::Member::Rating < ActiveRecord::Base belongs_to :member end EOF runner.prepare('app/models/community/member/rating.rb', content) content =<<-EOF class Community::Member < ActiveRecord::Base has_many :ratings end EOF runner.prepare('app/models/community/member.rb', content) runner.after_prepare model_associations = Prepares.model_associations expect(model_associations.get_association('Community::Member::Rating', 'member')).to eq({'meta' => 'belongs_to', 'class_name' => 'Community::Member'}) expect(model_associations.get_association('Community::Member', 'ratings')).to eq({'meta' => 'has_many', 'class_name' => 'Community::Member::Rating'}) end end end context 'mongoid embeds' do it 'should parse embeds_many' do content =<<-EOF class Person include Mongoid::Document embeds_many :addresses end EOF runner.prepare('app/models/person.rb', content) model_associations = Prepares.model_associations expect(model_associations.get_association('Person', 'addresses')).to eq({'meta' => 'embeds_many', 'class_name' => 'Address'}) end it 'should parse embeds_one' do content =<<-EOF class Lush include Mongoid::Document embeds_one :whiskey, class_name: "Drink", inverse_of: :alcoholic end EOF runner.prepare('app/models/lush.rb', content) model_associations = Prepares.model_associations expect(model_associations.get_association('Lush', 'whiskey')).to eq({'meta' => 'embeds_one', 'class_name' => 'Drink'}) end it 'should parse embedded_in' do content =<<-EOF class Drink include Mongoid::Document embedded_in :alcoholic, class_name: "Lush", inverse_of: :whiskey end EOF runner.prepare('app/models/drink.rb', content) model_associations = Prepares.model_associations expect(model_associations.get_association('Drink', 'alcoholic')).to eq({'meta' => 'embedded_in', 'class_name' => 'Lush'}) end end context 'mongomapper many/one' do it 'should parse one' do content =<<-EOF class Employee include MongoMapper::Document one :desk end EOF runner.prepare('app/models/employee.rb', content) model_associations = Prepares.model_associations expect(model_associations.get_association('Employee', 'desk')).to eq({'meta' => 'one', 'class_name' => 'Desk'}) end it 'should parse many' do content =<<-EOF class Tree include MongoMapper::Document many :birds end EOF runner.prepare('app/models/tree.rb', content) model_associations = Prepares.model_associations expect(model_associations.get_association('Tree', 'birds')).to eq({'meta' => 'many', 'class_name' => 'Bird'}) end end end context 'methods' do it 'should parse model methods' do content =<<-EOF class Post < ActiveRecord::Base def save; end def find; end end EOF runner.prepare('app/models/post.rb', content) methods = Prepares.model_methods expect(methods.get_methods('Post').map(&:method_name)).to eq(['save', 'find']) end it 'should parse model methods with access control' do content =<<-EOF class Post < ActiveRecord::Base def save; end def find; end protected def create_or_update; end private def find_by_sql; end end EOF runner.prepare('app/models/post.rb', content) methods = Prepares.model_methods expect(methods.get_methods('Post').map(&:method_name)).to eq(['save', 'find', 'create_or_update', 'find_by_sql']) expect(methods.get_methods('Post', 'public').map(&:method_name)).to eq(['save', 'find']) expect(methods.get_methods('Post', 'protected').map(&:method_name)).to eq(['create_or_update']) expect(methods.get_methods('Post', 'private').map(&:method_name)).to eq(['find_by_sql']) end it 'should parse model methods with module ::' do content =<<-EOF class Admin::Blog::Post < ActiveRecord::Base def save; end def find; end end EOF runner.prepare('app/models/admin/blog/post.rb', content) methods = Prepares.model_methods expect(methods.get_methods('Admin::Blog::Post').map(&:method_name)).to eq(['save', 'find']) end it 'should parse model methods with module' do content =<<-EOF module Admin module Blog class Post < ActiveRecord::Base def save; end def find; end end end end EOF runner.prepare('app/models/admin/blog/post.rb', content) methods = Prepares.model_methods expect(methods.get_methods('Admin::Blog::Post').map(&:method_name)).to eq(['save', 'find']) end it 'should not add methods from module' do content =<<-EOF class Model < ActiveRecord::Base end EOF runner.prepare('app/models/model.rb', content) content =<<-EOF module Mixin def mixed_method end end EOF runner.prepare('app/models/mixins/mixin.rb', content) methods = Prepares.model_methods expect(methods.get_methods('Model')).to be_empty end end context 'scope' do it 'should treat named_scope as method' do content =<<-EOF class Post < ActiveRecord::Base named_scope :active, conditions: {active: true} end EOF runner.prepare('app/models/post.rb', content) methods = Prepares.model_methods expect(methods.get_methods('Post').map(&:method_name)).to eq(['active']) end it 'should treat scope as method' do content =<<-EOF class Post < ActiveRecord::Base scope :active, where(active: true) end EOF runner.prepare('app/models/post.rb', content) methods = Prepares.model_methods expect(methods.get_methods('Post').map(&:method_name)).to eq(['active']) end end context 'alias' do it 'should treat alias as method' do content =<<-EOF class Post < ActiveRecord::Base alias :new :old end EOF runner.prepare('app/models/post.rb', content) methods = Prepares.model_methods expect(methods.get_methods('Post').map(&:method_name)).to eq(['new']) end it 'should treat alias_method as method' do content =<<-EOF class Post < ActiveRecord::Base alias_method :new, :old end EOF runner.prepare('app/models/post.rb', content) methods = Prepares.model_methods expect(methods.get_methods('Post').map(&:method_name)).to eq(['new']) end it 'should treat alias_method_chain as method' do content =<<-EOF class Post < ActiveRecord::Base alias_method_chain :method, :feature end EOF runner.prepare('app/models/post.rb', content) methods = Prepares.model_methods expect(methods.get_methods('Post').map(&:method_name)).to eq(['method_with_feature', 'method']) end end context 'attributes' do it 'should parse mongoid field' do content =<<-EOF class Post include Mongoid::Document field :title field :tags, type: Array field :comments_count, type: Integer field :deleted_at, type: DateTime field :active, type: Boolean end EOF runner.prepare('app/models/post.rb', content) model_attributes = Prepares.model_attributes expect(model_attributes.get_attribute_type('Post', 'title')).to eq('String') expect(model_attributes.get_attribute_type('Post', 'tags')).to eq('Array') expect(model_attributes.get_attribute_type('Post', 'comments_count')).to eq('Integer') expect(model_attributes.get_attribute_type('Post', 'deleted_at')).to eq('DateTime') expect(model_attributes.get_attribute_type('Post', 'active')).to eq('Boolean') end it 'should parse mongomapper field' do content =<<-EOF class Post include MongoMapper::Document key :first_name, String key :last_name, String key :age, Integer key :born_at, Time key :active, Boolean key :fav_colors, Array end EOF runner.prepare('app/models/post.rb', content) model_attributes = Prepares.model_attributes expect(model_attributes.get_attribute_type('Post', 'first_name')).to eq('String') expect(model_attributes.get_attribute_type('Post', 'last_name')).to eq('String') expect(model_attributes.get_attribute_type('Post', 'age')).to eq('Integer') expect(model_attributes.get_attribute_type('Post', 'born_at')).to eq('Time') expect(model_attributes.get_attribute_type('Post', 'active')).to eq('Boolean') expect(model_attributes.get_attribute_type('Post', 'fav_colors')).to eq('Array') end end context 'no error' do it 'should raised for finder_sql option' do content =<<-EOF class EventSubscription < ActiveRecord::Base has_many :event_notification_template, finder_sql: ? end EOF content.sub!('?', '\'SELECT event_notification_templates.* from event_notification_templates where event_type_id=#{event_type_id} and delivery_method_id=#{delivery_method_id}\'') expect { runner.prepare('app/models/event_subscription.rb', content) }.not_to raise_error end end end end end