Commit 5f37a974 by cuong.tran

Merge branch 'release/v2.7.2'

parents 0b054562 edd17864
!/spec/integration/*/log/.gitkeep
!/spec/integration/*/tmp/.gitkeep
*.gem
.DS_Store .DS_Store
.bundle
.gems
.rbenv-version
.ruby-*
/.idea/
/.rbx
/.rvmrc
/.yardoc/* /.yardoc/*
/doc/* /Gemfile.lock
/coverage/* /coverage/*
/spec/debug.log
/pkg/*
/dist /dist
/Gemfile.lock /doc/*
*.gem /pkg/*
/.idea/ /spec/debug.log
/.rvmrc
.bundle
/.rbx
/spec/integration/*/bin/ /spec/integration/*/bin/
/spec/integration/*/db/test.*
/spec/integration/*/log/* /spec/integration/*/log/*
!/spec/integration/*/log/.gitkeep
/spec/integration/*/tmp/* /spec/integration/*/tmp/*
!/spec/integration/*/tmp/.gitkeep .byebug_history
/spec/integration/*/db/test.*
.rbenv-version
\ No newline at end of file
# Use this file to configure the Overcommit hooks you wish to use. This will
# extend the default configuration defined in:
# https://github.com/brigade/overcommit/blob/master/config/default.yml
#
# At the topmost level of this YAML file is a key representing type of hook
# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can
# customize each hook, such as whether to only run it on certain files (via
# `include`), whether to only display output if it fails (via `quiet`), etc.
#
# For a complete list of hooks, see:
# https://github.com/brigade/overcommit/tree/master/lib/overcommit/hook
#
# For a complete list of options that you can use to customize hooks, see:
# https://github.com/brigade/overcommit#configuration
#
# Uncomment the following lines to make the configuration take effect.
PreCommit:
RuboCop:
enabled: true
on_warn: fail # Treat all warnings as failures
PrePush:
RSpec:
enabled: true
on_warn: fail # Treat all warnings as failures
#
# TrailingWhitespace:
# enabled: true
# exclude:
# - '**/db/structure.sql' # Ignore trailing whitespace in generated files
#
#PostCheckout:
# ALL: # Special hook name that customizes all hooks of this type
# quiet: true # Change all post-checkout hooks to only display output on failure
#
# IndexTags:
# enabled: true # Generate a tags file with `ctags` each time HEAD changes
inherit_from: ./.rubocop_todo.yml
AllCops:
Exclude:
- 'vendor/**/*'
- 'spec/fixtures/**/*'
- 'tmp/**/*'
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2016-12-17 10:16:05 +0100 using RuboCop version 0.46.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: 9
# Configuration parameters: Include.
# Include: **/Gemfile, **/gems.rb
Bundler/DuplicatedGem:
Exclude:
- 'spec/integration/rails_2.3_with_bundler/Gemfile'
- 'spec/integration/rails_3.2.2/Gemfile'
- 'spec/integration/rails_3.2.8/Gemfile'
- 'spec/integration/rails_3.2_autoloading_factory_girl/Gemfile'
- 'spec/integration/rails_3.2_custom_inflections/Gemfile'
- 'spec/integration/rails_3.2_with_asset_pipeline/Gemfile'
- 'spec/integration/rails_4.1.1/Gemfile'
- 'spec/integration/rails_4.2.0/Gemfile'
- 'spec/integration/standalone/Gemfile'
# Offense count: 2
# Configuration parameters: Include.
# Include: **/Gemfile, **/gems.rb
Bundler/OrderedGems:
Exclude:
- 'spec/integration/rails_2.3_with_bundler/Gemfile'
- 'spec/integration/rails_3.2_with_asset_pipeline/Gemfile'
# Offense count: 3
# Configuration parameters: AllowSafeAssignment.
Lint/AssignmentInCondition:
Exclude:
- 'lib/annotate/annotate_models.rb'
- 'spec/integration/rails_2.3_with_bundler/config/boot.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: AlignWith, SupportedStyles.
# SupportedStyles: either, start_of_block, start_of_line
Lint/BlockAlignment:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 6
# Cop supports --auto-correct.
Lint/DeprecatedClassMethods:
Exclude:
- 'lib/annotate/annotate_routes.rb'
- 'spec/integration/rails_3.2.2/config/boot.rb'
- 'spec/integration/rails_3.2.8/config/boot.rb'
- 'spec/integration/rails_3.2_autoloading_factory_girl/config/boot.rb'
- 'spec/integration/rails_3.2_custom_inflections/config/boot.rb'
- 'spec/integration/rails_3.2_with_asset_pipeline/config/boot.rb'
# Offense count: 1
Lint/DuplicateMethods:
Exclude:
- 'spec/integration/rails_2.3_with_bundler/config/boot.rb'
# Offense count: 1
Lint/HandleExceptions:
Exclude:
- 'bin/annotate'
# Offense count: 8
Lint/IneffectiveAccessModifier:
Exclude:
- 'lib/annotate/annotate_routes.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: runtime_error, standard_error
Lint/InheritException:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 2
Lint/RescueException:
Exclude:
- 'Rakefile'
# Offense count: 1
Lint/ShadowingOuterLocalVariable:
Exclude:
- 'Rakefile'
# Offense count: 6
# Cop supports --auto-correct.
# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
Lint/UnusedBlockArgument:
Exclude:
- 'bin/annotate'
# Offense count: 1
# Configuration parameters: ContextCreatingMethods.
Lint/UselessAccessModifier:
Exclude:
- 'lib/annotate/annotate_routes.rb'
# Offense count: 17
Metrics/AbcSize:
Max: 159
# Offense count: 3
# Configuration parameters: CountComments.
Metrics/BlockLength:
Max: 134
# Offense count: 2
Metrics/BlockNesting:
Max: 4
# Offense count: 8
Metrics/CyclomaticComplexity:
Max: 42
# Offense count: 350
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 543
# Offense count: 24
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 79
# Offense count: 1
# Configuration parameters: CountComments.
Metrics/ModuleLength:
Max: 116
# Offense count: 7
Metrics/PerceivedComplexity:
Max: 48
# Offense count: 1
Style/AccessorMethodName:
Exclude:
- 'lib/annotate.rb'
# Offense count: 3
# Cop supports --auto-correct.
Style/AlignArray:
Exclude:
- 'spec/annotate/annotate_models_spec.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: percent_q, bare_percent
Style/BarePercentLiterals:
Exclude:
- 'spec/integration/rails_2.3_with_bundler/config/boot.rb'
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: braces, no_braces, context_dependent
Style/BracesAroundHashParameters:
Exclude:
- 'spec/integration/rails_4.1.1/lib/tasks/auto_annotate_models.rake'
- 'spec/integration/rails_4.2.0/lib/tasks/auto_annotate_models.rake'
- 'spec/integration/standalone/config/init.rb'
# Offense count: 1
Style/CaseEquality:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 14
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: nested, compact
Style/ClassAndModuleChildren:
Exclude:
- 'lib/annotate/active_record_patch.rb'
- 'spec/integration/rails_2.3_with_bundler/config/boot.rb'
- 'spec/integration/rails_2.3_with_bundler/test/test_helper.rb'
- 'spec/integration/rails_3.2.2/test/test_helper.rb'
- 'spec/integration/rails_3.2.8/test/test_helper.rb'
- 'spec/integration/rails_3.2_autoloading_factory_girl/test/test_helper.rb'
- 'spec/integration/rails_3.2_custom_inflections/test/test_helper.rb'
- 'spec/integration/rails_3.2_with_asset_pipeline/test/test_helper.rb'
- 'spec/integration/rails_4.1.1/app/models/sub1/sub2/sub3/event.rb'
- 'spec/integration/rails_4.1.1/app/models/sub1/user.rb'
- 'spec/integration/rails_4.1.1/test/test_helper.rb'
- 'spec/integration/rails_4.2.0/app/models/sub1/sub2/sub3/event.rb'
- 'spec/integration/rails_4.2.0/app/models/sub1/user.rb'
- 'spec/integration/rails_4.2.0/test/test_helper.rb'
# Offense count: 2
Style/ClassVars:
Exclude:
- 'lib/tasks/annotate_models_migrate.rake'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly.
# SupportedStyles: assign_to_condition, assign_inside_condition
Style/ConditionalAssignment:
Exclude:
- 'bin/annotate'
# Offense count: 7
Style/Documentation:
Exclude:
- 'spec/**/*'
- 'test/**/*'
- 'lib/annotate.rb'
- 'lib/annotate/active_record_patch.rb'
- 'lib/annotate/annotate_models.rb'
- 'lib/annotate/version.rb'
- 'lib/generators/annotate/install_generator.rb'
- 'lib/tasks/annotate_models_migrate.rake'
# Offense count: 2
# Cop supports --auto-correct.
Style/EmptyLines:
Exclude:
- 'spec/integration/rails_4.1.1/Gemfile'
- 'spec/integration/rails_4.2.0/Gemfile'
# Offense count: 1
# Cop supports --auto-correct.
Style/EmptyLinesAroundAccessModifier:
Exclude:
- 'spec/integration/rails_2.3_with_bundler/config/boot.rb'
# Offense count: 6
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: empty_lines, no_empty_lines
Style/EmptyLinesAroundBlockBody:
Exclude:
- 'spec/integration/rails_2.3_with_bundler/db/schema.rb'
- 'spec/integration/rails_4.1.1/db/schema.rb'
- 'spec/integration/rails_4.2.0/db/schema.rb'
# Offense count: 23
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
Style/ExtraSpacing:
Exclude:
- 'Guardfile'
- 'lib/annotate/annotate_models.rb'
- 'lib/tasks/annotate_routes.rake'
- 'spec/integration/rails_2.3_with_bundler/script/console'
- 'spec/integration/rails_3.2.2/script/rails'
- 'spec/integration/rails_3.2.8/script/rails'
- 'spec/integration/rails_3.2_autoloading_factory_girl/script/rails'
- 'spec/integration/rails_3.2_custom_inflections/script/rails'
- 'spec/integration/rails_3.2_with_asset_pipeline/script/rails'
- 'spec/integration/rails_4.1.1/Gemfile'
- 'spec/integration/rails_4.1.1/config.ru'
- 'spec/integration/rails_4.2.0/Gemfile'
- 'spec/integration/rails_4.2.0/config.ru'
# Offense count: 9
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: format, sprintf, percent
Style/FormatString:
Exclude:
- 'lib/annotate/annotate_models.rb'
- 'lib/annotate/annotate_routes.rb'
# Offense count: 181
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: when_needed, always
Style/FrozenStringLiteralComment:
Enabled: false
# Offense count: 7
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Exclude:
- 'lib/annotate/annotate_models.rb'
- 'lib/annotate/annotate_routes.rb'
- 'lib/tasks/annotate_models_migrate.rake'
- 'spec/integration/rails_2.3_with_bundler/config/boot.rb'
# Offense count: 57
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
Style/HashSyntax:
Enabled: false
# Offense count: 6
# Cop supports --auto-correct.
# Configuration parameters: SupportedStyles, IndentationWidth.
# SupportedStyles: special_inside_parentheses, consistent, align_braces
Style/IndentHash:
EnforcedStyle: consistent
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: normal, rails
Style/IndentationConsistency:
Exclude:
- 'spec/integration/rails_2.3_with_bundler/config/boot.rb'
# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: Width.
Style/IndentationWidth:
Exclude:
- 'spec/integration/rails_4.1.1/app/models/sub1/sub2/sub3/event.rb'
- 'spec/integration/rails_4.1.1/app/models/task.rb'
- 'spec/integration/rails_4.2.0/app/models/sub1/sub2/sub3/event.rb'
- 'spec/integration/rails_4.2.0/app/models/task.rb'
# Offense count: 1
Style/MethodMissing:
Exclude:
- 'lib/annotate/active_record_patch.rb'
# Offense count: 3
Style/MultilineBlockChain:
Exclude:
- 'Rakefile'
- 'lib/annotate/annotate_models.rb'
- 'spec/spec_helper.rb'
# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
# SupportedStyles: aligned, indented
Style/MultilineOperationIndentation:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 3
# Cop supports --auto-correct.
Style/MutableConstant:
Exclude:
- 'lib/annotate/annotate_routes.rb'
- 'spec/integration/rails_2.3_with_bundler/config/boot.rb'
- 'spec/integration/rails_2.3_with_bundler/config/environment.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/NestedParenthesizedCalls:
Exclude:
- 'bin/annotate'
# Offense count: 9
# Cop supports --auto-correct.
Style/NumericLiterals:
MinDigits: 15
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles.
# SupportedStyles: predicate, comparison
Style/NumericPredicate:
Exclude:
- 'spec/**/*'
- 'lib/annotate.rb'
- 'lib/annotate/annotate_models.rb'
# Offense count: 6
# Cop supports --auto-correct.
# Configuration parameters: PreferredDelimiters.
Style/PercentLiteralDelimiters:
Exclude:
- 'annotate.gemspec'
# Offense count: 1
# Cop supports --auto-correct.
Style/PerlBackrefs:
Exclude:
- 'spec/integration/rails_2.3_with_bundler/config/boot.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: compact, exploded
Style/RaiseArgs:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/RedundantBegin:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/RedundantParentheses:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: AllowMultipleReturnValues.
Style/RedundantReturn:
Exclude:
- 'lib/annotate/annotate_routes.rb'
# Offense count: 2
# Cop supports --auto-correct.
Style/RedundantSelf:
Exclude:
- 'lib/tasks/annotate_models_migrate.rake'
# Offense count: 12
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
# SupportedStyles: slashes, percent_r, mixed
Style/RegexpLiteral:
Exclude:
- 'Rakefile'
- 'lib/annotate/annotate_models.rb'
- 'lib/annotate/annotate_routes.rb'
# Offense count: 2
# Cop supports --auto-correct.
Style/RescueModifier:
Exclude:
- 'lib/annotate/annotate_models.rb'
- 'spec/integration/rails_2.3_with_bundler/config/boot.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: AllowAsExpressionSeparator.
Style/Semicolon:
Exclude:
- 'bin/annotate'
- 'spec/integration/rails_2.3_with_bundler/config/initializers/unified_initializer.rb'
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: space, no_space
Style/SpaceAroundEqualsInParameterDefault:
Exclude:
- 'lib/annotate/annotate_routes.rb'
- 'spec/integration/common_validation.rb'
# Offense count: 9
# Cop supports --auto-correct.
Style/SpaceAroundKeyword:
Exclude:
- 'spec/integration/rails_2.3_with_bundler/Gemfile'
- 'spec/integration/rails_3.2.2/Gemfile'
- 'spec/integration/rails_3.2.8/Gemfile'
- 'spec/integration/rails_3.2_autoloading_factory_girl/Gemfile'
- 'spec/integration/rails_3.2_custom_inflections/Gemfile'
- 'spec/integration/rails_3.2_with_asset_pipeline/Gemfile'
- 'spec/integration/rails_4.1.1/Gemfile'
- 'spec/integration/rails_4.2.0/Gemfile'
- 'spec/integration/standalone/Gemfile'
# Offense count: 6
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment.
Style/SpaceAroundOperators:
Exclude:
- 'lib/annotate/annotate_models.rb'
- 'lib/tasks/annotate_models.rake'
- 'lib/tasks/annotate_routes.rake'
# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: space, no_space
Style/SpaceBeforeBlockBraces:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/SpaceBeforeComment:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
# SupportedStyles: space, no_space
Style/SpaceInsideBlockBraces:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 2
# Cop supports --auto-correct.
Style/SpaceInsideBrackets:
Exclude:
- 'spec/integration/rails_2.3_with_bundler/config/environment.rb'
# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles.
# SupportedStyles: space, no_space, compact
Style/SpaceInsideHashLiteralBraces:
Exclude:
- 'lib/tasks/annotate_models.rake'
# Offense count: 4
# Cop supports --auto-correct.
Style/SpaceInsideParens:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: space, no_space
Style/SpaceInsideStringInterpolation:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 223
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline.
# SupportedStyles: single_quotes, double_quotes
Style/StringLiterals:
Enabled: false
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: single_quotes, double_quotes
Style/StringLiteralsInInterpolation:
Exclude:
- 'lib/annotate/annotate_models.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/SymbolLiteral:
Exclude:
- 'spec/annotate/annotate_models_spec.rb'
# Offense count: 6
# Cop supports --auto-correct.
Style/Tab:
Exclude:
- 'spec/integration/rails_4.1.1/app/models/sub1/sub2/sub3/event.rb'
- 'spec/integration/rails_4.1.1/app/models/task.rb'
- 'spec/integration/rails_4.2.0/app/models/sub1/sub2/sub3/event.rb'
- 'spec/integration/rails_4.2.0/app/models/task.rb'
# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: final_newline, final_blank_line
Style/TrailingBlankLines:
Exclude:
- 'spec/integration/rails_4.1.1/app/models/task_observer.rb'
- 'spec/integration/rails_4.1.1/config/initializers/cookies_serializer.rb'
- 'spec/integration/rails_4.2.0/app/models/task_observer.rb'
- 'spec/integration/rails_4.2.0/config/initializers/cookies_serializer.rb'
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
# SupportedStyles: comma, consistent_comma, no_comma
Style/TrailingCommaInLiteral:
Exclude:
- 'spec/annotate/annotate_models_spec.rb'
- 'spec/integration/rails_4.1.1/lib/tasks/auto_annotate_models.rake'
- 'spec/integration/rails_4.2.0/lib/tasks/auto_annotate_models.rake'
# Offense count: 2
# Cop supports --auto-correct.
Style/TrailingWhitespace:
Exclude:
- 'spec/annotate/annotate_routes_spec.rb'
- 'spec/integration/rails_2.3_with_bundler/db/schema.rb'
# Offense count: 2
# Cop supports --auto-correct.
Style/UnneededInterpolation:
Exclude:
- 'bin/annotate'
# Offense count: 8
# Cop supports --auto-correct.
Style/UnneededPercentQ:
Exclude:
- 'annotate.gemspec'
- 'spec/integration/rails_2.3_with_bundler/config/boot.rb'
...@@ -4,7 +4,16 @@ rvm: ...@@ -4,7 +4,16 @@ rvm:
- 1.9.3 - 1.9.3
- 2.0 - 2.0
- 2.1 - 2.1
- 2.2 - 2.2.5
- 2.3.0-preview1 - 2.3.0
- 2.4.0
- ruby-head
matrix:
allow_failures:
- rvm: ruby-head
- rvm: 1.9.3
before_install: before_install:
- rvm @global do gem install bundler - gem update --system
- gem update bundler
script:
- bundle exec rubocop && bundle exec rspec
...@@ -29,5 +29,6 @@ With help from: ...@@ -29,5 +29,6 @@ With help from:
- Paul Alexander - Paul Alexander
- Dmitry Lihachev - Dmitry Lihachev
- qichunren - qichunren
- Guillermo Guerrero - http://github.com/ryanfox1985
and many others that I may have forgotten to add. and many others that I may have forgotten to add.
== 2.7.2
See https://github.com/ctran/annotate_models/releases/tag/v2.7.2
== 2.7.1
See https://github.com/ctran/annotate_models/releases/tag/v2.7.1
== 2.7.0 == 2.7.0
See https://github.com/ctran/annotate_models/releases/tag/v2.7.0 See https://github.com/ctran/annotate_models/releases/tag/v2.7.0
......
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'rake', '>= 10.4.2', :require => false gem 'activerecord', '>= 4.2.5', require: false
gem 'activerecord', '>= 4.2.5', :require => false gem 'rake', require: false
group :development do group :development do
gem 'mg', :require => false
gem 'bump' gem 'bump'
platforms :mri do gem 'mg', require: false
gem 'yard', :require => false platforms :mri, :mingw do
gem 'yard', require: false
end end
end end
group :development, :test do group :development, :test do
gem 'rspec', :require => false gem 'byebug'
gem 'guard-rspec', :require => false gem 'guard-rspec', require: false
gem 'terminal-notifier-guard', :require => false gem 'rspec', require: false
platforms :mri do gem 'rubocop', '~> 0.46.0', require: false unless RUBY_VERSION =~ /^1.8/
gem 'pry', :require => false gem 'simplecov', require: false
gem 'pry-coolline', :require => false gem 'terminal-notifier-guard', require: false
gem 'codeclimate-test-reporter'
gem 'coveralls'
gem 'overcommit'
gem 'ruby_dep', '1.3.1'
platforms :mri, :mingw do
gem 'pry', require: false
gem 'pry-coolline', require: false
end end
end end
group :test do group :test do
gem 'wrong', :require => false gem 'files', require: false
gem 'files', :require => false gem 'wrong', require: false
end end
# Note: The cmd option is now required due to the increasing number of ways # Note: The cmd option is now required due to the increasing number of ways
# rspec may be run, below are examples of the most common uses. # rspec may be run, below are examples of the most common uses.
# * bundler: 'bundle exec rspec' # * bundler: 'bundle exec rspec'
...@@ -11,16 +9,16 @@ ...@@ -11,16 +9,16 @@
guard :rspec, cmd: 'bundle exec rspec' do guard :rspec, cmd: 'bundle exec rspec' do
watch(%r{^spec/.+_spec\.rb$}) watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" } watch('spec/spec_helper.rb') { 'spec' }
# Rails example # Rails example
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" } watch(%r{^spec/support/(.+)\.rb$}) { 'spec' }
watch('config/routes.rb') { "spec/routing" } watch('config/routes.rb') { 'spec/routing' }
watch('app/controllers/application_controller.rb') { "spec/controllers" } watch('app/controllers/application_controller.rb') { 'spec/controllers' }
watch('spec/rails_helper.rb') { "spec" } watch('spec/rails_helper.rb') { 'spec' }
# Capybara features specs # Capybara features specs
watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" } watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
...@@ -29,4 +27,3 @@ guard :rspec, cmd: 'bundle exec rspec' do ...@@ -29,4 +27,3 @@ guard :rspec, cmd: 'bundle exec rspec' do
watch(%r{^spec/acceptance/(.+)\.feature$}) watch(%r{^spec/acceptance/(.+)\.feature$})
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
end end
== Annotate (aka AnnotateModels) == Annotate (aka AnnotateModels)
{<img src="https://badge.fury.io/rb/annotate.svg" alt="Gem Version" />}[http://badge.fury.io/rb/annotate] {<img src="https://badge.fury.io/rb/annotate.svg" alt="Gem Version" />}[http://badge.fury.io/rb/annotate]
{<img src="https://travis-ci.org/ctran/annotate_models.png" />}[https://travis-ci.org/ctran/annotate_models] {<img src="https://img.shields.io/gem/dt/annotate.svg?style=flat" />}[https://rubygems.org/gems/annotate]
{<img src="https://travis-ci.org/ctran/annotate_models.svg?branch=develop" />}[https://travis-ci.org/ctran/annotate_models]
{<img src="https://coveralls.io/repos/ctran/annotate_models/badge.svg?branch=develop" />}[https://coveralls.io/r/ctran/annotate_models?branch=develop]
{<img src="https://codeclimate.com/github/ctran/annotate_models/badges/gpa.svg" />}[https://codeclimate.com/github/ctran/annotate_models]
{<img src="http://inch-ci.org/github/ctran/annotate_models.svg?branch=develop" alt="Inline docs" />}[http://inch-ci.org/github/ctran/annotate_models]
{<img src="https://gemnasium.com/ctran/annotate_models.png" />}[https://gemnasium.com/ctran/annotate_models] {<img src="https://gemnasium.com/ctran/annotate_models.png" />}[https://gemnasium.com/ctran/annotate_models]
Add a comment summarizing the current schema to the top or bottom of each of Add a comment summarizing the current schema to the top or bottom of each of
...@@ -44,7 +48,7 @@ It also annotates geometrical columns, geom type and srid, when using ...@@ -44,7 +48,7 @@ It also annotates geometrical columns, geom type and srid, when using
# path :geometry line_string, 4326 # path :geometry line_string, 4326
Also, if you pass the -r option, it'll annotate routes.rb with the output of Also, if you pass the -r option, it'll annotate routes.rb with the output of
+rake routes+. <code>rake routes</code>.
== Install == Install
...@@ -55,7 +59,7 @@ Into Gemfile from rubygems.org: ...@@ -55,7 +59,7 @@ Into Gemfile from rubygems.org:
Into Gemfile from Github: Into Gemfile from Github:
gem 'annotate', github: 'ctran/annotate_models' gem 'annotate', git: 'https://github.com/ctran/annotate_models.git'
Into environment gems from rubygems.org: Into environment gems from rubygems.org:
...@@ -63,7 +67,7 @@ Into environment gems from rubygems.org: ...@@ -63,7 +67,7 @@ Into environment gems from rubygems.org:
Into environment gems from Github checkout: Into environment gems from Github checkout:
git clone git://github.com/ctran/annotate_models.git annotate_models git clone https://github.com/ctran/annotate_models.git annotate_models
cd annotate_models cd annotate_models
rake build rake build
gem install pkg/annotate-*.gem gem install pkg/annotate-*.gem
...@@ -71,7 +75,7 @@ Into environment gems from Github checkout: ...@@ -71,7 +75,7 @@ Into environment gems from Github checkout:
== Usage == Usage
(If you used the Gemfile install, prefix the below commands with +bundle exec+.) (If you used the Gemfile install, prefix the below commands with <code>bundle exec</code>.)
=== Usage in Rails === Usage in Rails
...@@ -100,7 +104,7 @@ To remove routes.rb annotations: ...@@ -100,7 +104,7 @@ To remove routes.rb annotations:
annotate --routes --delete annotate --routes --delete
To automatically annotate every time you run +db:migrate+, either run +rails g annotate:install+ or add +Annotate.load_tasks+ to your `Rakefile`. See the [configuration in Rails](#configuration-in-rails) section for more info. To automatically annotate every time you run <code>db:migrate</code>, either run <code>rails g annotate:install</code> or add +Annotate.load_tasks+ to your `Rakefile`. See the {configuration in Rails}[link:README.rdoc#configuration-in-rails] section for more info.
=== Usage Outside of Rails === Usage Outside of Rails
...@@ -136,7 +140,7 @@ functionality: ...@@ -136,7 +140,7 @@ functionality:
rake remove_annotation # Remove schema information from model and fixture files rake remove_annotation # Remove schema information from model and fixture files
By default, once you've generated a configuration file, annotate will be By default, once you've generated a configuration file, annotate will be
executed whenever you run +rake db:migrate+ (but only in development mode). executed whenever you run <code>rake db:migrate</code> (but only in development mode).
If you want to disable this behavior permanently, edit the +.rake+ file and If you want to disable this behavior permanently, edit the +.rake+ file and
change: change:
...@@ -146,7 +150,7 @@ To: ...@@ -146,7 +150,7 @@ To:
'skip_on_db_migrate' => 'true', 'skip_on_db_migrate' => 'true',
If you want to run +rake db:migrate+ as a one-off without running annotate, If you want to run <code>rake db:migrate</code> as a one-off without running annotate,
you can do so with a simple environment variable, instead of editing the you can do so with a simple environment variable, instead of editing the
+.rake+ file: +.rake+ file:
...@@ -175,12 +179,13 @@ you can do so with a simple environment variable, instead of editing the ...@@ -175,12 +179,13 @@ you can do so with a simple environment variable, instead of editing the
--wo, --wrapper-open STR Annotation wrapper opening. --wo, --wrapper-open STR Annotation wrapper opening.
--wc, --wrapper-close STR Annotation wrapper closing --wc, --wrapper-close STR Annotation wrapper closing
-r, --routes Annotate routes.rb with the output of 'rake routes' -r, --routes Annotate routes.rb with the output of 'rake routes'
-aa, --active-admin Annotate all activeadmin models
-v, --version Show the current version of this gem -v, --version Show the current version of this gem
-m, --show-migration Include the migration version number in the annotation -m, --show-migration Include the migration version number in the annotation
-i, --show-indexes List the table's database indexes in the annotation -i, --show-indexes List the table's database indexes in the annotation
-k, --show-foreign-keys List the table's foreign key constraints in the annotation -k, --show-foreign-keys List the table's foreign key constraints in the annotation
-s, --simple-indexes Concat the column's related indexes in the annotation -s, --simple-indexes Concat the column's related indexes in the annotation
--model-dir dir Annotate model files stored in dir rather than app/models, separate multiple dirs with comas --model-dir dir Annotate model files stored in dir rather than app/models, separate multiple dirs with commas
--ignore-model-subdirects Ignore subdirectories of the models directory --ignore-model-subdirects Ignore subdirectories of the models directory
--sort Sort columns alphabetically, rather than in creation order --sort Sort columns alphabetically, rather than in creation order
-R, --require path Additional file to require before loading models, may be used multiple times -R, --require path Additional file to require before loading models, may be used multiple times
......
def exit_exception(e)
$stderr.puts e.message
exit e.status_code
end
# Note : this causes annoying psych warnings under Ruby 1.9.2-p180; to fix, upgrade to 1.9.3 # Note : this causes annoying psych warnings under Ruby 1.9.2-p180; to fix, upgrade to 1.9.3
begin begin
require 'bundler' require 'bundler'
Bundler.setup(:default, :development) Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e rescue Bundler::BundlerError => e
$stderr.puts e.message $stderr.puts 'Run `bundle install` to install missing gems'
$stderr.puts "Run `bundle install` to install missing gems" exit_exception(e)
exit e.status_code
end end
using_dsl = false using_dsl = false
...@@ -14,67 +18,65 @@ begin ...@@ -14,67 +18,65 @@ begin
using_dsl = true using_dsl = true
rescue Exception => e rescue Exception => e
# We might just be on an old version of Rake... # We might just be on an old version of Rake...
exit_exception(e)
end end
require 'rake' require 'rake'
if(using_dsl) include Rake::DSL if using_dsl
include Rake::DSL
end
require "./lib/annotate"
require "mg" require './lib/annotate'
require 'mg'
begin begin
MG.new("annotate.gemspec") MG.new('annotate.gemspec')
rescue Exception => e rescue Exception
STDERR.puts("WARNING: Couldn't read gemspec. As such, a number of tasks may be unavailable to you until you run 'rake gem:gemspec' to correct the issue.") STDERR.puts("WARNING: Couldn't read gemspec. As such, a number of tasks may be unavailable to you until you run 'rake gem:gemspec' to correct the issue.")
# Gemspec is probably in a broken state, so let's give ourselves a chance to # Gemspec is probably in a broken state, so let's give ourselves a chance to
# build a new one... # build a new one...
end end
DEVELOPMENT_GROUPS=[:development, :test] DEVELOPMENT_GROUPS = [:development, :test].freeze
RUNTIME_GROUPS=Bundler.definition.groups - DEVELOPMENT_GROUPS RUNTIME_GROUPS = Bundler.definition.groups - DEVELOPMENT_GROUPS
namespace :gem do namespace :gem do
task :gemspec do task :gemspec do
spec = Gem::Specification.new do |gem| spec = Gem::Specification.new do |gem|
# See http://docs.rubygems.org/read/chapter/20 # See http://docs.rubygems.org/read/chapter/20
# for more options. # for more options.
gem.version = Annotate.version gem.version = Annotate.version
gem.name = "annotate" gem.name = 'annotate'
gem.homepage = "http://github.com/ctran/annotate_models" gem.homepage = 'http://github.com/ctran/annotate_models'
gem.rubyforge_project = "annotate" gem.rubyforge_project = 'annotate'
gem.license = "Ruby" gem.license = 'Ruby'
gem.summary = %q{Annotates Rails Models, routes, fixtures, and others based on the database schema.} gem.summary = 'Annotates Rails Models, routes, fixtures, and others based on the database schema.'
gem.description = %q{Annotates Rails/ActiveRecord Models, routes, fixtures, and others based on the database schema.} gem.description = 'Annotates Rails/ActiveRecord Models, routes, fixtures, and others based on the database schema.'
gem.email = ["alex@stinky.com", "cuong@gmail.com", "x@nofxx.com", "turadg@aleahmad.net", "jon@cloudability.com"] gem.email = ['alex@stinky.com', 'cuong@gmail.com', 'x@nofxx.com', 'turadg@aleahmad.net', 'jon@cloudability.com']
gem.authors = ["Alex Chaffee", "Cuong Tran", "Marcos Piccinini", "Turadg Aleahmad", "Jon Frisby"] gem.authors = ['Alex Chaffee', 'Cuong Tran', 'Marcos Piccinini', 'Turadg Aleahmad', 'Jon Frisby']
gem.require_paths = ["lib"] gem.require_paths = ['lib']
# gem.rdoc_options = ["--charset=UTF-8"] # gem.rdoc_options = ["--charset=UTF-8"]
# gem.required_ruby_version = "> 1.9.2" # gem.required_ruby_version = "> 1.9.2"
Bundler.load.dependencies_for(*RUNTIME_GROUPS).each do |dep| Bundler.load.dependencies_for(*RUNTIME_GROUPS).each do |dep|
runtime_resolved = Bundler.definition.specs_for(RUNTIME_GROUPS).select { |spec| spec.name == dep.name }.first runtime_resolved = Bundler.definition.specs_for(RUNTIME_GROUPS).find { |spec| spec.name == dep.name }
if(!runtime_resolved.nil?) unless runtime_resolved.nil?
gem.add_dependency(dep.name, dep.requirement) gem.add_dependency(dep.name, dep.requirement)
end end
end end
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
gem.extra_rdoc_files = ['README.rdoc', 'CHANGELOG.rdoc', 'TODO.rdoc'] gem.extra_rdoc_files = ['README.rdoc', 'CHANGELOG.rdoc', 'TODO.rdoc']
gem.files = `git ls-files -- .`.split("\n").reject do |fn| gem.files = `git ls-files -- .`.split("\n").reject do |fn|
fn =~ /^Gemfile.*/ || fn =~ /^Gemfile.*/ ||
fn =~ /^Rakefile/ || fn =~ /^Rakefile/ ||
fn =~ /^\.rvmrc/ || fn =~ /^\.rvmrc/ ||
fn =~ /^\.gitignore/ || fn =~ /^\.gitignore/ ||
fn =~ /^\.rspec/ || fn =~ /^\.rspec/ ||
fn =~ /^\.document/ || fn =~ /^\.document/ ||
fn =~ /^\.yardopts/ || fn =~ /^\.yardopts/ ||
fn =~ /^pkg/ || fn =~ /^pkg/ ||
fn =~ /^spec/ || fn =~ /^spec/ ||
fn =~ /^doc/ || fn =~ /^doc/ ||
fn =~ /^vendor\/cache/ fn =~ /^vendor\/cache/
end.sort end.sort
end end
File.open("annotate.gemspec", "wb") do |fh| File.open('annotate.gemspec', 'wb') do |fh|
fh.write("# This file is auto-generated!\n") fh.write("# This file is auto-generated!\n")
fh.write("# DO NOT EDIT THIS FILE DIRECTLY!\n") fh.write("# DO NOT EDIT THIS FILE DIRECTLY!\n")
fh.write("# Instead, edit the Rakefile and run 'rake gems:gemspec'.") fh.write("# Instead, edit the Rakefile and run 'rake gems:gemspec'.")
...@@ -85,12 +87,12 @@ end ...@@ -85,12 +87,12 @@ end
namespace :jeweler do namespace :jeweler do
task :clobber do task :clobber do
FileUtils.rm_f("pkg") FileUtils.rm_f('pkg')
end end
end end
task :clobber => :'jeweler:clobber' task clobber: :'jeweler:clobber'
require "rspec/core/rake_task" # RSpec 2.0 require 'rspec/core/rake_task' # RSpec 2.0
RSpec::Core::RakeTask.new(:spec) do |t| RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = ['spec/*_spec.rb', 'spec/**/*_spec.rb'] t.pattern = ['spec/*_spec.rb', 'spec/**/*_spec.rb']
t.rspec_opts = ['--backtrace', '--format d'] t.rspec_opts = ['--backtrace', '--format d']
...@@ -105,78 +107,76 @@ end ...@@ -105,78 +107,76 @@ end
namespace :gemsets do namespace :gemsets do
desc "Completely empty any gemsets used by scenarios, so they'll be perfectly clean on the next run." desc "Completely empty any gemsets used by scenarios, so they'll be perfectly clean on the next run."
task :empty => [:integration_environment] do task empty: [:integration_environment] do
Annotate::Integration::SCENARIOS.each do |test_rig, base_dir, test_name| Annotate::Integration::SCENARIOS.each do |test_rig, _base_dir, _test_name|
Annotate::Integration.empty_gemset(test_rig) Annotate::Integration.empty_gemset(test_rig)
end end
end end
end end
task :clobber => :'gemsets:empty' task clobber: :'gemsets:empty'
namespace :integration do namespace :integration do
desc "Remove any cruft generated by manual debugging runs which is .gitignore'd." desc "Remove any cruft generated by manual debugging runs which is .gitignore'd."
task :clean => :integration_environment do task clean: :integration_environment do
Annotate::Integration.nuke_all_cruft Annotate::Integration.nuke_all_cruft
end end
desc "Reset any changed files, and remove any untracked files in spec/integration/*/, plus run integration:clean." desc 'Reset any changed files, and remove any untracked files in spec/integration/*/, plus run integration:clean.'
task :clobber => [:integration_environment, :'integration:clean'] do task clobber: [:integration_environment, :'integration:clean'] do
Annotate::Integration.reset_dirty_files Annotate::Integration.reset_dirty_files
Annotate::Integration.clear_untracked_files Annotate::Integration.clear_untracked_files
end end
task :symlink => [:integration_environment] do task symlink: [:integration_environment] do
require 'digest/md5' require 'digest/md5'
integration_dir = File.expand_path(File.join(File.dirname(__FILE__), 'spec', 'integration')) integration_dir = File.expand_path(File.join(File.dirname(__FILE__), 'spec', 'integration'))
fixture_dir = File.expand_path(File.join(File.dirname(__FILE__), 'spec', 'fixtures')) # fixture_dir = File.expand_path(File.join(File.dirname(__FILE__), 'spec', 'fixtures'))
target_dir = File.expand_path(ENV['TARGET']) if(ENV['TARGET']) target_dir = File.expand_path(ENV['TARGET']) if ENV['TARGET']
raise "Must specify TARGET=x, where x is an integration test scenario!" unless(target_dir && Dir.exist?(target_dir)) raise 'Must specify TARGET=x, where x is an integration test scenario!' unless target_dir && Dir.exist?(target_dir)
raise "TARGET directory must be within spec/integration/!" unless(target_dir.start_with?(integration_dir)) raise 'TARGET directory must be within spec/integration/!' unless target_dir.start_with?(integration_dir)
candidates = {} candidates = {}
FileList[ FileList[
"#{target_dir}/.rvmrc", "#{target_dir}/.rvmrc",
"#{target_dir}/**/*" "#{target_dir}/**/*"
].select { |fname| !(File.symlink?(fname) || File.directory?(fname)) }. ].select { |fname| !(File.symlink?(fname) || File.directory?(fname)) }
map { |fname| fname.sub(integration_dir, '') }. .map { |fname| fname.sub(integration_dir, '') }
reject do |fname| .reject do |fname|
fname =~ /\/\.gitkeep$/ || fname =~ /\/\.gitkeep$/ ||
fname =~ /\/app\/models\// || fname =~ /\/app\/models\// ||
fname =~ /\/routes\.rb$/ || fname =~ /\/routes\.rb$/ ||
fname =~ /\/fixtures\// || fname =~ /\/fixtures\// ||
fname =~ /\/factories\// || fname =~ /\/factories\// ||
fname =~ /\.sqlite3$/ || fname =~ /\.sqlite3$/ ||
(fname =~ /\/test\// && fname !~ /_helper\.rb$/) || (fname =~ /\/test\// && fname !~ /_helper\.rb$/) ||
(fname =~ /\/spec\// && fname !~ /_helper\.rb$/) (fname =~ /\/spec\// && fname !~ /_helper\.rb$/)
end. end
map { |fname| "#{integration_dir}#{fname}"}. .map { |fname| "#{integration_dir}#{fname}" }
each do |fname| .each do |fname|
digest = Digest::MD5.hexdigest(File.read(fname)) digest = Digest::MD5.hexdigest(File.read(fname))
candidates[digest] ||= [] candidates[digest] ||= []
candidates[digest] << fname candidates[digest] << fname
end end
fixtures = {} fixtures = {}
FileList["spec/fixtures/**/*"].each do |fname| FileList['spec/fixtures/**/*'].each do |fname|
fixtures[Digest::MD5.hexdigest(File.read(fname))] = File.expand_path(fname) fixtures[Digest::MD5.hexdigest(File.read(fname))] = File.expand_path(fname)
end end
candidates.keys.each do |digest| candidates.keys.each do |digest|
if(fixtures.has_key?(digest)) next unless fixtures.key?(digest)
candidates[digest].each do |fname| candidates[digest].each do |fname|
# Double-check contents in case of hash collision... # Double-check contents in case of hash collision...
if(FileUtils.identical?(fname, fixtures[digest])) next unless FileUtils.identical?(fname, fixtures[digest])
destination_dir = Pathname.new(File.dirname(fname)) destination_dir = Pathname.new(File.dirname(fname))
relative_target = Pathname.new(fixtures[digest]).relative_path_from(destination_dir) relative_target = Pathname.new(fixtures[digest]).relative_path_from(destination_dir)
Dir.chdir(destination_dir) do Dir.chdir(destination_dir) do
sh("ln", "-sfn", relative_target.to_s, File.basename(fname)) sh('ln', '-sfn', relative_target.to_s, File.basename(fname))
end
end
end end
end end
end end
end end
end end
task :clobber => :'integration:clobber' task clobber: :'integration:clobber'
require 'yard' require 'yard'
YARD::Rake::YardocTask.new do |t| YARD::Rake::YardocTask.new do |t|
...@@ -186,19 +186,19 @@ end ...@@ -186,19 +186,19 @@ end
namespace :yard do namespace :yard do
task :clobber do task :clobber do
FileUtils.rm_f(".yardoc") FileUtils.rm_f('.yardoc')
FileUtils.rm_f("doc") FileUtils.rm_f('doc')
end end
end end
task :clobber => :'yard:clobber' task clobber: :'yard:clobber'
namespace :rubinius do namespace :rubinius do
task :clobber do task :clobber do
FileList["**/*.rbc"].each { |fname| FileUtils.rm_f(fname) } FileList['**/*.rbc'].each { |fname| FileUtils.rm_f(fname) }
FileList[".rbx/**/*"].each { |fname| FileUtils.rm_f(fname) } FileList['.rbx/**/*'].each { |fname| FileUtils.rm_f(fname) }
end end
end end
task :clobber => :'rubinius:clobber' task clobber: :'rubinius:clobber'
# want other tests/tasks run by default? Add them to the list # want other tests/tasks run by default? Add them to the list
task :default => [:spec] task default: [:spec]
...@@ -4,36 +4,45 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) ...@@ -4,36 +4,45 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'annotate/version' require 'annotate/version'
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = "annotate" s.name = 'annotate'
s.version = Annotate.version s.version = Annotate.version
s.required_ruby_version = '>= 1.9.3' s.required_ruby_version = '>= 1.9.3'
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
s.authors = ["Alex Chaffee", "Cuong Tran", "Marcos Piccinini", "Turadg Aleahmad", "Jon Frisby"] s.authors = ['Alex Chaffee', 'Cuong Tran', 'Marcos Piccinini', 'Turadg Aleahmad', 'Jon Frisby']
s.description = "Annotates Rails/ActiveRecord Models, routes, fixtures, and others based on the database schema." s.description = 'Annotates Rails/ActiveRecord Models, routes, fixtures, and others based on the database schema.'
s.email = ["alex@stinky.com", "cuong.tran@gmail.com", "x@nofxx.com", "turadg@aleahmad.net", "jon@cloudability.com"] s.email = ['alex@stinky.com', 'cuong.tran@gmail.com', 'x@nofxx.com', 'turadg@aleahmad.net', 'jon@cloudability.com']
s.executables = ["annotate"] s.executables = ['annotate']
s.extra_rdoc_files = ["README.rdoc", "CHANGELOG.rdoc", "TODO.rdoc"] s.extra_rdoc_files = ['README.rdoc', 'CHANGELOG.rdoc', 'TODO.rdoc']
s.files = ["AUTHORS.rdoc", "CHANGELOG.rdoc", "LICENSE.txt", "README.rdoc", "TODO.rdoc", "annotate.gemspec", "bin/annotate", "lib/annotate.rb", "lib/annotate/active_record_patch.rb", "lib/annotate/annotate_models.rb", "lib/annotate/annotate_routes.rb", "lib/annotate/tasks.rb", "lib/annotate/version.rb", "lib/generators/annotate/USAGE", "lib/generators/annotate/install_generator.rb", "lib/generators/annotate/templates/auto_annotate_models.rake", "lib/tasks/annotate_models.rake", "lib/tasks/annotate_routes.rake", "lib/tasks/migrate.rake"] s.files = [
s.homepage = "http://github.com/ctran/annotate_models" 'AUTHORS.rdoc',
s.licenses = ["Ruby"] 'CHANGELOG.rdoc',
s.require_paths = ["lib"] 'LICENSE.txt',
s.rubyforge_project = "annotate" 'README.rdoc',
s.rubygems_version = "2.1.11" 'TODO.rdoc',
s.summary = "Annotates Rails Models, routes, fixtures, and others based on the database schema." 'annotate.gemspec',
'bin/annotate',
'lib/annotate.rb',
'lib/annotate/active_record_patch.rb',
'lib/annotate/annotate_models.rb',
'lib/annotate/annotate_routes.rb',
'lib/annotate/tasks.rb',
'lib/annotate/version.rb',
'lib/generators/annotate/USAGE',
'lib/generators/annotate/install_generator.rb',
'lib/generators/annotate/templates/auto_annotate_models.rake',
'lib/tasks/annotate_models.rake',
'lib/tasks/annotate_routes.rake',
'lib/tasks/annotate_models_migrate.rake'
]
s.homepage = 'http://github.com/ctran/annotate_models'
s.licenses = ['Ruby']
s.require_paths = ['lib']
s.rubyforge_project = 'annotate'
s.rubygems_version = '2.1.11'
s.summary = 'Annotates Rails Models, routes, fixtures, and others based on the database schema.'
if s.respond_to? :specification_version then s.specification_version = 4 if s.respond_to? :specification_version
s.specification_version = 4 s.add_runtime_dependency(%q<rake>, ['>= 10.4', '< 13.0'])
s.add_runtime_dependency(%q<activerecord>, ['>= 3.2', '< 6.0'])
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<rake>, ["~> 10.4"])
s.add_runtime_dependency(%q<activerecord>, [">= 3.2", "< 6.0"])
else
s.add_dependency(%q<rake>, ["~> 10.4"])
s.add_dependency(%q<activerecord>, [">= 3.2", "< 6.0"])
end
else
s.add_dependency(%q<rake>, [">= 0.8.7"])
s.add_dependency(%q<activerecord>, [">= 3.2", "< 6.0"])
end
end end
#!/usr/bin/env ruby #!/usr/bin/env ruby
unless File.exists?('./Rakefile') || File.exists?('./Gemfile') unless File.exist?('./Rakefile') || File.exist?('./Gemfile')
abort "Please run annotate from the root of the project." abort 'Please run annotate from the root of the project.'
end end
require 'rubygems' require 'rubygems'
begin begin
require 'bundler' require 'bundler'
Bundler.setup Bundler.setup
rescue Exception => e rescue StandardError
end end
here = File.expand_path(File.dirname __FILE__) here = File.expand_path(File.dirname __FILE__)
$:<< "#{here}/../lib" $LOAD_PATH << "#{here}/../lib"
require 'optparse' require 'optparse'
require 'annotate' require 'annotate'
...@@ -20,58 +20,55 @@ Annotate.bootstrap_rake ...@@ -20,58 +20,55 @@ Annotate.bootstrap_rake
has_set_position = {} has_set_position = {}
target_action = :do_annotations target_action = :do_annotations
positions = %w(before top after bottom)
OptionParser.new do |opts| OptionParser.new do |opts|
opts.banner = "Usage: annotate [options] [model_file]*" opts.banner = 'Usage: annotate [options] [model_file]*'
opts.on('-d', '--delete',
"Remove annotations from all model files or the routes.rb file") do
opts.on('-d', '--delete', 'Remove annotations from all model files or the routes.rb file') do
target_action = :remove_annotations target_action = :remove_annotations
end end
opts.on('-p', '--position [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'], opts.on('-p', '--position [before|top|after|bottom]', positions,
"Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory/route/serializer file(s)") do |p| 'Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory/route/serializer file(s)') do |p|
ENV['position'] = p ENV['position'] = p
[ %w(position_in_class position_in_factory position_in_fixture position_in_test position_in_routes position_in_serializer).each do |key|
'position_in_class','position_in_factory','position_in_fixture','position_in_test', 'position_in_routes', 'position_in_serializer' ENV[key] = p unless has_set_position[key]
].each do |key|
ENV[key] = p unless(has_set_position[key])
end end
end end
opts.on('--pc', '--position-in-class [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'], opts.on('--pc', '--position-in-class [before|top|after|bottom]', positions,
"Place the annotations at the top (before) or the bottom (after) of the model file") do |p| 'Place the annotations at the top (before) or the bottom (after) of the model file') do |p|
ENV['position_in_class'] = p ENV['position_in_class'] = p
has_set_position['position_in_class'] = true has_set_position['position_in_class'] = true
end end
opts.on('--pf', '--position-in-factory [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'], opts.on('--pf', '--position-in-factory [before|top|after|bottom]', positions,
"Place the annotations at the top (before) or the bottom (after) of any factory files") do |p| 'Place the annotations at the top (before) or the bottom (after) of any factory files') do |p|
ENV['position_in_factory'] = p ENV['position_in_factory'] = p
has_set_position['position_in_factory'] = true has_set_position['position_in_factory'] = true
end end
opts.on('--px', '--position-in-fixture [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'], opts.on('--px', '--position-in-fixture [before|top|after|bottom]', positions,
"Place the annotations at the top (before) or the bottom (after) of any fixture files") do |p| 'Place the annotations at the top (before) or the bottom (after) of any fixture files') do |p|
ENV['position_in_fixture'] = p ENV['position_in_fixture'] = p
has_set_position['position_in_fixture'] = true has_set_position['position_in_fixture'] = true
end end
opts.on('--pt', '--position-in-test [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'], opts.on('--pt', '--position-in-test [before|top|after|bottom]', positions,
"Place the annotations at the top (before) or the bottom (after) of any test files") do |p| 'Place the annotations at the top (before) or the bottom (after) of any test files') do |p|
ENV['position_in_test'] = p ENV['position_in_test'] = p
has_set_position['position_in_test'] = true has_set_position['position_in_test'] = true
end end
opts.on('--pr', '--position-in-routes [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'], opts.on('--pr', '--position-in-routes [before|top|after|bottom]', positions,
"Place the annotations at the top (before) or the bottom (after) of the routes.rb file") do |p| 'Place the annotations at the top (before) or the bottom (after) of the routes.rb file') do |p|
ENV['position_in_routes'] = p ENV['position_in_routes'] = p
has_set_position['position_in_routes'] = true has_set_position['position_in_routes'] = true
end end
opts.on('--ps', '--position-in-serializer [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'], opts.on('--ps', '--position-in-serializer [before|top|after|bottom]', positions,
"Place the annotations at the top (before) or the bottom (after) of the serializer files") do |p| 'Place the annotations at the top (before) or the bottom (after) of the serializer files') do |p|
ENV['position_in_serializer'] = p ENV['position_in_serializer'] = p
has_set_position['position_in_serializer'] = true has_set_position['position_in_serializer'] = true
end end
...@@ -89,59 +86,60 @@ OptionParser.new do |opts| ...@@ -89,59 +86,60 @@ OptionParser.new do |opts|
ENV['wrapper_close'] = p ENV['wrapper_close'] = p
end end
opts.on('-r', '--routes', opts.on('-r', '--routes', "Annotate routes.rb with the output of 'rake routes'") do
"Annotate routes.rb with the output of 'rake routes'") do
ENV['routes'] = 'true' ENV['routes'] = 'true'
end end
opts.on('-v', '--version', opts.on('-aa', '--active-admin', 'Annotate active_admin models') do
"Show the current version of this gem") do ENV['active_admin'] = 'true'
end
opts.on('-v', '--version', 'Show the current version of this gem') do
puts "annotate v#{Annotate.version}"; exit puts "annotate v#{Annotate.version}"; exit
end end
opts.on('-m', '--show-migration', opts.on('-m', '--show-migration', 'Include the migration version number in the annotation') do
"Include the migration version number in the annotation") do ENV['include_version'] = 'yes'
ENV['include_version'] = "yes"
end end
opts.on('-k', '--show-foreign-keys', opts.on('-k', '--show-foreign-keys',
"List the table's foreign key constraints in the annotation") do "List the table's foreign key constraints in the annotation") do
ENV['show_foreign_keys'] = "yes" ENV['show_foreign_keys'] = 'yes'
end end
opts.on('-i', '--show-indexes', opts.on('-i', '--show-indexes',
"List the table's database indexes in the annotation") do "List the table's database indexes in the annotation") do
ENV['show_indexes'] = "yes" ENV['show_indexes'] = 'yes'
end end
opts.on('-s', '--simple-indexes', opts.on('-s', '--simple-indexes',
"Concat the column's related indexes in the annotation") do "Concat the column's related indexes in the annotation") do
ENV['simple_indexes'] = "yes" ENV['simple_indexes'] = 'yes'
end end
opts.on('--model-dir dir', opts.on('--model-dir dir',
"Annotate model files stored in dir rather than app/models, separate multiple dirs with comas") do |dir| "Annotate model files stored in dir rather than app/models, separate multiple dirs with commas") do |dir|
ENV['model_dir'] = dir ENV['model_dir'] = dir
end end
opts.on('--root-dir dir', opts.on('--root-dir dir',
"Annotate files stored within root dir projects, separate multiple dirs with comas") do |dir| "Annotate files stored within root dir projects, separate multiple dirs with commas") do |dir|
ENV['root_dir'] = dir ENV['root_dir'] = dir
end end
opts.on('--ignore-model-subdirects', opts.on('--ignore-model-subdirects',
"Ignore subdirectories of the models directory") do |dir| "Ignore subdirectories of the models directory") do |dir|
ENV['ignore_model_sub_dir'] = "yes" ENV['ignore_model_sub_dir'] = 'yes'
end end
opts.on('--sort', opts.on('--sort',
"Sort columns alphabetically, rather than in creation order") do |dir| "Sort columns alphabetically, rather than in creation order") do |dir|
ENV['sort'] = "yes" ENV['sort'] = 'yes'
end end
opts.on('--classified-sort', opts.on('--classified-sort',
"Sort columns alphabetically, but first goes id, then the rest columns, then the timestamp columns and then the association columns") do |dir| "Sort columns alphabetically, but first goes id, then the rest columns, then the timestamp columns and then the association columns") do |dir|
ENV['classified_sort'] = "yes" ENV['classified_sort'] = 'yes'
end end
opts.on('-R', '--require path', opts.on('-R', '--require path',
...@@ -155,10 +153,10 @@ OptionParser.new do |opts| ...@@ -155,10 +153,10 @@ OptionParser.new do |opts|
opts.on('-e', '--exclude [tests,fixtures,factories,serializers]', Array, "Do not annotate fixtures, test files, factories, and/or serializers") do |exclusions| opts.on('-e', '--exclude [tests,fixtures,factories,serializers]', Array, "Do not annotate fixtures, test files, factories, and/or serializers") do |exclusions|
exclusions ||= %w(tests fixtures factories) exclusions ||= %w(tests fixtures factories)
exclusions.each { |exclusion| ENV["exclude_#{exclusion}"] = "yes" } exclusions.each { |exclusion| ENV["exclude_#{exclusion}"] = 'yes' }
end end
opts.on('-f', '--format [bare|rdoc|markdown]', ['bare', 'rdoc', 'markdown'], 'Render Schema Infomation as plain/RDoc/Markdown') do |fmt| opts.on('-f', '--format [bare|rdoc|markdown]', %w(bare rdoc markdown), 'Render Schema Infomation as plain/RDoc/Markdown') do |fmt|
ENV["format_#{fmt}"] = 'yes' ENV["format_#{fmt}"] = 'yes'
end end
...@@ -174,21 +172,30 @@ OptionParser.new do |opts| ...@@ -174,21 +172,30 @@ OptionParser.new do |opts|
ENV['trace'] = 'yes' ENV['trace'] = 'yes'
end end
opts.on('-I', '--ignore-columns REGEX', "don't annotate columns that match a given REGEX (i.e., `annotate -I '^(id|updated_at|created_at)'`" ) do |regex| opts.on('-I', '--ignore-columns REGEX', "don't annotate columns that match a given REGEX (i.e., `annotate -I '^(id|updated_at|created_at)'`") do |regex|
ENV['ignore_columns'] = regex ENV['ignore_columns'] = regex
end end
opts.on('--hide-limit-column-types VALUES', "don't show limit for given column types, separated by comas (i.e., `integer,boolean,text`)" ) do |values| opts.on('--ignore-routes REGEX', "don't annotate routes that match a given REGEX (i.e., `annotate -I '(mobile|resque|pghero)'`") do |regex|
ENV['ignore_routes'] = regex
end
opts.on('--hide-limit-column-types VALUES', "don't show limit for given column types, separated by commas (i.e., `integer,boolean,text`)") do |values|
ENV['hide_limit_column_types'] = "#{values}" ENV['hide_limit_column_types'] = "#{values}"
end end
opts.on('--ignore-unknown-models', "don't display warnings for bad model files" ) do |values| opts.on('--hide-default-column-types VALUES', "don't show default for given column types, separated by commas (i.e., `json,jsonb,hstore`)") do |values|
ENV['ignore_unknown_models'] = "true" ENV['hide_default_column_types'] = "#{values}"
end end
opts.on('--ignore-unknown-models', "don't display warnings for bad model files") do |values|
ENV['ignore_unknown_models'] = 'true'
end
end.parse! end.parse!
options = Annotate.setup_options({ :is_rake => ENV['is_rake'] && !ENV['is_rake'].empty? }) options = Annotate.setup_options(
is_rake: ENV['is_rake'] && !ENV['is_rake'].empty?
)
Annotate.eager_load(options) Annotate.eager_load(options)
AnnotateModels.send(target_action, options) if Annotate.include_models? AnnotateModels.send(target_action, options) if Annotate.include_models?
......
machine:
ruby:
version: 2.2.6
test:
override:
- bundle exec rubocop && bundle exec rspec
$:.unshift(File.dirname(__FILE__)) # rubocop:disable Metrics/ModuleLength
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'annotate/version' require 'annotate/version'
require 'annotate/annotate_models' require 'annotate/annotate_models'
require 'annotate/annotate_routes' require 'annotate/annotate_routes'
...@@ -7,60 +9,70 @@ begin ...@@ -7,60 +9,70 @@ begin
# ActiveSupport 3.x... # ActiveSupport 3.x...
require 'active_support/hash_with_indifferent_access' require 'active_support/hash_with_indifferent_access'
require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/blank'
rescue Exception rescue StandardError
# ActiveSupport 2.x... # ActiveSupport 2.x...
require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/blank' require 'active_support/core_ext/blank'
end end
module Annotate module Annotate
TRUE_RE = /^(true|t|yes|y|1)$/i
## ##
# The set of available options to customize the behavior of Annotate. # The set of available options to customize the behavior of Annotate.
# #
POSITION_OPTIONS=[ POSITION_OPTIONS = [
:position_in_routes, :position_in_class, :position_in_test, :position_in_routes, :position_in_class, :position_in_test,
:position_in_fixture, :position_in_factory, :position, :position_in_fixture, :position_in_factory, :position,
:position_in_serializer :position_in_serializer
] ].freeze
FLAG_OPTIONS=[ FLAG_OPTIONS = [
:show_indexes, :simple_indexes, :include_version, :exclude_tests, :show_indexes, :simple_indexes, :include_version, :exclude_tests,
:exclude_fixtures, :exclude_factories, :ignore_model_sub_dir, :exclude_fixtures, :exclude_factories, :ignore_model_sub_dir,
:format_bare, :format_rdoc, :format_markdown, :sort, :force, :trace, :format_bare, :format_rdoc, :format_markdown, :sort, :force, :trace,
:timestamp, :exclude_serializers, :classified_sort, :show_foreign_keys, :timestamp, :exclude_serializers, :classified_sort, :show_foreign_keys,
:exclude_scaffolds, :exclude_controllers, :exclude_helpers, :ignore_unknown_models, :exclude_scaffolds, :exclude_controllers, :exclude_helpers,
] :exclude_sti_subclasses, :ignore_unknown_models
OTHER_OPTIONS=[ ].freeze
:ignore_columns, :skip_on_db_migrate, :wrapper_open, :wrapper_close, :wrapper, :routes, OTHER_OPTIONS = [
:hide_limit_column_types, :ignore_columns, :skip_on_db_migrate, :wrapper_open, :wrapper_close,
] :wrapper, :routes, :hide_limit_column_types, :hide_default_column_types,
PATH_OPTIONS=[ :ignore_routes, :active_admin
].freeze
PATH_OPTIONS = [
:require, :model_dir, :root_dir :require, :model_dir, :root_dir
] ].freeze
def self.all_options
[POSITION_OPTIONS, FLAG_OPTIONS, PATH_OPTIONS, OTHER_OPTIONS]
end
## ##
# Set default values that can be overridden via environment variables. # Set default values that can be overridden via environment variables.
# #
def self.set_defaults(options = {}) def self.set_defaults(options = {})
return if(@has_set_defaults) return if @has_set_defaults
@has_set_defaults = true @has_set_defaults = true
options = HashWithIndifferentAccess.new(options) options = HashWithIndifferentAccess.new(options)
[POSITION_OPTIONS, FLAG_OPTIONS, PATH_OPTIONS, OTHER_OPTIONS].flatten.each do |key| all_options.flatten.each do |key|
if options.has_key?(key) if options.key?(key)
default_value = if options[key].is_a?(Array) default_value = if options[key].is_a?(Array)
options[key].join(",") options[key].join(',')
else else
options[key] options[key]
end end
end end
default_value = ENV[key.to_s] if !ENV[key.to_s].blank? default_value = ENV[key.to_s] unless ENV[key.to_s].blank?
ENV[key.to_s] = default_value.nil? ? nil : default_value.to_s ENV[key.to_s] = default_value.nil? ? nil : default_value.to_s
end end
end end
TRUE_RE = /^(true|t|yes|y|1)$/i ##
# TODO: what is the difference between this and set_defaults?
#
def self.setup_options(options = {}) def self.setup_options(options = {})
POSITION_OPTIONS.each do |key| POSITION_OPTIONS.each do |key|
options[key] = fallback(ENV[key.to_s], ENV['position'], 'before') options[key] = fallback(ENV[key.to_s], ENV['position'], 'before')
...@@ -69,30 +81,27 @@ module Annotate ...@@ -69,30 +81,27 @@ module Annotate
options[key] = true?(ENV[key.to_s]) options[key] = true?(ENV[key.to_s])
end end
OTHER_OPTIONS.each do |key| OTHER_OPTIONS.each do |key|
options[key] = (!ENV[key.to_s].blank?) ? ENV[key.to_s] : nil options[key] = !ENV[key.to_s].blank? ? ENV[key.to_s] : nil
end end
PATH_OPTIONS.each do |key| PATH_OPTIONS.each do |key|
options[key] = (!ENV[key.to_s].blank?) ? ENV[key.to_s].split(',') : [] options[key] = !ENV[key.to_s].blank? ? ENV[key.to_s].split(',') : []
end end
if(options[:model_dir].empty?) options[:model_dir] = ['app/models'] if options[:model_dir].empty?
options[:model_dir] = ['app/models']
end
if(options[:root_dir].empty?)
options[:root_dir] = ['']
end
options[:wrapper_open] ||= options[:wrapper] options[:wrapper_open] ||= options[:wrapper]
options[:wrapper_close] ||= options[:wrapper] options[:wrapper_close] ||= options[:wrapper]
return options # These were added in 2.7.0 but so this is to revert to old behavior by default
options[:exclude_scaffolds] = Annotate.true?(ENV.fetch('exclude_scaffolds', 'true'))
options[:exclude_controllers] = Annotate.true?(ENV.fetch('exclude_controllers', 'true'))
options[:exclude_helpers] = Annotate.true?(ENV.fetch('exclude_helpers', 'true'))
options
end end
def self.reset_options def self.reset_options
[POSITION_OPTIONS, FLAG_OPTIONS, PATH_OPTIONS, OTHER_OPTIONS].flatten.each do |key| all_options.flatten.each { |key| ENV[key.to_s] = nil }
ENV[key.to_s] = nil
end
end end
def self.skip_on_migration? def self.skip_on_migration?
...@@ -107,26 +116,34 @@ module Annotate ...@@ -107,26 +116,34 @@ module Annotate
true true
end end
def self.loaded_tasks=(val); @loaded_tasks = val; end def self.loaded_tasks=(val)
def self.loaded_tasks; return @loaded_tasks; end @loaded_tasks = val
end
def self.loaded_tasks
@loaded_tasks
end
def self.load_tasks def self.load_tasks
return if(self.loaded_tasks) return if loaded_tasks
self.loaded_tasks = true self.loaded_tasks = true
Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each { |rake| load rake } Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each do |rake|
load rake
end
end end
def self.load_requires(options) def self.load_requires(options)
options[:require].each { |path| require path } if options[:require].count > 0 options[:require].count > 0 &&
options[:require].each { |path| require path }
end end
def self.eager_load(options) def self.eager_load(options)
self.load_requires(options) load_requires(options)
require "annotate/active_record_patch" require 'annotate/active_record_patch'
if(defined?(Rails)) if defined?(Rails::Application)
if(Rails.version.split('.').first.to_i < 3) if Rails.version.split('.').first.to_i < 3
Rails.configuration.eager_load_paths.each do |load_path| Rails.configuration.eager_load_paths.each do |load_path|
matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/ matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/
Dir.glob("#{load_path}/**/*.rb").sort.each do |file| Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
...@@ -149,33 +166,38 @@ module Annotate ...@@ -149,33 +166,38 @@ module Annotate
def self.bootstrap_rake def self.bootstrap_rake
begin begin
require 'rake/dsl_definition' require 'rake/dsl_definition'
rescue Exception rescue StandardError => e
# We might just be on an old version of Rake... # We might just be on an old version of Rake...
puts e.message
exit e.status_code
end end
require 'rake' require 'rake'
if File.exists?('./Rakefile') load './Rakefile' if File.exist?('./Rakefile')
load './Rakefile' begin
Rake::Task[:environment].invoke
rescue
nil
end end
Rake::Task[:environment].invoke rescue nil unless defined?(Rails)
if(!defined?(Rails))
# Not in a Rails project, so time to load up the parts of # Not in a Rails project, so time to load up the parts of
# ActiveSupport we need. # ActiveSupport we need.
require 'active_support' require 'active_support'
require 'active_support/core_ext/class/subclasses' require 'active_support/core_ext/class/subclasses'
require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/string/inflections'
end end
self.load_tasks
load_tasks
Rake::Task[:set_annotation_options].invoke Rake::Task[:set_annotation_options].invoke
end end
def self.fallback(*args) def self.fallback(*args)
return args.detect { |arg| !arg.blank? } args.detect { |arg| !arg.blank? }
end end
def self.true?(val) def self.true?(val)
return false if(val.blank?) return false if val.blank?
return false unless(val =~ TRUE_RE) return false unless val =~ TRUE_RE
return true true
end end
end end
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
module ::ActiveRecord module ::ActiveRecord
class Base class Base
def self.method_missing(name, *args) def self.method_missing(_name, *_args)
# ignore this, so unknown/unloaded macros won't cause parsing to fail # ignore this, so unknown/unloaded macros won't cause parsing to fail
end end
end end
......
# rubocop:disable Metrics/ModuleLength
require 'bigdecimal' require 'bigdecimal'
module AnnotateModels module AnnotateModels
TRUE_RE = /^(true|t|yes|y|1)$/i
# Annotate Models plugin use this header # Annotate Models plugin use this header
COMPAT_PREFIX = "== Schema Info" COMPAT_PREFIX = '== Schema Info'.freeze
COMPAT_PREFIX_MD = "## Schema Info" COMPAT_PREFIX_MD = '## Schema Info'.freeze
PREFIX = "== Schema Information" PREFIX = '== Schema Information'.freeze
PREFIX_MD = "## Schema Information" PREFIX_MD = '## Schema Information'.freeze
END_MARK = "== Schema Information End" END_MARK = '== Schema Information End'.freeze
PATTERN = /^\r?\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\r?\n(#.*\r?\n)*(\r?\n)*/
MATCHED_TYPES = %w(test fixture factory serializer scaffold controller helper) MATCHED_TYPES = %w(test fixture factory serializer scaffold controller helper).freeze
# File.join for windows reverse bar compat? # File.join for windows reverse bar compat?
# I dont use windows, can`t test # I dont use windows, can`t test
UNIT_TEST_DIR = File.join("test", "unit") UNIT_TEST_DIR = File.join('test', "unit")
MODEL_TEST_DIR = File.join("test", "models") # since rails 4.0 MODEL_TEST_DIR = File.join('test', "models") # since rails 4.0
SPEC_MODEL_DIR = File.join("spec", "models") SPEC_MODEL_DIR = File.join('spec', "models")
FIXTURE_TEST_DIR = File.join("test", "fixtures") FIXTURE_TEST_DIR = File.join('test', "fixtures")
FIXTURE_SPEC_DIR = File.join("spec", "fixtures") FIXTURE_SPEC_DIR = File.join('spec', "fixtures")
# Other test files # Other test files
CONTROLLER_TEST_DIR = File.join("test", "controllers") CONTROLLER_TEST_DIR = File.join('test', "controllers")
CONTROLLER_SPEC_DIR = File.join("spec", "controllers") CONTROLLER_SPEC_DIR = File.join('spec', "controllers")
REQUEST_SPEC_DIR = File.join("spec", "requests") REQUEST_SPEC_DIR = File.join('spec', "requests")
ROUTING_SPEC_DIR = File.join("spec", "routing") ROUTING_SPEC_DIR = File.join('spec', "routing")
# Object Daddy http://github.com/flogic/object_daddy/tree/master # Object Daddy http://github.com/flogic/object_daddy/tree/master
EXEMPLARS_TEST_DIR = File.join("test", "exemplars") EXEMPLARS_TEST_DIR = File.join('test', "exemplars")
EXEMPLARS_SPEC_DIR = File.join("spec", "exemplars") EXEMPLARS_SPEC_DIR = File.join('spec', "exemplars")
# Machinist http://github.com/notahat/machinist # Machinist http://github.com/notahat/machinist
BLUEPRINTS_TEST_DIR = File.join("test", "blueprints") BLUEPRINTS_TEST_DIR = File.join('test', "blueprints")
BLUEPRINTS_SPEC_DIR = File.join("spec", "blueprints") BLUEPRINTS_SPEC_DIR = File.join('spec', "blueprints")
# Factory Girl http://github.com/thoughtbot/factory_girl # Factory Girl http://github.com/thoughtbot/factory_girl
FACTORY_GIRL_TEST_DIR = File.join("test", "factories") FACTORY_GIRL_TEST_DIR = File.join('test', "factories")
FACTORY_GIRL_SPEC_DIR = File.join("spec", "factories") FACTORY_GIRL_SPEC_DIR = File.join('spec', "factories")
# Fabrication https://github.com/paulelliott/fabrication.git # Fabrication https://github.com/paulelliott/fabrication.git
FABRICATORS_TEST_DIR = File.join("test", "fabricators") FABRICATORS_TEST_DIR = File.join('test', "fabricators")
FABRICATORS_SPEC_DIR = File.join("spec", "fabricators") FABRICATORS_SPEC_DIR = File.join('spec', "fabricators")
# Serializers https://github.com/rails-api/active_model_serializers # Serializers https://github.com/rails-api/active_model_serializers
SERIALIZERS_DIR = File.join("app", "serializers") SERIALIZERS_DIR = File.join('app', "serializers")
SERIALIZERS_TEST_DIR = File.join("test", "serializers") SERIALIZERS_TEST_DIR = File.join('test', "serializers")
SERIALIZERS_SPEC_DIR = File.join("spec", "serializers") SERIALIZERS_SPEC_DIR = File.join('spec', "serializers")
# Controller files # Controller files
CONTROLLER_DIR = File.join("app", "controllers") CONTROLLER_DIR = File.join('app', "controllers")
# Active admin registry files
ACTIVEADMIN_DIR = File.join('app', "admin")
# Helper files # Helper files
HELPER_DIR = File.join("app", "helpers") HELPER_DIR = File.join('app', "helpers")
# Don't show limit (#) on these column types # Don't show limit (#) on these column types
# Example: show "integer" instead of "integer(4)" # Example: show "integer" instead of "integer(4)"
NO_LIMIT_COL_TYPES = ["integer", "boolean"] NO_LIMIT_COL_TYPES = %w(integer boolean).freeze
# Don't show default value for these column types
NO_DEFAULT_COL_TYPES = %w(json jsonb hstore).freeze
class << self class << self
def model_dir def annotate_pattern(options = {})
@model_dir.is_a?(Array) ? @model_dir : [@model_dir || "app/models"] if options[:wrapper_open]
return /(?:^\n?# (?:#{options[:wrapper_open]}).*\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\n(#.*\n)*\n*)|^\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\n(#.*\n)*\n*/
end
/^\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\n(#.*\n)*\n*/
end end
def model_dir=(dir) def model_dir
@model_dir = dir @model_dir.is_a?(Array) ? @model_dir : [@model_dir || 'app/models']
end end
attr_writer :model_dir
def root_dir def root_dir
@root_dir.is_a?(Array) ? @root_dir : [@root_dir || ""] if @root_dir.blank?
['']
elsif @root_dir.is_a?(String)
@root_dir.split(',')
else
@root_dir
end
end end
def root_dir=(dir) attr_writer :root_dir
@root_dir = dir
def test_files(root_directory)
[
File.join(root_directory, UNIT_TEST_DIR, "%MODEL_NAME%_test.rb"),
File.join(root_directory, MODEL_TEST_DIR, "%MODEL_NAME%_test.rb"),
File.join(root_directory, SPEC_MODEL_DIR, "%MODEL_NAME%_spec.rb")
]
end
def fixture_files(root_directory)
[
File.join(root_directory, FIXTURE_TEST_DIR, "%TABLE_NAME%.yml"),
File.join(root_directory, FIXTURE_SPEC_DIR, "%TABLE_NAME%.yml"),
File.join(root_directory, FIXTURE_TEST_DIR, "%PLURALIZED_MODEL_NAME%.yml"),
File.join(root_directory, FIXTURE_SPEC_DIR, "%PLURALIZED_MODEL_NAME%.yml")
]
end
def scaffold_files(root_directory)
[
File.join(root_directory, CONTROLLER_TEST_DIR, "%PLURALIZED_MODEL_NAME%_controller_test.rb"),
File.join(root_directory, CONTROLLER_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_controller_spec.rb"),
File.join(root_directory, REQUEST_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_spec.rb"),
File.join(root_directory, ROUTING_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_routing_spec.rb")
]
end
def factory_files(root_directory)
[
File.join(root_directory, EXEMPLARS_TEST_DIR, "%MODEL_NAME%_exemplar.rb"),
File.join(root_directory, EXEMPLARS_SPEC_DIR, "%MODEL_NAME%_exemplar.rb"),
File.join(root_directory, BLUEPRINTS_TEST_DIR, "%MODEL_NAME%_blueprint.rb"),
File.join(root_directory, BLUEPRINTS_SPEC_DIR, "%MODEL_NAME%_blueprint.rb"),
File.join(root_directory, FACTORY_GIRL_TEST_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
File.join(root_directory, FACTORY_GIRL_SPEC_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
File.join(root_directory, FACTORY_GIRL_TEST_DIR, "%TABLE_NAME%.rb"), # (new style)
File.join(root_directory, FACTORY_GIRL_SPEC_DIR, "%TABLE_NAME%.rb"), # (new style)
File.join(root_directory, FABRICATORS_TEST_DIR, "%MODEL_NAME%_fabricator.rb"),
File.join(root_directory, FABRICATORS_SPEC_DIR, "%MODEL_NAME%_fabricator.rb")
]
end
def serialize_files(root_directory)
[
File.join(root_directory, SERIALIZERS_DIR, "%MODEL_NAME%_serializer.rb"),
File.join(root_directory, SERIALIZERS_TEST_DIR, "%MODEL_NAME%_serializer_spec.rb"),
File.join(root_directory, SERIALIZERS_SPEC_DIR, "%MODEL_NAME%_serializer_spec.rb")
]
end
def files_by_pattern(root_directory, pattern_type)
case pattern_type
when 'test' then test_files(root_directory)
when 'fixture' then fixture_files(root_directory)
when 'scaffold' then scaffold_files(root_directory)
when 'factory' then factory_files(root_directory)
when 'serializer' then serialize_files(root_directory)
when 'controller'
[File.join(root_directory, CONTROLLER_DIR, "%PLURALIZED_MODEL_NAME%_controller.rb")]
when 'admin'
[File.join(root_directory, ACTIVEADMIN_DIR, "%MODEL_NAME%.rb")]
when 'helper'
[File.join(root_directory, HELPER_DIR, "%PLURALIZED_MODEL_NAME%_helper.rb")]
else
[]
end
end end
def get_patterns(pattern_types=MATCHED_TYPES) def get_patterns(pattern_types = [])
current_patterns = [] current_patterns = []
root_dir.each do |root_directory| root_dir.each do |root_directory|
Array(pattern_types).each do |pattern_type| Array(pattern_types).each do |pattern_type|
current_patterns += case pattern_type current_patterns += files_by_pattern(root_directory, pattern_type)
when 'test'
[
File.join(root_directory, UNIT_TEST_DIR, "%MODEL_NAME%_test.rb"),
File.join(root_directory, MODEL_TEST_DIR, "%MODEL_NAME%_test.rb"),
File.join(root_directory, SPEC_MODEL_DIR, "%MODEL_NAME%_spec.rb"),
]
when 'fixture'
[
File.join(root_directory, FIXTURE_TEST_DIR, "%TABLE_NAME%.yml"),
File.join(root_directory, FIXTURE_SPEC_DIR, "%TABLE_NAME%.yml"),
File.join(root_directory, FIXTURE_TEST_DIR, "%PLURALIZED_MODEL_NAME%.yml"),
File.join(root_directory, FIXTURE_SPEC_DIR, "%PLURALIZED_MODEL_NAME%.yml"),
]
when 'scaffold'
[
File.join(root_directory, CONTROLLER_TEST_DIR, "%PLURALIZED_MODEL_NAME%_controller_test.rb"),
File.join(root_directory, CONTROLLER_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_controller_spec.rb"),
File.join(root_directory, REQUEST_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_spec.rb"),
File.join(root_directory, ROUTING_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_routing_spec.rb"),
]
when 'factory'
[
File.join(root_directory, EXEMPLARS_TEST_DIR, "%MODEL_NAME%_exemplar.rb"),
File.join(root_directory, EXEMPLARS_SPEC_DIR, "%MODEL_NAME%_exemplar.rb"),
File.join(root_directory, BLUEPRINTS_TEST_DIR, "%MODEL_NAME%_blueprint.rb"),
File.join(root_directory, BLUEPRINTS_SPEC_DIR, "%MODEL_NAME%_blueprint.rb"),
File.join(root_directory, FACTORY_GIRL_TEST_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
File.join(root_directory, FACTORY_GIRL_SPEC_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
File.join(root_directory, FACTORY_GIRL_TEST_DIR, "%TABLE_NAME%.rb"), # (new style)
File.join(root_directory, FACTORY_GIRL_SPEC_DIR, "%TABLE_NAME%.rb"), # (new style)
File.join(root_directory, FABRICATORS_TEST_DIR, "%MODEL_NAME%_fabricator.rb"),
File.join(root_directory, FABRICATORS_SPEC_DIR, "%MODEL_NAME%_fabricator.rb"),
]
when 'serializer'
[
File.join(root_directory, SERIALIZERS_DIR, "%MODEL_NAME%_serializer.rb"),
File.join(root_directory, SERIALIZERS_TEST_DIR, "%MODEL_NAME%_serializer_spec.rb"),
File.join(root_directory, SERIALIZERS_SPEC_DIR, "%MODEL_NAME%_serializer_spec.rb")
]
when 'controller'
[
File.join(root_directory, CONTROLLER_DIR, "%PLURALIZED_MODEL_NAME%_controller.rb")
]
when 'helper'
[
File.join(root_directory, HELPER_DIR, "%PLURALIZED_MODEL_NAME%_helper.rb")
]
end
end end
end end
current_patterns.map{ |p| p.sub(/^[\/]*/, '') } current_patterns.map { |p| p.sub(/^[\/]*/, '') }
end end
# Simple quoting for the default column value # Simple quoting for the default column value
def quote(value) def quote(value)
case value case value
when NilClass then "NULL" when NilClass then 'NULL'
when TrueClass then "TRUE" when TrueClass then 'TRUE'
when FalseClass then "FALSE" when FalseClass then 'FALSE'
when Float, Fixnum, Bignum then value.to_s when Float, Integer then value.to_s
# BigDecimals need to be output in a non-normalized form and quoted. # BigDecimals need to be output in a non-normalized form and quoted.
when BigDecimal then value.to_s('F') when BigDecimal then value.to_s('F')
when Array then value.map {|v| quote(v)} when Array then value.map { |v| quote(v) }
else else
value.inspect value.inspect
end end
...@@ -150,31 +187,38 @@ module AnnotateModels ...@@ -150,31 +187,38 @@ module AnnotateModels
quote(klass.column_defaults[column.name]) quote(klass.column_defaults[column.name])
end end
def retrieve_indexes_from_table(klass)
table_name = klass.table_name
return [] unless table_name
indexes = klass.connection.indexes(table_name)
return indexes if indexes.any? || !klass.table_name_prefix
# Try to search the table without prefix
table_name.to_s.slice!(klass.table_name_prefix)
klass.connection.indexes(table_name)
end
# Use the column information in an ActiveRecord class # Use the column information in an ActiveRecord class
# to create a comment block containing a line for # to create a comment block containing a line for
# each column. The line contains the column name, # each column. The line contains the column name,
# the type (and length), and any optional attributes # the type (and length), and any optional attributes
def get_schema_info(klass, header, options = {}) def get_schema_info(klass, header, options = {})
info = "# #{header}\n" info = "# #{header}\n"
info<< "#\n" info << get_schema_header_text(klass, options)
if(options[:format_markdown])
info<< "# Table name: `#{klass.table_name}`\n"
info<< "#\n"
info<< "# ### Columns\n"
else
info<< "# Table name: #{klass.table_name}\n"
end
info<< "#\n"
max_size = klass.column_names.map{|name| name.size}.max || 0 max_size = klass.column_names.map(&:size).max || 0
with_comment = options[:with_comment] && klass.columns.first.respond_to?(:comment)
max_size = klass.columns.map{|col| col.name.size + col.comment.size }.max || 0 if with_comment
max_size += 2 if with_comment
max_size += options[:format_rdoc] ? 5 : 1 max_size += options[:format_rdoc] ? 5 : 1
md_names_overhead = 6 md_names_overhead = 6
md_type_allowance = 18 md_type_allowance = 18
bare_type_allowance = 16 bare_type_allowance = 16
if(options[:format_markdown]) if options[:format_markdown]
info<< sprintf( "# %-#{max_size + md_names_overhead}.#{max_size + md_names_overhead}s | %-#{md_type_allowance}.#{md_type_allowance}s | %s\n", 'Name', 'Type', 'Attributes' ) info << sprintf( "# %-#{max_size + md_names_overhead}.#{max_size + md_names_overhead}s | %-#{md_type_allowance}.#{md_type_allowance}s | %s\n", 'Name', 'Type', 'Attributes' )
info<< "# #{ '-' * ( max_size + md_names_overhead ) } | #{'-' * md_type_allowance} | #{ '-' * 27 }\n" info << "# #{ '-' * ( max_size + md_names_overhead ) } | #{'-' * md_type_allowance} | #{ '-' * 27 }\n"
end end
cols = if ignore_columns = options[:ignore_columns] cols = if ignore_columns = options[:ignore_columns]
...@@ -185,20 +229,20 @@ module AnnotateModels ...@@ -185,20 +229,20 @@ module AnnotateModels
klass.columns klass.columns
end end
cols = cols.sort_by(&:name) if(options[:sort]) cols = cols.sort_by(&:name) if options[:sort]
cols = classified_sort(cols) if(options[:classified_sort]) cols = classified_sort(cols) if options[:classified_sort]
cols.each do |col| cols.each do |col|
col_type = (col.type || col.sql_type).to_s col_type = (col.type || col.sql_type).to_s
attrs = [] attrs = []
attrs << "default(#{schema_default(klass, col)})" unless col.default.nil? || col_type == "jsonb" attrs << "default(#{schema_default(klass, col)})" unless col.default.nil? || hide_default?(col_type, options)
attrs << "not null" unless col.null attrs << 'unsigned' if col.respond_to?(:unsigned?) && col.unsigned?
attrs << "primary key" if klass.primary_key && (klass.primary_key.is_a?(Array) ? klass.primary_key.collect{|c|c.to_sym}.include?(col.name.to_sym) : col.name.to_sym == klass.primary_key.to_sym) attrs << 'not null' unless col.null
attrs << 'primary key' if klass.primary_key && (klass.primary_key.is_a?(Array) ? klass.primary_key.collect(&:to_sym).include?(col.name.to_sym) : col.name.to_sym == klass.primary_key.to_sym)
if col_type == "decimal" if col_type == 'decimal'
col_type << "(#{col.precision}, #{col.scale})" col_type << "(#{col.precision}, #{col.scale})"
elsif col_type != "spatial" elsif col_type != 'spatial'
if (col.limit) if col.limit
if col.limit.is_a? Array if col.limit.is_a? Array
attrs << "(#{col.limit.join(', ')})" attrs << "(#{col.limit.join(', ')})"
else else
...@@ -208,38 +252,41 @@ module AnnotateModels ...@@ -208,38 +252,41 @@ module AnnotateModels
end end
# Check out if we got an array column # Check out if we got an array column
if col.respond_to?(:array) && col.array attrs << 'is an Array' if col.respond_to?(:array) && col.array
attrs << "is an Array"
end
# Check out if we got a geometric column # Check out if we got a geometric column
# and print the type and SRID # and print the type and SRID
if col.respond_to?(:geometry_type) if col.respond_to?(:geometry_type)
attrs << "#{col.geometry_type}, #{col.srid}" attrs << "#{col.geometry_type}, #{col.srid}"
elsif col.respond_to?(:geometric_type) and col.geometric_type.present? elsif col.respond_to?(:geometric_type) && col.geometric_type.present?
attrs << "#{col.geometric_type.to_s.downcase}, #{col.srid}" attrs << "#{col.geometric_type.to_s.downcase}, #{col.srid}"
end end
# Check if the column has indices and print "indexed" if true # Check if the column has indices and print "indexed" if true
# If the index includes another column, print it too. # If the index includes another column, print it too.
if options[:simple_indexes] && klass.table_exists?# Check out if this column is indexed if options[:simple_indexes] && klass.table_exists?# Check out if this column is indexed
indices = klass.connection.indexes(klass.table_name) indices = retrieve_indexes_from_table(klass)
if indices = indices.select { |ind| ind.columns.include? col.name } if indices = indices.select { |ind| ind.columns.include? col.name }
indices.sort_by{|ind| ind.name}.each do |ind| indices.sort_by(&:name).each do |ind|
next if ind.columns.is_a?(String)
ind = ind.columns.reject! { |i| i == col.name } ind = ind.columns.reject! { |i| i == col.name }
attrs << (ind.length == 0 ? "indexed" : "indexed => [#{ind.join(", ")}]") attrs << (ind.empty? ? "indexed" : "indexed => [#{ind.join(", ")}]")
end end
end end
end end
col_name = if with_comment
"#{col.name}(#{col.comment})"
else
col.name
end
if options[:format_rdoc] if options[:format_rdoc]
info << sprintf("# %-#{max_size}.#{max_size}s<tt>%s</tt>", "*#{col.name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n" info << sprintf("# %-#{max_size}.#{max_size}s<tt>%s</tt>", "*#{col_name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n"
elsif options[:format_markdown] elsif options[:format_markdown]
name_remainder = max_size - col.name.length name_remainder = max_size - col_name.length
type_remainder = (md_type_allowance - 2) - col_type.length type_remainder = (md_type_allowance - 2) - col_type.length
info << (sprintf("# **`%s`**%#{name_remainder}s | `%s`%#{type_remainder}s | `%s`", col.name, " ", col_type, " ", attrs.join(", ").rstrip)).gsub('``', ' ').rstrip + "\n" info << (sprintf("# **`%s`**%#{name_remainder}s | `%s`%#{type_remainder}s | `%s`", col_name, " ", col_type, " ", attrs.join(", ").rstrip)).gsub('``', ' ').rstrip + "\n"
else else
info << sprintf("# %-#{max_size}.#{max_size}s:%-#{bare_type_allowance}.#{bare_type_allowance}s %s", col.name, col_type, attrs.join(", ")).rstrip + "\n" info << sprintf("# %-#{max_size}.#{max_size}s:%-#{bare_type_allowance}.#{bare_type_allowance}s %s", col_name, col_type, attrs.join(", ")).rstrip + "\n"
end end
end end
...@@ -251,6 +298,23 @@ module AnnotateModels ...@@ -251,6 +298,23 @@ module AnnotateModels
info << get_foreign_key_info(klass, options) info << get_foreign_key_info(klass, options)
end end
info << get_schema_footer_text(klass, options)
end
def get_schema_header_text(klass, options = {})
info = "#\n"
if options[:format_markdown]
info << "# Table name: `#{klass.table_name}`\n"
info << "#\n"
info << "# ### Columns\n"
else
info << "# Table name: #{klass.table_name}\n"
end
info << "#\n"
end
def get_schema_footer_text(_klass, options = {})
info = ''
if options[:format_rdoc] if options[:format_rdoc]
info << "#--\n" info << "#--\n"
info << "# #{END_MARK}\n" info << "# #{END_MARK}\n"
...@@ -260,29 +324,30 @@ module AnnotateModels ...@@ -260,29 +324,30 @@ module AnnotateModels
end end
end end
def get_index_info(klass, options={}) def get_index_info(klass, options = {})
if(options[:format_markdown]) index_info = if options[:format_markdown]
index_info = "#\n# ### Indexes\n#\n" "#\n# ### Indexes\n#\n"
else else
index_info = "#\n# Indexes\n#\n" "#\n# Indexes\n#\n"
end end
indexes = klass.connection.indexes(klass.table_name) indexes = retrieve_indexes_from_table(klass)
return "" if indexes.empty? return '' if indexes.empty?
max_size = indexes.collect{|index| index.name.size}.max + 1 max_size = indexes.collect{|index| index.name.size}.max + 1
indexes.sort_by{|index| index.name}.each do |index| indexes.sort_by(&:name).each do |index|
if(options[:format_markdown]) index_info << if options[:format_markdown]
index_info << sprintf("# * `%s`%s:\n# * **`%s`**\n", index.name, index.unique ? " (_unique_)" : "", index.columns.join("`**\n# * **`")) sprintf("# * `%s`%s:\n# * **`%s`**\n", index.name, index.unique ? " (_unique_)" : "", Array(index.columns).join("`**\n# * **`"))
else else
index_info << sprintf("# %-#{max_size}.#{max_size}s %s %s", index.name, "(#{index.columns.join(",")})", index.unique ? "UNIQUE" : "").rstrip + "\n" sprintf("# %-#{max_size}.#{max_size}s %s %s", index.name, "(#{Array(index.columns).join(",")})", index.unique ? "UNIQUE" : "").rstrip + "\n"
end end
end end
return index_info
index_info
end end
def hide_limit?(col_type, options) def hide_limit?(col_type, options)
excludes = excludes =
if options[:hide_limit_column_types].blank? if options[:hide_limit_column_types].blank?
NO_LIMIT_COL_TYPES NO_LIMIT_COL_TYPES
else else
...@@ -292,45 +357,67 @@ module AnnotateModels ...@@ -292,45 +357,67 @@ module AnnotateModels
excludes.include?(col_type) excludes.include?(col_type)
end end
def get_foreign_key_info(klass, options={}) def hide_default?(col_type, options)
if(options[:format_markdown]) excludes =
fk_info = "#\n# ### Foreign Keys\n#\n" if options[:hide_default_column_types].blank?
else NO_DEFAULT_COL_TYPES
fk_info = "#\n# Foreign Keys\n#\n" else
end options[:hide_default_column_types].split(',')
end
excludes.include?(col_type)
end
def get_foreign_key_info(klass, options = {})
fk_info = if options[:format_markdown]
"#\n# ### Foreign Keys\n#\n"
else
"#\n# Foreign Keys\n#\n"
end
return "" unless klass.connection.supports_foreign_keys? && klass.connection.respond_to?(:foreign_keys) return '' unless klass.connection.respond_to?(:supports_foreign_keys?) &&
klass.connection.supports_foreign_keys? && klass.connection.respond_to?(:foreign_keys)
foreign_keys = klass.connection.foreign_keys(klass.table_name) foreign_keys = klass.connection.foreign_keys(klass.table_name)
return "" if foreign_keys.empty? return '' if foreign_keys.empty?
format_name = ->(fk) { options[:show_complete_foreign_keys] ? fk.name : fk.name.gsub(/(?<=^fk_rails_)[0-9a-f]{10}$/, '...') }
max_size = foreign_keys.collect{|fk| fk.name.size}.max + 1 max_size = foreign_keys.map(&format_name).map(&:size).max + 1
foreign_keys.sort_by{|fk| fk.name}.each do |fk| foreign_keys.sort_by {|fk| [format_name.call(fk), fk.column]}.each do |fk|
ref_info = "#{fk.column} => #{fk.to_table}.#{fk.primary_key}" ref_info = "#{fk.column} => #{fk.to_table}.#{fk.primary_key}"
if(options[:format_markdown]) constraints_info = ''
fk_info << sprintf("# * `%s`:\n# * **`%s`**\n", fk.name, ref_info) constraints_info += "ON DELETE => #{fk.on_delete} " if fk.on_delete
else constraints_info += "ON UPDATE => #{fk.on_update} " if fk.on_update
fk_info << sprintf("# %-#{max_size}.#{max_size}s %s", fk.name, "(#{ref_info})").rstrip + "\n" constraints_info.strip!
end
fk_info << if options[:format_markdown]
sprintf("# * `%s`%s:\n# * **`%s`**\n", format_name.call(fk), constraints_info.blank? ? '' : " (_#{constraints_info}_)", ref_info)
else
sprintf("# %-#{max_size}.#{max_size}s %s %s", format_name.call(fk), "(#{ref_info})", constraints_info).rstrip + "\n"
end
end end
return fk_info
fk_info
end end
# Add a schema block to a file. If the file already contains # Add a schema block to a file. If the file already contains
# a schema info block (a comment starting with "== Schema Information"), check if it # a schema info block (a comment starting with "== Schema Information"),
# matches the block that is already there. If so, leave it be. If not, remove the old # check if it matches the block that is already there. If so, leave it be.
# info block and write a new one. # If not, remove the old info block and write a new one.
# Returns true or false depending on whether the file was modified. #
# == Returns:
# true or false depending on whether the file was modified.
# #
# === Options (opts) # === Options (opts)
# :force<Symbol>:: whether to update the file even if it doesn't seem to need it. # :force<Symbol>:: whether to update the file even if it doesn't seem to need it.
# :position_in_*<Symbol>:: where to place the annotated section in fixture or model file, # :position_in_*<Symbol>:: where to place the annotated section in fixture or model file,
# :before, :top, :after or :bottom. Default is :before. # :before, :top, :after or :bottom. Default is :before.
# #
def annotate_one_file(file_name, info_block, position, options={}) def annotate_one_file(file_name, info_block, position, options = {})
if File.exist?(file_name) if File.exist?(file_name)
old_content = File.read(file_name) old_content = File.read(file_name)
return false if(old_content =~ /# -\*- SkipSchemaAnnotations.*\n/) return false if old_content =~ /# -\*- SkipSchemaAnnotations.*\n/
# Ignore the Schema version line because it changes with each migration # Ignore the Schema version line because it changes with each migration
header_pattern = /(^# Table name:.*?\n(#.*[\r]?\n)*[\r]?)/ header_pattern = /(^# Table name:.*?\n(#.*[\r]?\n)*[\r]?)/
...@@ -341,17 +428,17 @@ module AnnotateModels ...@@ -341,17 +428,17 @@ module AnnotateModels
old_columns = old_header && old_header.scan(column_pattern).sort old_columns = old_header && old_header.scan(column_pattern).sort
new_columns = new_header && new_header.scan(column_pattern).sort new_columns = new_header && new_header.scan(column_pattern).sort
magic_comment_matcher= Regexp.new(/(^#\s*encoding:.*\n)|(^# coding:.*\n)|(^# -\*- coding:.*\n)|(^# -\*- encoding\s?:.*\n)|(^#\s*frozen_string_literal:.+\n)|(^# -\*- frozen_string_literal\s*:.+-\*-\n)/) magic_comment_matcher = Regexp.new(/(^#\s*encoding:.*\n)|(^# coding:.*\n)|(^# -\*- coding:.*\n)|(^# -\*- encoding\s?:.*\n)|(^#\s*frozen_string_literal:.+\n)|(^# -\*- frozen_string_literal\s*:.+-\*-\n)/)
magic_comments= old_content.scan(magic_comment_matcher).flatten.compact magic_comments = old_content.scan(magic_comment_matcher).flatten.compact
if old_columns == new_columns && !options[:force] if old_columns == new_columns && !options[:force]
return false return false
else else
# Replace inline the old schema info with the new schema info # Replace inline the old schema info with the new schema info
new_content = old_content.sub(PATTERN, info_block + "\n") new_content = old_content.sub(annotate_pattern(options), info_block + "\n")
if new_content.end_with?(info_block + "\n") if new_content.end_with?(info_block + "\n")
new_content = old_content.sub(PATTERN, "\n" + info_block) new_content = old_content.sub(annotate_pattern(options), "\n" + info_block)
end end
wrapper_open = options[:wrapper_open] ? "# #{options[:wrapper_open]}\n" : "" wrapper_open = options[:wrapper_open] ? "# #{options[:wrapper_open]}\n" : ""
...@@ -361,41 +448,48 @@ module AnnotateModels ...@@ -361,41 +448,48 @@ module AnnotateModels
# we simply need to insert it in correct position # we simply need to insert it in correct position
if new_content == old_content || options[:force] if new_content == old_content || options[:force]
old_content.sub!(magic_comment_matcher, '') old_content.sub!(magic_comment_matcher, '')
old_content.sub!(PATTERN, '') old_content.sub!(annotate_pattern(options), '')
new_content = %w(after bottom).include?(options[position].to_s) ? new_content = if %w(after bottom).include?(options[position].to_s)
(magic_comments.join + (old_content.rstrip + "\n\n" + wrapped_info_block)) : magic_comments.join + (old_content.rstrip + "\n\n" + wrapped_info_block)
(magic_comments.join + wrapped_info_block + "\n" + old_content) else
magic_comments.join + wrapped_info_block + "\n" + old_content
end
end end
File.open(file_name, "wb") { |f| f.puts new_content } File.open(file_name, 'wb') { |f| f.puts new_content }
return true return true
end end
else else
return false false
end end
end end
def remove_annotation_of_file(file_name) def remove_annotation_of_file(file_name, options = {})
if File.exist?(file_name) if File.exist?(file_name)
content = File.read(file_name) content = File.read(file_name)
wrapper_open = options[:wrapper_open] ? "# #{options[:wrapper_open]}\n" : ''
content.sub!(/(#{wrapper_open})?#{annotate_pattern(options)}/, '')
content.sub!(PATTERN, '') File.open(file_name, 'wb') { |f| f.puts content }
File.open(file_name, "wb") { |f| f.puts content } true
return true
else else
return false false
end end
end end
def matched_types(options)
types = MATCHED_TYPES
types << 'admin' if options[:active_admin] =~ TRUE_RE && !types.include?('admin')
types
end
# Given the name of an ActiveRecord class, create a schema # Given the name of an ActiveRecord class, create a schema
# info block (basically a comment containing information # info block (basically a comment containing information
# on the columns and their types) and put it at the front # on the columns and their types) and put it at the front
# of the model and fixture source files. # of the model and fixture source files.
# Returns true or false depending on whether the source
# files were modified.
# #
# === Options (opts) # === Options (opts)
# :position_in_class<Symbol>:: where to place the annotated section in model file # :position_in_class<Symbol>:: where to place the annotated section in model file
...@@ -410,41 +504,54 @@ module AnnotateModels ...@@ -410,41 +504,54 @@ module AnnotateModels
# :exclude_scaffolds<Symbol>:: whether to skip modification of scaffold files # :exclude_scaffolds<Symbol>:: whether to skip modification of scaffold files
# :exclude_controllers<Symbol>:: whether to skip modification of controller files # :exclude_controllers<Symbol>:: whether to skip modification of controller files
# :exclude_helpers<Symbol>:: whether to skip modification of helper files # :exclude_helpers<Symbol>:: whether to skip modification of helper files
# :exclude_sti_subclasses<Symbol>:: whether to skip modification of files for STI subclasses
#
# == Returns:
# an array of file names that were annotated.
# #
def annotate(klass, file, header, options={}) def annotate(klass, file, header, options = {})
begin begin
klass.reset_column_information
info = get_schema_info(klass, header, options) info = get_schema_info(klass, header, options)
did_annotate = false
model_name = klass.name.underscore model_name = klass.name.underscore
table_name = klass.table_name table_name = klass.table_name
model_file_name = File.join(file) model_file_name = File.join(file)
annotated = []
if annotate_one_file(model_file_name, info, :position_in_class, options_with_position(options, :position_in_class)) if annotate_one_file(model_file_name, info, :position_in_class, options_with_position(options, :position_in_class))
did_annotate = true annotated << model_file_name
end end
MATCHED_TYPES.each do |key| matched_types(options).each do |key|
exclusion_key = "exclude_#{key.pluralize}".to_sym exclusion_key = "exclude_#{key.pluralize}".to_sym
position_key = "position_in_#{key}".to_sym position_key = "position_in_#{key}".to_sym
unless options[exclusion_key] # Same options for active_admin models
did_annotate = self.get_patterns(key). if key == 'admin'
map { |f| resolve_filename(f, model_name, table_name) }. exclusion_key = 'exclude_class'.to_sym
map { |f| annotate_one_file(f, info, position_key, options_with_position(options, position_key)) }. position_key = 'position_in_class'.to_sym
detect { |result| result } || did_annotate
end end
end
return did_annotate next if options[exclusion_key]
rescue Exception => e get_patterns(key)
.map { |f| resolve_filename(f, model_name, table_name) }
.each do |f|
if annotate_one_file(f, info, position_key, options_with_position(options, position_key))
annotated << f
end
end
end
rescue StandardError => e
puts "Unable to annotate #{file}: #{e.message}" puts "Unable to annotate #{file}: #{e.message}"
puts "\t" + e.backtrace.join("\n\t") if options[:trace] puts "\t" + e.backtrace.join("\n\t") if options[:trace]
end end
annotated
end end
# position = :position_in_fixture or :position_in_class # position = :position_in_fixture or :position_in_class
def options_with_position(options, position_in) def options_with_position(options, position_in)
options.merge(:position=>(options[position_in] || options[:position])) options.merge(position: (options[position_in] || options[:position]))
end end
# Return a list of the model files to annotate. # Return a list of the model files to annotate.
...@@ -453,8 +560,8 @@ module AnnotateModels ...@@ -453,8 +560,8 @@ module AnnotateModels
# in the model_dir directory. # in the model_dir directory.
def get_model_files(options) def get_model_files(options)
models = [] models = []
if(!options[:is_rake]) unless options[:is_rake]
models = ARGV.dup.reject{|m| m.match(/^(.*)=/)} models = ARGV.dup.reject { |m| m.match(/^(.*)=/) }
end end
if models.empty? if models.empty?
...@@ -488,13 +595,13 @@ module AnnotateModels ...@@ -488,13 +595,13 @@ module AnnotateModels
model_path = file.gsub(/\.rb$/, '') model_path = file.gsub(/\.rb$/, '')
model_dir.each { |dir| model_path = model_path.gsub(/^#{dir}/, '').gsub(/^\//, '') } model_dir.each { |dir| model_path = model_path.gsub(/^#{dir}/, '').gsub(/^\//, '') }
begin begin
get_loaded_model(model_path) or raise BadModelFileError.new get_loaded_model(model_path) || raise(BadModelFileError.new)
rescue LoadError rescue LoadError
# this is for non-rails projects, which don't get Rails auto-require magic # this is for non-rails projects, which don't get Rails auto-require magic
file_path = File.expand_path(file) file_path = File.expand_path(file)
if File.file?(file_path) && silence_warnings { Kernel.require(file_path) } if File.file?(file_path) && silence_warnings { Kernel.require(file_path) }
retry retry
elsif model_path.match(/\//) elsif model_path =~ /\//
model_path = model_path.split('/')[1..-1].join('/').to_s model_path = model_path.split('/')[1..-1].join('/').to_s
retry retry
else else
...@@ -505,43 +612,42 @@ module AnnotateModels ...@@ -505,43 +612,42 @@ module AnnotateModels
# Retrieve loaded model class by path to the file where it's supposed to be defined. # Retrieve loaded model class by path to the file where it's supposed to be defined.
def get_loaded_model(model_path) def get_loaded_model(model_path)
begin ActiveSupport::Inflector.constantize(ActiveSupport::Inflector.camelize(model_path))
ActiveSupport::Inflector.constantize(ActiveSupport::Inflector.camelize(model_path)) rescue
rescue # Revert to the old way but it is not really robust
# Revert to the old way but it is not really robust ObjectSpace.each_object(::Class)
ObjectSpace.each_object(::Class). .select do |c|
select do |c| Class === c && # note: we use === to avoid a bug in activesupport 2.3.14 OptionMerger vs. is_a?
Class === c and # note: we use === to avoid a bug in activesupport 2.3.14 OptionMerger vs. is_a? c.ancestors.respond_to?(:include?) && # to fix FactoryGirl bug, see https://github.com/ctran/annotate_models/pull/82
c.ancestors.respond_to?(:include?) and # to fix FactoryGirl bug, see https://github.com/ctran/annotate_models/pull/82 c.ancestors.include?(ActiveRecord::Base)
c.ancestors.include?(ActiveRecord::Base) end.detect { |c| ActiveSupport::Inflector.underscore(c.to_s) == model_path }
end. end
detect { |c| ActiveSupport::Inflector.underscore(c.to_s) == model_path }
end def parse_options(options = {})
self.model_dir = options[:model_dir] if options[:model_dir]
self.root_dir = options[:root_dir] if options[:root_dir]
end end
# We're passed a name of things that might be # We're passed a name of things that might be
# ActiveRecord models. If we can find the class, and # ActiveRecord models. If we can find the class, and
# if its a subclass of ActiveRecord::Base, # if its a subclass of ActiveRecord::Base,
# then pass it to the associated block # then pass it to the associated block
def do_annotations(options={}) def do_annotations(options = {})
header = options[:format_markdown] ? PREFIX_MD.dup : PREFIX.dup parse_options(options)
if options[:include_version] header = options[:format_markdown] ? PREFIX_MD.dup : PREFIX.dup
version = ActiveRecord::Migrator.current_version rescue 0 version = ActiveRecord::Migrator.current_version rescue 0
if version > 0 if options[:include_version] && version > 0
header << "\n# Schema version: #{version}" header << "\n# Schema version: #{version}"
end
end end
self.model_dir = options[:model_dir] if options[:model_dir]
self.root_dir = options[:root_dir] if options[:root_dir]
annotated = [] annotated = []
get_model_files(options).each do |file| get_model_files(options).each do |path, filename|
annotate_model_file(annotated, File.join(file), header, options) annotate_model_file(annotated, File.join(path, filename), header, options)
end end
if annotated.empty? if annotated.empty?
puts "Model files unchanged." puts 'Model files unchanged.'
else else
puts "Annotated (#{annotated.length}): #{annotated.join(', ')}" puts "Annotated (#{annotated.length}): #{annotated.join(', ')}"
end end
...@@ -549,27 +655,29 @@ module AnnotateModels ...@@ -549,27 +655,29 @@ module AnnotateModels
def annotate_model_file(annotated, file, header, options) def annotate_model_file(annotated, file, header, options)
begin begin
return false if (/# -\*- SkipSchemaAnnotations.*/ =~ (File.exist?(file) ? File.read(file) : '') ) return false if /# -\*- SkipSchemaAnnotations.*/ =~ (File.exist?(file) ? File.read(file) : '')
klass = get_model_class(file) klass = get_model_class(file)
if klass && klass < ActiveRecord::Base && !klass.abstract_class? && klass.table_exists? do_annotate = klass &&
if annotate(klass, file, header, options) klass < ActiveRecord::Base &&
annotated << file (!options[:exclude_sti_subclasses] || !(klass.superclass < ActiveRecord::Base && klass.table_name == klass.superclass.table_name)) &&
end !klass.abstract_class? &&
end klass.table_exists?
annotated.concat(annotate(klass, file, header, options)) if do_annotate
rescue BadModelFileError => e rescue BadModelFileError => e
unless options[:ignore_unknown_models] unless options[:ignore_unknown_models]
puts "Unable to annotate #{file}: #{e.message}" puts "Unable to annotate #{file}: #{e.message}"
puts "\t" + e.backtrace.join("\n\t") if options[:trace] puts "\t" + e.backtrace.join("\n\t") if options[:trace]
end end
rescue Exception => e rescue StandardError => e
puts "Unable to annotate #{file}: #{e.message}" puts "Unable to annotate #{file}: #{e.message}"
puts "\t" + e.backtrace.join("\n\t") if options[:trace] puts "\t" + e.backtrace.join("\n\t") if options[:trace]
end end
end end
def remove_annotations(options={}) def remove_annotations(options = {})
self.model_dir = options[:model_dir] if options[:model_dir] parse_options(options)
self.root_dir = options[:root_dir] if options[:root_dir]
deannotated = [] deannotated = []
deannotated_klass = false deannotated_klass = false
get_model_files(options).each do |file| get_model_files(options).each do |file|
...@@ -580,19 +688,19 @@ module AnnotateModels ...@@ -580,19 +688,19 @@ module AnnotateModels
model_name = klass.name.underscore model_name = klass.name.underscore
table_name = klass.table_name table_name = klass.table_name
model_file_name = file model_file_name = file
deannotated_klass = true if(remove_annotation_of_file(model_file_name)) deannotated_klass = true if remove_annotation_of_file(model_file_name, options)
get_patterns. get_patterns(matched_types(options))
map { |f| resolve_filename(f, model_name, table_name) }. .map { |f| resolve_filename(f, model_name, table_name) }
each do |f| .each do |f|
if File.exist?(f) if File.exist?(f)
remove_annotation_of_file(f) remove_annotation_of_file(f, options)
deannotated_klass = true deannotated_klass = true
end end
end end
end end
deannotated << klass if(deannotated_klass) deannotated << klass if deannotated_klass
rescue Exception => e rescue StandardError => e
puts "Unable to deannotate #{File.join(file)}: #{e.message}" puts "Unable to deannotate #{File.join(file)}: #{e.message}"
puts "\t" + e.backtrace.join("\n\t") if options[:trace] puts "\t" + e.backtrace.join("\n\t") if options[:trace]
end end
...@@ -601,10 +709,10 @@ module AnnotateModels ...@@ -601,10 +709,10 @@ module AnnotateModels
end end
def resolve_filename(filename_template, model_name, table_name) def resolve_filename(filename_template, model_name, table_name)
return filename_template. filename_template
gsub('%MODEL_NAME%', model_name). .gsub('%MODEL_NAME%', model_name)
gsub('%PLURALIZED_MODEL_NAME%', model_name.pluralize). .gsub('%PLURALIZED_MODEL_NAME%', model_name.pluralize)
gsub('%TABLE_NAME%', table_name || model_name.pluralize) .gsub('%TABLE_NAME%', table_name || model_name.pluralize)
end end
def classified_sort(cols) def classified_sort(cols)
...@@ -613,25 +721,26 @@ module AnnotateModels ...@@ -613,25 +721,26 @@ module AnnotateModels
associations = [] associations = []
id = nil id = nil
cols = cols.each do |c| cols.each do |c|
if c.name.eql?("id") if c.name.eql?('id')
id = c id = c
elsif (c.name.eql?("created_at") || c.name.eql?("updated_at")) elsif c.name.eql?('created_at') || c.name.eql?('updated_at')
timestamps << c timestamps << c
elsif c.name[-3,3].eql?("_id") elsif c.name[-3, 3].eql?('_id')
associations << c associations << c
else else
rest_cols << c rest_cols << c
end end
end end
[rest_cols, timestamps, associations].each {|a| a.sort_by!(&:name) } [rest_cols, timestamps, associations].each { |a| a.sort_by!(&:name) }
return ([id] << rest_cols << timestamps << associations).flatten ([id] << rest_cols << timestamps << associations).flatten.compact
end end
# Ignore warnings for the duration of the block () # Ignore warnings for the duration of the block ()
def silence_warnings def silence_warnings
old_verbose, $VERBOSE = $VERBOSE, nil old_verbose = $VERBOSE
$VERBOSE = nil
yield yield
ensure ensure
$VERBOSE = old_verbose $VERBOSE = old_verbose
......
...@@ -18,139 +18,181 @@ ...@@ -18,139 +18,181 @@
# Released under the same license as Ruby. No Support. No Warranty. # Released under the same license as Ruby. No Support. No Warranty.
# #
module AnnotateRoutes module AnnotateRoutes
PREFIX = "# == Route Map" PREFIX = '== Route Map'.freeze
PREFIX_MD = '## Route Map'.freeze
HEADER_ROW = ['Prefix', 'Verb', 'URI Pattern', 'Controller#Action']
def self.do_annotations(options={}) class << self
return unless(routes_exists?) def content(line, maxs, options = {})
return line.rstrip unless options[:format_markdown]
position_after = ! %w(before top).include?(options[:position_in_routes]) line.each_with_index.map do |elem, index|
min_length = maxs.map { |arr| arr[index] }.max || 0
routes_map = `rake routes`.split(/\n/, -1) sprintf("%-#{min_length}.#{min_length}s", elem.tr('|', '-'))
end.join(' | ')
end
# In old versions of Rake, the first line of output was the cwd. Not so def header(options = {})
# much in newer ones. We ditch that line if it exists, and if not, we routes_map = app_routes_map(options)
# keep the line around.
routes_map.shift if(routes_map.first =~ /^\(in \//)
header = [
"#{PREFIX}" + (options[:timestamp] ? " (Updated #{Time.now.strftime("%Y-%m-%d %H:%M")})" : ""),
"#"
] + routes_map.map { |line| "# #{line}".rstrip }
existing_text = File.read(routes_file)
(content, where_header_found) = strip_annotations(existing_text)
changed = where_header_found != 0 # This will either be :before, :after, or
# a number. If the number is > 0, the
# annotation was found somewhere in the
# middle of the file. If the number is
# zero, no annotation was found.
if(position_after)
# Ensure we have adequate trailing newlines at the end of the file to
# ensure a blank line separating the content from the annotation.
content << '' if(content.last != '')
# We're moving something from the top of the file to the bottom, so ditch out = ["# #{options[:format_markdown] ? PREFIX_MD : PREFIX}" + (options[:timestamp] ? " (Updated #{Time.now.strftime('%Y-%m-%d %H:%M')})" : '')]
# the spacer we put in the first time around. out += ['#']
if(changed && where_header_found == :before) return out if routes_map.size.zero?
content.shift if(content.first == '')
maxs = [HEADER_ROW.map(&:size)] + routes_map[1..-1].map { |line| line.split.map(&:size) }
if options[:format_markdown]
max = maxs.map(&:max).compact.max
out += ["# #{content(HEADER_ROW, maxs, options)}"]
out += ["# #{content(['-' * max, '-' * max, '-' * max, '-' * max], maxs, options)}"]
else
out += ["# #{content(routes_map[0], maxs, options)}"]
end end
else
header = header << '' if(content.first != '' || changed) out + routes_map[1..-1].map { |line| "# #{content(options[:format_markdown] ? line.split(' ') : line, maxs, options)}" }
end end
content = position_after ? (content + header) : header + content def do_annotations(options = {})
return unless routes_exists?
existing_text = File.read(routes_file)
if write_contents(existing_text, content) if write_contents(existing_text, header(options), options)
puts "#{routes_file} annotated." puts "#{routes_file} annotated."
else end
puts "#{routes_file} unchanged."
end end
end
def self.remove_annotations(options={}) def remove_annotations(options={})
return unless(routes_exists?) return unless routes_exists?
existing_text = File.read(routes_file) existing_text = File.read(routes_file)
(content, where_header_found) = strip_annotations(existing_text) content, where_header_found = strip_annotations(existing_text)
content = strip_on_removal(content, where_header_found) content = strip_on_removal(content, where_header_found)
if write_contents(existing_text, content) if write_contents(existing_text, content, options)
puts "Removed annotations from #{routes_file}." puts "Removed annotations from #{routes_file}."
else end
puts "#{routes_file} unchanged."
end end
end end
protected private
def self.app_routes_map(options)
routes_map = `rake routes`.split(/\n/, -1)
# In old versions of Rake, the first line of output was the cwd. Not so
# much in newer ones. We ditch that line if it exists, and if not, we
# keep the line around.
routes_map.shift if routes_map.first =~ /^\(in \//
# Skip routes which match given regex
# Note: it matches the complete line (route_name, path, controller/action)
if options[:ignore_routes]
routes_map.reject! { |line| line =~ /#{options[:ignore_routes]}/ }
end
routes_map
end
def self.routes_file def self.routes_file
@routes_rb ||= File.join("config", "routes.rb") @routes_rb ||= File.join('config', 'routes.rb')
end end
def self.routes_exists? def self.routes_exists?
routes_exists = File.exists?(routes_file) routes_exists = File.exists?(routes_file)
puts "Can`t find routes.rb" if(!routes_exists) puts "Can't find routes.rb" unless routes_exists
return routes_exists
routes_exists
end end
def self.write_contents(existing_text, new_content) def self.write_contents(existing_text, header, options = {})
content, where_header_found = strip_annotations(existing_text)
new_content = annotate_routes(header, content, where_header_found, options)
# Make sure we end on a trailing newline. # Make sure we end on a trailing newline.
new_content << '' unless(new_content.last == '') new_content << '' unless new_content.last == ''
new_text = new_content.join("\n") new_text = new_content.join("\n")
return false if existing_text == new_text if existing_text == new_text
puts "#{routes_file} unchanged."
false
else
File.open(routes_file, 'wb') { |f| f.puts(new_text) }
true
end
end
def self.annotate_routes(header, content, where_header_found, options = {})
if %w(before top).include?(options[:position_in_routes])
header = header << '' if content.first != ''
new_content = header + content
else
# Ensure we have adequate trailing newlines at the end of the file to
# ensure a blank line separating the content from the annotation.
content << '' unless content.last == ''
# We're moving something from the top of the file to the bottom, so ditch
# the spacer we put in the first time around.
content.shift if where_header_found == :before && content.first == ''
new_content = content + header
end
File.open(routes_file, "wb") { |f| f.puts(new_text) } new_content
return true
end end
# TODO: write the method doc using ruby rdoc formats
# where_header_found => This will either be :before, :after, or
# a number. If the number is > 0, the
# annotation was found somewhere in the
# middle of the file. If the number is
# zero, no annotation was found.
def self.strip_annotations(content) def self.strip_annotations(content)
real_content = [] real_content = []
mode = :content mode = :content
line_number = 0
header_found_at = 0 header_found_at = 0
content.split(/\n/, -1).each do |line|
line_number += 1 content.split(/\n/, -1).each_with_index do |line, line_number|
begin if mode == :header && line !~ /\s*#/
if(mode == :header) mode = :content
if(line !~ /\s*#/) next unless line == ''
mode = :content elsif mode == :content
raise unless (line == '') if line =~ /^\s*#\s*== Route.*$/
end header_found_at = line_number + 1 # index start's at 0
elsif(mode == :content) mode = :header
if(line =~ /^\s*#\s*== Route.*$/) else
header_found_at = line_number real_content << line
mode = :header
else
real_content << line
end
end end
rescue
retry
end end
end end
content_lines = real_content.count
# By default assume the annotation was found in the middle of the file... where_header_found(real_content, header_found_at)
where_header_found = header_found_at end
def self.where_header_found(real_content, header_found_at)
# By default assume the annotation was found in the middle of the file
# ... unless we have evidence it was at the beginning ... # ... unless we have evidence it was at the beginning ...
where_header_found = :before if(header_found_at == 1) return real_content, :before if header_found_at == 1
# ... or that it was at the end. # ... or that it was at the end.
where_header_found = :after if(header_found_at >= content_lines) return real_content, :after if header_found_at >= real_content.count
return real_content, where_header_found # and the default
return real_content, header_found_at
end end
def self.strip_on_removal(content, where_header_found) def self.strip_on_removal(content, where_header_found)
if(where_header_found == :before) if where_header_found == :before
content.shift while(content.first == '') content.shift while content.first == ''
elsif(where_header_found == :after) elsif where_header_found == :after
content.pop while(content.last == '') content.pop while content.last == ''
end end
# TODO: If the user buried it in the middle, we should probably see about # TODO: If the user buried it in the middle, we should probably see about
# TODO: preserving a single line of space between the content above and # TODO: preserving a single line of space between the content above and
# TODO: below... # TODO: below...
return content content
end end
end end
module Annotate module Annotate
def self.version def self.version
'2.7.0' '2.7.2'
end end
end end
module Annotate module Annotate
module Generators module Generators
class InstallGenerator < Rails::Generators::Base class InstallGenerator < Rails::Generators::Base
desc "Copy annotate_models rakefiles for automatic annotation" desc 'Copy annotate_models rakefiles for automatic annotation'
source_root File.expand_path('../templates', __FILE__) source_root File.expand_path('../templates', __FILE__)
# copy rake tasks # copy rake tasks
def copy_tasks def copy_tasks
template "auto_annotate_models.rake", "lib/tasks/auto_annotate_models.rake" template 'auto_annotate_models.rake', 'lib/tasks/auto_annotate_models.rake'
end end
end end
end end
end end
...@@ -2,44 +2,50 @@ ...@@ -2,44 +2,50 @@
# NOTE: are sensitive to local FS writes, and besides -- it's just not proper # NOTE: are sensitive to local FS writes, and besides -- it's just not proper
# NOTE: to have a dev-mode tool do its thing in production. # NOTE: to have a dev-mode tool do its thing in production.
if Rails.env.development? if Rails.env.development?
require 'annotate'
task :set_annotation_options do task :set_annotation_options do
# You can override any of these by setting an environment variable of the # You can override any of these by setting an environment variable of the
# same name. # same name.
Annotate.set_defaults( Annotate.set_defaults(
'routes' => 'false', 'routes' => 'false',
'position_in_routes' => 'before', 'position_in_routes' => 'before',
'position_in_class' => 'before', 'position_in_class' => 'before',
'position_in_test' => 'before', 'position_in_test' => 'before',
'position_in_fixture' => 'before', 'position_in_fixture' => 'before',
'position_in_factory' => 'before', 'position_in_factory' => 'before',
'position_in_serializer' => 'before', 'position_in_serializer' => 'before',
'show_foreign_keys' => 'true', 'show_foreign_keys' => 'true',
'show_indexes' => 'true', 'show_complete_foreign_keys' => 'false',
'simple_indexes' => 'false', 'show_indexes' => 'true',
'model_dir' => 'app/models', 'simple_indexes' => 'false',
'root_dir' => '', 'model_dir' => 'app/models',
'include_version' => 'false', 'root_dir' => '',
'require' => '', 'include_version' => 'false',
'exclude_tests' => 'false', 'require' => '',
'exclude_fixtures' => 'false', 'exclude_tests' => 'false',
'exclude_factories' => 'false', 'exclude_fixtures' => 'false',
'exclude_serializers' => 'false', 'exclude_factories' => 'false',
'exclude_scaffolds' => 'false', 'exclude_serializers' => 'false',
'exclude_controllers' => 'false', 'exclude_scaffolds' => 'true',
'exclude_helpers' => 'false', 'exclude_controllers' => 'true',
'ignore_model_sub_dir' => 'false', 'exclude_helpers' => 'true',
'ignore_columns' => nil, 'exclude_sti_subclasses' => 'false',
'ignore_unknown_models' => 'false', 'ignore_model_sub_dir' => 'false',
'hide_limit_column_types' => '<%= AnnotateModels::NO_LIMIT_COL_TYPES.join(',') %>', 'ignore_columns' => nil,
'skip_on_db_migrate' => 'false', 'ignore_routes' => nil,
'format_bare' => 'true', 'ignore_unknown_models' => 'false',
'format_rdoc' => 'false', 'hide_limit_column_types' => '<%= AnnotateModels::NO_LIMIT_COL_TYPES.join(",") %>',
'format_markdown' => 'false', 'hide_default_column_types' => '<%= AnnotateModels::NO_DEFAULT_COL_TYPES.join(",") %>',
'sort' => 'false', 'skip_on_db_migrate' => 'false',
'force' => 'false', 'format_bare' => 'true',
'trace' => 'false', 'format_rdoc' => 'false',
'wrapper_open' => nil, 'format_markdown' => 'false',
'wrapper_close' => nil, 'sort' => 'false',
'force' => 'false',
'trace' => 'false',
'wrapper_open' => nil,
'wrapper_close' => nil,
'with_comment' => true
) )
end end
......
annotate_lib = File.expand_path(File.dirname(File.dirname(__FILE__))) annotate_lib = File.expand_path(File.dirname(File.dirname(__FILE__)))
if !ENV['is_cli'] unless ENV['is_cli']
task :set_annotation_options task :set_annotation_options
task :annotate_models => :set_annotation_options task annotate_models: :set_annotation_options
end end
desc "Add schema information (as comments) to model and fixture files" desc 'Add schema information (as comments) to model and fixture files'
task :annotate_models => :environment do task annotate_models: :environment do
require "#{annotate_lib}/annotate/annotate_models" require "#{annotate_lib}/annotate/annotate_models"
require "#{annotate_lib}/annotate/active_record_patch" require "#{annotate_lib}/annotate/active_record_patch"
options={ :is_rake => true } options = {is_rake: true}
ENV['position'] = options[:position] = Annotate.fallback(ENV['position'], 'before') ENV['position'] = options[:position] = Annotate.fallback(ENV['position'], 'before')
options[:position_in_class] = Annotate.fallback(ENV['position_in_class'], ENV['position']) options[:position_in_class] = Annotate.fallback(ENV['position_in_class'], ENV['position'])
options[:position_in_fixture] = Annotate.fallback(ENV['position_in_fixture'], ENV['position']) options[:position_in_fixture] = Annotate.fallback(ENV['position_in_fixture'], ENV['position'])
...@@ -18,10 +18,11 @@ task :annotate_models => :environment do ...@@ -18,10 +18,11 @@ task :annotate_models => :environment do
options[:position_in_test] = Annotate.fallback(ENV['position_in_test'], ENV['position']) options[:position_in_test] = Annotate.fallback(ENV['position_in_test'], ENV['position'])
options[:position_in_serializer] = Annotate.fallback(ENV['position_in_serializer'], ENV['position']) options[:position_in_serializer] = Annotate.fallback(ENV['position_in_serializer'], ENV['position'])
options[:show_foreign_keys] = Annotate.true?(ENV['show_foreign_keys']) options[:show_foreign_keys] = Annotate.true?(ENV['show_foreign_keys'])
options[:show_complete_foreign_keys] = Annotate.true?(ENV['show_complete_foreign_keys'])
options[:show_indexes] = Annotate.true?(ENV['show_indexes']) options[:show_indexes] = Annotate.true?(ENV['show_indexes'])
options[:simple_indexes] = Annotate.true?(ENV['simple_indexes']) options[:simple_indexes] = Annotate.true?(ENV['simple_indexes'])
options[:model_dir] = ENV['model_dir'] ? ENV['model_dir'].split(',') : ['app/models'] options[:model_dir] = ENV['model_dir'] ? ENV['model_dir'].split(',') : ['app/models']
options[:root_dir] = ENV['root_dir'] ? ENV['root_dir'].split(',') : [''] options[:root_dir] = ENV['root_dir']
options[:include_version] = Annotate.true?(ENV['include_version']) options[:include_version] = Annotate.true?(ENV['include_version'])
options[:require] = ENV['require'] ? ENV['require'].split(',') : [] options[:require] = ENV['require'] ? ENV['require'].split(',') : []
options[:exclude_tests] = Annotate.true?(ENV['exclude_tests']) options[:exclude_tests] = Annotate.true?(ENV['exclude_tests'])
...@@ -29,8 +30,9 @@ task :annotate_models => :environment do ...@@ -29,8 +30,9 @@ task :annotate_models => :environment do
options[:exclude_fixtures] = Annotate.true?(ENV['exclude_fixtures']) options[:exclude_fixtures] = Annotate.true?(ENV['exclude_fixtures'])
options[:exclude_serializers] = Annotate.true?(ENV['exclude_serializers']) options[:exclude_serializers] = Annotate.true?(ENV['exclude_serializers'])
options[:exclude_scaffolds] = Annotate.true?(ENV['exclude_scaffolds']) options[:exclude_scaffolds] = Annotate.true?(ENV['exclude_scaffolds'])
options[:exclude_controllers] = Annotate.true?(ENV['exclude_controllers']) options[:exclude_controllers] = Annotate.true?(ENV.fetch('exclude_controllers', 'true'))
options[:exclude_helpers] = Annotate.true?(ENV['exclude_helpers']) options[:exclude_helpers] = Annotate.true?(ENV.fetch('exclude_helpers', 'true'))
options[:exclude_sti_subclasses] = Annotate.true?(ENV['exclude_sti_subclasses'])
options[:ignore_model_sub_dir] = Annotate.true?(ENV['ignore_model_sub_dir']) options[:ignore_model_sub_dir] = Annotate.true?(ENV['ignore_model_sub_dir'])
options[:format_bare] = Annotate.true?(ENV['format_bare']) options[:format_bare] = Annotate.true?(ENV['format_bare'])
options[:format_rdoc] = Annotate.true?(ENV['format_rdoc']) options[:format_rdoc] = Annotate.true?(ENV['format_rdoc'])
...@@ -42,16 +44,19 @@ task :annotate_models => :environment do ...@@ -42,16 +44,19 @@ task :annotate_models => :environment do
options[:wrapper_open] = Annotate.fallback(ENV['wrapper_open'], ENV['wrapper']) options[:wrapper_open] = Annotate.fallback(ENV['wrapper_open'], ENV['wrapper'])
options[:wrapper_close] = Annotate.fallback(ENV['wrapper_close'], ENV['wrapper']) options[:wrapper_close] = Annotate.fallback(ENV['wrapper_close'], ENV['wrapper'])
options[:ignore_columns] = ENV.fetch('ignore_columns', nil) options[:ignore_columns] = ENV.fetch('ignore_columns', nil)
options[:ignore_routes] = ENV.fetch('ignore_routes', nil)
options[:hide_limit_column_types] = Annotate.fallback(ENV['hide_limit_column_types'], '')
options[:hide_default_column_types] = Annotate.fallback(ENV['hide_default_column_types'], '')
AnnotateModels.do_annotations(options) AnnotateModels.do_annotations(options)
end end
desc "Remove schema information from model and fixture files" desc 'Remove schema information from model and fixture files'
task :remove_annotation => :environment do task remove_annotation: :environment do
require "#{annotate_lib}/annotate/annotate_models" require "#{annotate_lib}/annotate/annotate_models"
require "#{annotate_lib}/annotate/active_record_patch" require "#{annotate_lib}/annotate/active_record_patch"
options={ :is_rake => true } options = {is_rake: true}
options[:model_dir] = ENV['model_dir'] options[:model_dir] = ENV['model_dir']
options[:root_dir] = ENV['root_dir'] options[:root_dir] = ENV['root_dir']
options[:require] = ENV['require'] ? ENV['require'].split(',') : [] options[:require] = ENV['require'] ? ENV['require'].split(',') : []
......
...@@ -6,6 +6,7 @@ task :annotate_routes => :environment do ...@@ -6,6 +6,7 @@ task :annotate_routes => :environment do
options={} options={}
ENV['position'] = options[:position] = Annotate.fallback(ENV['position'], 'before') ENV['position'] = options[:position] = Annotate.fallback(ENV['position'], 'before')
options[:position_in_routes] = Annotate.fallback(ENV['position_in_routes'], ENV['position']) options[:position_in_routes] = Annotate.fallback(ENV['position_in_routes'], ENV['position'])
options[:ignore_routes] = Annotate.fallback(ENV['ignore_routes'], nil)
options[:require] = ENV['require'] ? ENV['require'].split(',') : [] options[:require] = ENV['require'] ? ENV['require'].split(',') : []
AnnotateRoutes.do_annotations(options) AnnotateRoutes.do_annotations(options)
end end
......
Colons can be used to align columns.
| Tables | Are | Cool |
| ------------- |:-------------:| -----:|
| col 3 is | right-aligned | $1600 |
| col 2 is | centered | $12 |
| zebra stripes | are neat | $1 |
There must be at least 3 dashes separating each header cell.
The outer pipes (|) are optional, and you don't need to make the
raw Markdown line up prettily. You can also use inline Markdown.
Markdown | Less | Pretty
--- | --- | ---
*Still* | `renders` | **nicely**
1 | 2 | 3
## Route Map
 Prefix | Verb | URI Pattern | Controller#Action
--------- | ---------- | --------------- | --------------------
myaction1 | GET | /url1(.:format) | mycontroller1#action
myaction2 | POST | /url2(.:format) | mycontroller2#action
 myaction3 | DELETE-GET | /url3(.:format) | mycontroller3#action \n")
Table name: `users`
### Columns
Name | Type | Attributes
----------------------- | ------------------ | ---------------------------
**`id`** | `integer` | `not null, primary key`
**`foreign_thing_id`** | `integer` | `not null`
### Foreign Keys
* `fk_rails_...` (_ON DELETE => on_delete_value ON UPDATE => on_update_value_):
* **`foreign_thing_id => foreign_things.id`**
#encoding: utf-8 # encoding: utf-8
require File.dirname(__FILE__) + '/../spec_helper.rb' require File.dirname(__FILE__) + '/../spec_helper.rb'
require 'annotate/annotate_models' require 'annotate/annotate_models'
require 'annotate/active_record_patch' require 'annotate/active_record_patch'
require 'active_support/core_ext/string' require 'active_support/core_ext/string'
describe AnnotateModels do describe AnnotateModels do
def mock_foreign_key(name, from_column, to_table, to_column = 'id') def mock_index(name, columns = [], unique = false)
double("ForeignKeyDefinition", double('IndexKeyDefinition',
:name => name, name: name,
:column => from_column, columns: columns,
:to_table => to_table, unique: unique)
:primary_key => to_column, end
)
def mock_foreign_key(name, from_column, to_table, to_column = 'id', constraints = {})
double('ForeignKeyDefinition',
name: name,
column: from_column,
to_table: to_table,
primary_key: to_column,
on_delete: constraints[:on_delete],
on_update: constraints[:on_update])
end end
def mock_connection(indexes = [], foreign_keys = []) def mock_connection(indexes = [], foreign_keys = [])
double("Conn", double('Conn',
:indexes => indexes, indexes: indexes,
:foreign_keys => foreign_keys, foreign_keys: foreign_keys,
:supports_foreign_keys? => true, supports_foreign_keys?: true)
)
end end
def mock_class(table_name, primary_key, columns, foreign_keys = []) def mock_class(table_name, primary_key, columns, indexes = [], foreign_keys = [])
options = { options = {
:connection => mock_connection([], foreign_keys), connection: mock_connection(indexes, foreign_keys),
:table_exists? => true, table_exists?: true,
:table_name => table_name, table_name: table_name,
:primary_key => primary_key, primary_key: primary_key,
:column_names => columns.map { |col| col.name.to_s }, column_names: columns.map { |col| col.name.to_s },
:columns => columns, columns: columns,
:column_defaults => Hash[columns.map { |col| column_defaults: Hash[columns.map { |col| [col.name, col.default] }],
[col.name, col.default] table_name_prefix: ''
}]
} }
double("An ActiveRecord class", options) double('An ActiveRecord class', options)
end end
def mock_column(name, type, options={}) def mock_column(name, type, options = {})
default_options = { default_options = {
:limit => nil, limit: nil,
:null => false, null: false,
:default => nil default: nil
} }
stubs = default_options.dup stubs = default_options.dup
stubs.merge!(options) stubs.merge!(options)
stubs.merge!(:name => name, :type => type) stubs[:name] = name
stubs[:type] = type
double("Column", stubs) double('Column', stubs)
end end
it { expect(AnnotateModels.quote(nil)).to eql("NULL") } it { expect(AnnotateModels.quote(nil)).to eql('NULL') }
it { expect(AnnotateModels.quote(true)).to eql("TRUE") } it { expect(AnnotateModels.quote(true)).to eql('TRUE') }
it { expect(AnnotateModels.quote(false)).to eql("FALSE") } it { expect(AnnotateModels.quote(false)).to eql('FALSE') }
it { expect(AnnotateModels.quote(25)).to eql("25") } it { expect(AnnotateModels.quote(25)).to eql('25') }
it { expect(AnnotateModels.quote(25.6)).to eql("25.6") } it { expect(AnnotateModels.quote(25.6)).to eql('25.6') }
it { expect(AnnotateModels.quote(1e-20)).to eql("1.0e-20") } it { expect(AnnotateModels.quote(1e-20)).to eql('1.0e-20') }
it { expect(AnnotateModels.quote(BigDecimal.new("1.2"))).to eql("1.2") } it { expect(AnnotateModels.quote(BigDecimal.new('1.2'))).to eql('1.2') }
it { expect(AnnotateModels.quote([BigDecimal.new("1.2")])).to eql(["1.2"]) } it { expect(AnnotateModels.quote([BigDecimal.new('1.2')])).to eql(['1.2']) }
it "should get schema info with default options" do it 'should get schema info with default options' do
klass = mock_class(:users, :id, [ klass = mock_class(:users,
mock_column(:id, :integer, :limit => 8), :id,
mock_column(:name, :string, :limit => 50), [
mock_column(:notes, :text, :limit => 55), mock_column(:id, :integer, limit: 8),
]) mock_column(:name, :string, limit: 50),
mock_column(:notes, :text, limit: 55)
expect(AnnotateModels.get_schema_info(klass, "Schema Info")).to eql(<<-EOS) ])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info')).to eql(<<-EOS)
# Schema Info # Schema Info
# #
# Table name: users # Table name: users
...@@ -80,13 +89,15 @@ describe AnnotateModels do ...@@ -80,13 +89,15 @@ describe AnnotateModels do
EOS EOS
end end
it "should get schema info even if the primary key is not set" do it 'should get schema info even if the primary key is not set' do
klass = mock_class(:users, nil, [ klass = mock_class(:users,
mock_column(:id, :integer), nil,
mock_column(:name, :string, :limit => 50) [
]) mock_column(:id, :integer),
mock_column(:name, :string, limit: 50)
])
expect(AnnotateModels.get_schema_info(klass, "Schema Info")).to eql(<<-EOS) expect(AnnotateModels.get_schema_info(klass, 'Schema Info')).to eql(<<-EOS)
# Schema Info # Schema Info
# #
# Table name: users # Table name: users
...@@ -97,14 +108,16 @@ EOS ...@@ -97,14 +108,16 @@ EOS
EOS EOS
end end
it "should get schema info even if the primary key is array, if using composite_primary_keys" do it 'should get schema info even if the primary key is array, if using composite_primary_keys' do
klass = mock_class(:users, [:a_id, :b_id], [ klass = mock_class(:users,
mock_column(:a_id, :integer), [:a_id, :b_id],
mock_column(:b_id, :integer), [
mock_column(:name, :string, :limit => 50) mock_column(:a_id, :integer),
]) mock_column(:b_id, :integer),
mock_column(:name, :string, limit: 50)
])
expect(AnnotateModels.get_schema_info(klass, "Schema Info")).to eql(<<-EOS) expect(AnnotateModels.get_schema_info(klass, 'Schema Info')).to eql(<<-EOS)
# Schema Info # Schema Info
# #
# Table name: users # Table name: users
...@@ -115,13 +128,16 @@ EOS ...@@ -115,13 +128,16 @@ EOS
# #
EOS EOS
end end
it "should get schema info with enum type " do
klass = mock_class(:users, nil, [
mock_column(:id, :integer),
mock_column(:name, :enum, :limit => [:enum1, :enum2])
])
expect(AnnotateModels.get_schema_info(klass, "Schema Info")).to eql(<<-EOS) it 'should get schema info with enum type' do
klass = mock_class(:users,
nil,
[
mock_column(:id, :integer),
mock_column(:name, :enum, limit: [:enum1, :enum2])
])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info')).to eql(<<-EOS)
# Schema Info # Schema Info
# #
# Table name: users # Table name: users
...@@ -132,13 +148,40 @@ EOS ...@@ -132,13 +148,40 @@ EOS
EOS EOS
end end
it "should get schema info for integer and boolean with default" do it 'should get schema info with unsigned' do
klass = mock_class(:users, :id, [ klass = mock_class(:users,
mock_column(:id, :integer), nil,
mock_column(:size, :integer, :default => 20), [
mock_column(:flag, :boolean, :default => false) mock_column(:id, :integer),
]) mock_column(:integer, :integer, unsigned?: true),
expect(AnnotateModels.get_schema_info(klass, "Schema Info")).to eql(<<-EOS) mock_column(:bigint, :bigint, unsigned?: true),
mock_column(:float, :float, unsigned?: true),
mock_column(:decimal, :decimal, unsigned?: true, precision: 10, scale: 2),
])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info')).to eql(<<-EOS)
# Schema Info
#
# Table name: users
#
# id :integer not null
# integer :integer unsigned, not null
# bigint :bigint unsigned, not null
# float :float unsigned, not null
# decimal :decimal(10, 2) unsigned, not null
#
EOS
end
it 'should get schema info for integer and boolean with default' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:size, :integer, default: 20),
mock_column(:flag, :boolean, default: false)
])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info')).to eql(<<-EOS)
# Schema Info # Schema Info
# #
# Table name: users # Table name: users
...@@ -150,19 +193,26 @@ EOS ...@@ -150,19 +193,26 @@ EOS
EOS EOS
end end
it "should get foreign key info" do it 'should get foreign key info' do
klass = mock_class(:users, :id, [ klass = mock_class(:users,
mock_column(:id, :integer), :id,
mock_column(:foreign_thing_id, :integer), [
], mock_column(:id, :integer),
[ mock_column(:foreign_thing_id, :integer)
mock_foreign_key( ],
'fk_rails_02e851e3b7', [],
'foreign_thing_id', [
'foreign_things' mock_foreign_key('fk_rails_cf2568e89e',
) 'foreign_thing_id',
]) 'foreign_things'),
expect(AnnotateModels.get_schema_info(klass, "Schema Info", :show_foreign_keys => true)).to eql(<<-EOS) mock_foreign_key('custom_fk_name',
'other_thing_id',
'other_things'),
mock_foreign_key('fk_rails_a70234b26c',
'third_thing_id',
'third_things')
])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_foreign_keys: true)).to eql(<<-EOS)
# Schema Info # Schema Info
# #
# Table name: users # Table name: users
...@@ -172,17 +222,168 @@ EOS ...@@ -172,17 +222,168 @@ EOS
# #
# Foreign Keys # Foreign Keys
# #
# fk_rails_02e851e3b7 (foreign_thing_id => foreign_things.id) # custom_fk_name (other_thing_id => other_things.id)
# fk_rails_... (foreign_thing_id => foreign_things.id)
# fk_rails_... (third_thing_id => third_things.id)
# #
EOS EOS
end end
it "should get schema info as RDoc" do it 'should get complete foreign key info' do
klass = mock_class(:users, :id, [ klass = mock_class(:users,
mock_column(:id, :integer), :id,
mock_column(:name, :string, :limit => 50) [
]) mock_column(:id, :integer),
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, :format_rdoc => true)).to eql(<<-EOS) mock_column(:foreign_thing_id, :integer)
],
[],
[
mock_foreign_key('fk_rails_cf2568e89e',
'foreign_thing_id',
'foreign_things'),
mock_foreign_key('custom_fk_name',
'other_thing_id',
'other_things'),
mock_foreign_key('fk_rails_a70234b26c',
'third_thing_id',
'third_things')
])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_foreign_keys: true, show_complete_foreign_keys: true)).to eql(<<-EOS)
# Schema Info
#
# Table name: users
#
# id :integer not null, primary key
# foreign_thing_id :integer not null
#
# Foreign Keys
#
# custom_fk_name (other_thing_id => other_things.id)
# fk_rails_a70234b26c (third_thing_id => third_things.id)
# fk_rails_cf2568e89e (foreign_thing_id => foreign_things.id)
#
EOS
end
it 'should get foreign key info if on_delete/on_update options present' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:foreign_thing_id, :integer)
],
[],
[
mock_foreign_key('fk_rails_02e851e3b7',
'foreign_thing_id',
'foreign_things',
'id',
on_delete: 'on_delete_value',
on_update: 'on_update_value')
])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_foreign_keys: true)).to eql(<<-EOS)
# Schema Info
#
# Table name: users
#
# id :integer not null, primary key
# foreign_thing_id :integer not null
#
# Foreign Keys
#
# fk_rails_... (foreign_thing_id => foreign_things.id) ON DELETE => on_delete_value ON UPDATE => on_update_value
#
EOS
end
it 'should get indexes keys' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:foreign_thing_id, :integer)
], [mock_index('index_rails_02e851e3b7', ['id']),
mock_index('index_rails_02e851e3b8', ['foreign_thing_id'])])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS)
# Schema Info
#
# Table name: users
#
# id :integer not null, primary key
# foreign_thing_id :integer not null
#
# Indexes
#
# index_rails_02e851e3b7 (id)
# index_rails_02e851e3b8 (foreign_thing_id)
#
EOS
end
it 'should get simple indexes keys' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:foreign_thing_id, :integer)
], [mock_index('index_rails_02e851e3b7', ['id']),
mock_index('index_rails_02e851e3b8', ['foreign_thing_id'])])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', simple_indexes: true)).to eql(<<-EOS)
# Schema Info
#
# Table name: users
#
# id :integer not null, primary key
# foreign_thing_id :integer not null
#
EOS
end
it 'should get simple indexes keys if one is in string form' do
klass = mock_class(:users,
:id,
[
mock_column("id", :integer),
mock_column("name", :string)
], [mock_index('index_rails_02e851e3b7', ['id']),
mock_index('index_rails_02e851e3b8', 'LOWER(name)')])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', simple_indexes: true)).to eql(<<-EOS)
# Schema Info
#
# Table name: users
#
# id :integer not null, primary key, indexed
# name :string not null
#
EOS
end
it 'should not crash getting indexes keys' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:foreign_thing_id, :integer)
], [])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS)
# Schema Info
#
# Table name: users
#
# id :integer not null, primary key
# foreign_thing_id :integer not null
#
EOS
end
it 'should get schema info as RDoc' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:name, :string, limit: 50)
])
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_rdoc: true)).to eql(<<-EOS)
# #{AnnotateModels::PREFIX} # #{AnnotateModels::PREFIX}
# #
# Table name: users # Table name: users
...@@ -195,76 +396,302 @@ EOS ...@@ -195,76 +396,302 @@ EOS
EOS EOS
end end
describe "#get_schema_info with custom options" do it 'should get schema info as Markdown' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:name, :string, limit: 50)
])
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true)).to eql(<<-EOS)
# #{AnnotateModels::PREFIX}
#
# Table name: `users`
#
# ### Columns
#
# Name | Type | Attributes
# ----------- | ------------------ | ---------------------------
# **`id`** | `integer` | `not null, primary key`
# **`name`** | `string(50)` | `not null`
#
EOS
end
it 'should get schema info as Markdown with foreign keys' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:foreign_thing_id, :integer)
],
[],
[
mock_foreign_key('fk_rails_02e851e3b7',
'foreign_thing_id',
'foreign_things',
'id',
on_delete: 'on_delete_value',
on_update: 'on_update_value')
])
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_foreign_keys: true)).to eql(<<-EOS)
# #{AnnotateModels::PREFIX}
#
# Table name: `users`
#
# ### Columns
#
# Name | Type | Attributes
# ----------------------- | ------------------ | ---------------------------
# **`id`** | `integer` | `not null, primary key`
# **`foreign_thing_id`** | `integer` | `not null`
#
# ### Foreign Keys
#
# * `fk_rails_...` (_ON DELETE => on_delete_value ON UPDATE => on_update_value_):
# * **`foreign_thing_id => foreign_things.id`**
#
EOS
end
it 'should get schema info as Markdown with indexes' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:name, :string, limit: 50)
], [mock_index('index_rails_02e851e3b7', ['id']),
mock_index('index_rails_02e851e3b8', ['foreign_thing_id'])])
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS)
# #{AnnotateModels::PREFIX}
#
# Table name: `users`
#
# ### Columns
#
# Name | Type | Attributes
# ----------- | ------------------ | ---------------------------
# **`id`** | `integer` | `not null, primary key`
# **`name`** | `string(50)` | `not null`
#
# ### Indexes
#
# * `index_rails_02e851e3b7`:
# * **`id`**
# * `index_rails_02e851e3b8`:
# * **`foreign_thing_id`**
#
EOS
end
describe '#get_schema_info with custom options' do
def self.when_called_with(options = {}) def self.when_called_with(options = {})
expected = options.delete(:returns) expected = options.delete(:returns)
default_columns = [
[:id, :integer, { limit: 8 }],
[:active, :boolean, { limit: 1 }],
[:name, :string, { limit: 50 }],
[:notes, :text, { limit: 55 }]
]
it "should work with options = #{options}" do it "should work with options = #{options}" do
klass = mock_class(:users, :id, [ with_columns = (options.delete(:with_columns) || default_columns).map do |column|
mock_column(:id, :integer, :limit => 8), mock_column(column[0], column[1], column[2])
mock_column(:active, :boolean, :limit => 1), end
mock_column(:name, :string, :limit => 50),
mock_column(:notes, :text, :limit => 55), klass = mock_class(:users, :id, with_columns)
])
schema_info = AnnotateModels.get_schema_info(klass, "Schema Info", options) schema_info = AnnotateModels.get_schema_info(klass, 'Schema Info', options)
expect(schema_info).to eql(expected) expect(schema_info).to eql(expected)
end end
end end
when_called_with hide_limit_column_types: '', returns: <<-EOS.strip_heredoc describe 'hide_limit_column_types option' do
# Schema Info when_called_with hide_limit_column_types: '', returns: <<-EOS.strip_heredoc
# # Schema Info
# Table name: users #
# # Table name: users
# id :integer not null, primary key #
# active :boolean not null # id :integer not null, primary key
# name :string(50) not null # active :boolean not null
# notes :text(55) not null # name :string(50) not null
# # notes :text(55) not null
EOS #
EOS
when_called_with hide_limit_column_types: 'integer,boolean', returns: when_called_with hide_limit_column_types: 'integer,boolean', returns:
<<-EOS.strip_heredoc <<-EOS.strip_heredoc
# Schema Info # Schema Info
# #
# Table name: users # Table name: users
# #
# id :integer not null, primary key # id :integer not null, primary key
# active :boolean not null # active :boolean not null
# name :string(50) not null # name :string(50) not null
# notes :text(55) not null # notes :text(55) not null
# #
EOS EOS
when_called_with hide_limit_column_types: 'integer,boolean,string,text', returns: when_called_with hide_limit_column_types: 'integer,boolean,string,text',
<<-EOS.strip_heredoc returns:
# Schema Info <<-EOS.strip_heredoc
# # Schema Info
# Table name: users #
# # Table name: users
# id :integer not null, primary key #
# active :boolean not null # id :integer not null, primary key
# name :string not null # active :boolean not null
# notes :text not null # name :string not null
# # notes :text not null
EOS #
end EOS
end
describe "#get_model_class" do describe 'hide_default_column_types option' do
require "tmpdir" mocked_columns_without_id = [
[:profile, :json, default: {}],
[:settings, :jsonb, default: {}],
[:parameters, :hstore, default: {}]
]
when_called_with hide_default_column_types: '',
with_columns: mocked_columns_without_id,
returns:
<<-EOS.strip_heredoc
# Schema Info
#
# Table name: users
#
# profile :json not null
# settings :jsonb not null
# parameters :hstore not null
#
EOS
module ::ActiveRecord when_called_with hide_default_column_types: 'skip',
class Base with_columns: mocked_columns_without_id,
def self.has_many name returns:
end <<-EOS.strip_heredoc
# Schema Info
#
# Table name: users
#
# profile :json default({}), not null
# settings :jsonb default({}), not null
# parameters :hstore default({}), not null
#
EOS
when_called_with hide_default_column_types: 'json',
with_columns: mocked_columns_without_id,
returns:
<<-EOS.strip_heredoc
# Schema Info
#
# Table name: users
#
# profile :json not null
# settings :jsonb default({}), not null
# parameters :hstore default({}), not null
#
EOS
end
describe 'classified_sort option' do
mocked_columns_without_id = [
[:active, :boolean, { limit: 1 }],
[:name, :string, { limit: 50 }],
[:notes, :text, { limit: 55 }]
]
when_called_with classified_sort: 'yes',
with_columns: mocked_columns_without_id, returns:
<<-EOS.strip_heredoc
# Schema Info
#
# Table name: users
#
# active :boolean not null
# name :string(50) not null
# notes :text(55) not null
#
EOS
end
describe 'with_comment option' do
mocked_columns_with_comment = [
[:id, :integer, { limit: 8, comment: 'ID' }],
[:active, :boolean, { limit: 1, comment: 'Active' }],
[:name, :string, { limit: 50, comment: 'Name' }],
[:notes, :text, { limit: 55, comment: 'Notes' }]
]
when_called_with with_comment: 'yes',
with_columns: mocked_columns_with_comment, returns:
<<-EOS.strip_heredoc
# Schema Info
#
# Table name: users
#
# id(ID) :integer not null, primary key
# active(Active) :boolean not null
# name(Name) :string(50) not null
# notes(Notes) :text(55) not null
#
EOS
it 'should get schema info as RDoc' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer, comment: 'ID'),
mock_column(:name, :string, limit: 50, comment: 'Name')
])
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_rdoc: true, with_comment: true)).to eql(<<-EOS.strip_heredoc)
# #{AnnotateModels::PREFIX}
#
# Table name: users
#
# *id(ID)*:: <tt>integer, not null, primary key</tt>
# *name(Name)*:: <tt>string(50), not null</tt>
#--
# #{AnnotateModels::END_MARK}
#++
EOS
end
it 'should get schema info as Markdown' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer, comment: 'ID'),
mock_column(:name, :string, limit: 50, comment: 'Name')
])
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, with_comment: true)).to eql(<<-EOS.strip_heredoc)
# #{AnnotateModels::PREFIX}
#
# Table name: `users`
#
# ### Columns
#
# Name | Type | Attributes
# ----------------- | ------------------ | ---------------------------
# **`id(ID)`** | `integer` | `not null, primary key`
# **`name(Name)`** | `string(50)` | `not null`
#
EOS
end end
end end
end
describe '#get_model_class' do
require 'tmpdir'
# todo: use 'files' gem instead # TODO: use 'files' gem instead
def create(file, body="hi") def create(file, body = 'hi')
file_path = File.join(AnnotateModels.model_dir[0], file) file_path = File.join(AnnotateModels.model_dir[0], file)
FileUtils.mkdir_p(File.dirname(file_path)) FileUtils.mkdir_p(File.dirname(file_path))
File.open(file_path, "wb") do |f| File.open(file_path, 'wb') do |f|
f.puts(body) f.puts(body)
end end
file_path file_path
...@@ -281,7 +708,7 @@ EOS ...@@ -281,7 +708,7 @@ EOS
AnnotateModels.model_dir = Dir.mktmpdir 'annotate_models' AnnotateModels.model_dir = Dir.mktmpdir 'annotate_models'
end end
it "should work" do it 'should work' do
create 'foo.rb', <<-EOS create 'foo.rb', <<-EOS
class Foo < ActiveRecord::Base class Foo < ActiveRecord::Base
end end
...@@ -289,7 +716,7 @@ EOS ...@@ -289,7 +716,7 @@ EOS
check_class_name 'foo.rb', 'Foo' check_class_name 'foo.rb', 'Foo'
end end
it "should find models with non standard capitalization" do it 'should find models with non standard capitalization' do
create 'foo_with_capitals.rb', <<-EOS create 'foo_with_capitals.rb', <<-EOS
class FooWithCAPITALS < ActiveRecord::Base class FooWithCAPITALS < ActiveRecord::Base
end end
...@@ -297,7 +724,7 @@ EOS ...@@ -297,7 +724,7 @@ EOS
check_class_name 'foo_with_capitals.rb', 'FooWithCAPITALS' check_class_name 'foo_with_capitals.rb', 'FooWithCAPITALS'
end end
it "should find models inside modules" do it 'should find models inside modules' do
create 'bar/foo_inside_bar.rb', <<-EOS create 'bar/foo_inside_bar.rb', <<-EOS
module Bar module Bar
class FooInsideBar < ActiveRecord::Base class FooInsideBar < ActiveRecord::Base
...@@ -307,7 +734,7 @@ EOS ...@@ -307,7 +734,7 @@ EOS
check_class_name 'bar/foo_inside_bar.rb', 'Bar::FooInsideBar' check_class_name 'bar/foo_inside_bar.rb', 'Bar::FooInsideBar'
end end
it "should find AR model when duplicated by a nested model" do it 'should find AR model when duplicated by a nested model' do
create 'foo.rb', <<-EOS create 'foo.rb', <<-EOS
class Foo < ActiveRecord::Base class Foo < ActiveRecord::Base
end end
...@@ -321,7 +748,7 @@ EOS ...@@ -321,7 +748,7 @@ EOS
check_class_name 'foo.rb', 'Foo' check_class_name 'foo.rb', 'Foo'
end end
it "should find AR model nested inside a class" do it 'should find AR model nested inside a class' do
create 'voucher.rb', <<-EOS create 'voucher.rb', <<-EOS
class Voucher < ActiveRecord::Base class Voucher < ActiveRecord::Base
end end
...@@ -338,8 +765,7 @@ EOS ...@@ -338,8 +765,7 @@ EOS
check_class_name 'voucher/foo.rb', 'Voucher::Foo' check_class_name 'voucher/foo.rb', 'Voucher::Foo'
end end
it 'should not care about unknown macros' do
it "should not care about unknown macros" do
create 'foo_with_macro.rb', <<-EOS create 'foo_with_macro.rb', <<-EOS
class FooWithMacro < ActiveRecord::Base class FooWithMacro < ActiveRecord::Base
acts_as_awesome :yah acts_as_awesome :yah
...@@ -348,7 +774,7 @@ EOS ...@@ -348,7 +774,7 @@ EOS
check_class_name 'foo_with_macro.rb', 'FooWithMacro' check_class_name 'foo_with_macro.rb', 'FooWithMacro'
end end
it "should not care about known macros" do it 'should not care about known macros' do
create('foo_with_known_macro.rb', <<-EOS) create('foo_with_known_macro.rb', <<-EOS)
class FooWithKnownMacro < ActiveRecord::Base class FooWithKnownMacro < ActiveRecord::Base
has_many :yah has_many :yah
...@@ -357,7 +783,7 @@ EOS ...@@ -357,7 +783,7 @@ EOS
check_class_name 'foo_with_known_macro.rb', 'FooWithKnownMacro' check_class_name 'foo_with_known_macro.rb', 'FooWithKnownMacro'
end end
it "should work with class names with ALL CAPS segments" do it 'should work with class names with ALL CAPS segments' do
create('foo_with_capitals.rb', <<-EOS) create('foo_with_capitals.rb', <<-EOS)
class FooWithCAPITALS < ActiveRecord::Base class FooWithCAPITALS < ActiveRecord::Base
acts_as_awesome :yah acts_as_awesome :yah
...@@ -366,7 +792,7 @@ EOS ...@@ -366,7 +792,7 @@ EOS
check_class_name 'foo_with_capitals.rb', 'FooWithCAPITALS' check_class_name 'foo_with_capitals.rb', 'FooWithCAPITALS'
end end
it "should not complain of invalid multibyte char (USASCII)" do it 'should not complain of invalid multibyte char (USASCII)' do
create 'foo_with_utf8.rb', <<-EOS create 'foo_with_utf8.rb', <<-EOS
#encoding: utf-8 #encoding: utf-8
class FooWithUtf8 < ActiveRecord::Base class FooWithUtf8 < ActiveRecord::Base
...@@ -376,7 +802,7 @@ EOS ...@@ -376,7 +802,7 @@ EOS
check_class_name 'foo_with_utf8.rb', 'FooWithUtf8' check_class_name 'foo_with_utf8.rb', 'FooWithUtf8'
end end
it "should find models inside modules with non standard capitalization" do it 'should find models inside modules with non standard capitalization' do
create 'bar/foo_inside_capitals_bar.rb', <<-EOS create 'bar/foo_inside_capitals_bar.rb', <<-EOS
module BAR module BAR
class FooInsideCapitalsBAR < ActiveRecord::Base class FooInsideCapitalsBAR < ActiveRecord::Base
...@@ -386,7 +812,7 @@ EOS ...@@ -386,7 +812,7 @@ EOS
check_class_name 'bar/foo_inside_capitals_bar.rb', 'BAR::FooInsideCapitalsBAR' check_class_name 'bar/foo_inside_capitals_bar.rb', 'BAR::FooInsideCapitalsBAR'
end end
it "should find non-namespaced models inside subdirectories" do it 'should find non-namespaced models inside subdirectories' do
create 'bar/non_namespaced_foo_inside_bar.rb', <<-EOS create 'bar/non_namespaced_foo_inside_bar.rb', <<-EOS
class NonNamespacedFooInsideBar < ActiveRecord::Base class NonNamespacedFooInsideBar < ActiveRecord::Base
end end
...@@ -394,7 +820,7 @@ EOS ...@@ -394,7 +820,7 @@ EOS
check_class_name 'bar/non_namespaced_foo_inside_bar.rb', 'NonNamespacedFooInsideBar' check_class_name 'bar/non_namespaced_foo_inside_bar.rb', 'NonNamespacedFooInsideBar'
end end
it "should find non-namespaced models with non standard capitalization inside subdirectories" do it 'should find non-namespaced models with non standard capitalization inside subdirectories' do
create 'bar/non_namespaced_foo_with_capitals_inside_bar.rb', <<-EOS create 'bar/non_namespaced_foo_with_capitals_inside_bar.rb', <<-EOS
class NonNamespacedFooWithCapitalsInsideBar < ActiveRecord::Base class NonNamespacedFooWithCapitalsInsideBar < ActiveRecord::Base
end end
...@@ -402,7 +828,7 @@ EOS ...@@ -402,7 +828,7 @@ EOS
check_class_name 'bar/non_namespaced_foo_with_capitals_inside_bar.rb', 'NonNamespacedFooWithCapitalsInsideBar' check_class_name 'bar/non_namespaced_foo_with_capitals_inside_bar.rb', 'NonNamespacedFooWithCapitalsInsideBar'
end end
it "should allow known macros" do it 'should allow known macros' do
create('foo_with_known_macro.rb', <<-EOS) create('foo_with_known_macro.rb', <<-EOS)
class FooWithKnownMacro < ActiveRecord::Base class FooWithKnownMacro < ActiveRecord::Base
has_many :yah has_many :yah
...@@ -410,10 +836,10 @@ EOS ...@@ -410,10 +836,10 @@ EOS
EOS EOS
expect(capturing(:stderr) do expect(capturing(:stderr) do
check_class_name 'foo_with_known_macro.rb', 'FooWithKnownMacro' check_class_name 'foo_with_known_macro.rb', 'FooWithKnownMacro'
end).to eq("") end).to eq('')
end end
it "should not require model files twice" do it 'should not require model files twice' do
create 'loaded_class.rb', <<-EOS create 'loaded_class.rb', <<-EOS
class LoadedClass < ActiveRecord::Base class LoadedClass < ActiveRecord::Base
CONSTANT = 1 CONSTANT = 1
...@@ -423,21 +849,22 @@ EOS ...@@ -423,21 +849,22 @@ EOS
Kernel.load "#{path}.rb" Kernel.load "#{path}.rb"
expect(Kernel).not_to receive(:require).with(path) expect(Kernel).not_to receive(:require).with(path)
expect(capturing(:stderr) { expect(capturing(:stderr) do
check_class_name 'loaded_class.rb', 'LoadedClass' check_class_name 'loaded_class.rb', 'LoadedClass'
}).not_to include("warning: already initialized constant LoadedClass::CONSTANT") end).not_to include('warning: already initialized constant LoadedClass::CONSTANT')
end end
end end
describe "#remove_annotation_of_file" do describe '#remove_annotation_of_file' do
require "tmpdir" require 'tmpdir'
def create(file, body="hi") def create(file, body = 'hi')
path = File.join(@dir, file) path = File.join(@dir, file)
File.open(path, "w") do |f| File.open(path, 'w') do |f|
f.puts(body) f.puts(body)
end end
return path
path
end end
def content(path) def content(path)
...@@ -448,8 +875,8 @@ EOS ...@@ -448,8 +875,8 @@ EOS
@dir = Dir.mktmpdir 'annotate_models' @dir = Dir.mktmpdir 'annotate_models'
end end
it "should remove before annotate" do it 'should remove before annotate' do
path = create "before.rb", <<-EOS path = create 'before.rb', <<-EOS
# == Schema Information # == Schema Information
# #
# Table name: foo # Table name: foo
...@@ -471,8 +898,8 @@ end ...@@ -471,8 +898,8 @@ end
EOS EOS
end end
it "should remove after annotate" do it 'should remove after annotate' do
path = create "after.rb", <<-EOS path = create 'after.rb', <<-EOS
class Foo < ActiveRecord::Base class Foo < ActiveRecord::Base
end end
...@@ -494,10 +921,58 @@ class Foo < ActiveRecord::Base ...@@ -494,10 +921,58 @@ class Foo < ActiveRecord::Base
end end
EOS EOS
end end
it 'should remove opening wrapper' do
path = create 'opening_wrapper.rb', <<-EOS
# wrapper
# == Schema Information
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
class Foo < ActiveRecord::Base
end
EOS
AnnotateModels.remove_annotation_of_file(path, wrapper_open: 'wrapper')
expect(content(path)).to eq <<-EOS
class Foo < ActiveRecord::Base
end
EOS
end
it 'should remove closing wrapper' do
path = create 'closing_wrapper.rb', <<-EOS
class Foo < ActiveRecord::Base
end
# == Schema Information
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
# wrapper
EOS
AnnotateModels.remove_annotation_of_file(path, wrapper_close: 'wrapper')
expect(content(path)).to eq <<-EOS
class Foo < ActiveRecord::Base
end
EOS
end
end end
describe '#resolve_filename' do describe '#resolve_filename' do
it 'should return the test path for a model' do it 'should return the test path for a model' do
filename_template = 'test/unit/%MODEL_NAME%_test.rb' filename_template = 'test/unit/%MODEL_NAME%_test.rb'
model_name = 'example_model' model_name = 'example_model'
...@@ -525,30 +1000,33 @@ end ...@@ -525,30 +1000,33 @@ end
expect(filename). to eq 'test/fixtures/parent/children.yml' expect(filename). to eq 'test/fixtures/parent/children.yml'
end end
end end
describe "annotating a file" do describe 'annotating a file' do
before do before do
@model_dir = Dir.mktmpdir('annotate_models') @model_dir = Dir.mktmpdir('annotate_models')
(@model_file_name, @file_content) = write_model "user.rb", <<-EOS (@model_file_name, @file_content) = write_model 'user.rb', <<-EOS
class User < ActiveRecord::Base class User < ActiveRecord::Base
end end
EOS EOS
@klass = mock_class(:users, :id, [ @klass = mock_class(:users,
mock_column(:id, :integer), :id,
mock_column(:name, :string, :limit => 50) [
]) mock_column(:id, :integer),
@schema_info = AnnotateModels.get_schema_info(@klass, "== Schema Info") mock_column(:name, :string, limit: 50)
])
@schema_info = AnnotateModels.get_schema_info(@klass, '== Schema Info')
Annotate.reset_options Annotate.reset_options
end end
def write_model file_name, file_content def write_model(file_name, file_content)
fname = File.join(@model_dir, file_name) fname = File.join(@model_dir, file_name)
FileUtils.mkdir_p(File.dirname(fname)) FileUtils.mkdir_p(File.dirname(fname))
File.open(fname, "wb") { |f| f.write file_content } File.open(fname, 'wb') { |f| f.write file_content }
return fname, file_content
[fname, file_content]
end end
def annotate_one_file options = {} def annotate_one_file(options = {})
Annotate.set_defaults(options) Annotate.set_defaults(options)
options = Annotate.setup_options(options) options = Annotate.setup_options(options)
AnnotateModels.annotate_one_file(@model_file_name, @schema_info, :position_in_class, options) AnnotateModels.annotate_one_file(@model_file_name, @schema_info, :position_in_class, options)
...@@ -572,135 +1050,112 @@ end ...@@ -572,135 +1050,112 @@ end
"# frozen_string_literal: true\n# encoding: utf-8", "# frozen_string_literal: true\n# encoding: utf-8",
'# frozen_string_literal: true', '# frozen_string_literal: true',
'#frozen_string_literal: false', '#frozen_string_literal: false',
'# -*- frozen_string_literal : true -*-', '# -*- frozen_string_literal : true -*-'
].each{|magic_comment| yield magic_comment } ].each { |magic_comment| yield magic_comment }
end
it "should put annotation before class if :position == 'before'" do
annotate_one_file :position => "before"
expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}")
end
it "should put annotation before class if :position => :before" do
annotate_one_file :position => :before
expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}")
end
it "should put annotation before class if :position == 'top'" do
annotate_one_file :position => "top"
expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}")
end
it "should put annotation before class if :position => :top" do
annotate_one_file :position => :top
expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}")
end
it "should put annotation after class if :position => 'after'" do
annotate_one_file :position => 'after'
expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}")
end
it "should put annotation after class if :position => :after" do
annotate_one_file :position => :after
expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}")
end end
it "should put annotation after class if :position => 'bottom'" do ['before', :before, 'top', :top].each do |position|
annotate_one_file :position => 'bottom' it "should put annotation before class if :position == #{position}" do
expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}") annotate_one_file position: position
expect(File.read(@model_file_name))
.to eq("#{@schema_info}\n#{@file_content}")
end
end end
it "should put annotation after class if :position => :bottom" do ['after', :after, 'bottom', :bottom].each do |position|
annotate_one_file :position => :bottom it "should put annotation after class if position: #{position}" do
expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}") annotate_one_file position: position
expect(File.read(@model_file_name))
.to eq("#{@file_content}\n#{@schema_info}")
end
end end
it 'should wrap annotation if wrapper is specified' do it 'should wrap annotation if wrapper is specified' do
annotate_one_file :wrapper_open => 'START', :wrapper_close => 'END' annotate_one_file wrapper_open: 'START', wrapper_close: 'END'
expect(File.read(@model_file_name)).to eq("# START\n#{@schema_info}# END\n\n#{@file_content}") expect(File.read(@model_file_name))
.to eq("# START\n#{@schema_info}# END\n\n#{@file_content}")
end end
describe "with existing annotation => :before" do describe 'with existing annotation => :before' do
before do before do
annotate_one_file :position => :before annotate_one_file position: :before
another_schema_info = AnnotateModels.get_schema_info(mock_class(:users, :id, [mock_column(:id, :integer),]), another_schema_info = AnnotateModels.get_schema_info(mock_class(:users, :id, [mock_column(:id, :integer)]), '== Schema Info')
"== Schema Info")
@schema_info = another_schema_info @schema_info = another_schema_info
end end
it "should retain current position" do it 'should retain current position' do
annotate_one_file annotate_one_file
expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}") expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}")
end end
it "should retain current position even when :position is changed to :after" do it 'should retain current position even when :position is changed to :after' do
annotate_one_file :position => :after annotate_one_file position: :after
expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}") expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}")
end end
it "should change position to :after when :force => true" do it 'should change position to :after when force: true' do
annotate_one_file :position => :after, :force => true annotate_one_file position: :after, force: true
expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}") expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}")
end end
end end
describe "with existing annotation => :after" do describe 'with existing annotation => :after' do
before do before do
annotate_one_file :position => :after annotate_one_file position: :after
another_schema_info = AnnotateModels.get_schema_info(mock_class(:users, :id, [mock_column(:id, :integer),]), another_schema_info = AnnotateModels.get_schema_info(mock_class(:users, :id, [mock_column(:id, :integer)]), '== Schema Info')
"== Schema Info")
@schema_info = another_schema_info @schema_info = another_schema_info
end end
it "should retain current position" do it 'should retain current position' do
annotate_one_file annotate_one_file
expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}") expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}")
end end
it "should retain current position even when :position is changed to :before" do it 'should retain current position even when :position is changed to :before' do
annotate_one_file :position => :before annotate_one_file position: :before
expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}") expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}")
end end
it "should change position to :before when :force => true" do it 'should change position to :before when force: true' do
annotate_one_file :position => :before, :force => true annotate_one_file position: :before, force: true
expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}") expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}")
end end
end end
it 'should skip columns with option[:ignore_columns] set' do it 'should skip columns with option[:ignore_columns] set' do
output = AnnotateModels.get_schema_info(@klass, "== Schema Info", output = AnnotateModels.get_schema_info(@klass, '== Schema Info',
:ignore_columns => '(id|updated_at|created_at)') :ignore_columns => '(id|updated_at|created_at)')
expect(output.match(/id/)).to be_nil expect(output.match(/id/)).to be_nil
end end
it "works with namespaced models (i.e. models inside modules/subdirectories)" do it 'works with namespaced models (i.e. models inside modules/subdirectories)' do
(model_file_name, file_content) = write_model "foo/user.rb", <<-EOS (model_file_name, file_content) = write_model 'foo/user.rb', <<-EOS
class Foo::User < ActiveRecord::Base class Foo::User < ActiveRecord::Base
end end
EOS EOS
klass = mock_class(:'foo_users', :id, [ klass = mock_class(:'foo_users',
mock_column(:id, :integer), :id,
mock_column(:name, :string, :limit => 50) [
]) mock_column(:id, :integer),
schema_info = AnnotateModels.get_schema_info(klass, "== Schema Info") mock_column(:name, :string, limit: 50)
AnnotateModels.annotate_one_file(model_file_name, schema_info, :position => :before) ])
schema_info = AnnotateModels.get_schema_info(klass, '== Schema Info')
AnnotateModels.annotate_one_file(model_file_name, schema_info, position: :before)
expect(File.read(model_file_name)).to eq("#{schema_info}\n#{file_content}") expect(File.read(model_file_name)).to eq("#{schema_info}\n#{file_content}")
end end
it "should not touch magic comments" do it 'should not touch magic comments' do
magic_comments_list_each do |magic_comment| magic_comments_list_each do |magic_comment|
write_model "user.rb", <<-EOS write_model 'user.rb', <<-EOS
#{magic_comment} #{magic_comment}
class User < ActiveRecord::Base class User < ActiveRecord::Base
end end
EOS EOS
annotate_one_file :position => :before annotate_one_file position: :before
lines= magic_comment.split("\n") lines = magic_comment.split("\n")
File.open @model_file_name do |file| File.open @model_file_name do |file|
lines.count.times do |index| lines.count.times do |index|
expect(file.readline).to eq "#{lines[index]}\n" expect(file.readline).to eq "#{lines[index]}\n"
...@@ -710,69 +1165,69 @@ end ...@@ -710,69 +1165,69 @@ end
end end
describe "if a file can't be annotated" do describe "if a file can't be annotated" do
before do before do
allow(AnnotateModels).to receive(:get_loaded_model).with('user').and_return(nil) allow(AnnotateModels).to receive(:get_loaded_model).with('user').and_return(nil)
write_model('user.rb', <<-EOS) write_model('user.rb', <<-EOS)
class User < ActiveRecord::Base class User < ActiveRecord::Base
raise "oops" raise "oops"
end end
EOS EOS
end end
it "displays an error message" do it 'displays an error message' do
expect(capturing(:stdout) { expect(capturing(:stdout) do
AnnotateModels.do_annotations :model_dir => @model_dir, :is_rake => true AnnotateModels.do_annotations model_dir: @model_dir, is_rake: true
}).to include("Unable to annotate #{@model_dir}/user.rb: oops") end).to include("Unable to annotate #{@model_dir}/user.rb: oops")
end end
it "displays the full stack trace with --trace" do it 'displays the full stack trace with --trace' do
expect(capturing(:stdout) { expect(capturing(:stdout) do
AnnotateModels.do_annotations :model_dir => @model_dir, :trace => true, :is_rake => true AnnotateModels.do_annotations model_dir: @model_dir, trace: true, is_rake: true
}).to include("/spec/annotate/annotate_models_spec.rb:") end).to include('/spec/annotate/annotate_models_spec.rb:')
end end
it "omits the full stack trace without --trace" do it 'omits the full stack trace without --trace' do
expect(capturing(:stdout) { expect(capturing(:stdout) do
AnnotateModels.do_annotations :model_dir => @model_dir, :trace => false, :is_rake => true AnnotateModels.do_annotations model_dir: @model_dir, trace: false, is_rake: true
}).not_to include("/spec/annotate/annotate_models_spec.rb:") end).not_to include('/spec/annotate/annotate_models_spec.rb:')
end end
end end
describe "if a file can't be deannotated" do describe "if a file can't be deannotated" do
before do before do
allow(AnnotateModels).to receive(:get_loaded_model).with('user').and_return(nil) allow(AnnotateModels).to receive(:get_loaded_model).with('user').and_return(nil)
write_model('user.rb', <<-EOS) write_model('user.rb', <<-EOS)
class User < ActiveRecord::Base class User < ActiveRecord::Base
raise "oops" raise "oops"
end end
EOS EOS
end end
it "displays an error message" do it 'displays an error message' do
expect(capturing(:stdout) { expect(capturing(:stdout) do
AnnotateModels.remove_annotations :model_dir => @model_dir, :is_rake => true AnnotateModels.remove_annotations model_dir: @model_dir, is_rake: true
}).to include("Unable to deannotate #{@model_dir}/user.rb: oops") end).to include("Unable to deannotate #{@model_dir}/user.rb: oops")
end end
it "displays the full stack trace" do it 'displays the full stack trace' do
expect(capturing(:stdout) { expect(capturing(:stdout) do
AnnotateModels.remove_annotations :model_dir => @model_dir, :trace => true, :is_rake => true AnnotateModels.remove_annotations model_dir: @model_dir, trace: true, is_rake: true
}).to include("/user.rb:2:in `<class:User>'") end).to include("/user.rb:2:in `<class:User>'")
end end
it "omits the full stack trace without --trace" do it 'omits the full stack trace without --trace' do
expect(capturing(:stdout) { expect(capturing(:stdout) do
AnnotateModels.remove_annotations :model_dir => @model_dir, :trace => false, :is_rake => true AnnotateModels.remove_annotations model_dir: @model_dir, trace: false, is_rake: true
}).not_to include("/user.rb:2:in `<class:User>'") end).not_to include("/user.rb:2:in `<class:User>'")
end end
end end
end end
describe '.annotate_model_file' do describe '.annotate_model_file' do
before do before do
class Foo < ActiveRecord::Base; end; class Foo < ActiveRecord::Base; end
allow(AnnotateModels).to receive(:get_model_class).with('foo.rb') { Foo } allow(AnnotateModels).to receive(:get_model_class).with('foo.rb') { Foo }
allow(Foo).to receive(:table_exists?) { false } allow(Foo).to receive(:table_exists?) { false }
end end
...@@ -780,7 +1235,7 @@ end ...@@ -780,7 +1235,7 @@ end
after { Object.send :remove_const, 'Foo' } after { Object.send :remove_const, 'Foo' }
it 'skips attempt to annotate if no table exists for model' do it 'skips attempt to annotate if no table exists for model' do
annotate_model_file = AnnotateModels.annotate_model_file([], 'foo.rb', nil, nil) annotate_model_file = AnnotateModels.annotate_model_file([], 'foo.rb', nil, {})
expect(annotate_model_file).to eq nil expect(annotate_model_file).to eq nil
end end
......
...@@ -2,118 +2,168 @@ require File.dirname(__FILE__) + '/../spec_helper.rb' ...@@ -2,118 +2,168 @@ require File.dirname(__FILE__) + '/../spec_helper.rb'
require 'annotate/annotate_routes' require 'annotate/annotate_routes'
describe AnnotateRoutes do describe AnnotateRoutes do
ROUTE_FILE = "config/routes.rb" ROUTE_FILE = 'config/routes.rb'.freeze
ANNOTATION_ADDED = "#{ROUTE_FILE} annotated." ANNOTATION_ADDED = "#{ROUTE_FILE} annotated.".freeze
ANNOTATION_REMOVED = "Removed annotations from #{ROUTE_FILE}." ANNOTATION_REMOVED = "Removed annotations from #{ROUTE_FILE}.".freeze
FILE_UNCHANGED = "#{ROUTE_FILE} unchanged." FILE_UNCHANGED = "#{ROUTE_FILE} unchanged.".freeze
def mock_file(stubs={}) def mock_file(stubs = {})
@mock_file ||= double(File, stubs) @mock_file ||= double(File, stubs)
end end
it "should check if routes.rb exists" do it 'should check if routes.rb exists' do
expect(File).to receive(:exists?).with(ROUTE_FILE).and_return(false) expect(File).to receive(:exists?).with(ROUTE_FILE).and_return(false)
expect(AnnotateRoutes).to receive(:puts).with("Can`t find routes.rb") expect(AnnotateRoutes).to receive(:puts).with("Can't find routes.rb")
AnnotateRoutes.do_annotations AnnotateRoutes.do_annotations
end end
describe "When adding" do describe 'Annotate#example' do
before(:each) do before(:each) do
expect(File).to receive(:exists?).with(ROUTE_FILE).and_return(true) expect(File).to receive(:exists?).with(ROUTE_FILE).and_return(true)
expect(AnnotateRoutes).to receive(:`).with("rake routes").and_return("")
expect(File).to receive(:read).with(ROUTE_FILE).and_return("")
expect(AnnotateRoutes).to receive(:`).with('rake routes').and_return(' Prefix Verb URI Pattern Controller#Action
myaction1 GET /url1(.:format) mycontroller1#action
myaction2 POST /url2(.:format) mycontroller2#action
myaction3 DELETE|GET /url3(.:format) mycontroller3#action')
expect(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED)
end
it 'annotate normal' do
expect(File).to receive(:open).with(ROUTE_FILE, 'wb').and_yield(mock_file)
expect(@mock_file).to receive(:puts).with("
# == Route Map
#
# Prefix Verb URI Pattern Controller#Action
# myaction1 GET /url1(.:format) mycontroller1#action
# myaction2 POST /url2(.:format) mycontroller2#action
# myaction3 DELETE|GET /url3(.:format) mycontroller3#action\n")
AnnotateRoutes.do_annotations
end
it 'annotate markdown' do
expect(File).to receive(:open).with(ROUTE_FILE, 'wb').and_yield(mock_file)
expect(@mock_file).to receive(:puts).with("
# ## Route Map
#
# Prefix | Verb | URI Pattern | Controller#Action
# --------- | ---------- | --------------- | --------------------
# myaction1 | GET | /url1(.:format) | mycontroller1#action
# myaction2 | POST | /url2(.:format) | mycontroller2#action
# myaction3 | DELETE-GET | /url3(.:format) | mycontroller3#action\n")
AnnotateRoutes.do_annotations(format_markdown: true)
end end
end
it "should insert annotations if file does not contain annotations" do describe 'When adding' do
before(:each) do
expect(File).to receive(:exists?).with(ROUTE_FILE).and_return(true)
expect(AnnotateRoutes).to receive(:`).with('rake routes').and_return('')
end
it 'should insert annotations if file does not contain annotations' do
expect(File).to receive(:read).with(ROUTE_FILE).and_return("") expect(File).to receive(:read).with(ROUTE_FILE).and_return("")
expect(File).to receive(:open).with(ROUTE_FILE, "wb").and_yield(mock_file) expect(File).to receive(:open).with(ROUTE_FILE, 'wb').and_yield(mock_file)
expect(@mock_file).to receive(:puts).with("\n# == Route Map\n#\n") expect(@mock_file).to receive(:puts).with("\n# == Route Map\n#\n")
expect(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED) expect(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED)
AnnotateRoutes.do_annotations AnnotateRoutes.do_annotations
end end
it "should skip annotations if file does already contain annotation" do it 'should insert annotations if file does not contain annotations and ignore routes' do
expect(File).to receive(:read).with(ROUTE_FILE).and_return("")
expect(File).to receive(:open).with(ROUTE_FILE, 'wb').and_yield(mock_file)
expect(@mock_file).to receive(:puts).with("\n# == Route Map\n#\n")
expect(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED)
AnnotateRoutes.do_annotations(ignore_routes: 'my_route')
end
it 'should insert annotations if file does not contain annotations and position top' do
expect(File).to receive(:read).with(ROUTE_FILE).and_return("")
expect(File).to receive(:open).with(ROUTE_FILE, 'wb').and_yield(mock_file)
expect(@mock_file).to receive(:puts).with("# == Route Map\n#\n")
expect(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED)
AnnotateRoutes.do_annotations(position_in_routes: 'top')
end
it 'should skip annotations if file does already contain annotation' do
expect(File).to receive(:read).with(ROUTE_FILE).and_return("\n# == Route Map\n#\n") expect(File).to receive(:read).with(ROUTE_FILE).and_return("\n# == Route Map\n#\n")
expect(AnnotateRoutes).to receive(:puts).with(FILE_UNCHANGED) expect(AnnotateRoutes).to receive(:puts).with(FILE_UNCHANGED)
AnnotateRoutes.do_annotations AnnotateRoutes.do_annotations
end end
end end
describe "When adding with older Rake versions" do describe 'When adding with older Rake versions' do
before(:each) do before(:each) do
expect(File).to receive(:exists?).with(ROUTE_FILE).and_return(true) expect(File).to receive(:exists?).with(ROUTE_FILE).and_return(true)
expect(AnnotateRoutes).to receive(:`).with("rake routes").and_return("(in /bad/line)\ngood line") expect(AnnotateRoutes).to receive(:`).with('rake routes').and_return("(in /bad/line)\ngood line")
expect(File).to receive(:open).with(ROUTE_FILE, "wb").and_yield(mock_file) expect(File).to receive(:open).with(ROUTE_FILE, 'wb').and_yield(mock_file)
expect(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED) expect(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED)
end end
it "should annotate and add a newline!" do it 'should annotate and add a newline!' do
expect(File).to receive(:read).with(ROUTE_FILE).and_return("ActionController::Routing...\nfoo") expect(File).to receive(:read).with(ROUTE_FILE).and_return("ActionController::Routing...\nfoo")
expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map\n#\n# good line\n/) expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map\n#\n# good line\n/)
AnnotateRoutes.do_annotations AnnotateRoutes.do_annotations
end end
it "should not add a newline if there are empty lines" do it 'should not add a newline if there are empty lines' do
expect(File).to receive(:read).with(ROUTE_FILE).and_return("ActionController::Routing...\nfoo\n") expect(File).to receive(:read).with(ROUTE_FILE).and_return("ActionController::Routing...\nfoo\n")
expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map\n#\n# good line\n/) expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map\n#\n# good line\n/)
AnnotateRoutes.do_annotations AnnotateRoutes.do_annotations
end end
end end
describe "When adding with newer Rake versions" do describe 'When adding with newer Rake versions' do
before(:each) do before(:each) do
expect(File).to receive(:exists?).with(ROUTE_FILE).and_return(true) expect(File).to receive(:exists?).with(ROUTE_FILE).and_return(true)
expect(AnnotateRoutes).to receive(:`).with("rake routes").and_return("another good line\ngood line") expect(AnnotateRoutes).to receive(:`).with('rake routes').and_return("another good line\ngood line")
expect(File).to receive(:open).with(ROUTE_FILE, "wb").and_yield(mock_file) expect(File).to receive(:open).with(ROUTE_FILE, 'wb').and_yield(mock_file)
expect(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED) expect(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED)
end end
it 'should annotate and add a newline!' do
it "should annotate and add a newline!" do
expect(File).to receive(:read).with(ROUTE_FILE).and_return("ActionController::Routing...\nfoo") expect(File).to receive(:read).with(ROUTE_FILE).and_return("ActionController::Routing...\nfoo")
expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map\n#\n# another good line\n# good line\n/) expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map\n#\n# another good line\n# good line\n/)
AnnotateRoutes.do_annotations AnnotateRoutes.do_annotations
end end
it "should not add a newline if there are empty lines" do it 'should not add a newline if there are empty lines' do
expect(File).to receive(:read).with(ROUTE_FILE).and_return("ActionController::Routing...\nfoo\n") expect(File).to receive(:read).with(ROUTE_FILE).and_return("ActionController::Routing...\nfoo\n")
expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map\n#\n# another good line\n# good line\n/) expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map\n#\n# another good line\n# good line\n/)
AnnotateRoutes.do_annotations AnnotateRoutes.do_annotations
end end
it "should add a timestamp when :timestamp is passed" do it 'should add a timestamp when :timestamp is passed' do
expect(File).to receive(:read).with(ROUTE_FILE).and_return("ActionController::Routing...\nfoo") expect(File).to receive(:read).with(ROUTE_FILE).and_return("ActionController::Routing...\nfoo")
expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map \(Updated \d{4}-\d{2}-\d{2} \d{2}:\d{2}\)\n#\n# another good line\n# good line\n/) expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map \(Updated \d{4}-\d{2}-\d{2} \d{2}:\d{2}\)\n#\n# another good line\n# good line\n/)
AnnotateRoutes.do_annotations :timestamp => true AnnotateRoutes.do_annotations timestamp: true
end end
end end
describe "When removing" do describe 'When removing' do
before(:each) do before(:each) do
expect(File).to receive(:exists?).with(ROUTE_FILE).and_return(true) expect(File).to receive(:exists?).with(ROUTE_FILE).and_return(true)
expect(File).to receive(:open).with(ROUTE_FILE, "wb").and_yield(mock_file) expect(File).to receive(:open).with(ROUTE_FILE, 'wb').and_yield(mock_file)
expect(AnnotateRoutes).to receive(:puts).with(ANNOTATION_REMOVED) expect(AnnotateRoutes).to receive(:puts).with(ANNOTATION_REMOVED)
end end
it "should remove trailing annotation and trim trailing newlines, but leave leading newlines alone" do it 'should remove trailing annotation and trim trailing newlines, but leave leading newlines alone' do
expect(File).to receive(:read).with(ROUTE_FILE).and_return("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nActionController::Routing...\nfoo\n\n\n\n\n\n\n\n\n\n\n# == Route Map\n#\n# another good line\n# good line\n") expect(File).to receive(:read).with(ROUTE_FILE).and_return("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nActionController::Routing...\nfoo\n\n\n\n\n\n\n\n\n\n\n# == Route Map\n#\n# another good line\n# good line\n")
expect(@mock_file).to receive(:puts).with(/\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nActionController::Routing...\nfoo\n/) expect(@mock_file).to receive(:puts).with(/\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nActionController::Routing...\nfoo\n/)
AnnotateRoutes.remove_annotations AnnotateRoutes.remove_annotations
end end
it "should remove prepended annotation and trim leading newlines, but leave trailing newlines alone" do it 'should remove prepended annotation and trim leading newlines, but leave trailing newlines alone' do
expect(File).to receive(:read).with(ROUTE_FILE).and_return("# == Route Map\n#\n# another good line\n# good line\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nActionController::Routing...\nfoo\n\n\n\n\n\n\n\n\n\n\n") expect(File).to receive(:read).with(ROUTE_FILE).and_return("# == Route Map\n#\n# another good line\n# good line\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nActionController::Routing...\nfoo\n\n\n\n\n\n\n\n\n\n\n")
expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n\n\n\n\n\n\n\n\n\n/) expect(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n\n\n\n\n\n\n\n\n\n/)
AnnotateRoutes.remove_annotations AnnotateRoutes.remove_annotations
end end
end end
end end
require File.dirname(__FILE__) + '/spec_helper.rb' require File.dirname(__FILE__) + '/spec_helper.rb'
describe Annotate do describe Annotate do
it 'should have a version' do
it "should have a version" do
expect(Annotate.version).to be_instance_of(String) expect(Annotate.version).to be_instance_of(String)
end end
end end
...@@ -12,11 +12,9 @@ ...@@ -12,11 +12,9 @@
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20120816164927) do ActiveRecord::Schema.define(:version => 20120816164927) do
create_table "tasks", :force => true do |t| create_table "tasks", :force => true do |t|
t.string "content" t.string "content"
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
end end
end end
...@@ -2,10 +2,7 @@ module Annotate ...@@ -2,10 +2,7 @@ module Annotate
module Validations module Validations
module Common module Common
def self.test_commands def self.test_commands
return %q{ 'bin/annotate && bin/annotate --routes'
bin/annotate &&
bin/annotate --routes
}
end end
def self.verify_output(output) def self.verify_output(output)
...@@ -13,54 +10,52 @@ module Annotate ...@@ -13,54 +10,52 @@ module Annotate
output.should =~ /Route file annotated./ output.should =~ /Route file annotated./
end end
def self.verify_files(which_files, test_rig, schema_annotation, routes_annotation, place_before=true) def self.verify_files(which_files, test_rig, schema_annotation, routes_annotation, place_before = true)
check_task_model(test_rig, schema_annotation, place_before) if(which_files[:model]) check_task_model(test_rig, schema_annotation, place_before) if which_files[:model]
check_task_unittest(test_rig, schema_annotation, place_before) if(which_files[:test]) check_task_unittest(test_rig, schema_annotation, place_before) if which_files[:test]
check_task_fixture(test_rig, schema_annotation, place_before) if(which_files[:fixture]) check_task_fixture(test_rig, schema_annotation, place_before) if which_files[:fixture]
check_task_factory(test_rig, schema_annotation, place_before) if(which_files[:factory]) check_task_factory(test_rig, schema_annotation, place_before) if which_files[:factory]
check_routes(test_rig, routes_annotation, place_before) if(which_files[:routes]) check_routes(test_rig, routes_annotation, place_before) if which_files[:routes]
end end
def self.check_task_model(test_rig, annotation, place_before=true) def self.check_task_model(test_rig, annotation, place_before = true)
model = apply_annotation(test_rig, "app/models/task.rb", annotation, place_before) model = apply_annotation(test_rig, 'app/models/task.rb', annotation, place_before)
File.read("app/models/task.rb").should == model File.read('app/models/task.rb').should == model
end end
def self.check_routes(test_rig, annotation, place_before=true) def self.check_routes(test_rig, annotation, place_before = true)
routes = apply_annotation(test_rig, "config/routes.rb", annotation, place_before) routes = apply_annotation(test_rig, 'config/routes.rb', annotation, place_before)
File.read("config/routes.rb"). File.read('config/routes.rb')
sub(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}/, 'YYYY-MM-DD HH:MM'). .sub(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}/, 'YYYY-MM-DD HH:MM')
should == routes .should == routes
end end
def self.check_task_unittest(test_rig, annotation, place_before=true) def self.check_task_unittest(test_rig, annotation, place_before = true)
unittest = apply_annotation(test_rig, "test/unit/task_test.rb", annotation, place_before) unittest = apply_annotation(test_rig, 'test/unit/task_test.rb', annotation, place_before)
File.read("test/unit/task_test.rb").should == unittest File.read('test/unit/task_test.rb').should == unittest
end end
def self.check_task_modeltest(test_rig, annotation, place_before=true) def self.check_task_modeltest(test_rig, annotation, place_before=true)
unittest = apply_annotation(test_rig, "test/models/task_test.rb", annotation, place_before) unittest = apply_annotation(test_rig, 'test/models/task_test.rb', annotation, place_before)
File.read("test/models/task_test.rb").should == unittest File.read('test/models/task_test.rb').should == unittest
end end
def self.check_task_factory(test_rig, annotation, place_before=true) def self.check_task_factory(test_rig, annotation, place_before=true)
fixture = apply_annotation(test_rig, "test/factories/tasks.rb", annotation, place_before) fixture = apply_annotation(test_rig, 'test/factories/tasks.rb', annotation, place_before)
File.read("test/factories/tasks.rb").should == fixture File.read('test/factories/tasks.rb').should == fixture
end end
def self.check_task_fixture(test_rig, annotation, place_before=true) def self.check_task_fixture(test_rig, annotation, place_before = true)
fixture = apply_annotation(test_rig, "test/fixtures/tasks.yml", annotation, place_before) fixture = apply_annotation(test_rig, 'test/fixtures/tasks.yml', annotation, place_before)
File.read("test/fixtures/tasks.yml").should == fixture File.read('test/fixtures/tasks.yml').should == fixture
end end
protected def self.apply_annotation(test_rig, fname, annotation, place_before = true)
def self.apply_annotation(test_rig, fname, annotation, place_before=true)
corpus = File.read(File.join(test_rig, fname)) corpus = File.read(File.join(test_rig, fname))
if(place_before) if place_before
corpus = annotation + "\n" + corpus annotation + "\n" + corpus
else else
corpus = corpus + "\n" + annotation corpus + "\n" + annotation
end end
end end
end end
...@@ -71,27 +66,28 @@ module Annotate ...@@ -71,27 +66,28 @@ module Annotate
module Validations module Validations
class Base class Base
def self.test_commands def self.test_commands
return Annotate::Validations::Common.test_commands Annotate::Validations::Common.test_commands
end end
def self.verify_output(output) def self.verify_output(output)
return Annotate::Validations::Common.verify_output(output) Annotate::Validations::Common.verify_output(output)
end end
def self.verify_files(test_rig) def self.verify_files(test_rig)
return Annotate::Validations::Common.verify_files({ Annotate::Validations::Common.verify_files(
:model => true, {
:test => true, model: true,
:fixture => true, test: true,
:factory => false, fixture: true,
:routes => true factory: false,
}, test_rig, self.schema_annotation, self.route_annotation, true) routes: true
}, test_rig, schema_annotation, route_annotation, true
)
end end
def self.foo def self.foo
require 'pry' require 'pry'
require 'pry-coolline' require 'pry-coolline'
binding.pry
end end
end end
end end
......
# Smoke test to assure basic functionality works on a variety of Rails versions. # Smoke test to assure basic functionality works on a variety of Rails versions.
$:.unshift(File.dirname(__FILE__)) $LOAD_PATH.unshift(File.dirname(__FILE__))
require 'spec_helper' require 'spec_helper'
require 'files' require 'files'
require 'wrong' require 'wrong'
...@@ -7,7 +7,7 @@ require 'rake' ...@@ -7,7 +7,7 @@ require 'rake'
include Files include Files
include Wrong::D include Wrong::D
BASEDIR=File.expand_path(File.join(File.dirname(__FILE__), '..', '..')) BASEDIR = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
RVM_BIN = `which rvm`.chomp RVM_BIN = `which rvm`.chomp
USING_RVM = (RVM_BIN != '') USING_RVM = (RVM_BIN != '')
...@@ -16,25 +16,24 @@ ENV['rvm_pretty_print_flag'] = '0' ...@@ -16,25 +16,24 @@ ENV['rvm_pretty_print_flag'] = '0'
ENV['BUNDLE_GEMFILE'] = './Gemfile' ENV['BUNDLE_GEMFILE'] = './Gemfile'
describe "annotate inside Rails, using #{CURRENT_RUBY}" do describe "annotate inside Rails, using #{CURRENT_RUBY}" do
here = File.expand_path('..', __FILE__)
chosen_scenario = nil chosen_scenario = nil
if(!ENV['SCENARIO'].blank?) unless ENV['SCENARIO'].blank?
chosen_scenario = File.expand_path(ENV['SCENARIO']) chosen_scenario = File.expand_path(ENV['SCENARIO'])
raise "Can't find specified scenario '#{chosen_scenario}'!" unless(File.directory?(chosen_scenario)) raise "Can't find specified scenario '#{chosen_scenario}'!" unless File.directory?(chosen_scenario)
end end
Annotate::Integration::SCENARIOS.each do |test_rig, base_dir, test_name| Annotate::Integration::SCENARIOS.each do |test_rig, base_dir, test_name|
next if(chosen_scenario && chosen_scenario != test_rig) next if chosen_scenario && chosen_scenario != test_rig
it "works under #{test_name}" do it "works under #{test_name}" do
if(!USING_RVM) unless USING_RVM
skip "Must have RVM installed." skip 'Must have RVM installed.'
next next
end end
# Don't proceed if the working copy is dirty! # Don't proceed if the working copy is dirty!
expect(Annotate::Integration.is_clean?(test_rig)).to eq(true) expect(Annotate::Integration.clean?(test_rig)).to eq(true)
skip "temporarily ignored until Travis can run them" skip 'temporarily ignored until Travis can run them'
Bundler.with_clean_env do Bundler.with_clean_env do
dir base_dir do dir base_dir do
...@@ -53,10 +52,10 @@ describe "annotate inside Rails, using #{CURRENT_RUBY}" do ...@@ -53,10 +52,10 @@ describe "annotate inside Rails, using #{CURRENT_RUBY}" do
# By default, rvm_ruby_string isn't inherited over properly, so let's # By default, rvm_ruby_string isn't inherited over properly, so let's
# make sure it's there so our .rvmrc will work. # make sure it's there so our .rvmrc will work.
ENV['rvm_ruby_string']=CURRENT_RUBY ENV['rvm_ruby_string'] = CURRENT_RUBY
require "#{base_dir}" # Will get "#{base_dir}.rb"... require base_dir.to_s # Will get "#{base_dir}.rb"...
klass = "Annotate::Validations::#{base_dir.gsub('.', '_').classify}".constantize klass = "Annotate::Validations::#{base_dir.tr('.', '_').classify}".constantize
Dir.chdir(temp_dir) do Dir.chdir(temp_dir) do
# bash is required by rvm # bash is required by rvm
......
...@@ -4,7 +4,7 @@ module Annotate ...@@ -4,7 +4,7 @@ module Annotate
module Validations module Validations
class Rails23WithBundler < Base class Rails23WithBundler < Base
def self.schema_annotation def self.schema_annotation
return <<-RUBY <<-RUBY
# == Schema Information # == Schema Information
# #
# Table name: tasks # Table name: tasks
...@@ -18,7 +18,7 @@ RUBY ...@@ -18,7 +18,7 @@ RUBY
end end
def self.route_annotation def self.route_annotation
return <<-RUBY <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM) # == Route Map (Updated YYYY-MM-DD HH:MM)
# #
# tasks GET /tasks(.:format) {:controller=>"tasks", :action=>"index"} # tasks GET /tasks(.:format) {:controller=>"tasks", :action=>"index"}
......
...@@ -4,7 +4,7 @@ module Annotate ...@@ -4,7 +4,7 @@ module Annotate
module Validations module Validations
class Rails322 < Base class Rails322 < Base
def self.schema_annotation def self.schema_annotation
return <<-RUBY <<-RUBY
# == Schema Information # == Schema Information
# #
# Table name: tasks # Table name: tasks
...@@ -18,7 +18,7 @@ RUBY ...@@ -18,7 +18,7 @@ RUBY
end end
def self.route_annotation def self.route_annotation
return <<-RUBY <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM) # == Route Map (Updated YYYY-MM-DD HH:MM)
# #
# tasks GET /tasks(.:format) tasks#index # tasks GET /tasks(.:format) tasks#index
......
...@@ -4,7 +4,7 @@ module Annotate ...@@ -4,7 +4,7 @@ module Annotate
module Validations module Validations
class Rails328 < Base class Rails328 < Base
def self.schema_annotation def self.schema_annotation
return <<-RUBY <<-RUBY
# == Schema Information # == Schema Information
# #
# Table name: tasks # Table name: tasks
...@@ -18,7 +18,7 @@ RUBY ...@@ -18,7 +18,7 @@ RUBY
end end
def self.route_annotation def self.route_annotation
return <<-RUBY <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM) # == Route Map (Updated YYYY-MM-DD HH:MM)
# #
# tasks GET /tasks(.:format) tasks#index # tasks GET /tasks(.:format) tasks#index
......
...@@ -4,7 +4,7 @@ module Annotate ...@@ -4,7 +4,7 @@ module Annotate
module Validations module Validations
class Rails32AutoloadingFactoryGirl < Base class Rails32AutoloadingFactoryGirl < Base
def self.schema_annotation def self.schema_annotation
return <<-RUBY <<-RUBY
# == Schema Information # == Schema Information
# #
# Table name: tasks # Table name: tasks
...@@ -18,7 +18,7 @@ RUBY ...@@ -18,7 +18,7 @@ RUBY
end end
def self.route_annotation def self.route_annotation
return <<-RUBY <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM) # == Route Map (Updated YYYY-MM-DD HH:MM)
# #
# tasks GET /tasks(.:format) tasks#index # tasks GET /tasks(.:format) tasks#index
...@@ -33,13 +33,15 @@ RUBY ...@@ -33,13 +33,15 @@ RUBY
end end
def self.verify_files(test_rig) def self.verify_files(test_rig)
return Annotate::Validations::Common.verify_files({ Annotate::Validations::Common.verify_files(
:model => true, {
:test => true, model: true,
:fixture => false, test: true,
:factory => true, fixture: false,
:routes => true factory: true,
}, test_rig, self.schema_annotation, self.route_annotation, true) routes: true
}, test_rig, schema_annotation, route_annotation, true
)
end end
end end
end end
......
...@@ -4,7 +4,7 @@ module Annotate ...@@ -4,7 +4,7 @@ module Annotate
module Validations module Validations
class Rails32CustomInflection < Base class Rails32CustomInflection < Base
def self.schema_annotation def self.schema_annotation
return <<-RUBY <<-RUBY
# == Schema Information # == Schema Information
# #
# Table name: tasks # Table name: tasks
...@@ -18,7 +18,7 @@ RUBY ...@@ -18,7 +18,7 @@ RUBY
end end
def self.route_annotation def self.route_annotation
return <<-RUBY <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM) # == Route Map (Updated YYYY-MM-DD HH:MM)
# #
# tasks GET /tasks(.:format) tasks#index # tasks GET /tasks(.:format) tasks#index
......
...@@ -4,7 +4,7 @@ module Annotate ...@@ -4,7 +4,7 @@ module Annotate
module Validations module Validations
class Rails32WithAssetPipeline < Base class Rails32WithAssetPipeline < Base
def self.schema_annotation def self.schema_annotation
return <<-RUBY <<-RUBY
# == Schema Information # == Schema Information
# #
# Table name: tasks # Table name: tasks
...@@ -18,7 +18,7 @@ RUBY ...@@ -18,7 +18,7 @@ RUBY
end end
def self.route_annotation def self.route_annotation
return <<-RUBY <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM) # == Route Map (Updated YYYY-MM-DD HH:MM)
# #
# tasks GET /tasks(.:format) tasks#index # tasks GET /tasks(.:format) tasks#index
......
...@@ -4,7 +4,7 @@ module Annotate ...@@ -4,7 +4,7 @@ module Annotate
module Validations module Validations
class Standalone < Base class Standalone < Base
def self.schema_annotation def self.schema_annotation
return <<-RUBY <<-RUBY
# == Schema Information # == Schema Information
# #
# Table name: tasks # Table name: tasks
...@@ -18,9 +18,7 @@ RUBY ...@@ -18,9 +18,7 @@ RUBY
end end
def self.test_commands def self.test_commands
return %q{ 'bin/annotate --require ./config/init.rb'
bin/annotate --require ./config/init.rb
}
end end
def self.verify_output(output) def self.verify_output(output)
...@@ -28,13 +26,15 @@ RUBY ...@@ -28,13 +26,15 @@ RUBY
end end
def self.verify_files(test_rig) def self.verify_files(test_rig)
return Annotate::Validations::Common.verify_files({ Annotate::Validations::Common.verify_files(
:model => true, {
:test => false, model: true,
:fixture => false, test: false,
:factory => false, fixture: false,
:routes => false factory: false,
}, test_rig, self.schema_annotation, nil, true) routes: false
}, test_rig, schema_annotation, nil, true
)
end end
end end
end end
......
require 'coveralls'
require 'codeclimate-test-reporter'
require 'simplecov'
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
[
Coveralls::SimpleCov::Formatter,
SimpleCov::Formatter::HTMLFormatter,
CodeClimate::TestReporter::Formatter
]
)
SimpleCov.start
require 'rubygems' require 'rubygems'
require 'bundler' require 'bundler'
Bundler.setup Bundler.setup
require 'rake'
require 'rspec' require 'rspec'
require 'wrong/adapters/rspec' require 'wrong/adapters/rspec'
$:.unshift(File.join(File.dirname(__FILE__), '../lib')) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '../lib'))
$:.unshift(File.dirname(__FILE__)) $LOAD_PATH.unshift(File.dirname(__FILE__))
require 'active_support' require 'active_support'
require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/class/subclasses' require 'active_support/core_ext/class/subclasses'
require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/string/inflections'
require 'annotate' require 'annotate'
require 'byebug'
module Annotate module Annotate
module Integration module Integration
ABSOLUTE_GEM_ROOT=File.expand_path('../../', __FILE__) ABSOLUTE_GEM_ROOT = File.expand_path('../../', __FILE__)
CRUFT_PATTERNS=[ CRUFT_PATTERNS = %w(
"%SCENARIO%/bin/*", "%SCENARIO%/log/*", "%SCENARIO%/tmp/*", %SCENARIO%/bin/*
"%SCENARIO%/.bundle" %SCENARIO%/log/*
] %SCENARIO%/tmp/*
SCENARIO_HOME=File.join(File.dirname(__FILE__), 'integration') %SCENARIO%/.bundle
SCENARIOS=Dir.glob("#{SCENARIO_HOME}/*"). ).freeze
select { |candidate| File.directory?(candidate) }.
map do |test_rig| SCENARIO_HOME = File.join(File.dirname(__FILE__), 'integration')
base_dir = File.basename(test_rig) SCENARIOS = Dir.glob("#{SCENARIO_HOME}/*").select do |candidate|
[test_rig, base_dir, base_dir.titlecase] File.directory?(candidate)
end end.map do |test_rig|
base_dir = File.basename(test_rig)
[test_rig, base_dir, base_dir.titlecase]
end
def self.nuke_cruft(test_rig) def self.nuke_cruft(test_rig)
FileList[ FileList[
Annotate::Integration::CRUFT_PATTERNS. Annotate::Integration::CRUFT_PATTERNS.map do |pattern|
map { |pattern| pattern.sub('%SCENARIO%', test_rig) } pattern.sub('%SCENARIO%', test_rig)
end
].each do |fname| ].each do |fname|
FileUtils.rm_rf(fname) FileUtils.rm_rf(fname)
end end
end end
def self.nuke_all_cruft def self.nuke_all_cruft
SCENARIOS.each do |test_rig, base_dir, test_name| SCENARIOS.each do |test_rig, _base_dir, _test_name|
nuke_cruft(test_rig) nuke_cruft(test_rig)
end end
end end
def self.empty_gemset(test_rig) def self.empty_gemset(test_rig)
Dir.chdir(test_rig) do Dir.chdir(test_rig) do
system(%q{ system('
( (
export SKIP_BUNDLER=1 export SKIP_BUNDLER=1
source .rvmrc && source .rvmrc &&
rvm --force gemset empty rvm --force gemset empty
) 2>&1 ) 2>&1
}) ')
end end
end end
...@@ -65,8 +85,8 @@ module Annotate ...@@ -65,8 +85,8 @@ module Annotate
system("git clean -dfx #{SCENARIO_HOME}/*/") system("git clean -dfx #{SCENARIO_HOME}/*/")
end end
def self.is_clean?(test_rig) def self.clean?(test_rig)
return `git status --porcelain #{test_rig}/ | wc -l`.strip.to_i == 0 `git status --porcelain #{test_rig}/ | wc -l`.strip.to_i.zero?
end end
end end
end end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment