pax_global_header00006660000000000000000000000064140232234420014506gustar00rootroot0000000000000052 comment=1ccbfc17559b650afae264b9027227a9ba199681 data-migrate-6.7.0/000077500000000000000000000000001402322344200140575ustar00rootroot00000000000000data-migrate-6.7.0/.github/000077500000000000000000000000001402322344200154175ustar00rootroot00000000000000data-migrate-6.7.0/.github/workflows/000077500000000000000000000000001402322344200174545ustar00rootroot00000000000000data-migrate-6.7.0/.github/workflows/gempush.yml000066400000000000000000000012321402322344200216450ustar00rootroot00000000000000name: Ruby Gem on: push: branches: - master jobs: build: name: Build + Publish runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: Set up Ruby 2.6 uses: actions/setup-ruby@v1 with: version: 2.6.x - name: Publish to RubyGems run: | mkdir -p $HOME/.gem touch $HOME/.gem/credentials chmod 0600 $HOME/.gem/credentials printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials gem build *.gemspec gem push *.gem env: GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}} data-migrate-6.7.0/.gitignore000066400000000000000000000002041402322344200160430ustar00rootroot00000000000000/.rvmrc *.gem *.lock gemfiles/.bundle spec/db/test.db spec/db/other_test.db spec/db/data_schema.rb .vscode/ .DS_Store .idea/ vendor/data-migrate-6.7.0/.hound.yml000066400000000000000000000000741402322344200157760ustar00rootroot00000000000000fail_on_violations: true ruby: config_file: .rubocop.yml data-migrate-6.7.0/.overcommit.yml000066400000000000000000000016121402322344200170440ustar00rootroot00000000000000# 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 failuresdata-migrate-6.7.0/.rbenv-gemsets000066400000000000000000000000251402322344200166360ustar00rootroot00000000000000data-migrate -global data-migrate-6.7.0/.rspec000066400000000000000000000000241402322344200151700ustar00rootroot00000000000000--color --format d data-migrate-6.7.0/.rubocop.yml000066400000000000000000000001111402322344200163220ustar00rootroot00000000000000# Use same configration as used by HoundCI inherit_from: .ruby-style.yml data-migrate-6.7.0/.ruby-style.yml000066400000000000000000001066221402322344200170060ustar00rootroot00000000000000AllCops: TargetRubyVersion: 2.4 Include: - "**/*.podspec" - "**/*.jbuilder" - "**/*.rake" - "**/*.opal" - "**/Gemfile" - "**/Rakefile" - "**/Capfile" - "**/Guardfile" - "**/Podfile" - "**/Thorfile" - "**/Vagrantfile" - "**/Berksfile" - "**/Cheffile" - "**/Vagabondfile" Exclude: - "**/*.gemspec" - "vendor/**/*" - "**/schema.rb" - "db/migrate/*" - "db/data/*" - "db/data_schema.rb" - "tasks/databases.rake" Rails/Date: EnforcedStyle: strict Style/FrozenStringLiteralComment: Enabled: false Layout/AccessModifierIndentation: Description: Check indentation of private/protected visibility modifiers. StyleGuide: https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected Enabled: true EnforcedStyle: indent SupportedStyles: - outdent - indent Layout/AlignHash: Description: Align the elements of a hash literal if they span more than one line. Enabled: true EnforcedHashRocketStyle: key EnforcedColonStyle: key EnforcedLastArgumentHashStyle: always_inspect SupportedLastArgumentHashStyles: - always_inspect - always_ignore - ignore_implicit - ignore_explicit Layout/AlignParameters: Description: Align the parameters of a method call if they span more than one line. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-double-indent Enabled: true EnforcedStyle: with_first_parameter SupportedStyles: - with_first_parameter - with_fixed_indentation Style/AndOr: Description: Use &&/|| instead of and/or. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-and-or-or Enabled: false EnforcedStyle: always SupportedStyles: - always - conditionals Style/BarePercentLiterals: Description: Checks if usage of %() or %Q() matches configuration. StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand Enabled: true EnforcedStyle: bare_percent SupportedStyles: - percent_q - bare_percent Style/BracesAroundHashParameters: Description: Enforce braces style around hash parameters. Enabled: false EnforcedStyle: no_braces SupportedStyles: - braces - no_braces - context_dependent Layout/CaseIndentation: Description: Indentation of when in a case/when/[else/]end. StyleGuide: https://github.com/bbatsov/ruby-style-guide#indent-when-to-case Enabled: true EnforcedStyle: case SupportedStyles: - case - end IndentOneStep: false Style/ClassAndModuleChildren: Description: Checks style of children classes and modules. Enabled: false EnforcedStyle: nested SupportedStyles: - nested - compact Style/ClassCheck: Description: Enforces consistent use of `Object#is_a?` or `Object#kind_of?`. Enabled: true EnforcedStyle: is_a? SupportedStyles: - is_a? - kind_of? Style/CollectionMethods: Description: Preferred collection methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#map-find-select-reduce-size Enabled: false PreferredMethods: collect: map collect!: map! find: detect find_all: select reduce: inject Style/CommentAnnotation: Description: Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK, REVIEW). StyleGuide: https://github.com/bbatsov/ruby-style-guide#annotate-keywords Enabled: false Keywords: - TODO - FIXME - OPTIMIZE - HACK - REVIEW Layout/MultilineMethodCallIndentation: EnforcedStyle: indented Enabled: true SupportedStyles: - aligned - indented - indented_relative_to_receiver Layout/DotPosition: Description: Checks the position of the dot in multi-line method calls. StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains Enabled: false EnforcedStyle: leading SupportedStyles: - leading - trailing Layout/EmptyLineBetweenDefs: Description: Use empty lines between defs. StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods Enabled: true AllowAdjacentOneLineDefs: false Layout/EmptyLinesAroundBlockBody: Description: Keeps track of empty lines around block bodies. Enabled: false EnforcedStyle: no_empty_lines SupportedStyles: - empty_lines - no_empty_lines Layout/EmptyLinesAroundClassBody: Description: Keeps track of empty lines around class bodies. Enabled: false EnforcedStyle: no_empty_lines SupportedStyles: - empty_lines - no_empty_lines Layout/EmptyLinesAroundModuleBody: Description: Keeps track of empty lines around module bodies. Enabled: true EnforcedStyle: no_empty_lines SupportedStyles: - empty_lines - no_empty_lines Style/Encoding: Description: Use UTF-8 as the source file encoding. StyleGuide: https://github.com/bbatsov/ruby-style-guide#utf-8 Enabled: false Naming/FileName: Description: Use snake_case for source file names. StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-files Enabled: false Exclude: [] Layout/FirstParameterIndentation: Description: Checks the indentation of the first parameter in a method call. Enabled: true EnforcedStyle: special_for_inner_method_call_in_parentheses SupportedStyles: - consistent - special_for_inner_method_call - special_for_inner_method_call_in_parentheses Style/For: Description: Checks use of for or each in multiline loops. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-for-loops Enabled: true EnforcedStyle: each SupportedStyles: - for - each Style/FormatString: Description: Enforce the use of Kernel#sprintf, Kernel#format or String#%. StyleGuide: https://github.com/bbatsov/ruby-style-guide#sprintf Enabled: false EnforcedStyle: format SupportedStyles: - format - sprintf - percent Style/GlobalVars: Description: Do not introduce global variables. StyleGuide: https://github.com/bbatsov/ruby-style-guide#instance-vars Enabled: false AllowedVariables: [] Style/GuardClause: Description: Check for conditionals that can be replaced with guard clauses StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals Enabled: false MinBodyLength: 1 Style/HashSyntax: Description: 'Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax { :a => 1, :b => 2 }.' StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-literals Enabled: true EnforcedStyle: ruby19 SupportedStyles: - ruby19 - hash_rockets Style/IdenticalConditionalBranches: Description: >- Checks that conditional statements do not have an identical line at the end of each branch, which can validly be moved out of the conditional. Enabled: true Style/IfInsideElse: Description: 'Finds if nodes inside else, which can be converted to elsif.' Enabled: true Style/IfUnlessModifier: Description: >- Favor modifier if/unless usage when you have a single-line body. StyleGuide: '#if-as-a-modifier' Enabled: false Style/IfUnlessModifierOfIfUnless: Description: >- Avoid modifier if/unless usage on conditionals. Enabled: true Style/IfWithSemicolon: Description: 'Do not use if x; .... Use the ternary operator instead.' StyleGuide: '#no-semicolon-ifs' Enabled: true Style/InfiniteLoop: Description: 'Use Kernel#loop for infinite loops.' StyleGuide: '#infinite-loop' Enabled: true Style/InverseMethods: Description: >- Use the inverse method instead of `!.method` if an inverse method is defined. Enabled: true Layout/IndentationWidth: Description: Use 2 spaces for indentation. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-indentation Enabled: true Width: 2 Layout/IndentHash: Description: Checks the indentation of the first key in a hash literal. Enabled: true EnforcedStyle: consistent SupportedStyles: - consistent IndentationWidth: ~ Style/LambdaCall: Description: Use lambda.call(...) instead of lambda.(...). StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc-call Enabled: false EnforcedStyle: call SupportedStyles: - call - braces Style/Next: Description: Use `next` to skip iteration instead of a condition at the end. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals Enabled: false EnforcedStyle: skip_modifier_ifs MinBodyLength: 3 SupportedStyles: - skip_modifier_ifs - always Style/NonNilCheck: Description: Checks for redundant nil checks. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks Enabled: true IncludeSemanticChanges: false Style/MethodDefParentheses: Description: Checks if the method definitions have or don't have parentheses. StyleGuide: https://github.com/bbatsov/ruby-style-guide#method-parens Enabled: true EnforcedStyle: require_parentheses SupportedStyles: - require_parentheses - require_no_parentheses Naming/MethodName: Description: Use the configured style when naming methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars Enabled: true EnforcedStyle: snake_case SupportedStyles: - snake_case - camelCase Layout/MultilineOperationIndentation: Description: Checks indentation of binary operations that span more than one line. Enabled: true EnforcedStyle: aligned SupportedStyles: - aligned - indented Style/NumericLiterals: Description: Add underscores to large numeric literals to improve their readability. StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics Enabled: false MinDigits: 5 Style/ParenthesesAroundCondition: Description: Don't use parentheses around the condition of an if/unless/while. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-parens-if Enabled: true AllowSafeAssignment: true Style/PercentLiteralDelimiters: Description: Use `%`-literal delimiters consistently StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-literal-braces Enabled: false PreferredDelimiters: "%": "()" "%i": "()" "%q": "()" "%Q": "()" "%r": "{}" "%s": "()" "%w": "()" "%W": "()" "%x": "()" Style/PercentQLiterals: Description: Checks if uses of %Q/%q match the configured preference. Enabled: true EnforcedStyle: lower_case_q SupportedStyles: - lower_case_q - upper_case_q Naming/PredicateName: Description: Check the names of predicate methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark Enabled: true NamePrefix: - is_ - has_ - have_ NamePrefixBlacklist: - is_ Exclude: - spec/**/* Style/RaiseArgs: Description: Checks the arguments passed to raise/fail. StyleGuide: https://github.com/bbatsov/ruby-style-guide#exception-class-messages Enabled: false EnforcedStyle: exploded SupportedStyles: - compact - exploded Style/RedundantReturn: Description: Don't use return where it's not required. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-explicit-return Enabled: true AllowMultipleReturnValues: false Style/RegexpLiteral: Description: Use %r for regular expressions matching more than `MaxSlashes` '/' characters. Use %r only for regular expressions matching more than `MaxSlashes` '/' character. StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-r Enabled: false Style/Semicolon: Description: Don't use semicolons to terminate expressions. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-semicolon Enabled: true AllowAsExpressionSeparator: false Style/SignalException: Description: Checks for proper usage of fail and raise. StyleGuide: https://github.com/bbatsov/ruby-style-guide#fail-method Enabled: false EnforcedStyle: semantic SupportedStyles: - only_raise - only_fail - semantic Style/SingleLineBlockParams: Description: Enforces the names of some block params. StyleGuide: https://github.com/bbatsov/ruby-style-guide#reduce-blocks Enabled: false Methods: - reduce: - a - e - inject: - a - e Style/SingleLineMethods: Description: Avoid single-line methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-single-line-methods Enabled: false AllowIfMethodIsEmpty: true Style/StringLiterals: Description: Checks if uses of quotes match the configured preference. StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-string-literals Enabled: true EnforcedStyle: double_quotes SupportedStyles: - single_quotes - double_quotes Style/StringLiteralsInInterpolation: Description: Checks if uses of quotes inside expressions in interpolated strings match the configured preference. Enabled: false EnforcedStyle: single_quotes SupportedStyles: - single_quotes - double_quotes Layout/SpaceAroundBlockParameters: Description: Checks the spacing inside and after block parameters pipes. Enabled: false EnforcedStyleInsidePipes: no_space Layout/SpaceAroundEqualsInParameterDefault: Description: Checks that the equals signs in parameter default assignments have or don't have surrounding space depending on configuration. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-around-equals Enabled: true EnforcedStyle: space SupportedStyles: - space - no_space Layout/SpaceBeforeBlockBraces: Description: Checks that the left block brace has or doesn't have space before it. Enabled: true EnforcedStyle: space SupportedStyles: - space - no_space Layout/SpaceInsideBlockBraces: Description: Checks that block braces have or don't have surrounding space. For blocks taking parameters, checks that the left brace has or doesn't have trailing space. Enabled: false EnforcedStyle: space SupportedStyles: - space - no_space EnforcedStyleForEmptyBraces: no_space SpaceBeforeBlockParameters: true Layout/SpaceInsideHashLiteralBraces: Description: Use spaces inside hash literal braces - or don't. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators Enabled: false EnforcedStyle: space EnforcedStyleForEmptyBraces: no_space SupportedStyles: - space - no_space Style/SymbolProc: Description: Use symbols as procs instead of blocks when possible. Enabled: true IgnoredMethods: - respond_to Layout/TrailingBlankLines: Description: Checks trailing blank lines and final newline. StyleGuide: https://github.com/bbatsov/ruby-style-guide#newline-eof Enabled: true EnforcedStyle: final_newline SupportedStyles: - final_newline - final_blank_line Style/TrivialAccessors: Description: Prefer attr_* methods to trivial readers/writers. StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr_family Enabled: false ExactNameMatch: false AllowPredicates: false AllowDSLWriters: false Whitelist: - to_ary - to_a - to_c - to_enum - to_h - to_hash - to_i - to_int - to_io - to_open - to_path - to_proc - to_r - to_regexp - to_str - to_s - to_sym Naming/VariableName: Description: Use the configured style when naming variables. StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars Enabled: true EnforcedStyle: snake_case SupportedStyles: - snake_case - camelCase Style/WhileUntilModifier: Description: Favor modifier while/until usage when you have a single-line body. StyleGuide: https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier Enabled: false Style/WordArray: Description: Use %w or %W for arrays of words. StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-w Enabled: false MinSize: 0 WordRegex: !ruby/regexp /\A[\p{Word}]+\z/ Metrics/BlockLength: Enabled: true Exclude: - '**/spec/**/*' - '**/script/**/*' Metrics/AbcSize: Description: A calculated magnitude based on number of assignments, branches, and conditions. Enabled: true Max: 27 Exclude: - "db/data/*.rb" Metrics/BlockNesting: Description: Avoid excessive block nesting StyleGuide: https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count Enabled: false Max: 3 Metrics/ClassLength: Description: Avoid classes longer than 100 lines of code. Enabled: false CountComments: false Max: 100 Metrics/CyclomaticComplexity: Description: A complexity metric that is strongly correlated to the number of test cases needed to validate a method. Enabled: false Max: 6 Metrics/LineLength: Description: Limit lines to 120 characters. StyleGuide: https://github.com/bbatsov/ruby-style-guide#80-character-limits Enabled: true Max: 120 AllowURI: true URISchemes: - http - https Metrics/MethodLength: Description: Avoid methods longer than 10 lines of code. StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods Enabled: true CountComments: true Max: 20 Exclude: - "db/data/*.rb" Metrics/ParameterLists: Description: Avoid parameter lists longer than three or four parameters. StyleGuide: https://github.com/bbatsov/ruby-style-guide#too-many-params Enabled: false Max: 5 CountKeywordArgs: true Metrics/PerceivedComplexity: Description: A complexity metric geared towards measuring complexity for a human reader. Enabled: false Max: 7 Lint/AssignmentInCondition: Description: Don't use assignment in conditions. StyleGuide: https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition Enabled: false AllowSafeAssignment: true Rails/ActionFilter: Description: Enforces consistent use of action filter methods. Enabled: false EnforcedStyle: action SupportedStyles: - action - filter Include: - app/controllers/**/*.rb Rails/HasAndBelongsToMany: Description: Prefer has_many :through to has_and_belongs_to_many. Enabled: true Include: - app/models/**/*.rb Rails/Output: Description: Checks for calls to puts, print, etc. Enabled: true Include: - app/**/*.rb - config/**/*.rb - db/**/*.rb - lib/**/*.rb Rails/ReadWriteAttribute: Description: Checks for read_attribute(:attr) and write_attribute(:attr, val). Enabled: true Include: - app/models/**/*.rb Rails/ScopeArgs: Description: Checks the arguments of ActiveRecord scopes. Enabled: true Include: - app/models/**/*.rb Rails/Validation: Description: Use validates :attribute, hash of validations. Enabled: true Include: - app/models/**/*.rb Style/InlineComment: Description: Avoid inline comments. Enabled: false Style/MethodCalledOnDoEndBlock: Description: Avoid chaining a method call on a do...end block. StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks Enabled: false Style/SymbolArray: Description: Use %i or %I for arrays of symbols. StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-i Enabled: false Layout/ExtraSpacing: Description: Do not use unnecessary spacing. Enabled: false Naming/AccessorMethodName: Description: Check the naming of accessor methods for get_/set_. Enabled: false Style/Alias: Description: Use alias_method instead of alias. StyleGuide: https://github.com/bbatsov/ruby-style-guide#alias-method Enabled: false Layout/AlignArray: Description: Align the elements of an array literal if they span more than one line. StyleGuide: https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays Enabled: true Style/ArrayJoin: Description: Use Array#join instead of Array#*. StyleGuide: https://github.com/bbatsov/ruby-style-guide#array-join Enabled: false Style/AsciiComments: Description: Use only ascii symbols in comments. StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-comments Enabled: false Naming/AsciiIdentifiers: Description: Use only ascii symbols in identifiers. StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-identifiers Enabled: false Style/Attr: Description: Checks for uses of Module#attr. StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr Enabled: false Style/BeginBlock: Description: Avoid the use of BEGIN blocks. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks Enabled: true Style/BlockComments: Description: Do not use block comments. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-block-comments Enabled: true Layout/BlockEndNewline: Description: Put end statement of multiline block on its own line. Enabled: true Style/BlockDelimiters: Enabled: false Style/CaseEquality: Description: Avoid explicit use of the case equality operator(===). StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-case-equality Enabled: false Style/CharacterLiteral: Description: Checks for uses of character literals. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-character-literals Enabled: false Naming/ClassAndModuleCamelCase: Description: Use CamelCase for classes and modules. StyleGuide: https://github.com/bbatsov/ruby-style-guide#camelcase-classes Enabled: true Style/ClassMethods: Description: Use self when defining module/class methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#def-self-singletons Enabled: true Style/ClassVars: Description: Avoid the use of class variables. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-class-vars Enabled: false Style/ColonMethodCall: Description: 'Do not use :: for method call.' StyleGuide: https://github.com/bbatsov/ruby-style-guide#double-colons Enabled: false Layout/CommentIndentation: Description: Indentation of comments. Enabled: true Naming/ConstantName: Description: Constants should use SCREAMING_SNAKE_CASE. StyleGuide: https://github.com/bbatsov/ruby-style-guide#screaming-snake-case Enabled: true Style/DefWithParentheses: Description: Use def with parentheses when there are arguments. StyleGuide: https://github.com/bbatsov/ruby-style-guide#method-parens Enabled: true Style/PreferredHashMethods: Description: Checks for use of deprecated Hash methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-key Enabled: false Style/Documentation: Description: Document classes and non-namespace modules. Enabled: false Style/DoubleNegation: Description: Checks for uses of double negation (!!). StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-bang-bang Enabled: false Style/EachWithObject: Description: Prefer `each_with_object` over `inject` or `reduce`. Enabled: false Layout/ElseAlignment: Description: Align elses and elsifs correctly. Enabled: true Style/EmptyElse: Description: Avoid empty else-clauses. Enabled: true Layout/EmptyLines: Description: Don't use several empty lines in a row. Enabled: false Layout/EmptyLinesAroundAccessModifier: Description: Keep blank lines around access modifiers. Enabled: true Layout/EmptyLinesAroundMethodBody: Description: Keeps track of empty lines around method bodies. Enabled: true Style/EmptyLiteral: Description: Prefer literals to Array.new/Hash.new/String.new. StyleGuide: https://github.com/bbatsov/ruby-style-guide#literal-array-hash Enabled: false Style/EndBlock: Description: Avoid the use of END blocks. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-END-blocks Enabled: true Layout/EndOfLine: Description: Use Unix-style line endings. StyleGuide: https://github.com/bbatsov/ruby-style-guide#crlf Enabled: true EnforcedStyle: lf Style/EvenOdd: Description: Favor the use of Fixnum#even? && Fixnum#odd? StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods Enabled: false Style/FlipFlop: Description: Checks for flip flops StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-flip-flops Enabled: false Style/IfWithSemicolon: Description: Do not use if x; .... Use the ternary operator instead. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs Enabled: false Layout/IndentationConsistency: Description: Keep indentation straight. Enabled: true Layout/IndentArray: Description: Checks the indentation of the first element in an array literal. Enabled: true Style/InfiniteLoop: Description: Use Kernel#loop for infinite loops. StyleGuide: https://github.com/bbatsov/ruby-style-guide#infinite-loop Enabled: true Style/Lambda: Description: Use the new lambda literal syntax for single-line blocks. StyleGuide: https://github.com/bbatsov/ruby-style-guide#lambda-multi-line Enabled: false Layout/LeadingCommentSpace: Description: Comments should start with a space. StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-space Enabled: false Style/LineEndConcatenation: Description: Use \ instead of + or << to concatenate two string literals at line end. Enabled: false Style/MethodCallWithoutArgsParentheses: Description: Do not use parentheses for method calls with no arguments. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-args-no-parens Enabled: true Style/ModuleFunction: Description: Checks for usage of `extend self` in modules. StyleGuide: https://github.com/bbatsov/ruby-style-guide#module-function Enabled: false Style/MultilineBlockChain: Description: Avoid multi-line chains of blocks. StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks Enabled: false Layout/MultilineBlockLayout: Description: Ensures newlines after multiline block do statements. Enabled: true Style/MultilineIfThen: Description: Do not use then for multi-line if/unless. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-then Enabled: true Style/MultilineTernaryOperator: Description: 'Avoid multi-line ?: (the ternary operator); use if/unless instead.' StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary Enabled: false Style/NegatedIf: Description: Favor unless over if for negative conditions (or control flow or). StyleGuide: https://github.com/bbatsov/ruby-style-guide#unless-for-negatives Enabled: false Style/NegatedWhile: Description: Favor until over while for negative conditions. StyleGuide: https://github.com/bbatsov/ruby-style-guide#until-for-negatives Enabled: false Style/NestedTernaryOperator: Description: Use one expression per branch in a ternary operator. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-ternary Enabled: true Style/NilComparison: Description: Prefer x.nil? to x == nil. StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods Enabled: false Style/Not: Description: Use ! instead of not. StyleGuide: https://github.com/bbatsov/ruby-style-guide#bang-not-not Enabled: false Style/OneLineConditional: Description: Favor the ternary operator(?:) over if/then/else/end constructs. StyleGuide: https://github.com/bbatsov/ruby-style-guide#ternary-operator Enabled: false Style/PerlBackrefs: Description: Avoid Perl-style regex back references. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers Enabled: false Style/Proc: Description: Use proc instead of Proc.new. StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc Enabled: false Style/RedundantBegin: Description: Don't use begin blocks when they are not needed. StyleGuide: https://github.com/bbatsov/ruby-style-guide#begin-implicit Enabled: true Style/RedundantException: Description: Checks for an obsolete RuntimeException argument in raise/fail. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror Enabled: true Style/RedundantSelf: Description: Don't use self where it's not needed. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-self-unless-required Enabled: false Style/RescueModifier: Description: Avoid using rescue in its modifier form. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers Enabled: true Style/SelfAssignment: Description: Checks for places where self-assignment shorthand should have been used. StyleGuide: https://github.com/bbatsov/ruby-style-guide#self-assignment Enabled: false Layout/SpaceAfterColon: Description: Use spaces after colons. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators Enabled: true Layout/SpaceAfterComma: Description: Use spaces after commas. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators Enabled: true Layout/SpaceAfterMethodName: Description: Do not put a space between a method name and the opening parenthesis in a method definition. StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-no-spaces Enabled: true Layout/SpaceAfterNot: Description: Tracks redundant space after the ! operator. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-space-bang Enabled: true Layout/SpaceAfterSemicolon: Description: Use spaces after semicolons. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators Enabled: true Layout/SpaceBeforeComma: Description: No spaces before commas. Enabled: true Layout/SpaceBeforeComment: Description: Checks for missing space between code and a comment on the same line. Enabled: false Layout/SpaceBeforeSemicolon: Description: No spaces before semicolons. Enabled: true Layout/SpaceAroundOperators: Description: Use spaces around operators. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators Enabled: false Layout/SpaceInsideParens: Description: No spaces after ( or before ). StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-spaces-braces Enabled: false Layout/SpaceInsideRangeLiteral: Description: No spaces inside range literals. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals Enabled: true Style/SpecialGlobalVars: Description: Avoid Perl-style global variables. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms Enabled: false Style/StructInheritance: Description: Checks for inheritance from Struct.new. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-extend-struct-new Enabled: true Layout/Tab: Description: No hard tabs. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-indentation Enabled: true Layout/TrailingWhitespace: Description: Avoid trailing whitespace. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace Enabled: true Style/UnlessElse: Description: Do not use unless with else. Rewrite these with the positive case first. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-else-with-unless Enabled: true Style/UnneededCapitalW: Description: Checks for %W when interpolation is not needed. Enabled: true Style/UnneededPercentQ: Description: Checks for %q/%Q when single quotes or double quotes would do. StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-q Enabled: true Style/VariableInterpolation: Description: Don't interpolate global, instance and class variables directly in strings. StyleGuide: https://github.com/bbatsov/ruby-style-guide#curlies-interpolate Enabled: false Style/WhenThen: Description: Use when x then ... for one-line cases. StyleGuide: https://github.com/bbatsov/ruby-style-guide#one-line-cases Enabled: false Style/WhileUntilDo: Description: Checks for redundant do after while or until. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do Enabled: true Lint/AmbiguousOperator: Description: Checks for ambiguous operators in the first argument of a method invocation without parentheses. StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-as-args Enabled: false Lint/AmbiguousRegexpLiteral: Description: Checks for ambiguous regexp literals in the first argument of a method invocation without parenthesis. Enabled: false Lint/BlockAlignment: Description: Align block ends correctly. Enabled: true Lint/ConditionPosition: Description: Checks for condition placed in a confusing position relative to the keyword. StyleGuide: https://github.com/bbatsov/ruby-style-guide#same-line-condition Enabled: false Lint/Debugger: Description: Check for debugger calls. Enabled: false Lint/DeprecatedClassMethods: Description: Check for deprecated class method calls. Enabled: false Lint/DuplicateMethods: Description: Check for duplicate methods calls. Enabled: true Lint/ElseLayout: Description: Check for odd code arrangement in an else block. Enabled: false Lint/EmptyEnsure: Description: Checks for empty ensure block. Enabled: true Lint/EmptyInterpolation: Description: Checks for empty string interpolation. Enabled: true Lint/EndInMethod: Description: END blocks should not be placed inside method definitions. Enabled: true Lint/EnsureReturn: Description: Do not use return in an ensure block. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-return-ensure Enabled: true Security/Eval: Description: The use of eval represents a serious security risk. Enabled: true Lint/HandleExceptions: Description: Don't suppress exception. StyleGuide: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions Enabled: false Lint/LiteralInInterpolation: Description: Checks for literals used in interpolation. Enabled: false Lint/Loop: Description: Use Kernel#loop with break rather than begin/end/until or begin/end/while for post-loop tests. StyleGuide: https://github.com/bbatsov/ruby-style-guide#loop-with-break Enabled: false Lint/ParenthesesAsGroupedExpression: Description: Checks for method calls with a space before the opening parenthesis. StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-no-spaces Enabled: false Lint/RequireParentheses: Description: Use parentheses in the method call to avoid confusion about precedence. Enabled: false Lint/RescueException: Description: Avoid rescuing the Exception class. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-blind-rescues Enabled: true Lint/ShadowingOuterLocalVariable: Description: Do not use the same name as outer local variable for block arguments or block local variables. Enabled: true Layout/SpaceBeforeFirstArg: Description: Put a space between a method name and the first argument in a method call without parentheses. Enabled: true Lint/StringConversionInInterpolation: Description: Checks for Object#to_s usage in string interpolation. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-to-s Enabled: true Lint/UnderscorePrefixedVariableName: Description: Do not use prefix `_` for a variable that is used. Enabled: false Lint/UnusedBlockArgument: Description: Checks for unused block arguments. StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars Enabled: true Lint/UnusedMethodArgument: Description: Checks for unused method arguments. StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars Enabled: true Lint/UnreachableCode: Description: Unreachable code. Enabled: true Lint/UselessAccessModifier: Description: Checks for useless access modifiers. Enabled: true Lint/UselessAssignment: Description: Checks for useless assignment to a local variable. StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars Enabled: true Lint/UselessComparison: Description: Checks for comparison of something with itself. Enabled: true Lint/UselessElseWithoutRescue: Description: Checks for useless `else` in `begin..end` without `rescue`. Enabled: true Lint/UselessSetterCall: Description: Checks for useless setter call to a local variable. Enabled: true Lint/Void: Description: Possible use of operator/literal/variable in void context. Enabled: false Rails/Delegate: Description: Prefer delegate method for delegations. Enabled: false Style/FrozenStringLiteralComment: Description: >- Add the frozen_string_literal comment to the top of files to help transition from Ruby 2.3.0 to Ruby 3.0. Enabled: false Layout/SpaceBeforeFirstArg: Enabled: false Style/FormatStringToken: Enabled: falsedata-migrate-6.7.0/.ruby-version000066400000000000000000000000061402322344200165200ustar00rootroot000000000000002.6.1 data-migrate-6.7.0/.travis.yml000066400000000000000000000005321402322344200161700ustar00rootroot00000000000000language: ruby rvm: - 2.4.4 - 2.5.5 - 2.6.2 script: bundle exec rspec gemfile: - gemfiles/rails_5.1.gemfile - gemfiles/rails_5.2.gemfile - gemfiles/rails_6.0.gemfile - gemfiles/rails_6.1.gemfile matrix: exclude: - rvm: 2.4.4 gemfile: gemfiles/rails_6.0.gemfile - rvm: 2.4.4 gemfile: gemfiles/rails_6.1.gemfile data-migrate-6.7.0/Appraisals000066400000000000000000000004161402322344200161020ustar00rootroot00000000000000appraise 'rails-5.0' do gem 'rails', '5.0.7.2' end appraise 'rails-5.1' do gem 'rails', '5.1.7' end appraise 'rails-5.2' do gem 'rails', '~> 5.2.3' end appraise 'rails-6.0' do gem 'rails', '~> 6.0.0' end appraise 'rails-6.1' do gem 'rails', '~> 6.1.0' end data-migrate-6.7.0/Changelog.md000066400000000000000000000063441402322344200162770ustar00rootroot00000000000000# Changelog # 6.7.0 Add configuration for which database name is to be used for database migrations [lewhit](https://github.com/lewhit) Add tests for Rails 6.1 [lewhit](https://github.com/lewhit) Migrations files should end only in .rb [kroehre](https://github.com/kroehre) ## 6.6.1 configs_for deprecation notice [borama](https://github.com/borama) ## 6.6.0 Allow data dump connection to be configured [lewhit](https://github.com/lewhit) ## 6.4.0 Add primary key to data_migrations table [aandis](https://github.com/aandis) ## 6.3.0 Add `abort_if_pending_migrations` rake tasks [tomgia](https://github.com/tomgia) ## 6.2.0 Add `rake data:schema:load` [timkrins](https://github.com/timkrins) ## 6.1.0 Fixing `rake db:schema:load:with_data` for Rails 6 Note: Rails 5.0 is no longer maintained. The gem will still work but it is not being actively tested. ## 6.0.5 Fixing `needs_migration?` method for Rails 5.2 and up [EnomaDebby](https://github.com/EnomaDebby) ## 6.0.4.beta Fix rolling back schema migrations failing for Rails 5.2 and above ## 6.0.3.beta Compatiblity with Rails 6 RC2 [y-yagi](https://github.com/y-yagi) ## 6.0.1.beta Fix migrations being generated in wrong folder ## 6.0.0 Support for Rails 6 No longer supporting Rails 4.2 ## 5.3.3 Ruby 2.2 and 2.3 are no longer actively validated with tests since they are both EOL ## 5.3.2 Fix capistrano migration tasks to only skip migrations if there are no changes in the db/data and db/migrate folders ## 5.3.1 Change database task to use data_migrations_path_configuration ## 5.3.0 Add support to configure data migration path ## 5.1.0 Fixes to `db:schema:load:with_data` + `db:structure:load:with_data` definition, thanks to [craineum](https://github.com/craineum) ## 5.0.0 Remove support for legacy migrations (from v2). **IMPORTANT**: If you used this gem from before version 2, make sure to run migration script ``` DataMigrate::LegacyMigrator.new.migrate ``` **Failure to do so may cause re-running old migrations** ## 4.0.0 Support for Rails 5.2 Deprecated support for Rails 4.1 Internal changes to make data-migrate behavior more similar to Rails migrations ## 3.5.0 Deprecated support for rails 4.0 Improvements to timestamped migrations, thanks to [Pierre-Michard](https://github.com/Pierre-Michard) ## 3.4.0 `rake data:migrate:status` to return result in chronological order ## 3.3.1 Regression fix, thanks to [subakva](https://github.com/subakva) ## 3.3.0 The concept of schema:dump to data migrations, thanks to [tobyndockerill](https://github.com/tobyndockerill) ## 3.2.1 data_migrate table into rails schema dump, thanks to [jturkel](https://github.com/jturkel) ## 3.2.0 - Add support for Rails 5.1 - No longer testing EOL rubies ## 3.1.0 Rails 5.0 support thanks to [jturkel](https://github.com/jturkel) and [abreckner](https://github.com/abreckner) ## 3.0.1 ([gacha](https://github.com/gacha)) Capistrano fixes ## 3.0.0 `--skip-schema-migration` removed deprecated. This gem will no longer generate schema migrations. It still supports running schema/data migrations with one command. ## 2.2.0 ([bilby91](https://github.com/bilby91)) Capistrano support ## 2.1.0 User `Rails.application.config.paths["db/migrate"]` instead of hard coded path to db migrations data-migrate-6.7.0/Gemfile000066400000000000000000000002201402322344200153440ustar00rootroot00000000000000source "http://rubygems.org" # Specify your gem's dependencies in data_migrate.gemspec gem 'rails', '~> 6.0.0' gem 'sqlite3', "~> 1.4" gemspec data-migrate-6.7.0/Gemfile.rails5000066400000000000000000000001701402322344200165460ustar00rootroot00000000000000source "http://rubygems.org" # Specify your gem's dependencies in data_migrate.gemspec gemspec gem 'rails', '~> 5.0.0' data-migrate-6.7.0/Gemfile.rails5.1000066400000000000000000000001701402322344200167050ustar00rootroot00000000000000source "http://rubygems.org" # Specify your gem's dependencies in data_migrate.gemspec gemspec gem 'rails', '~> 5.1.7' data-migrate-6.7.0/Gemfile.rails5.2000066400000000000000000000001661402322344200167130ustar00rootroot00000000000000source "http://rubygems.org" # Specify your gem's dependencies in data_migrate.gemspec gemspec gem 'rails', '~> 5.2' data-migrate-6.7.0/Gemfile.rails6.1000066400000000000000000000002201402322344200167020ustar00rootroot00000000000000source "http://rubygems.org" # Specify your gem's dependencies in data_migrate.gemspec gem 'rails', '~> 6.1.0' gem 'sqlite3', "~> 1.4" gemspec data-migrate-6.7.0/LICENSE000066400000000000000000000020621402322344200150640ustar00rootroot00000000000000The MIT License Copyright (c) 2011 Andrew J Vargo Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.data-migrate-6.7.0/README.md000066400000000000000000000165651402322344200153530ustar00rootroot00000000000000# Data Migrate - [![Version](http://img.shields.io/gem/v/data_migrate.svg?style=flat-square)](https://rubygems.org/gems/data_migrate) - [![License](http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT) - [![Travis](https://img.shields.io/travis/ilyakatz/data-migrate.svg)](https://travis-ci.org/ilyakatz/data-migrate) - [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com) Run data migrations alongside schema migrations. Data migrations are stored in `db/data`. They act like schema migrations, except they should be reserved for data migrations. For instance, if you realize you need to titleize all your titles, this is the place to do it. ![Travis](./screenshot.png) ## Why should I use this? With data-migrate you can generate your migrations for data as you would schema in your regular work flow. For setting tasks that don't require any intermediate AR activity, like dev and test, you stick with `db:migrate`. For production and QA, you change their scripts to `db:migrate:with_data`. Of course you want to test your migration, so you have the choice of `db:migrate:with_data` or `data:migrate` to just capture that data change. ## What does it do? Data migrations are stored in `db/data`. They act like schema migrations, except they should be reserved for data migrations. For instance, if you realize you need to titleize all yours titles, this is the place to do it. Running any of the provided rake tasks also creates a data schema table to mirror the usual schema migrations table to track all migrations. ## Rails Support Support Rails 5.0 through 6.0 ### Important notes for older versions #### v2 If you upgraded to Rails 4 while using `data_migrate` prior to version 2, the gem wrote data migration versions into `schema_migrations` table. After the fix, it was corrected to write into `data_migrations`. If you need to use these old migrations, add the following configuration It is recommended to move all legacy migrations from `schema_migrations` table into `data_migrations` table This may cause some unintended consequences. See [#22](https://github.com/ilyakatz/data-migrate/issues/22) #### v1 If you've installed previous to v1.1.0, you'll want to delete the `create_data_migrations_table` migration. ## Installation Add the gem to your project # Gemfile gem 'data_migrate' Then `bundle install` and you are ready to go. So you know, when you use one of the provide rake tasks, a table called `data_migrations` will be created in your database. This is to mirror the way the standard `db` rake tasks work. ## Usage ### Generating Migrations You can generate a data migration as you would a schema migration: rails g data_migration add_this_to_that ### Rake Tasks $> rake -T data rake data:abort_if_pending_migrations # Raises an error if there are pending data migrations rake data:dump # Create a db/data_schema.rb file that stores the current data version rake data:forward # Pushes the schema to the next version (specify steps w/ STEP=n) rake data:migrate # Migrate data migrations (options: VERSION=x, VERBOSE=false) rake data:migrate:down # Runs the "down" for a given migration VERSION rake data:migrate:redo # Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x) rake data:migrate:status # Display status of data migrations rake data:migrate:up # Runs the "up" for a given migration VERSION rake data:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n) rake data:schema:load # Load data_schema.rb file into the database without running the data migrations rake data:version # Retrieves the current schema version number for data migrations rake db:abort_if_pending_migrations:with_data # Raises an error if there are pending migrations or data migrations rake db:forward:with_data # Pushes the schema to the next version (specify steps w/ STEP=n) rake db:migrate:down:with_data # Runs the "down" for a given migration VERSION rake db:migrate:redo:with_data # Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x) rake db:migrate:status:with_data # Display status of data and schema migrations rake db:migrate:up:with_data # Runs the "up" for a given migration VERSION rake db:migrate:with_data # Migrate the database data and schema (options: VERSION=x, VERBOSE=false) rake db:rollback:with_data # Rolls the schema back to the previous version (specify steps w/ STEP=n) rake db:schema:load:with_data # Load both schema.rb and data_schema.rb file into the database rake db:structure:load:with_data # Load both structure.sql and data_schema.rb file into the database rake db:version:with_data # Retrieves the current schema version numbers for data and schema migrations Tasks work as they would with the 'vanilla' db version. The 'with_data' addition to the 'db' tasks will run the task in the context of both the data and schema migrations. That is, rake db:rollback:with_data will check to see if it was a schema or data migration invoked last, and do that. Tasks invoked in that space also have an additional line of output, indicating if the action is performed on data or schema. With 'up' and 'down', you can specify the option 'BOTH', which defaults to false. Using true, will migrate both the data and schema (in the desired direction) if they both match the version provided. Again, going up, schema is given precedence. Down its data. `rake db:migrate:status:with_data` provides and additional column to indicate which type of migration. ### Configuration `data_migrate` respects `ActiveRecord::Base.dump_schema_after_migration`. If it is set to `false`, data schema file will not be generated By default, data migrations are added to the `db/data/` path. You can override this setting in `config/initializers/data_migrate.rb` ```ruby DataMigrate.configure do |config| config.data_migrations_path = 'db/awesomepath/' config.db_configuration = { 'host' => '127.0.0.1', 'database' => 'awesome_database', 'adapter' => 'mysql2', 'username' => 'root', 'password' => nil, } config.spec_name = 'primary' end ``` ## Capistrano Support The gem comes with a capistrano task that can be used instead of `capistrano/rails/migrations`. Just add this line to your Capfile: ```ruby require 'capistrano/data_migrate' ``` From now on capistrano will run `rake db:migrate:with_data` in every deploy. ### Contributing ## Testing Run tests for a specific version of Rails ``` bundle exec appraisal install bundle exec appraisal rails-5.1 rspec bundle exec appraisal rails-5.2 rspec bundle exec appraisal rails-6.0 rspec ``` ## Thanks [Andrew J Vargo](http://github.com/ajvargo) Andrew was the original creator and maintainer of this project! [Jeremy Durham](http://jeremydurham.com/) for fleshing out the idea and providing guidance. You! Yes, you. Thanks for checking it out. data-migrate-6.7.0/Rakefile000066400000000000000000000000631402322344200155230ustar00rootroot00000000000000require 'bundler' Bundler::GemHelper.install_tasks data-migrate-6.7.0/data_migrate.gemspec000066400000000000000000000031521402322344200200460ustar00rootroot00000000000000# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "data_migrate/version" Gem::Specification.new do |s| s.name = "data_migrate" s.version = DataMigrate::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Andrew J Vargo", "Ilya Katz", "Deborah Enomah"] s.email = ["ajvargo@computer.org", "ilyakatz@gmail.com", "enomaomorogieva@gmail.com"] s.homepage = "https://github.com/ilyakatz/data-migrate" s.summary = %q{Rake tasks to migrate data alongside schema changes.} s.description = %q{Rake tasks to migrate data alongside schema changes.} s.license = "MIT" s.rubyforge_project = "data_migrate" s.add_dependency('rails', '>= 5.0') s.add_development_dependency "appraisal" s.add_development_dependency "rake" s.add_development_dependency "rspec" s.add_development_dependency "rspec-core" s.add_development_dependency "pry" s.add_development_dependency "rb-readline" s.add_development_dependency "sqlite3", "~> 1.3.6" s.add_development_dependency "timecop" s.add_development_dependency "rubocop" s.add_development_dependency "overcommit" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] s.post_install_message = <<-POST_INSTALL_MESSAGE #{"*" * 80} data-migrate: IMPORTANT: Breaking change introduced for migrations from v2. Failure to run the migration can have serious consequences. See Readme for more info. #{"*" * 80} POST_INSTALL_MESSAGE end data-migrate-6.7.0/gemfiles/000077500000000000000000000000001402322344200156525ustar00rootroot00000000000000data-migrate-6.7.0/gemfiles/rails_4.1.gemfile000066400000000000000000000001611402322344200206760ustar00rootroot00000000000000# This file was generated by Appraisal source "http://rubygems.org" gem "rails", "4.1.16" gemspec path: "../" data-migrate-6.7.0/gemfiles/rails_4.2.gemfile000066400000000000000000000002451402322344200207020ustar00rootroot00000000000000# This file was generated by Appraisal source "http://rubygems.org" gem "rails", "4.2.11.1" gem "nokogiri", "1.6.8.1" gem "bundler" , "< 2.0" gemspec path: "../" data-migrate-6.7.0/gemfiles/rails_5.0.gemfile000066400000000000000000000002121402322344200206730ustar00rootroot00000000000000# This file was generated by Appraisal source "http://rubygems.org" gem "rails", "5.0.7.2" gem "sqlite3", "~> 1.4" gemspec path: "../" data-migrate-6.7.0/gemfiles/rails_5.1.gemfile000066400000000000000000000002101402322344200206720ustar00rootroot00000000000000# This file was generated by Appraisal source "http://rubygems.org" gem "rails", "5.1.7" gem "sqlite3", "~> 1.4" gemspec path: "../" data-migrate-6.7.0/gemfiles/rails_5.2.gemfile000066400000000000000000000002131402322344200206760ustar00rootroot00000000000000# This file was generated by Appraisal source "http://rubygems.org" gem "rails", "~> 5.2.3" gem "sqlite3", "~> 1.4" gemspec path: "../" data-migrate-6.7.0/gemfiles/rails_6.0.gemfile000066400000000000000000000002131402322344200206750ustar00rootroot00000000000000# This file was generated by Appraisal source "http://rubygems.org" gem "rails", "~> 6.0.0" gem "sqlite3", "~> 1.4" gemspec path: "../" data-migrate-6.7.0/gemfiles/rails_6.1.gemfile000066400000000000000000000002131402322344200206760ustar00rootroot00000000000000# This file was generated by Appraisal source "http://rubygems.org" gem "rails", "~> 6.1.0" gem "sqlite3", "~> 1.4" gemspec path: "../" data-migrate-6.7.0/lib/000077500000000000000000000000001402322344200146255ustar00rootroot00000000000000data-migrate-6.7.0/lib/capistrano/000077500000000000000000000000001402322344200167705ustar00rootroot00000000000000data-migrate-6.7.0/lib/capistrano/data_migrate.rb000066400000000000000000000001411402322344200217320ustar00rootroot00000000000000require 'capistrano' require File.expand_path("#{File.dirname(__FILE__)}/data_migrate/migrate") data-migrate-6.7.0/lib/capistrano/data_migrate/000077500000000000000000000000001402322344200214115ustar00rootroot00000000000000data-migrate-6.7.0/lib/capistrano/data_migrate/migrate.rb000066400000000000000000000025121402322344200233660ustar00rootroot00000000000000namespace :deploy do desc 'Runs rake data:migrate if migrations are set' Rake::Task['deploy:migrate'].clear_actions task :migrate => [:set_rails_env] do on fetch(:migration_servers) do conditionally_migrate = fetch(:conditionally_migrate) info '[deploy:migrate] Checking changes in db/migrate or db/data' if conditionally_migrate if conditionally_migrate && test("diff -q #{release_path}/db/migrate #{current_path}/db/migrate") && test("diff -q #{release_path}/db/data #{current_path}/db/data") info '[deploy:migrate] Skip `deploy:migrate` (nothing changed in db/migrate or db/data)' else info '[deploy:migrate] Run `rake db:migrate:with_data`' invoke :'deploy:migrating_with_data' end end end desc 'Runs rake db:migrate:with_data' task migrating_with_data: [:set_rails_env] do on fetch(:migration_servers) do within release_path do with rails_env: fetch(:rails_env) do execute :rake, 'db:migrate:with_data' end end end end after 'deploy:updated', 'deploy:migrate' end namespace :load do task :defaults do set :conditionally_migrate, fetch(:conditionally_migrate, false) set :migration_role, fetch(:migration_role, :db) set :migration_servers, -> { primary(fetch(:migration_role)) } end end data-migrate-6.7.0/lib/data_migrate.rb000066400000000000000000000041261402322344200175760ustar00rootroot00000000000000# frozen_string_literal: true if Rails::VERSION::MAJOR == 6 require File.join(File.dirname(__FILE__), "data_migrate", "data_migrator_five") elsif Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR == 2 require File.join(File.dirname(__FILE__), "data_migrate", "data_migrator_five") else require File.join(File.dirname(__FILE__), "data_migrate", "data_migrator") end require File.join(File.dirname(__FILE__), "data_migrate", "data_schema_migration") require File.join(File.dirname(__FILE__), "data_migrate", "data_schema") require File.join(File.dirname(__FILE__), "data_migrate", "database_tasks") require File.join(File.dirname(__FILE__), "data_migrate", "schema_dumper") if Rails::VERSION::MAJOR == 6 require File.join(File.dirname(__FILE__), "data_migrate", "status_service_five") require File.join(File.dirname(__FILE__), "data_migrate", "schema_migration_six") elsif Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR == 2 require File.join(File.dirname(__FILE__), "data_migrate", "status_service_five") require File.join(File.dirname(__FILE__), "data_migrate", "schema_migration_five") else require File.join(File.dirname(__FILE__), "data_migrate", "status_service") require File.join(File.dirname(__FILE__), "data_migrate", "schema_migration") end if Rails::VERSION::MAJOR == 6 require File.join(File.dirname(__FILE__), "data_migrate", "migration_context") # require File.join(File.dirname(__FILE__), "data_migrate", "migration_five") elsif Rails::VERSION::MAJOR == 5 if Rails::VERSION::MINOR == 2 require File.join(File.dirname(__FILE__), "data_migrate", "migration_context") else require File.join(File.dirname(__FILE__), "data_migrate", "migration_five") end else require File.join(File.dirname(__FILE__), "data_migrate", "migration") end require File.join(File.dirname(__FILE__), "data_migrate", "railtie") require File.join(File.dirname(__FILE__), "data_migrate", "tasks/data_migrate_tasks") require File.join(File.dirname(__FILE__), "data_migrate", "legacy_migrator") require File.join(File.dirname(__FILE__), "data_migrate", "config") module DataMigrate end data-migrate-6.7.0/lib/data_migrate/000077500000000000000000000000001402322344200172465ustar00rootroot00000000000000data-migrate-6.7.0/lib/data_migrate/config.rb000066400000000000000000000006151402322344200210420ustar00rootroot00000000000000module DataMigrate include ActiveSupport::Configurable class << self def configure yield config end def config @config ||= Config.new end end class Config attr_accessor :data_migrations_path, :db_configuration, :spec_name def initialize @data_migrations_path = "db/data/" @db_configuration = nil @spec_name = nil end end end data-migrate-6.7.0/lib/data_migrate/data_migrator.rb000066400000000000000000000060141402322344200224110ustar00rootroot00000000000000# frozen_string_literal: true require "active_record" module DataMigrate class DataMigrator < ActiveRecord::Migrator def record_version_state_after_migrating(version) if down? migrated.delete(version) DataMigrate::DataSchemaMigration.where(version: version.to_s).delete_all else migrated << version DataMigrate::DataSchemaMigration.create!(version: version.to_s) end end def load_migrated(connection = ActiveRecord::Base.connection) self.class.get_all_versions(connection) end class << self alias_method :migrations_status_orig, :migrations_status def migrations_status migrations_status_orig([DataMigrate.config.data_migrations_path]) end def current_version(connection = ActiveRecord::Base.connection) get_all_versions(connection).max || 0 end def get_all_versions(connection = ActiveRecord::Base.connection) if table_exists?(connection, schema_migrations_table_name) DataMigrate::DataSchemaMigration.all.map { |x| x.version.to_i }.sort else [] end end def schema_migrations_table_name ActiveRecord::Base.table_name_prefix + "data_migrations" + ActiveRecord::Base.table_name_suffix end def migrations_path DataMigrate.config.data_migrations_path end ## # Provides the full migrations_path filepath # @return (String) def full_migrations_path File.join(Rails.root, *migrations_path.split(File::SEPARATOR)) end def assure_data_schema_table ActiveRecord::Base.establish_connection(db_config) sm_table = DataMigrate::DataMigrator.schema_migrations_table_name unless table_exists?(ActiveRecord::Base.connection, sm_table) create_table(sm_table) end end ## # Compares the given filename with what we expect data migration # filenames to be, eg the "20091231235959_some_name.rb" pattern # @param (String) filename # @return (MatchData) def match(filename) /(\d{14})_(.+)\.rb$/.match(filename) end private def create_table(sm_table) ActiveRecord::Base.connection.create_table(sm_table, id: false) do |schema_migrations_table| schema_migrations_table.string :version, primary_key: true end end def table_exists?(connection, table_name) # Avoid the warning that table_exists? prints in Rails 5.0 due a # change in behavior between Rails 5.0 and Rails 5.1 of this method # with respect to database views. if ActiveRecord.version >= Gem::Version.new("5.0") && ActiveRecord.version < Gem::Version.new("5.1") connection.data_source_exists?(table_name) else connection.table_exists?(schema_migrations_table_name) end end def db_config ActiveRecord::Base.configurations[Rails.env || "development"] || ENV["DATABASE_URL"] end end end end data-migrate-6.7.0/lib/data_migrate/data_migrator_five.rb000066400000000000000000000054131402322344200234240ustar00rootroot00000000000000# frozen_string_literal: true require "active_record" require "data_migrate/config" module DataMigrate class DataMigrator < ActiveRecord::Migrator def self.migrations_paths [DataMigrate.config.data_migrations_path] end def self.assure_data_schema_table ActiveRecord::Base.establish_connection(db_config) DataMigrate::DataSchemaMigration.create_table end def initialize(direction, migrations, target_version = nil) @direction = direction @target_version = target_version @migrated_versions = nil @migrations = migrations validate(@migrations) DataMigrate::DataSchemaMigration.create_table ActiveRecord::InternalMetadata.create_table end def load_migrated @migrated_versions = DataMigrate::DataSchemaMigration.normalized_versions.map(&:to_i).sort end class << self def current_version DataMigrate::MigrationContext.new(migrations_paths).current_version end ## # Compares the given filename with what we expect data migration # filenames to be, eg the "20091231235959_some_name.rb" pattern # @param (String) filename # @return (MatchData) def match(filename) /(\d{14})_(.+)\.rb$/.match(filename) end def needs_migration? DataMigrate::DatabaseTasks.pending_migrations.count.positive? end ## # Provides the full migrations_path filepath # @return (String) def full_migrations_path File.join(Rails.root, *migrations_paths.split(File::SEPARATOR)) end def migrations_status DataMigrate::MigrationContext.new(migrations_paths).migrations_status end # TODO: this was added to be backward compatible, need to re-evaluate def migrations(_migrations_paths) #DataMigrate::MigrationContext.new(migrations_paths).migrations DataMigrate::MigrationContext.new(_migrations_paths).migrations end #TODO: this was added to be backward compatible, need to re-evaluate def run(direction, migration_paths, version) DataMigrate::MigrationContext.new(migration_paths).run(direction, version) end def rollback(migrations_path, steps) DataMigrate::MigrationContext.new(migrations_path).rollback(steps) end def db_config ActiveRecord::Base.configurations[Rails.env || "development"] || ENV["DATABASE_URL"] end end private def record_version_state_after_migrating(version) if down? migrated.delete(version) DataMigrate::DataSchemaMigration.where(version: version.to_s).delete_all else migrated << version DataMigrate::DataSchemaMigration.create!(version: version.to_s) end end end end data-migrate-6.7.0/lib/data_migrate/data_schema.rb000066400000000000000000000031261402322344200220260ustar00rootroot00000000000000# frozen_string_literal: true module DataMigrate ## # Provides the definition method for data_schema.rb class Data < ActiveRecord::Schema # This method is based on the following two methods # ActiveRecord::Schema#define # ActiveRecord::ConnectionAdapters::SchemaStatements # #assume_migrated_upto_version def define(info) DataMigrate::DataMigrator.assure_data_schema_table return if info[:version].blank? version = info[:version].to_i unless migrated.include?(version) execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')" end insert(version) end private def migrated @migrated ||= select_values("SELECT version FROM #{sm_table}").map(&:to_i) end def versions @versions ||= begin versions = [] Dir.foreach(DataMigrate::DataMigrator.full_migrations_path) do |file| match_data = DataMigrate::DataMigrator.match(file) versions << match_data[1].to_i if match_data end versions end end def insert(version) inserted = Set.new (versions - migrated).each do |v| if inserted.include?(v) raise "Duplicate data migration #{v}. Please renumber your data " \ "migrations to resolve the conflict." elsif v < version execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" inserted << v end end end def sm_table quote_table_name(table_name) end def table_name DataMigrate::DataSchemaMigration.table_name end end end data-migrate-6.7.0/lib/data_migrate/data_schema_migration.rb000066400000000000000000000004611402322344200240760ustar00rootroot00000000000000module DataMigrate class DataSchemaMigration < ::ActiveRecord::SchemaMigration class << self def table_name ActiveRecord::Base.table_name_prefix + 'data_migrations' + ActiveRecord::Base.table_name_suffix end def primary_key "version" end end end end data-migrate-6.7.0/lib/data_migrate/database_tasks.rb000066400000000000000000000065441402322344200225550ustar00rootroot00000000000000# frozen_string_literal: true require "data_migrate/config" module DataMigrate ## # This class extends DatabaseTasks to add a schema_file method. class DatabaseTasks extend ActiveRecord::Tasks::DatabaseTasks class << self def schema_file_type(_format = nil) "data_schema.rb" end def dump_filename(namespace, format = ActiveRecord::Base.schema_format) filename = if namespace == "primary" schema_file_type(format) else "#{namespace}_#{schema_file_type(format)}" end ENV["DATA_SCHEMA"] || File.join(schema_location, filename) end def schema_location db_dir end def check_schema_file(filename) unless File.exist?(filename) message = +%{#{filename} doesn't exist yet. Run `rake data:migrate` to create it, then try again.} Kernel.abort message end end end # This overrides ActiveRecord::Tasks::DatabaseTasks def self.schema_file(_format = nil) File.join(db_dir, "data_schema.rb") end def self.forward(step = 1) DataMigrate::DataMigrator.assure_data_schema_table migrations = pending_migrations.reverse.pop(step).reverse migrations.each do | pending_migration | if pending_migration[:kind] == :data ActiveRecord::Migration.write("== %s %s" % ["Data", "=" * 71]) DataMigrate::DataMigrator.run(:up, data_migrations_path, pending_migration[:version]) elsif pending_migration[:kind] == :schema ActiveRecord::Migration.write("== %s %s" % ["Schema", "=" * 69]) DataMigrate::SchemaMigration.run(:up, schema_migrations_path, pending_migration[:version]) end end end def self.data_migrations_path DataMigrate.config.data_migrations_path end def self.schema_migrations_path "db/migrate/" end def self.pending_migrations sort_migrations(pending_schema_migrations, pending_data_migrations) end def self.pending_data_migrations data_migrations = DataMigrate::DataMigrator.migrations(data_migrations_path) sort_migrations(DataMigrate::DataMigrator.new(:up, data_migrations ). pending_migrations.map {|m| { version: m.version, name: m.name, kind: :data }}) end def self.pending_schema_migrations ::DataMigrate::SchemaMigration.pending_schema_migrations end def self.sort_migrations(set1, set2 = nil) migrations = set1 + (set2 || []) migrations.sort {|a, b| sort_string(a) <=> sort_string(b)} end def self.sort_string(migration) "#{migration[:version]}_#{migration[:kind] == :data ? 1 : 0}" end def self.past_migrations(sort = nil) sort = sort.downcase if sort db_list_data = if DataMigrate::DataSchemaMigration.table_exists? DataMigrate::DataSchemaMigration.normalized_versions.sort else [] end db_list_schema = ActiveRecord::SchemaMigration.normalized_versions.sort.sort migrations = db_list_data.map do |d| { version: d.to_i, kind: :data } end + db_list_schema.map do |d| { version: d.to_i, kind: :schema } end sort == "asc" ? sort_migrations(migrations) : sort_migrations(migrations).reverse end end end data-migrate-6.7.0/lib/data_migrate/legacy_migrator.rb000066400000000000000000000011601402322344200227410ustar00rootroot00000000000000module DataMigrate class LegacyMigrator def initialize(migrations_paths = "db/data") @migrations_paths = migrations_paths || "db/data" end def migrate dates = DataMigrate::DataMigrator.migrations(@migrations_paths).collect(&:version) legacy = ActiveRecord::SchemaMigration.where(version: dates) legacy.each do |v| begin version = v.version puts "Creating #{version} in data schema" DataMigrate::DataSchemaMigration.create(version: version) rescue ActiveRecord::RecordNotUnique nil end end end end end data-migrate-6.7.0/lib/data_migrate/migration.rb000066400000000000000000000011771402322344200215720ustar00rootroot00000000000000module DataMigrate class Migration < ::ActiveRecord::Migration class << self def check_pending!(connection = ::ActiveRecord::Base.connection) raise ActiveRecord::PendingMigrationError if DataMigrator::Migrator.needs_migration?(connection) end def migrate(direction) new.migrate direction end def table_name ActiveRecord::Base.table_name_prefix + "data_migrations" + ActiveRecord::Base.table_name_suffix end def primary_key "version" end end def initialize(name = self.class.name, version = nil) super(name, version) end end end data-migrate-6.7.0/lib/data_migrate/migration_context.rb000066400000000000000000000047441402322344200233410ustar00rootroot00000000000000module DataMigrate class MigrationContext < ActiveRecord::MigrationContext def initialize(migrations_paths = "db/data") @migrations_paths = migrations_paths || "db/data" end def up(target_version = nil) selected_migrations = if block_given? migrations.select { |m| yield m } else migrations end DataMigrator.new(:up, selected_migrations, target_version).migrate end def down(target_version = nil) selected_migrations = if block_given? migrations.select { |m| yield m } else migrations end DataMigrator.new(:down, selected_migrations, target_version).migrate end def run(direction, target_version) DataMigrator.new(direction, migrations, target_version).run end def current_version get_all_versions.max || 0 rescue ActiveRecord::NoDatabaseError end def migration_files paths = Array(migrations_paths) Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }] end def migrations_status db_list = DataSchemaMigration.normalized_versions file_list = migration_files.map do |file| version, name, scope = parse_migration_filename(file) raise ActiveRecord::IllegalMigrationNameError.new(file) unless version version = ActiveRecord::SchemaMigration.normalize_migration_number(version) status = db_list.delete(version) ? "up" : "down" [status, version, (name + scope).humanize] end.compact db_list.map! do |version| ["up", version, "********** NO FILE **********"] end (db_list + file_list).sort_by { |_, version, _| version } end private def get_all_versions if DataMigrate::DataSchemaMigration.table_exists? DataSchemaMigration.normalized_versions.map(&:to_i) else [] end end def move(direction, steps) migrator = DataMigrator.new(direction, migrations) if current_version != 0 && !migrator.current_migration raise ActiveRecord::UnknownMigrationVersionError.new(current_version) end start_index = if current_version.zero? 0 else migrator.migrations.index(migrator.current_migration) end finish = migrator.migrations[start_index + steps] version = finish ? finish.version : 0 send(direction, version) end end end data-migrate-6.7.0/lib/data_migrate/migration_five.rb000066400000000000000000000012101402322344200225670ustar00rootroot00000000000000module DataMigrate class MigrationFive < ::ActiveRecord::Migration[5.0] class << self def check_pending!(connection = ::ActiveRecord::Base.connection) raise ActiveRecord::PendingMigrationError if DataMigrator::Migrator.needs_migration?(connection) end def migrate(direction) new.migrate direction end def table_name ActiveRecord::Base.table_name_prefix + 'data_migrations' + ActiveRecord::Base.table_name_suffix end def primary_key "version" end end def initialize(name = self.class.name, version = nil) super(name, version) end end end data-migrate-6.7.0/lib/data_migrate/railtie.rb000066400000000000000000000004151402322344200212240ustar00rootroot00000000000000module DataMigrate class Railtie < ::Rails::Railtie generators = config.respond_to?(:app_generators) ? config.app_generators : config.generators rake_tasks do load File.join(File.dirname(__FILE__), '..', '..', 'tasks/databases.rake') end end end data-migrate-6.7.0/lib/data_migrate/schema_dumper.rb000066400000000000000000000020031402322344200224020ustar00rootroot00000000000000# frozen_string_literal: true module DataMigrate ## # Provides the capability to write the current data schema version to # the data_schema file Based on ActiveRecord::SchemaDumper class SchemaDumper private_class_method :new class << self def dump(connection = ActiveRecord::Base.connection, stream = STDOUT) new(connection).dump(stream) stream end end def dump(stream) define_params = @version ? "version: #{@version}" : "" if stream.respond_to?(:external_encoding) && stream.external_encoding stream.puts "# encoding: #{stream.external_encoding.name}" end stream.puts "DataMigrate::Data.define(#{define_params})" stream end private def initialize(connection) @connection = connection all_versions = DataSchemaMigration.normalized_versions @version = begin all_versions.max rescue StandardError 0 end end end end data-migrate-6.7.0/lib/data_migrate/schema_migration.rb000066400000000000000000000016741402322344200231140ustar00rootroot00000000000000module DataMigrate # Helper class to getting access to db schema # to allow data/schema combiation tasks class SchemaMigration def self.pending_schema_migrations all_migrations = DataMigrate::DataMigrator.migrations(migrations_paths) sort_migrations( ActiveRecord::Migrator.new(:up, all_migrations). pending_migrations. map {|m| { version: m.version, name: m.name, kind: :schema }} ) end def self.run(direction, migration_paths, version) ActiveRecord::Migrator.run(direction, migration_paths, version) end def self.sort_migrations(set1, set2 = nil) migrations = set1 + (set2 || []) migrations.sort {|a, b| sort_string(a) <=> sort_string(b)} end def self.migrations_paths Rails.application.config.paths["db/migrate"].to_a end def self.sort_string(migration) "#{migration[:version]}_#{migration[:kind] == :data ? 1 : 0}" end end end data-migrate-6.7.0/lib/data_migrate/schema_migration_five.rb000066400000000000000000000017201402322344200241150ustar00rootroot00000000000000module DataMigrate # Helper class to getting access to db schema # to allow data/schema combiation tasks class SchemaMigration def self.pending_schema_migrations all_migrations = DataMigrate::MigrationContext.new(migrations_paths).migrations sort_migrations( ActiveRecord::Migrator.new(:up, all_migrations). pending_migrations. map {|m| { version: m.version, name: m.name, kind: :schema }} ) end def self.run(direction, migration_paths, version) ActiveRecord::MigrationContext.new(migration_paths).run(direction, version) end def self.sort_migrations(set1, set2 = nil) migrations = set1 + (set2 || []) migrations.sort {|a, b| sort_string(a) <=> sort_string(b)} end def self.migrations_paths Rails.application.config.paths["db/migrate"].to_a end def self.sort_string(migration) "#{migration[:version]}_#{migration[:kind] == :data ? 1 : 0}" end end end data-migrate-6.7.0/lib/data_migrate/schema_migration_six.rb000066400000000000000000000026111402322344200237670ustar00rootroot00000000000000module DataMigrate # Helper class to getting access to db schema # to allow data/schema combiation tasks class SchemaMigration def self.pending_schema_migrations all_migrations = DataMigrate::MigrationContext.new(migrations_paths).migrations sort_migrations( ActiveRecord::Migrator.new(:up, all_migrations, ActiveRecord::Base.connection.schema_migration). pending_migrations. map {|m| { version: m.version, kind: :schema }} ) end def self.run(direction, migration_paths, version) ActiveRecord::MigrationContext.new(migration_paths, ActiveRecord::Base.connection.schema_migration).run(direction, version) end def self.sort_migrations(set1, set2 = nil) migrations = set1 + (set2 || []) migrations.sort {|a, b| sort_string(a) <=> sort_string(b)} end def self.migrations_paths spec_name = DataMigrate.config.spec_name if spec_name && Rails.version > '6.1' ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: spec_name).migrations_paths elsif spec_name ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name).migrations_paths else Rails.application.config.paths["db/migrate"].to_a end end def self.sort_string(migration) "#{migration[:version]}_#{migration[:kind] == :data ? 1 : 0}" end end end data-migrate-6.7.0/lib/data_migrate/status_service.rb000066400000000000000000000034541402322344200226440ustar00rootroot00000000000000module DataMigrate class StatusService class << self def dump(connection = ActiveRecord::Base.connection, stream = STDOUT) new(connection).dump(stream) stream end end def initialize(connection) @connection = connection end def root_folder Rails.root end def dump(stream) unless @connection.table_exists?(table_name) stream.puts "Data migrations table does not exist yet." return end sql = "SELECT version FROM #{DataMigrate::DataMigrator.schema_migrations_table_name}" db_list = ActiveRecord::Base.connection.select_values(sql) output(stream, db_list) end private def table_name DataMigrate::DataMigrator.schema_migrations_table_name end def output(stream, db_list) stream.puts "#{"Status".center(8)} #{"Migration ID".ljust(14)} Migration Name" stream.puts "-" * 50 list = migration_files(db_list) + migration_list(db_list) list.sort! {|line1, line2| line1[1] <=> line2[1]} list.each do |file| stream.puts "#{file[0].center(8)} #{file[1].ljust(14)} #{file[2]}" end stream.puts end def migration_list(db_list) list = [] db_list.each do |version| list << ["up", version, "********** NO FILE *************"] end list end def migration_files(db_list) file_list = [] Dir.foreach(File.join(root_folder, DataMigrate.config.data_migrations_path)) do |file| # only files matching "20091231235959_some_name.rb" pattern if match_data = DataMigrate::DataMigrator.match(file) status = db_list.delete(match_data[1]) ? "up" : "down" file_list << [status, match_data[1], match_data[2].humanize] end end file_list end end end data-migrate-6.7.0/lib/data_migrate/status_service_five.rb000066400000000000000000000021061402322344200236460ustar00rootroot00000000000000module DataMigrate class StatusService class << self def dump(connection = ActiveRecord::Base.connection, stream = STDOUT) new(connection).dump(stream) stream end end def initialize(connection) @connection = connection end def root_folder Rails.root end def dump(stream) output(stream) end private def table_name DataMigrate::DataSchemaMigration.table_name end def output(stream) unless DataMigrate::DataSchemaMigration.table_exists? stream.puts "Data migrations table does not exist yet." return end # output stream.puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n" stream.puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name" stream.puts "-" * 50 db_list.each do |status, version, name| stream.puts "#{status.center(8)} #{version.ljust(14)} #{name}" end stream.puts end def db_list DataMigrate::DataMigrator.migrations_status end end end data-migrate-6.7.0/lib/data_migrate/tasks/000077500000000000000000000000001402322344200203735ustar00rootroot00000000000000data-migrate-6.7.0/lib/data_migrate/tasks/data_migrate_tasks.rb000066400000000000000000000027401402322344200245510ustar00rootroot00000000000000module DataMigrate module Tasks module DataMigrateTasks extend self def migrations_paths @migrations_paths ||= DataMigrate.config.data_migrations_path end def dump if ActiveRecord::Base.dump_schema_after_migration filename = DataMigrate::DatabaseTasks.schema_file ActiveRecord::Base.establish_connection(DataMigrate.config.db_configuration) if DataMigrate.config.db_configuration File.open(filename, "w:utf-8") do |file| DataMigrate::SchemaDumper.dump(ActiveRecord::Base.connection, file) end end end def migrate DataMigrate::DataMigrator.assure_data_schema_table target_version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil if (Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR == 2) || Rails::VERSION::MAJOR == 6 DataMigrate::MigrationContext.new(migrations_paths).migrate(target_version) else DataMigrate::DataMigrator.migrate(migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) end end def abort_if_pending_migrations(migrations, message) if migrations.any? puts "You have #{migrations.size} pending #{migrations.size > 1 ? 'migrations:' : 'migration:'}" migrations.each do |pending_migration| puts " %4d %s" % [pending_migration[:version], pending_migration[:name]] end abort message end end end end end data-migrate-6.7.0/lib/data_migrate/version.rb000066400000000000000000000000621402322344200212560ustar00rootroot00000000000000module DataMigrate VERSION = "6.7.0".freeze end data-migrate-6.7.0/lib/generators/000077500000000000000000000000001402322344200167765ustar00rootroot00000000000000data-migrate-6.7.0/lib/generators/data_migrate.rb000066400000000000000000000005061402322344200217450ustar00rootroot00000000000000require 'rails/generators/named_base' module DataMigrate module Generators class DataMigrationGenerator < Rails::Generators::NamedBase #:nodoc: def self.source_root @_data_migrate_source_root ||= File.expand_path(File.join(File.dirname(__FILE__), generator_name, 'templates')) end end end end data-migrate-6.7.0/lib/generators/data_migration/000077500000000000000000000000001402322344200217605ustar00rootroot00000000000000data-migrate-6.7.0/lib/generators/data_migration/data_migration_generator.rb000066400000000000000000000025301402322344200273350ustar00rootroot00000000000000require "generators/data_migrate" require "rails/generators" require "rails/generators/active_record/migration" require "rails/generators/migration" require "data_migrate/config" module DataMigrate module Generators class DataMigrationGenerator < Rails::Generators::NamedBase namespace "data_migration" include ActiveRecord::Generators::Migration argument :attributes, type: :array, default: [], banner: "field:type field:type" def create_data_migration set_local_assigns! migration_template "data_migration.rb", data_migrations_file_path end protected def set_local_assigns! if file_name =~ /^(add|remove)_.*_(?:to|from)_(.*)/ @migration_action = $1 @table_name = $2.pluralize end end def migration_base_class_name if ActiveRecord.version >= Gem::Version.new("5.0") "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]" elsif ActiveRecord.version >= Gem::Version.new("5.2") "DataMigrate::MigrationContext" else "ActiveRecord::Migration" end end def data_migrations_file_path File.join(data_migrations_path, "#{file_name}.rb") end def data_migrations_path DataMigrate.config.data_migrations_path end end end end data-migrate-6.7.0/lib/generators/data_migration/templates/000077500000000000000000000000001402322344200237565ustar00rootroot00000000000000data-migrate-6.7.0/lib/generators/data_migration/templates/data_migration.rb000066400000000000000000000002301402322344200272600ustar00rootroot00000000000000class <%= migration_class_name %> < <%= migration_base_class_name %> def up end def down raise ActiveRecord::IrreversibleMigration end end data-migrate-6.7.0/lib/generators/data_migration/templates/migration.rb000066400000000000000000000011601402322344200262720ustar00rootroot00000000000000class <%= migration_class_name %> < ActiveRecord::Migration def self.up <% attributes.each do |attribute| -%> <%- if migration_action -%> <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end %> <%- end -%> <%- end -%> end def self.down <% attributes.reverse.each do |attribute| -%> <%- if migration_action -%> <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end %> <%- end -%> <%- end -%> end end data-migrate-6.7.0/screenshot.png000066400000000000000000001261461402322344200167540ustar00rootroot00000000000000‰PNG  IHDR C|æÚusRGB®Îé@IDATxì]`”Eöÿ}ÛR!“®õ„„ EÚÿD+ž(Bð‚§ BB ¤ E”Pz!*ÒÄ‚Rä,P’IŽÝM¶ÿßÌ·›F² &”7ÊÎ~óͼ™yóæ½÷›™ì(žÏr Š8f>°ð<`=Àz œô€òçùlay80˜Ìæs \9 Q‘¨Óc‡8VG€ùÀ|àyÁzõÀ_©©ÜåOæs€9À(gè*zïgB|,§º¬Ûaá1:6žÒ…Â{TÌ–ž¬n6=@H ª¾ÇN§¹¹9 *UyQ«0>É>_ ….;¹Ÿ}o—ZË1XXX®×y@Kp¥?÷Ѽ$ÿã(bÆÄ!8Øc„|óT{õŒT營aᄎÆS ¡\|«ó3¿XnxÞ°¸>ôB(½‡Ø¤IN<‰‰‰ãa4šÈˆà+=µ”/Ÿ‡S–¢_ÛÅùÕ1a>0XX®½”éÜýíÚã­#‘vú¡qd„ŒÔBaYE(i¬æöýóÊôMY‘‘%Œb~>«UÔâ‚)+&“Ó]¥xo¥ÿDlB•SMi~95#?3XXX®Õ<(ÓÝáD³ûZ£K×nX»f–/Yˆÿ¬Êg‰=&wvŸ#oÙÌøvf©F ySæDå#K1`ì稥·ã¬MU" ›Ž¾­îÀ‘Uшû̆ÛdËvÔÆÐ©ïâ¾:~ôä­NW‡ŒùÀ|`9`9ð]Ê„€Du{väõ£Aƒ;Ñ/ª?¥øê! *¥ Å×c=sx`0–mØD‡–¢ýÿv`ç#´:aLlh9jž<ô0#æ)ì˜:'á„^+_áõy¢Ì:ŒìhÅô©[‰P|=œÎ|‘âÁò¡²ùÀ|ðM/” íúökÌ›3õêÕÇø¸h þîAQI=¢E|úZ<}ÿºíÑ¥õ—X”€ts:þër¡““ì¬4'йi5˜r,¨Þªê`+2³„XÍдé&UmôNAË'†ë‹È¥R†÷£øö”œ\^~æóAp€åàVƒ2! ßM)`|ÂÁ7 ¨–)ÍgñõœÚ1CÆ®AÍtÄó/¾„fŠ÷ŠUbBŽÕ.+³ÙÜKt~n:V ,vñÝ ›Cm0[¾÷§øv1æ‹*UÌæë>Ô(nO£4ñ€A¯">a‚D>¥)¯ bé>½Õg=}JD$žêpjùgá! 9S a`Ö}}œb+Žn[ƒ44D 23†@8Z€oS”/ Û–¯‡Ò B)§·z8]õP™Ì1ƒYXJ#:—D, } ÏÌ·ØfwBÑéE½¥*/ê+mðÖÞF]^C½~ãñ|—€^˜ÕÖª~gdm‰^súÑ#*q1j9]¸DOJ:Øý"æŠúæH\Ô‘°£ÔýòÖ>N÷]Î|•KÎïۇ݆¬ƒ"¡5bfEÁ`3Âf†–Òsi)Îf=Cócæìư­Ð멼ZŠ«hþpý#—Ìwæ{qú…åâÚÊE™еðœÆÇ'B«ÇÐÔá.Iì Ô’kgÔ3ÔržØá°Áœã€Fã‚5GØ4õ½.(JP6þ—›‹ íWåäÒBœ@`.†ÖFP¦…ÊY !@gϱú)L×CŸcæKqrÇrÁrÁrq¹>¾Ú¼PÎÑ}@ªèxTõÍkõzø“³äÚ Ž!¨¦Gõ~ZsÂd±J¿øRÜßÂrÁü`~ðüø‹íAY~ ŽÅ“Åó/Ï<Ëõ°9d}sóé›2‚#í@ˆA0…cæËÏÖ¬|Ó´S"<+á_rÌ|`9àyÀz€õ@ùéF@ŒàÄ|c$ËrÀrÀó Üõ# F~Rñ°çÏž¿––ƒò”F@ìùJÄk×¾­]3¿˜_bâ°”M1’ˆ=_ö|… °°”§0b$ {reóä˜Ì?1‘X|“F@Œ€¤bÏ—=_!,,å)Œ€IÄž›ožó‹ù%&ËAÙä€# i€ØóeÏWËËAyÊA…ÿÜÛñã’rP*Á‚aá1:6^â!ÂÎrÌ|`9àyÀzàæÑeú5l§øUê\3ƒÝž“ᅥ*ŒOò†O/3/]:w’öHõÈ|§ËåÔiÊ|`>uÅrÀrp=ÊA™ЇóæàøÇ3f<* #ä»eîÕ3R_>ÂÂ#0&6ÎK}™„¨Œ¿ã²÷ÆÔ}Ø“Q ´ºƒ1¼4ãÅ„ùÆr㻞ãySü¼)ÓP“&8uò&&ŽÇ%£¸WTT"ijä±ÌìãÇaBM^ë1ŸÄ”øO`.¦çö-À¢ù?Ð=@%oŸ×zŠ¡/ºÁù}æó‹çÍ­«7Êt îþvíñÖˆh¤>EF(F£‘…°õÂC(Y,3—âÃ+}ÅÕCé¶Sªß”•‰l“J\äë„Õ‹zÀ_¶Ï&_ØLYÈÈ2•¸½^ë-a¹|Éä‚ùÄ|”åàæ–ƒ2íÙN4»¯%ºt통kVaù’…ðïפبžíÕמ¥(Ňwútó¶é'Ä ø '²Uï²å ix£ý¸øS†|~?æ¿ ‡õÅšt `S ‘ÞI±$‘ÍÕÛí½~Q'—gþ°ð<`=p5=P&$ìÆÞÝ»‘¼þc4hp'úEõ÷Ùc)…í‘E¼zFt¥¶Í–†»úÏ”‡&öÀþ9ïàWZwÓjýXY\ÿ­Õì‚…åt"y^4\)+qÆDiôŸ3Xx°økõ@™ö€v}û5>˜>wÜQãã ÷ ðyDjûR|¨–µ¸µSÀ„úèÔ®L9„=†VÊYütLÝ£Ê«Š–æîÚšœ\äT½ªÃN€È;]^«¼cþ°°ð<¸Vz Lè÷_SP¯^}i|t±³â»Ç$ •âûg"ˆYá°Ú%U‡Í"øùë Õ""èh ÑIXÇiwBô×ZzïíåzÅÀ0XXn½yP&4`ЫˆK˜-ŸÒZD!t¥ ÞëíãœÁòÏJÓö}2~qÕÃý÷[M>õuþ3{º‚#Ì––ž•Ð ÏSL±ÒÄ6BZ¡ÔåÕ©-ÄÛ÷ൽt¦@Ú†ýÑo±EŽLüuí.dÓ“Ç …â¹@Ï:žxž9.½\”Vž¸\¾<²ü±üÝóáÏóYÔOÿÕî–w–ÿbô Ïÿ¿D/–é—®ÅT ðÓC£ÑgJ¼¹Ýé„ÅbóúÞÓ.EQýH%°H±iñÈÇ<X\'ú ¢Ðu²X-±ZâyÀz <õ€ûœçï_8˜ILBŽ™,<XüÅz€{<åéñ°‡ÍòÆòÆHÛ£1âaÄLj—?ëŠÑŒ€Ø#e”=RGÊ1ëƒòÔŒ€Øó©χùÎ|gäÉÈ“{<åéñ°‡ÍòÆòƈۣ±'Ξ8{â쉳¨=Àˆ=RöHÙ#õx¤³>(O}Àˆ=ŸŠñ|˜ïÌwFžŒ<±ÇSž{Ø,o,oŒ¸=z€{â쉳'Ξ8ëŠÑ€&ÄÇâpÊ!ê|áÞ£cã)‘=FöÙcäyÀzàfÔîû€Ôß|—©¿]²ØátÀ’›‹€À@ŸÊ¬GŸä ›.+ߥóÓÒøøÒž‚t¹œ¸O¥dãÈù˜O<_x¾Tˆ( úh^Žÿq”îæ‰Cp°¸êÍwOµWÏÈÂЧOaᄎÆû\ß2v„¬BM'°ãû hûØ}tA{V¥7öÈYnXn|×wÄÎÛH‡>ÅÈG²ðþªw»(%K¯ @°Þ:jÿñS± }±ŒB$oHBÓÿÎÇŠŸ.R?Ùª¨i+T:mõX¾ã"^šŽøgªÁï‰D¬_Ü•=¢ã­=œÎüaùàùÁz x=P&ôí·_aÞœY¨W¯>ÆÇ%@kðóÙÓ/…í‘E¼yP§ö}Mó>¸·ª¦Z ^ƒupÀœ{)¯\~Fúæ4\¦=˜žð2" ޹\øß¾3èó¸@9ãuˆŽa?Œ] ¬+‚—p”+áòך…yk§³ÇÇòÁóƒõ@a= +‹eþý×Ã…ŒOi<½|càÛ7oíö¢smYVäÚÅ’š‚\:&.b}DPËɯô¡•oª´x‘OÔ…ÖÚôÝÿ…àÐÚp]ø™Þ(ÍaV:,T¸<{vÞÆÓ‹÷ø˜/Ì—ÒèÉ›UnÊt nÀ #žN–yOiN]xL¯qAäQ°Þ;[w‚ëx¾<’I$øfROôyw7™a„ÄЫ±º³ˆÖïBæ·ÇP7aw…`Ïâ9øÏy€¤Ñr/Õ~©åmfØÒ³è‚êѬß[»8]å#óùÀó…õ†G” Ùì¤Ðub›¾ôH@¨ùÒo­Öc˜8è'ÄŒ} a}s$ÎkG¦B]‚SËQzh· µŸƒ¾?ÀÏo¹aˆè÷V‚î¸ÊÚ(‡Çc£"²„ :ô‚vä{tá’æö¦ôÒ÷?Ÿ¾§ŽË"OÌO––ŸG)çÎgÓ®GÅ­ÍNš‡ƒHå_ÒÆEüê­Ý?=ô»<:l e4«V‡þþ:¸è{.-½4ô+´ŸCßEºÖf¢×AðÓÓÁkÚçqê  dXrl°SM::´`Ð:“ë€F¯G€ŽŽp»œDC}/,º·öpzÅÉ Ë%Ï¿ëxþ•å—„§Qh™ªÏþ~¤ü5ZŸè8œ.ä’‘¸býÔ4­B{7Nu/èjíÔÐb¤Îæ¿=~¯zâÌæÃç)¹mü¾ìzôFge:w-< ‹Eà ™®kì©¹ÈøB))]Êʆ8PR~q¾k,¯×Zþ™Ïça>W4ºQ-7·›=WöÜo]Ïçÿµ™ÿe:'AxÂ3Xx°`=à³`tm,9{DÌGFDŒˆXø¦1‚cËHžW2XTŒ`ä›Åf‡ùÅH‡‘ëk£±çS1žóùÎÈ“‘'# kcÉÙ#b>22bdÄzÀ7=Àˆ=qöÄÙgOœõ@ÅèF@¾Ylöp˜_Œté°¸6z€{>ãù0ß™ïŒ<y2º6–œ="æ###FF¬|Óþ[poÇGJÊAò ‡°ðÆ'=„kñ›sâ/t™ÿÖËÏÖ×(Ó}@Nñ«Ô¹&Kå.<@1¸¾ÄÂø$oø”ʶœ]:wò‰Ž¯õr~߯‰ùÅüòe^³¼°¼”D^ÊtЇófãø fÌ8T ®$¯f¯žÝ CŸ< t4&6¾TõùÚ>…~©;íÈï0ÖÇÝ¡Øñ}&Ú>ÖÁŒ¨Ê‰ÿì± eî»Ü2ߘo׿ÜhÊâ©4iÒ§NžÀÄÄ8\2ŠG}G@%°7—e9œrH*¿ÒÔçk÷Mí‡cÇáóßÒaýs/Î_ˆtºŠÈW:œŸ=ÂòW–3–³IÎÊt îþvâ­ÑH;}J!#!á© õ\Òø2ëR„«Ñ·™²‘qQÞ4T¨=V3¥ŸƒÙZ¸âF"X8Ÿ‘k^ûÓqh¿Ç.Bô“‡£ÞX³v.ù‹!VËggdÀdRï²ZKÞoOyŽ óƒùQh¾ºçËÅM*e=§Õ*X¿v Ö®Y…|þý™•Y%‰{õŒ,¡É)œmÙª½Öóó’7ñîç'ÝjaèÔ©¸¯Ž?,¦ï›ò=2l:ú¶º©£1fÅ J·¹ß5EÜü7q(¶Ö¤9dZ•'ÇaF7'^Ú)«ÑåÝiHêó:v‘Ñ¡VmrÂŒ¨z*yÿÅdãüÌ/–ž·¢(“fïîÝH^ÿ14¸ý¢¢HõªÊ¤¤±PÞ¥ ÞèÛR7’ñ±cܼMt¸a¢ÉÆû«R«Žâ2>Ï$~$Ó&öÀŽ©SpL,§Y„á‰À´µñɆ$´Rþ‹¯N(è1k:4蘰 ^ihü¬÷‡Žú}fã{ø.ðY,¤ÉT¦IŽŠ¿Æçþ{ë§û&GÌ/æ—ÐG,7–”ih×·ßàƒéSqÇu1>.z¿@ßÖ`Kc|DoõœØ·šæÏ#¢ª¦î¼ëF·ñèœA<ö7J·" ì124g±ï„ -Ëù=‰:.Œ95Ðéá¿A!Îää:@7uÃåpÀ$”Z³@J?m>…6ŸA@N.L¹5ñTäÝ0ñÞ×qñ6^œîÛ|a~1¿RºYä Lè÷_SP¯^}i|tÚ!¶øê•^šà­ž€  ¸²,°ÚU'¬¹dˆrmð7г96iRà°QJñó׺Þ(/+W¨ŒSDoTO‚¾PðôK} ¨q»éfÃ!ò¹ìÈÍÌ”/½µ‹Óo,όNjÇ+Þ{æ?Ç×z^”  ô*â&@KƧ´Ù£Ò}½Õ× u'¸ŽÏÅŽ#ˆ¤_Mê>ïî®AÔÇÏXùõQJWúýzü⪇ûïÆÆ<ž…xö¬I‹ïÓÅr‚¿?Þ,šŽïœÂ™”mx{]‚ÈÆyk§³çš/Gyâ˜çÅ­=/ÊôK6»ZA*]a…0ù õ^šà­G­Ç1qЈû ÂúæHœ×N€$LyƒGŒD¯9ò¢£®Ý…ß Qðx8TŒz"žCƒ\R<ý¢$ae%ÝÖ.ñY;ö (uZ£oŸ–ØzN¨˜ÒñË1ßJ3XnXnnh¹)ë)¸|¤àA ¾Å“&ŒÇÁH©—<4Žh"ÿøU¨ûâê÷óóƒNCKotÔ:È ‡ÝjƒÅá„No€L®ž ”î´ÛiIΙ_¡}Úóôüüý uØ`¶)´t§j>hô4(´¬gÃù”ϰñ;õ\K¨ËßO슩!c±¤_8Ñ(¾]œÎ|)N^Y.X.nU¹(Ó/!\ Ëàg€FîD•Ü“±;°Xìd*<Ȥ˜XÑ@CÈÅI[>…óÑI5ZxT(Ýq¥òWA2zãïÙ4R‰†"»0 œEê+y¿ ·“Ë1?Š‘ë«È%#"ž77Ô¼©htÃZ~­–ÐæÌ‹´zg@•*AyH‹=ZöhoX¹–Ÿo+Y¥åW™NÁ‰J…ÇuKÆt4Ûœc>¨i_(—Žv‹e¾[–·ªp¿oÍùÏã~mÆPùXúÒz\ŽÇ‡5#²›U0bOæÚx2ÌGæã­¼"Âò_:ùgÄ6{Øìa߬6÷ëúÖoŒ€Øs)çÂ|c¾1â¡Yp‹î_«~3º¾=öàx|¡2B½Yõ# keÉ™#F4 cÁ|(¡0b›=lö°oV›ûu}ë7F@ì±1ra•=vÖ£]ß{p<>ŒP¡Þ¬z€{>ãù0ß™ïŒ<yV4š‹Ã)‡h2aá1:6žÙf˜=`ž¬nF=P¦û€®Å¯® 㓼a“ô ÒëÒùii|Äi’‚éüÌü`yà_Éf=p“è² ']a-®R(h™tU‚F£-1réÕ3’òúÂÂ#§B%ñŒ/Â2!,üvÀt‡3*#¬~%*ËUÉøÇ|b>•dž±œ°œø.'¥ÞZÿñ|0í¸È e.<’ß~MAÌ[o"#ýœ|ö¤_)¦Â>‡Ã)KL_1ŸÂ”øOK%rNmÂÄõG¨>>§/Æ‹ùÀrÀrÀó "õ]Í& ˆh‚o±@>û؇ù%Éò'O¦bê”I°Z-Ò()=Y¸W£o̺ˆ,å2ø¡:Ý„*«¢P»ŠŸ¬-3ã"Lôíjtø½orÁüb~‰ ÆrÀrP9(õP÷îÏC«Ñ`õª•ÐiuøÏþˆøÄ‰¨ZEH «­ÕŠF–&x§kÃwI¯aöW$Ùz S½™DL.áÜÞxuK*²åÛP œú>ÚÕ r¿W=b1y¼Ó¿IÖ^¥Iæ~ò8³<ó|¯8=PjdµÛѵ[wôèù<¶oÛNKÆçmi|\.1¤%óJc|Doô-©ëÈøø!vÞF:ܰÂ.ÁšîÎolYYè?ëcyð!>2IÑë ]ŸÞøÌé%“oæóéJúêV—÷X5k¡¾Å6»‘Ýz _T7ò©*‘/td¥¥øðÖÞS»¾†¦ùó¸·ª¦ u§gÈêXeÏh}JhWü_ …n3µ!쩞ôîk¤ÑR7zœî»\ø2þÌ_æ/Ë‹oz÷fâ—®¬X ¡'ž| vºŽZ O()]O~_coôµ~pÙ,ȵ;%I«ÃÓ&Ol…Õ.J»`¡+µ@hi[È=NgVËËËÁµŸeB@b@„+ÍP|õhÕR¾z«§áCOÀup.v¥Òk&>_´”P­½ „g0À•µ _¤ˆSzf|ÿñ<(u:ânƒ Æž¨æËËσòÒeF@eõ …°—&x«×V»â#÷"6úEˆóy nÏ­Ä…6hÿLz K¬Âb6FìÂÎPœ6~“ÍðF—Ó™?B@XXX®ÝqBoù\dy„ññöžÓK7žÌ7æ[iôËÍ-*7€¤Õâæs€9À¸å8Páˆ=Ÿ[Ôó)%bfyayZšåà&‘F@·œÓÁf0˜×±'.‘=ʛģdyfy&Ü0ó™Ðuáp#˜ÌæÀ-ÇF@ì1J¡¿a<&//âËëM‚ØÝrNw˜9À`\`ĵDö(o’å™å™8pÃÌgF@×…#À`0˜·±Ç(…þ†ñ˜x¼x¼ˆ,¯7 bgtË9Üaæs€9p]p@'<‰ý×aãcq8åÐe oŒ1±ñ7|ÿnôñáö—ÿ¯Ìß óšåææ—› ¿H™e‰…ñIÞ°é2Ô¥óÓe¢[Övqù²+óùW½ÀòsýËO…ß䢟¬VäNT>s8tEƒ¶ÄÈå…ž‘BÖ| áát§Pìx¹–|uOë"!,ÂÂo/aþ›ßsa;_^¯.?,,/,/ÅÍMEzë?^ƒ÷§½'Ýïãiǯ¿¦ æ­7‘ž~NOúÕbŸ¬eNI9XrúæS˜ÿ r¨„÷v\ÄŽ[N9Dðžß3X>x~°z BOÁ‰Káöÿ° >š+‘Å©“'0uÊ$X­Èûä¨Âsáj±ÌTŠ+Ñ5ee"ÛDD ~¨®7@\›§æ·";#Y&k~»²þÀÂ"5ƒîý¾R¾öçJíʧïiÇ̯’ÍæóézÒºô\÷žò6ÔÕ«VB§Õá?û@PP âßFåЪpÉËÂKæ)¦–&ï‰Yñ]Ò`Ìþê‚$R¯rª7“ß-i»0tØ4d»+ }t8fö©ŒQ'É”÷ÿ c¢{È/Ï׿-##FˆRNŠ—;FFÌ—’é»›…OŠ€lv"»õ@žÏcû¶-Ðé´d|&JããÖSùã*ÈÁm|ŽŠó­©ëÉøø!vÞF|²a=„]‚5]%}ñÔi<8d:¥Šäåc‘µý#ü¦i‚ék'"zLX¾ /7 A±ù¬ê^€ T\½œÎ|a¹`„r+é E@‚ÑV»]»u'ä„V­Ûñ©"‘x'BI-½šÛ÷ÏâèŸÜõ54Íû IUŒ´ñÓªÓ3Àç¤M¼£}´üò L]„óÙTa t6ºVú»59ZÅæ£,ÅÕ'ZÍé·–çÇãÍãÍó¾‚÷€ÄO ¡ŽOv*òñxŒ‚Vi‚§|ÁXç—Í ‹] ÀæP'‹ø¾cLŒ_z M;wGŸþSŠYV«úmâ«úÍ{>õ}ÁúÔRœÎ|`ÈóâÖÒz N('(/{>žržXÐ*Mð”/7|è ¸&áÛT#\Ö‹ø|ÑR(¡j«çŽ8Ñ;5 ‡ÿŸ¿åUé²PPÒ³Œ”v…|DE„‚õñ3óƒåçÅ­¨nŠ_BWÚ <.1ù ƶÚ¹c£_Â\"Üàá‘{@ÁèÓ ƒâ^C·€>TOo«Éª• úèÕ"³†¾„c±‹ÑÙ[¾bê+Z??æó£àüdy¸‰äáÏŒ,—8òLÎäÑç1žôv8à“ jAˆ:f¼—~+tòÚ½†–á`€x#„Þ–KG¬é8¶™m }õ#ûã"ÞÙ-XèŹv8í6Ø¡ó’ïÆåó.'Üþ{žóøÝ|ãWá¿„p-<??t>ür‚0&Nú†²"Wª_<ýYåBù(QK/ô´…Ò‹ )aœJ”¯h9~¾2_™?Ìâ#¡"zéFœ7bÏèæóŒnD$ÎrÈrÈrëÛ F¡Y#¶ÿ8f>°ð<`=Àz <õ€Fœ<ËL3Xx°`=Pžz€#@FÀ¼À+ ¬*D0bȘWx„õ@…èF@ìùTˆçÃ{ ¼×Pž{ ,o×§¼1bϧB<ÞkཆòÜk`y»>å# F@¼Ä{@¬*D0bĈ÷€xˆõ@…èF@ìùTˆçÃkò×çš< KyîÍ1bϧB<^“¿>×äy\x\ÊsoŽ# F@¼Ä{@¬*FðoÁþí¢uk×`ýº5¤’¼qƒk×Èî4iù·¯@2X¼üª<ÏžWÖ:uÍ—`§ôníxÝÇ«ñÍ7;‘4ï#Ô¬Q“<ÇÅšp~œ~.£GGKë$ óÍÍ–UN˜Ì¡/XJ$¼äÞZ·–ŒÏ×;‘ø6*‡TÑœ sŽEÆ]:?g;w‚ˆ·nÝŠÄ ñí×_!ùã5W==d1žÃÙ´t‹Ùk2g¥#íô9ÐeªW¥sŵyS&Ñ9 3ÝQtY>‹‘êOCºÑrÙ^ͤÖ/./Z.3ý4Îfd^–.òe½´ôK—Ñ»âÚ1µã¼—v(t—l:µÿ|–©Øú.§ëkþËûW´¿%{ÎDÊ¡S¾õ»˜q¿¼?ÞÚw…úŒÇ±cÛÅÊUÉé{«·dégS!³Lý+Y=åÕ®§üǃy*ä#ŒOHhU8Ť*àÁ\Œy¡¸@Fhìèz¥ K·ç å—ˆI±aßüxÛ)wq=z'|ˆŽ¯$=ƒ”U#ñö†cîwM7,î pÕŒ}‹ãðþæc軽õX1ð%|–å,Ø Tyrf¼ØÙ‡WâÕøuyïz',Æ’í1ù ¯] œÈPûɼóbKªß‚ý ¢1}ÛIw9ѶÔr棘:(?ZÅYÂ1f~‚4Tî"6Æ ÄšT‡|£4í¹1O"Þ˜®Â+c?–éžµý•‘™²¯Å¯÷$£ö£C0¡{èE¹ÛñjtR^•ðç1#6Uÿ­iXû¶¤Ö 6Ì@#âÏó½‚㦎Ã5DhæS˜’ð f¯z ä£p=Yؾb/šþ«#n+k{®PŸíÜ^,œÿîjßA!7×°Ÿ%jw6%L@›ù+Xõ—w¹¾Âr~møÁˆŒ–®1ÞÅj“Õõ„ò4§û‹FÑ —nA¥9»ìetæni|†ÍÛˆO6lB|dm,KÚ ‹0niÛ¥ñy}ÖÇHÞ°ZÂøöP¬ø|X_¼¿ÛõéI§%¥ï F¿yk±lí:¬Y» ÉË'¢µ£Ý?î¦g03a=Ú›Et6á½A­°tìLœ§÷.W¦“k2hš¬?yÖPœÙ<_ž¦Kô²öHã3œÚ&Ê%ȶ})Àñ/çà§j½°ŒÒ?¡¶õ½ów,Úþ›D&g¶M!ãsfÑ»äå éçcö®³’w§UÚÄHz¢¿«©½/7 ¡rF$'|‚;ú©íX-Ú±ýÌ—ùY±cò‡¨Ñ5Q-·ü]ÔKY…íÇTî8Þê3ûª4#C¥Ðí´Ä·+æï/÷à,bœ(Ýh¼ˆlÓåËBéç éÉqñ”·ÒM¸={z–ã«ø¡º>ZO>jOfú9¢ë~/Ò3báÆq<#·P{Œ™”¯„Ïœ•‰L#ñÇ ê3äÕ'‘2!IAǯQo’‡¹hèØ<íó´Ëj*Ð~ ²3ÒUzž÷ËþQ¾ÌLc^;ÕzóŸ óÓBõþªõ[È?©ZÁt]°B¼ ÂåŠ—ËæKA~h'绹ùLj¬H·î=Éh7£cãP­zõB%͇¼Ðóù¡Ýí7n,~äDvë«Ý^(¿ôaì¸ÙhVÕcŽu›EÀµî,œTß‘]ë i>ÔPh™Ïí{wÅÜ¡Ûɜ܋‘±Xýp6öèŽórƒ_à@G׫ÚIáøj!΄vÁÄð`oÄaWk$µ«Ct¬¸ýñ3‡Ò,ø‡ù[z÷æ>T?ïÞÇßîÆj2 :› 9Ɔˆ¥¶Ý[ÕFå€;Üm³S=zq§¸!z¢g„!Bÿ#eHÈl×úc¸oXªåZ`Ö×E¯®·á­¿ÂÕ®&2Ž‚9´ þ<ùÒ2l¨sOC„èD¹Êxiî<(Õn#>X  &#ú£”"•k•j•eû]úPu°»4PrtˆŒ™GÚ˜Ñ÷ÙD*q•üÅ!ë!Œy1®Úœ=CÚ“Bè£Ã1³[j— ?-w?÷ ÔÚ:õ´¬ãÀ’_Bî˜x¹q0[ ^ê“€˜ù+qF’ v“Ò?ó=Þö²Õ$•nŸÊ=h²Lyð n{ ‹‡½­i*‚5jœÓÁŵ—Fz÷Ü!˜õ•p!€z S½™¬ïĦhŒ^qT¦CÿOÌKºoEíÆ;«ú`µ×84 CZW¥~Ǥ¡Æ˜…x©úa¼5x2Ϊ¥Ð4*# Ãñ#1¦­¹ó;bõ7ð¥e+á}07¶³D¶de>¼¯ÄopS E,!R} G¿‚Ô™2ÝÐræT"[O9ޝ R¸Yù¨S=;·çH“ÿV|¶Û D˜ï6BÕ„Rù!_ÐG¾ñCÆçQ2>Ýaµ ãs9ß”ÀÚhÚ” {ƒÙ0Gethå‚);E°‚0WWÈoÆ®È8Ð5í‚ šû¢‘<§l§÷â•WñÙ£Ëñ˜ë 2>$.Úˆˆ* þ»ä5ŒKøóçüz*DR”u)ZÑZ‰À(‚H¿pê$2ý¹ Šqž}a~i!¦­ˆ~ÏÅðåëæoÇáuã± /¢ì‚`¤a³ƒ°òçVèßôo—µ×vb=Æ2mVÆ=s`òµ>§EàŸæ˜¼<Ää\<€`½´„;GÝ7Öÿ¥õpÞ®fx¹™?6õý&ã“Îápû]^‰Ã/ÿ·•Ý´¦­¿­ ?&‘ñi…ÞD(µqéûŸá¢Å‰ ’!>ÐÔ¢5e1žnX6KþkÊÂÙæC±æƒ‡ Ï<ˆ×^ŠÁšßZ£WCZf–òZd<ŠŽ?ßò|â¿"­%*P-[ˆtáyˆo|òÆG ‡|ïÉW0v9ȵ’‚WÎ`΋£pŠ”îhB*7=²8yôQ’‚â„•ÊUõY$¹Û'âã›gâ$¡ŸÎwH$å.%쓚Ïå„Kè)*ïvÖÑÚ:,˜•„O–O¥å­%ØpØ«ÍAõœAÒK¢mobL»ÛåÜ¡ä9øÙUQ1oaبWÑXIÁôE?åñA4ÚÓOb™¬K´¶jø³ˆ2Ÿ|¼ 6¬Ãà&iä”ålÄ'Cõpt{±/>Ýν`Ë)Z¶"Œ•œ”¥Áˆyk0¢ýºãIXùK6±Â2²¤ÿÝAŒÓòà“§}"&›‰^]Z —½FS‹ÈßÃØón„Rûd\„ÒˆŽP}gÙ%¢'yödÀokŒ'#êÓ©A±7¦àŽ{ªÀúÛÊgÄÑÃé¨ñw4mUhÝ©]1ë‹ßС§«*H\óoÜ£¥~7¦úÒ¾ÆèoN {X„äUãôÞt²ùUþI¾¹KZ¨oŽB4´T*É‘gïDÆú¡P9Y¨ÝÄQø Þ“¬_]z‡D ºêoÝy¯éŽÛ G=&YÍU°½äˆ2UZ<€ÈÇë‚ìôÝÿE{'µ%ÿÄ8ç/uTçG|µYȱÈØ»Û1΄ô+ÈyÚë©Fä³ù…¡×í—°ó‡Cpn5£ßŒÆ°*G¨Œ Íé†Çª+„Zõxî9BjVÂÙESÉÙ!ú’¿ÕZ“Ó°G~Ü‹—àõ’0tÎ ´$d˜×>Q„èYÉ) VÊt‘b4‹Z pÚm°˜\ªePùF„‹öŸ 7óƒV'ò<â†ê[ý¹8$tÿb|¬Çø\ùäóÏŒm“¢ðYZSLŒ}–‹¸@›àâ}ÇzÀuh&v¤Ò†¯’…K’iYª#jÑxÊ‹É-‚çùÈ–$œ íMè'ö‚ÔtÝ=ÐLù ¶“ùR6¯¤¥¯¸·|Ïôî,Öï~1ýÛ&,'ï¼~?z6aûä(|šv/&QÛr.Ð&µÙLéT¡M˜;~ÀBHkvíøJh :õŒÖ½›àÀ¬E8JŠF1§`Eòy´íAH ø}Ñ[xyèjd 92¥aë—p{³P4—°(~¦íü!)©‹?ዃv4nDgÄüh“Ôäöï~“ˆ,÷b*v|‘ƒPj£§ßb’ªè–(a9;N‹} 3Ê<òVd:^=w¶~®ƒs±ëL ~ì^GûTuÆßý‚qOs-·?(÷ÞÎþ°“x*‚JW˜:»‡Œ£NDôŽD‡fá÷ço2‡ÌEƒc¢éY‚ŸAhÝñ.d~{ õÂvWö,žƒýç‹GÎ ¢%4jÓ7B.hó³EK‰÷¤Üó¨ç˃Êá4P»<ÔïIìœ:ß„<…¶5ɨú5ÀS·ëðÝoÿCݰpÜu› +ß_Ž b°ÜÁ3ßÿÜ=¯ÅnA¦00fr±áÈjV ´ûé”_Qaaq¡Qbnòâ-H§“æ3ßcö;žzà.êwvŠöñs!¾1?òåƒP1žZQ$$æ«Xš»ÒžOž§è¦gù}#ý,LÅOˆîû‚ A¡6Æ.~áµÃĨ½ˆ‰~ bñú˜±¬ ¡±—£zŽä€ÓrÇcJÅâ'Ñ{ZG¹÷C«ªj«‰a“_F¿#ñÂ|A(CfÀßȃ¶¡bf¾^¯Ž@¯9âÐ6j2žª£Çÿœ×¶‘Ú»d"'Çâ—ãñÊWÓÕB¨Q3;Ó^ƒµ;ŒÄ+»úa\Ôsò]•6ƒ1®Uu8ÈXµ¦FáÕž+å;¥A'|ðôÔNÃÆuÛqùí¨Òf >èp;!…j<‡Áônçûju²\ÏF´Ç$”´è§H§s¾¤ÆÃ=%È_Àó–$õ¨mß„WzªLhAñOÕ1Ð)°N˜uÑÑ´/&òé[c¢δ¯fG‹®}á3ý¾¢‰R§9è«P¢+ò‰3yÁx"&ƒâ^Cä **PR5ñ® úèÝ"³†¾ˆ?b¢ïScÐïÇxãùmò½!¢7Þ§MW1{VöÚè´ä^Ä’\Ì¥Ü nÒþ¢^= «`ùl¡¶xžýš>FË¥ŸÁ¯wGø‹ñ§ƒOLNÀ¾¾£ð™› fî rá77-AW”¯ÝìQܾ`^꺈R€ÛF´r=€Û·¡Ý¿žBUqÖ[/|>T­jüˆ¡}ËrwÒiÆgëj‘ëFpêøåçççãW€·2_”s糉br O…ã‚|Ðé4XOl*BÞi·òIKëlþ:A­0_í:âëtÁ¼yÙH¾´‚À`B5´<'ÒÕü hÂÛ­È¥¹e÷sqÒQ=~:ÌFª‡N˜Ð2SY³N‡@W.Ò1a¿ ªЩ{ -+Rß 7ùötdÙ®ÑÃO¯¡“r¸L‡¬´,D“‡¨øc„J߃`É{Vt”]Kåè„ZN`RŽVÚ±‘µÔè–ö.Ä‘_}0•ÓÂFå–ÐÓZ—AK{FùT¨œlµ'€–n¾yËdËTLÙ|–NÈá¢Îd¡úãÿÂÑE+0ì“%h¢Ø Ñéa “y&âµh¿ÁOO§þD:µÃf¥å/²úZâ?ñÊh¤þ“â_ÖOgö‚¨–\+CÑnZÚ²Òq=q†òþYZøÓXi)Á!Ç‹–ûijŽF"ò£¼–K¿cÊÈiÈÈ[Þ£²¶z¼GÂ{$¼Grõ=’›}ž0bϧB<Þ#á=Þ#¹úÉÍ>OA@âׯ=¿€M¯Š âúnq5{pìÁÝì*÷‘ê_©çÄ/¾ó¯×Ò,|X¿n ¾ùz'æÎûÕkÔ(vcöÏsç0ftŒ”ÊÈçÈ1ÿX~ÜòS¿¥ÅòÇúëF–;ú-8·…¿ÅcñIH|Á!U`¦{îôOÄÏv~]蟈·mÛ†Ä ñí7_a¡¥«ñÏfNGZZ:ÝÈy9ŸYé8{&]Þ¼y5:W|oÊ$:i0Ò…eWÌGx.ÿ½ éii8ŸM×uJ¿¼ò=Ýg“Fù³Íâ·Ã Ò¡k™¯Ðóù4¤ed¨×Cßê®_Ü;T˜ž…®V|1I/š¯<žÏ>„Ìë ùãæá_ùÆæ3)HÍ¢Ÿ§)FŽËc¸Þòïòâ7# š=Oåª4Ò…ï5!ý“Ö­]Mß]H˜ð6ÆŽ%ÓårÑ‘ƒ–[±ÁHLßvÊ]VÞ ≻ƒe¾C+Gâí ÇÜïš"î£1¸;˜~ÑeÂþ%ñ˜¾ùžŒ]ˆÂ4X5¨/>Í*x13PåÉq˜ùbdZ×Ö»é½ã¡ã=Á8´d Þþü|^ºø¢„<ƒ¤¤>¦ë´c¢Æá¤Tý@Ó>‰xëÉF²]ÆÓßaÂðé8ò4æÎíCÚg÷ˆ·¦nÉ£•Ÿ?¢bM*]b#C-¼öî»ø¿ÛéÚo×,þ¶¤¹Û­oŠ1IcÐ(.8;óF ›Œ³îR†¦}0#úÓ½Ïû>‰÷·àYü‡²?ÂÓ?»1µc“ìûŒ>MŠð»(ÿ¯Å³›& Íüå  æÊ7o|óégÞ†=®{ѱqbß{+w¥ôÌCÛ°W¹O„÷Roy¦Ÿút¶¶Á!ªüLJòlן/72ßÑä—¯Q„\«>/÷ðÝz2/ÒÐåbºíS ¼–.`#¤*‰‚qöi|†ÏÛˆä ›YË’vÂ&”IÚvi|†ÌúŸlX‰WZÂø{‰Ž›‡÷ÃôÝþh@-’Ö)•ÐwÞZ,_»«×nÂ'Ë'¢ÕÓîwSþ3˜•˜Œ6ÃfM˜:¨–ÆÎÝâŒæ/ÏŲåë°†þ%S½ªhQåÁV¥ú·%Æát›7±šÊ,Ÿö~Y2{³éæÍÔ 0|*5§-èzêW–¾·ÿ=KöcabÊÿNÑM­Æ”õd|îÀ´µ©þux­If¯þIò#c÷|2>-1êHÞ°]‚`Ñö?$Ÿö/œŠsmb$½OMDµ_–àë„IJöHã3Œx&ú/x6w§Dˆ'6 #ãó-š7 ‹Ýè¦O9é ò[ðU>Ó³26!3+³äiEfF:!9«;¿ZÎFCŸ?Žt#©|¦ûAC«ƒ.Í«ÏfÎD:!:9ŽîúO~»Ë·œ(Œd 1Š|Å!ßüzÜt­&dŸÏ,„øNï"š_œ(T…îyʧö¯@{Ez¶IÒíµé{Ú[”_2=˔ׯË޻阳Õviüª!Ô_aµ_Eóósñ(V.ù}A¹ôÈ # âJ÷î=¥!™7£ÇÆÑþOõBž,ÉS^èùü¿ðÀí1~Ü<üÈ£ìvº„˜èH¦zb¿†ˆ7÷V¡ë¦s\¸£Y\ë΂. ÅÉ]ë i> ík(0šýСwW̺Á½h9kn‚ =ºã<åuÑ •dê ! á$„pdçBœ 킉áA0ވîVHjW‡èXP÷ñ3)iT«ãOW+ÓžÔrDZíX–Y‰½"è çøä¸¯E?'-/jîh.†Ùعÿ4š6ª‹˜YÑʺ ]‡’r¢².W%<=õ[Ö–Ë‘u[AOA·>#$¬7æ.2 ªËJõ+ ¢ÎY.äîžHœV!b“®¢nÚ&.‘¡>ÝÓs Þ­ß ¹ôÎXÉ _×Cû7ÄXâY³ª6¢çB]7φ ¬Û³6<ˆœ%ý0šÖæ.㷛￯ŒAÜçV„ØÎ"[ŽZ-¼9õ]´¨íåü>8yÕ~t8£ÚÂ~x>^‰·aÞªñYú2Lñt0ò4®’Vìž;³¾ò J¢;ãTÚÉ;iíS5@E˜9„ÔÞ ¤¦B¾“ùÖ+9¤}÷!FÌÈG—= Á6üO&¹iö#šsçöÄÏscö×Ü4kaè euißÍ¡ò;Ôô«Œ²@IDATZÐg+ˆ™ÿ>î ´’&¾Ùƒ&["nþHºÖFýIýð¤‡cÌü8‰Lóä×–‚Q}bóê? ‰·¢ë¹÷.‡-©©în=ˆwæF-½wd˜Gèm¼8½ˆÞpËñ­ÂF@49¬d@Ä2Ú#>Š ãžž^È3tÏzx5>”ÁcÑ=±6¨š4­-¯˜vY`ÜØÏqß°gPYèhRÞúê!È‘×VÓÈkFJýLæ 4ëÐö\òÞ…ºéŠ+¬ôìpÇ¬Ž kL7lNhä¦ë«Á@Ö@Ôk±ÖÂý N\"eNªÓ.®¾vZðÙ{‹PµËëh¨Ðµâ„€PÜæoƒX8sÚ5húpŽœº„ ­q_M.üO(TRôOQѨU8ÕGF&lŠ Gó!hLË…v­?BƒlØ:+o ë† {lx³ÿ}²\P04¬  %5ã¿0áµnbÙLA­°æ¸ÃÏ!ë?ñé8luµÄSM*C ¬ƒ¦Ä3‹à‹-ŸgÁÔŽÛ[·Cµ\ .©M»Œß¾ëu”™àÉëy®CtG¦MÝJIVlŒ}–§ó—²ý]lHÍ…&À ùApFÐ6z­ì=©|°Gšæ˜²\E´cÛ^À¬ÍñÒtÄ?S ~O$býâþQŽ` Ÿ>ÓVÈzf oŽ¥ÑË@vA¥S(6aëÌmh½H"¾1ÏÁ˜ž-i&¸i&ÍJÖó8«½“©n Ç´=Ù_$z©˜NÆçéÄÅjù·¹;òfXÛ‰Íd|´˜BèT ð7[ü‚¸%?B1Å<2>£ÜýˆîZ™ÿ+‚„4*úˆš,éF5 Þ`CVÚ=˜)颭ñÞ^÷›×qðŒÇÅûåúâVå# ·Ça%+Œâ #!‘æÕøxñX;œäº‹½¤Gá-yM'¤bÊuï—¢ºNÚ‡ÇØ8‘+€Fwï¥pR|â‹™8ú,âï&ÅnuÊò"›(-óŒÙ$± è)g9¾ËN×@ÂÔ{`uªHM–!m«Ö¯.9Š4'­;å1A€­óçàƒÙ[17úaaÕÕv§ÑKå2aûªNäØ\´¼e“ôÎÑKŽâõYPƒÒˆc CåÎ`Ž›gÓ«ƒ®Õõ©íŸžþa5CÓ¦?šHä Ü÷øÃpÑRVŽå(¶d:Õ¥±D^®À0to¡Çâ½'ðL›Ât=Ožê$Ÿüê¢k×¶øbÑ4,#åü/VuÒÂB V‡Ï s.!Á#?ÒÂ(ðŸÅÓñ35N±¥'#N Tîñ‰þyÚ_ -®Š)“^ { ‰’üõmU‡x`‘H™F„Âþõ¯‹.]Ôº—Ÿ£ºÿk“uÛN ºZ#>¬’DÀ5Z=ŽúøN6ÿÄÞÝgaÅ䢢Àñq7k?Œ¯>‡䤼ýB$îmÿ!ø^hU;€xL¹<í¢’&rRu¸fBÖt³9lÄ×;zvDu§•pþÙ»-’ß%z=ÑÞ§?•G~.(ï—ˇN  ÇùHHÌ`„FÇÒr\õêÞÏUùfªáCñ]ý¾XCËGÒ¸h!oý5 G¸ä»%ãgò\#äétiPDDÈB³§¢ëä ÐúqÒ¤×ˆí© ¦PHÃ÷´d׳ŠXî†ÅŠ-³VúyýXȈ(ŠžIJN…îºM<[ñó—9h8 TÖë©OÔ- ›ç9eÕ0LÿªyÔ£q;ÕgLû õq7!š:´aÔ&Â]û.ĦhDËq.S F y £¦ad;Z³“í-®¤eªèOHáv¹¤§ÖgŠaoHž-%žYˆgÛ!Ú%‚‡NÑX¾´Z`%« ÊÙÅú-¡µ Š­9vZö#M0ÔBÆ0(ˆöðÄÞ ŠO‚^ ±•X'ËÓ7µþÓ_ Ï°$´é=ݽÇ1XAˆüž ê µ„ânÝ@ø Ñ}ÞŒZ}ihŠ´Û…ÖƒbY×ß°wÏÚ¿{óÛÇ`þ û<$Õü§òë~Žê>æŠÁJªÌ¯2˜¡¿l’:#AŽ‚…jTC@09¡-ùÜ“ä”XaÐ?Cp(B·aàÚ xæÀ°çÛM?x9šÿí2>;lv¸tK«Ád$ç#G lcz]¨\ÑñàgæorÀ¿„@Ü:Dz*{\Ž#$”Ng»ĸØbö| ”ó”ÏMØ>) Ÿ¦Ý‹I± ç|:2M´QLõ5|¨\‡fâËT#=gâË%ÉäµwDm1¹Ýí!Ò2xžßœ„³¡½Ñ™ÐЩ"ÝpO4S~Æ‚mÇäóáÍ+É´t@Ó:ê{Û‘õXMègdÏ{@º_í§þti¢ÃìÙ[h±†ŒÈï›±¼ß6»½D徜Ø6‘MœÀ )#PýRÎgš¤1»tpF ž†£YV‰Ü~ùúK*Õ«DJËx“ûÃÙæñæãÕ‘ñçy˜M´ANí6§n§=—uh1h2¬áÀ¹óaȇüîm£ðYZSL$žY.¤ã‚ѳ¥¶G´K_ŠÆ0ÂùÓ|sÜHô2±uùz( "„úýÔ:ÌZ´Sí÷ñ¯ðá;k{'‚«ßC†|'þK{g.ãlûò‚hÿLô_›-ÒgsôêúÂjéñÇoNˆ-/Q?lfØÒé`}ÕÕoNÈÆÿ @XXcT½´3X•Náö¦cÞ«±=»&Úw„‘}êÃòýaÙ>Má4䨻Õ}Ü]·R-­”xoñ7HKOÃö9“É ¤VÐ(´~œuüˆKþµ¨GöžUXº?Žô=x£O<2ê6C—AÃñÂíZì=x–ú‘/>š‹”tBm’‚úái¯žøz1y)gZ ˜Rññ¼ý¸½ÓrÏLä÷ä㸸qfþx“ F@B‰Ð*h¡={Bb $$‚0Hyè¹`þ¢åųíè&,úYø #û¾@±µ»ä}4ªõ&EíEtôKX ’õ0sy[Z"S—™Dyá„;¨mj=©X¼ò$zMë(÷~U‘î°ÕÄðÉ/£ïˆ‘è5ŸÉó~}ÖT¥=;!›s“qGï·q'¡«§Ÿ.Šž„]½ÞÂ+=?…ððYhY™–¿„’tçˆ@ö‹×¼ù?Ê|sF¼ˆ9òÐ9a^x&]~x£¢žs§†¢ÿ”Ù¨E2åÓù8 ¤î§$ôë‘$ß+!ÏÓ†zGì˜ù¡|þqÎHôq¬ÓyZþ„E¿¨<‹.À³±‚gtM´‡ì‚ˆ9žva¥N|óæŠ}s$."@ý~ë9T¢ošQáÃôwhw¼¬n‡ý6Ä,|“†ŽÆˆÍN´î…6U„@²Q»â£~Bìë½ä³àCÂÜ0(•Íèöâú<+Ó•:Oàƒ>M¡XáÓíÛpÿ/ ¬ª@Éjðð×fÕ£~?Ìô99Ôþ]0'òN8iÿ1¿?ž~qìáÇ—ë×BòògF–K2‰R9&fàƒA¯•l*¦bžñ)ðþJüK/îå Òéê$¥ØF{V¢aðóƒÞl”Goƒƒƒ`¥ý¹¼!ékà@ªÖnS׿5:ˆMqϳ0îvh¨ž F:¦ÃöR,´î䢅û€R¼9BIl·–èÑ!€úËU‚è ý ÷ž åK{~´e¡ý»¢CÕ-BÁ~8¨-¹´çO&ê‡ ¦¥?­H§%!Ù?Z–#rùå¨9´f ¾ÑÉ÷ütÊã¤ý)‹“úM<+X(o§=‰àè… K5"ó×/0zÒFèC…©öJªuFÏZK0Ý‹•/‡ÃNýÓ“•°ÓÒ”8µ§è¨ß-=S¿©ªÓ$ûMõúëÉ "¥å2U* VŽÅ)Ó]T>ס%~ê ¡å,…Ê’¥e<µ¼Ê/j·ìŸ•öЈ6õÃLõRýN»‡?ŸŒw6ÿIHL6–! ÕŸŒÁȧ ×Ò©?¿`ÑV÷ø©ãJF—hZÈ3Ðûë ¥ºE»EpQÝ9– lžó1<÷2ZÜA{uá[tí7 q Wà.:2íçO¼ÒО -'R9Ño Y€à?Ñ2“›¨'gE¤»ÔqvŠñshH>´på»XÖ%¡·==•³ZHf‰Wš…äŽÆŸ™_å… NLfúWTéyž :õ4”ôÓÑšµžñ#uJ§ ô´èåA6×ϸ*t´YO¿`{б‘µó´¿4±I_$ä䧯‚€@',tHDQ–ëBü.  Ž—¿öó‰y·¤çÁý¾iÆÝƒ@Årø›1–gF7 "åSpƒʘcæÃ *.ZzµÑ¾Ÿ4>,Ç,Ç73b$À3ÉÀâ1r;yÅâfB¸Œ€Øcdñò©óŠÅÍ´bÈ# F@ŒYTˆ`Ĉ# Þe=P!z€{>âùð^ïeÜL{,Ï¥“gF@ìùTˆçÃ{¼—q3íe°<—Nž1bD2ÀlédkSÖ}¼ß|³Ió>BÍ5ŠõÒÏÃèÑ1Ò: C$™é.ï¡Ã±{’1_X> Ì/žÔþUR‘&ñÙ’ö9ú [Ñ·™Ô7Qƒ¢dbCtašID3˜ò«ï+&>ýÙ$lm;CšªãYÔ³“ÏY)Øô½Oÿ3âºEB‡·4ëŒðjb¼¿½óóÔ§jŸÓŃWÊw5:üþÊ|¾•øÃˆF[\Î%ìŒÅj“t‰ï=z,4tM.Ý)&¡F\bFo æ—Ï™{¤ñ6o#>Ù° ñ‘µ±,i'Ýmr—!Ä=‰wý`ß1+NP:Ý›.ƒ@ÜoâPì«øZtâ7pŠPçB*߉ÿ¶Œê›×ÖneÐcï¢q„œSÿŸ½ë€ªØÞß&ÙM')Š $””¢HW@A4é)O@‘Z€„j €Ò«""à,¨ *üx (- I d7ÙÝìîÿ›¹»É&„úòœËܽSΜsfî9ç›™ÝÑÈèãECPÚ@§$ûËÕoê®ô!Þßë…€„a§ Z<õ¦N‰A £N÷HT{Û€B•·ÜË‹g_„‡‡Àle¤k9Š˜ ŸÑrªŠí9r}É ˜sDK#ü€@#4Ð&„7€=›‘¯ è¤+Ž?Îá—ûŸ˜ûÞQ´î=Ï\ö²Ñ èKÀ`Αí[-¥ÑÐàS—­òÈdqܲÍfÆgo/E±ÈWQMÇãŸ* YÝÒ0f˜%}ñGÇ´&•FÖ¥¼4‡#µ›E '+ûëðÍ΃ 뜉¼„½_Ÿ†.ØO‡rŒ@ñ"¾ ÈpØrP¼v=8ýŽl¶‘“•ó¶X z}*æN‰Æ î¯ã°3‚×¥|‰ÉS=«5‚(»Q3A°’f‰ _˜éIuö«]WÒ4É|W9ç§Ž Ý{>:F"­Æ;ÞÄ¿O˜aþs qK]ˆÔ€Ù±Ÿ y çórtu°P 7¢º§=va6Q8ÅÔŸÿxðªäÇK|€7û*ïr˜SqΣ!f®ÒèŽoŠy[~E`X/¬Œ‹dÁ–Xµq!­Ø<ñMøö)ñ†…ÃppÑd, Yð×¼¿Zʱ8¦!v¼»&R;³émìñ{‹"$¯5²r óˆñ(Þ óéÁ:a¾;"+Ƭä8b%ž ÔÀìu›¤|õtÿ‡Ý'Î +цã¥uì*Ìí‘ë<©}ÖŠÒø"ZÕë­HOª‚¹’Î D–ÚƒiëË_¯°~!‚ï‡zã[éE! aì8\Nˆ¥rGB"­PçSòqÒe6ƒp±ö‹Óõ_G<‘J–Y[VL´+"%‹¼ˆv,œÔ&gœINz¢ÜŸ[æâTð ˆ­ì »Õ.ë‹RåȈ‹ÎÌ!ƒÜ¼ˆÔzr Vž)…¸·ªÐˆ g™ƒ,Á—§F_üuWaài‹s/¼²™¨„—ƒÆ¸Cì¬< ÝÿíJ%­`~v£eqÉÅ3Òd†È*ö:”y=Z„’_ vïŒ7ÖþŠÅQcãÄy¨Ô7†˜eë¨)¨ÎK8`×3Ï^ãÅ)Auµã~§¿ÂÀÈ:È&Bs” GN=¿`Äéã[‰:û¡ª¿@¤@h›.D¤+pÂú,LÉ4x½‘J¡%QÝŽÿ\$ý"¢1y¹Ú’-™H·<"Û7ÀÖ¥ñXÅÀ%õ þm=Ù?d{ Îô’XŽaoš{×"öGÊCY"ùát&j…rm-Ÿ<Þx¼K;ìZ¿ 1KÏ##õ tþ/Ð!Xp`ËiÔ'mÛ¡2¾"¨3ÿ‰øiÙldèrŽñ)gM¸æg‚w«—Âé?SV)´iþ¾d_ge3pa),d‰ †ŸÝù0"ƒšU‘èZäYI§\—Ö()éøãÙ °áÍýÈìZí®¯É¹ó]Põœ¿_ï%}¨ï±7äàå½P'T¢dáÎG a4Üê_ùlÄêá¯aO…>ŒFÃÌHT”×Ë’iFÃä³5åMQ ‰…ú'AÇuåÒ7że'Ð~æTè|1HÈz,ì#ùH·tB]¸H,ŽgÓN[ç¬&ú™†ª°Ñ|åç×Õ†hN¶ãJp=;ås%ëhz/ÖÂÒUËq1éÍNK“|xûøóŒF;r-‘×— (¨ÃŸ²OMAå³ëEYŽ3ùWû#ùe‚û]äØ¬Ô¡—¶Ö%žDl;²ˆ´uìs/¦»×Ë“C¥+½äÙ×ÂÆ…‡LäRw¾,ÔƒË åNÇqÁ¸a£Æ˜4ñ:k>âåͧG#¶ÏˆÂ¿“Â1}b ˜/&ã ›ÐsÕ¦94;Op«2wyí\¾õ[£4 ¸ú¤äåz>º-ç‚{ày¢;‹t¯*ÍP[w‹·sºˆÏ‡·¬A:š¡V-ß|ôc|T £ºTX2ÈÏŸ³ÞÜÓ]©®vÅÝu‰rûgõB÷±;Xé”°À›S¶£FŒ€ýQ¯GMü’°Ç8µ¦3Æê ©hж:Í$ðûÒ‘xyØÈ =‡1 ŸqekWDÐ#­äÖ÷¸ƒÑï_ƒÑë¹Pj<‡A}ê"ˆËEó8çÜÆÃXCšõIÓ!è®;ŸâY˜Ö¥K¿€‰ùiGwc5[XÉ<\¯%¿.À7'¹•›nå»õ é@›ãoÀá§„È·Vîò[õq**F”„Ã?ÕuØ} ‰íñóö/dô Úq]Ù–¿ø1Ý;´@µÒzüù›´É_D³°¦ Ê×yW@›²^ØóÛ%” C¥â™XóÎ*\S«¤OË%œ¦åêÜ¡ÂCCúëï4ô‰¼ª´¬‡?–ÎÆ÷ÇÎ éðvL_ŸfÁP1ŠüyÉÕB«ãË{ñî²}<¦ÛÅiž¾\)R²˜ÂMZû)غh§ˆàÅU*¯žÞà‡‹VàH:W%M'ðÑÂý(Û¶‘Üå˜ÿ‚ò¨çüý«ô‘«…€øÊ—ÇíîrBâkBâIn8¸!äÃû÷ÍXz@¸ŠŸ1¦O7IÁ„eï ¬ôÓ˜µÑczc±ÈÑ7Ü•õ3Útò!bí¼ÿ–­>ÅùýÖríGldå`}Ãg¾Œ¾£F£Û"A(CFáÂ#+§I¾X°åzLC%®ýˆ)¶‚rÒôŠJ2–íÊ'-ÍҸ»xk@MÇÆâ›>cÑ»Ã2Yºhóá˜Ó¼ r²mi6ý¿é‹˜¨NZ^ý!ˆ©[ŠQ´ †NAø€±x¥Ë™§«Øï>÷°t¦ÕBKÈ4ñÇâõ0J&G Z¹’2(Ýt4¤Y¯r8•YPÍfê’³ý»Ì—4ëp¡¿M×zÚbFÔ¯C'Š}=L]ú<¬¶K0Ña–©qqQ݈(oD_,jUŽkwDö} 1o¾†]L‰¨à’š¾$ þñ¯Ú‘ÀÈçù¤gØ@… ½Td€`)wŽZÍŒÅ>ê®Û6Ñy\_é‹*ô˜\ÎËíw©wŸÜ®2âw’¼Šu7q ¤ô@ÃшMŸ ^£­‡Þ=Ãöó©<„¸¸—Ñgül“¥¹ !.) Ç!௠"è‹K3Ýõ“}ÅØÙ¯a°Øúþ’ŸîØÎ€«BƒõÒ™‹²®zV‹ê{cîÀn t˜^1ó;< »s òŠþ`E)º+=pÜ6>ÔoÁñEï¤|É Ü½¼<ñ1¿l*®ÜÝnü|µòîéžœñáÔEAº9f.ˆÓàx{{ÃË”)–ýüiP-š“ô=àëK‘cE6ç;:/øs¡Ùîzf]/¶ãíeIqƒA|Y&‹uDË>>xpÊÅHá*Ÿ{÷ð‚¯·'lN~d:Óü™&x”FQ¶ã'/’®'ë<‘•)„þþ^ÈÎæöuß>zdµ/¡úúûÁÌ<€.D(¾?&ü‰pyõ\õoϲJÚW×Çíiçïâ_ѽWúG! ñró?ß'u¿ô ÷ò’F0¯ßhü97)PV~w½~%Â#Ââ–9^¯ümΧCÖË/ùæÑµ‹@‚޶ÐqéB Ü|`&þ+Æ '’£3¸ú/¯ßòäVüÿíB@"d¾Zt§îJ4Ž÷äx(NC!6á¨$QãU×{u¼^/…€òQ‘³Bj@Ù»bÔ/!ˆW >uWzPã@½ÊÜQ; Š|îJä£Öôpì)=(=ü“ÇB@*â¹£Bš q«5ãâ² )¤"q…DSvà®Ø…€RH5Ô8PãàŽ¿ ©Èç®D>jíC­}ü“×>Ôø×Æ¿B@*òU‘¯Š|ïxä«§Z NX! …€/‚j¨qpÇß…€RH85Ô8PãàŽ¿ÿ(ŽG&9|ˆÃ'ÿVã'NV‘Õ¢"|µæ¢Ö\Ô{p¯Ùÿ‰ó€„óÙ°qóF6òùç4N­ËWÝ•ÒQã@5î‰÷à®# ñ³ô:þ"°»g?ãïÉ´غué˜úÜÀSÑÑX¢#÷vo´=W¹?lÇ…ÇQ§LÀEÇEOÝU„úߌG5~Ôø¹ßÆÏ]]‡½½ÿ¦v6 ß¡¼ÿ9‚葯#ù|²fÔéך£¿sE‘ÃDM²³n€~áåLøuñûøðG×Lê×âïFó3xÄòÖCço½mW•»=ý§ô¨ôx;ìÀ?iyÈéлq‡šíÿa½¿@¶êÔI¼õÆ ž”É3çyh˜ŒèØ×»_á]n0áêtyÊ#O‡äyÈÈç®húÉLOC†‰'_Êçt\ð!ætªžÇŸ1 ©FíYœ)Ëñ¤Sq²Îˆ´t#x€¦L·šÒœ’–û,ôêë%Xµídn{²¾Õ(Ë™n@W—çîô¯âGéýnØ5îîŸqÇ5 q¢¥vò⾿øb9ÕöÁÚ5<ÅÑ?îß/\ž7 E‚ŠJ7r#üÉ‚·ð‡6ÈåJùMGÖ¢ÿ“Ì|q,³.ä14+þ¾ø¿‹²•°ž31ö™‡ñã¼AØÓ`ú×.‚¤=ó1jÎN™ ÒÐgè½è”9¹HK»‚1áý¹¸¸ò5$ìNu¦•ÆësÞEàŽ1˜¹‹ž ³5 { kÿr¼öÖfg9=zÌx­ò¿kýu§Ç‡jïî¼—JïJï…ÙÅ¿c\QÙ4qwãnɱ¡CÇѹKWìØþ9Äñ×Òù“F÷FùrZ蛾]¾‡¯¤5(á#|²qšœû{ŠD7Äáå_ÁD½é}üáåëIýÀl:Ÿçâ–±üfÌùW:.“<úÙËI«ß¬e¬¿µ|."Éã ÌZµIÒ›Ðà¶üнgcJ»ðn‡—õCîfÑùôŒ_-ËÍcV"ã.ö×Õô¥ÒïÎû£ô®ô~7ìöíww åY¬9Ò ùûû£n½úò!*¹|ÓžÇYájˆ\±D[<ö ™&‚õ:4{: ÙY<¹H æYá)&{y‰›õä/8‹z˜ÈòÙ(U·%*`–ï¤Õ¸R L¬ïЗCûö °uiµ^÷‚H¨õ3m`µÙ¥E¿Y+-ý-ü¹V;b¥ÆbuÀ—^J¸#‡Ù›¸»ÚáKﺼ‹Ä–Œ «å龸†%¦îÄ¥•rpMÉ.kAwfzODýCÑñ©røÓÕ$*úÁu‰6|}ýù7:vD©éôt5 t:ªé^è·kéOñw÷fT¿¨÷ã~yÿ¸ Ž‘4MÝݾ $D&n‰—á¾ÙûU凗+_{r>;ó„¾\—®DêêNâíe_!)ù vÌŸ‰$øÉl‡[yAÏdþ‹#н} „–Öãßìðg†¤g5ÁšÌ ,áU¡6*"\òEhhu»¼s–í£“¾7úënÕ¾®÷SÝï¾ý¾Õ÷ñž@@ÿmÄ&-ý-ü¹Z»^þ@oÍ]3Œd‘¾,oÐò\ˆElT°åGô’i˜1lFm±£^(Ô/ú…äÈà¤åŠH«¶EäÃ0$òyæë‰qì I‘_±Y7xŽŽGßDZ`a_®‡½Œ¾ã‡àsI‰›âÀrtÕ QWë?•®Æ‡ë}S÷{ü=ù+%Í!;é>CžwÆ´)øõ—_¤™¾Ñ?ÕkÔÀØñ“4Ä%Œ™›üžoøx9`β"Ç¡ãt˜:›YbM¯‡¯—Y&37 øÀS¦_À–ù¡b§—Q§œ7¾Fû¾ñ˜¼d5 4ÀkH¢¼]¶ãAzzxp›¶ŽÎŒ³iÐÙm0f[áÅv½I[G^²¸^äÁgŸM™øÑKÙsreÉÑœ¿÷{ÿ)þó?¥¥w{ô¿<îú/!Hø,Œ0ÿËÈõîÞÞzxÝÄ/'ˆvìüµ…,3×Vn¡½+ùôÀ±u£1iýQRÓ®‡ÛOÄÌ.µ`ËZL*ØŽ'¿å°sÊ‘y…åç•×¢‘a‡\ƒÊK¿^=•m½*ý(ý\ù^ª÷ë¿ÿ è^ˆ ÞèMF\ä7X½õEáëgƒÙœÃmj·Ú½Ð?ÿ”ˆRɩ޷ûé}ûŸ@@÷J$'~ÓΓ!”06'ºQÕލ®‹(?÷Êû¢øPìžÙ'#7a<b¸Oïâ‡U­ÜR.Ï},ÇýªÅ÷ýýþ¨þûçõŸB@*b¾Î”B *RW‘ºtŽ´ê~{íB@ ©Ü·ˆSEÌÿ¼ˆù~ž!QãõÊñªB@ ©ÈVEöÊÜ; B@  ã£ÆjÜñ÷@! ùÜ•ÈGÍ¥ßÞ¹t¥O¥Ïûq­R! ùªÈWE¾w<òUˆóþÝí{;ײRH! Ž… ‚¸Äý>nRH! …€Rvà®Ø…€RH! …•¸+vடˆ*ç‚ üõÝü-£õë>ÀÇë×q8^ýjÏcÄÛwè¤~•úê·{m)~Ôo²ÝM;v¿Œ?…€èg\s¿ë?ú_}¹ qS§ãÁR¥rÓ]ùâž|þ<Æ‹Fã&Í Ñý>«øÏë÷~VzQzQãáï_Uk@ι_|„󉛆"AE‘iʆ‰gòˆ{äóÏáþ÷Ï?ÿ\:¨¯¿Ü tX2ÒNì*sÈæÌó8—”ŒÌBòMéÉH:s^ž€zµú7”nL#30ñôÕ+Ê›3Ù~’3Í76Ç›yQ–OË´_QÞjÔø5"o&O‚=—’veûŽL¤ŠöÓ²¯ §ƒÉä;5Ý|ë©ÝEÒy6ÞNü€ûNÒ…Œ›Bê»Ó=±ïsü””YH¿ý}ú?wøÒ®Ã×ã_•¿-ýýwéõâDT©ˆé F {oò.‘ÏW»¥ó .Æ£wœÓN:¼å^¢¬èÑØ©Ó0aÜX™)Ðí[±oÑh¼³ý´³.O4}­ |^;Ó6wæ…cò¢ñ¨Ä“Wu:ö-›‚w¶Gë‰KÐ#Ì«öÁ¿ÓÅÑuyWÑgb0§WMdYƒW¦|œ›Ñ#v)Z± ÁOæÑÏ0xÂbéàDg¢ñF¯Geû¦³ß"nølœz‰ zÊ“VÿÜþ&Æ/ú>—VX׌{¾&Ë[°ñhÌ. ‹ÖÎ9,þ>Orò§ÇøÄñå9ããaŒŠÁ)Ù"Þ3£ž­&Ÿt¦#ˆ.7’yR·Zœ_Ù·>.®=žÒ±cõ>„¿Ô Åïh»·.Ï_ûcé-Ѩ^9x_1^¯C7ã6}kG»gk°ÿLøuÉ"|ýÌCx´LU­?o–ÞM—ÏÂæØ©¨¿h‚yÚýëçëèå¦åPôÜß+…€èlÄáp"º3[¬ò :÷HOxþ‚—‡ÎÙfžhÊ‚<‚¡`yùœö½t>ÃnÂ'7cJ‡¬LܳpnI;¤óy5á#lظê¤w¿' >Þï|çƒ |òòô`„€¾ ×aåºõøpÝflX5eÈГOTf‰³˜» †'Îf¼=¨.VLH@*óŽ$̦«9(^¶¿!aÎn™Ž/Îð»OlDÿáñŒ %Êâ%Ê›Ç<:Ÿq«dù•³úàðš÷pÌL$”þ½t>#(‹h'Ö)‹•õλˆÎç1,`ú'W 2à,Ýñ‡ŒŒwÄMÆ™ú¯ãCæ­ŒŒƒËÇã»4 YmÕò>`Þ*gÞÞtÐGýÜj½Bûô¬DˆÉÉó!M³YC„™iiNÀBDÆr¹ÏnéiÉçó#Ì´ãX²é=ü™’Ùe¦GFºQÊq5~\é…ñåph|™‰D3Œš>RLËåËœ_–K%ò”ãÊɯÕb‘í „æ†|C{$bíÒá+Ê›ŒFd2ßJ¹Íf7äÂç‚òfœú,ÿ çÄx@:.øs:UÏ““ôRSˆôÝé°É—S¯b¼¸GÒ¦ô4ò—IzùÓµgÁ—HÏ”ú4ódâÀà’<¾ž|²?ÓÒ®VÏMŽBéªüÂõ}wô¢½HÇ»HG7™ÿÄÉ(Q²d¾‹ã8÷êÒõ%<Ù¨ &ÇŒGóO¡Ñ…G0\‘ùVÅ„˜y¨]ÌŠÌ,Ê×fä¸þœ<–ûè7ëá1Jñ¸í,o4éÑ †í¤; GÅðAóšØÔùE¤Ò÷ét<œ¯(OGÔÉÝKp68ÓÃü‘ydŽ8ê"ñÉ2rº°lËΞ?‡’ÌxÂô óšbAÓ’8ðÝ÷°=PÂØ{ñ­¾x¢<Æ&l£–•è0ŒÆ’Òé<½¤!(èA~yÜx`0SiŠyÀ‘ç*&R–Z”ÅDYÊ9eÉa‰àGº 6¾<‚³9]éðGx}_l¼@Ž­GðÉŸ6 ‰n„A¯|Dæa÷OIhÐè2>9aÃà±`G;óvýxFË»•z-ʉ\ñ:ÞøÌ…BKãõ·ÞDÇ1¾×Dê[»ž¥£niü¯½µÙ™B´:ã=´ª€sß%bä»;éÀSD½Ã/aÜ ™2í!ÝHõr>$hï‡9Ñ­%²”úÍ)[ppÅè+ù*“ƒM#úâÃ$›¤]ôÙ¼Ýâ^c°!S‚QZŸêc£OuoìI‚y»/8y+asÞÅcAG0¶çD¤êž(¯0"ÏqD—ž‡¡Ÿ=‰E#Æc£°ÙÙŽ,¤ VDáR!òv þÿš¶]ÑëU¢äwa_1{¼þµ‘üÃ2 ËÕЂ:ê[·,þÜ4ãWŸd='#ßéëÏbñÐ×ð…ÕëÂzbÁÄv °É}LG>@ÿ)›d›e˜È6õþ&,7'N¦ÉtÃcƒ°`D PÔÜzW¼‡ùô®ÊÝkúQˆrˆØPÐâ©§0uJ R’“óEjη…:kN¡œÎ/áá!0[1ZŽ"fÂgxtx;°=-·¾dÌ9"Â¥óz 6_¢C0™hÀ›Ö†=›(ˉKDŒâèî›÷?1÷½£hÝz‹ ^6¾Øú0ðäUQÎj)†œºlEŽ€5Ø…»aöŠ¥ˆù ºD„TòX±"JYv‰!&/é«`ô zXðjW¼òz?t!rªÔ7a|»=ýÊ f­Ò<눼)¹d)º~¥CQ­¼/¬äÑzb&m5bHGNÛé<™Œ ŒÆù)Çâðæ¾8zòA—–WÜÛ*OŒµ[y§.ßz=Ê_0²HïÏlˆq"·1-20{í!Ù¾([3j¦DtQáɘEÚ3~µ|ž3"+Ƭ¤Ñ·à¯? xÕ™¾8¦!v¼»&ŸZˆ_7Òé·j^¢±} ÛÑ+%LDøÿ-šò#Ö[$_S—º¥ñ±ŸÒL{Àb¢5úJ:‹_®Žo¿ ÏÈ8Éׇ ƒ‘euÀ×‹ÈØœŠs 1“í „=¾A*æmùUêOœà^³Ÿ†|G4Äáå_Ê5H/OøzROèð‰†ŒWM¡@‡©Ýty½ÂzaE\$Kµ$_ ¨ƒÞÇ^¾¡ÿŽ7¨»çâÞ“ôǽˆoÍ’È™ƒŸuj`ö:!g"êéþ»O^†éøV:Ÿ&XB¾Ezäƒi¸(‘Uòðõf] jÖ2‰¬kòÁ˜Žs•úHD½aét”Ø?þÆñÄ,wd¥žï}(äŒ\NH zá„Ü‘H+Ôù†|œô`·!›ö]§;‡ù½Æâ4§¢â‰T²ÌZt+Â6Wdl³iÎ:;,yùJ‰VóGln™‹SÁ/ ¶²/ìt$ÎZ«/2²q0M›:š2ñýâ?³½áaü ¯u‰Gšâ¥°@H. ²ˆÆGæÌOü·ŠBïæ•ôÅ",Xü>6œ„jþ9œšüžEbïqR–Ù”ÅHYt99Dfl?õ;ô³BN6*f‡9ÛIÛË5ç-äµgñÑA?¤EdÎ<-[dÁqÓõ\íäÝOîÛJ¤Ù5$rÂ-öÔVfæ¯œÔ ÆÀfUMvéØ~‰†~\6„#Ë9F2qÆ8wi‡]ë—!f)§ÖRBçÿ<ì9Èbßþ°YÌÈò4áÐWg¸æµ³c¢œ:'K{ÏáåÚhúu‹Ä5¾ú¡Š¿@”@µ6]ˆŽWà˳4²@«ÎÍ¡'¢4šÿÀ.¢”>ÏU—|:JÕEdYOœ¥"u¾åÙ¾¶.Ç*L©­ðoKçÂn7rpµoXA"Ù¢µ["X¿zfÈU:æ;ø)›ôHÃâî“Ø7/Uò“έ0yDÍOÑ{zX²´ …4úÄÈÇ÷Qwõ0!´¸lÏ?´%êêÖaß)·˜àÝêe„8,”³Ú4_2ø,AíOEŸŽÿA£g: cT?„è­ rŽÞ%jj‹&•%]1IΙWA>ù#1#Vs~O…>X>¦1çÙm2RÓÓð[þL–.B"—”|Ýj H,Ü;ù/¸¸\ÏrfÙ ¢Ÿ0çÃr Y“äôŒV. ßÒ U,*6:C‚ÊåtÈÓcúÊhè͵+çz—‹®hC¸ªLÎñq´Á´íZ9 Oœ‰H#ØÃˆU䋹øµÃ‡IYVD7&M7äÇM#†¼jQñÕ¨O";=Ís:w¿iîT¬mø" U &=g^ºK>‹Ì«Z%˜íÜj=W;yw_8¸ÃÎ"ù¡sz òˆËÎ>´Q¾ŒæZkÔ±#:vz{ŽÅŒøwQ-ð,&÷øV§—Äóº¡gëZt2:wÍø üDzš»/Z§§d;áù:aô¬7ðn×GòúÏmœI¾¬—aaàá`YhP(d¯9l6‰(u>(NƒšvIãÓ‘c‚™FX\ŽS[ÐsðX/U^ê–5½À%#Ò¹tŒœrZ­^|²æòAveºèÿýoý »ª÷Ç»ÏUDµIW—WeÐ$7èˆÏÎËÛGL€YY_VK–”ÁÛGs!¬AY_ bôBϼJÔÃb®}ÎÛ¾§WãÕnñímmÐ5.¥bK¾D}-%ÓD´O¶:, ìïí+6Nóú_=kz»—õà¡# ÿN Çô‰-`¾˜Œ \ôú®Ú´3‡æbç .¤êÒ±sùxÔoÒÂ88éÈ•\ÏG·-À¹àx¾²Ÿ†8XΫJ3ÔÖÄâíÇe¹Ã[ÖÐä7C­2@@•VÌ;‡w“/lÆo\짬PÔ[>»èŠvÄ íÀ¿Ûðå\Цa¾øÇ·ØEØSÔ׋ùFì˜ÙŸ&Õ ʒu‹Û&“FÇt 3úÅைx­eI¤&§"“FZç]/Ð(ÎKüœX‚‘ò±­Øh±£m²pª RäÍßåÈ<ºEæ=QöÖë9õæ’KÜ+Ök ÇŸ °óèEêÇ„Ý3º ç[{%ßdI^¢œWÅT¤ÿã’/BC«£Øå½˜³l¬Ù—pš†¯s‡v A꯿$’Ðö…ƒMzðG½Ö•öõqø>†ÐJAø~Ù|ìO¥Q/”¯–püº_Éþ7aÏG ¡+Ó8©`LÖ³‡ u}/,™µÇΦàðö÷°:Í-J¶å„µý}ö«™˜ýCÌä:UÖe¬‚OË5äåXSÁ›¨ïº ›£`íWÈôSß}‚ƒŽ‡Ðða1g¦]¢¼hßuýõÝ| ž¸¥Â›a`ôXnª±âèê1ý0–,úÉ¢¼h¹øwý׆eÛÌY±‹sÞ/9hÓèaMNÁ¿³u×ô}?èAý Œänw—/Œ˜Ž—@ErÃ@>b°»•/ìÙüûf,= &§~Ƙ>Ý$ H&pš'¬ôÓ˜µÑczc±ÈÑ7Ü•õÃõ ®ˆ‡å c!Û9‰e«O¡G|k¹öcw¶ëƒ>óeô5Ý BÁš0 0â·âADÏ} Ý_îóEЀkmÊ-)B¯—¤W…nÜÝö& ï gqT|.ªø#ûðš\YF»É2qù;Ðm^„_È#~NDTçDÙŽ.¨+; ù˜ØÓ}$ty_¦·š€:6X¸^Ô”yß0¯¿3¯9ó äÔPŽýë]Ù6¡çA?!zB§ž#· !å=J—¡]2²ÎySâ¨ÇñCð¹Læ&„¸¹ð)…Áí*#np'É‚ƒ…uFØþУŽ/†õÆÜÄЧÍ8ôý‰N¸ëvIÁP£;Þ©J$*"øãÅÒ–ýÿ«ìÿ¢´¾â–¶CŽý2‚éEĶ­ßmxlô"ô™9“†oA™z=Ð>¢˜D:þUÛ òáù¼ Àž·s‡˜¨'¤¸ŸšQ†¿^£ÇTv•ØU÷ÍÚŸødǨn/ˆÂÐ…´Ã‚Ù}¯*¯_E;†‘Þs¨Ü„ Vñö±q ÆÍzƒ9µq¦G¿¸e(gµáw·БH­‰1â¬Rû)”]< ½Û/“ie›Ç‹¡ÈþÏ/عc;ž|©-Š‘gpZNÓƒö¾Á?uKý„a=µz·Ã å½òÆs=Ô»z¾¾Ýr×÷Зú%¾âÕÐLKþ»——'>ßûá•»ÛŸ¯VÞ=Ý“ól>\Ç(H7‡ÛÅ\·7§¼LÜ Kz~þÈá¶n9.é{À×—/ §²Åt›ŽÓ(>ž°»žYÆE׋íx{ARpÀ—e²XGäë¼ôðs˜pÑh…·Q.^Û¸¥Maˆ|Ö%ÀA§¥D ƒ·úœLd £a -/®åp!ÙÁr~®5É¢wÇî,IDATŸÖ¾•²@O98uäâGÞilÌ\Ëñ ßÞ6IPlÝõõskŸy¾ÌËbžƒy~Ì3‘7Yÿfë]>†ø1o#…|º.«õ! ™1 þ¸ÖB™ü8e™Ã-Êf.>ù{{R眞ÓC¬¤g]e4‘?Zr;×¶„}¨OÁ?ÓXËîœ~úò€§Ÿ¸IPNeÈgk&w-À[O\þ³F_›/±ÕXoÐk|ÙØï¤©ãæѶàëÈgïãd…vèøh9¶Ó#_F±˜¥èY­Ûó‚' 8ÄT,/׳ȇ/e³9Ç™Nè™S¬‚gÊèãi‡1Û.Ûáll^¿±Ï²(„þjòÊþò”Ó²ß Þðtò)ƺ7ug¡n½É‹˜ãÖÀ1®×Ù霮5øhuL”ÓÏàÉéA±•:^z1=ª3_Òôs<ÅûÃmÜ›"ŒÓœB¿b|ÐÏ0À¼(¯™cF¬Æ‰t÷÷O=ß'úø+%Íq'<Ýö¬·«=á„Ä`¶Ü ò¹™v==5ÚNu‰µˆ[î.ì CH Ä(º~ÏGOaçËjçw)®Ëßn=-“< ƒ{Ýò×å›ô„ó"oÜÈW€ž‡ó;XDlÌÌ/ÿMÔãùNýi¿+ò÷Bé’Eaæ:‹ŽßÕë6býâÚò°œV0_x2Qð/(_»>9 þ„´ yn€/ÙmWåËƣ܎ÂùÄÆMCPp±\-²»ué˜ëâ…Óyúé§1aÜX4nÒ ]벓‘Ê£§ƒÊ”Ì=ÍUÞ˜žŒ ±\œyy'Ù¸roânLÃYžÌT¢ x`gþËbÄÙ”tèƒK Ä™ù‹Ê''-¿ày8š{‰kÊ’"Ž÷CH‰¢îUøÙˆ”$*À¿8J{ߦ¼dÔãí×€ñ,§AXqR®º”þ (DãB>Âù **{á®úõë>ࣱS§I'$f:Åtœ{y-5cÿâ1˜½ý´³:OÙŒ}­*ûKÄuhÍhLÛxÜ™ŽÉïCåqîL&ö/Åì-Çñ OÚìªÃÚA}ñizþƒŠ>3s{ÕBÚ¡Õû±“ÐcÊ´®(ù1Û‚W&,–‡w‰!ÏDcVÏ:²ýÌ3{0uÄlœz ôäÁuœÜñÆ-ú>—VX׌mWƒå­Ø÷þh¼³ÃM–) í$aňaØ–ääOŽñ‰ãQ‡aêLGƒSòä ¼gF>SUCœ™‡0¶ß¤¼¼±Õ&TÓãµê™Ù^ Û;QJžÌ)Û¹ÃçÀÙ±‘´CXIÑ_÷ òÈ8‚M{lhצæ-óg:µÓ·ÔÃòaµµþºå¼Ãý®åíG–B©âú'ß=y›Ð‚ø)×UP®tq÷à!ofžþ(T'ïWÁòºŒ½ÒùŒX¸ ŸlÜÌã®C°2q¬¢RÒé|^Møˆyk0 Î!Lš³T,Ø2" ³¿óAEräéɃ·tEÐgá:¬Z·®ÛŒOVMG–|ò‰Gø÷,â6 ÁðÙÆÛƒêbÅÄyH‘üœC<XÍAñ2ï“„a8»e:vŸe+'6bÀˆxF’Ž‡Ù‰òÖcH óé·J–_5«¯yÇ…JÒ¿—Î'Ÿ, v‹Ó–‘òÝb:ŸÇ°2~²q"~ÁÒH}l›Œ3õ_LJÌ[?—ÇÞ Vâµcê”üy+&àût͘_½Þ Œìõ*ö­Íã yÖU…ÞÝÇ3Ñ_*á§‘ŒºÊY­Z›2˜N>´t 2RSnÔÚüåO}ízO.à›Å«°ã4™[Ž'tšÒ‘’’æVÎUþÊ»1= ©â,\W;Î;Ñgnº“OW»¦ ò—nÌG_œ@*ò%’6ir‰çK§¾Æ+¶àœÅM« éäÏ”+GA¾´úB¡BŠzKú×®WŽz¾¢_ ö³zÎÇ 1²ëôb¾pˆ›ƒq&Éõ÷ÈV (×Õ¥ëKhÔ¨ &ÅŒGóOÉ)8qð™{yaÞU11fjµÀ˜”«]Žõçx–‹§¾Yˆáh\J‡L“M{´Ç‚a;èNj¡b‡ ø°yMlìü"R ¯Ä‰‘žçéA€aÓYptל ŽÄô0lÂG]$>Y†t²Q®egÏCIfL²É2EŸÁõýq~ïbükÛ §‚1ð­Ùx2DCðùå*(§zVúÑ‚ŸëéA! ¾üV:1Öâ©§05v’““s=´0"®« óu„ó—«œëîé_µÂCxÔ43-G3á3<:¼Šž‡ÜëK!›'‹é„@Dô‡`2ù£v³زHFÎI×γ±ÅqÒvûŸHxÿ(ÚGw„Áj‡§–G_sŽlßb)†œºl…]/øÞ…]ºcöŠ¥ˆ9£?ÂE¶P±}Њ‹—†Ñ.wŒT ^튽Þ]ÍF¥¾1ó¦óó+ƒpÊbü)i²<@Võ/†ªå}Aˆ¬6`ÒV#†t¬I`%pU0Šû~²çx ¼¹/ŽžÎ$„Ôòð¶Êã›mVgÞ©Ë×®PÍê–†é’Ycš]úÎ?Š7è|Úż'ÑÜ‚è–Øùö'Hg»¯ÖŒš)Ó£Â}±9æ-øöÕž?Y8 MÁÿ\Y5é;á7K:'¬D[ê¸uì*$ô€õäV:OÌZ§!Ý×ëÄäå?Ê—åìI Ñ`¬r¢Å¦—va÷1œÀl:ŸvqËd{sÿU‡Ó¦&¢kàäæ·ñ9z;ë$¢ö/‹±úÀêÉK 5ûiwÁˆ†8¼üKxUï…•q‘¤Ù’u"¼H¶ÍÙŽÇÇ,ÕhGwBæùŒüyÀbbßÖè+ÛYürt:¶ôtô“(] ø $ŽÙp ¥½'ùûájý£Ò•ž´ñ¢3B§hº¶VO’ ñmFAç#6Çu5o£s°ÛE$˜„Ä^cqšSQ³‰TŒÙôâòÒn¢¾Ý¦9èìÈÎΛî%ÜéŸÜ:'ƒ_À”Ê4øÍñÉ2®r4õ ‹ÎÕhFÅ„¶¹øoü ¯w‰O5A·êE$FÆu9.ÆüÄP±Uú6¯„3_,‚Åïãà1 ÈA¶Y8Ës˜ï”%¾ad‘§-ÅËäHþýǬ@}N6.fGQŸ¸ž‚¡ò&Sòþ8è‡4ù\ÜŠg-¿ÐzôÜY‚mÖSt×äƒLÇ÷MÖÃ3áÅ%2 ®;¸lc¹ü QZ05«BgŸM]ý޽ivdì]‹Øý¤ë-ú†ë]§21°q$Ûµ oMXŠÔKbRÓ_È¥9T‡-&:þ“{¿c^:VÏŒ¥öu°ýÁ“XÓ÷#s`½8Ç—¸û”oŒÈz_`Iì$SðÔI[zgËÉ_$¿íB%¿%ë¶Dì!]~ýê ƽˆý‰úÓá8é\Ú{ýªë)‹‘O<$kpxKë9ËTÍRçz˜‰<Ž@Ôi^ ³fôÆ ð&x¡}'ô©WYÙÎ~“ü±)úÁV›Ã“HÖ$úKLï·ÇãDéÆ, ªµé ¬_ˆ3™ÝPU®UŠñ_>õ¬ôá>ÞodEбd2.Õú²œ8†Û©]º hFèЪ¼œj3¼ø¸CQ§;Á"KŽC¢Ýœ/>9 s(Štøaײ«Úÿ†ï¿ßÉ ƒ±¸I4zT”ÊåO`a‡ÍÆiU§=¹Žs-"°Ð I'ìãÌ'Ÿâr—O=+}ÜìxP¿„ÀP\Âs‹Ë…„ät‘P2¿÷ód£Æˆ™8.wÍÇ…|DyW½+ï™Ø># Ÿ&ÕÂŒ‰-•zFY¾jS~èÐ\ì<‘Éç‹Ø¹|<ê·BYpÑ´Ååzþ}K"Î÷Àó•}äÚ‹H×Wi†ÚºX¼ý¸,wxËšüf/CSQ¥%óÎáãG%´ÿlÂ*:† Eé´ÜèÊþñÛm·a×± R©Çö`—ÝŽ¢>bºÌˆíÓ5Y¦S–ì ɸɩ4^ŽÌß1£_ ÎE İ–%‘òW Œ\õwè« ²¦æÍÛ*‚kdþ¾-v<[»,`¨‚DÞümȤÆ£Z^›ˆ²×®'Úsö“h[\®g÷»¾b}„écùöÿÀÌò'¿œÎ­ô3¨›¼K–7T@›²^ØóÛ%” C¥2±æ•Hµ™pþ¨5{t@Óð0xÿõ[nEYÞ!%ý¢L«X¯%é?á²OiT }߯Åòý)°9ùtçË|æ,§¹: M³Gñ O:±ŒAØña¨«;‰·–}…¤ä3Ø>&’ˆ¸„ƒ©×ºÒ¾>ßò¡­„ï–ÍÇ©y»XrŽ_ç£lOl¦±rÓ7!8çñÞ+ñyƃhÚ~Fõ¬ó·G¤Þö¶úËEHÞsù5è)×Jl‘ùFìùh!teZ£2›¸rË"§ÊWú¹Ññ¡P!‘œkMH¼H ‰«ÅSOË)ºC>|ÿ}Š¥D ü3F÷é&iD2qù;-ý4fDíŘ1½±Xäè›aîªÇaµk¬HnÂFã¤E'°lÍ)ôˆoMôãH¤Û­bÄÌ—ÑgÔht_$jãÕ„Q(ƨ5G‚蹯¡û+cÐm¾Èpí£MY²IéFÉNU}x5–ÔdS@ݧ‹ñ‹0B?'¢og­¦.¨+·vwD³13ðM÷‘èßE2‡CðX;Ìœrlμ=ÌÐõ}ÙšÈ{4PË»z=—>Da óž]òhz)±3_ÁàQãÑש—žq³Qœ(Ã_ÖrÖ³{ §Îöõ‹nÛ4C^ƒÛÁ«úûãè4y0:Îa÷ «[BÖÔéðdßG1vö0 æ&„yýÚ"6êgLxµ»FY¸ÜN®#2áåÎWµÈÁ¨ÐwºFJ¢2_´jÏ)Žè%Ó0cØ8ŒÚbGýQ¨_ô ‘ƒ2m(ÃOðZ×í²¼¡F¼S•7K Lq9‘ˆøèϽ~˜Fb¸Ø„°(Mž{cÇvÇ2Q†cðÕ„NÒeá×µÛ±õ©'кzüÎ׿æòkÑ!˜k…?΂åtd@uÄ,!:æ¯é¹å y˜­ò•^Ä0¸þ8ø+%Í!“ðXêž_½ÄTÅ%Ö| Ýív½éôzøyñ{"¢øßu·fgÃÂiƒ·z“GøÃÂi21£õƒ'||iôr¬rI硇/‘ˆÃõìÖ_lÇ×ËHtðæ4™HÐñ¤sñqpû­‘“KþEÉÓqysÿ^ox{9¸V`åšQ7¼r2aâl‹Ã/®yˆ5)–óᔕ»l9fdzncwÉ'ïl;‹ëvOòæmCV&àççÖ¾§Œ˜gdžŽy¾¾\ßkJBŸ×ª—›ï›9 ÿ~c2¶%ÛÅŒ“¼¬„9ÏDOBËGŠR6îè#ý€lÔ]¶ÍƒüˆzÔ ï’OX¨@¡?/» f®í½ŠúBDowp*KÈ+êyûø€YôäŸúÔž-Ro~D9–tü{Ö¤+øj3.ÏV b¾PÛç?²­±eþG¨ØéeÔ)ç Ç…¯Ñ¾o<&/YJìw_Žk&'Ø8Ågç2•Ÿ©?æå˜9žÄZ£ƒ›#ĺMêOÛpae>ôìWnkàñ ÐÓá ];àëgàîJö‰Ù:/®[šØB/B~OÒŸ9…é \6"«ì®t¹?e7”Ýüoƃú%§áºÚMï%¦ ˆhœk>W+w+ébë7m7í˜]F•·BCÖ᪼0ˆN™Ñ>æ¿Ø†Xî›ØÌõ¯ÜòDZÜ}÷ß_:xÑ‘ä\AïVó\\yÈ©Mç~W"‚J—‚'×S„ë;ÉbannáD_° wºå ã+ú'_¢[¾ûG–bj:»_â;6izàèºÑ˜´þh.ŇÛOÄÌ.µè\´$ÉÝø ñ’KÅíC>þÜÒoࣇTÂõux¤T¥|Ð)¤"˜ÿ&‚¹Û°'ϹXL·ÒiÜM¹n–/"¾È/õxë‹hHL¬%ÝM9îvÿªöÿ·û_! |þX=( Ü= ¸qáhñîñ¥ZVø»4 vÁ1—ˆ´Ô]éánŽ;§PÅT¯˜ª¼›|¨÷@½wjü)$ÝŽú£4 4 4 4p§5 B>rÌÝ©ˆGµ£¶pj¨q Æ ep OþÍ´êYéC õ^(; ìÀßfœHûþ‰pBZd¢îJZP¢ô ô ì‚²‡—PHE¸*ÂUîß᪙5³t­™5…€äŠpþ®GÑUJ!(e_®fRH! …€Rvà®Ø…€’ÆçjŠJWF!…`þ.; Š|îJä£ÖÔÚÀµÖÔøøgŒu"*ûom­_÷>^¿ŽFùê—øeìö:å«'"w:êYéC 9(=(=\Í*䆀Öô¾úr7¦NŽR¥JêÎó€ºqã¢Ñ¸I3žÔ‰eþ‘Š’Sõ³B,Út¬ÒÃíÓƒZr® ä#œOlÜ4E¦)Fž­"î/<ÿ\îÿÏ?ÿqtP_³ìÇ­»î÷¦Ì™çq6)Ež@)Œ¸û\ª1=IgÎó@êüéË]÷Ù˜N:g`ä!bîôe=s&Û?‹äLË}#íd^”åÓ2µã ÜéYŒ¿ÆBèdò$ϳ)éW¶ïÈDŠh?çÒ\QÏ„dò’Îóf®X‹»F=‹IʛΣV¯¬§Öl®Ôs!ãÂM߯¤#8‘veß,UþÚzVú¹R?ÎQµéáÙ\þ§Ý%òùJs>AÁÅhÔ´C·òô@•8/y@U;u&Œ+SÛ ]©7+~X4³·ŸvÖÔ£GìB´~$Ï^;Ó6wæ…cò¢ñ¨ì/ôoÂ˦`ö–ãxfâbtóÄš}ðizþmŠò4ι½j ãȼ2åc'°¥lÃ_òc<ú^™°˜‡—iWÈ3cðF¯Çøà€éì·ˆ>§ƒžãé¥=àÏ~?±ýMŒ[ô}.­°®1îùZ|6cÿâ1dyÏÙÎ9,þ¶%9ùÓ‡c|â8„úóÐã!Œš„Sr—Þ3£ž •íëLGã–Ǽªš¯QïÜþ÷1ò­m¹<†÷t«w'ÇoÆlúÖ†vÏÖÔä¹ß›ÓŸÎÀç æahQmÜäû¦=Pòß¹~Wˆ‘ 'On×ÌÅ/_©äÚ;ùÁƒÀeótLas´ƒÂ®ô쎴ï¥Á¾p>Ù¸S:„`eân˜Eä™´S:ŸW>†k0 Î!LzW~+>Þ³¿óAräéÉSPè³pV®[×mƆUÓQ†%Ÿ|¢2ÿžÃÜØ h0Žñ´L¤ï•²Œ ,¢X)Ë.‰ÜλˆÎç1,`ú'W 2à,Ýñ§D&Ûã¦àLý×ñ!óVÆÆÁåð3ÒÞ;Yæ}À¼U2o<¾çi¦Ñ\½^*V¼ý9£É»$.’4ßÇ)ž0z-$$frš8{6¯Ÿd?I¥¹¥[ˆ$““/æ+'èºÒÝ‘jÆ©¯ñÁò-8'ô“K׊´äó<öÚü€ãL¶ïªÇgwþééHGܺòw!‡;¿b¼˜Íñir¸ø“üºÉUŽÅÙžh'“òyx—@°·xˆH ‘¿`}õœ7ŽÜûMéåæõ¢½H§»HG79ã&NFÉ’%´H<7¢æ»é¼ºt} 5Á¤˜ñhÞâ)®½ÈŸÐÏ)Pž„oULŒ™‡ðbVNåÙQ¾v 8ÖŸ…íûf=<"†£q)LYÞhÚ£= Û‰³GÅða󨨹3Ri[t< Zžœ Q†“»—àlp$¦‡ùÃxdŽ8ê"ñÉÒ1£\ËΞ?‡’²ñ„éæ5Å‚¦%qà»ï`{ ²tž<²;íD9ŒMØ„Ç,+Ñ~­¥ÓÓ‹­ÅyŒuV6¼ƒùd‚<æÓÇ% æñ£å¤,çÈÁtA\|yóHq£Ãáõ}±ñB6tÖ#øäO†D7‚MÐ+ß‘†yØýSotŸœ`ÞØF°“oOgÞ®O£A£Ì«×kQÆLE…ºš¼~åãQÜŸÂÂc`u<ŽZÊ‘‰$aňa¹èLÒo¼ÝæMc0nõ1–ç¥ WtÃáåDx[\hõ1"ÒÑD¤f|·àU$ìNÕÊ¢4^Ÿó.ª¥¯Á¿¦m—i#z½Šñ‹ÞA¨éGŒ2“!v…G™=íDt¹ãHD–&¬èÕYã¡õ©§ÞD†Ñ‹V£ª> ‹‡ÃN´« ë‰ÛÁùå0„Ganô3 ŒFÿ)›œ-c⢹¸°"šüºä%oSˆFE»¢wyg¿Œë9‘cM»žeR—Çmï]ƒm'Nh‰úÆxcÑ„ˆcÃ]õÔºQú¸ÝãAí‚c(Î`;ÛÄ5uŠæ„J”МáÈ,çódçc±jÎG”ãºëüBP+\Ç•`Öcˆ™ð>,gÏ¡Ý+„ìqî #èjÓn€ÉDÞ´6r²/!åÄ%è P¦Ñ=‰¹ïEû™1Ð[xê§´õ%`0Ó°œÅR iˆO]ÎA=oQ{tÞ…à2ež”]X/$Nh‹ Šõá°àâ!By‘o}ŒTC_튵!ÁÈ8›ŽJ}ã¦r eP³é[Ù‘’K–@™—EUè‘üœÜ€I[xua-8t‡I9,<Û®O…7÷Åg'/M¼´³pBnúú8¹ùm|n¤ƒÙ8%hÄçwú>Ü÷Ú‰>Af­šŒG¸|ä#:OÌZ· tøzr{L^þ#VD… I÷8˽Ìr:ü4½f}ö+–õë…qvôoÄʃáÍu¶F¼ ß¾3±áù0þÂïx¡ÿdüß«QÓ¯ _:øP§qV¸Ðåõç?OŒÇ·Ñù4Á’¯St:ïü‰°R¶Qôƪ‘ðcúôaÕÏuÑ3؇4€~³–¡Mբȹx/ÑùŒ]µ uÉïÞeóv‰HÊ?Ð9~´ö„+ª5qíÂ`˱âÈ!+ÇGUÌ]÷.Ê2°ü•Þ˜¶þiÌíšWú,¨_õL}*½üWãB! gd—C'$ÐŒ¸\N( A:ŸB>Nz;ñŠE@˜s˜ßk,Ns*jö“e`¢³+¢²ÓxÊKg£q~ÖRdçºÊØ2§‚_@leØ­yåèÆ´rtfa[iØ´IE§øÐ¦¢:ãïÖm$6iŠnaD/:¢g#¢~æÌOü·ŠBŸæãÌ‹±`ñ{8Øp2#h+,va¶’Ø{œ›,ô¤D éR¿Cÿ1+ät`ãb6d‹=¼^ÎÈ›7ɱÔ3ÏSð- 1ùÖ’øìÌ+´õcS^ì_ð/¬:ñ(f7¦ÃÓ¡®v\wö}tuE™1¡®ÛHiÅá£&x·zé¢ûsŸ˜þLÇêY±°;t°ý‘{ú0¾RŸÛíÇÖ¥ñX™œŒÔƒø·õ$ 3,äÞ–,¶m9†½œZÌØ»q?R&õÂé±}§.£Vhɯ;.·¯õ+³å¥C‘#èx¦¢OÇÿ Ñ3Ð1* !ú |üÕ8Œ{û“Ð4ŽÓè]Ú÷z¶”Ú¢q¥@ÊÁÏþ¥ÑŒÈ´nP«I'Žçî¨â‹lyísiŽ­ jVYD¦\í„•S‘庴F)‡™tüñlØð&åïZM® j|ºôªîJâ½½=ã@! ·æjHèfOþˆÈ„Õ\ ßS¡VŒiÌu#m®_OÃoùO2G˜Œ ¬)h®j ˆ‘¼ÃÉkF‰CÝÅŸX£Yv‚è':Ÿ¦ë $d=AÓ©£ñƒ" ßÒ u)‡I§T.Çi>h‡¾2žðöÀ91p´ÈÍÕ†xÎäšÆG¬Øž<‘3´ÊL$o{N^FhXò‘‰µ#^—²¬ŒnLãEú„’?n(1ämT‹ŠÇ¨†!È"r8ôŒë¹¦’æ@唋ÆíÀY¨Ò_Líekyé:Tz@ðm•yU³œ3¯ÐzZ{‡×ŽÀì݉XÆ¡8e#Õøpñ#ï:øû³¨+‘ŸCž5¤(}4M/Ù”õ|è`uÁ4ØÏ©Y`0t‚! A§·¡óðDÔï1Ÿ*‡?íÑX­yQÍa2ˆ°Sïððaÿ9Ñ¢#ž.)\;ü`ékZrûQ”'"-Ùz‰ð3Èõ°˜k‚GÚ‹›–ãÕn 0lþû”(ZçIthõ…†_‚P@Ä#êY锹˜ ½¸t²í~ùß½ 1CV£ñèD ˆ(î¦Váe³r½ÓKô‹FîzA†ÃaCÖeêß³¯…êU3>îòc¤ž•^nexÜ.Oö¿BÇ…„Z<õ”DB)))œvkŒI¯³æÃ—Pƒ<=˜°}FþŽ[À|1™2¿jÓá84;O\æs:v.ßú­"Œˆ“Ž´üãz>ºmÎ÷Àó•ý4ÄÁrú*MQ[w‹·“åoYC“ß áe¨ÒŠyçðñÎc’¯Œß¸ØOôU¡¨˜›sµãjÅßÀ>lפeXqño±‹sE}ÅtY&vÌì‡O“jIY²/œGšÉ¤Ñ1ÃŒ~1ø+b †µ,ÔäTf輫 ²¦æ%~Έ›»îŽmÅF‹m딥õuæÍß å0Ý"óÚD”½v=Ò9¹c7pœÄ Y£P23 ©é&®‘¹äq¿ ^çšøuÁ*3rMÊtouí„w÷‹M®K+ÿp½–p¤ÿŒË>¥élAÆ÷k±b?·Î›ÿbÁtoß¡¥õøã7;¨Ëz\ȇ5›* MY/ìùíʇV§S¥³~g.Ø î¦í J„'>ÿüÉ÷¹v±ÏÄ¥ÃùïæcðÄ­(Þ £Çr³‰G/õZWBÚ×À·|(B+ã»eóñÓzOcÈË)wÊ÷x­ç¤–Gä ‘è^Ö{2$c뢅8œ"øqV‘nM«§7øá↕8’®é飅ûQ¶mc NºêžûºÞGu/hïníY! §Ñwà "!ñÊ ‡$¦è®¶æã^_ Nóï›±ô€˜Šù£ûts¾õ!˜°l6ÂJ·Äô¨}ˆÓ‹E޾欬Çu -BõÅì˜}ªÑ=‰e«O¡G|k¹ö#62ˆt»µ4†Ï|}GA·E‚P0†&ŒB16kŃˆžûº¿2Ýæ‹< çýÛ”18מ´È2ç&‡ ݸ»í7LÞÎâ¨ø\4:UñGöá5…Ê2qù;Ðm^„_Dýs"úvN”í肺"1±š™oºDÿ.ïËôCP'€ÓhDXMsó$ãhμGíäÍ~õzÙǰpÑO’ÖüQ½rù|>v):UÖ¶çõƒ !­F£ÿÁˆ‰ê.ë"úbþ%pÓxkzÌ i‹)Q?câ«Z9è#» ”(£$"€!‘ϳ¾žÚµ#@t ·_Åf¨m‰á=‡r¡ZΌž>cÑm›¦×=bQE °+Ö¦ì¨Ó¾ìãß@ŸÝ¤T&õEIÓÒµŸBÙţлý2ÑÊ6ŽCðÿ휽‹QÀ'»ûv“lÐSQ¯’CÑÒB,Åëä°õÀJK EAÁÂZlü+üÀâüì,ÄÂV8D»˜K6wþæm>ÈÞ‘äÎÃät¶Ùì¾÷fgæÍ›7_/•“åÆÇ›rûÊ’Ÿº.ÏN¤â¾‚ŒÓ0§#_óMÉž»!öOKPq¾(ûw…âÏýÐÒ­vÛª_qœ>.‘ŠÏc0¸w.¡|[pœ”ÅÕWðÝ×ÒÞû>‰ê Ê%k¿GQ¨®–#Š:Ï^yäx†|§­BpÊäd4æ¯p‚ÈIeƒÝU‚Béð!¼²ÆîÔ²ù”Á±A%Ze'N\,µ¬ãšTéßÐþô«®)^­5=:ŠM„ ‚HªI[ê+ EZ% æs`иB›â–+S/´uÆù÷¼~}y'wž¼7ãwÉ˾$î^CdüµòÀšÄŽb ð ’X…«üîΧŸ éiO© káá4±ÊñÄÌ<ׄÜ^]ñôøç¸(š%è‰Ciiùt­&Ž~??¿—{OßlÂëÑýy9e~ÎÒ¸T:f*IYËO&lÏñÕ¹ÉñX¡O-§Öu~{ó§\éöƒèMòFHQáV“0—Ÿ6²D(Ö9TúcøáE•Võàk˜·Ë»÷õBwýØýÏå£ômùÇÆnìdÿªE± ©èëùl‡zÎÇ‹5¡®öžØØó„¾ZZPüJe`I~Ý ÖQˆš`‰JÈ‘'ÒóPªŸFö‰7ðÐnš !"U€Ç$¾E+}Å\ɰqƒt„¨Ûåïuà($è×»Û'Gö'>WRq¦t¨>JçÁ|I„ïÛç£~,”áãi/)=ŒoüáxáuRð—m¢›qþü—zÃßË߃Ç:_í´ýù£t…Ípüù¢³=Γñc|~˜„²bµš¥·—ùÀÆû²æþ<êF£¡Ô‰ZîÓŠ—ÉûdåÂøßã¿ý‚š{j1Û}ïòoMÿÅBÏ)uïú{âó:­x™¼ÛzŸ}gÐ^¶üÍ’êYRõtllL ýĸëÑ< )±Ì3Otâ›yFæýe}hY.;²\Ƶp¬Ÿåwb›ÜürcÐ_ÞñÍÓ1OÇ<˹šÈõÀopì`žoxIEND®B`‚data-migrate-6.7.0/spec/000077500000000000000000000000001402322344200150115ustar00rootroot00000000000000data-migrate-6.7.0/spec/data_migrate/000077500000000000000000000000001402322344200174325ustar00rootroot00000000000000data-migrate-6.7.0/spec/data_migrate/config_spec.rb000066400000000000000000000012031402322344200222320ustar00rootroot00000000000000require "spec_helper" describe DataMigrate::Config do it "sets default data_migrations_path path", :no_override do expect(DataMigrate.config.data_migrations_path).to eq "db/data/" end describe "data migration path configured" do before do @before = DataMigrate.config.data_migrations_path DataMigrate.configure do |config| config.data_migrations_path = "db/awesome/" end end after do DataMigrate.configure do |config| config.data_migrations_path = @before end end it do expect(DataMigrate.config.data_migrations_path).to eq "db/awesome/" end end end data-migrate-6.7.0/spec/data_migrate/data_migrator_spec.rb000066400000000000000000000060461402322344200236140ustar00rootroot00000000000000require "spec_helper" describe DataMigrate::DataMigrator do let(:subject) { DataMigrate::DataMigrator } let(:db_config) do { adapter: "sqlite3", database: "spec/db/test.db" } end before do allow(DataMigrate::DataMigrator).to receive(:db_config) { db_config } ActiveRecord::Base.establish_connection(db_config) end describe :load_migrated do before do allow(subject).to receive(:db_config) { db_config }.at_least(:once) ActiveRecord::Base.establish_connection(db_config) ::ActiveRecord::SchemaMigration.create_table DataMigrate::DataSchemaMigration.create_table end after do ActiveRecord::Migration.drop_table("data_migrations") ActiveRecord::Migration.drop_table("schema_migrations") end it do subject.assure_data_schema_table DataMigrate::DataSchemaMigration.create(version: 20090000000000) ::ActiveRecord::SchemaMigration.create(version: 20100000000000) DataMigrate::DataSchemaMigration.create(version: 20110000000000) ::ActiveRecord::SchemaMigration.create(version: 20120000000000) migrated = subject.new(:up, []).load_migrated expect(migrated.count).to eq 2 expect(migrated).to include 20090000000000 expect(migrated).to include 20110000000000 end end describe :assure_data_schema_table do before do allow(subject).to receive(:db_config) { db_config }.at_least(:once) ActiveRecord::Base.establish_connection(db_config) end after do ActiveRecord::Migration.drop_table("data_migrations") end it do ActiveRecord::Migration.drop_table("data_migrations") rescue nil subject.assure_data_schema_table expect( ActiveRecord::Base.connection.table_exists?("data_migrations") ).to eq true end end describe "#migrations_status" do before do allow(subject).to receive(:db_config) { db_config }.at_least(:once) ActiveRecord::Base.establish_connection(db_config) ::ActiveRecord::SchemaMigration.create_table DataMigrate::DataSchemaMigration.create_table end it "returns all migrations statuses" do status = subject.migrations_status expect(status.length).to eq 2 expect(status.first).to eq ["down", "20091231235959", "Some name"] expect(status.second).to eq ["down", "20171231235959", "Super update"] end end describe :match do context "when the file does not match" do it "returns nil" do expect(subject.match("not_a_data_migration_file")).to be_nil end end context "when the file doesn't end in .rb" do it "returns nil" do expect(subject.match("20091231235959_some_name.rb.un~")).to be_nil end end context "when the file matches" do it "returns a valid MatchData object" do match_data = subject.match("20091231235959_some_name.rb") expect(match_data[0]).to eq "20091231235959_some_name.rb" expect(match_data[1]).to eq "20091231235959" expect(match_data[2]).to eq "some_name" end end end end data-migrate-6.7.0/spec/data_migrate/data_schema_migration_spec.rb000066400000000000000000000006171402322344200252770ustar00rootroot00000000000000require 'spec_helper' describe DataMigrate::DataSchemaMigration do let(:subject) { DataMigrate::DataSchemaMigration } describe :table_name do it "returns correct table name" do expect(subject.table_name).to eq("data_migrations") end end describe :index_name do it "returns correct primary key name" do expect(subject.primary_key).to eq("version") end end end data-migrate-6.7.0/spec/data_migrate/data_spec.rb000066400000000000000000000044201402322344200217020ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe DataMigrate::Data do let(:subject) { DataMigrate::Data } let(:db_config) do { adapter: "sqlite3", database: "spec/db/test.db" } end let(:fixture_file_timestamps) do %w[20091231235959 20101231235959 20111231235959] end around do |example| Dir.mktmpdir do |temp_dir| @temp_dir = temp_dir # create the fake data migration files fixture_file_timestamps.each do |timestamp| FileUtils.touch File.join(temp_dir, "#{timestamp}_data_migration.rb") end example.run end end describe :define do before do allow(DataMigrate::DataMigrator). to receive(:db_config) { db_config } end after do ActiveRecord::Migration.drop_table("data_migrations") end context "when no version is supplied" do it "returns nil" do expect(subject.define(version: nil)).to be_nil end end context "when a version is supplied" do before do allow(DataMigrate::DataMigrator). to receive(:full_migrations_path).and_return(@temp_dir) end it "sets the current version to the supplied version" do version = fixture_file_timestamps[1] expect(DataMigrate::DataMigrator.current_version).not_to eq version.to_i subject.define(version: version) expect(DataMigrate::DataMigrator.current_version).to eq version.to_i end it "creates entries for migration versions that come " \ "before the supplied version" do version = fixture_file_timestamps[1] subject.define(version: version) sql_select = <<-SQL SELECT version FROM #{DataMigrate::DataSchemaMigration.table_name} SQL db_list_data = ActiveRecord::Base.connection. select_values(sql_select).map(&:to_i) expect(db_list_data).to match_array( [fixture_file_timestamps[0], fixture_file_timestamps[1]].map(&:to_i) ) # The last remaining migration (fixture_file_timestamps[2]) was # not included as part of the supplied version and so should not # appear in the data_migrations table. expect(db_list_data).not_to include(fixture_file_timestamps[2]) end end end end data-migrate-6.7.0/spec/data_migrate/database_tasks_spec.rb000066400000000000000000000104741402322344200237500ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe DataMigrate::DatabaseTasks do let(:subject) { DataMigrate::DatabaseTasks } let(:migration_path) { if Rails::VERSION::MAJOR == 6 "spec/db/migrate/6.0" elsif Rails::VERSION::MAJOR == 5 if Rails::VERSION::MINOR == 2 "spec/db/migrate/5.2" else "spec/db/migrate/5.0" end else "spec/db/migrate/4.2" end } let(:data_migrations_path) { DataMigrate.config.data_migrations_path } let(:db_config) do { adapter: "sqlite3", database: "spec/db/test.db" } end before do # In a normal Rails installation, db_dir would defer to # Rails.application.config.paths["db"].first # @see https://github.com/rails/rails/blob/a7d49ef78c36df2d1ca876451f30915ada1079a5/activerecord/lib/active_record/tasks/database_tasks.rb#L54 allow(subject).to receive(:db_dir).and_return("db") end before do allow(DataMigrate::Tasks::DataMigrateTasks).to receive(:migrations_paths) { data_migrations_path } allow(DataMigrate::DataMigrator).to receive(:db_config) { db_config } ActiveRecord::Base.establish_connection(db_config) if Rails.version >= '6.1' hash_config = ActiveRecord::DatabaseConfigurations::HashConfig.new('test', 'test', db_config) config_obj = ActiveRecord::DatabaseConfigurations.new([hash_config]) allow(ActiveRecord::Base).to receive(:configurations).and_return(config_obj) else ActiveRecord::Base.configurations[:test] = db_config end end describe :schema_file do it "returns the correct data schema file path" do expect(subject.schema_file(nil)).to eq "db/data_schema.rb" end end context "migrations" do after do begin ActiveRecord::Migration.drop_table("data_migrations") rescue ActiveRecord::StatementInvalid end ActiveRecord::Migration.drop_table("schema_migrations") end before do ActiveRecord::SchemaMigration.create_table allow(DataMigrate::SchemaMigration).to receive(:migrations_paths) { migration_path } allow(DataMigrate::DatabaseTasks).to receive(:data_migrations_path) { data_migrations_path }.at_least(:once) allow(DataMigrate::DatabaseTasks).to receive(:schema_migrations_path) { migration_path }.at_least(:once) end describe :past_migrations do it do subject.forward m = subject.past_migrations expect(m.count).to eq 1 expect(m.first[:version]).to eq 20091231235959 end it "shows nothing without any migrations" do m = subject.past_migrations expect(m.count).to eq 0 end end describe :load_schema_current do before do allow(DataMigrate::DataMigrator).to receive(:full_migrations_path).and_return(migration_path) end it "loads the current schema file" do if Rails::VERSION::MAJOR < 6 skip("Not implemented for Rails lower than 6") end allow(subject).to receive(:schema_location).and_return("spec/db/data/schema/") subject.load_schema_current versions = DataMigrate::DataSchemaMigration.normalized_versions expect(versions.count).to eq(2) end it "loads schema file that has not been update with latest data migrations" do if Rails::VERSION::MAJOR < 6 skip("Not implemented for Rails lower than 6") end allow(subject).to receive(:schema_location).and_return("spec/db/data/partial_schema/") subject.load_schema_current versions = DataMigrate::DataSchemaMigration.normalized_versions expect(versions.count).to eq(1) end end describe :forward do it "run forward default amount of times" do subject.forward versions = DataMigrate::DataSchemaMigration.normalized_versions expect(versions.count).to eq(1) end it "run forward defined number of times" do subject.forward(2) versions = DataMigrate::DataSchemaMigration.normalized_versions expect(versions.count).to eq(1) expect(versions.first).to eq "20091231235959" versions = ActiveRecord::SchemaMigration.normalized_versions expect(versions.count).to eq(1) expect(versions.first).to eq "20131111111111" end end end end data-migrate-6.7.0/spec/data_migrate/legacy_migrator_spec.rb000066400000000000000000000025211402322344200241410ustar00rootroot00000000000000require "spec_helper" describe DataMigrate::LegacyMigrator do let(:context) { DataMigrate::MigrationContext.new("spec/db/data") } after do begin ActiveRecord::Migration.drop_table("data_migrations") ActiveRecord::Migration.drop_table("schema_migrations") rescue StandardError nil end end before do ActiveRecord::Base.establish_connection(db_config) ActiveRecord::SchemaMigration.create_table DataMigrate::DataSchemaMigration.create_table end let(:db_config) do { adapter: "sqlite3", database: "spec/db/test.db" } end it "migrate legacy migrations to be in correct table" do DataMigrate::DataSchemaMigration.create_table # simulate creation of legacy data migration when # it was recorded in schema table ActiveRecord::SchemaMigration.create(version: "20091231235959") # create one migration in correct place DataMigrate::DataSchemaMigration.create(version: "20171231235959") migrated = DataMigrate::DataMigrator .new(:up, []).load_migrated expect(migrated.count).to eq 1 DataMigrate::LegacyMigrator.new("spec/db/data").migrate # after migacy migrator has been run, we should have records # of both migrations migrated = DataMigrate::DataMigrator .new(:up, []).load_migrated expect(migrated.count).to eq 2 end end data-migrate-6.7.0/spec/data_migrate/migration.rb000066400000000000000000000007171402322344200217550ustar00rootroot00000000000000require 'spec_helper' if Rails::VERSION::MAJOR >= 5 subject = DataMigrate::MigrationFive else subject = DataMigrate::Migration end describe subject do it "uses correct table name" do expect(subject.table_name).to eq("data_migrations") end it "uses correct index name" do expect(subject).to receive(:table_name_prefix) { "" } expect(subject).to receive(:table_name_suffix) { "" } expect(subject.primary_key).to eq("version") end end data-migrate-6.7.0/spec/data_migrate/migration_context_spec.rb000066400000000000000000000074671402322344200245440ustar00rootroot00000000000000require "spec_helper" describe DataMigrate::DataMigrator do let(:context) { if (Rails::VERSION::MAJOR == 6) DataMigrate::MigrationContext.new("spec/db/data-6.0") else DataMigrate::MigrationContext.new("spec/db/data") end } let(:schema_context) { if (Rails::VERSION::MAJOR == 6) ActiveRecord::MigrationContext.new("spec/db/migrate/6.0", ActiveRecord::Base.connection.schema_migration) else ActiveRecord::MigrationContext.new("spec/db/migrate/5.2") end } before do unless (Rails::VERSION::MAJOR == 5 and Rails::VERSION::MINOR == 2) || Rails::VERSION::MAJOR == 6 skip("Tests are only applicable for Rails 5.2") end end after do begin ActiveRecord::Migration.drop_table("data_migrations") ActiveRecord::Migration.drop_table("schema_migrations") rescue StandardError nil end end let(:db_config) do { adapter: "sqlite3", database: "spec/db/test.db" } end describe :migrate do before do ActiveRecord::Base.establish_connection(db_config) ActiveRecord::SchemaMigration.create_table end after do ActiveRecord::Migration.drop_table("data_migrations") ActiveRecord::Migration.drop_table("schema_migrations") end it "migrates existing file" do context.migrate(nil) context.migrations_status versions = DataMigrate::DataSchemaMigration.normalized_versions expect(versions.count).to eq(2) expect(versions).to include("20091231235959") expect(versions).to include("20171231235959") end it "undo migration" do context.migrate(nil) context.run(:down, 20171231235959) versions = DataMigrate::DataSchemaMigration.normalized_versions expect(versions.count).to eq(1) expect(versions).to include("20091231235959") end it "does not do anything if migration is undone twice" do context.migrate(nil) expect { context.run(:down, 20171231235959) }.to output(/Undoing SuperUpdate/).to_stdout expect { context.run(:down, 20171231235959) }.not_to output(/Undoing SuperUpdate/).to_stdout end it "runs a specific migration" do context.run(:up, 20171231235959) versions = DataMigrate::DataSchemaMigration.normalized_versions expect(versions.count).to eq(1) expect(versions).to include("20171231235959") end it "does not do anything if migration is ran twice" do expect { context.run(:up, 20171231235959) }.to output(/Doing SuperUpdate/).to_stdout expect { context.run(:down, 20171231235959) }.not_to output(/Doing SuperUpdate/).to_stdout end it "alerts for an invalid specific migration" do expect { context.run(:up, 201712312) }.to raise_error( ActiveRecord::UnknownMigrationVersionError, /No migration with version number 201712312/ ) end it "rolls back latest migration" do context.migrate(nil) expect { context.rollback }.to output(/Undoing SuperUpdate/).to_stdout versions = DataMigrate::DataSchemaMigration.normalized_versions expect(versions.count).to eq(1) expect(versions).to include("20091231235959") end it "rolls back 2 migrations" do context.migrate(nil) schema_context.migrate(nil) expect { context.rollback(2) }.to output(/Undoing SomeName/).to_stdout versions = DataMigrate::DataSchemaMigration.normalized_versions expect(versions.count).to eq(0) end it "rolls back 2 migrations" do context.migrate(nil) expect { context.rollback(2) }.to output(/Undoing SomeName/).to_stdout versions = DataMigrate::DataSchemaMigration.normalized_versions expect(versions.count).to eq(0) end end end data-migrate-6.7.0/spec/data_migrate/schema_dumper_spec.rb000066400000000000000000000024321402322344200236060ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe DataMigrate::SchemaDumper do let(:subject) { DataMigrate::SchemaDumper } let(:db_config) do { adapter: "sqlite3", database: "spec/db/test.db" } end let(:fixture_file_timestamps) do %w[20091231235959 20101231235959 20111231235959] end describe :dump do before do allow(DataMigrate::DataMigrator). to receive(:db_config) { db_config }.at_least(:once) ActiveRecord::Base.establish_connection(db_config) ActiveRecord::SchemaMigration.create_table DataMigrate::DataMigrator.assure_data_schema_table ActiveRecord::Base.connection.execute <<-SQL INSERT INTO #{DataMigrate::DataSchemaMigration.table_name} VALUES #{fixture_file_timestamps.map { |t| "(#{t})" }.join(', ')} SQL end after do ActiveRecord::Migration.drop_table("data_migrations") end it "writes the define method with the version key to the stream" do stream = StringIO.new DataMigrate::SchemaDumper.dump(ActiveRecord::Base.connection, stream) stream.rewind last_version = fixture_file_timestamps.last expected = "DataMigrate::Data.define(version: #{last_version})" expect(stream.read).to include expected end end end data-migrate-6.7.0/spec/data_migrate/schema_migration_spec.rb000066400000000000000000000054741402322344200243140ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe DataMigrate::SchemaMigration do let(:migration_path) { if Rails::VERSION::MAJOR == 6 "spec/db/migrate/6.0" elsif Rails::VERSION::MAJOR == 5 if Rails::VERSION::MINOR == 2 "spec/db/migrate/5.2" else "spec/db/migrate/5.0" end else "spec/db/migrate/4.2" end } let(:subject) { DataMigrate::SchemaMigration } let(:db_config) do { adapter: "sqlite3", database: "spec/db/test.db" } end let(:fixture_file_timestamps) do %w[20091231235959 20101231235959 20111231235959] end before do ActiveRecord::Base.establish_connection(db_config) ActiveRecord::SchemaMigration.create_table end after do ActiveRecord::Migration.drop_table("schema_migrations") end describe :pending_schema_migrations do it "list sorted schema migrations" do expect(subject).to receive(:migrations_paths) { migration_path } migrations = subject.pending_schema_migrations expect(migrations.count).to eq 2 expect(migrations[0][:version]).to eq(20131111111111) expect(migrations[1][:version]).to eq(20202020202011) end end describe :run do it do expect { subject.run(:up, migration_path, 20202020202011) }.to output(/20202020202011 DbMigration: migrating/).to_stdout versions = ActiveRecord::SchemaMigration.normalized_versions expect(versions.first).to eq("20202020202011") end it "undo migration" do subject.run(:up, migration_path, 20202020202011) expect { subject.run(:down, migration_path, 20202020202011) }.to output(/Undoing DbMigration/).to_stdout versions = ActiveRecord::SchemaMigration.normalized_versions expect(versions.count).to eq(0) end end if Rails.version > '6' describe :migrations_paths do context 'when a db_name is configured' do let(:paths) { ['spec/db/migrate/6.0', 'spec/db/components/migrate/6.0'] } if Rails.version > '6.1' before do allow(ActiveRecord::Base.configurations.configs_for(env_name: 'test', name: 'primary')).to receive(:migrations_paths).and_return(paths) DataMigrate.configure do |config| config.spec_name = 'primary' end end else before do allow(ActiveRecord::Base.configurations.configs_for(env_name: 'test', spec_name: 'primary')).to receive(:migrations_paths).and_return(paths) DataMigrate.configure do |config| config.spec_name = 'primary' end end end it 'lists schema migration paths' do expect(subject.migrations_paths.size).to eq(2) expect(subject.migrations_paths).to eq(paths) end end end end end data-migrate-6.7.0/spec/data_migrate/status_service_spec.rb000066400000000000000000000055001402322344200240340ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe DataMigrate::StatusService do let(:subject) { DataMigrate::SchemaDumper } let(:db_config) do { adapter: "sqlite3", database: "spec/db/test.db" } end let(:service) { DataMigrate::StatusService } context "table does not exists" do before do ActiveRecord::Base.establish_connection(db_config) end it "show error message" do allow_any_instance_of(service).to receive(:table_name) { "bogus"} stream = StringIO.new service.dump(ActiveRecord::Base.connection, stream) stream.rewind expected = "Data migrations table does not exist" expect(stream.read).to include expected end end context "table exists" do let(:fixture_file_timestamps) do %w[20091231235959 20101231235959 20111231235959] end before do allow(DataMigrate::DataMigrator). to receive(:db_config) { db_config }.at_least(:once) ActiveRecord::Base.establish_connection(db_config) ActiveRecord::SchemaMigration.create_table DataMigrate::DataMigrator.assure_data_schema_table ActiveRecord::Base.connection.execute <<-SQL INSERT INTO #{DataMigrate::DataSchemaMigration.table_name} VALUES #{fixture_file_timestamps.map { |t| "(#{t})" }.join(', ')} SQL allow_any_instance_of(service).to receive(:root_folder) { "./" } end after do ActiveRecord::Migration.drop_table("data_migrations") end it "shows successfully executed migration" do stream = StringIO.new service.dump(ActiveRecord::Base.connection, stream) stream.rewind expected = " up 20091231235959 Some name" expect(stream.read).to include expected end it "excludes files without .rb extension" do stream = StringIO.new service.dump(ActiveRecord::Base.connection, stream) stream.rewind expected = "20181128000207 Excluded file" expect(stream.read).to_not include expected end it "shows missing file migration" do stream = StringIO.new service.dump(ActiveRecord::Base.connection, stream) stream.rewind expected = " up 20101231235959 ********** NO FILE **********" s = stream.read expect(s).to include expected end it "shows migration that has not run yet" do stream = StringIO.new service.dump(ActiveRecord::Base.connection, stream) stream.rewind expected = " down 20171231235959 Super update" s = stream.read expect(s).to include expected end it "outputs migrations in chronological order" do stream = StringIO.new service.dump(ActiveRecord::Base.connection, stream) stream.rewind s = stream.read expect(s.index("20091231235959")).to be < s.index("20111231235959") end end end data-migrate-6.7.0/spec/data_migrate/tasks/000077500000000000000000000000001402322344200205575ustar00rootroot00000000000000data-migrate-6.7.0/spec/data_migrate/tasks/data_migrate_tasks_spec.rb000066400000000000000000000060061402322344200257460ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe DataMigrate::Tasks::DataMigrateTasks do describe :dump do let(:db_config) do { adapter: "sqlite3", database: "spec/db/other_test.db" } end before do allow(DataMigrate::DataMigrator).to receive(:db_config) { db_config } allow(DataMigrate::DatabaseTasks).to receive(:db_dir).and_return("spec/db") end after do ActiveRecord::Migration.drop_table("data_migrations") end context 'when not given a separate db config' do it 'does not override the default connection' do DataMigrate::Tasks::DataMigrateTasks.migrate expect(ActiveRecord::Base).not_to receive(:establish_connection) expect(DataMigrate::SchemaDumper).to receive(:dump) DataMigrate::Tasks::DataMigrateTasks.dump end end context 'when given ' do let(:override_config) do { 'host' => '127.0.0.1', 'database' => 'other_test', 'adapter' => 'sqlite3', 'username' => 'root', 'password' => nil, } end before do DataMigrate.configure do |config| config.db_configuration = override_config end end it 'overrides the default connection' do DataMigrate::Tasks::DataMigrateTasks.migrate expect(ActiveRecord::Base).to receive(:establish_connection).with(override_config) DataMigrate::Tasks::DataMigrateTasks.dump end end end describe :migrate do let(:db_config) do { adapter: "sqlite3", database: "spec/db/test.db" } end before do allow(DataMigrate::DataMigrator).to receive(:db_config) { db_config } ActiveRecord::Base.establish_connection(db_config) end after do ActiveRecord::Migration.drop_table("data_migrations") end it do expect { DataMigrate::Tasks::DataMigrateTasks.migrate }.to output(/20091231235959 SomeName: migrating/).to_stdout end it do expect { DataMigrate::Tasks::DataMigrateTasks.migrate }.to output(/20171231235959 SuperUpdate: migrating/).to_stdout end end describe :abort_if_pending_migrations do subject { DataMigrate::Tasks::DataMigrateTasks.abort_if_pending_migrations(migrations, message) } let(:message) { "ABORT_MESSAGE" } context "when there are no pending migrations" do let(:migrations) { [] } it "shouldn't do anything" do expect { subject }.to_not raise_error end end context "when there are pending migrations" do let(:migrations) do [{ name: "A", version: 1 }, { name: 'B', version: 2 }] end it "should abort with given message and print names and versions of pending migrations" do expect { subject } .to raise_error(SystemExit, message) .and output("You have 2 pending migrations:\n 1 A\n 2 B\n").to_stdout end end end end data-migrate-6.7.0/spec/db/000077500000000000000000000000001402322344200153765ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/5.0/000077500000000000000000000000001402322344200157005ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/5.0/20091231235959_some_name.rb000066400000000000000000000002411402322344200216470ustar00rootroot00000000000000class SomeName < ActiveRecord::Migration[5.0] def up puts "Doing data migration" end def down raise ActiveRecord::IrreversibleMigration end end data-migrate-6.7.0/spec/db/5.0/20171231235959_super_update.rb000066400000000000000000000002441402322344200224060ustar00rootroot00000000000000class SuperUpdate < ActiveRecord::Migration[5.0] def up puts "Doing data migration" end def down raise ActiveRecord::IrreversibleMigration end end data-migrate-6.7.0/spec/db/6.0/000077500000000000000000000000001402322344200157015ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/6.0/20091231235959_some_name.rb000066400000000000000000000002411402322344200216500ustar00rootroot00000000000000class SomeName < ActiveRecord::Migration[6.0] def up puts "Doing data migration" end def down raise ActiveRecord::IrreversibleMigration end end data-migrate-6.7.0/spec/db/6.0/20171231235959_super_update.rb000066400000000000000000000002441402322344200224070ustar00rootroot00000000000000class SuperUpdate < ActiveRecord::Migration[6.0] def up puts "Doing data migration" end def down raise ActiveRecord::IrreversibleMigration end end data-migrate-6.7.0/spec/db/data-6.0/000077500000000000000000000000001402322344200166105ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/data-6.0/20091231235959_some_name.rb000066400000000000000000000002171402322344200225620ustar00rootroot00000000000000class SomeName < ActiveRecord::Migration[6.0] def up puts "Doing data migration" end def down puts "Undoing SomeName" end end data-migrate-6.7.0/spec/db/data-6.0/20171231235959_super_update.rb000066400000000000000000000002221402322344200233120ustar00rootroot00000000000000class SuperUpdate < ActiveRecord::Migration[6.0] def up puts "Doing SuperUpdate" end def down puts "Undoing SuperUpdate" end end data-migrate-6.7.0/spec/db/data-6.0/20181128000207_excluded_file.rb.other_ext000066400000000000000000000000371402322344200253700ustar00rootroot00000000000000# This file should be excluded data-migrate-6.7.0/spec/db/data/000077500000000000000000000000001402322344200163075ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/data/20091231235959_some_name.rb000066400000000000000000000002171402322344200222610ustar00rootroot00000000000000class SomeName < ActiveRecord::Migration[5.2] def up puts "Doing data migration" end def down puts "Undoing SomeName" end end data-migrate-6.7.0/spec/db/data/20171231235959_super_update.rb000066400000000000000000000002221402322344200230110ustar00rootroot00000000000000class SuperUpdate < ActiveRecord::Migration[5.2] def up puts "Doing SuperUpdate" end def down puts "Undoing SuperUpdate" end end data-migrate-6.7.0/spec/db/data/20181128000207_excluded_file.rb.other_ext000066400000000000000000000000371402322344200250670ustar00rootroot00000000000000# This file should be excluded data-migrate-6.7.0/spec/db/data/partial_schema/000077500000000000000000000000001402322344200212635ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/data/partial_schema/data_schema.rb000066400000000000000000000001041402322344200240340ustar00rootroot00000000000000# encoding: UTF-8 DataMigrate::Data.define(version: 20091231235959) data-migrate-6.7.0/spec/db/data/partial_schema/test_data_schema.rb000066400000000000000000000001041402322344200250730ustar00rootroot00000000000000# encoding: UTF-8 DataMigrate::Data.define(version: 20091231235959) data-migrate-6.7.0/spec/db/data/schema/000077500000000000000000000000001402322344200175475ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/data/schema/data_schema.rb000066400000000000000000000001041402322344200223200ustar00rootroot00000000000000# encoding: UTF-8 DataMigrate::Data.define(version: 20171231235959) data-migrate-6.7.0/spec/db/data/schema/test_data_schema.rb000066400000000000000000000001041402322344200233570ustar00rootroot00000000000000# encoding: UTF-8 DataMigrate::Data.define(version: 20171231235959) data-migrate-6.7.0/spec/db/migrate/000077500000000000000000000000001402322344200170265ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/migrate/4.2/000077500000000000000000000000001402322344200173315ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/migrate/4.2/20131111111111_late_migration.rb000066400000000000000000000002321402322344200242700ustar00rootroot00000000000000class LateMigration < ActiveRecord::Migration def up puts "Doing schema LateMigration" end def down puts "Undoing LateMigration" end end data-migrate-6.7.0/spec/db/migrate/4.2/20202020202011_db_migration.rb000066400000000000000000000002221402322344200237250ustar00rootroot00000000000000class DbMigration < ActiveRecord::Migration def up puts "Doing schema migration" end def down puts "Undoing DbMigration" end end data-migrate-6.7.0/spec/db/migrate/5.0/000077500000000000000000000000001402322344200173305ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/migrate/5.0/20131111111111_late_migration.rb000066400000000000000000000002371402322344200242740ustar00rootroot00000000000000class LateMigration < ActiveRecord::Migration[5.0] def up puts "Doing schema LateMigration" end def down puts "Undoing LateMigration" end end data-migrate-6.7.0/spec/db/migrate/5.0/20202020202011_db_migration.rb000066400000000000000000000002271402322344200237310ustar00rootroot00000000000000class DbMigration < ActiveRecord::Migration[5.0] def up puts "Doing schema migration" end def down puts "Undoing DbMigration" end end data-migrate-6.7.0/spec/db/migrate/5.2/000077500000000000000000000000001402322344200173325ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/migrate/5.2/20131111111111_late_migration.rb000066400000000000000000000002371402322344200242760ustar00rootroot00000000000000class LateMigration < ActiveRecord::Migration[5.2] def up puts "Doing schema LateMigration" end def down puts "Undoing LateMigration" end end data-migrate-6.7.0/spec/db/migrate/5.2/20202020202011_db_migration.rb000066400000000000000000000002271402322344200237330ustar00rootroot00000000000000class DbMigration < ActiveRecord::Migration[5.2] def up puts "Doing schema migration" end def down puts "Undoing DbMigration" end end data-migrate-6.7.0/spec/db/migrate/6.0/000077500000000000000000000000001402322344200173315ustar00rootroot00000000000000data-migrate-6.7.0/spec/db/migrate/6.0/20131111111111_late_migration.rb000066400000000000000000000002371402322344200242750ustar00rootroot00000000000000class LateMigration < ActiveRecord::Migration[6.0] def up puts "Doing schema LateMigration" end def down puts "Undoing LateMigration" end end data-migrate-6.7.0/spec/db/migrate/6.0/20202020202011_db_migration.rb000066400000000000000000000002271402322344200237320ustar00rootroot00000000000000class DbMigration < ActiveRecord::Migration[6.0] def up puts "Doing schema migration" end def down puts "Undoing DbMigration" end end data-migrate-6.7.0/spec/generators/000077500000000000000000000000001402322344200171625ustar00rootroot00000000000000data-migrate-6.7.0/spec/generators/data_migration/000077500000000000000000000000001402322344200221445ustar00rootroot00000000000000data-migrate-6.7.0/spec/generators/data_migration/data_migration_generator_spec.rb000066400000000000000000000037731402322344200305450ustar00rootroot00000000000000require 'spec_helper' require 'rails/generators' require 'rails/generators/migration' require 'generators/data_migration/data_migration_generator' describe DataMigrate::Generators::DataMigrationGenerator do let(:subject) { DataMigrate::Generators::DataMigrationGenerator } describe :next_migration_number do it "next migration" do Timecop.freeze("2016-12-03 22:15:26 -0800") do expect(ActiveRecord::Base).to receive(:timestamped_migrations) { true } expect(subject.next_migration_number(1)).to eq("20161204061526") end end end describe :migration_base_class_name do let(:subject) { DataMigrate::Generators::DataMigrationGenerator.new(['my_migration']) } it "returns the correct base class name" do if ActiveRecord.version >= Gem::Version.new('5.0') expect(subject.send(:migration_base_class_name)).to eq("ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]") else expect(subject.send(:migration_base_class_name)).to eq('ActiveRecord::Migration') end end end describe :create_data_migration do let(:subject) { DataMigrate::Generators::DataMigrationGenerator.new(['my_migration']) } let(:data_migrations_file_path) { 'abc/my_migration.rb' } context 'when custom data migrations path has a trailing slash' do before do DataMigrate.config.data_migrations_path = 'abc/' end it 'returns correct file path' do expect(subject).to receive(:migration_template).with( 'data_migration.rb', data_migrations_file_path ) subject.create_data_migration end end context 'when custom data migrations path does not have a trailing slash' do before do DataMigrate.config.data_migrations_path = 'abc' end it 'returns correct file path' do expect(subject).to receive(:migration_template).with( 'data_migration.rb', data_migrations_file_path ) subject.create_data_migration end end end end data-migrate-6.7.0/spec/spec_helper.rb000066400000000000000000000021631402322344200176310ustar00rootroot00000000000000require 'rspec' require 'rails' require 'sqlite3' require 'data_migrate' require 'pry' require 'timecop' RSpec.configure do |config| config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true end config.after(:each) do DataMigrate.configure do |config| config.data_migrations_path = @prev_data_migrations_path end end config.before(:each) do |example| if example.metadata[:no_override] else @prev_data_migrations_path = DataMigrate.config.data_migrations_path if Rails::VERSION::MAJOR == 6 DataMigrate.configure do |config| config.data_migrations_path = "spec/db/6.0" end elsif Rails::VERSION::MAJOR == 5 if Rails::VERSION::MINOR == 2 DataMigrate.configure do |config| config.data_migrations_path = "spec/db/data" end else DataMigrate.configure do |config| config.data_migrations_path = "spec/db/5.0" end end else DataMigrate.configure do |config| config.data_migrations_path = "spec/db/5.0" end end end end end data-migrate-6.7.0/tasks/000077500000000000000000000000001402322344200152045ustar00rootroot00000000000000data-migrate-6.7.0/tasks/.gitkeep000066400000000000000000000000001402322344200166230ustar00rootroot00000000000000data-migrate-6.7.0/tasks/databases.rake000066400000000000000000000361021402322344200200010ustar00rootroot00000000000000require 'data_migrate/tasks/data_migrate_tasks' namespace :db do namespace :migrate do desc "Migrate the database data and schema (options: VERSION=x, VERBOSE=false)." task :with_data => :environment do assure_data_schema_table ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true target_version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil migrations = [] if target_version.nil? migrations = pending_migrations.map{ |m| m.merge(:direction =>:up) } else current_schema_version = ActiveRecord::Migrator.current_version schema_migrations = if target_version > current_schema_version pending_schema_migrations.keep_if{ |m| m[:version] <= target_version }.map{ |m| m.merge(:direction =>:up) } elsif target_version < current_schema_version past_migrations.keep_if{ |m| m[:version] > target_version }.map{ |m| m.merge(:direction =>:down) } else # == [] end current_data_version = ActiveRecord::Migrator.current_version data_migrations = if target_version > current_data_version pending_data_migrations.keep_if{ |m| m[:version] <= target_version }.map{ |m| m.merge(:direction =>:up) } elsif target_version < current_data_version past_migrations.keep_if{ |m| m[:version] > target_version }.map{ |m| m.merge(:direction =>:down) } else # == [] end migrations = if schema_migrations.empty? data_migrations elsif data_migrations.empty? schema_migrations elsif target_version > current_data_version && target_version > current_schema_version sort_migrations data_migrations, schema_migrations elsif target_version < current_data_version && target_version < current_schema_version sort_migrations(data_migrations, schema_migrations).reverse elsif target_version > current_data_version && target_version < current_schema_version schema_migrations + data_migrations elsif target_version < current_data_version && target_version > current_schema_version schema_migrations + data_migrations end end migrations.each do |migration| if migration[:kind] == :data ActiveRecord::Migration.write("== %s %s" % ['Data', "=" * 71]) DataMigrate::DataMigrator.run(migration[:direction], data_migrations_path, migration[:version]) else ActiveRecord::Migration.write("== %s %s" % ['Schema', "=" * 69]) DataMigrate::SchemaMigration.run( migration[:direction], Rails.application.config.paths["db/migrate"], migration[:version] ) end end Rake::Task["db:_dump"].invoke Rake::Task["data:dump"].invoke end namespace :redo do desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).' task :with_data => :environment do assure_data_schema_table if ENV["VERSION"] Rake::Task["db:migrate:down:with_data"].invoke Rake::Task["db:migrate:up:with_data"].invoke else Rake::Task["db:rollback:with_data"].invoke Rake::Task["db:migrate:with_data"].invoke end end end namespace :up do desc 'Runs the "up" for a given migration VERSION. (options both=false)' task :with_data => :environment do version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil raise "VERSION is required" unless version assure_data_schema_table run_both = ENV["BOTH"] == "true" migrations = pending_migrations.keep_if{|m| m[:version] == version} unless run_both || migrations.size < 2 migrations = migrations.slice(0,1) end migrations.each do |migration| if migration[:kind] == :data ActiveRecord::Migration.write("== %s %s" % ['Data', "=" * 71]) DataMigrate::DataMigrator.run(:up, data_migrations_path, migration[:version]) else ActiveRecord::Migration.write("== %s %s" % ['Schema', "=" * 69]) DataMigrate::SchemaMigration.run(:up, "db/migrate/", migration[:version]) end end Rake::Task["db:_dump"].invoke Rake::Task["data:dump"].invoke end end namespace :down do desc 'Runs the "down" for a given migration VERSION. (option BOTH=false)' task :with_data => :environment do version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil raise "VERSION is required" unless version assure_data_schema_table run_both = ENV["BOTH"] == "true" migrations = past_migrations.keep_if{|m| m[:version] == version} unless run_both || migrations.size < 2 migrations = migrations.slice(0,1) end migrations.each do |migration| if migration[:kind] == :data ActiveRecord::Migration.write("== %s %s" % ['Data', "=" * 71]) DataMigrate::DataMigrator.run(:down, data_migrations_path, migration[:version]) else ActiveRecord::Migration.write("== %s %s" % ['Schema', "=" * 69]) DataMigrate::SchemaMigration.run(:down, "db/migrate/", migration[:version]) end end Rake::Task["db:_dump"].invoke Rake::Task["data:dump"].invoke end end namespace :status do desc "Display status of data and schema migrations" task :with_data => :environment do config = connect_to_database next unless config db_list_data = ActiveRecord::Base.connection.select_values( "SELECT version FROM #{DataMigrate::DataSchemaMigration.table_name}" ) db_list_schema = ActiveRecord::Base.connection.select_values( "SELECT version FROM #{ActiveRecord::SchemaMigration.schema_migrations_table_name}" ) file_list = [] Dir.foreach(File.join(Rails.root, data_migrations_path)) do |file| # only files matching "20091231235959_some_name.rb" pattern if match_data = /(\d{14})_(.+)\.rb/.match(file) status = db_list_data.delete(match_data[1]) ? 'up' : 'down' file_list << [status, match_data[1], match_data[2], 'data'] end end Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file| # only files matching "20091231235959_some_name.rb" pattern if match_data = /(\d{14})_(.+)\.rb/.match(file) status = db_list_schema.delete(match_data[1]) ? 'up' : 'down' file_list << [status, match_data[1], match_data[2], 'schema'] end end file_list.sort!{|a,b| "#{a[1]}_#{a[3] == 'data' ? 1 : 0}" <=> "#{b[1]}_#{b[3] == 'data' ? 1 : 0}" } # output puts "\ndatabase: #{config['database']}\n\n" puts "#{"Status".center(8)} #{"Type".center(8)} #{"Migration ID".ljust(14)} Migration Name" puts "-" * 60 file_list.each do |file| puts "#{file[0].center(8)} #{file[3].center(8)} #{file[1].ljust(14)} #{file[2].humanize}" end db_list_schema.each do |version| puts "#{'up'.center(8)} #{version.ljust(14)} *** NO SCHEMA FILE ***" end db_list_data.each do |version| puts "#{'up'.center(8)} #{version.ljust(14)} *** NO DATA FILE ***" end puts end end end # END OF MIGRATE NAME SPACE namespace :rollback do desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).' task :with_data => :environment do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 assure_data_schema_table past_migrations[0..(step - 1)].each do | past_migration | if past_migration[:kind] == :data ActiveRecord::Migration.write("== %s %s" % ['Data', "=" * 71]) DataMigrate::DataMigrator.run(:down, data_migrations_path, past_migration[:version]) elsif past_migration[:kind] == :schema ActiveRecord::Migration.write("== %s %s" % ['Schema', "=" * 69]) DataMigrate::SchemaMigration.run(:down, "db/migrate/", past_migration[:version]) end end Rake::Task["db:_dump"].invoke Rake::Task["data:dump"].invoke end end namespace :forward do desc 'Pushes the schema to the next version (specify steps w/ STEP=n).' task :with_data => :environment do assure_data_schema_table step = ENV['STEP'] ? ENV['STEP'].to_i : 1 DataMigrate::DatabaseTasks.forward(step) Rake::Task["db:_dump"].invoke Rake::Task["data:dump"].invoke end end namespace :version do desc "Retrieves the current schema version numbers for data and schema migrations" task :with_data => :environment do assure_data_schema_table puts "Current Schema version: #{ActiveRecord::Migrator.current_version}" puts "Current Data version: #{DataMigrate::DataMigrator.current_version}" end end namespace :abort_if_pending_migrations do desc "Raises an error if there are pending migrations or data migrations" task with_data: :environment do message = %{Run `rake db:migrate:with_data` to update your database then try again.} DataMigrate::Tasks::DataMigrateTasks.abort_if_pending_migrations(pending_migrations, message) end end namespace :schema do namespace :load do desc "Load both schema.rb and data_schema.rb file into the database" task with_data: :environment do Rake::Task["db:schema:load"].invoke DataMigrate::DatabaseTasks.load_schema_current( :ruby, ENV["DATA_SCHEMA"] ) end end end namespace :structure do namespace :load do desc "Load both structure.sql and data_schema.rb file into the database" task with_data: :environment do Rake::Task["db:structure:load"].invoke DataMigrate::DatabaseTasks.load_schema_current( :ruby, ENV["DATA_SCHEMA"] ) end end end end namespace :data do desc 'Migrate data migrations (options: VERSION=x, VERBOSE=false)' task :migrate => :environment do DataMigrate::Tasks::DataMigrateTasks.migrate Rake::Task["data:dump"].invoke end namespace :migrate do desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).' task :redo => :environment do assure_data_schema_table if ENV["VERSION"] Rake::Task["data:migrate:down"].invoke Rake::Task["data:migrate:up"].invoke else Rake::Task["data:rollback"].invoke Rake::Task["data:migrate"].invoke end end desc 'Runs the "up" for a given migration VERSION.' task :up => :environment do assure_data_schema_table version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil raise "VERSION is required" unless version DataMigrate::DataMigrator.run(:up, data_migrations_path, version) Rake::Task["data:dump"].invoke end desc 'Runs the "down" for a given migration VERSION.' task :down => :environment do version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil raise "VERSION is required" unless version assure_data_schema_table DataMigrate::DataMigrator.run(:down, data_migrations_path, version) Rake::Task["data:dump"].invoke end desc "Display status of data migrations" task :status => :environment do config = ActiveRecord::Base.configurations[Rails.env || 'development'] ActiveRecord::Base.establish_connection(config) connection = ActiveRecord::Base.connection puts "\ndatabase: #{config['database']}\n\n" DataMigrate::StatusService.dump(connection) end end desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).' task :rollback => :environment do assure_data_schema_table step = ENV['STEP'] ? ENV['STEP'].to_i : 1 DataMigrate::DataMigrator.rollback(data_migrations_path, step) Rake::Task["data:dump"].invoke end desc 'Pushes the schema to the next version (specify steps w/ STEP=n).' task :forward => :environment do assure_data_schema_table step = ENV['STEP'] ? ENV['STEP'].to_i : 1 # TODO: No worky for .forward # DataMigrate::DataMigrator.forward('db/data/', step) migrations = pending_data_migrations.reverse.pop(step).reverse migrations.each do | pending_migration | DataMigrate::DataMigrator.run(:up, data_migrations_path, pending_migration[:version]) end Rake::Task["data:dump"].invoke end desc "Retrieves the current schema version number for data migrations" task :version => :environment do assure_data_schema_table puts "Current data version: #{DataMigrate::DataMigrator.current_version}" end desc "Raises an error if there are pending data migrations" task abort_if_pending_migrations: :environment do message = %{Run `rake data:migrate` to update your database then try again.} DataMigrate::Tasks::DataMigrateTasks.abort_if_pending_migrations(pending_data_migrations, message) end desc "Create a db/data_schema.rb file that stores the current data version" task dump: :environment do DataMigrate::Tasks::DataMigrateTasks.dump # Allow this task to be called as many times as required. An example # is the migrate:redo task, which calls other two internally # that depend on this one. Rake::Task["data:dump"].reenable end namespace :schema do desc "Load data_schema.rb file into the database" task load: :environment do DataMigrate::DatabaseTasks.load_schema_current( :ruby, ENV["DATA_SCHEMA"] ) end end end def pending_migrations DataMigrate::DatabaseTasks.sort_migrations( DataMigrate::DatabaseTasks.pending_schema_migrations, DataMigrate::DatabaseTasks.pending_data_migrations ) end def pending_data_migrations DataMigrate::DatabaseTasks.pending_data_migrations end def pending_schema_migrations DataMigrate::DatabaseTasks.pending_schema_migrations end def sort_migrations set_1, set_2=nil migrations = set_1 + (set_2 || []) migrations.sort{|a,b| sort_string(a) <=> sort_string(b)} end def sort_string migration "#{migration[:version]}_#{migration[:kind] == :data ? 1 : 0}" end def connect_to_database config = ActiveRecord::Base.configurations[Rails.env || 'development'] ActiveRecord::Base.establish_connection(config) unless DataMigrate::DataSchemaMigration.table_exists? puts 'Data migrations table does not exist yet.' config = nil end unless ActiveRecord::SchemaMigration.table_exists? puts 'Schema migrations table does not exist yet.' config = nil end config end def past_migrations(sort=nil) DataMigrate::DatabaseTasks.past_migrations(sort) end def assure_data_schema_table DataMigrate::DataMigrator.assure_data_schema_table end def data_migrations_path DataMigrate.config.data_migrations_path end