diff --git a/.gitignore b/.gitignore
index 5f7d1feca..c2e56f804 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,20 +1,23 @@
+!/spec/integration/*/log/.gitkeep
+!/spec/integration/*/tmp/.gitkeep
+*.gem
.DS_Store
+.bundle
+.gems
+.rbenv-version
+.ruby-*
+/.idea/
+/.rbx
+/.rvmrc
/.yardoc/*
-/doc/*
+/Gemfile.lock
/coverage/*
-/spec/debug.log
-/pkg/*
/dist
-/Gemfile.lock
-*.gem
-/.idea/
-/.rvmrc
-.bundle
-/.rbx
+/doc/*
+/pkg/*
+/spec/debug.log
/spec/integration/*/bin/
+/spec/integration/*/db/test.*
/spec/integration/*/log/*
-!/spec/integration/*/log/.gitkeep
/spec/integration/*/tmp/*
-!/spec/integration/*/tmp/.gitkeep
-/spec/integration/*/db/test.*
-.rbenv-version
\ No newline at end of file
+.byebug_history
diff --git a/.overcommit.yml b/.overcommit.yml
new file mode 100644
index 000000000..1f28db927
--- /dev/null
+++ b/.overcommit.yml
@@ -0,0 +1,38 @@
+# 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
diff --git a/.rbenv-gemsets b/.rbenv-gemsets
new file mode 100644
index 000000000..a9606b749
--- /dev/null
+++ b/.rbenv-gemsets
@@ -0,0 +1 @@
+.gems
diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 000000000..c58fbed4c
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,7 @@
+inherit_from: ./.rubocop_todo.yml
+
+AllCops:
+ Exclude:
+ - 'vendor/**/*'
+ - 'spec/fixtures/**/*'
+ - 'tmp/**/*'
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
new file mode 100644
index 000000000..03801c2e1
--- /dev/null
+++ b/.rubocop_todo.yml
@@ -0,0 +1,589 @@
+# 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'
diff --git a/.travis.yml b/.travis.yml
index b6a32cd82..c72f5d690 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,16 @@ rvm:
- 1.9.3
- 2.0
- 2.1
- - 2.2
- - 2.3.0-preview1
+ - 2.2.5
+ - 2.3.0
+ - 2.4.0
+ - ruby-head
+matrix:
+ allow_failures:
+ - rvm: ruby-head
+ - rvm: 1.9.3
before_install:
- - rvm @global do gem install bundler
+ - gem update --system
+ - gem update bundler
+script:
+ - bundle exec rubocop && bundle exec rspec
diff --git a/AUTHORS.rdoc b/AUTHORS.rdoc
index 364f29807..5c18e2551 100644
--- a/AUTHORS.rdoc
+++ b/AUTHORS.rdoc
@@ -29,5 +29,6 @@ With help from:
- Paul Alexander
- Dmitry Lihachev
- qichunren
+- Guillermo Guerrero - http://github.com/ryanfox1985
and many others that I may have forgotten to add.
diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc
index 40e5df77d..bd6ce32ff 100644
--- a/CHANGELOG.rdoc
+++ b/CHANGELOG.rdoc
@@ -1,3 +1,9 @@
+== 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
See https://github.com/ctran/annotate_models/releases/tag/v2.7.0
diff --git a/Gemfile b/Gemfile
index 8455ab359..f30abba0d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,28 +1,38 @@
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
- gem 'mg', :require => false
gem 'bump'
- platforms :mri do
- gem 'yard', :require => false
+ gem 'mg', require: false
+ platforms :mri, :mingw do
+ gem 'yard', require: false
end
end
group :development, :test do
- gem 'rspec', :require => false
- gem 'guard-rspec', :require => false
- gem 'terminal-notifier-guard', :require => false
+ gem 'byebug'
+ gem 'guard-rspec', require: false
+ gem 'rspec', require: false
- platforms :mri do
- gem 'pry', :require => false
- gem 'pry-coolline', :require => false
+ gem 'rubocop', '~> 0.46.0', require: false unless RUBY_VERSION =~ /^1.8/
+ gem 'simplecov', 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
group :test do
- gem 'wrong', :require => false
- gem 'files', :require => false
+ gem 'files', require: false
+ gem 'wrong', require: false
end
diff --git a/Guardfile b/Guardfile
index 5120ddd02..602c7b57c 100644
--- a/Guardfile
+++ b/Guardfile
@@ -1,5 +1,3 @@
-
-
# 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.
# * bundler: 'bundle exec rspec'
@@ -11,16 +9,16 @@
guard :rspec, cmd: 'bundle exec rspec' do
watch(%r{^spec/.+_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
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/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('config/routes.rb') { "spec/routing" }
- watch('app/controllers/application_controller.rb') { "spec/controllers" }
- watch('spec/rails_helper.rb') { "spec" }
+ watch(%r{^spec/support/(.+)\.rb$}) { 'spec' }
+ watch('config/routes.rb') { 'spec/routing' }
+ watch('app/controllers/application_controller.rb') { 'spec/controllers' }
+ watch('spec/rails_helper.rb') { 'spec' }
# Capybara features specs
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
watch(%r{^spec/acceptance/(.+)\.feature$})
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
end
-
diff --git a/README.rdoc b/README.rdoc
old mode 100755
new mode 100644
index ac85a4b92..efd634586
--- a/README.rdoc
+++ b/README.rdoc
@@ -1,7 +1,11 @@
== Annotate (aka AnnotateModels)
{}[http://badge.fury.io/rb/annotate]
-{}[https://travis-ci.org/ctran/annotate_models]
+{}[https://rubygems.org/gems/annotate]
+{}[https://travis-ci.org/ctran/annotate_models]
+{}[https://coveralls.io/r/ctran/annotate_models?branch=develop]
+{}[https://codeclimate.com/github/ctran/annotate_models]
+{}[http://inch-ci.org/github/ctran/annotate_models]
{}[https://gemnasium.com/ctran/annotate_models]
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
# path :geometry line_string, 4326
Also, if you pass the -r option, it'll annotate routes.rb with the output of
-+rake routes+.
+rake routes
.
== Install
@@ -55,7 +59,7 @@ Into Gemfile from rubygems.org:
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:
@@ -63,7 +67,7 @@ Into environment gems from rubygems.org:
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
rake build
gem install pkg/annotate-*.gem
@@ -71,7 +75,7 @@ Into environment gems from Github checkout:
== 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 bundle exec
.)
=== Usage in Rails
@@ -100,7 +104,7 @@ To remove routes.rb annotations:
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 db:migrate
, either run rails g annotate:install
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
@@ -136,7 +140,7 @@ functionality:
rake remove_annotation # Remove schema information from model and fixture files
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 rake db:migrate
(but only in development mode).
If you want to disable this behavior permanently, edit the +.rake+ file and
change:
@@ -146,7 +150,7 @@ To:
'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 rake db:migrate
as a one-off without running annotate,
you can do so with a simple environment variable, instead of editing the
+.rake+ file:
@@ -175,12 +179,13 @@ you can do so with a simple environment variable, instead of editing the
--wo, --wrapper-open STR Annotation wrapper opening.
--wc, --wrapper-close STR Annotation wrapper closing
-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
-m, --show-migration Include the migration version number 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
-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
--sort Sort columns alphabetically, rather than in creation order
-R, --require path Additional file to require before loading models, may be used multiple times
diff --git a/Rakefile b/Rakefile
index 250e7111e..65aae5e59 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,11 +1,15 @@
+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
begin
require 'bundler'
Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e
- $stderr.puts e.message
- $stderr.puts "Run `bundle install` to install missing gems"
- exit e.status_code
+ $stderr.puts 'Run `bundle install` to install missing gems'
+ exit_exception(e)
end
using_dsl = false
@@ -14,67 +18,65 @@ begin
using_dsl = true
rescue Exception => e
# We might just be on an old version of Rake...
+ exit_exception(e)
end
require 'rake'
-if(using_dsl)
- include Rake::DSL
-end
-
-require "./lib/annotate"
+include Rake::DSL if using_dsl
-require "mg"
+require './lib/annotate'
+require 'mg'
begin
- MG.new("annotate.gemspec")
-rescue Exception => e
+ MG.new('annotate.gemspec')
+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.")
# Gemspec is probably in a broken state, so let's give ourselves a chance to
# build a new one...
end
-DEVELOPMENT_GROUPS=[:development, :test]
-RUNTIME_GROUPS=Bundler.definition.groups - DEVELOPMENT_GROUPS
+DEVELOPMENT_GROUPS = [:development, :test].freeze
+RUNTIME_GROUPS = Bundler.definition.groups - DEVELOPMENT_GROUPS
namespace :gem do
task :gemspec do
spec = Gem::Specification.new do |gem|
# See http://docs.rubygems.org/read/chapter/20
# for more options.
gem.version = Annotate.version
- gem.name = "annotate"
- gem.homepage = "http://github.com/ctran/annotate_models"
- gem.rubyforge_project = "annotate"
- gem.license = "Ruby"
- gem.summary = %q{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.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.require_paths = ["lib"]
+ gem.name = 'annotate'
+ gem.homepage = 'http://github.com/ctran/annotate_models'
+ gem.rubyforge_project = 'annotate'
+ gem.license = 'Ruby'
+ gem.summary = 'Annotates Rails 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.authors = ['Alex Chaffee', 'Cuong Tran', 'Marcos Piccinini', 'Turadg Aleahmad', 'Jon Frisby']
+ gem.require_paths = ['lib']
# gem.rdoc_options = ["--charset=UTF-8"]
# gem.required_ruby_version = "> 1.9.2"
Bundler.load.dependencies_for(*RUNTIME_GROUPS).each do |dep|
- runtime_resolved = Bundler.definition.specs_for(RUNTIME_GROUPS).select { |spec| spec.name == dep.name }.first
- if(!runtime_resolved.nil?)
+ runtime_resolved = Bundler.definition.specs_for(RUNTIME_GROUPS).find { |spec| spec.name == dep.name }
+ unless runtime_resolved.nil?
gem.add_dependency(dep.name, dep.requirement)
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.files = `git ls-files -- .`.split("\n").reject do |fn|
fn =~ /^Gemfile.*/ ||
- fn =~ /^Rakefile/ ||
- fn =~ /^\.rvmrc/ ||
- fn =~ /^\.gitignore/ ||
- fn =~ /^\.rspec/ ||
- fn =~ /^\.document/ ||
- fn =~ /^\.yardopts/ ||
- fn =~ /^pkg/ ||
- fn =~ /^spec/ ||
- fn =~ /^doc/ ||
- fn =~ /^vendor\/cache/
+ fn =~ /^Rakefile/ ||
+ fn =~ /^\.rvmrc/ ||
+ fn =~ /^\.gitignore/ ||
+ fn =~ /^\.rspec/ ||
+ fn =~ /^\.document/ ||
+ fn =~ /^\.yardopts/ ||
+ fn =~ /^pkg/ ||
+ fn =~ /^spec/ ||
+ fn =~ /^doc/ ||
+ fn =~ /^vendor\/cache/
end.sort
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("# DO NOT EDIT THIS FILE DIRECTLY!\n")
fh.write("# Instead, edit the Rakefile and run 'rake gems:gemspec'.")
@@ -85,12 +87,12 @@ end
namespace :jeweler do
task :clobber do
- FileUtils.rm_f("pkg")
+ FileUtils.rm_f('pkg')
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|
t.pattern = ['spec/*_spec.rb', 'spec/**/*_spec.rb']
t.rspec_opts = ['--backtrace', '--format d']
@@ -105,78 +107,76 @@ end
namespace :gemsets do
desc "Completely empty any gemsets used by scenarios, so they'll be perfectly clean on the next run."
- task :empty => [:integration_environment] do
- Annotate::Integration::SCENARIOS.each do |test_rig, base_dir, test_name|
+ task empty: [:integration_environment] do
+ Annotate::Integration::SCENARIOS.each do |test_rig, _base_dir, _test_name|
Annotate::Integration.empty_gemset(test_rig)
end
end
end
-task :clobber => :'gemsets:empty'
+task clobber: :'gemsets:empty'
namespace :integration do
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
end
- desc "Reset any changed files, and remove any untracked files in spec/integration/*/, plus run integration:clean."
- task :clobber => [:integration_environment, :'integration:clean'] do
+ desc 'Reset any changed files, and remove any untracked files in spec/integration/*/, plus run integration:clean.'
+ task clobber: [:integration_environment, :'integration:clean'] do
Annotate::Integration.reset_dirty_files
Annotate::Integration.clear_untracked_files
end
- task :symlink => [:integration_environment] do
+ task symlink: [:integration_environment] do
require 'digest/md5'
integration_dir = File.expand_path(File.join(File.dirname(__FILE__), 'spec', 'integration'))
- fixture_dir = File.expand_path(File.join(File.dirname(__FILE__), 'spec', 'fixtures'))
- 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 "TARGET directory must be within spec/integration/!" unless(target_dir.start_with?(integration_dir))
+ # fixture_dir = File.expand_path(File.join(File.dirname(__FILE__), 'spec', 'fixtures'))
+ 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 'TARGET directory must be within spec/integration/!' unless target_dir.start_with?(integration_dir)
candidates = {}
FileList[
"#{target_dir}/.rvmrc",
"#{target_dir}/**/*"
- ].select { |fname| !(File.symlink?(fname) || File.directory?(fname)) }.
- map { |fname| fname.sub(integration_dir, '') }.
- reject do |fname|
+ ].select { |fname| !(File.symlink?(fname) || File.directory?(fname)) }
+ .map { |fname| fname.sub(integration_dir, '') }
+ .reject do |fname|
fname =~ /\/\.gitkeep$/ ||
- fname =~ /\/app\/models\// ||
- fname =~ /\/routes\.rb$/ ||
- fname =~ /\/fixtures\// ||
- fname =~ /\/factories\// ||
- fname =~ /\.sqlite3$/ ||
- (fname =~ /\/test\// && fname !~ /_helper\.rb$/) ||
- (fname =~ /\/spec\// && fname !~ /_helper\.rb$/)
- end.
- map { |fname| "#{integration_dir}#{fname}"}.
- each do |fname|
+ fname =~ /\/app\/models\// ||
+ fname =~ /\/routes\.rb$/ ||
+ fname =~ /\/fixtures\// ||
+ fname =~ /\/factories\// ||
+ fname =~ /\.sqlite3$/ ||
+ (fname =~ /\/test\// && fname !~ /_helper\.rb$/) ||
+ (fname =~ /\/spec\// && fname !~ /_helper\.rb$/)
+ end
+ .map { |fname| "#{integration_dir}#{fname}" }
+ .each do |fname|
digest = Digest::MD5.hexdigest(File.read(fname))
candidates[digest] ||= []
candidates[digest] << fname
end
fixtures = {}
- FileList["spec/fixtures/**/*"].each do |fname|
+ FileList['spec/fixtures/**/*'].each do |fname|
fixtures[Digest::MD5.hexdigest(File.read(fname))] = File.expand_path(fname)
end
candidates.keys.each do |digest|
- if(fixtures.has_key?(digest))
- candidates[digest].each do |fname|
- # Double-check contents in case of hash collision...
- if(FileUtils.identical?(fname, fixtures[digest]))
- destination_dir = Pathname.new(File.dirname(fname))
- relative_target = Pathname.new(fixtures[digest]).relative_path_from(destination_dir)
- Dir.chdir(destination_dir) do
- sh("ln", "-sfn", relative_target.to_s, File.basename(fname))
- end
- end
+ next unless fixtures.key?(digest)
+ candidates[digest].each do |fname|
+ # Double-check contents in case of hash collision...
+ next unless FileUtils.identical?(fname, fixtures[digest])
+ destination_dir = Pathname.new(File.dirname(fname))
+ relative_target = Pathname.new(fixtures[digest]).relative_path_from(destination_dir)
+ Dir.chdir(destination_dir) do
+ sh('ln', '-sfn', relative_target.to_s, File.basename(fname))
end
end
end
end
end
-task :clobber => :'integration:clobber'
+task clobber: :'integration:clobber'
require 'yard'
YARD::Rake::YardocTask.new do |t|
@@ -186,19 +186,19 @@ end
namespace :yard do
task :clobber do
- FileUtils.rm_f(".yardoc")
- FileUtils.rm_f("doc")
+ FileUtils.rm_f('.yardoc')
+ FileUtils.rm_f('doc')
end
end
-task :clobber => :'yard:clobber'
+task clobber: :'yard:clobber'
namespace :rubinius do
task :clobber do
- FileList["**/*.rbc"].each { |fname| FileUtils.rm_f(fname) }
- FileList[".rbx/**/*"].each { |fname| FileUtils.rm_f(fname) }
+ FileList['**/*.rbc'].each { |fname| FileUtils.rm_f(fname) }
+ FileList['.rbx/**/*'].each { |fname| FileUtils.rm_f(fname) }
end
end
-task :clobber => :'rubinius:clobber'
+task clobber: :'rubinius:clobber'
# want other tests/tasks run by default? Add them to the list
-task :default => [:spec]
+task default: [:spec]
diff --git a/annotate.gemspec b/annotate.gemspec
index 56d5972f8..e46a4ff00 100644
--- a/annotate.gemspec
+++ b/annotate.gemspec
@@ -4,36 +4,45 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'annotate/version'
Gem::Specification.new do |s|
- s.name = "annotate"
+ s.name = 'annotate'
s.version = Annotate.version
s.required_ruby_version = '>= 1.9.3'
- 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.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.executables = ["annotate"]
- 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.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."
+ 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.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.executables = ['annotate']
+ 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/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 Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
- s.add_runtime_dependency(%q, ["~> 10.4"])
- s.add_runtime_dependency(%q, [">= 3.2", "< 6.0"])
- else
- s.add_dependency(%q, ["~> 10.4"])
- s.add_dependency(%q, [">= 3.2", "< 6.0"])
- end
- else
- s.add_dependency(%q, [">= 0.8.7"])
- s.add_dependency(%q, [">= 3.2", "< 6.0"])
- end
+ s.specification_version = 4 if s.respond_to? :specification_version
+ s.add_runtime_dependency(%q, ['>= 10.4', '< 13.0'])
+ s.add_runtime_dependency(%q, ['>= 3.2', '< 6.0'])
end
diff --git a/bin/annotate b/bin/annotate
index 24a8f9cca..a5acf0818 100755
--- a/bin/annotate
+++ b/bin/annotate
@@ -1,18 +1,18 @@
#!/usr/bin/env ruby
-unless File.exists?('./Rakefile') || File.exists?('./Gemfile')
- abort "Please run annotate from the root of the project."
+unless File.exist?('./Rakefile') || File.exist?('./Gemfile')
+ abort 'Please run annotate from the root of the project.'
end
require 'rubygems'
begin
require 'bundler'
Bundler.setup
-rescue Exception => e
+rescue StandardError
end
here = File.expand_path(File.dirname __FILE__)
-$:<< "#{here}/../lib"
+$LOAD_PATH << "#{here}/../lib"
require 'optparse'
require 'annotate'
@@ -20,58 +20,55 @@ Annotate.bootstrap_rake
has_set_position = {}
target_action = :do_annotations
+positions = %w(before top after bottom)
OptionParser.new do |opts|
- opts.banner = "Usage: annotate [options] [model_file]*"
-
- opts.on('-d', '--delete',
- "Remove annotations from all model files or the routes.rb file") do
+ opts.banner = 'Usage: annotate [options] [model_file]*'
+ opts.on('-d', '--delete', 'Remove annotations from all model files or the routes.rb file') do
target_action = :remove_annotations
end
- opts.on('-p', '--position [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'],
- "Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory/route/serializer file(s)") do |p|
+ 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|
ENV['position'] = p
- [
- 'position_in_class','position_in_factory','position_in_fixture','position_in_test', 'position_in_routes', 'position_in_serializer'
- ].each do |key|
- ENV[key] = p unless(has_set_position[key])
+ %w(position_in_class position_in_factory position_in_fixture position_in_test position_in_routes position_in_serializer).each do |key|
+ ENV[key] = p unless has_set_position[key]
end
end
- opts.on('--pc', '--position-in-class [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'],
- "Place the annotations at the top (before) or the bottom (after) of the model file") do |p|
+ 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|
ENV['position_in_class'] = p
has_set_position['position_in_class'] = true
end
- opts.on('--pf', '--position-in-factory [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'],
- "Place the annotations at the top (before) or the bottom (after) of any factory files") do |p|
+ 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|
ENV['position_in_factory'] = p
has_set_position['position_in_factory'] = true
end
- opts.on('--px', '--position-in-fixture [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'],
- "Place the annotations at the top (before) or the bottom (after) of any fixture files") do |p|
+ 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|
ENV['position_in_fixture'] = p
has_set_position['position_in_fixture'] = true
end
- opts.on('--pt', '--position-in-test [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'],
- "Place the annotations at the top (before) or the bottom (after) of any test files") do |p|
+ 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|
ENV['position_in_test'] = p
has_set_position['position_in_test'] = true
end
- opts.on('--pr', '--position-in-routes [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'],
- "Place the annotations at the top (before) or the bottom (after) of the routes.rb file") do |p|
+ 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|
ENV['position_in_routes'] = p
has_set_position['position_in_routes'] = true
end
- opts.on('--ps', '--position-in-serializer [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'],
- "Place the annotations at the top (before) or the bottom (after) of the serializer files") do |p|
+ 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|
ENV['position_in_serializer'] = p
has_set_position['position_in_serializer'] = true
end
@@ -89,59 +86,60 @@ OptionParser.new do |opts|
ENV['wrapper_close'] = p
end
- opts.on('-r', '--routes',
- "Annotate routes.rb with the output of 'rake routes'") do
+ opts.on('-r', '--routes', "Annotate routes.rb with the output of 'rake routes'") do
ENV['routes'] = 'true'
end
- opts.on('-v', '--version',
- "Show the current version of this gem") do
+ opts.on('-aa', '--active-admin', 'Annotate active_admin models') do
+ ENV['active_admin'] = 'true'
+ end
+
+ opts.on('-v', '--version', 'Show the current version of this gem') do
puts "annotate v#{Annotate.version}"; exit
end
- opts.on('-m', '--show-migration',
- "Include the migration version number in the annotation") do
- ENV['include_version'] = "yes"
+ opts.on('-m', '--show-migration', 'Include the migration version number in the annotation') do
+ ENV['include_version'] = 'yes'
end
opts.on('-k', '--show-foreign-keys',
"List the table's foreign key constraints in the annotation") do
- ENV['show_foreign_keys'] = "yes"
+ ENV['show_foreign_keys'] = 'yes'
end
opts.on('-i', '--show-indexes',
"List the table's database indexes in the annotation") do
- ENV['show_indexes'] = "yes"
+ ENV['show_indexes'] = 'yes'
end
opts.on('-s', '--simple-indexes',
"Concat the column's related indexes in the annotation") do
- ENV['simple_indexes'] = "yes"
+ ENV['simple_indexes'] = 'yes'
end
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
end
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
end
opts.on('--ignore-model-subdirects',
"Ignore subdirectories of the models directory") do |dir|
- ENV['ignore_model_sub_dir'] = "yes"
+ ENV['ignore_model_sub_dir'] = 'yes'
end
opts.on('--sort',
"Sort columns alphabetically, rather than in creation order") do |dir|
- ENV['sort'] = "yes"
+ ENV['sort'] = 'yes'
end
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|
- ENV['classified_sort'] = "yes"
+ ENV['classified_sort'] = 'yes'
end
opts.on('-R', '--require path',
@@ -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|
exclusions ||= %w(tests fixtures factories)
- exclusions.each { |exclusion| ENV["exclude_#{exclusion}"] = "yes" }
+ exclusions.each { |exclusion| ENV["exclude_#{exclusion}"] = 'yes' }
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'
end
@@ -174,21 +172,30 @@ OptionParser.new do |opts|
ENV['trace'] = 'yes'
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
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}"
end
- opts.on('--ignore-unknown-models', "don't display warnings for bad model files" ) do |values|
- ENV['ignore_unknown_models'] = "true"
+ 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['hide_default_column_types'] = "#{values}"
end
+ opts.on('--ignore-unknown-models', "don't display warnings for bad model files") do |values|
+ ENV['ignore_unknown_models'] = 'true'
+ end
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)
AnnotateModels.send(target_action, options) if Annotate.include_models?
diff --git a/circle.yml b/circle.yml
new file mode 100644
index 000000000..342ed0dec
--- /dev/null
+++ b/circle.yml
@@ -0,0 +1,7 @@
+machine:
+ ruby:
+ version: 2.2.6
+
+test:
+ override:
+ - bundle exec rubocop && bundle exec rspec
diff --git a/lib/annotate.rb b/lib/annotate.rb
old mode 100755
new mode 100644
index fa9b6607e..a17b14215
--- a/lib/annotate.rb
+++ b/lib/annotate.rb
@@ -1,4 +1,6 @@
-$:.unshift(File.dirname(__FILE__))
+# rubocop:disable Metrics/ModuleLength
+
+$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'annotate/version'
require 'annotate/annotate_models'
require 'annotate/annotate_routes'
@@ -7,60 +9,70 @@
# ActiveSupport 3.x...
require 'active_support/hash_with_indifferent_access'
require 'active_support/core_ext/object/blank'
-rescue Exception
+rescue StandardError
# ActiveSupport 2.x...
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/blank'
end
module Annotate
+ TRUE_RE = /^(true|t|yes|y|1)$/i
+
##
# 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_fixture, :position_in_factory, :position,
:position_in_serializer
- ]
- FLAG_OPTIONS=[
+ ].freeze
+ FLAG_OPTIONS = [
:show_indexes, :simple_indexes, :include_version, :exclude_tests,
:exclude_fixtures, :exclude_factories, :ignore_model_sub_dir,
:format_bare, :format_rdoc, :format_markdown, :sort, :force, :trace,
:timestamp, :exclude_serializers, :classified_sort, :show_foreign_keys,
- :exclude_scaffolds, :exclude_controllers, :exclude_helpers, :ignore_unknown_models,
- ]
- OTHER_OPTIONS=[
- :ignore_columns, :skip_on_db_migrate, :wrapper_open, :wrapper_close, :wrapper, :routes,
- :hide_limit_column_types,
- ]
- PATH_OPTIONS=[
+ :exclude_scaffolds, :exclude_controllers, :exclude_helpers,
+ :exclude_sti_subclasses, :ignore_unknown_models
+ ].freeze
+ OTHER_OPTIONS = [
+ :ignore_columns, :skip_on_db_migrate, :wrapper_open, :wrapper_close,
+ :wrapper, :routes, :hide_limit_column_types, :hide_default_column_types,
+ :ignore_routes, :active_admin
+ ].freeze
+ PATH_OPTIONS = [
: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.
#
def self.set_defaults(options = {})
- return if(@has_set_defaults)
+ return if @has_set_defaults
@has_set_defaults = true
options = HashWithIndifferentAccess.new(options)
- [POSITION_OPTIONS, FLAG_OPTIONS, PATH_OPTIONS, OTHER_OPTIONS].flatten.each do |key|
- if options.has_key?(key)
+ all_options.flatten.each do |key|
+ if options.key?(key)
default_value = if options[key].is_a?(Array)
- options[key].join(",")
- else
- options[key]
- end
+ options[key].join(',')
+ else
+ options[key]
+ 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
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 = {})
POSITION_OPTIONS.each do |key|
options[key] = fallback(ENV[key.to_s], ENV['position'], 'before')
@@ -69,30 +81,27 @@ def self.setup_options(options = {})
options[key] = true?(ENV[key.to_s])
end
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
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
- if(options[:model_dir].empty?)
- options[:model_dir] = ['app/models']
- end
-
- if(options[:root_dir].empty?)
- options[:root_dir] = ['']
- end
+ options[:model_dir] = ['app/models'] if options[:model_dir].empty?
options[:wrapper_open] ||= 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
def self.reset_options
- [POSITION_OPTIONS, FLAG_OPTIONS, PATH_OPTIONS, OTHER_OPTIONS].flatten.each do |key|
- ENV[key.to_s] = nil
- end
+ all_options.flatten.each { |key| ENV[key.to_s] = nil }
end
def self.skip_on_migration?
@@ -107,26 +116,34 @@ def self.include_models?
true
end
- def self.loaded_tasks=(val); @loaded_tasks = val; end
- def self.loaded_tasks; return @loaded_tasks; end
+ def self.loaded_tasks=(val)
+ @loaded_tasks = val
+ end
+
+ def self.loaded_tasks
+ @loaded_tasks
+ end
def self.load_tasks
- return if(self.loaded_tasks)
+ return if loaded_tasks
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
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
def self.eager_load(options)
- self.load_requires(options)
- require "annotate/active_record_patch"
+ load_requires(options)
+ require 'annotate/active_record_patch'
- if(defined?(Rails))
- if(Rails.version.split('.').first.to_i < 3)
+ if defined?(Rails::Application)
+ if Rails.version.split('.').first.to_i < 3
Rails.configuration.eager_load_paths.each do |load_path|
matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
@@ -149,33 +166,38 @@ def self.eager_load(options)
def self.bootstrap_rake
begin
require 'rake/dsl_definition'
- rescue Exception
+ rescue StandardError => e
# We might just be on an old version of Rake...
+ puts e.message
+ exit e.status_code
end
require 'rake'
- if File.exists?('./Rakefile')
- load './Rakefile'
+ load './Rakefile' if File.exist?('./Rakefile')
+ begin
+ Rake::Task[:environment].invoke
+ rescue
+ nil
end
- Rake::Task[:environment].invoke rescue nil
- if(!defined?(Rails))
+ unless defined?(Rails)
# Not in a Rails project, so time to load up the parts of
# ActiveSupport we need.
require 'active_support'
require 'active_support/core_ext/class/subclasses'
require 'active_support/core_ext/string/inflections'
end
- self.load_tasks
+
+ load_tasks
Rake::Task[:set_annotation_options].invoke
end
def self.fallback(*args)
- return args.detect { |arg| !arg.blank? }
+ args.detect { |arg| !arg.blank? }
end
def self.true?(val)
- return false if(val.blank?)
- return false unless(val =~ TRUE_RE)
- return true
+ return false if val.blank?
+ return false unless val =~ TRUE_RE
+ true
end
end
diff --git a/lib/annotate/active_record_patch.rb b/lib/annotate/active_record_patch.rb
index 0b2d9db5c..8983b0465 100644
--- a/lib/annotate/active_record_patch.rb
+++ b/lib/annotate/active_record_patch.rb
@@ -2,7 +2,7 @@
module ::ActiveRecord
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
end
end
diff --git a/lib/annotate/annotate_models.rb b/lib/annotate/annotate_models.rb
index 72c0a1c5e..1c9d2f950 100644
--- a/lib/annotate/annotate_models.rb
+++ b/lib/annotate/annotate_models.rb
@@ -1,146 +1,183 @@
+# rubocop:disable Metrics/ModuleLength
+
require 'bigdecimal'
module AnnotateModels
+ TRUE_RE = /^(true|t|yes|y|1)$/i
+
# Annotate Models plugin use this header
- COMPAT_PREFIX = "== Schema Info"
- COMPAT_PREFIX_MD = "## Schema Info"
- PREFIX = "== Schema Information"
- PREFIX_MD = "## Schema Information"
- END_MARK = "== Schema Information End"
- PATTERN = /^\r?\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\r?\n(#.*\r?\n)*(\r?\n)*/
+ COMPAT_PREFIX = '== Schema Info'.freeze
+ COMPAT_PREFIX_MD = '## Schema Info'.freeze
+ PREFIX = '== Schema Information'.freeze
+ PREFIX_MD = '## Schema Information'.freeze
+ END_MARK = '== Schema Information End'.freeze
- 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?
# I dont use windows, can`t test
- UNIT_TEST_DIR = File.join("test", "unit")
- MODEL_TEST_DIR = File.join("test", "models") # since rails 4.0
- SPEC_MODEL_DIR = File.join("spec", "models")
- FIXTURE_TEST_DIR = File.join("test", "fixtures")
- FIXTURE_SPEC_DIR = File.join("spec", "fixtures")
+ UNIT_TEST_DIR = File.join('test', "unit")
+ MODEL_TEST_DIR = File.join('test', "models") # since rails 4.0
+ SPEC_MODEL_DIR = File.join('spec', "models")
+ FIXTURE_TEST_DIR = File.join('test', "fixtures")
+ FIXTURE_SPEC_DIR = File.join('spec', "fixtures")
# Other test files
- CONTROLLER_TEST_DIR = File.join("test", "controllers")
- CONTROLLER_SPEC_DIR = File.join("spec", "controllers")
- REQUEST_SPEC_DIR = File.join("spec", "requests")
- ROUTING_SPEC_DIR = File.join("spec", "routing")
+ CONTROLLER_TEST_DIR = File.join('test', "controllers")
+ CONTROLLER_SPEC_DIR = File.join('spec', "controllers")
+ REQUEST_SPEC_DIR = File.join('spec', "requests")
+ ROUTING_SPEC_DIR = File.join('spec', "routing")
# Object Daddy http://github.com/flogic/object_daddy/tree/master
- EXEMPLARS_TEST_DIR = File.join("test", "exemplars")
- EXEMPLARS_SPEC_DIR = File.join("spec", "exemplars")
+ EXEMPLARS_TEST_DIR = File.join('test', "exemplars")
+ EXEMPLARS_SPEC_DIR = File.join('spec', "exemplars")
# Machinist http://github.com/notahat/machinist
- BLUEPRINTS_TEST_DIR = File.join("test", "blueprints")
- BLUEPRINTS_SPEC_DIR = File.join("spec", "blueprints")
+ BLUEPRINTS_TEST_DIR = File.join('test', "blueprints")
+ BLUEPRINTS_SPEC_DIR = File.join('spec', "blueprints")
# Factory Girl http://github.com/thoughtbot/factory_girl
- FACTORY_GIRL_TEST_DIR = File.join("test", "factories")
- FACTORY_GIRL_SPEC_DIR = File.join("spec", "factories")
+ FACTORY_GIRL_TEST_DIR = File.join('test', "factories")
+ FACTORY_GIRL_SPEC_DIR = File.join('spec', "factories")
# Fabrication https://github.com/paulelliott/fabrication.git
- FABRICATORS_TEST_DIR = File.join("test", "fabricators")
- FABRICATORS_SPEC_DIR = File.join("spec", "fabricators")
+ FABRICATORS_TEST_DIR = File.join('test', "fabricators")
+ FABRICATORS_SPEC_DIR = File.join('spec', "fabricators")
# Serializers https://github.com/rails-api/active_model_serializers
- SERIALIZERS_DIR = File.join("app", "serializers")
- SERIALIZERS_TEST_DIR = File.join("test", "serializers")
- SERIALIZERS_SPEC_DIR = File.join("spec", "serializers")
+ SERIALIZERS_DIR = File.join('app', "serializers")
+ SERIALIZERS_TEST_DIR = File.join('test', "serializers")
+ SERIALIZERS_SPEC_DIR = File.join('spec', "serializers")
# 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_DIR = File.join("app", "helpers")
+ HELPER_DIR = File.join('app', "helpers")
# Don't show limit (#) on these column types
# 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
- def model_dir
- @model_dir.is_a?(Array) ? @model_dir : [@model_dir || "app/models"]
+ def annotate_pattern(options = {})
+ 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
- def model_dir=(dir)
- @model_dir = dir
+ def model_dir
+ @model_dir.is_a?(Array) ? @model_dir : [@model_dir || 'app/models']
end
+ attr_writer :model_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
- def root_dir=(dir)
- @root_dir = dir
+ attr_writer :root_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
- def get_patterns(pattern_types=MATCHED_TYPES)
+ def get_patterns(pattern_types = [])
current_patterns = []
root_dir.each do |root_directory|
Array(pattern_types).each do |pattern_type|
- current_patterns += case 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
+ current_patterns += files_by_pattern(root_directory, pattern_type)
end
end
- current_patterns.map{ |p| p.sub(/^[\/]*/, '') }
+ current_patterns.map { |p| p.sub(/^[\/]*/, '') }
end
# Simple quoting for the default column value
def quote(value)
case value
- when NilClass then "NULL"
- when TrueClass then "TRUE"
- when FalseClass then "FALSE"
- when Float, Fixnum, Bignum then value.to_s
+ when NilClass then 'NULL'
+ when TrueClass then 'TRUE'
+ when FalseClass then 'FALSE'
+ when Float, Integer then value.to_s
# BigDecimals need to be output in a non-normalized form and quoted.
when BigDecimal then value.to_s('F')
- when Array then value.map {|v| quote(v)}
+ when Array then value.map { |v| quote(v) }
else
value.inspect
end
@@ -150,31 +187,38 @@ def schema_default(klass, column)
quote(klass.column_defaults[column.name])
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
# to create a comment block containing a line for
# each column. The line contains the column name,
# the type (and length), and any optional attributes
def get_schema_info(klass, header, options = {})
info = "# #{header}\n"
- 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"
+ info << get_schema_header_text(klass, options)
- 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
md_names_overhead = 6
md_type_allowance = 18
bare_type_allowance = 16
- 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<< "# #{ '-' * ( max_size + md_names_overhead ) } | #{'-' * md_type_allowance} | #{ '-' * 27 }\n"
+ 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 << "# #{ '-' * ( max_size + md_names_overhead ) } | #{'-' * md_type_allowance} | #{ '-' * 27 }\n"
end
cols = if ignore_columns = options[:ignore_columns]
@@ -185,20 +229,20 @@ def get_schema_info(klass, header, options = {})
klass.columns
end
- cols = cols.sort_by(&:name) if(options[:sort])
- cols = classified_sort(cols) if(options[:classified_sort])
+ cols = cols.sort_by(&:name) if options[:sort]
+ cols = classified_sort(cols) if options[:classified_sort]
cols.each do |col|
col_type = (col.type || col.sql_type).to_s
-
attrs = []
- attrs << "default(#{schema_default(klass, col)})" unless col.default.nil? || col_type == "jsonb"
- attrs << "not null" unless col.null
- 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 << "default(#{schema_default(klass, col)})" unless col.default.nil? || hide_default?(col_type, options)
+ attrs << 'unsigned' if col.respond_to?(:unsigned?) && col.unsigned?
+ 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})"
- elsif col_type != "spatial"
- if (col.limit)
+ elsif col_type != 'spatial'
+ if col.limit
if col.limit.is_a? Array
attrs << "(#{col.limit.join(', ')})"
else
@@ -208,38 +252,41 @@ def get_schema_info(klass, header, options = {})
end
# Check out if we got an array column
- if col.respond_to?(:array) && col.array
- attrs << "is an Array"
- end
+ attrs << 'is an Array' if col.respond_to?(:array) && col.array
# Check out if we got a geometric column
# and print the type and SRID
if col.respond_to?(:geometry_type)
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}"
end
# Check if the column has indices and print "indexed" if true
# If the index includes another column, print it too.
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 }
- 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 }
- attrs << (ind.length == 0 ? "indexed" : "indexed => [#{ind.join(", ")}]")
+ attrs << (ind.empty? ? "indexed" : "indexed => [#{ind.join(", ")}]")
end
end
end
-
+ col_name = if with_comment
+ "#{col.name}(#{col.comment})"
+ else
+ col.name
+ end
if options[:format_rdoc]
- info << sprintf("# %-#{max_size}.#{max_size}s%s", "*#{col.name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n"
+ info << sprintf("# %-#{max_size}.#{max_size}s%s", "*#{col_name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n"
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
- 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
- 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
@@ -251,6 +298,23 @@ def get_schema_info(klass, header, options = {})
info << get_foreign_key_info(klass, options)
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]
info << "#--\n"
info << "# #{END_MARK}\n"
@@ -260,29 +324,30 @@ def get_schema_info(klass, header, options = {})
end
end
- def get_index_info(klass, options={})
- if(options[:format_markdown])
- index_info = "#\n# ### Indexes\n#\n"
- else
- index_info = "#\n# Indexes\n#\n"
- end
+ def get_index_info(klass, options = {})
+ index_info = if options[:format_markdown]
+ "#\n# ### Indexes\n#\n"
+ else
+ "#\n# Indexes\n#\n"
+ end
- indexes = klass.connection.indexes(klass.table_name)
- return "" if indexes.empty?
+ indexes = retrieve_indexes_from_table(klass)
+ return '' if indexes.empty?
max_size = indexes.collect{|index| index.name.size}.max + 1
- indexes.sort_by{|index| index.name}.each do |index|
- if(options[:format_markdown])
- index_info << sprintf("# * `%s`%s:\n# * **`%s`**\n", index.name, index.unique ? " (_unique_)" : "", index.columns.join("`**\n# * **`"))
- else
- index_info << sprintf("# %-#{max_size}.#{max_size}s %s %s", index.name, "(#{index.columns.join(",")})", index.unique ? "UNIQUE" : "").rstrip + "\n"
- end
+ indexes.sort_by(&:name).each do |index|
+ index_info << if options[:format_markdown]
+ sprintf("# * `%s`%s:\n# * **`%s`**\n", index.name, index.unique ? " (_unique_)" : "", Array(index.columns).join("`**\n# * **`"))
+ else
+ sprintf("# %-#{max_size}.#{max_size}s %s %s", index.name, "(#{Array(index.columns).join(",")})", index.unique ? "UNIQUE" : "").rstrip + "\n"
+ end
end
- return index_info
+
+ index_info
end
def hide_limit?(col_type, options)
- excludes =
+ excludes =
if options[:hide_limit_column_types].blank?
NO_LIMIT_COL_TYPES
else
@@ -292,45 +357,67 @@ def hide_limit?(col_type, options)
excludes.include?(col_type)
end
- def get_foreign_key_info(klass, options={})
- if(options[:format_markdown])
- fk_info = "#\n# ### Foreign Keys\n#\n"
- else
- fk_info = "#\n# Foreign Keys\n#\n"
- end
+ def hide_default?(col_type, options)
+ excludes =
+ if options[:hide_default_column_types].blank?
+ NO_DEFAULT_COL_TYPES
+ else
+ 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)
- 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
- foreign_keys.sort_by{|fk| fk.name}.each do |fk|
+ max_size = foreign_keys.map(&format_name).map(&:size).max + 1
+ foreign_keys.sort_by {|fk| [format_name.call(fk), fk.column]}.each do |fk|
ref_info = "#{fk.column} => #{fk.to_table}.#{fk.primary_key}"
- if(options[:format_markdown])
- fk_info << sprintf("# * `%s`:\n# * **`%s`**\n", fk.name, ref_info)
- else
- fk_info << sprintf("# %-#{max_size}.#{max_size}s %s", fk.name, "(#{ref_info})").rstrip + "\n"
- end
+ constraints_info = ''
+ constraints_info += "ON DELETE => #{fk.on_delete} " if fk.on_delete
+ constraints_info += "ON UPDATE => #{fk.on_update} " if fk.on_update
+ constraints_info.strip!
+
+ 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
- return fk_info
+
+ fk_info
end
# 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
- # matches the block that is already there. If so, leave it be. If not, remove the old
- # info block and write a new one.
- # Returns true or false depending on whether the file was modified.
+ # a schema info block (a comment starting with "== Schema Information"),
+ # check if it matches the block that is already there. If so, leave it be.
+ # If not, remove the old info block and write a new one.
+ #
+ # == Returns:
+ # true or false depending on whether the file was modified.
#
# === Options (opts)
# :force:: whether to update the file even if it doesn't seem to need it.
# :position_in_*:: where to place the annotated section in fixture or model file,
# :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)
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
header_pattern = /(^# Table name:.*?\n(#.*[\r]?\n)*[\r]?)/
@@ -341,17 +428,17 @@ def annotate_one_file(file_name, info_block, position, options={})
old_columns = old_header && old_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_comments= old_content.scan(magic_comment_matcher).flatten.compact
+ 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
if old_columns == new_columns && !options[:force]
return false
else
# 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")
- new_content = old_content.sub(PATTERN, "\n" + info_block)
+ new_content = old_content.sub(annotate_pattern(options), "\n" + info_block)
end
wrapper_open = options[:wrapper_open] ? "# #{options[:wrapper_open]}\n" : ""
@@ -361,41 +448,48 @@ def annotate_one_file(file_name, info_block, position, options={})
# we simply need to insert it in correct position
if new_content == old_content || options[:force]
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) ?
- (magic_comments.join + (old_content.rstrip + "\n\n" + wrapped_info_block)) :
- (magic_comments.join + wrapped_info_block + "\n" + old_content)
+ new_content = if %w(after bottom).include?(options[position].to_s)
+ magic_comments.join + (old_content.rstrip + "\n\n" + wrapped_info_block)
+ else
+ magic_comments.join + wrapped_info_block + "\n" + old_content
+ end
end
- File.open(file_name, "wb") { |f| f.puts new_content }
+ File.open(file_name, 'wb') { |f| f.puts new_content }
return true
end
else
- return false
+ false
end
end
- def remove_annotation_of_file(file_name)
+ def remove_annotation_of_file(file_name, options = {})
if File.exist?(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 }
-
- return true
+ true
else
- return false
+ false
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
# info block (basically a comment containing information
# on the columns and their types) and put it at the front
# of the model and fixture source files.
- # Returns true or false depending on whether the source
- # files were modified.
#
# === Options (opts)
# :position_in_class:: where to place the annotated section in model file
@@ -410,41 +504,54 @@ def remove_annotation_of_file(file_name)
# :exclude_scaffolds:: whether to skip modification of scaffold files
# :exclude_controllers:: whether to skip modification of controller files
# :exclude_helpers:: whether to skip modification of helper files
+ # :exclude_sti_subclasses:: 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
+ klass.reset_column_information
info = get_schema_info(klass, header, options)
- did_annotate = false
model_name = klass.name.underscore
table_name = klass.table_name
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))
- did_annotate = true
+ annotated << model_file_name
end
- MATCHED_TYPES.each do |key|
+ matched_types(options).each do |key|
exclusion_key = "exclude_#{key.pluralize}".to_sym
position_key = "position_in_#{key}".to_sym
- unless options[exclusion_key]
- did_annotate = self.get_patterns(key).
- map { |f| resolve_filename(f, model_name, table_name) }.
- map { |f| annotate_one_file(f, info, position_key, options_with_position(options, position_key)) }.
- detect { |result| result } || did_annotate
+ # Same options for active_admin models
+ if key == 'admin'
+ exclusion_key = 'exclude_class'.to_sym
+ position_key = 'position_in_class'.to_sym
end
- end
- return did_annotate
- rescue Exception => e
+ next if options[exclusion_key]
+ 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 "\t" + e.backtrace.join("\n\t") if options[:trace]
end
+
+ annotated
end
# position = :position_in_fixture or :position_in_class
def options_with_position(options, position_in)
- options.merge(:position=>(options[position_in] || options[:position]))
+ options.merge(position: (options[position_in] || options[:position]))
end
# Return a list of the model files to annotate.
@@ -453,8 +560,8 @@ def options_with_position(options, position_in)
# in the model_dir directory.
def get_model_files(options)
models = []
- if(!options[:is_rake])
- models = ARGV.dup.reject{|m| m.match(/^(.*)=/)}
+ unless options[:is_rake]
+ models = ARGV.dup.reject { |m| m.match(/^(.*)=/) }
end
if models.empty?
@@ -488,13 +595,13 @@ def get_model_class(file)
model_path = file.gsub(/\.rb$/, '')
model_dir.each { |dir| model_path = model_path.gsub(/^#{dir}/, '').gsub(/^\//, '') }
begin
- get_loaded_model(model_path) or raise BadModelFileError.new
+ get_loaded_model(model_path) || raise(BadModelFileError.new)
rescue LoadError
# this is for non-rails projects, which don't get Rails auto-require magic
file_path = File.expand_path(file)
if File.file?(file_path) && silence_warnings { Kernel.require(file_path) }
retry
- elsif model_path.match(/\//)
+ elsif model_path =~ /\//
model_path = model_path.split('/')[1..-1].join('/').to_s
retry
else
@@ -505,43 +612,42 @@ def get_model_class(file)
# Retrieve loaded model class by path to the file where it's supposed to be defined.
def get_loaded_model(model_path)
- begin
- ActiveSupport::Inflector.constantize(ActiveSupport::Inflector.camelize(model_path))
- rescue
- # Revert to the old way but it is not really robust
- ObjectSpace.each_object(::Class).
- select do |c|
- Class === c and # note: we use === to avoid a bug in activesupport 2.3.14 OptionMerger vs. is_a?
- c.ancestors.respond_to?(:include?) and # to fix FactoryGirl bug, see https://github.com/ctran/annotate_models/pull/82
- c.ancestors.include?(ActiveRecord::Base)
- end.
- detect { |c| ActiveSupport::Inflector.underscore(c.to_s) == model_path }
- end
+ ActiveSupport::Inflector.constantize(ActiveSupport::Inflector.camelize(model_path))
+ rescue
+ # Revert to the old way but it is not really robust
+ ObjectSpace.each_object(::Class)
+ .select do |c|
+ Class === c && # 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.include?(ActiveRecord::Base)
+ 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
# We're passed a name of things that might be
# ActiveRecord models. If we can find the class, and
# if its a subclass of ActiveRecord::Base,
# then pass it to the associated block
- def do_annotations(options={})
- header = options[:format_markdown] ? PREFIX_MD.dup : PREFIX.dup
+ def do_annotations(options = {})
+ parse_options(options)
- if options[:include_version]
- version = ActiveRecord::Migrator.current_version rescue 0
- if version > 0
- header << "\n# Schema version: #{version}"
- end
+ header = options[:format_markdown] ? PREFIX_MD.dup : PREFIX.dup
+ version = ActiveRecord::Migrator.current_version rescue 0
+ if options[:include_version] && version > 0
+ header << "\n# Schema version: #{version}"
end
- self.model_dir = options[:model_dir] if options[:model_dir]
- self.root_dir = options[:root_dir] if options[:root_dir]
-
annotated = []
- get_model_files(options).each do |file|
- annotate_model_file(annotated, File.join(file), header, options)
+ get_model_files(options).each do |path, filename|
+ annotate_model_file(annotated, File.join(path, filename), header, options)
end
+
if annotated.empty?
- puts "Model files unchanged."
+ puts 'Model files unchanged.'
else
puts "Annotated (#{annotated.length}): #{annotated.join(', ')}"
end
@@ -549,27 +655,29 @@ def do_annotations(options={})
def annotate_model_file(annotated, file, header, options)
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)
- if klass && klass < ActiveRecord::Base && !klass.abstract_class? && klass.table_exists?
- if annotate(klass, file, header, options)
- annotated << file
- end
- end
+ do_annotate = klass &&
+ klass < ActiveRecord::Base &&
+ (!options[:exclude_sti_subclasses] || !(klass.superclass < ActiveRecord::Base && klass.table_name == klass.superclass.table_name)) &&
+ !klass.abstract_class? &&
+ klass.table_exists?
+
+ annotated.concat(annotate(klass, file, header, options)) if do_annotate
rescue BadModelFileError => e
unless options[:ignore_unknown_models]
puts "Unable to annotate #{file}: #{e.message}"
puts "\t" + e.backtrace.join("\n\t") if options[:trace]
end
- rescue Exception => e
+ rescue StandardError => e
puts "Unable to annotate #{file}: #{e.message}"
puts "\t" + e.backtrace.join("\n\t") if options[:trace]
end
end
- def remove_annotations(options={})
- self.model_dir = options[:model_dir] if options[:model_dir]
- self.root_dir = options[:root_dir] if options[:root_dir]
+ def remove_annotations(options = {})
+ parse_options(options)
+
deannotated = []
deannotated_klass = false
get_model_files(options).each do |file|
@@ -580,19 +688,19 @@ def remove_annotations(options={})
model_name = klass.name.underscore
table_name = klass.table_name
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.
- map { |f| resolve_filename(f, model_name, table_name) }.
- each do |f|
+ get_patterns(matched_types(options))
+ .map { |f| resolve_filename(f, model_name, table_name) }
+ .each do |f|
if File.exist?(f)
- remove_annotation_of_file(f)
+ remove_annotation_of_file(f, options)
deannotated_klass = true
end
end
end
- deannotated << klass if(deannotated_klass)
- rescue Exception => e
+ deannotated << klass if deannotated_klass
+ rescue StandardError => e
puts "Unable to deannotate #{File.join(file)}: #{e.message}"
puts "\t" + e.backtrace.join("\n\t") if options[:trace]
end
@@ -601,10 +709,10 @@ def remove_annotations(options={})
end
def resolve_filename(filename_template, model_name, table_name)
- return filename_template.
- gsub('%MODEL_NAME%', model_name).
- gsub('%PLURALIZED_MODEL_NAME%', model_name.pluralize).
- gsub('%TABLE_NAME%', table_name || model_name.pluralize)
+ filename_template
+ .gsub('%MODEL_NAME%', model_name)
+ .gsub('%PLURALIZED_MODEL_NAME%', model_name.pluralize)
+ .gsub('%TABLE_NAME%', table_name || model_name.pluralize)
end
def classified_sort(cols)
@@ -613,25 +721,26 @@ def classified_sort(cols)
associations = []
id = nil
- cols = cols.each do |c|
- if c.name.eql?("id")
+ cols.each do |c|
+ if c.name.eql?('id')
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
- elsif c.name[-3,3].eql?("_id")
+ elsif c.name[-3, 3].eql?('_id')
associations << c
else
rest_cols << c
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
# Ignore warnings for the duration of the block ()
def silence_warnings
- old_verbose, $VERBOSE = $VERBOSE, nil
+ old_verbose = $VERBOSE
+ $VERBOSE = nil
yield
ensure
$VERBOSE = old_verbose
diff --git a/lib/annotate/annotate_routes.rb b/lib/annotate/annotate_routes.rb
index e5fea8351..c1b0a9628 100644
--- a/lib/annotate/annotate_routes.rb
+++ b/lib/annotate/annotate_routes.rb
@@ -18,139 +18,181 @@
# Released under the same license as Ruby. No Support. No Warranty.
#
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={})
- return unless(routes_exists?)
+ class << self
+ 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
- # 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 \//)
-
- 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 != '')
+ def header(options = {})
+ routes_map = app_routes_map(options)
- # We're moving something from the top of the file to the bottom, so ditch
- # the spacer we put in the first time around.
- if(changed && where_header_found == :before)
- content.shift if(content.first == '')
+ out = ["# #{options[:format_markdown] ? PREFIX_MD : PREFIX}" + (options[:timestamp] ? " (Updated #{Time.now.strftime('%Y-%m-%d %H:%M')})" : '')]
+ out += ['#']
+ return out if routes_map.size.zero?
+
+ 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
- else
- header = header << '' if(content.first != '' || changed)
+
+ out + routes_map[1..-1].map { |line| "# #{content(options[:format_markdown] ? line.split(' ') : line, maxs, options)}" }
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)
- puts "#{routes_file} annotated."
- else
- puts "#{routes_file} unchanged."
+ if write_contents(existing_text, header(options), options)
+ puts "#{routes_file} annotated."
+ end
end
- end
- def self.remove_annotations(options={})
- return unless(routes_exists?)
- existing_text = File.read(routes_file)
- (content, where_header_found) = strip_annotations(existing_text)
+ def remove_annotations(options={})
+ return unless routes_exists?
+ existing_text = File.read(routes_file)
+ 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)
- puts "Removed annotations from #{routes_file}."
- else
- puts "#{routes_file} unchanged."
+ if write_contents(existing_text, content, options)
+ puts "Removed annotations from #{routes_file}."
+ 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
- @routes_rb ||= File.join("config", "routes.rb")
+ @routes_rb ||= File.join('config', 'routes.rb')
end
def self.routes_exists?
routes_exists = File.exists?(routes_file)
- puts "Can`t find routes.rb" if(!routes_exists)
- return routes_exists
+ puts "Can't find routes.rb" unless routes_exists
+
+ routes_exists
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.
- new_content << '' unless(new_content.last == '')
+ new_content << '' unless new_content.last == ''
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) }
- return true
+ new_content
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)
real_content = []
mode = :content
- line_number = 0
header_found_at = 0
- content.split(/\n/, -1).each do |line|
- line_number += 1
- begin
- if(mode == :header)
- if(line !~ /\s*#/)
- mode = :content
- raise unless (line == '')
- end
- elsif(mode == :content)
- if(line =~ /^\s*#\s*== Route.*$/)
- header_found_at = line_number
- mode = :header
- else
- real_content << line
- end
+
+ content.split(/\n/, -1).each_with_index do |line, line_number|
+ if mode == :header && line !~ /\s*#/
+ mode = :content
+ next unless line == ''
+ elsif mode == :content
+ if line =~ /^\s*#\s*== Route.*$/
+ header_found_at = line_number + 1 # index start's at 0
+ mode = :header
+ else
+ real_content << line
end
- rescue
- retry
end
end
- content_lines = real_content.count
- # By default assume the annotation was found in the middle of the file...
- where_header_found = header_found_at
+ where_header_found(real_content, 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 ...
- 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.
- 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
def self.strip_on_removal(content, where_header_found)
- if(where_header_found == :before)
- content.shift while(content.first == '')
- elsif(where_header_found == :after)
- content.pop while(content.last == '')
+ if where_header_found == :before
+ content.shift while content.first == ''
+ elsif where_header_found == :after
+ content.pop while content.last == ''
end
+
# 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: below...
- return content
+ content
end
end
diff --git a/lib/annotate/version.rb b/lib/annotate/version.rb
index ebcbe77de..fd53beb47 100644
--- a/lib/annotate/version.rb
+++ b/lib/annotate/version.rb
@@ -1,5 +1,5 @@
module Annotate
def self.version
- '2.7.0'
+ '2.7.2'
end
end
diff --git a/lib/generators/annotate/install_generator.rb b/lib/generators/annotate/install_generator.rb
index 903fda786..2d2d6ec97 100644
--- a/lib/generators/annotate/install_generator.rb
+++ b/lib/generators/annotate/install_generator.rb
@@ -1,14 +1,13 @@
module Annotate
module Generators
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__)
# copy rake 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
diff --git a/lib/generators/annotate/templates/auto_annotate_models.rake b/lib/generators/annotate/templates/auto_annotate_models.rake
index fc9c2d6a7..6f7c00641 100644
--- a/lib/generators/annotate/templates/auto_annotate_models.rake
+++ b/lib/generators/annotate/templates/auto_annotate_models.rake
@@ -2,44 +2,50 @@
# 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.
if Rails.env.development?
+ require 'annotate'
task :set_annotation_options do
# You can override any of these by setting an environment variable of the
# same name.
Annotate.set_defaults(
- 'routes' => 'false',
- 'position_in_routes' => 'before',
- 'position_in_class' => 'before',
- 'position_in_test' => 'before',
- 'position_in_fixture' => 'before',
- 'position_in_factory' => 'before',
- 'position_in_serializer' => 'before',
- 'show_foreign_keys' => 'true',
- 'show_indexes' => 'true',
- 'simple_indexes' => 'false',
- 'model_dir' => 'app/models',
- 'root_dir' => '',
- 'include_version' => 'false',
- 'require' => '',
- 'exclude_tests' => 'false',
- 'exclude_fixtures' => 'false',
- 'exclude_factories' => 'false',
- 'exclude_serializers' => 'false',
- 'exclude_scaffolds' => 'false',
- 'exclude_controllers' => 'false',
- 'exclude_helpers' => 'false',
- 'ignore_model_sub_dir' => 'false',
- 'ignore_columns' => nil,
- 'ignore_unknown_models' => 'false',
- 'hide_limit_column_types' => '<%= AnnotateModels::NO_LIMIT_COL_TYPES.join(',') %>',
- 'skip_on_db_migrate' => 'false',
- 'format_bare' => 'true',
- 'format_rdoc' => 'false',
- 'format_markdown' => 'false',
- 'sort' => 'false',
- 'force' => 'false',
- 'trace' => 'false',
- 'wrapper_open' => nil,
- 'wrapper_close' => nil,
+ 'routes' => 'false',
+ 'position_in_routes' => 'before',
+ 'position_in_class' => 'before',
+ 'position_in_test' => 'before',
+ 'position_in_fixture' => 'before',
+ 'position_in_factory' => 'before',
+ 'position_in_serializer' => 'before',
+ 'show_foreign_keys' => 'true',
+ 'show_complete_foreign_keys' => 'false',
+ 'show_indexes' => 'true',
+ 'simple_indexes' => 'false',
+ 'model_dir' => 'app/models',
+ 'root_dir' => '',
+ 'include_version' => 'false',
+ 'require' => '',
+ 'exclude_tests' => 'false',
+ 'exclude_fixtures' => 'false',
+ 'exclude_factories' => 'false',
+ 'exclude_serializers' => 'false',
+ 'exclude_scaffolds' => 'true',
+ 'exclude_controllers' => 'true',
+ 'exclude_helpers' => 'true',
+ 'exclude_sti_subclasses' => 'false',
+ 'ignore_model_sub_dir' => 'false',
+ 'ignore_columns' => nil,
+ 'ignore_routes' => nil,
+ 'ignore_unknown_models' => 'false',
+ 'hide_limit_column_types' => '<%= AnnotateModels::NO_LIMIT_COL_TYPES.join(",") %>',
+ 'hide_default_column_types' => '<%= AnnotateModels::NO_DEFAULT_COL_TYPES.join(",") %>',
+ 'skip_on_db_migrate' => 'false',
+ 'format_bare' => 'true',
+ 'format_rdoc' => 'false',
+ 'format_markdown' => 'false',
+ 'sort' => 'false',
+ 'force' => 'false',
+ 'trace' => 'false',
+ 'wrapper_open' => nil,
+ 'wrapper_close' => nil,
+ 'with_comment' => true
)
end
diff --git a/lib/tasks/annotate_models.rake b/lib/tasks/annotate_models.rake
index 45fee7428..95cee4768 100644
--- a/lib/tasks/annotate_models.rake
+++ b/lib/tasks/annotate_models.rake
@@ -1,16 +1,16 @@
annotate_lib = File.expand_path(File.dirname(File.dirname(__FILE__)))
-if !ENV['is_cli']
+unless ENV['is_cli']
task :set_annotation_options
- task :annotate_models => :set_annotation_options
+ task annotate_models: :set_annotation_options
end
-desc "Add schema information (as comments) to model and fixture files"
-task :annotate_models => :environment do
+desc 'Add schema information (as comments) to model and fixture files'
+task annotate_models: :environment do
require "#{annotate_lib}/annotate/annotate_models"
require "#{annotate_lib}/annotate/active_record_patch"
- options={ :is_rake => true }
+ options = {is_rake: true}
ENV['position'] = options[:position] = Annotate.fallback(ENV['position'], 'before')
options[:position_in_class] = Annotate.fallback(ENV['position_in_class'], ENV['position'])
options[:position_in_fixture] = Annotate.fallback(ENV['position_in_fixture'], ENV['position'])
@@ -18,10 +18,11 @@ task :annotate_models => :environment do
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[: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[:simple_indexes] = Annotate.true?(ENV['simple_indexes'])
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[:require] = ENV['require'] ? ENV['require'].split(',') : []
options[:exclude_tests] = Annotate.true?(ENV['exclude_tests'])
@@ -29,8 +30,9 @@ task :annotate_models => :environment do
options[:exclude_fixtures] = Annotate.true?(ENV['exclude_fixtures'])
options[:exclude_serializers] = Annotate.true?(ENV['exclude_serializers'])
options[:exclude_scaffolds] = Annotate.true?(ENV['exclude_scaffolds'])
- options[:exclude_controllers] = Annotate.true?(ENV['exclude_controllers'])
- options[:exclude_helpers] = Annotate.true?(ENV['exclude_helpers'])
+ options[:exclude_controllers] = Annotate.true?(ENV.fetch('exclude_controllers', 'true'))
+ 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[:format_bare] = Annotate.true?(ENV['format_bare'])
options[:format_rdoc] = Annotate.true?(ENV['format_rdoc'])
@@ -42,16 +44,19 @@ task :annotate_models => :environment do
options[:wrapper_open] = Annotate.fallback(ENV['wrapper_open'], ENV['wrapper'])
options[:wrapper_close] = Annotate.fallback(ENV['wrapper_close'], ENV['wrapper'])
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)
end
-desc "Remove schema information from model and fixture files"
-task :remove_annotation => :environment do
+desc 'Remove schema information from model and fixture files'
+task remove_annotation: :environment do
require "#{annotate_lib}/annotate/annotate_models"
require "#{annotate_lib}/annotate/active_record_patch"
- options={ :is_rake => true }
+ options = {is_rake: true}
options[:model_dir] = ENV['model_dir']
options[:root_dir] = ENV['root_dir']
options[:require] = ENV['require'] ? ENV['require'].split(',') : []
diff --git a/lib/tasks/migrate.rake b/lib/tasks/annotate_models_migrate.rake
similarity index 100%
rename from lib/tasks/migrate.rake
rename to lib/tasks/annotate_models_migrate.rake
diff --git a/lib/tasks/annotate_routes.rake b/lib/tasks/annotate_routes.rake
index 9605fd339..26a99fecc 100644
--- a/lib/tasks/annotate_routes.rake
+++ b/lib/tasks/annotate_routes.rake
@@ -6,6 +6,7 @@ task :annotate_routes => :environment do
options={}
ENV['position'] = options[:position] = Annotate.fallback(ENV['position'], 'before')
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(',') : []
AnnotateRoutes.do_annotations(options)
end
diff --git a/potato.md b/potato.md
new file mode 100644
index 000000000..278088f5c
--- /dev/null
+++ b/potato.md
@@ -0,0 +1,41 @@
+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`**
diff --git a/spec/annotate/annotate_models_spec.rb b/spec/annotate/annotate_models_spec.rb
old mode 100755
new mode 100644
index b56bc34e5..ccbc53a46
--- a/spec/annotate/annotate_models_spec.rb
+++ b/spec/annotate/annotate_models_spec.rb
@@ -1,74 +1,83 @@
-#encoding: utf-8
+# encoding: utf-8
require File.dirname(__FILE__) + '/../spec_helper.rb'
require 'annotate/annotate_models'
require 'annotate/active_record_patch'
require 'active_support/core_ext/string'
describe AnnotateModels do
- def mock_foreign_key(name, from_column, to_table, to_column = 'id')
- double("ForeignKeyDefinition",
- :name => name,
- :column => from_column,
- :to_table => to_table,
- :primary_key => to_column,
- )
+ def mock_index(name, columns = [], unique = false)
+ double('IndexKeyDefinition',
+ name: name,
+ columns: columns,
+ unique: unique)
+ 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
def mock_connection(indexes = [], foreign_keys = [])
- double("Conn",
- :indexes => indexes,
- :foreign_keys => foreign_keys,
- :supports_foreign_keys? => true,
- )
+ double('Conn',
+ indexes: indexes,
+ foreign_keys: foreign_keys,
+ supports_foreign_keys?: true)
end
- def mock_class(table_name, primary_key, columns, foreign_keys = [])
+ def mock_class(table_name, primary_key, columns, indexes = [], foreign_keys = [])
options = {
- :connection => mock_connection([], foreign_keys),
- :table_exists? => true,
- :table_name => table_name,
- :primary_key => primary_key,
- :column_names => columns.map { |col| col.name.to_s },
- :columns => columns,
- :column_defaults => Hash[columns.map { |col|
- [col.name, col.default]
- }]
+ connection: mock_connection(indexes, foreign_keys),
+ table_exists?: true,
+ table_name: table_name,
+ primary_key: primary_key,
+ column_names: columns.map { |col| col.name.to_s },
+ columns: columns,
+ column_defaults: Hash[columns.map { |col| [col.name, col.default] }],
+ table_name_prefix: ''
}
- double("An ActiveRecord class", options)
+ double('An ActiveRecord class', options)
end
- def mock_column(name, type, options={})
+ def mock_column(name, type, options = {})
default_options = {
- :limit => nil,
- :null => false,
- :default => nil
+ limit: nil,
+ null: false,
+ default: nil
}
stubs = default_options.dup
stubs.merge!(options)
- stubs.merge!(:name => name, :type => type)
+ stubs[:name] = name
+ stubs[:type] = type
- double("Column", stubs)
+ double('Column', stubs)
end
- it { expect(AnnotateModels.quote(nil)).to eql("NULL") }
- it { expect(AnnotateModels.quote(true)).to eql("TRUE") }
- it { expect(AnnotateModels.quote(false)).to eql("FALSE") }
- it { expect(AnnotateModels.quote(25)).to eql("25") }
- 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(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
- klass = mock_class(:users, :id, [
- 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)
+ it { expect(AnnotateModels.quote(nil)).to eql('NULL') }
+ it { expect(AnnotateModels.quote(true)).to eql('TRUE') }
+ it { expect(AnnotateModels.quote(false)).to eql('FALSE') }
+ it { expect(AnnotateModels.quote(25)).to eql('25') }
+ 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(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
+ klass = mock_class(:users,
+ :id,
+ [
+ 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)
# Schema Info
#
# Table name: users
@@ -80,13 +89,15 @@ def mock_column(name, type, options={})
EOS
end
- it "should get schema info even if the primary key is not set" do
- klass = mock_class(:users, nil, [
- mock_column(:id, :integer),
- mock_column(:name, :string, :limit => 50)
- ])
+ it 'should get schema info even if the primary key is not set' do
+ klass = mock_class(:users,
+ nil,
+ [
+ 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
#
# Table name: users
@@ -97,14 +108,16 @@ def mock_column(name, type, options={})
EOS
end
- 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], [
- mock_column(:a_id, :integer),
- mock_column(:b_id, :integer),
- mock_column(:name, :string, :limit => 50)
- ])
+ 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],
+ [
+ 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
#
# Table name: users
@@ -115,13 +128,16 @@ def mock_column(name, type, options={})
#
EOS
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
#
# Table name: users
@@ -132,13 +148,40 @@ def mock_column(name, type, options={})
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)
+ it 'should get schema info with unsigned' do
+ klass = mock_class(:users,
+ nil,
+ [
+ mock_column(:id, :integer),
+ mock_column(:integer, :integer, unsigned?: true),
+ 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
#
# Table name: users
@@ -150,19 +193,26 @@ def mock_column(name, type, options={})
EOS
end
- it "should get foreign key info" 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'
- )
- ])
- expect(AnnotateModels.get_schema_info(klass, "Schema Info", :show_foreign_keys => true)).to eql(<<-EOS)
+ it 'should get foreign key info' do
+ klass = mock_class(:users,
+ :id,
+ [
+ mock_column(:id, :integer),
+ 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)).to eql(<<-EOS)
# Schema Info
#
# Table name: users
@@ -172,17 +222,168 @@ def mock_column(name, type, options={})
#
# 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
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)
+ it 'should get complete foreign key info' do
+ klass = mock_class(:users,
+ :id,
+ [
+ mock_column(:id, :integer),
+ 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}
#
# Table name: users
@@ -195,76 +396,302 @@ def mock_column(name, type, options={})
EOS
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 = {})
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
- klass = mock_class(:users, :id, [
- mock_column(:id, :integer, :limit => 8),
- mock_column(:active, :boolean, :limit => 1),
- mock_column(:name, :string, :limit => 50),
- mock_column(:notes, :text, :limit => 55),
- ])
- schema_info = AnnotateModels.get_schema_info(klass, "Schema Info", options)
+ with_columns = (options.delete(:with_columns) || default_columns).map do |column|
+ mock_column(column[0], column[1], column[2])
+ end
+
+ klass = mock_class(:users, :id, with_columns)
+
+ schema_info = AnnotateModels.get_schema_info(klass, 'Schema Info', options)
expect(schema_info).to eql(expected)
end
end
- when_called_with hide_limit_column_types: '', returns: <<-EOS.strip_heredoc
- # Schema Info
- #
- # Table name: users
- #
- # id :integer not null, primary key
- # active :boolean not null
- # name :string(50) not null
- # notes :text(55) not null
- #
- EOS
+ describe 'hide_limit_column_types option' do
+ when_called_with hide_limit_column_types: '', returns: <<-EOS.strip_heredoc
+ # Schema Info
+ #
+ # Table name: users
+ #
+ # id :integer not null, primary key
+ # active :boolean not null
+ # name :string(50) not null
+ # notes :text(55) not null
+ #
+ EOS
- when_called_with hide_limit_column_types: 'integer,boolean', returns:
- <<-EOS.strip_heredoc
- # Schema Info
- #
- # Table name: users
- #
- # id :integer not null, primary key
- # active :boolean not null
- # name :string(50) not null
- # notes :text(55) not null
- #
- EOS
-
- when_called_with hide_limit_column_types: 'integer,boolean,string,text', returns:
- <<-EOS.strip_heredoc
- # Schema Info
- #
- # Table name: users
- #
- # id :integer not null, primary key
- # active :boolean not null
- # name :string not null
- # notes :text not null
- #
- EOS
- end
+ when_called_with hide_limit_column_types: 'integer,boolean', returns:
+ <<-EOS.strip_heredoc
+ # Schema Info
+ #
+ # Table name: users
+ #
+ # id :integer not null, primary key
+ # active :boolean not null
+ # name :string(50) not null
+ # notes :text(55) not null
+ #
+ EOS
+
+ when_called_with hide_limit_column_types: 'integer,boolean,string,text',
+ returns:
+ <<-EOS.strip_heredoc
+ # Schema Info
+ #
+ # Table name: users
+ #
+ # id :integer not null, primary key
+ # active :boolean not null
+ # name :string not null
+ # notes :text not null
+ #
+ EOS
+ end
- describe "#get_model_class" do
- require "tmpdir"
+ describe 'hide_default_column_types option' do
+ 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
- class Base
- def self.has_many name
- end
+ when_called_with hide_default_column_types: 'skip',
+ with_columns: mocked_columns_without_id,
+ returns:
+ <<-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)*:: integer, not null, primary key
+ # *name(Name)*:: string(50), not null
+ #--
+ # #{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
+
+ describe '#get_model_class' do
+ require 'tmpdir'
- # todo: use 'files' gem instead
- def create(file, body="hi")
+ # TODO: use 'files' gem instead
+ def create(file, body = 'hi')
file_path = File.join(AnnotateModels.model_dir[0], file)
FileUtils.mkdir_p(File.dirname(file_path))
- File.open(file_path, "wb") do |f|
+ File.open(file_path, 'wb') do |f|
f.puts(body)
end
file_path
@@ -281,7 +708,7 @@ def check_class_name(file, class_name)
AnnotateModels.model_dir = Dir.mktmpdir 'annotate_models'
end
- it "should work" do
+ it 'should work' do
create 'foo.rb', <<-EOS
class Foo < ActiveRecord::Base
end
@@ -289,7 +716,7 @@ class Foo < ActiveRecord::Base
check_class_name 'foo.rb', 'Foo'
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
class FooWithCAPITALS < ActiveRecord::Base
end
@@ -297,7 +724,7 @@ class FooWithCAPITALS < ActiveRecord::Base
check_class_name 'foo_with_capitals.rb', 'FooWithCAPITALS'
end
- it "should find models inside modules" do
+ it 'should find models inside modules' do
create 'bar/foo_inside_bar.rb', <<-EOS
module Bar
class FooInsideBar < ActiveRecord::Base
@@ -307,7 +734,7 @@ class FooInsideBar < ActiveRecord::Base
check_class_name 'bar/foo_inside_bar.rb', 'Bar::FooInsideBar'
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
class Foo < ActiveRecord::Base
end
@@ -321,7 +748,7 @@ class Bar::Foo
check_class_name 'foo.rb', 'Foo'
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
class Voucher < ActiveRecord::Base
end
@@ -338,8 +765,7 @@ class Foo
check_class_name 'voucher/foo.rb', 'Voucher::Foo'
end
-
- it "should not care about unknown macros" do
+ it 'should not care about unknown macros' do
create 'foo_with_macro.rb', <<-EOS
class FooWithMacro < ActiveRecord::Base
acts_as_awesome :yah
@@ -348,7 +774,7 @@ class FooWithMacro < ActiveRecord::Base
check_class_name 'foo_with_macro.rb', 'FooWithMacro'
end
- it "should not care about known macros" do
+ it 'should not care about known macros' do
create('foo_with_known_macro.rb', <<-EOS)
class FooWithKnownMacro < ActiveRecord::Base
has_many :yah
@@ -357,7 +783,7 @@ class FooWithKnownMacro < ActiveRecord::Base
check_class_name 'foo_with_known_macro.rb', 'FooWithKnownMacro'
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)
class FooWithCAPITALS < ActiveRecord::Base
acts_as_awesome :yah
@@ -366,7 +792,7 @@ class FooWithCAPITALS < ActiveRecord::Base
check_class_name 'foo_with_capitals.rb', 'FooWithCAPITALS'
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
#encoding: utf-8
class FooWithUtf8 < ActiveRecord::Base
@@ -376,7 +802,7 @@ class FooWithUtf8 < ActiveRecord::Base
check_class_name 'foo_with_utf8.rb', 'FooWithUtf8'
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
module BAR
class FooInsideCapitalsBAR < ActiveRecord::Base
@@ -386,7 +812,7 @@ class FooInsideCapitalsBAR < ActiveRecord::Base
check_class_name 'bar/foo_inside_capitals_bar.rb', 'BAR::FooInsideCapitalsBAR'
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
class NonNamespacedFooInsideBar < ActiveRecord::Base
end
@@ -394,7 +820,7 @@ class NonNamespacedFooInsideBar < ActiveRecord::Base
check_class_name 'bar/non_namespaced_foo_inside_bar.rb', 'NonNamespacedFooInsideBar'
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
class NonNamespacedFooWithCapitalsInsideBar < ActiveRecord::Base
end
@@ -402,7 +828,7 @@ class NonNamespacedFooWithCapitalsInsideBar < ActiveRecord::Base
check_class_name 'bar/non_namespaced_foo_with_capitals_inside_bar.rb', 'NonNamespacedFooWithCapitalsInsideBar'
end
- it "should allow known macros" do
+ it 'should allow known macros' do
create('foo_with_known_macro.rb', <<-EOS)
class FooWithKnownMacro < ActiveRecord::Base
has_many :yah
@@ -410,10 +836,10 @@ class FooWithKnownMacro < ActiveRecord::Base
EOS
expect(capturing(:stderr) do
check_class_name 'foo_with_known_macro.rb', 'FooWithKnownMacro'
- end).to eq("")
+ end).to eq('')
end
- it "should not require model files twice" do
+ it 'should not require model files twice' do
create 'loaded_class.rb', <<-EOS
class LoadedClass < ActiveRecord::Base
CONSTANT = 1
@@ -423,21 +849,22 @@ class LoadedClass < ActiveRecord::Base
Kernel.load "#{path}.rb"
expect(Kernel).not_to receive(:require).with(path)
- expect(capturing(:stderr) {
+ expect(capturing(:stderr) do
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
- describe "#remove_annotation_of_file" do
- require "tmpdir"
+ describe '#remove_annotation_of_file' do
+ require 'tmpdir'
- def create(file, body="hi")
+ def create(file, body = 'hi')
path = File.join(@dir, file)
- File.open(path, "w") do |f|
+ File.open(path, 'w') do |f|
f.puts(body)
end
- return path
+
+ path
end
def content(path)
@@ -448,8 +875,8 @@ def content(path)
@dir = Dir.mktmpdir 'annotate_models'
end
- it "should remove before annotate" do
- path = create "before.rb", <<-EOS
+ it 'should remove before annotate' do
+ path = create 'before.rb', <<-EOS
# == Schema Information
#
# Table name: foo
@@ -471,8 +898,8 @@ class Foo < ActiveRecord::Base
EOS
end
- it "should remove after annotate" do
- path = create "after.rb", <<-EOS
+ it 'should remove after annotate' do
+ path = create 'after.rb', <<-EOS
class Foo < ActiveRecord::Base
end
@@ -491,13 +918,61 @@ class Foo < ActiveRecord::Base
expect(content(path)).to eq <<-EOS
class Foo < ActiveRecord::Base
+end
+ EOS
+ 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
describe '#resolve_filename' do
-
it 'should return the test path for a model' do
filename_template = 'test/unit/%MODEL_NAME%_test.rb'
model_name = 'example_model'
@@ -525,30 +1000,33 @@ class Foo < ActiveRecord::Base
expect(filename). to eq 'test/fixtures/parent/children.yml'
end
end
- describe "annotating a file" do
+ describe 'annotating a file' do
before do
@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
end
EOS
- @klass = mock_class(:users, :id, [
- mock_column(:id, :integer),
- mock_column(:name, :string, :limit => 50)
- ])
- @schema_info = AnnotateModels.get_schema_info(@klass, "== Schema Info")
+ @klass = mock_class(:users,
+ :id,
+ [
+ mock_column(:id, :integer),
+ mock_column(:name, :string, limit: 50)
+ ])
+ @schema_info = AnnotateModels.get_schema_info(@klass, '== Schema Info')
Annotate.reset_options
end
- def write_model file_name, file_content
+ def write_model(file_name, file_content)
fname = File.join(@model_dir, file_name)
FileUtils.mkdir_p(File.dirname(fname))
- File.open(fname, "wb") { |f| f.write file_content }
- return fname, file_content
+ File.open(fname, 'wb') { |f| f.write file_content }
+
+ [fname, file_content]
end
- def annotate_one_file options = {}
+ def annotate_one_file(options = {})
Annotate.set_defaults(options)
options = Annotate.setup_options(options)
AnnotateModels.annotate_one_file(@model_file_name, @schema_info, :position_in_class, options)
@@ -572,135 +1050,112 @@ def magic_comments_list_each
"# frozen_string_literal: true\n# encoding: utf-8",
'# frozen_string_literal: true',
'#frozen_string_literal: false',
- '# -*- frozen_string_literal : true -*-',
- ].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}")
+ '# -*- frozen_string_literal : true -*-'
+ ].each { |magic_comment| yield magic_comment }
end
- it "should put annotation after class if :position => 'bottom'" do
- annotate_one_file :position => 'bottom'
- expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}")
+ ['before', :before, 'top', :top].each do |position|
+ it "should put annotation before class if :position == #{position}" do
+ annotate_one_file position: position
+ expect(File.read(@model_file_name))
+ .to eq("#{@schema_info}\n#{@file_content}")
+ end
end
- it "should put annotation after class if :position => :bottom" do
- annotate_one_file :position => :bottom
- expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}")
+ ['after', :after, 'bottom', :bottom].each do |position|
+ it "should put annotation after class if position: #{position}" do
+ annotate_one_file position: position
+ expect(File.read(@model_file_name))
+ .to eq("#{@file_content}\n#{@schema_info}")
+ end
end
it 'should wrap annotation if wrapper is specified' do
- 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}")
+ 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}")
end
- describe "with existing annotation => :before" do
+ describe 'with existing annotation => :before' do
before do
- annotate_one_file :position => :before
- another_schema_info = AnnotateModels.get_schema_info(mock_class(:users, :id, [mock_column(:id, :integer),]),
- "== Schema Info")
+ annotate_one_file position: :before
+ another_schema_info = AnnotateModels.get_schema_info(mock_class(:users, :id, [mock_column(:id, :integer)]), '== Schema Info')
@schema_info = another_schema_info
end
- it "should retain current position" do
+ it 'should retain current position' do
annotate_one_file
expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}")
end
- it "should retain current position even when :position is changed to :after" do
- annotate_one_file :position => :after
+ it 'should retain current position even when :position is changed to :after' do
+ annotate_one_file position: :after
expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}")
end
- it "should change position to :after when :force => true" do
- annotate_one_file :position => :after, :force => true
+ it 'should change position to :after when force: true' do
+ annotate_one_file position: :after, force: true
expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}")
end
end
- describe "with existing annotation => :after" do
+ describe 'with existing annotation => :after' do
before do
- annotate_one_file :position => :after
- another_schema_info = AnnotateModels.get_schema_info(mock_class(:users, :id, [mock_column(:id, :integer),]),
- "== Schema Info")
+ annotate_one_file position: :after
+ another_schema_info = AnnotateModels.get_schema_info(mock_class(:users, :id, [mock_column(:id, :integer)]), '== Schema Info')
@schema_info = another_schema_info
end
- it "should retain current position" do
+ it 'should retain current position' do
annotate_one_file
expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}")
end
- it "should retain current position even when :position is changed to :before" do
- annotate_one_file :position => :before
+ it 'should retain current position even when :position is changed to :before' do
+ annotate_one_file position: :before
expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}")
end
- it "should change position to :before when :force => true" do
- annotate_one_file :position => :before, :force => true
+ it 'should change position to :before when force: true' do
+ annotate_one_file position: :before, force: true
expect(File.read(@model_file_name)).to eq("#{@schema_info}\n#{@file_content}")
end
end
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)')
expect(output.match(/id/)).to be_nil
end
- it "works with namespaced models (i.e. models inside modules/subdirectories)" do
- (model_file_name, file_content) = write_model "foo/user.rb", <<-EOS
+ it 'works with namespaced models (i.e. models inside modules/subdirectories)' do
+ (model_file_name, file_content) = write_model 'foo/user.rb', <<-EOS
class Foo::User < ActiveRecord::Base
end
EOS
- klass = mock_class(:'foo_users', :id, [
- mock_column(:id, :integer),
- mock_column(:name, :string, :limit => 50)
- ])
- schema_info = AnnotateModels.get_schema_info(klass, "== Schema Info")
- AnnotateModels.annotate_one_file(model_file_name, schema_info, :position => :before)
+ klass = mock_class(:'foo_users',
+ :id,
+ [
+ mock_column(:id, :integer),
+ mock_column(:name, :string, limit: 50)
+ ])
+ 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}")
end
- it "should not touch magic comments" do
+ it 'should not touch magic comments' do
magic_comments_list_each do |magic_comment|
- write_model "user.rb", <<-EOS
+ write_model 'user.rb', <<-EOS
#{magic_comment}
class User < ActiveRecord::Base
end
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|
lines.count.times do |index|
expect(file.readline).to eq "#{lines[index]}\n"
@@ -710,69 +1165,69 @@ class User < ActiveRecord::Base
end
describe "if a file can't be annotated" do
- before do
- allow(AnnotateModels).to receive(:get_loaded_model).with('user').and_return(nil)
-
- write_model('user.rb', <<-EOS)
- class User < ActiveRecord::Base
- raise "oops"
- end
- EOS
- end
-
- it "displays an error message" do
- expect(capturing(:stdout) {
- AnnotateModels.do_annotations :model_dir => @model_dir, :is_rake => true
- }).to include("Unable to annotate #{@model_dir}/user.rb: oops")
- end
-
- it "displays the full stack trace with --trace" do
- expect(capturing(:stdout) {
- AnnotateModels.do_annotations :model_dir => @model_dir, :trace => true, :is_rake => true
- }).to include("/spec/annotate/annotate_models_spec.rb:")
- end
-
- it "omits the full stack trace without --trace" do
- expect(capturing(:stdout) {
- AnnotateModels.do_annotations :model_dir => @model_dir, :trace => false, :is_rake => true
- }).not_to include("/spec/annotate/annotate_models_spec.rb:")
- end
+ before do
+ allow(AnnotateModels).to receive(:get_loaded_model).with('user').and_return(nil)
+
+ write_model('user.rb', <<-EOS)
+ class User < ActiveRecord::Base
+ raise "oops"
+ end
+ EOS
+ end
+
+ it 'displays an error message' do
+ expect(capturing(:stdout) do
+ AnnotateModels.do_annotations model_dir: @model_dir, is_rake: true
+ end).to include("Unable to annotate #{@model_dir}/user.rb: oops")
+ end
+
+ it 'displays the full stack trace with --trace' do
+ expect(capturing(:stdout) do
+ AnnotateModels.do_annotations model_dir: @model_dir, trace: true, is_rake: true
+ end).to include('/spec/annotate/annotate_models_spec.rb:')
+ end
+
+ it 'omits the full stack trace without --trace' do
+ expect(capturing(:stdout) do
+ AnnotateModels.do_annotations model_dir: @model_dir, trace: false, is_rake: true
+ end).not_to include('/spec/annotate/annotate_models_spec.rb:')
+ end
end
describe "if a file can't be deannotated" do
- before do
- allow(AnnotateModels).to receive(:get_loaded_model).with('user').and_return(nil)
-
- write_model('user.rb', <<-EOS)
- class User < ActiveRecord::Base
- raise "oops"
- end
- EOS
- end
-
- it "displays an error message" do
- expect(capturing(:stdout) {
- AnnotateModels.remove_annotations :model_dir => @model_dir, :is_rake => true
- }).to include("Unable to deannotate #{@model_dir}/user.rb: oops")
- end
-
- it "displays the full stack trace" do
- expect(capturing(:stdout) {
- AnnotateModels.remove_annotations :model_dir => @model_dir, :trace => true, :is_rake => true
- }).to include("/user.rb:2:in `'")
- end
-
- it "omits the full stack trace without --trace" do
- expect(capturing(:stdout) {
- AnnotateModels.remove_annotations :model_dir => @model_dir, :trace => false, :is_rake => true
- }).not_to include("/user.rb:2:in `'")
- end
+ before do
+ allow(AnnotateModels).to receive(:get_loaded_model).with('user').and_return(nil)
+
+ write_model('user.rb', <<-EOS)
+ class User < ActiveRecord::Base
+ raise "oops"
+ end
+ EOS
+ end
+
+ it 'displays an error message' do
+ expect(capturing(:stdout) do
+ AnnotateModels.remove_annotations model_dir: @model_dir, is_rake: true
+ end).to include("Unable to deannotate #{@model_dir}/user.rb: oops")
+ end
+
+ it 'displays the full stack trace' do
+ expect(capturing(:stdout) do
+ AnnotateModels.remove_annotations model_dir: @model_dir, trace: true, is_rake: true
+ end).to include("/user.rb:2:in `'")
+ end
+
+ it 'omits the full stack trace without --trace' do
+ expect(capturing(:stdout) do
+ AnnotateModels.remove_annotations model_dir: @model_dir, trace: false, is_rake: true
+ end).not_to include("/user.rb:2:in `'")
+ end
end
end
describe '.annotate_model_file' 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(Foo).to receive(:table_exists?) { false }
end
@@ -780,7 +1235,7 @@ class Foo < ActiveRecord::Base; end;
after { Object.send :remove_const, 'Foo' }
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
end
diff --git a/spec/annotate/annotate_routes_spec.rb b/spec/annotate/annotate_routes_spec.rb
index 27f3ac351..037a4a2bf 100644
--- a/spec/annotate/annotate_routes_spec.rb
+++ b/spec/annotate/annotate_routes_spec.rb
@@ -2,118 +2,168 @@
require 'annotate/annotate_routes'
describe AnnotateRoutes do
- ROUTE_FILE = "config/routes.rb"
- ANNOTATION_ADDED = "#{ROUTE_FILE} annotated."
- ANNOTATION_REMOVED = "Removed annotations from #{ROUTE_FILE}."
- FILE_UNCHANGED = "#{ROUTE_FILE} unchanged."
+ ROUTE_FILE = 'config/routes.rb'.freeze
+ ANNOTATION_ADDED = "#{ROUTE_FILE} annotated.".freeze
+ ANNOTATION_REMOVED = "Removed annotations from #{ROUTE_FILE}.".freeze
+ FILE_UNCHANGED = "#{ROUTE_FILE} unchanged.".freeze
- def mock_file(stubs={})
+ def mock_file(stubs = {})
@mock_file ||= double(File, stubs)
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(AnnotateRoutes).to receive(:puts).with("Can`t find routes.rb")
+ expect(AnnotateRoutes).to receive(:puts).with("Can't find routes.rb")
AnnotateRoutes.do_annotations
end
- describe "When adding" do
+ describe 'Annotate#example' do
before(:each) do
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
- 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(: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(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED)
AnnotateRoutes.do_annotations
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(AnnotateRoutes).to receive(:puts).with(FILE_UNCHANGED)
AnnotateRoutes.do_annotations
end
-
end
- describe "When adding with older Rake versions" do
-
+ describe 'When adding with older Rake versions' do
before(:each) do
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(File).to receive(:open).with(ROUTE_FILE, "wb").and_yield(mock_file)
+ 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(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED)
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(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map\n#\n# good line\n/)
AnnotateRoutes.do_annotations
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(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n# == Route Map\n#\n# good line\n/)
AnnotateRoutes.do_annotations
end
-
end
- describe "When adding with newer Rake versions" do
-
+ describe 'When adding with newer Rake versions' do
before(:each) do
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(File).to receive(:open).with(ROUTE_FILE, "wb").and_yield(mock_file)
+ 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(AnnotateRoutes).to receive(:puts).with(ANNOTATION_ADDED)
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(@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
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(@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
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(@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
- describe "When removing" do
-
+ describe 'When removing' do
before(:each) do
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)
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(@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
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(@mock_file).to receive(:puts).with(/ActionController::Routing...\nfoo\n\n\n\n\n\n\n\n\n\n\n/)
AnnotateRoutes.remove_annotations
end
-
end
-
end
diff --git a/spec/annotate_spec.rb b/spec/annotate_spec.rb
index e1ce2dbc9..542ac63ba 100644
--- a/spec/annotate_spec.rb
+++ b/spec/annotate_spec.rb
@@ -1,9 +1,7 @@
require File.dirname(__FILE__) + '/spec_helper.rb'
describe Annotate do
-
- it "should have a version" do
+ it 'should have a version' do
expect(Annotate.version).to be_instance_of(String)
end
-
end
diff --git a/spec/fixtures/rails_32_schema.rb b/spec/fixtures/rails_32_schema.rb
index 758d09c60..26ccdc90e 100644
--- a/spec/fixtures/rails_32_schema.rb
+++ b/spec/fixtures/rails_32_schema.rb
@@ -12,11 +12,9 @@
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20120816164927) do
-
create_table "tasks", :force => true do |t|
t.string "content"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
-
end
diff --git a/spec/integration/common_validation.rb b/spec/integration/common_validation.rb
index c941a7f4c..6145e851a 100644
--- a/spec/integration/common_validation.rb
+++ b/spec/integration/common_validation.rb
@@ -2,10 +2,7 @@ module Annotate
module Validations
module Common
def self.test_commands
- return %q{
- bin/annotate &&
- bin/annotate --routes
- }
+ 'bin/annotate && bin/annotate --routes'
end
def self.verify_output(output)
@@ -13,54 +10,52 @@ def self.verify_output(output)
output.should =~ /Route file annotated./
end
- 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_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_factory(test_rig, schema_annotation, place_before) if(which_files[:factory])
- check_routes(test_rig, routes_annotation, place_before) if(which_files[:routes])
+ 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_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_factory(test_rig, schema_annotation, place_before) if which_files[:factory]
+ check_routes(test_rig, routes_annotation, place_before) if which_files[:routes]
end
- def self.check_task_model(test_rig, annotation, place_before=true)
- model = apply_annotation(test_rig, "app/models/task.rb", annotation, place_before)
- File.read("app/models/task.rb").should == model
+ def self.check_task_model(test_rig, annotation, place_before = true)
+ model = apply_annotation(test_rig, 'app/models/task.rb', annotation, place_before)
+ File.read('app/models/task.rb').should == model
end
- def self.check_routes(test_rig, annotation, place_before=true)
- routes = apply_annotation(test_rig, "config/routes.rb", annotation, place_before)
- File.read("config/routes.rb").
- sub(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}/, 'YYYY-MM-DD HH:MM').
- should == routes
+ def self.check_routes(test_rig, annotation, place_before = true)
+ routes = apply_annotation(test_rig, 'config/routes.rb', annotation, place_before)
+ File.read('config/routes.rb')
+ .sub(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}/, 'YYYY-MM-DD HH:MM')
+ .should == routes
end
- def self.check_task_unittest(test_rig, annotation, place_before=true)
- unittest = apply_annotation(test_rig, "test/unit/task_test.rb", annotation, place_before)
- File.read("test/unit/task_test.rb").should == unittest
+ def self.check_task_unittest(test_rig, annotation, place_before = true)
+ unittest = apply_annotation(test_rig, 'test/unit/task_test.rb', annotation, place_before)
+ File.read('test/unit/task_test.rb').should == unittest
end
def self.check_task_modeltest(test_rig, annotation, place_before=true)
- unittest = apply_annotation(test_rig, "test/models/task_test.rb", annotation, place_before)
- File.read("test/models/task_test.rb").should == unittest
+ unittest = apply_annotation(test_rig, 'test/models/task_test.rb', annotation, place_before)
+ File.read('test/models/task_test.rb').should == unittest
end
def self.check_task_factory(test_rig, annotation, place_before=true)
- fixture = apply_annotation(test_rig, "test/factories/tasks.rb", annotation, place_before)
- File.read("test/factories/tasks.rb").should == fixture
+ fixture = apply_annotation(test_rig, 'test/factories/tasks.rb', annotation, place_before)
+ File.read('test/factories/tasks.rb').should == fixture
end
- def self.check_task_fixture(test_rig, annotation, place_before=true)
- fixture = apply_annotation(test_rig, "test/fixtures/tasks.yml", annotation, place_before)
- File.read("test/fixtures/tasks.yml").should == fixture
+ def self.check_task_fixture(test_rig, annotation, place_before = true)
+ fixture = apply_annotation(test_rig, 'test/fixtures/tasks.yml', annotation, place_before)
+ File.read('test/fixtures/tasks.yml').should == fixture
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))
- if(place_before)
- corpus = annotation + "\n" + corpus
+ if place_before
+ annotation + "\n" + corpus
else
- corpus = corpus + "\n" + annotation
+ corpus + "\n" + annotation
end
end
end
@@ -71,27 +66,28 @@ module Annotate
module Validations
class Base
def self.test_commands
- return Annotate::Validations::Common.test_commands
+ Annotate::Validations::Common.test_commands
end
def self.verify_output(output)
- return Annotate::Validations::Common.verify_output(output)
+ Annotate::Validations::Common.verify_output(output)
end
def self.verify_files(test_rig)
- return Annotate::Validations::Common.verify_files({
- :model => true,
- :test => true,
- :fixture => true,
- :factory => false,
- :routes => true
- }, test_rig, self.schema_annotation, self.route_annotation, true)
+ Annotate::Validations::Common.verify_files(
+ {
+ model: true,
+ test: true,
+ fixture: true,
+ factory: false,
+ routes: true
+ }, test_rig, schema_annotation, route_annotation, true
+ )
end
def self.foo
require 'pry'
require 'pry-coolline'
- binding.pry
end
end
end
diff --git a/spec/integration/integration_spec.rb b/spec/integration/integration_spec.rb
index d76b4277b..79f57493d 100644
--- a/spec/integration/integration_spec.rb
+++ b/spec/integration/integration_spec.rb
@@ -1,5 +1,5 @@
# 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 'files'
require 'wrong'
@@ -7,7 +7,7 @@
include Files
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
USING_RVM = (RVM_BIN != '')
@@ -16,25 +16,24 @@
ENV['BUNDLE_GEMFILE'] = './Gemfile'
describe "annotate inside Rails, using #{CURRENT_RUBY}" do
-
- here = File.expand_path('..', __FILE__)
chosen_scenario = nil
- if(!ENV['SCENARIO'].blank?)
+ unless ENV['SCENARIO'].blank?
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
+
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
- if(!USING_RVM)
- skip "Must have RVM installed."
+ unless USING_RVM
+ skip 'Must have RVM installed.'
next
end
# 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
dir base_dir do
@@ -53,10 +52,10 @@
# By default, rvm_ruby_string isn't inherited over properly, so let's
# 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"...
- klass = "Annotate::Validations::#{base_dir.gsub('.', '_').classify}".constantize
+ require base_dir.to_s # Will get "#{base_dir}.rb"...
+ klass = "Annotate::Validations::#{base_dir.tr('.', '_').classify}".constantize
Dir.chdir(temp_dir) do
# bash is required by rvm
diff --git a/spec/integration/rails_2.3_with_bundler.rb b/spec/integration/rails_2.3_with_bundler.rb
index ffbd2fe9c..9c86871cf 100644
--- a/spec/integration/rails_2.3_with_bundler.rb
+++ b/spec/integration/rails_2.3_with_bundler.rb
@@ -4,7 +4,7 @@ module Annotate
module Validations
class Rails23WithBundler < Base
def self.schema_annotation
- return <<-RUBY
+ <<-RUBY
# == Schema Information
#
# Table name: tasks
@@ -18,7 +18,7 @@ def self.schema_annotation
end
def self.route_annotation
- return <<-RUBY
+ <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM)
#
# tasks GET /tasks(.:format) {:controller=>"tasks", :action=>"index"}
diff --git a/spec/integration/rails_3.2.2.rb b/spec/integration/rails_3.2.2.rb
index 2a47e1ff6..e1087c403 100644
--- a/spec/integration/rails_3.2.2.rb
+++ b/spec/integration/rails_3.2.2.rb
@@ -4,7 +4,7 @@ module Annotate
module Validations
class Rails322 < Base
def self.schema_annotation
- return <<-RUBY
+ <<-RUBY
# == Schema Information
#
# Table name: tasks
@@ -18,7 +18,7 @@ def self.schema_annotation
end
def self.route_annotation
- return <<-RUBY
+ <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM)
#
# tasks GET /tasks(.:format) tasks#index
diff --git a/spec/integration/rails_3.2.8.rb b/spec/integration/rails_3.2.8.rb
index d55c11c44..35b3137a5 100644
--- a/spec/integration/rails_3.2.8.rb
+++ b/spec/integration/rails_3.2.8.rb
@@ -4,7 +4,7 @@ module Annotate
module Validations
class Rails328 < Base
def self.schema_annotation
- return <<-RUBY
+ <<-RUBY
# == Schema Information
#
# Table name: tasks
@@ -18,7 +18,7 @@ def self.schema_annotation
end
def self.route_annotation
- return <<-RUBY
+ <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM)
#
# tasks GET /tasks(.:format) tasks#index
diff --git a/spec/integration/rails_3.2_autoloading_factory_girl.rb b/spec/integration/rails_3.2_autoloading_factory_girl.rb
index 83aa022d7..e582f32fe 100644
--- a/spec/integration/rails_3.2_autoloading_factory_girl.rb
+++ b/spec/integration/rails_3.2_autoloading_factory_girl.rb
@@ -4,7 +4,7 @@ module Annotate
module Validations
class Rails32AutoloadingFactoryGirl < Base
def self.schema_annotation
- return <<-RUBY
+ <<-RUBY
# == Schema Information
#
# Table name: tasks
@@ -18,7 +18,7 @@ def self.schema_annotation
end
def self.route_annotation
- return <<-RUBY
+ <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM)
#
# tasks GET /tasks(.:format) tasks#index
@@ -33,13 +33,15 @@ def self.route_annotation
end
def self.verify_files(test_rig)
- return Annotate::Validations::Common.verify_files({
- :model => true,
- :test => true,
- :fixture => false,
- :factory => true,
- :routes => true
- }, test_rig, self.schema_annotation, self.route_annotation, true)
+ Annotate::Validations::Common.verify_files(
+ {
+ model: true,
+ test: true,
+ fixture: false,
+ factory: true,
+ routes: true
+ }, test_rig, schema_annotation, route_annotation, true
+ )
end
end
end
diff --git a/spec/integration/rails_3.2_custom_inflections.rb b/spec/integration/rails_3.2_custom_inflections.rb
index 253985ea4..d1cab92c5 100644
--- a/spec/integration/rails_3.2_custom_inflections.rb
+++ b/spec/integration/rails_3.2_custom_inflections.rb
@@ -4,7 +4,7 @@ module Annotate
module Validations
class Rails32CustomInflection < Base
def self.schema_annotation
- return <<-RUBY
+ <<-RUBY
# == Schema Information
#
# Table name: tasks
@@ -18,7 +18,7 @@ def self.schema_annotation
end
def self.route_annotation
- return <<-RUBY
+ <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM)
#
# tasks GET /tasks(.:format) tasks#index
diff --git a/spec/integration/rails_3.2_with_asset_pipeline.rb b/spec/integration/rails_3.2_with_asset_pipeline.rb
index ecd8365aa..4d35010d4 100644
--- a/spec/integration/rails_3.2_with_asset_pipeline.rb
+++ b/spec/integration/rails_3.2_with_asset_pipeline.rb
@@ -4,7 +4,7 @@ module Annotate
module Validations
class Rails32WithAssetPipeline < Base
def self.schema_annotation
- return <<-RUBY
+ <<-RUBY
# == Schema Information
#
# Table name: tasks
@@ -18,7 +18,7 @@ def self.schema_annotation
end
def self.route_annotation
- return <<-RUBY
+ <<-RUBY
# == Route Map (Updated YYYY-MM-DD HH:MM)
#
# tasks GET /tasks(.:format) tasks#index
diff --git a/spec/integration/standalone.rb b/spec/integration/standalone.rb
index 2b45442b7..14545304d 100644
--- a/spec/integration/standalone.rb
+++ b/spec/integration/standalone.rb
@@ -4,7 +4,7 @@ module Annotate
module Validations
class Standalone < Base
def self.schema_annotation
- return <<-RUBY
+ <<-RUBY
# == Schema Information
#
# Table name: tasks
@@ -18,9 +18,7 @@ def self.schema_annotation
end
def self.test_commands
- return %q{
- bin/annotate --require ./config/init.rb
- }
+ 'bin/annotate --require ./config/init.rb'
end
def self.verify_output(output)
@@ -28,13 +26,15 @@ def self.verify_output(output)
end
def self.verify_files(test_rig)
- return Annotate::Validations::Common.verify_files({
- :model => true,
- :test => false,
- :fixture => false,
- :factory => false,
- :routes => false
- }, test_rig, self.schema_annotation, nil, true)
+ Annotate::Validations::Common.verify_files(
+ {
+ model: true,
+ test: false,
+ fixture: false,
+ factory: false,
+ routes: false
+ }, test_rig, schema_annotation, nil, true
+ )
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 4d15c2322..6362aeec4 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,59 +1,79 @@
+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 'bundler'
Bundler.setup
+require 'rake'
require 'rspec'
require 'wrong/adapters/rspec'
-$:.unshift(File.join(File.dirname(__FILE__), '../lib'))
-$:.unshift(File.dirname(__FILE__))
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '../lib'))
+$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'active_support'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/class/subclasses'
require 'active_support/core_ext/string/inflections'
require 'annotate'
+require 'byebug'
module Annotate
module Integration
- ABSOLUTE_GEM_ROOT=File.expand_path('../../', __FILE__)
+ ABSOLUTE_GEM_ROOT = File.expand_path('../../', __FILE__)
- CRUFT_PATTERNS=[
- "%SCENARIO%/bin/*", "%SCENARIO%/log/*", "%SCENARIO%/tmp/*",
- "%SCENARIO%/.bundle"
- ]
- SCENARIO_HOME=File.join(File.dirname(__FILE__), 'integration')
- SCENARIOS=Dir.glob("#{SCENARIO_HOME}/*").
- select { |candidate| File.directory?(candidate) }.
- map do |test_rig|
- base_dir = File.basename(test_rig)
- [test_rig, base_dir, base_dir.titlecase]
- end
+ CRUFT_PATTERNS = %w(
+ %SCENARIO%/bin/*
+ %SCENARIO%/log/*
+ %SCENARIO%/tmp/*
+ %SCENARIO%/.bundle
+ ).freeze
+
+ SCENARIO_HOME = File.join(File.dirname(__FILE__), 'integration')
+ SCENARIOS = Dir.glob("#{SCENARIO_HOME}/*").select do |candidate|
+ File.directory?(candidate)
+ 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)
FileList[
- Annotate::Integration::CRUFT_PATTERNS.
- map { |pattern| pattern.sub('%SCENARIO%', test_rig) }
+ Annotate::Integration::CRUFT_PATTERNS.map do |pattern|
+ pattern.sub('%SCENARIO%', test_rig)
+ end
].each do |fname|
FileUtils.rm_rf(fname)
end
end
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)
end
end
def self.empty_gemset(test_rig)
Dir.chdir(test_rig) do
- system(%q{
+ system('
(
export SKIP_BUNDLER=1
source .rvmrc &&
rvm --force gemset empty
) 2>&1
- })
+ ')
end
end
@@ -65,8 +85,8 @@ def self.clear_untracked_files
system("git clean -dfx #{SCENARIO_HOME}/*/")
end
- def self.is_clean?(test_rig)
- return `git status --porcelain #{test_rig}/ | wc -l`.strip.to_i == 0
+ def self.clean?(test_rig)
+ `git status --porcelain #{test_rig}/ | wc -l`.strip.to_i.zero?
end
end
end