pax_global_header00006660000000000000000000000064136017256530014522gustar00rootroot0000000000000052 comment=ddb670f968353a52b74dc4aa5d0f400754a89c4c tmuxinator-1.1.4/000077500000000000000000000000001360172565300137375ustar00rootroot00000000000000tmuxinator-1.1.4/.codeclimate.yml000066400000000000000000000006361360172565300170160ustar00rootroot00000000000000--- engines: bundler-audit: enabled: false brakeman: enabled: false complexity-ruby: enabled: false duplication: enabled: true config: languages: - ruby fixme: enabled: true exclude_paths: - ".rubocop.yml" flog: enabled: true reek: enabled: false rubocop: enabled: true ratings: paths: - Gemfile.lock - "**.rb" exclude_paths: - spec/ tmuxinator-1.1.4/.github/000077500000000000000000000000001360172565300152775ustar00rootroot00000000000000tmuxinator-1.1.4/.github/ISSUE_TEMPLATE/000077500000000000000000000000001360172565300174625ustar00rootroot00000000000000tmuxinator-1.1.4/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000015241360172565300221560ustar00rootroot00000000000000--- name: Bug report about: Create a bug report to help us improve tmuxinator --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior. **Expected behavior** A clear and concise description of what you expected to happen. **Environment** - tmuxinator Version: [output of `tmuxinator -v`, e.g. "tmuxinator 0.13.0"] - tmux Version: [output of `tmux -V`, e.g. "tmux 2.3"] - Ruby Version: [output of `ruby -v`, e.g. "ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]"] - OS: [specific command varies, e.g. "Debian GNU/Linux 9.5 (stretch)"] **Additional information** Add any other context or (potentially) useful information about the problem here. Feel free to include screenshots, videos, scripts, Dockerfiles, etc. which may help others diagnose/reproduce the issue. tmuxinator-1.1.4/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000010601360172565300232040ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. tmuxinator-1.1.4/.gitignore000066400000000000000000000002521360172565300157260ustar00rootroot00000000000000*.gem *.rbc .bundle .config .yardoc .coveralls.yml Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports tmp db vendor/ tags .env* .env tmuxinator-1.1.4/.rspec000066400000000000000000000000371360172565300150540ustar00rootroot00000000000000--color --format=documentation tmuxinator-1.1.4/.rubocop.yml000066400000000000000000001045171360172565300162210ustar00rootroot00000000000000AllCops: Include: - "**/*.podspec" - "**/*.jbuilder" - "**/*.opal" - "**/Vagrantfile" - "**/Berksfile" - "**/Cheffile" - "**/Vagabondfile" Exclude: - "vendor/**/*" - "db/schema.rb" DisplayCopNames: true StyleGuideCopsOnly: false Rails: Enabled: false Style/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 Style/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 Style/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: true 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: true EnforcedStyle: no_braces SupportedStyles: - braces - no_braces - context_dependent Style/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: true 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 Style/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: true EnforcedStyle: trailing SupportedStyles: - leading - trailing Style/EmptyLineBetweenDefs: Description: Use empty lines between defs. StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods Enabled: true AllowAdjacentOneLineDefs: false Style/EmptyLinesAroundBlockBody: Description: Keeps track of empty lines around block bodies. Enabled: true EnforcedStyle: no_empty_lines SupportedStyles: - empty_lines - no_empty_lines Style/EmptyLinesAroundClassBody: Description: Keeps track of empty lines around class bodies. Enabled: true EnforcedStyle: no_empty_lines SupportedStyles: - empty_lines - no_empty_lines Style/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 EnforcedStyle: always SupportedStyles: - when_needed - always Style/FileName: Description: Use snake_case for source file names. StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-files Enabled: false Exclude: [] Style/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/IfUnlessModifier: Description: Favor modifier if/unless usage when you have a single-line body. StyleGuide: https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier Enabled: false MaxLineLength: 80 Style/IndentationWidth: Description: Use 2 spaces for indentation. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-indentation Enabled: true Width: 2 Style/IndentHash: Description: Checks the indentation of the first key in a hash literal. Enabled: true EnforcedStyle: special_inside_parentheses SupportedStyles: - special_inside_parentheses - consistent 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 Style/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 Style/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 Style/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: true EnforcedStyle: single_quotes SupportedStyles: - single_quotes - double_quotes Style/SpaceAroundBlockParameters: Description: Checks the spacing inside and after block parameters pipes. Enabled: true EnforcedStyleInsidePipes: no_space Style/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 Style/SpaceBeforeBlockBraces: Description: Checks that the left block brace has or doesn't have space before it. Enabled: true EnforcedStyle: space SupportedStyles: - space - no_space Style/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: true EnforcedStyle: space SupportedStyles: - space - no_space EnforcedStyleForEmptyBraces: no_space SpaceBeforeBlockParameters: true Style/SpaceInsideHashLiteralBraces: Description: Use spaces inside hash literal braces - or don't. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators Enabled: true 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 Style/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/TrailingCommaInLiteral: Enabled: false Style/TrailingCommaInArguments: Enabled: true 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 Style/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 MaxLineLength: 80 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/AbcSize: Description: A calculated magnitude based on number of assignments, branches, and conditions. Enabled: false Max: 15 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 80 characters. StyleGuide: https://github.com/bbatsov/ruby-style-guide#80-character-limits Enabled: true Max: 80 AllowURI: true URISchemes: - http - https Exclude: - tmuxinator.gemspec - lib/tmuxinator/window.rb - spec/lib/tmuxinator/window_spec.rb Metrics/MethodLength: Description: Avoid methods longer than 10 lines of code. StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods Enabled: false CountComments: false Max: 10 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 Metrics/BlockLength: Exclude: - tmuxinator.gemspec - lib/tmuxinator/cli.rb - spec/factories/**/*.rb - spec/matchers/**/*.rb - spec/**/*_spec.rb Style/InverseMethods: Exclude: - lib/tmuxinator/project.rb Lint/PercentStringArray: Exclude: - lib/tmuxinator/cli.rb Style/MutableConstant: Exclude: - lib/tmuxinator/project.rb 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 Lint/EndAlignment: Description: Align ends correctly. Enabled: true EnforcedStyleAlignWith: keyword Lint/DefEndAlignment: Description: Align ends corresponding to defs correctly. Enabled: true EnforcedStyleAlignWith: start_of_line 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 Style/ExtraSpacing: Description: Do not use unnecessary spacing. Enabled: true Style/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 Style/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 Style/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 Style/BlockEndNewline: Description: Put end statement of multiline block on its own line. Enabled: true # Style/Blocks: # Description: Avoid using {...} for multi-line blocks (multiline chaining is always # ugly). Prefer {...} over do...end for single-line blocks. # StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks # Enabled: true 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 Style/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 Style/CommentIndentation: Description: Indentation of comments. Enabled: true Style/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/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 Style/ElseAlignment: Description: Align elses and elsifs correctly. Enabled: true Style/EmptyElse: Description: Avoid empty else-clauses. Enabled: true Style/EmptyLines: Description: Don't use several empty lines in a row. Enabled: true Style/EmptyLinesAroundAccessModifier: Description: Keep blank lines around access modifiers. Enabled: true Style/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 Style/EndOfLine: Description: Use Unix-style line endings. StyleGuide: https://github.com/bbatsov/ruby-style-guide#crlf Enabled: true 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 Style/IndentationConsistency: Description: Keep indentation straight. Enabled: true Style/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 Style/LeadingCommentSpace: Description: Comments should start with a space. StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-space Enabled: true 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: true Style/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: true 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/OpMethod: Description: When defining binary operators, name the argument other. StyleGuide: https://github.com/bbatsov/ruby-style-guide#other-arg 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: true 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 Style/SpaceBeforeFirstArg: Description: Checks that exactly one space is used between a method name and the first argument for method calls without parentheses. Enabled: true Style/SpaceAfterColon: Description: Use spaces after colons. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators Enabled: true Style/SpaceAfterComma: Description: Use spaces after commas. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators Enabled: true Style/SpaceAroundKeyword: Enabled: true Style/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 Style/SpaceAfterNot: Description: Tracks redundant space after the ! operator. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-space-bang Enabled: true Style/SpaceAfterSemicolon: Description: Use spaces after semicolons. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators Enabled: true Style/SpaceBeforeComma: Description: No spaces before commas. Enabled: true Style/SpaceBeforeComment: Description: Checks for missing space between code and a comment on the same line. Enabled: true Style/SpaceBeforeSemicolon: Description: No spaces before semicolons. Enabled: true Style/SpaceAroundOperators: Description: Use spaces around operators. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators Enabled: true Style/SpaceInsideBrackets: Description: No spaces after [ or before ]. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-spaces-braces Enabled: true Style/SpaceInsideParens: Description: No spaces after ( or before ). StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-spaces-braces Enabled: true Style/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 Style/Tab: Description: No hard tabs. StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-indentation Enabled: true Style/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 Exclude: - tmuxinator.gemspec # Style/UnneededPercentX: # Description: Checks for %x when `` would do. # StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-x # 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: true 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/InvalidCharacterLiteral: Description: Checks for invalid character literals with a non-escaped whitespace character. Enabled: false Lint/LiteralInCondition: Description: Checks of literals used in conditions. 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 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 tmuxinator-1.1.4/.travis.yml000066400000000000000000000015121360172565300160470ustar00rootroot00000000000000language: ruby rvm: - "2.4.6" - "2.5.5" - "2.6.2" - "2.7.0" env: - TMUX_VERSION=3.0a - TMUX_VERSION=3.0 - TMUX_VERSION=2.9a - TMUX_VERSION=2.9 - TMUX_VERSION=2.8 - TMUX_VERSION=2.7 - TMUX_VERSION=2.6 - TMUX_VERSION=2.5 - TMUX_VERSION=2.4 - TMUX_VERSION=2.3 - TMUX_VERSION=2.2 - TMUX_VERSION=2.1 - TMUX_VERSION=2.0 - TMUX_VERSION=1.9 - TMUX_VERSION=1.8 - TMUX_VERSION=1.7 - TMUX_VERSION=1.6 - TMUX_VERSION=1.5 matrix: allow_failures: - env: TMUX_VERSION=1.7 before_install: - gem install bundler - sudo apt-get update -qq - sudo apt-get install -qq libevent-dev libncurses-dev - git clone https://github.com/tmux/tmux.git tmux - cd tmux - git checkout $TMUX_VERSION - sh autogen.sh - ./configure && make && sudo make install - cd .. - tmux -V script: bundle exec rake test tmuxinator-1.1.4/CHANGELOG.md000066400000000000000000000275231360172565300155610ustar00rootroot00000000000000## 1.1.4 ### Misc - bump Thor version to ~> 1.0 in order to accommodate Arch package and ecosystem (#739) - add Ruby 2.7.0 to Travis test matrix ### Bugfixes - fix various completion script issues (#705/#737) ## 1.1.3 ### Bugfixes - correct edge tmux version detection (#728) ### Misc - document local project creation (#439) - add support for tmux 3.0 and 3.0a (#734) ## 1.1.2 ### Bugfixes - prevent commands from being re-run when re-attaching to session using custom socket (#719) ### Misc - add zshell completions for command aliases - add note to README which covers temporary workaround for layout issues (#651) ## 1.1.1 ### Bugfixes - increase min XDG version in gemspec in order to exclude broken release (#708) ## 1.1.0 ### Misc - add support for tmux 2.9a ## 1.0.0 ### Misc - add support for tmux 2.9 ## 0.16.0 ### Bugfixes - fix wemux class_eval error (#590) ### Misc - drop support for ruby 2.3 - bump required_ruby_version - bump test matrix patch versions - Add `-n, [--newline], [--no-newline]` flag for list command Force output to be one entry per line - make pre/post deprecation warnings more descriptive - remove pre/post from project configuration template - remove support for Ruby 2.2 - bundler version constraint now supports bundler >= 2 (required by TravisCI) ## 0.15.0 ### Misc - add support for Ruby 2.6 to the TravisCI test matrix - add support for project config files using .yaml extension (#663) - allow test suite to pass when $TMUXINATOR_CONFIG is set (#665) ## 0.14.0 ### Misc - Add `--suppress-tmux-version-warning` flag to prevent tmux version warning (#583) - Separate version warning from deprecation messages - Add unsupported version warnings for `stop` and `local` as well - quiet deprecation warnings in test output (#619) - reword "Project Configuration Location" section of README to reflect current behavior (#621) - correct some type on readme about aliases (#660) ## 0.13.0 ### Bugfixes - prevent optargs from being lost when using the project-config flag (#639) - Add support for tmux 2.8 (#653) ## 0.12.0 ### Bugfixes - Fix zsh completion when there are no projects - Run stop hook before killing the session ### Misc - Allow YAML Anchors & Aliases as per [spec](http://yaml.org/spec/1.2/spec.html#id2765878) - Remove confusing README section about the pane-base-index and window-base-index options. These options can be set independently of one another now that #542 and #543 are merged. ## 0.11.3 ### Misc - replace j3rn's email with ethagnawl's in COC - use correct paths in generated config file comment (#440) ### Bugfixes - fix "wrong namespace" RuboCop warnings (#620) - fix [#431](https://github.com/tmuxinator/tmuxinator/issues/431), where Thor-based commands (e.g. "-v", "help") were failing ## 0.11.2 ### Bugfixes - Fix [#555](https://github.com/tmuxinator/tmuxinator/issues/555), restoring `on_project_exit` hook behaviour (same as deprecated `post`) ## 0.11.1 ### Misc - Add support for tmux 2.7 (#611) - Fix load order when multiple versions of tmuxinator are installed (#603) ## 0.11.0 ### Misc - Make Config#xdg comment reference correct XDG variable and include example of degenerate case (#597) - Introduce factory_bot, to replace factory_girl, which was renamed recently. - Add Ruby 2.5 to the TravisCI test matrix and bump patch level of existing Rubies (2.2, 2.3, 2.4) ### New Features - Add optional `--project-config=...` parameter to `tmuxinator start` (#595) ## 0.10.1 - Handle emojis in project names (#564) - Fix remaining sites where the base-index option (for windows) was incorrectly used in place of the pane-base-index option. - Treat 'tmux master' as an arbitrarily high version and display a deprecation warning for unsupported tmux versions (#524, #570) - Add tmux 2.4, 2.5, and 2.6 to the TravisCI test matrix - Updates `rubocop` to resolve security vulnerability ## 0.10.0 - Fix a bug causing the user's global pane-base-index setting not to be respected - Remove Object#blank? monkey patch (#458) - Add _Project Configuration Location_ entry to README (#360, #534) - Attach original exception message to exception re-raised by Project::load - Remove unused attr_readers from Tmuxinator::Window - Add ability for pre_window commands to parse yaml arrays - Refactor Tmuxinator::Config by extracting a Tmuxinator::Doctor class (#457) - Fix a bug where startup_window and startup_pane were not respected if running tmuxinator from within an existing tmux session (#537) - Fix a bug causing the pane-base-index option to override base-index ### Misc - Removed support for Ruby 1.9.3, 2.0, & 2.1 - Move gem dependencies from Gemfile to tmuxinator.gemspec - Add tmux 2.2 and 2.3 the TravisCI test matrix - Fix typos - Support user-specified and XDG Base Dirs configuration directories ### New Features - add on_project_start, on_project_first_start, on_project_restart, on_project_exit and on_project_stop hooks for project ## 0.9.0 ### Misc - Temporarily hiding Shorthand entry in README.md to prevent new bug reports about the mux symlink being broken - Use `alias` (bash, zsh) and `abbr` (fish) instead of a symlink to hash `mux`. #401 - replace instances of `File.exists?` (deprecated) with `File.exist?` - Refactor Config.root ### New Features - Allow multiple panes to be defined using yaml hash or array #266, #406 - Add `startup_pane` #380 - Add synchronizations panes support #97 - Add `before` and `after` options to synchronization functionality - Add deprecation warning if `synchronize: true` or `before` is used ### Bugfixes - Suppress `tmux ls` non-zero exit status/message when no sessions exist (#414) - Will no longer crash when no panes are specified in a window - Locking activesupport at < 5.0.0 to prevent broken builds on Ruby < 2.2.3 - Fixed whitespace issues in help ## 0.8.1 ### Bugfixes - Fixed broken shell completions ## 0.8.0 ### New features - Add support for deleting multiple projects at once, using `mux delete ...` - Add stop command to kill tmux sessions ### Bugfixes - Bugfix for issue with using numbers as window names - Bugfix for zsh-completion loading throwing an error if tmuxinator is not yet available. - Bugfix for using `mux delete` to delete local projects ## 0.7.2 - Bugfix for attaching to sessions by prefix when running `start` - Bugfix for "pane could not be created" error ## 0.7.1 - Bugfix where `mux open` or similar would delete the contents of the file ## 0.7.0 ### New features - Add support for starting in detached mode #307 - Support windows without names #292, #323 - Add per project `.tmuxinator.yml` support #335 :sparkles: - Support passing args on the command line #343 :tada: ### Bug fixes and Misc - Fix some RSpec deprecations - Explain what ERB is in the readme #319 - Prevent project names containing only numbers from raising a NoMethodError #324 - Fix YAML syntax highlighting in readme #325 - Add `asset_path` helper #326 - Switch to just plain Rubocop instead of hound #339 - Fix typo in readme #346 - Fix thor not returning correct exit status #192 - Add gitter badge ## 0.6.11 - Add aliasing of projects to create multiple sessions for a single project #143, #273 - ERB support for projects #267 - Post and attach options #293 - Fix typo in gemspec #294 - Fix completions not searching subdirectory #295 - Remove duplicate attribute #298 - Fix support for tmux 1.8 and below - Project cleanup #311 - Fix error when no project name is provided #303 ## 0.6.10 - Interpret config file as ERB template #255 - Fix zsh completions #262 - Alias `e` to edit and `o` to open #275 - Fix fish completions #280 - Add `startup_window` #282 - Add per window root option #283 - Fix project path detection #274 - Include completions in gemspec #270 ## 0.6.9 - Update to RSpec 3.x - Allow for earlier versions of thor #234, #235 - Remove dependency on git and fix warnings in gemspec #232, #233, #239 - Switch from `which` to `type` to stop errors in OSX 10.10 #236, #237 - Optional project root #185, #144 - Clear rbenv environment variables before starting tmux #208 - Update readme with correct fish completions path #247 - Escape path to deal with special characters #251, #256, #257 - Fix `copy` overwriting files #254, #258 ## 0.6.8 - Remove some duplication #212 - Add wemux support #88 - Thanks to Andrew Thal (@athal7) - Fix typos in readme #217, #216 - Fix encoding bug #229 - Fix specs not running due to changes in thor ## 0.6.7 - Remove use of grep for base-index #171 - Fix bugs in `Tmuxinator::Config.default?` #169 - Fix path for Rails log in directory sample #177 - Add completions for fish shell #179 - Fix grammar in readme #184 - Make commands take precedence over project names #182 - Improve error messages when $EDITOR isn't set #186, #194 - Add confirmation to deletion prompt #197 - Fix broken badge references after organisation move - Remove dependency on ActiveSupport #199 - Fix compatibility with tmux 1.9 ## 0.6.6 - Fix a bug caused by not escaping the root path #145 - Fix bash completion with a single argument #148 - Fix regression where an array of commands for a window wasn't working #149 - Add an option to call tmux wrappers or derivatives #154 - Refactor build\_panes to always return an array #157 - Clean up some branching code using `.presence` #163 - Setup TravisCI test matrix for different tmux versions #164 - Fix some grammar and spelling in readme #166 - Make multiple commands use tmux's `send-keys` rather than just using `&&` for both panes and windows #100 ## 0.6.5 - Change deprecation continue message from any key to just the enter key - Dramatically clean up the readme to be clearer for new users - Update the contributing guide with references to the GitHub styleguide and add examples of how to leave good commit messages - Use Erubis to render the project sample and fix a bad binding reference - Update the sample project to be much simpler - Fix not working delete command #142 - Fix an error in the bash completion script - Fix an issue where the wrong project path was being returned - Fix an issue where command aliases were being ignored ## 0.6.4 - Fixes broken backwards compatibility of multiple pre commands #129 - Fixes tmuxinator ignoring project root when started from within a tmux session #132 - Add gem version badge ## 0.6.3 - Remove stray pry #128 - Allow starting a tmuxinator project while inside a tmux session #130 - Set the tmux layout after pane creation to avoid pane too small errors #131 - Check for both pane-base-index and base-index #126 ## 0.6.2 - Also pass command line options to the `base_index` lookup. - Fixed bug #116. ## 0.6.1 - Remove stray binding.pry - Fix nil error when creating a new project. ## 0.6.0 - Rewrote core functionality with proper abstractions and unit tests - Fixed outstanding bugs #72 #89 #90 #93 #101 #102 #103 #104 #109 - Switched to thor for command line argument parsing - Switched to Erubis for more Rails like ERB - Added simplecov for test coverage - Added debug command line option to view generated shell commands - Added commands and completion options for completion scripts - Added `pre_window` option for running commands before all panes and windows - Deprecated `rbenv` in favour of `pre_window` - Deprecated `rvm` in favour of `pre_window` - Deprecated `cli_args` in favour of `tmux_options` - Deprecated `tabs` in favour of `windows` - Dropped support for Ruby 1.9.2 ## 0.5.0 - Added optional socket name support (Thanks to Adam Walters) - Added auto completion (Thanks to Jose Pablo Barrantes) ## 0.4.0 - Does not crash if given an invalid yaml file format. report it and exit gracefully. - Removed clunky scripts & shell aliases (Thanks to Dane O'Connor) - Config files are now rendered JIT (Thanks to Dane O'Connor) - Can now start sessions from cli (Thanks to Dane O'Connor) ## 0.3.0 - Added pre command (Thanks to Ian Yang) - Added multiple pre command (Thanks to Bjørn Arild Mæland) - Using tmux set default-path for project root - New aliases ## 0.2.0 - Added pane support (Thanks to Aaron Spiegel) - RVM support (Thanks to Jay Adkisoon) tmuxinator-1.1.4/CONTRIBUTING.md000066400000000000000000000034421360172565300161730ustar00rootroot00000000000000# Contributing ## First * Check if the issue you're going to submit isn't already submitted in the [Issues](https://github.com/tmuxinator/tmuxinator/issues) page. ## Issues * Submit a ticket for your issue, assuming one does not already exist. * The issue must: * Clearly describe the problem including steps to reproduce when it is a bug. * Also include all the information you can to make it easier for us to reproduce it, like OS version, gem versions, rbenv or rvm versions etc... * Even better, provide a failing test case for it. ## Pull Requests If you've gone the extra mile and have a patch that fixes the issue, you should submit a Pull Request! * Please follow the [GitHub Styleguide](https://github.com/styleguide/ruby) for Ruby in both implementation and tests! * Fork the repo on Github. * Create a topic branch from where you want to base your work. * Add a test for your change. Only refactoring and documentation changes require no new tests. If you are adding functionality or fixing a bug, we need a test! * Run _all_ the tests to ensure nothing else was broken. We only take pull requests with passing tests. You can run the tests with `rake test`. * Make a note in the `CHANGELOG.md` file with a brief summary of your change under the heading "Unreleased" at the top of the file. If that heading does not exist, you should add it. * Check for unnecessary whitespace with `git diff --check` before committing. * Structure your commit messages like this: ``` Summarize clearly in one line what the commit is about Describe the problem the commit solves or the use case for a new feature. Justify why you chose the particular solution. ``` * Use "fix", "add", "change" instead of "fixed", "added", "changed" in your commit messages. * Push to your fork and submit a pull request. tmuxinator-1.1.4/Gemfile000066400000000000000000000001371360172565300152330ustar00rootroot00000000000000source "https://rubygems.org" # Specify your gem's dependencies in tmuxinator.gemspec gemspec tmuxinator-1.1.4/LICENSE000066400000000000000000000020661360172565300147500ustar00rootroot00000000000000Copyright (c) 2010-2019 Allen Bargi, Christopher Chow 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. tmuxinator-1.1.4/README.md000066400000000000000000000265511360172565300152270ustar00rootroot00000000000000# Tmuxinator [![Gem Version](https://badge.fury.io/rb/tmuxinator.svg)](http://badge.fury.io/rb/tmuxinator) [![Build Status](https://secure.travis-ci.org/tmuxinator/tmuxinator.png?branch=master)](http://travis-ci.org/tmuxinator/tmuxinator?branch=master) [![Coverage Status](https://img.shields.io/coveralls/tmuxinator/tmuxinator.svg)](https://coveralls.io/r/tmuxinator/tmuxinator?branch=master) [![Code Climate](https://codeclimate.com/github/tmuxinator/tmuxinator/badges/gpa.svg)](https://codeclimate.com/github/tmuxinator/tmuxinator) [![Dependency Status](https://gemnasium.com/tmuxinator/tmuxinator.svg)](https://gemnasium.com/tmuxinator/tmuxinator) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tmuxinator/tmuxinator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) Create and manage tmux sessions easily.
Screenshot
## Installation ### Ruby gems ``` gem install tmuxinator ``` ### Homebrew ``` brew install tmuxinator ``` ## Editor and Shell tmuxinator uses your shell's default editor for opening files. If you're not sure what that is type: ``` bash echo $EDITOR ``` For me that produces "vim". If you want to change your default editor simply put a line in ~/.bashrc that changes it. Mine looks like this: ``` export EDITOR='vim' ``` ## tmux The recommended version of tmux to use is 1.8 or later, with the exception of 2.5, which is **not** supported (see [issue 536](https://github.com/tmuxinator/tmuxinator/issues/536) for details). Your mileage may vary for earlier versions. Refer to the FAQ for any odd behaviour. ## Completion Your distribution's package manager may install the completion files in the appropriate location for the completion to load automatically on startup. But, if you installed tmuxinator via Ruby's `gem`, you'll need to run the following commands to put the completion files where they'll be loaded by your shell. ### bash # wget https://raw.githubusercontent.com/tmuxinator/tmuxinator/master/completion/tmuxinator.bash -O /etc/bash_completion.d/tmuxinator.bash ### zsh # wget https://raw.githubusercontent.com/tmuxinator/tmuxinator/master/completion/tmuxinator.zsh -O /usr/local/share/zsh/site-functions/_tmuxinator Note: ZSH's completion files can be put in other locations in your `$fpath`. Please refer to the [manual](http://zsh.sourceforge.net/Doc/Release/Functions.html) for more details. ### fish $ wget https://raw.githubusercontent.com/tmuxinator/tmuxinator/master/completion/tmuxinator.fish ~/.config/fish/completions/ ## Usage A working knowledge of tmux is assumed. You should understand what windows and panes are in tmux. If not please consult the [man pages](http://manpages.ubuntu.com/manpages/precise/en/man1/tmux.1.html#contenttoc6) for tmux. ### Create a project Create or edit your projects with: ``` tmuxinator new [project] ``` Create or edit a local project where the config file will be stored in the current working directory (in `.tmuxinator.yml`) instead of the default project configuration file location (e.g. `~/.config/tmuxinator`): ``` tmuxinator new --local [project] ``` For editing you can also use `tmuxinator open [project]`. `new` is aliased to `n`,`open` to `o`, and `edit` to `e`. Please note that dots can't be used in project names as tmux uses them internally to delimit between windows and panes. Your default editor (`$EDITOR`) is used to open the file. If this is a new project you will see this default config: ```yaml # ~/.tmuxinator/sample.yml name: sample root: ~/ # Optional. tmux socket # socket_name: foo # Note that the pre and post options have been deprecated and will be replaced by # project hooks. # Project hooks # Runs on project start, always # on_project_start: command # Run on project start, the first time # on_project_first_start: command # Run on project start, after the first time # on_project_restart: command # Run on project exit ( detaching from tmux session ) # on_project_exit: command # Run on project stop # on_project_stop: command # Runs in each window and pane before window/pane specific commands. Useful for setting up interpreter versions. # pre_window: rbenv shell 2.0.0-p247 # Pass command line options to tmux. Useful for specifying a different tmux.conf. # tmux_options: -f ~/.tmux.mac.conf # Change the command to call tmux. This can be used by derivatives/wrappers like byobu. # tmux_command: byobu # Specifies (by name or index) which window will be selected on project startup. If not set, the first window is used. # startup_window: logs windows: - editor: layout: main-vertical panes: - vim - guard - server: bundle exec rails s - logs: tail -f log/development.log ``` ## Windows The windows option allows the specification of any number of tmux windows. Each window is denoted by a YAML array entry, followed by a name and command to be run. ```yaml windows: - editor: vim ``` ### Window specific root An optional root option can be specified per window: ```yaml name: test root: ~/projects/company windows: - small_project: root: ~/projects/company/small_project panes: - start this - start that ``` This takes precedence over the main root option. ## Panes **_Note that if you wish to use panes, make sure that you do not have `.` in your project name. tmux uses `.` to delimit between window and pane indices, and tmuxinator uses the project name in combination with these indices to target the correct pane or window._** Panes are optional and are children of window entries, but unlike windows, they do not need a name. In the following example, the `editor` window has 2 panes, one running vim, the other guard. ```yaml windows: - editor: layout: main-vertical panes: - vim - guard ``` The layout setting gets handed down to tmux directly, so you can choose from one of [the five standard layouts](http://manpages.ubuntu.com/manpages/precise/en/man1/tmux.1.html#contenttoc6) or [specify your own](http://stackoverflow.com/a/9976282/183537). **Please note the indentation here is deliberate. YAML's indentation rules can be confusing, so if your config isn't working as expected, please check the indentation.** For a more detailed explanation of _why_ YAML behaves this way, see [this](https://stackoverflow.com/questions/50594758/why-isnt-two-spaced-yaml-parsed-like-four-spaced-yaml/50600253#50600253) Stack Overflow question. **Note:** If you're noticing inconsistencies when using a custom layout it may be due [#651](https://github.com/tmuxinator/tmuxinator/issues/651). See [this comment](https://github.com/tmuxinator/tmuxinator/issues/651#issuecomment-497780424) for a workaround. ## Interpreter Managers & Environment Variables To use tmuxinator with rbenv, RVM, NVM etc, use the `pre_window` option. ```yaml pre_window: rbenv shell 2.0.0-p247 ``` These command(s) will run before any subsequent commands in all panes and windows. ## Custom session attachment You can set tmuxinator to skip auto-attaching to the session by using the `attach` option. ```yaml attach: false ``` If you want to attach to tmux in a non-standard way (e.g. for a program that makes use of tmux control mode like iTerm2), you can run arbitrary commands by using a project hook: ```yaml on_project_exit: tmux -CC attach ``` ## Passing directly to send-keys tmuxinator passes commands directly to send keys. This differs from simply chaining commands together using `&&` or `;`, in that tmux will directly send the commands to a shell as if you typed them in. This allows commands to be executed on a remote server over SSH for example. To support this both the window and pane options can take an array as an argument: ```yaml name: sample root: ~/ windows: - stats: - ssh stats@example.com - tail -f /var/log/stats.log - logs: layout: main-vertical panes: - logs: - ssh logs@example.com - cd /var/logs - tail -f development.log ``` ## ERB Project files support [ERB](https://en.wikipedia.org/wiki/ERuby#erb) for reusability across environments. Eg: ```yaml root: <%= ENV["MY_CUSTOM_DIR"] %> ``` You can also pass arguments to your projects, and access them with ERB. Simple arguments are available in an array named `@args`. Eg: ```bash $ tmuxinator start project foo ``` ```yaml # ~/.tmuxinator/project.yml name: project root: ~/<%= @args[0] %> ... ``` You can also pass key-value pairs using the format `key=value`. These will be available in a hash named `@settings`. Eg: ```bash $ tmuxinator start project workspace=~/workspace/todo ``` ```yaml # ~/.tmuxinator/project.yml name: project root: ~/<%= @settings["workspace"] %> ... ``` ## Starting a session This will fire up tmux with all the tabs and panes you configured, `start` is aliased to `s`. ``` tmuxinator start [project] -n [name] -p [project-config] ``` If you use the optional `[name]` argument, it will start a new tmux session with the custom name provided. This is to enable reuse of a project without tmux session name collision. If there is a `./.tmuxinator.yml` file in the current working directory but not a named project file in `~/.tmuxinator`, tmuxinator will use the local file. This is primarily intended to be used for sharing tmux configurations in complex development environments. You can provide tmuxinator with a project config file using the optional `[project-config]` argument (e.g. `--project-config=path/to/my-project.yaml` or `-p path/to/my-project.yaml`). This option will override a `[project]` name (if provided) and a local tmuxinator file (if present). ## Shorthand The [shell completion files](#completion) also include a shorthand alias for tmuxinator that can be used in place of the full name. ``` mux [command] ``` ## Other Commands Copy an existing project. Aliased to `c` and `cp` ``` tmuxinator copy [existing] [new] ``` List all the projects you have configured. Aliased to `l` and `ls` ``` tmuxinator list ``` Remove a project. Aliased to `rm` ``` tmuxinator delete [project] ``` Remove all tmuxinator configs, aliases and scripts. Aliased to `i` ``` tmuxinator implode ``` Examines your environment and identifies problems with your configuration ``` tmuxinator doctor ``` Shows tmuxinator's help. Aliased to `h` ``` tmuxinator help ``` Shows the shell commands that get executed for a project ``` tmuxinator debug [project] ``` Shows tmuxinator's version. ``` tmuxinator version ``` ## Project Configuration Location Using environment variables, it's possible to define which directory tmuxinator will use when creating or searching for project config files. (See [PR #511](https://github.com/tmuxinator/tmuxinator/pull/511).) Tmuxinator will attempt to use the following locations (in this order) when creating or searching for existing project configuration files: - `$TMUXINATOR_CONFIG` - `$XDG_CONFIG_HOME/tmuxinator` - `~/.tmuxinator` ## FAQ ### Window names are not displaying properly? Add `export DISABLE_AUTO_TITLE=true` to your `.zshrc` or `.bashrc` ## Contributing To contribute, please read the [contributing guide](https://github.com/tmuxinator/tmuxinator/blob/master/CONTRIBUTING.md). ## Copyright Copyright (c) 2010-2019 Allen Bargi, Christopher Chow. See LICENSE for further details. tmuxinator-1.1.4/Rakefile000066400000000000000000000002501360172565300154010ustar00rootroot00000000000000require "bundler/gem_tasks" require "rubocop/rake_task" require "rspec/core/rake_task" RuboCop::RakeTask.new RSpec::Core::RakeTask.new task test: ["spec", "rubocop"] tmuxinator-1.1.4/bin/000077500000000000000000000000001360172565300145075ustar00rootroot00000000000000tmuxinator-1.1.4/bin/tmuxinator000077500000000000000000000002151360172565300166450ustar00rootroot00000000000000#!/usr/bin/env ruby $:.unshift File.expand_path("../../lib/", __FILE__) require "thor" require "tmuxinator" Tmuxinator::Cli.bootstrap ARGV tmuxinator-1.1.4/code_of_conduct.md000066400000000000000000000062521360172565300174030ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at pdoherty+tmuxinator@protonmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ tmuxinator-1.1.4/completion/000077500000000000000000000000001360172565300161105ustar00rootroot00000000000000tmuxinator-1.1.4/completion/tmuxinator.bash000066400000000000000000000013141360172565300211600ustar00rootroot00000000000000#!/usr/bin/env bash _tmuxinator() { COMPREPLY=() local word word="${COMP_WORDS[COMP_CWORD]}" if [ "$COMP_CWORD" -eq 1 ]; then local commands="$(compgen -W "$(tmuxinator commands)" -- "$word")" local projects="$(compgen -W "$(tmuxinator completions start)" -- "$word")" COMPREPLY=( $commands $projects ) elif [ "$COMP_CWORD" -eq 2 ]; then local words words=("${COMP_WORDS[@]}") unset words[0] unset words[$COMP_CWORD] local completions completions=$(tmuxinator completions "${words[@]}") COMPREPLY=( $(compgen -W "$completions" -- "$word") ) fi } complete -F _tmuxinator tmuxinator mux alias mux="tmuxinator" tmuxinator-1.1.4/completion/tmuxinator.fish000066400000000000000000000022551360172565300212010ustar00rootroot00000000000000function __fish_tmuxinator_using_command set cmd (commandline -opc) if [ (count $cmd) -gt 1 ] if [ $argv[1] = $cmd[2] ] return 0 end end return 1 end set __fish_tmuxinator_program_cmd (commandline -o)[1] function __fish_tmuxinator_program eval "$__fish_tmuxinator_program_cmd $argv" end complete -f -c $__fish_tmuxinator_program_cmd -a '(__fish_tmuxinator_program completions start)' complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_use_subcommand' -x -a "(__fish_tmuxinator_program commands)" complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command start' -a "(__fish_tmuxinator_program completions start)" complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command open' -a "(__fish_tmuxinator_program completions open)" complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command copy' -a "(__fish_tmuxinator_program completions copy)" complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command delete' -a "(__fish_tmuxinator_program completions delete)" abbr --add mux "tmuxinator" tmuxinator-1.1.4/completion/tmuxinator.zsh000066400000000000000000000012771360172565300210570ustar00rootroot00000000000000#compdef _tmuxinator tmuxinator _tmuxinator() { local commands projects commands=(${(f)"$(tmuxinator commands zsh)"}) projects=(${(f)"$(tmuxinator completions start)"}) if (( CURRENT == 2 )); then _alternative \ 'commands:: _describe -t commands "tmuxinator subcommands" commands' \ 'projects:: _describe -t projects "tmuxinator projects" projects' elif (( CURRENT == 3)); then case $words[2] in copy|cp|c|debug|delete|rm|open|o|start|s|edit|e) _arguments '*:projects:($projects)' ;; esac fi return } # Local Variables: # mode: Shell-Script # sh-indentation: 2 # indent-tabs-mode: nil # sh-basic-offset: 2 # End: # vim: ft=zsh sw=2 ts=2 et tmuxinator-1.1.4/lib/000077500000000000000000000000001360172565300145055ustar00rootroot00000000000000tmuxinator-1.1.4/lib/tmuxinator.rb000066400000000000000000000010251360172565300172420ustar00rootroot00000000000000require "erubis" require "fileutils" require "shellwords" require "thor" require "thor/version" require "xdg" require "yaml" module Tmuxinator end require "tmuxinator/tmux_version" require "tmuxinator/util" require "tmuxinator/deprecations" require "tmuxinator/wemux_support" require "tmuxinator/cli" require "tmuxinator/config" require "tmuxinator/doctor" require "tmuxinator/hooks" require "tmuxinator/hooks/project" require "tmuxinator/pane" require "tmuxinator/project" require "tmuxinator/window" require "tmuxinator/version" tmuxinator-1.1.4/lib/tmuxinator/000077500000000000000000000000001360172565300167175ustar00rootroot00000000000000tmuxinator-1.1.4/lib/tmuxinator/assets/000077500000000000000000000000001360172565300202215ustar00rootroot00000000000000tmuxinator-1.1.4/lib/tmuxinator/assets/sample.yml000066400000000000000000000033011360172565300222220ustar00rootroot00000000000000# <%= path %> name: <%= name %> root: ~/ # Optional tmux socket # socket_name: foo # Note that the pre and post options have been deprecated and will be replaced by # project hooks. # Project hooks # Runs on project start, always # on_project_start: command # Run on project start, the first time # on_project_first_start: command # Run on project start, after the first time # on_project_restart: command # Run on project exit ( detaching from tmux session ) # on_project_exit: command # Run on project stop # on_project_stop: command # Runs in each window and pane before window/pane specific commands. Useful for setting up interpreter versions. # pre_window: rbenv shell 2.0.0-p247 # Pass command line options to tmux. Useful for specifying a different tmux.conf. # tmux_options: -f ~/.tmux.mac.conf # Change the command to call tmux. This can be used by derivatives/wrappers like byobu. # tmux_command: byobu # Specifies (by name or index) which window will be selected on project startup. If not set, the first window is used. # startup_window: editor # Specifies (by index) which pane of the specified window will be selected on project startup. If not set, the first pane is used. # startup_pane: 1 # Controls whether the tmux session should be attached to automatically. Defaults to true. # attach: false windows: - editor: layout: main-vertical # Synchronize all panes of this window, can be enabled before or after the pane commands run. # 'before' represents legacy functionality and will be deprecated in a future release, in favour of 'after' # synchronize: after panes: - vim - guard - server: bundle exec rails s - logs: tail -f log/development.log tmuxinator-1.1.4/lib/tmuxinator/assets/template-stop.erb000066400000000000000000000003171360172565300235120ustar00rootroot00000000000000#!<%= ENV["SHELL"] || "/bin/bash" %> <%- if tmux_has_session? name -%> cd <%= root || "." %> # Run on_project_stop command <%= hook_on_project_stop %> <%= tmux_kill_session_command %> <%- end -%> tmuxinator-1.1.4/lib/tmuxinator/assets/template.erb000066400000000000000000000047501360172565300225340ustar00rootroot00000000000000#!<%= ENV["SHELL"] || "/bin/bash" %> # Clear rbenv variables before starting tmux unset RBENV_VERSION unset RBENV_DIR <%= tmux %> start-server; cd <%= root || "." %> # Run on_project_start command. <%= hook_on_project_start %> <%- if !tmux_has_session? name -%> # Run pre command. <%= pre %> # Run on_project_first_start command. <%= hook_on_project_first_start %> # Create the session and the first window. Manually switch to root # directory if required to support tmux < 1.9 TMUX= <%= tmux_new_session_command %> <% if windows.first.root? %> <%= windows.first.tmux_window_command_prefix %> <%= "cd #{windows.first.root}".shellescape %> C-m <% end %> <% if Tmuxinator::Config.version < 1.7 %> # Set the default path for versions prior to 1.7 <% if root? %> <%= tmux %> set-option -t <%= name %> <%= Tmuxinator::Config.default_path_option %> <%= root -%> 1>/dev/null <% end %> <% end %> # Create other windows. <% windows.drop(1).each do |window| %> <%= window.tmux_new_window_command %> <% end %> <% windows.each do |window| %> # Window "<%= window.name %>" <% if window.synchronize_before? %> <%= window.tmux_synchronize_panes %> <% end %> <% unless window.panes? %> <% if window.project.pre_window %> <%= window.tmux_pre_window_command %> <% end %> <% window.commands.each do |command| %> <%= command %> <% end %> <% else %> <% window.panes.each do |pane| %> <% if pane.project.pre_window %> <%= pane.tmux_pre_window_command %> <% end %> <% if pane.tab.pre %> <%= pane.tmux_pre_command %> <% end %> <% pane.commands.each do |command| %> <%= pane.tmux_main_command(command) %> <% end %> <% unless pane.last? %> <%= pane.tmux_split_command %> <% end %> <%= window.tmux_tiled_layout_command %> <% end %> <%= window.tmux_layout_command %> <%= window.tmux_select_first_pane %> <% end %> <% if window.synchronize_after? %> <%= window.tmux_synchronize_panes %> <% end %> <% end %> <%= tmux %> select-window -t <%= startup_window %> <%= tmux %> select-pane -t <%= startup_pane %> <%- else -%> # Run on_project_restart command. <%= hook_on_project_restart %> <%- end -%> <%- if attach? -%> if [ -z "$TMUX" ]; then <%= tmux %> -u attach-session -t <%= name %> else <%= tmux %> -u switch-client -t <%= name %> fi <%- end -%> <%= post %> # Run on_project_exit command. <%= hook_on_project_exit %> tmuxinator-1.1.4/lib/tmuxinator/assets/wemux_template.erb000066400000000000000000000027321360172565300237570ustar00rootroot00000000000000#!<%= ENV["SHELL"] || "/bin/bash" %> wemux ls 2>/dev/null if [ "$?" -eq 127 ]; then cd <%= root || "." %> # Run pre command. <%= pre %> # Create the session and the first window. TMUX= <%= tmux_new_session_command %> # Set the default path. <%- if root? -%> <%= tmux %> set-option -t <%= name %> <%= Tmuxinator::Config.default_path_option %> <%= root -%> 1>/dev/null <%- end -%> # Create other windows. <%- windows.drop(1).each do |window| -%> <%= window.tmux_new_window_command %> <%- end -%> <%- windows.each do |window| -%> # Window "<%= window.name %>" <% if window.synchronize_before? %> <%= window.tmux_synchronize_panes %> <% end %> <%- unless window.panes? -%> <%= window.tmux_pre_window_command %> <%- window.commands.each do |command| -%> <%= command %> <%- end -%> <%- else -%> <%- window.panes.each do |pane| -%> <%= pane.tmux_pre_window_command %> <%= pane.tmux_pre_command %> <%- pane.commands.each do |command| -%> <%= pane.tmux_main_command(command) %> <%- end -%> <%- unless pane.last? -%> <%= pane.tmux_split_command %> <%- end -%> <%= window.tmux_layout_command %> <%- end -%> <%= window.tmux_select_first_pane %> <%- end -%> <%- if window.synchronize_after? -%> <%= window.tmux_synchronize_panes %> <%- end %> <%- end -%> <%= tmux %> select-window -t <%= startup_window %> <%= tmux %> select-pane -t <%= startup_pane %> fi wemux attach tmuxinator-1.1.4/lib/tmuxinator/cli.rb000066400000000000000000000325451360172565300200240ustar00rootroot00000000000000require "open3" module Tmuxinator class Cli < Thor # By default, Thor returns exit(0) when an error occurs. # Please see: https://github.com/tmuxinator/tmuxinator/issues/192 def self.exit_on_failure? true end include Tmuxinator::Util COMMANDS = { commands: "Lists commands available in tmuxinator", completions: "Used for shell completion", new: "Create a new project file and open it in your editor", edit: "Alias of new", open: "Alias of new", start: %w{ Start a tmux session using a project's name (with an optional [ALIAS] for project reuse) or a path to a project config file (via the -p flag) }.join(" "), stop: "Stop a tmux session using a project's tmuxinator config", local: "Start a tmux session using ./.tmuxinator.yml", debug: "Output the shell commands that are generated by tmuxinator", copy: %w{ Copy an existing project to a new project and open it in your editor }.join(" "), delete: "Deletes given project", implode: "Deletes all tmuxinator projects", version: "Display installed tmuxinator version", doctor: "Look for problems in your configuration", list: "Lists all tmuxinator projects" }.freeze # For future reference: due to how tmuxinator currently consumes # command-line arguments (see ::bootstrap, below), invocations of Thor's # base commands (i.e. 'help', etc) can be instead routed to #start (rather # than to ::start). In order to prevent this, the THOR_COMMANDS and # RESERVED_COMMANDS constants have been introduced. The former enumerates # any/all Thor commands we want to insure get passed through to Thor.start. # The latter is the superset of the Thor commands and any tmuxinator # commands, defined in COMMANDS, above. THOR_COMMANDS = %w[-v help].freeze RESERVED_COMMANDS = (COMMANDS.keys + THOR_COMMANDS).map(&:to_s).freeze package_name "tmuxinator" \ unless Gem::Version.create(Thor::VERSION) < Gem::Version.create("0.18") desc "commands", COMMANDS[:commands] def commands(shell = nil) out = if shell == "zsh" COMMANDS.map do |command, desc| "#{command}:#{desc}" end.join("\n") else COMMANDS.keys.join("\n") end say out end desc "completions [arg1 arg2]", COMMANDS[:completions] def completions(arg) if %w(start stop edit open copy delete).include?(arg) configs = Tmuxinator::Config.configs say configs.join("\n") end end desc "new [PROJECT] [SESSION]", COMMANDS[:new] map "open" => :new map "edit" => :new map "o" => :new map "e" => :new map "n" => :new method_option :local, type: :boolean, aliases: ["-l"], desc: "Create local project file at ./.tmuxinator.yml" def new(name, session = nil) if session new_project_with_session(name, session) else new_project(name) end end no_commands do def new_project(name) project_file = find_project_file(name, options[:local]) Kernel.system("$EDITOR #{project_file}") || doctor end def new_project_with_session(name, session) if Tmuxinator::Config.version < 1.6 raise "Creating projects from sessions is unsupported\ for tmux version 1.5 or lower." end windows, _, s0 = Open3.capture3(<<-CMD) tmux list-windows -t #{session}\ -F "#W \#{window_layout} \#{window_active} \#{pane_current_path}" CMD panes, _, s1 = Open3.capture3(<<-CMD) tmux list-panes -s -t #{session} -F "#W \#{pane_current_path}" CMD tmux_options, _, s2 = Open3.capture3(<<-CMD) tmux show-options -t #{session} CMD project_root = tmux_options[/^default-path "(.+)"$/, 1] unless [s0, s1, s2].all?(&:success?) raise "Session '#{session}' doesn't exist." end panes = panes.each_line.map(&:split).group_by(&:first) windows = windows.each_line.map do |line| window_name, layout, active, path = line.split(" ") project_root ||= path if active.to_i == 1 [ window_name, layout, Array(panes[window_name]).map do |_, pane_path| "cd #{pane_path}" end ] end yaml = { "name" => name, "project_root" => project_root, "windows" => windows.map do |window_name, layout, window_panes| { window_name => { "layout" => layout, "panes" => window_panes } } end } path = config_path(name, options[:local]) File.open(path, "w") do |f| f.write(YAML.dump(yaml)) end end def find_project_file(name, local = false) path = config_path(name, local) if File.exist?(path) path else generate_project_file(name, path) end end def config_path(name, local = false) if local Tmuxinator::Config::LOCAL_DEFAULT else Tmuxinator::Config.default_project(name) end end def generate_project_file(name, path) template = Tmuxinator::Config.default? ? :default : :sample content = File.read(Tmuxinator::Config.send(template.to_sym)) erb = Erubis::Eruby.new(content).result(binding) File.open(path, "w") { |f| f.write(erb) } path end def create_project(project_options = {}) # Strings provided to --attach are coerced into booleans by Thor. # "f" and "false" will result in `:attach` being `false` and any other # string or the empty flag will result in `:attach` being `true`. # If the flag is not present, `:attach` will be `nil`. attach = detach = false attach = true if project_options[:attach] == true detach = true if project_options[:attach] == false options = { args: project_options[:args], custom_name: project_options[:custom_name], force_attach: attach, force_detach: detach, name: project_options[:name], project_config: project_options[:project_config] } begin Tmuxinator::Config.validate(options) rescue => e exit! e.message end end def render_project(project) if project.deprecations.any? project.deprecations.each { |deprecation| say deprecation, :red } show_continuation_prompt end Kernel.exec(project.render) end def version_warning?(suppress_flag) !Tmuxinator::TmuxVersion.supported? && !suppress_flag end def show_version_warning say Tmuxinator::TmuxVersion::UNSUPPORTED_VERSION_MSG, :red show_continuation_prompt end def show_continuation_prompt say print "Press ENTER to continue." STDIN.getc end def kill_project(project) Kernel.exec(project.kill) end end desc "start [PROJECT] [ARGS]", COMMANDS[:start] map "s" => :start method_option :attach, type: :boolean, aliases: "-a", desc: "Attach to tmux session after creation." method_option :name, aliases: "-n", desc: "Give the session a different name" method_option "project-config", aliases: "-p", desc: "Path to project config file" method_option "suppress-tmux-version-warning", desc: "Don't show a warning for unsupported tmux versions" def start(name = nil, *args) # project-config takes precedence over a named project in the case that # both are provided. if options["project-config"] args.unshift name if name name = nil end params = { args: args, attach: options[:attach], custom_name: options[:name], name: name, project_config: options["project-config"] } show_version_warning if version_warning?( options["suppress-tmux-version-warning"] ) project = create_project(params) render_project(project) end desc "stop [PROJECT]", COMMANDS[:stop] map "st" => :stop method_option "suppress-tmux-version-warning", desc: "Don't show a warning for unsupported tmux versions" def stop(name) params = { name: name } show_version_warning if version_warning?( options["suppress-tmux-version-warning"] ) project = create_project(params) kill_project(project) end desc "local", COMMANDS[:local] map "." => :local method_option "suppress-tmux-version-warning", desc: "Don't show a warning for unsupported tmux versions" def local show_version_warning if version_warning?( options["suppress-tmux-version-warning"] ) render_project(create_project(attach: options[:attach])) end desc "debug [PROJECT] [ARGS]", COMMANDS[:debug] method_option :attach, type: :boolean, aliases: "-a", desc: "Attach to tmux session after creation." method_option :name, aliases: "-n", desc: "Give the session a different name" method_option "project-config", aliases: "-p", desc: "Path to project config file" def debug(name = nil, *args) # project-config takes precedence over a named project in the case that # both are provided. if options["project-config"] args.unshift name if name name = nil end params = { args: args, attach: options[:attach], custom_name: options[:name], name: name, project_config: options["project-config"] } project = create_project(params) say project.render end desc "copy [EXISTING] [NEW]", COMMANDS[:copy] map "c" => :copy map "cp" => :copy def copy(existing, new) existing_config_path = Tmuxinator::Config.project(existing) new_config_path = Tmuxinator::Config.project(new) exit!("Project #{existing} doesn't exist!") \ unless Tmuxinator::Config.exists?(name: existing) new_exists = Tmuxinator::Config.exists?(name: new) question = "#{new} already exists, would you like to overwrite it?" if !new_exists || yes?(question, :red) say "Overwriting #{new}" if Tmuxinator::Config.exists?(name: new) FileUtils.copy_file(existing_config_path, new_config_path) end Kernel.system("$EDITOR #{new_config_path}") end desc "delete [PROJECT1] [PROJECT2] ...", COMMANDS[:delete] map "d" => :delete map "rm" => :delete def delete(*projects) projects.each do |project| if Tmuxinator::Config.exists?(name: project) config = Tmuxinator::Config.project(project) if yes?("Are you sure you want to delete #{project}?(y/n)", :red) FileUtils.rm(config) say "Deleted #{project}" end else say "#{project} does not exist!" end end end desc "implode", COMMANDS[:implode] map "i" => :implode def implode if yes?("Are you sure you want to delete all tmuxinator configs?", :red) Tmuxinator::Config.directories.each do |directory| FileUtils.remove_dir(directory) end say "Deleted all tmuxinator projects." end end desc "list", COMMANDS[:list] map "l" => :list map "ls" => :list method_option :newline, type: :boolean, aliases: ["-n"], desc: "Force output to be one entry per line." def list say "tmuxinator projects:" if options[:newline] say Tmuxinator::Config.configs.join("\n") else print_in_columns Tmuxinator::Config.configs end end desc "version", COMMANDS[:version] map "-v" => :version def version say "tmuxinator #{Tmuxinator::VERSION}" end desc "doctor", COMMANDS[:doctor] def doctor say "Checking if tmux is installed ==> " yes_no Tmuxinator::Doctor.installed? say "Checking if $EDITOR is set ==> " yes_no Tmuxinator::Doctor.editor? say "Checking if $SHELL is set ==> " yes_no Tmuxinator::Doctor.shell? end # This method was defined as something of a workaround... Previously # the conditional contained within was in the executable (i.e. # bin/tmuxinator). It has been moved here so as to be testable. A couple # of notes: # - ::start (defined in Thor::Base) expects the first argument to be an # array or ARGV, not a varargs. Perhaps ::bootstrap should as well? # - ::start has a different purpose from #start and hence a different # signature def self.bootstrap(args = []) name = args[0] || nil if args.empty? && Tmuxinator::Config.local? Tmuxinator::Cli.new.local elsif name && !Tmuxinator::Cli::RESERVED_COMMANDS.include?(name) && Tmuxinator::Config.exists?(name: name) Tmuxinator::Cli.new.start(name, *args.drop(1)) else Tmuxinator::Cli.start(args) end end end end tmuxinator-1.1.4/lib/tmuxinator/config.rb000066400000000000000000000126641360172565300205220ustar00rootroot00000000000000module Tmuxinator class Config LOCAL_DEFAULT = "./.tmuxinator.yml".freeze NO_LOCAL_FILE_MSG = "Project file at ./.tmuxinator.yml doesn't exist.".freeze NO_PROJECT_FOUND_MSG = "Project could not be found.".freeze TMUX_MASTER_VERSION = Float::INFINITY class << self # The directory (created if needed) in which to store new projects def directory return environment if environment? return xdg if xdg? return home if home? # No project directory specified or existant, default to XDG: FileUtils::mkdir_p(xdg) xdg end def home ENV["HOME"] + "/.tmuxinator" end def home? File.directory?(home) end # ~/.config/tmuxinator unless $XDG_CONFIG_HOME has been configured to use # a custom value. (e.g. if $XDG_CONFIG_HOME is set to ~/my-config, the # return value will be ~/my-config/tmuxinator) def xdg XDG["CONFIG"].to_s + "/tmuxinator" end def xdg? File.directory?(xdg) end # $TMUXINATOR_CONFIG (and create directory) or "". def environment environment = ENV["TMUXINATOR_CONFIG"] return "" if environment.to_s.empty? # variable is unset (nil) or blank FileUtils::mkdir_p(environment) unless File.directory?(environment) environment end def environment? File.directory?(environment) end def sample asset_path "sample.yml" end def default "#{directory}/default.yml" end def default? exists?(name: "default") end def version if Tmuxinator::Doctor.installed? tmux_version = `tmux -V`.split(" ")[1] if tmux_version == "master" TMUX_MASTER_VERSION else tmux_version.to_s[/\d+(?:\.\d+)?/, 0].to_f end end end def default_path_option version && version < 1.8 ? "default-path" : "-c" end def exists?(name: nil, path: nil) return File.exist?(path) if path return File.exist?(project(name)) if name false end def local? local_project end # Pathname of given project searching only global directories def global_project(name) project_in(environment, name) || project_in(xdg, name) || project_in(home, name) end def local_project [LOCAL_DEFAULT].detect { |f| File.exist?(f) } end def default_project(name) "#{directory}/#{name}.yml" end # Pathname of the given project def project(name) global_project(name) || local_project || default_project(name) end def template asset_path "template.erb" end def stop_template asset_path "template-stop.erb" end def wemux_template asset_path "wemux_template.erb" end # Sorted list of all .yml files, including duplicates def configs directories.map do |directory| Dir["#{directory}/**/*.yml"].map do |path| path.gsub("#{directory}/", "").gsub(".yml", "") end end.flatten.sort end # Existant directories which may contain project files # Listed in search order # Used by `implode` and `list` commands def directories if environment? [environment] else [xdg, home].select { |d| File.directory? d } end end def valid_project_config?(project_config) return false unless project_config unless exists?(path: project_config) raise "Project config (#{project_config}) doesn't exist." end true end def valid_local_project?(name) return false if name raise NO_LOCAL_FILE_MSG unless local? true end def valid_standard_project?(name) return false unless name raise "Project #{name} doesn't exist." unless exists?(name: name) true end def validate(options = {}) name = options[:name] options[:force_attach] ||= false options[:force_detach] ||= false project_config = options.fetch(:project_config) { false } project_file = if valid_project_config?(project_config) project_config elsif valid_local_project?(name) local_project elsif valid_standard_project?(name) project(name) else # This branch should never be reached, # but just in case ... raise NO_PROJECT_FOUND_MSG end Tmuxinator::Project.load(project_file, options).validate! end # Deprecated methods: ignore the 1st, use the 2nd alias :root :directory alias :project_in_root :global_project alias :project_in_local :local_project private def asset_path(asset) "#{File.dirname(__FILE__)}/assets/#{asset}" end # The first pathname of the project named 'name' found while # recursively searching 'directory' def project_in(directory, name) return nil if String(directory).empty? projects = Dir.glob("#{directory}/**/*.{yml,yaml}").sort projects.detect { |project| File.basename(project, ".*") == name } end end end end tmuxinator-1.1.4/lib/tmuxinator/deprecations.rb000066400000000000000000000004711360172565300217260ustar00rootroot00000000000000module Tmuxinator module Deprecations def rvm? yaml["rvm"] end def rbenv? yaml["rbenv"] end def pre_tab? yaml["pre_tab"] end def cli_args? yaml["cli_args"] end def pre? yaml["pre"] end def post? yaml["post"] end end end tmuxinator-1.1.4/lib/tmuxinator/doctor.rb000066400000000000000000000004641360172565300205420ustar00rootroot00000000000000module Tmuxinator class Doctor class << self def editor? !ENV["EDITOR"].nil? && !ENV["EDITOR"].empty? end def installed? Kernel.system("type tmux > /dev/null") end def shell? !ENV["SHELL"].nil? && !ENV["SHELL"].empty? end end end end tmuxinator-1.1.4/lib/tmuxinator/hooks.rb000066400000000000000000000004101360172565300203620ustar00rootroot00000000000000module Tmuxinator module Hooks module_function def commands_from(project, hook_name) hook_config = project.yaml[hook_name] if hook_config.is_a?(Array) hook_config.join("; ") else hook_config end end end end tmuxinator-1.1.4/lib/tmuxinator/hooks/000077500000000000000000000000001360172565300200425ustar00rootroot00000000000000tmuxinator-1.1.4/lib/tmuxinator/hooks/project.rb000066400000000000000000000032121360172565300220330ustar00rootroot00000000000000module Tmuxinator module Hooks module Project module_function # Commands specified in this hook run when "tmuxinator start project" # command is issued def hook_on_project_start # this method can only be used from inside Tmuxinator::Project Tmuxinator::Hooks.commands_from self, "on_project_start" end # Commands specified in this hook run when "tmuxinator start project" # command is issued and there is no tmux session available named "project" def hook_on_project_first_start # this method can only be used from inside Tmuxinator::Project Tmuxinator::Hooks.commands_from self, "on_project_first_start" end # Commands specified in this hook run when "tmuxinator start project" # command is issued and there is no tmux session available named "project" def hook_on_project_restart # this method can only be used from inside Tmuxinator::Project Tmuxinator::Hooks.commands_from self, "on_project_restart" end # Commands specified in this hook run when you exit from a project ( aka # detach from a tmux session ) def hook_on_project_exit # this method can only be used from inside Tmuxinator::Project Tmuxinator::Hooks.commands_from self, "on_project_exit" end # Command specified in this hook run when "tmuxinator stop project" # command is issued def hook_on_project_stop # this method can only be used from inside Tmuxinator::Project Tmuxinator::Hooks.commands_from self, "on_project_stop" end end # End Project end # End Hooks end # End Tmuxinator tmuxinator-1.1.4/lib/tmuxinator/pane.rb000066400000000000000000000024351360172565300201730ustar00rootroot00000000000000module Tmuxinator class Pane attr_reader :commands, :project, :index, :tab def initialize(index, project, tab, *commands) @commands = commands @index = index @project = project @tab = tab end def tmux_window_and_pane_target "#{project.name}:#{window_index}.#{pane_index}" end def tmux_pre_command _send_target(tab.pre.shellescape) if tab.pre end def tmux_pre_window_command _send_target(project.pre_window.shellescape) if project.pre_window end def tmux_main_command(command) if command _send_target(command.shellescape) else "" end end def name project.name end def window_index tab.index + project.base_index end def pane_index index + tab.project.pane_base_index end def tmux_split_command path = if tab.root? "#{Tmuxinator::Config.default_path_option} #{tab.root}" end "#{project.tmux} splitw #{path} -t #{tab.tmux_window_target}" end def last? index == tab.panes.length - 1 end private def _send_target(e) _send_keys(tmux_window_and_pane_target, e) end def _send_keys(t, e) "#{project.tmux} send-keys -t #{t} #{e} C-m" end end end tmuxinator-1.1.4/lib/tmuxinator/project.rb000066400000000000000000000215731360172565300207220ustar00rootroot00000000000000module Tmuxinator class Project include Tmuxinator::Util include Tmuxinator::Deprecations include Tmuxinator::Hooks::Project RBENVRVM_DEP_MSG = <<-M DEPRECATION: rbenv/rvm-specific options have been replaced by the `pre_tab` option and will not be supported in 0.8.0. M TABS_DEP_MSG = <<-M DEPRECATION: The tabs option has been replaced by the `windows` option and will not be supported in 0.8.0. M CLIARGS_DEP_MSG = <<-M DEPRECATION: The `cli_args` option has been replaced by the `tmux_options` option and will not be supported in 0.8.0. M SYNC_DEP_MSG = <<-M DEPRECATION: The `synchronize` option's current default behaviour is to enable pane synchronization before running commands. In a future release, the default synchronization option will be `after`, i.e. synchronization of panes will occur after the commands described in each of the panes have run. At that time, the current behavior will need to be explicitly enabled, using the `synchronize: before` option. To use this behaviour now, use the 'synchronize: after' option. M PRE_DEP_MSG = <<-M DEPRECATION: The `pre` option has been replaced by project hooks (`on_project_start` and `on_project_restart`) and will be removed in a future release. M POST_DEP_MSG = <<-M DEPRECATION: The `post` option has been replaced by project hooks (`on_project_stop` and `on_project_exit`) and will be removed in a future release. M attr_reader :yaml attr_reader :force_attach attr_reader :force_detach attr_reader :custom_name def self.load(path, options = {}) yaml = begin raw_content = File.read(path) args = options[:args] || [] @settings = parse_settings(args) @args = args content = Erubis::Eruby.new(raw_content).result(binding) YAML.safe_load(content, [], [], true) rescue SyntaxError, StandardError => error raise "Failed to parse config file: #{error.message}" end new(yaml, options) end def self.parse_settings(args) settings = args.select { |x| x.match(/.*=.*/) } args.reject! { |x| x.match(/.*=.*/) } settings.map! do |setting| parts = setting.split("=") [parts[0], parts[1]] end Hash[settings] end def validate! raise "Your project file should include some windows." \ unless windows? raise "Your project file didn't specify a 'project_name'" \ unless name? self end def initialize(yaml, options = {}) options[:force_attach] = false if options[:force_attach].nil? options[:force_detach] = false if options[:force_detach].nil? @yaml = yaml @custom_name = options[:custom_name] @force_attach = options[:force_attach] @force_detach = options[:force_detach] raise "Cannot force_attach and force_detach at the same time" \ if @force_attach && @force_detach extend Tmuxinator::WemuxSupport if wemux? end def render self.class.render_template(Tmuxinator::Config.template, binding) end def kill self.class.render_template(Tmuxinator::Config.stop_template, binding) end def self.render_template(template, bndg) content = File.read(template) Erubis::Eruby.new(content).result(bndg) end def windows windows_yml = yaml["tabs"] || yaml["windows"] @windows ||= (windows_yml || {}).map.with_index do |window_yml, index| Tmuxinator::Window.new(window_yml, index, self) end end def root root = yaml["project_root"] || yaml["root"] blank?(root) ? nil : File.expand_path(root).shellescape end def name name = custom_name || yaml["project_name"] || yaml["name"] blank?(name) ? nil : name.to_s.shellescape end def pre pre_config = yaml["pre"] parsed_parameters(pre_config) end def attach? yaml_attach = if yaml["attach"].nil? true else yaml["attach"] end attach = force_attach || !force_detach && yaml_attach attach end def pre_window params = if rbenv? "rbenv shell #{yaml['rbenv']}" elsif rvm? "rvm use #{yaml['rvm']}" elsif pre_tab? yaml["pre_tab"] else yaml["pre_window"] end parsed_parameters(params) end def post post_config = yaml["post"] parsed_parameters(post_config) end def tmux "#{tmux_command}#{tmux_options}#{socket}" end def tmux_command yaml["tmux_command"] || "tmux" end def tmux_has_session?(name) # Redirect stderr to /dev/null in order to prevent "failed to connect # to server: Connection refused" error message and non-zero exit status # if no tmux sessions exist. # Please see issues #402 and #414. sessions = `#{tmux} ls 2> /dev/null` # Remove any escape sequences added by `shellescape` in Project#name. # Escapes can result in: "ArgumentError: invalid multibyte character" # when attempting to match `name` against `sessions`. # Please see issue #564. unescaped_name = name.shellsplit.join("") !(sessions !~ /^#{unescaped_name}:/) end def socket if socket_path " -S #{socket_path}" elsif socket_name " -L #{socket_name}" end end def socket_name yaml["socket_name"] end def socket_path yaml["socket_path"] end def tmux_options if cli_args? " #{yaml['cli_args'].to_s.strip}" elsif tmux_options? " #{yaml['tmux_options'].to_s.strip}" else "" end end def base_index get_base_index.to_i end def pane_base_index get_pane_base_index.to_i end def startup_window "#{name}:#{yaml['startup_window'] || base_index}" end def startup_pane "#{startup_window}.#{yaml['startup_pane'] || pane_base_index}" end def tmux_options? yaml["tmux_options"] end def windows? windows.any? end def root? !root.nil? end def name? !name.nil? end def window(i) "#{name}:#{i}" end def send_pane_command(cmd, window_index, _pane_index) send_keys(cmd, window_index) end def send_keys(cmd, window_index) if cmd.empty? "" else "#{tmux} send-keys -t #{window(window_index)} #{cmd.shellescape} C-m" end end def deprecations deprecation_checks.zip(deprecation_messages). inject([]) do |deps, (chk, msg)| deps << msg if chk deps end end def deprecation_checks [ rvm_or_rbenv?, tabs?, cli_args?, legacy_synchronize?, pre?, post? ] end def deprecation_messages [ RBENVRVM_DEP_MSG, TABS_DEP_MSG, CLIARGS_DEP_MSG, SYNC_DEP_MSG, PRE_DEP_MSG, POST_DEP_MSG ] end def rbenv? yaml["rbenv"] end def rvm? yaml["rvm"] end def rvm_or_rbenv? rvm? || rbenv? end def tabs? yaml["tabs"] end def cli_args? yaml["cli_args"] end def pre? yaml["pre"] end def post? yaml["post"] end def get_pane_base_index tmux_config["pane-base-index"] end def get_base_index tmux_config["base-index"] end def show_tmux_options "#{tmux} start-server\\; " \ "show-option -g base-index\\; " \ "show-window-option -g pane-base-index\\;" end def tmux_new_session_command window = windows.first.tmux_window_name_option "#{tmux} new-session -d -s #{name} #{window}" end def tmux_kill_session_command "#{tmux} kill-session -t #{name}" end private def blank?(object) (object.respond_to?(:empty?) && object.empty?) || !object end def tmux_config @tmux_config ||= extract_tmux_config end def extract_tmux_config options_hash = {} options_string = `#{show_tmux_options}` options_string.encode!("UTF-8", invalid: :replace) options_string.split("\n").map do |entry| key, value = entry.split("\s") options_hash[key] = value options_hash end options_hash end def legacy_synchronize? (synchronize_options & [true, "before"]).any? end def synchronize_options window_options.map do |option| option["synchronize"] if option.is_a?(Hash) end end def window_options yaml["windows"].map(&:values).flatten end def parsed_parameters(parameters) parameters.is_a?(Array) ? parameters.join("; ") : parameters end def wemux? yaml["tmux_command"] == "wemux" end end end tmuxinator-1.1.4/lib/tmuxinator/tmux_version.rb000066400000000000000000000011751360172565300220120ustar00rootroot00000000000000module Tmuxinator module TmuxVersion SUPPORTED_TMUX_VERSIONS = [ "3.0a", 3.0, "2.9a", 2.9, 2.8, 2.7, 2.6, 2.5, 2.4, 2.3, 2.2, 2.1, 2.0, 1.9, 1.8, 1.7, 1.6, 1.5, ].freeze UNSUPPORTED_VERSION_MSG = <<-MSG.freeze WARNING: You are running tmuxinator with an unsupported version of tmux. Please consider using a supported version: (#{SUPPORTED_TMUX_VERSIONS.join(', ')}) MSG def self.supported?(version = Tmuxinator::Config.version) SUPPORTED_TMUX_VERSIONS.include?(version) end end end tmuxinator-1.1.4/lib/tmuxinator/util.rb000066400000000000000000000003361360172565300202230ustar00rootroot00000000000000module Tmuxinator module Util include Thor::Actions def exit!(msg) puts msg Kernel.exit(1) end def yes_no(condition) condition ? say("Yes", :green) : say("No", :red) end end end tmuxinator-1.1.4/lib/tmuxinator/version.rb000066400000000000000000000000611360172565300207260ustar00rootroot00000000000000module Tmuxinator VERSION = "1.1.4".freeze end tmuxinator-1.1.4/lib/tmuxinator/wemux_support.rb000066400000000000000000000004011360172565300222000ustar00rootroot00000000000000module Tmuxinator module WemuxSupport def render Tmuxinator::Project.render_template( Tmuxinator::Config.wemux_template, binding ) end %i(name tmux).each do |m| define_method(m) { "wemux" } end end end tmuxinator-1.1.4/lib/tmuxinator/window.rb000066400000000000000000000064701360172565300205620ustar00rootroot00000000000000module Tmuxinator class Window include Tmuxinator::Util attr_reader :commands, :index, :name, :project def initialize(window_yaml, index, project) first_key = window_yaml.keys.first @name = first_key.to_s.shellescape unless first_key.nil? @yaml = window_yaml.values.first @project = project @index = index @commands = build_commands(tmux_window_command_prefix, @yaml) end def panes build_panes(yaml["panes"]) || [] end def _hashed? @yaml.is_a?(Hash) end def yaml _hashed? ? @yaml : {} end def layout yaml["layout"] ? yaml["layout"].shellescape : nil end def synchronize yaml["synchronize"] || false end def root _yaml_root || _project_root end def _yaml_root File.expand_path(yaml["root"]).shellescape if yaml["root"] end def _project_root project.root if project.root? end def build_panes(panes_yml) return if panes_yml.nil? Array(panes_yml).map.with_index do |pane_yml, index| commands = case pane_yml when Hash pane_yml.values.first when Array pane_yml else pane_yml end Tmuxinator::Pane.new(index, project, self, *commands) end.flatten end def build_commands(_prefix, command_yml) if command_yml.is_a?(Array) command_yml.map do |command| "#{tmux_window_command_prefix} #{command.shellescape} C-m" if command end.compact elsif command_yml.is_a?(String) && !command_yml.empty? ["#{tmux_window_command_prefix} #{command_yml.shellescape} C-m"] else [] end end def pre _pre = yaml["pre"] if _pre.is_a?(Array) _pre.join(" && ") elsif _pre.is_a?(String) _pre end end def root? !root.nil? end def panes? panes.any? end def tmux_window_target "#{project.name}:#{index + project.base_index}" end def tmux_pre_window_command return unless project.pre_window "#{project.tmux} send-keys -t #{tmux_window_target} #{project.pre_window.shellescape} C-m" end def tmux_window_command_prefix "#{project.tmux} send-keys -t #{project.name}:#{index + project.base_index}" end def tmux_window_name_option name ? "-n #{name}" : "" end def tmux_new_window_command path = root? ? "#{Tmuxinator::Config.default_path_option} #{root}" : nil "#{project.tmux} new-window #{path} -t #{tmux_window_target} #{tmux_window_name_option}" end def tmux_tiled_layout_command "#{project.tmux} select-layout -t #{tmux_window_target} tiled" end def tmux_synchronize_panes "#{project.tmux} set-window-option -t #{tmux_window_target} synchronize-panes on" end def tmux_layout_command "#{project.tmux} select-layout -t #{tmux_window_target} #{layout}" end def tmux_select_first_pane "#{project.tmux} select-pane -t #{tmux_window_target}.#{panes.first.index + project.pane_base_index}" end def synchronize_before? synchronize == true || synchronize == "before" end def synchronize_after? synchronize == "after" end end end tmuxinator-1.1.4/spec/000077500000000000000000000000001360172565300146715ustar00rootroot00000000000000tmuxinator-1.1.4/spec/factories/000077500000000000000000000000001360172565300166505ustar00rootroot00000000000000tmuxinator-1.1.4/spec/factories/projects.rb000066400000000000000000000060331360172565300210300ustar00rootroot00000000000000def yaml_load(file) YAML.safe_load(File.read(File.expand_path(file))) end FactoryBot.define do factory :project, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/sample.yml") } end initialize_with { Tmuxinator::Project.new(file) } end factory :project_with_force_attach, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/detach.yml") } end initialize_with { Tmuxinator::Project.new(file, force_attach: true) } end factory :project_with_force_detach, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/detach.yml") } end initialize_with { Tmuxinator::Project.new(file, force_detach: true) } end factory :project_with_custom_name, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/sample.yml") } end initialize_with { Tmuxinator::Project.new(file, custom_name: "custom") } end factory :project_with_number_as_name, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/sample_number_as_name.yml") } end initialize_with { Tmuxinator::Project.new(file) } end factory :project_with_emoji_as_name, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/sample_emoji_as_name.yml") } end initialize_with { Tmuxinator::Project.new(file) } end factory :project_with_literals_as_window_name, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/sample_literals_as_window_name.yml") } end initialize_with { Tmuxinator::Project.new(file) } end factory :project_with_deprecations, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/sample.deprecations.yml") } end initialize_with { Tmuxinator::Project.new(file) } end factory :wemux_project, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/sample_wemux.yml") } end initialize_with { Tmuxinator::Project.new(file) } end factory :noname_project, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/noname.yml") } end initialize_with { Tmuxinator::Project.new(file) } end factory :noroot_project, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/noroot.yml") } end initialize_with { Tmuxinator::Project.new(file) } end factory :nowindows_project, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/nowindows.yml") } end initialize_with { Tmuxinator::Project.new(file) } end factory :nameless_window_project, class: Tmuxinator::Project do transient do file { yaml_load("spec/fixtures/nameless_window.yml") } end initialize_with { Tmuxinator::Project.new(file) } end factory :project_with_alias, class: Tmuxinator::Project do transient do file { "spec/fixtures/sample_alias.yml" } end initialize_with { Tmuxinator::Project.load(file) } end end tmuxinator-1.1.4/spec/fixtures/000077500000000000000000000000001360172565300165425ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/TMUXINATOR_CONFIG/000077500000000000000000000000001360172565300213015ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/TMUXINATOR_CONFIG/TMUXINATOR_CONFIG.yml000066400000000000000000000000001360172565300245510ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/detach.yml000066400000000000000000000020721360172565300205160ustar00rootroot00000000000000# ~/.tmuxinator/sample.yml # you can make as many tabs as you wish... name: sample root: ~/test socket_name: foo # Remove to use default socket pre: sudo /etc/rc.d/mysqld start # Runs before everything pre_window: rbenv shell 2.0.0-p247 # Runs in each tab and pane tmux_options: -f ~/.tmux.mac.conf # Pass arguments to tmux attach: false windows: - editor: pre: - echo "I get run in each pane, before each pane command!" - layout: main-vertical panes: - vim - #empty, will just run plain bash - top - pane_with_multiple_commands: - ssh server - echo "Hello" - shell: - git pull - git merge - guard: layout: tiled pre: - echo "I get run in each pane." - echo "Before each pane command!" panes: - - #empty, will just run plain bash - - database: bundle exec rails db - server: bundle exec rails s - logs: tail -f log/development.log - console: bundle exec rails c - capistrano: - server: ssh user@example.com tmuxinator-1.1.4/spec/fixtures/dot-tmuxinator/000077500000000000000000000000001360172565300215405ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/dot-tmuxinator/both.yml000066400000000000000000000000001360172565300232050ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/dot-tmuxinator/dup/000077500000000000000000000000001360172565300223305ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/dot-tmuxinator/dup/local-dup.yml000066400000000000000000000000001360172565300247210ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/dot-tmuxinator/home.yml000066400000000000000000000000001360172565300232010ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/dot-tmuxinator/local-dup.yml000066400000000000000000000000001360172565300241310ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/nameless_window.yml000066400000000000000000000001751360172565300224660ustar00rootroot00000000000000name: nameless_window root: ~/ windows: - ~: echo "this is a window with no name" - other: echo "this window has a name" tmuxinator-1.1.4/spec/fixtures/noname.yml000066400000000000000000000001601360172565300205370ustar00rootroot00000000000000# ~/.tmuxinator/noname.yml # you can make as many tabs as you wish... root: ~/test windows: - test: echo foo tmuxinator-1.1.4/spec/fixtures/noroot.yml000066400000000000000000000001431360172565300206030ustar00rootroot00000000000000# ~/.tmuxinator/noroot.yml # you can make as many tabs as you wish... windows: - test: echo foo tmuxinator-1.1.4/spec/fixtures/nowindows.yml000066400000000000000000000000331360172565300213100ustar00rootroot00000000000000name: no_windows root: ~/ tmuxinator-1.1.4/spec/fixtures/sample.deprecations.yml000066400000000000000000000016461360172565300232340ustar00rootroot00000000000000# ~/.tmuxinator/sample.deprecations.yml project_name: sample project_root: ~/test socket_name: foo # Remove to use default socket pre: sudo /etc/rc.d/mysqld start # Runs before everything rbenv: 2.0.0-p247 cli_args: -f ~/.tmux.mac.conf # Pass arguments to tmux tabs: - editor: pre: - echo "I get run in each pane, before each pane command!" - layout: main-vertical panes: - vim - #empty, will just run plain bash - top - shell: git pull - guard: layout: tiled pre: - echo "I get run in each pane." - echo "Before each pane command!" panes: - - #empty, will just run plain bash - - database: bundle exec rails db - server: bundle exec rails s - logs: tail -f log/development.log - console: bundle exec rails c - capistrano: - server: ssh user@example.com windows: - sample: - synchronize: true tmuxinator-1.1.4/spec/fixtures/sample.yml000066400000000000000000000020071360172565300205450ustar00rootroot00000000000000# ~/.tmuxinator/sample.yml # you can make as many tabs as you wish... name: sample root: ~/test socket_name: foo # Remove to use default socket pre_window: rbenv shell 2.0.0-p247 # Runs in each tab and pane tmux_options: -f ~/.tmux.mac.conf # Pass arguments to tmux tmux_detached: false windows: - editor: pre: - echo "I get run in each pane, before each pane command!" - layout: main-vertical panes: - vim - #empty, will just run plain bash - top - pane_with_multiple_commands: - ssh server - echo "Hello" - shell: - git pull - git merge - guard: layout: tiled pre: - echo "I get run in each pane." - echo "Before each pane command!" panes: - - #empty, will just run plain bash - - database: bundle exec rails db - server: bundle exec rails s - logs: tail -f log/development.log - console: bundle exec rails c - capistrano: - server: ssh user@example.com tmuxinator-1.1.4/spec/fixtures/sample_alias.yml000066400000000000000000000006401360172565300217170ustar00rootroot00000000000000# ~/.tmuxinator/sample.yml # you can make as many tabs as you wish... defaults: &defaults pre: - echo "alias_is_working" name: sample_alias root: ~/test windows: - editor: <<: *defaults layout: main-vertical panes: - vim - #empty, will just run plain bash - top - pane_with_multiple_commands: - ssh server - echo "Hello" - guard: tmuxinator-1.1.4/spec/fixtures/sample_emoji_as_name.yml000066400000000000000000000001231360172565300234100ustar00rootroot00000000000000# ~/.tmuxinator/sample_emoji_as_name.yml name: 🍩 windows: - emoji: echo 🍩 tmuxinator-1.1.4/spec/fixtures/sample_literals_as_window_name.yml000066400000000000000000000011231360172565300255140ustar00rootroot00000000000000# ~/.tmuxinator/sample_literals_as_window_name.yml name: sample windows: - 222: echo Fixnum as window name fixture - 222_333: echo Fixnum as window name fixture - 111222333444555666777: echo BigNum as window name fixture - 222.3: echo Float as window name fixture - 4e5: echo Float as window name fixture - 4E5: echo Float as window name fixture - true: echo TrueClass as window name fixture - false: echo FalseClass as window name fixture - nil: echo NilClass as window name fixture - //: echo RegExp as window name fixture - /sample/: echo RegExp as window name fixture tmuxinator-1.1.4/spec/fixtures/sample_number_as_name.yml000066400000000000000000000001461360172565300236020ustar00rootroot00000000000000# ~/.tmuxinator/sample_number_as_name.yml name: 222 windows: - number: echo number as name fixture tmuxinator-1.1.4/spec/fixtures/sample_wemux.yml000066400000000000000000000016731360172565300220020ustar00rootroot00000000000000# ~/.tmuxinator/sample_wemux.yml # you can make as many tabs as you wish... name: sample root: ~/test pre: sudo /etc/rc.d/mysqld start # Runs before everything pre_window: rbenv shell 2.0.0-p247 # Runs in each tab and pane tmux_options: -f ~/.tmux.mac.conf # Pass arguments to tmux tmux_command: wemux windows: - editor: pre: - echo "I get run in each pane, before each pane command!" - layout: main-vertical panes: - vim - #empty, will just run plain bash - top - shell: - git pull - git merge - guard: layout: tiled pre: - echo "I get run in each pane." - echo "Before each pane command!" panes: - - #empty, will just run plain bash - - database: bundle exec rails db - server: bundle exec rails s - logs: tail -f log/development.log - console: bundle exec rails c - capistrano: - server: ssh user@example.com tmuxinator-1.1.4/spec/fixtures/sample_with_project_config.yml000066400000000000000000000002051360172565300246510ustar00rootroot00000000000000# ~/.tmuxinator/sample-with-project-config.yml name: sample_with_project_config windows: - editor: echo sample-with-project-config tmuxinator-1.1.4/spec/fixtures/xdg-tmuxinator/000077500000000000000000000000001360172565300215345ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/xdg-tmuxinator/both.yml000066400000000000000000000000001360172565300232010ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/xdg-tmuxinator/xdg.yml000066400000000000000000000000001360172565300230270ustar00rootroot00000000000000tmuxinator-1.1.4/spec/fixtures/yaml.yaml000066400000000000000000000020071360172565300203670ustar00rootroot00000000000000# ~/.tmuxinator/sample.yml # you can make as many tabs as you wish... name: sample root: ~/test socket_name: foo # Remove to use default socket pre_window: rbenv shell 2.0.0-p247 # Runs in each tab and pane tmux_options: -f ~/.tmux.mac.conf # Pass arguments to tmux tmux_detached: false windows: - editor: pre: - echo "I get run in each pane, before each pane command!" - layout: main-vertical panes: - vim - #empty, will just run plain bash - top - pane_with_multiple_commands: - ssh server - echo "Hello" - shell: - git pull - git merge - guard: layout: tiled pre: - echo "I get run in each pane." - echo "Before each pane command!" panes: - - #empty, will just run plain bash - - database: bundle exec rails db - server: bundle exec rails s - logs: tail -f log/development.log - console: bundle exec rails c - capistrano: - server: ssh user@example.com tmuxinator-1.1.4/spec/lib/000077500000000000000000000000001360172565300154375ustar00rootroot00000000000000tmuxinator-1.1.4/spec/lib/tmuxinator/000077500000000000000000000000001360172565300176515ustar00rootroot00000000000000tmuxinator-1.1.4/spec/lib/tmuxinator/cli_spec.rb000066400000000000000000000673031360172565300217700ustar00rootroot00000000000000require "spec_helper" describe Tmuxinator::Cli do shared_context :local_project_setup do let(:local_project_config) { ".tmuxinator.yml" } let(:content_fixture) { "../../fixtures/sample.yml" } let(:content_relpath) { File.join(File.dirname(__FILE__), content_fixture) } let(:content_path) { File.expand_path(content_relpath) } let(:content) { File.read(content_path) } let(:working_dir) { FileUtils.pwd } let(:local_project_relpath) { File.join(working_dir, local_project_config) } let(:local_project_path) { File.expand_path(local_project_relpath) } before do File.new(local_project_path, "w").tap do |f| f.write content end.close expect(File.exists?(local_project_path)).to be_truthy expect(File.read(local_project_path)).to eq content end after do File.delete(local_project_path) end end subject(:cli) { described_class } let(:fixtures_dir) { File.expand_path("../../../fixtures/", __FILE__) } let(:project) { FactoryBot.build(:project) } let(:project_config) do File.join(fixtures_dir, "sample_with_project_config.yml") end before do ARGV.clear allow(Kernel).to receive(:system) allow(FileUtils).to receive(:copy_file) allow(FileUtils).to receive(:rm) allow(FileUtils).to receive(:remove_dir) end context "no arguments" do it "runs without error" do _, err = capture_io { cli.start } expect(err).to be_empty end end context "base thor functionality" do shared_examples_for :base_thor_functionality do it "supports -v" do out, err = capture_io { cli.bootstrap(["-v"]) } expect(err).to eq "" expect(out).to include(Tmuxinator::VERSION) end it "supports help" do out, err = capture_io { cli.bootstrap(["help"]) } expect(err).to eq "" expect(out).to include("tmuxinator commands:") end end it_should_behave_like :base_thor_functionality context "with a local project config" do include_context :local_project_setup it_should_behave_like :base_thor_functionality end end describe "::bootstrap" do subject { cli.bootstrap(args) } let(:args) { [] } shared_examples_for :bootstrap_with_arguments do let(:args) { [arg1] } context "and the first arg is a tmuxinator command" do let(:arg1) { "list" } it "should call ::start" do expect(cli).to receive(:start).with(args) subject end end context "and the first arg is" do let(:arg1) { "sample" } context "a tmuxinator project name" do before do expect(Tmuxinator::Config).to \ receive(:exists?).with(name: arg1) { true } end it "should call #start" do instance = instance_double(cli) expect(cli).to receive(:new).and_return(instance) expect(instance).to receive(:start).with(*args) subject end end context "a thor command" do context "(-v)" do let(:arg1) { "-v" } it "should call ::start" do expect(cli).to receive(:start).with(args) subject end end context "(help)" do let(:arg1) { "help" } it "should call ::start" do expect(cli).to receive(:start).with(args) subject end end end context "something else" do before do expect(Tmuxinator::Config).to \ receive(:exists?).with(name: arg1) { false } end it "should call ::start" do expect(cli).to receive(:start).with(args) subject end end end end context "and there is a local project config" do include_context :local_project_setup context "when no args are supplied" do it "should call #local" do instance = instance_double(cli) expect(cli).to receive(:new).and_return(instance) expect(instance).to receive(:local) subject end end context "when one or more args are supplied" do it_should_behave_like :bootstrap_with_arguments end end context "and there is no local project config" do context "when no args are supplied" do it "should call ::start" do expect(cli).to receive(:start).with([]) subject end end context "when one or more args are supplied" do it_should_behave_like :bootstrap_with_arguments end end end describe "#completions" do before do ARGV.replace(["completions", "start"]) allow(Tmuxinator::Config).to receive_messages(configs: ["test.yml", "foo.yml"]) end it "gets completions" do out, _err = capture_io { cli.start } expect(out).to include("test.yml\nfoo.yml") end end describe "#commands" do before do ARGV.replace(["commands"]) end it "lists the commands" do out, _err = capture_io { cli.start } expected = %w(commands completions new edit open start stop local debug copy delete implode version doctor list) expect(out).to eq "#{expected.join("\n")}\n" end end shared_examples_for :unsupported_version_message do |*args| before do ARGV.replace([*args]) end context "unsupported version" do before do allow($stdin).to receive_messages(getc: "y") allow(Tmuxinator::TmuxVersion).to receive(:supported?).and_return(false) end it "prints the warning" do out, _err = capture_io { cli.start } expect(out).to include "WARNING" end context "with --suppress-tmux-version-warning flag" do before do ARGV.replace([*args, "--suppress-tmux-version-warning"]) end it "does not print the warning" do out, _err = capture_io { cli.start } expect(out).not_to include "WARNING" end end end context "supported version" do before do allow($stdin).to receive_messages(getc: "y") allow(Tmuxinator::TmuxVersion).to receive(:supported?).and_return(true) end it "does not print the warning" do out, _err = capture_io { cli.start } expect(out).not_to include "WARNING" end end end describe "#start" do before do ARGV.replace(["start", "foo"]) allow(Tmuxinator::Config).to receive_messages(validate: project) allow(Tmuxinator::Config).to receive_messages(version: 1.9) allow(Kernel).to receive(:exec) end context "no deprecations" do it "starts the project" do expect(Kernel).to receive(:exec) capture_io { cli.start } end it "accepts a flag for alternate name" do ARGV.replace(["start", "foo", "--name=bar"]) expect(Kernel).to receive(:exec) capture_io { cli.start } end it "accepts a project config file flag" do ARGV.replace(["start", "foo", "--project-config=sample.yml"]) expect(Kernel).to receive(:exec) capture_io { cli.start } end it "accepts additional arguments" do ARGV.replace(["start", "foo", "bar", "three=four"]) expect(Kernel).to receive(:exec) capture_io { cli.start } end end context "deprecations" do before do allow($stdin).to receive_messages(getc: "y") end let(:project) { FactoryBot.build(:project_with_deprecations) } it "prints the deprecations" do out, _err = capture_io { cli.start } expect(out).to include "DEPRECATION" end end include_examples :unsupported_version_message, :start, :foo end describe "#stop" do before do ARGV.replace(["stop", "foo"]) allow(Tmuxinator::Config).to receive_messages(validate: project) allow(Tmuxinator::Config).to receive_messages(version: 1.9) allow(Kernel).to receive(:exec) end context "with project name" do it "stop the project" do expect(Kernel).to receive(:exec) out, err = capture_io { cli.start } expect(err).to eq "" expect(out).to eq "" end end include_examples :unsupported_version_message, :stop, :foo end describe "#local" do before do allow(Tmuxinator::Config).to receive_messages(validate: project) allow(Tmuxinator::Config).to receive_messages(version: 1.9) allow(Kernel).to receive(:exec) end shared_examples_for :local_project do it "starts the project" do expect(Kernel).to receive(:exec) out, err = capture_io { cli.start } expect(err).to eq "" expect(out).to eq "" end end context "when the command used is 'local'" do before do ARGV.replace ["local"] end it_should_behave_like :local_project end context "when the command used is '.'" do before do ARGV.replace ["."] end it_should_behave_like :local_project end include_examples :unsupported_version_message, :local end describe "#start(custom_name)" do before do ARGV.replace(["start", "foo", "bar"]) allow(Tmuxinator::Config).to receive_messages(validate: project) allow(Tmuxinator::Config).to receive_messages(version: 1.9) allow(Kernel).to receive(:exec) end context "no deprecations" do it "starts the project" do expect(Kernel).to receive(:exec) capture_io { cli.start } end end end describe "#start(with project config file)" do before do allow(Tmuxinator::Config).to receive(:validate).and_call_original allow(Tmuxinator::Config).to receive_messages(version: 1.9) allow(Kernel).to receive(:exec) end context "no deprecations" do it "starts the project if given a valid project config file" do ARGV.replace(["start", "--project-config=#{project_config}"]) expect(Kernel).to receive(:exec) capture_io { cli.start } end it "does not start the project if given a bogus project config file" do ARGV.replace(["start", "--project-config=bogus.yml"]) expect(Kernel).not_to receive(:exec) expect { capture_io { cli.start } }.to raise_error(SystemExit) end it "passes additional arguments through" do ARGV.replace(["start", "--project-config=#{project_config}", "extra"]) expect(Tmuxinator::Config). to(receive(:validate). with(hash_including(args: array_including("extra")))) capture_io { cli.start } end it "does not set the project name" do ARGV.replace(["start", "--project-config=#{project_config}"]) expect(Tmuxinator::Config). to(receive(:validate). with(hash_including(name: nil))) capture_io { cli.start } end end end describe "#edit" do let(:file) { StringIO.new } let(:name) { "test" } let(:path) { Tmuxinator::Config.default_project(name) } context "when the project file _does_ already exist" do let(:extra) { " - extra: echo 'foobar'" } before do # make sure that no project file exists initially FileUtils.remove_file(path) if File.exist?(path) expect(File).not_to exist(path) # now generate a project file expect(described_class.new.generate_project_file(name, path)).to eq path expect(File).to exist path # add some content to the project file File.open(path, "w") do |f| f.write(extra) f.flush end expect(File.read(path)).to match %r{#{extra}} # get ready to run `tmuxinator edit #{name}` ARGV.replace ["edit", name] end it "should _not_ generate a new project file" do capture_io { cli.start } expect(File.read(path)).to match %r{#{extra}} end end end describe "#new" do let(:file) { StringIO.new } let(:name) { "test" } before do allow(File).to receive(:open) { |&block| block.yield file } end context "without the --local option" do before do ARGV.replace(["new", name]) end context "existing project doesn't exist" do before do expect(File).to receive_messages(exist?: false) end it "creates a new tmuxinator project file" do capture_io { cli.start } expect(file.string).to_not be_empty end end context "file exists" do let(:project_path) { Tmuxinator::Config.project(name).to_s } before do allow(File).to receive(:exist?).with(anything).and_return(false) expect(File).to receive(:exist?).with(project_path).and_return(true) end it "just opens the file" do expect(Kernel).to receive(:system).with(%r{#{project_path}}) capture_io { cli.start } end end end context "with the --local option" do before do ARGV.replace ["new", name, "--local"] end context "existing project doesn't exist" do before do allow(File).to receive(:exist?).at_least(:once) do false end end it "creates a new tmuxinator project file" do capture_io { cli.start } expect(file.string).to_not be_empty end end context "file exists" do let(:path) { Tmuxinator::Config::LOCAL_DEFAULT } before do expect(File).to receive(:exist?).with(path) { true } end it "just opens the file" do expect(Kernel).to receive(:system).with(%r{#{path}}) capture_io { cli.start } end end end # this command variant only works for tmux version 1.6 and up. context "from a session" do context "with tmux >= 1.6", if: Tmuxinator::Config.version >= 1.6 do before do # Necessary to make `Doctor.installed?` work in specs allow(Tmuxinator::Doctor).to receive(:installed?).and_return(true) end context "session exists" do before(:all) do # Can't add variables through `let` in `before :all`. @session = "for-testing-tmuxinator" # Pass the -d option, so that the session is not attached. Kernel.system "tmux new-session -d -s #{@session}" end before do ARGV.replace ["new", name, @session] end after(:all) do puts @session Kernel.system "tmux kill-session -t #{@session}" end it "creates a project file" do capture_io { cli.start } expect(file.string).to_not be_empty # make sure the output is valid YAML expect { YAML.parse file.string }.to_not raise_error end end context "session doesn't exist" do before do ARGV.replace ["new", name, "sessiondoesnotexist"] end it "fails" do expect { cli.start }.to raise_error RuntimeError end end end context "with tmux < 1.6" do before do ARGV.replace ["new", name, "sessionname"] allow(Tmuxinator::Config).to receive(:version).and_return(1.5) end it "is unsupported" do expect { cli.start }.to raise_error RuntimeError end end end end describe "#copy" do before do ARGV.replace(["copy", "foo", "bar"]) allow(Tmuxinator::Config).to receive(:exists?) { true } end context "new project already exists" do before do allow(Thor::LineEditor).to receive_messages(readline: "y") end it "prompts user to confirm overwrite" do expect(FileUtils).to receive(:copy_file) capture_io { cli.start } end end context "existing project doesn't exist" do before do allow(Tmuxinator::Config).to receive(:exists?) { false } end it "exit with error code" do expect { capture_io { cli.start } }.to raise_error SystemExit end end end describe "#debug" do context "named project" do let(:project_with_force_attach) do FactoryBot.build(:project_with_force_attach) end let(:project_with_force_detach) do FactoryBot.build(:project_with_force_detach) end before do allow(Tmuxinator::Config).to receive_messages(validate: project) expect(project).to receive(:render) end it "renders the project" do ARGV.replace(["debug", "foo"]) capture_io { cli.start } end it "force attach renders the project with attach code" do ARGV.replace(["debug", "--attach=true", "sample"]) capture_io { cli.start } # Currently no project is rendered at all, # because the project file is not found # expect(out).to include "attach-session" end it "force detach renders the project without attach code" do ARGV.replace(["debug", "--attach=false", "sample"]) capture_io { cli.start } # Currently no project is rendered at all # expect(out).to_not include "attach-session" end it "renders the project with custom session" do ARGV.replace(["debug", "sample", "bar"]) capture_io { cli.start } end end context "project config file" do before do allow(Tmuxinator::Config).to receive_messages(version: 1.9) expect(Tmuxinator::Config).to receive(:validate).and_call_original end it "renders the project if given a valid project config file" do ARGV.replace(["debug", "--project-config=#{project_config}"]) expect { cli.start }.to output(/sample_with_project_config/).to_stdout end it "does not render the project if given a bogus project config file" do ARGV.replace(["start", "--project-config=bogus.yml"]) expect { capture_io { cli.start } }.to raise_error(SystemExit) end end end describe "#delete" do context "with a single argument" do before do ARGV.replace(["delete", "foo"]) allow(Thor::LineEditor).to receive_messages(readline: "y") end context "project exists" do before do allow(Tmuxinator::Config).to receive(:exists?) { true } end it "deletes the project" do expect(FileUtils).to receive(:rm) capture_io { cli.start } end end context "local project exists" do before do allow(Tmuxinator::Config).to receive(:exists?) { true } expect(Tmuxinator::Config).to receive(:project) { "local" } end it "deletes the local project" do expect(FileUtils).to receive(:rm).with("local") capture_io { cli.start } end end context "project doesn't exist" do before do allow(Tmuxinator::Config).to receive(:exists?) { false } end it "outputs an error message" do expect(capture_io { cli.start }[0]).to match(/foo does not exist!/) end end end context "with multiple arguments" do before do ARGV.replace(["delete", "foo", "bar"]) allow(Thor::LineEditor).to receive_messages(readline: "y") end context "all projects exist" do before do allow(Tmuxinator::Config).to receive(:exists?).and_return(true) end it "deletes the projects" do expect(FileUtils).to receive(:rm).exactly(2).times capture_io { cli.start } end end context "only one project exists" do before do allow(Tmuxinator::Config).to receive(:exists?).with(name: "foo") { true } allow(Tmuxinator::Config).to receive(:exists?).with(name: "bar") { false } end it "deletes one project" do expect(FileUtils).to receive(:rm) capture_io { cli.start } end it "outputs an error message" do expect(capture_io { cli.start }[0]).to match(/bar does not exist!/) end end context "all projects do not exist" do before do allow(Tmuxinator::Config).to receive(:exists?).and_return(false) end it "outputs multiple error messages" do expect(capture_io { cli.start }[0]). to match(/foo does not exist!\nbar does not exist!/) end end end end describe "#implode" do before do ARGV.replace(["implode"]) allow(Thor::LineEditor).to receive_messages(readline: "y") end it "confirms deletion of all projects" do expect(Thor::LineEditor).to receive(:readline).and_return("y") capture_io { cli.start } end it "deletes the configuration directory(s)" do allow(Tmuxinator::Config).to receive(:directories) \ { [Tmuxinator::Config.xdg, Tmuxinator::Config.home] } expect(FileUtils).to receive(:remove_dir).once. with(Tmuxinator::Config.xdg) expect(FileUtils).to receive(:remove_dir).once. with(Tmuxinator::Config.home) expect(FileUtils).to receive(:remove_dir).never capture_io { cli.start } end context "$TMUXINATOR_CONFIG specified" do it "only deletes projects in that directory" do allow(ENV).to receive(:[]).and_call_original allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG").and_return "dir" allow(File).to receive(:directory?).with("dir").and_return true expect(FileUtils).to receive(:remove_dir).once.with("dir") expect(FileUtils).to receive(:remove_dir).never capture_io { cli.start } end end end describe "#list" do before do allow(Dir).to receive_messages(:[] => ["/path/to/project.yml"]) end context "set --newline flag " do ARGV.replace(["list --newline"]) it "force output to be one entry per line" do expect { capture_io { cli.start } }.to_not raise_error end end context "no arguments are given" do ARGV.replace(["list"]) it "lists all projects " do expect { capture_io { cli.start } }.to_not raise_error end end end describe "#version" do before do ARGV.replace(["version"]) end it "prints the current version" do out, _err = capture_io { cli.start } expect(out).to eq "tmuxinator #{Tmuxinator::VERSION}\n" end end describe "#doctor" do before do ARGV.replace(["doctor"]) end it "checks requirements" do expect(Tmuxinator::Doctor).to receive(:installed?) expect(Tmuxinator::Doctor).to receive(:editor?) expect(Tmuxinator::Doctor).to receive(:shell?) capture_io { cli.start } end end describe "#find_project_file" do let(:name) { "foobar" } let(:path) { Tmuxinator::Config.default_project(name) } after(:each) do FileUtils.remove_file(path) if File.exist?(path) end context "when the project file does not already exist" do before do expect(File).not_to exist(path), "expected file at #{path} not to exist" end it "should generate a project file" do new_path = described_class.new.find_project_file(name, false) expect(new_path).to eq path expect(File).to exist new_path end end context "when the project file _does_ already exist" do let(:extra) { " - extra: echo 'foobar'" } before do expect(File).not_to exist(path), "expected file at #{path} not to exist" expect(described_class.new.generate_project_file(name, path)).to eq path expect(File).to exist path File.open(path, "w") do |f| f.write(extra) f.flush end expect(File.read(path)).to match %r{#{extra}} end it "should _not_ generate a new project file" do new_path = described_class.new.find_project_file(name, false) expect(new_path).to eq path expect(File).to exist new_path expect(File.read(new_path)).to match %r{#{extra}} end end end describe "#generate_project_file" do let(:name) { "foobar-#{Time.now.to_i}" } it "should always generate a project file" do Dir.mktmpdir do |dir| path = "#{dir}/#{name}.yml" expect(File).not_to exist(path), "expected file at #{path} not to exist" new_path = described_class.new.generate_project_file(name, path) expect(new_path).to eq path expect(File).to exist new_path end end it "should generate a project file using the correct project file path" do file = StringIO.new allow(File).to receive(:open) { |&block| block.yield file } Dir.mktmpdir do |dir| path = "#{dir}/#{name}.yml" _ = described_class.new.generate_project_file(name, path) expect(file.string).to match %r{\A# #{path}$} end end end describe "#create_project" do before do allow(Tmuxinator::Config).to receive_messages(directory: path) end let(:name) { "sample" } let(:custom_name) { nil } let(:cli_options) { {} } let(:path) { File.expand_path("../../../fixtures", __FILE__) } shared_examples_for :a_proper_project do it "should create a valid project" do expect(subject).to be_a Tmuxinator::Project expect(subject.name).to eq name end end context "when creating a traditional named project" do let(:params) do { name: name, custom_name: custom_name } end subject { described_class.new.create_project(params) } it_should_behave_like :a_proper_project end context "attach option" do describe "detach" do it "sets force_detach to false when no attach argument is provided" do project = described_class.new.create_project(name: name) expect(project.force_detach).to eq(false) end it "sets force_detach to true when 'attach: false' is provided" do project = described_class.new.create_project(attach: false, name: name) expect(project.force_detach).to eq(true) end it "sets force_detach to false when 'attach: true' is provided" do project = described_class.new.create_project(attach: true, name: name) expect(project.force_detach).to eq(false) end end describe "attach" do it "sets force_attach to false when no attach argument is provided" do project = described_class.new.create_project(name: name) expect(project.force_attach).to eq(false) end it "sets force_attach to true when 'attach: true' is provided" do project = described_class.new.create_project(attach: true, name: name) expect(project.force_attach).to eq(true) end it "sets force_attach to false when 'attach: false' is provided" do project = described_class.new.create_project(attach: false, name: name) expect(project.force_attach).to eq(false) end end end end context "exit status" do before do ARGV.replace(["non-existent-command"]) end it "returns a non-zero status when an error occurs" do expect { capture_io { cli.start } }.to raise_error(SystemExit) do |e| expect(e.status).to eq 1 end end end end tmuxinator-1.1.4/spec/lib/tmuxinator/config_spec.rb000066400000000000000000000343541360172565300224660ustar00rootroot00000000000000require "spec_helper" describe Tmuxinator::Config do let(:fixtures_dir) { File.expand_path("../../../fixtures/", __FILE__) } let(:xdg_config_dir) { "#{fixtures_dir}/xdg-tmuxinator" } let(:home_config_dir) { "#{fixtures_dir}/dot-tmuxinator" } describe "#directory" do context "environment variable $TMUXINATOR_CONFIG non-blank" do it "is $TMUXINATOR_CONFIG" do allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG"). and_return "expected" allow(File).to receive(:directory?).and_return true expect(described_class.directory).to eq "expected" end end context "only ~/.tmuxinator exists" do it "is ~/.tmuxinator" do allow(described_class).to receive(:environment?).and_return false allow(described_class).to receive(:xdg?).and_return false allow(described_class).to receive(:home?).and_return true expect(described_class.directory).to eq described_class.home end end context "only $XDG_CONFIG_HOME/tmuxinator exists" do it "is #xdg" do allow(described_class).to receive(:environment?).and_return false allow(described_class).to receive(:xdg?).and_return true allow(described_class).to receive(:home?).and_return false expect(described_class.directory).to eq described_class.xdg end end context "both $XDG_CONFIG_HOME/tmuxinator and ~/.tmuxinator exist" do it "is #xdg" do allow(described_class).to receive(:environment?).and_return false allow(described_class).to receive(:xdg?).and_return true allow(described_class).to receive(:home?).and_return true expect(described_class.directory).to eq described_class.xdg end end context "defaulting to xdg with parent directory(s) that do not exist" do it "creates parent directories if required" do allow(described_class).to receive(:environment?).and_return false allow(described_class).to receive(:xdg?).and_return false allow(described_class).to receive(:home?).and_return false Dir.mktmpdir do |dir| config_parent = "#{dir}/non_existant_parent/s" allow(XDG).to receive(:[]).with("CONFIG").and_return config_parent expect(described_class.directory). to eq "#{config_parent}/tmuxinator" expect(File.directory?("#{config_parent}/tmuxinator")).to be true end end end end describe "#enviroment" do context "environment variable $TMUXINATOR_CONFIG is not empty" do it "is $TMUXINATOR_CONFIG" do allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG"). and_return "expected" # allow(XDG).to receive(:[]).with("CONFIG").and_return "expected" allow(File).to receive(:directory?).and_return true expect(described_class.environment).to eq "expected" end end context "environment variable $TMUXINATOR_CONFIG is nil" do it "is an empty string" do allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG"). and_return nil # allow(XDG).to receive(:[]).with("CONFIG").and_return nil allow(File).to receive(:directory?).and_return true expect(described_class.environment).to eq "" end end context "environment variable $TMUXINATOR_CONFIG is set and empty" do it "is an empty string" do allow(XDG).to receive(:[]).with("CONFIG").and_return "" allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG").and_return "" expect(described_class.environment).to eq "" end end end describe "#directories" do context "without TMUXINATOR_CONFIG environment" do before do allow(described_class).to receive(:environment?).and_return false end it "is empty if no configuration directories exist" do allow(File).to receive(:directory?).and_return false expect(described_class.directories).to eq [] end it "contains #xdg before #home" do allow(described_class).to receive(:xdg).and_return "XDG" allow(described_class).to receive(:home).and_return "HOME" allow(File).to receive(:directory?).and_return true expect(described_class.directories).to eq \ ["XDG", "HOME"] end end context "with TMUXINATOR_CONFIG environment" do before do allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG"). and_return "TMUXINATOR_CONFIG" end it "is only [$TMUXINATOR_CONFIG] if set" do allow(File).to receive(:directory?).and_return true expect(described_class.directories).to eq ["TMUXINATOR_CONFIG"] end end end describe "#home" do it "is ~/.tmuxinator" do expect(described_class.home).to eq "#{ENV['HOME']}/.tmuxinator" end end describe "#xdg" do it "is $XDG_CONFIG_HOME/tmuxinator" do expect(described_class.xdg).to eq "#{XDG['CONFIG_HOME']}/tmuxinator" end end describe "#sample" do it "gets the path of the sample project" do expect(described_class.sample).to include("sample.yml") end end describe "#default" do it "gets the path of the default config" do expect(described_class.default).to include("default.yml") end end describe "#version" do subject { described_class.version } before do expect(Tmuxinator::Doctor).to receive(:installed?).and_return(true) allow_any_instance_of(Kernel).to receive(:`).with(/tmux\s\-V/). and_return("tmux #{version}") end version_mapping = { "0.8" => 0.8, "1.0" => 1.0, "1.9" => 1.9, "1.9a" => 1.9, "2.4" => 2.4, "2.9a" => 2.9, "3.0-rc5" => 3.0, "next-3.1" => 3.1, "master" => Float::INFINITY, # Failsafes "foobar" => 0.0, "-123-" => 123.0, "5935" => 5935.0, "" => 0.0, "!@#^%" => 0.0, "2.9ä" => 2.9, "v3.5" => 3.5, "v3.12.0" => 3.12, "v3.12.5" => 3.12 }.freeze version_mapping.each do |string_version, parsed_numeric_version| context "when reported version is '#{string_version}'" do let(:version) { string_version } it { is_expected.to eq parsed_numeric_version } end end end describe "#default_path_option" do context ">= 1.8" do before do allow(described_class).to receive(:version).and_return(1.8) end it "returns -c" do expect(described_class.default_path_option).to eq "-c" end end context "< 1.8" do before do allow(described_class).to receive(:version).and_return(1.7) end it "returns default-path" do expect(described_class.default_path_option).to eq "default-path" end end end describe "#default?" do let(:directory) { described_class.directory } let(:local_default) { described_class::LOCAL_DEFAULT } let(:proj_default) { described_class.default } context "when the file exists" do before do allow(File).to receive(:exist?).with(local_default) { false } allow(File).to receive(:exist?).with(proj_default) { true } end it "returns true" do expect(described_class.default?).to be_truthy end end context "when the file doesn't exist" do before do allow(File).to receive(:exist?).with(local_default) { false } allow(File).to receive(:exist?).with(proj_default) { false } end it "returns true" do expect(described_class.default?).to be_falsey end end end describe "#configs" do before do allow(described_class).to receive_messages(xdg: xdg_config_dir) allow(described_class).to receive_messages(home: home_config_dir) end it "gets a sorted list of all projects" do allow(described_class).to receive(:environment?).and_return false expect(described_class.configs). to eq ["both", "both", "dup/local-dup", "home", "local-dup", "xdg"] end it "lists only projects in $TMUXINATOR_CONFIG when set" do allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG"). and_return "#{fixtures_dir}/TMUXINATOR_CONFIG" expect(described_class.configs).to eq ["TMUXINATOR_CONFIG"] end end describe "#exists?" do before do allow(File).to receive_messages(exist?: true) allow(described_class).to receive_messages(project: "") end it "checks if the given project exists" do expect(described_class.exists?(name: "test")).to be_truthy end end describe "#global_project" do let(:directory) { described_class.directory } let(:base) { "#{directory}/sample.yml" } let(:first_dup) { "#{home_config_dir}/dup/local-dup.yml" } let(:yaml) { "#{directory}/yaml.yaml" } before do allow(described_class).to receive(:environment?).and_return false allow(described_class).to receive(:xdg).and_return fixtures_dir allow(described_class).to receive(:home).and_return fixtures_dir end context "with project yml" do it "gets the project as path to the yml file" do expect(described_class.global_project("sample")).to eq base end end context "with project yaml" do it "gets the project as path to the yaml file" do expect(Tmuxinator::Config.global_project("yaml")).to eq yaml end end context "without project yml" do it "gets the project as path to the yml file" do expect(described_class.global_project("new-project")).to be_nil end end context "with duplicate project files" do it "is the first .yml file found" do expect(described_class.global_project("local-dup")).to eq first_dup end end end describe "#local?" do it "checks if the given project exists" do path = described_class::LOCAL_DEFAULT expect(File).to receive(:exist?).with(path) { true } expect(described_class.local?).to be_truthy end end describe "#local_project" do let(:default) { described_class::LOCAL_DEFAULT } context "with a project yml" do it "gets the project as path to the yml file" do expect(File).to receive(:exist?).with(default) { true } expect(described_class.local_project).to eq default end end context "without project yml" do it "gets the project as path to the yml file" do expect(described_class.local_project).to be_nil end end end describe "#project" do let(:directory) { described_class.directory } let(:default) { described_class::LOCAL_DEFAULT } context "with an non-local project yml" do before do allow(described_class).to receive_messages(directory: fixtures_dir) end it "gets the project as path to the yml file" do expect(described_class.project("sample")). to eq "#{directory}/sample.yml" end end context "with a local project, but no global project" do it "gets the project as path to the yml file" do expect(File).to receive(:exist?).with(default) { true } expect(described_class.project("sample")).to eq "./.tmuxinator.yml" end end context "without project yml" do let(:expected) { "#{directory}/new-project.yml" } it "gets the project as path to the yml file" do expect(described_class.project("new-project")).to eq expected end end end describe "#validate" do let(:default) { described_class::LOCAL_DEFAULT } context "when a project config file is provided" do it "should raise if the project config file can't be found" do project_config = "dont-exist.yml" regex = /Project config \(#{project_config}\) doesn't exist\./ expect do described_class.validate(project_config: project_config) end.to raise_error RuntimeError, regex end it "should load and validate the project" do project_config = File.join(fixtures_dir, "sample.yml") expect(described_class.validate(project_config: project_config)).to \ be_a Tmuxinator::Project end it "should take precedence over a named project" do allow(described_class).to receive_messages(directory: fixtures_dir) project_config = File.join(fixtures_dir, "sample_number_as_name.yml") project = described_class.validate(name: "sample", project_config: project_config) expect(project.name).to eq("222") end it "should take precedence over a local project" do expect(described_class).not_to receive(:local?) project_config = File.join(fixtures_dir, "sample_number_as_name.yml") project = described_class.validate(project_config: project_config) expect(project.name).to eq("222") end end context "when a project name is provided" do it "should raise if the project file can't be found" do expect do described_class.validate(name: "sample") end.to raise_error RuntimeError, %r{Project.+doesn't.exist} end it "should load and validate the project" do expect(described_class).to receive_messages(directory: fixtures_dir) expect(described_class.validate(name: "sample")).to \ be_a Tmuxinator::Project end end context "when no project name is provided" do it "should raise if the local project file doesn't exist" do expect(File).to receive(:exist?).with(default) { false } expect do described_class.validate end.to raise_error RuntimeError, %r{Project.+doesn't.exist} end it "should load and validate the project" do content = File.read(File.join(fixtures_dir, "sample.yml")) expect(File).to receive(:exist?).with(default).at_least(:once) { true } expect(File).to receive(:read).with(default).and_return(content) expect(described_class.validate).to be_a Tmuxinator::Project end end context "when no project can be found" do it "should raise with NO_PROJECT_FOUND_MSG" do expect(described_class).to receive_messages( valid_project_config?: false, valid_local_project?: false, valid_standard_project?: false ) expect do described_class.validate end.to raise_error RuntimeError, %r{Project could not be found\.} end end end end tmuxinator-1.1.4/spec/lib/tmuxinator/doctor_spec.rb000066400000000000000000000026641360172565300225120ustar00rootroot00000000000000require "spec_helper" describe Tmuxinator::Doctor do describe ".installed?" do context "tmux is installed" do before do allow(Kernel).to receive(:system) { true } end it "returns true" do expect(described_class.installed?).to be_truthy end end context "tmux is not installed" do before do allow(Kernel).to receive(:system) { false } end it "returns false" do expect(described_class.installed?).to be_falsey end end end describe ".editor?" do context "$EDITOR is set" do before do allow(ENV).to receive(:[]).with("EDITOR") { "vim" } end it "returns true" do expect(described_class.editor?).to be_truthy end end context "$EDITOR is not set" do before do allow(ENV).to receive(:[]).with("EDITOR") { nil } end it "returns false" do expect(described_class.editor?).to be_falsey end end end describe ".shell?" do context "$SHELL is set" do before do allow(ENV).to receive(:[]).with("SHELL") { "vim" } end it "returns true" do expect(described_class.shell?).to be_truthy end end context "$SHELL is not set" do before do allow(ENV).to receive(:[]).with("SHELL") { nil } end it "returns false" do expect(described_class.shell?).to be_falsey end end end end tmuxinator-1.1.4/spec/lib/tmuxinator/hooks/000077500000000000000000000000001360172565300207745ustar00rootroot00000000000000tmuxinator-1.1.4/spec/lib/tmuxinator/hooks/project_spec.rb000066400000000000000000000032041360172565300240000ustar00rootroot00000000000000require "spec_helper" shared_examples_for "a project hook" do let(:project) { FactoryBot.build(:project) } it "calls Hooks.commands_from" do expect(Tmuxinator::Hooks).to receive(:commands_from). with(kind_of(Tmuxinator::Project), hook_name).once project.send("hook_#{hook_name}") end context "hook value is string" do before { project.yaml[hook_name] = "echo 'on hook'" } it "returns the string" do expect(project.send("hook_#{hook_name}")).to eq("echo 'on hook'") end end context "hook value is Array" do before do project.yaml[hook_name] = [ "echo 'on hook'", "echo 'another command here'" ] end it "joins array using ;" do expect(project.send("hook_#{hook_name}")). to eq("echo 'on hook'; echo 'another command here'") end end end describe Tmuxinator::Hooks::Project do let(:project) { FactoryBot.build(:project) } describe "#hook_on_project_start" do it_should_behave_like "a project hook" do let(:hook_name) { "on_project_start" } end end describe "#hook_on_project_first_start" do it_should_behave_like "a project hook" do let(:hook_name) { "on_project_first_start" } end end describe "#hook_on_project_restart" do it_should_behave_like "a project hook" do let(:hook_name) { "on_project_restart" } end end describe "#hook_on_project_exit" do it_should_behave_like "a project hook" do let(:hook_name) { "on_project_exit" } end end describe "#hook_on_project_stop" do it_should_behave_like "a project hook" do let(:hook_name) { "on_project_stop" } end end end tmuxinator-1.1.4/spec/lib/tmuxinator/hooks_spec.rb000066400000000000000000000014171360172565300223360ustar00rootroot00000000000000require "spec_helper" describe Tmuxinator::Hooks do describe "#commands_from" do let(:project) { FactoryBot.build(:project) } let(:hook_name) { "generic_hook" } context "config value is string" do before { project.yaml[hook_name] = "echo 'on hook'" } it "returns the string" do expect(subject.commands_from(project, hook_name)). to eq("echo 'on hook'") end end context "config value is Array" do before do project.yaml[hook_name] = [ "echo 'on hook'", "echo 'another command here'" ] end it "joins array using ;" do expect(subject.commands_from(project, hook_name)). to eq("echo 'on hook'; echo 'another command here'") end end end end tmuxinator-1.1.4/spec/lib/tmuxinator/pane_spec.rb000066400000000000000000000015121360172565300221320ustar00rootroot00000000000000require "spec_helper" describe Tmuxinator::Pane do let(:klass) { described_class } let(:instance) { klass.new(index, project, window, *commands) } # let(:index) { "vim" } # let(:project) { 0 } # let(:tab) { nil } # let(:commands) { nil } let(:index) { 0 } let(:project) { double } let(:window) { double } let(:commands) { ["vim", "bash"] } before do allow(project).to receive(:name).and_return "foo" allow(project).to receive(:base_index).and_return 0 allow(project).to receive(:pane_base_index).and_return 1 allow(window).to receive(:project).and_return project allow(window).to receive(:index).and_return 0 end subject { instance } it "creates an instance" do expect(subject).to be_a(Tmuxinator::Pane) end it { expect(subject.tmux_window_and_pane_target).to eql "foo:0.1" } end tmuxinator-1.1.4/spec/lib/tmuxinator/project_spec.rb000066400000000000000000000417751360172565300226740ustar00rootroot00000000000000require "spec_helper" describe Tmuxinator::Project do let(:project) { FactoryBot.build(:project) } let(:project_with_custom_name) do FactoryBot.build(:project_with_custom_name) end let(:project_with_number_as_name) do FactoryBot.build(:project_with_number_as_name) end let(:project_with_emoji_as_name) do FactoryBot.build(:project_with_emoji_as_name) end let(:project_with_literals_as_window_name) do FactoryBot.build(:project_with_literals_as_window_name) end let(:project_with_deprecations) do FactoryBot.build(:project_with_deprecations) end let(:project_with_force_attach) do FactoryBot.build(:project_with_force_attach) end let(:project_with_force_detach) do FactoryBot.build(:project_with_force_detach) end let(:wemux_project) { FactoryBot.build(:wemux_project) } let(:noname_project) { FactoryBot.build(:noname_project) } let(:noroot_project) { FactoryBot.build(:noroot_project) } let(:nameless_window_project) do FactoryBot.build(:nameless_window_project) end let(:project_with_alias) do FactoryBot.build(:project_with_alias) end it "should include Hooks" do expect(project).to be_kind_of(Tmuxinator::Hooks::Project) end describe "#initialize" do context "valid yaml" do it "creates an instance" do expect(project).to be_a(Tmuxinator::Project) end end end describe "#render" do it "renders the tmux config" do expect(project.render).to_not be_empty end context "wemux" do it "renders the wemux config" do expect(wemux_project.render).to_not be_empty end end context "custom name" do it "renders the tmux config with custom name" do rendered = project_with_custom_name.render expect(rendered).to_not be_empty expect(rendered).to include("custom") expect(rendered).to_not include("sample") end end context "with alias" do it "renders the tmux config" do rendered = project_with_alias.render expect(rendered).to_not be_empty expect(rendered).to include("alias_is_working") end end end describe "#tmux_has_session?" do context "no active sessions" do before do tmux = project.tmux_command cmd = "#{tmux} -f ~/.tmux.mac.conf -L foo ls 2> /dev/null" resp = "" call_tmux_ls = receive(:`).with(cmd).at_least(:once).and_return(resp) expect(project).to call_tmux_ls end it "should return false if no sessions are found" do expect(project.tmux_has_session?("no server running")).to be false end end context "active sessions" do before do cmd = "#{project.tmux} ls 2> /dev/null" resp = ""\ "foo: 1 window (created Sun May 25 10:12:00 1986) [0x0] (detached)\n"\ "bar: 1 window (created Sat Sept 01 00:00:00 1990) [0x0] (detached)" call_tmux_ls = receive(:`).with(cmd).at_least(:once).and_return(resp) expect(project).to call_tmux_ls end it "should return true only when `tmux ls` has the named session" do expect(project.tmux_has_session?("foo")).to be true expect(project.tmux_has_session?("bar")).to be true expect(project.tmux_has_session?("quux")).to be false end it "should return false if a partial (prefix) match is found" do expect(project.tmux_has_session?("foobar")).to be false end end end describe "#windows" do context "without deprecations" do it "gets the list of windows" do expect(project.windows).to_not be_empty end end context "with deprecations" do it "still gets the list of windows" do expect(project_with_deprecations.windows).to_not be_empty end end end describe "#root" do context "without deprecations" do it "gets the root" do expect(project.root).to include("test") end end context "with deprecations" do it "still gets the root" do expect(project_with_deprecations.root).to include("test") end end context "without root" do it "doesn't throw an error" do expect { noroot_project.root }.to_not raise_error end end end describe "#name" do context "without deprecations" do it "gets the name" do expect(project.name).to eq "sample" end end context "with deprecations" do it "still gets the name" do expect(project_with_deprecations.name).to eq "sample" end end context "wemux" do it "is wemux" do expect(wemux_project.name).to eq "wemux" end end context "without name" do it "displays error message" do expect { noname_project.name }.to_not raise_error end end context "as number" do it "will gracefully handle a name given as a number" do rendered = project_with_number_as_name expect(rendered.name.to_i).to_not equal 0 end end context "as emoji" do it "will gracefully handle a name given as an emoji" do rendered = project_with_emoji_as_name # needs to allow for \\ present in shellescape'd project name expect(rendered.name).to match(/^\\*🍩/) end end context "window as non-string literal" do it "will gracefully handle a window name given as a non-string literal" do rendered = project_with_literals_as_window_name expect(rendered.windows.map(&:name)).to match_array( %w(222 222333 111222333444555666777 222.3 4e5 4E5 true false nil // /sample/) ) end end end describe "#pre_window" do subject(:pre_window) { project.pre_window } context "pre_window in yaml is string" do before { project.yaml["pre_window"] = "mysql.server start" } it "returns the string" do expect(pre_window).to eq("mysql.server start") end end context "pre_window in yaml is Array" do before do project.yaml["pre_window"] = [ "mysql.server start", "memcached -d" ] end it "joins array using ;" do expect(pre_window).to eq("mysql.server start; memcached -d") end end context "with deprecations" do context "rbenv option is present" do before do allow(project).to receive_messages(rbenv?: true) allow(project).to \ receive_message_chain(:yaml, :[]).and_return("2.0.0-p247") end it "still gets the correct pre_window command" do expect(project.pre_window).to eq "rbenv shell 2.0.0-p247" end end context "rvm option is present" do before do allow(project).to receive_messages(rbenv?: false) allow(project).to \ receive_message_chain(:yaml, :[]).and_return("ruby-2.0.0-p247") end it "still gets the correct pre_window command" do expect(project.pre_window).to eq "rvm use ruby-2.0.0-p247" end end context "pre_tab is present" do before do allow(project).to receive_messages(rbenv?: false) allow(project).to receive_messages(pre_tab?: true) end it "still gets the correct pre_window command" do expect(project.pre_window).to be_nil end end end end describe "#socket" do context "socket path is present" do before do allow(project).to receive_messages(socket_path: "/tmp") end it "gets the socket path" do expect(project.socket).to eq " -S /tmp" end end end describe "#tmux_command" do context "tmux_command specified" do before do project.yaml["tmux_command"] = "byobu" end it "gets the custom tmux command" do expect(project.tmux_command).to eq "byobu" end end context "tmux_command is not specified" do it "returns the default" do expect(project.tmux_command).to eq "tmux" end end end describe "#tmux_options" do context "no tmux options" do before do allow(project).to receive_messages(tmux_options?: false) end it "returns nothing" do expect(project.tmux_options).to eq "" end end context "with deprecations" do before do allow(project_with_deprecations).to receive_messages(cli_args?: true) end it "still gets the tmux options" do expect(project_with_deprecations.tmux_options).to \ eq " -f ~/.tmux.mac.conf" end end end describe "#get_pane_base_index" do it "extracts pane-base-index from the global tmux window options" do allow_any_instance_of(Kernel).to receive(:`). with(Regexp.new("tmux.+ show-window-option -g pane-base-index")). and_return("pane-base-index 3\n") expect(project.get_pane_base_index).to eq("3") end end describe "#get_base_index" do it "extracts base-index from the global tmux options" do allow_any_instance_of(Kernel).to receive(:`). with(Regexp.new("tmux.+ show-option -g base-index")). and_return("base-index 1\n") expect(project.get_base_index).to eq("1") end end describe "#base_index" do context "when pane_base_index is 1 and base_index is unset" do before do allow(project).to receive_messages(get_pane_base_index: "1") allow(project).to receive_messages(get_base_index: nil) end it "gets the tmux default of 0" do expect(project.base_index).to eq(0) end end context "base index present" do before do allow(project).to receive_messages(get_base_index: "1") end it "gets the base index" do expect(project.base_index).to eq 1 end end context "base index not present" do before do allow(project).to receive_messages(get_base_index: nil) end it "defaults to 0" do expect(project.base_index).to eq 0 end end end describe "#startup_window" do context "startup window specified" do it "gets the startup window from project config" do project.yaml["startup_window"] = "logs" expect(project.startup_window).to eq("sample:logs") end end context "startup window not specified" do it "returns base index instead" do allow(project).to receive_messages(base_index: 8) expect(project.startup_window).to eq("sample:8") end end end describe "#startup_pane" do context "startup pane specified" do it "get the startup pane index from project config" do project.yaml["startup_pane"] = 1 expect(project.startup_pane).to eq("sample:0.1") end end context "startup pane not specified" do it "returns the base pane instead" do allow(project).to receive_messages(pane_base_index: 4) expect(project.startup_pane).to eq("sample:0.4") end end end describe "#window" do it "gets the window and index for tmux" do expect(project.window(1)).to eq "sample:1" end end describe "#name?" do context "name is present" do it "returns true" do expect(project.name?).to be_truthy end end end describe "#windows?" do context "windows are present" do it "returns true" do expect(project.windows?).to be_truthy end end end describe "#root?" do context "root are present" do it "returns true" do expect(project.root?).to be_truthy end end end describe "#send_keys" do context "no command for window" do it "returns empty string" do expect(project.send_keys("", 1)).to be_empty end end context "command for window is not empty" do it "returns the tmux command" do expect(project.send_keys("vim", 1)).to \ eq "tmux -f ~/.tmux.mac.conf -L foo send-keys -t sample:1 vim C-m" end end end describe "#send_pane_command" do context "no command for pane" do it "returns empty string" do expect(project.send_pane_command("", 0, 0)).to be_empty end end context "command for pane is not empty" do it "returns the tmux command" do expect(project.send_pane_command("vim", 1, 0)).to \ eq "tmux -f ~/.tmux.mac.conf -L foo send-keys -t sample:1 vim C-m" end end end describe "#deprecations" do context "without deprecations" do it "is empty" do expect(project.deprecations).to be_empty end end context "with deprecations" do it "is not empty" do expect(project_with_deprecations.deprecations).to_not be_empty end end end describe "#commands" do let(:window) { project.windows.keep_if { |w| w.name == "shell" }.first } it "splits commands into an array" do commands = [ "tmux -f ~/.tmux.mac.conf -L foo send-keys -t sample:1 git\\ pull C-m", "tmux -f ~/.tmux.mac.conf -L foo send-keys -t sample:1 git\\ merge C-m" ] expect(window.commands).to eq(commands) end end describe "#pre" do subject(:pre) { project.pre } context "pre in yaml is string" do before { project.yaml["pre"] = "mysql.server start" } it "returns the string" do expect(pre).to eq("mysql.server start") end end context "pre in yaml is Array" do before do project.yaml["pre"] = [ "mysql.server start", "memcached -d" ] end it "joins array using ;" do expect(pre).to eq("mysql.server start; memcached -d") end end end describe "#attach?" do context "attach is true in yaml" do before { project.yaml["attach"] = true } it "returns true" do expect(project.attach?).to be_truthy end end context "attach is not defined in yaml" do it "returns true" do expect(project.attach?).to be_truthy end end context "attach is false in yaml" do before { project.yaml["attach"] = false } it "returns false" do expect(project.attach?).to be_falsey end end context "attach is true in yaml, but command line forces detach" do before { project_with_force_attach.yaml["attach"] = true } it "returns false" do expect(project_with_force_detach.attach?).to be_falsey end end context "attach is false in yaml, but command line forces attach" do before { project_with_force_detach.yaml["attach"] = false } it "returns true" do expect(project_with_force_attach.attach?).to be_truthy end end end describe "tmux_new_session_command" do let(:command) { "#{executable} new-session -d -s #{session} -n #{window}" } let(:executable) { project.tmux } let(:session) { project.name } let(:window) { project.windows.first.name } context "when first window has a name" do it "returns command to start a new detached session" do expect(project.tmux_new_session_command).to eq command end end context "when first window is nameless" do let(:project) { nameless_window_project } let(:command) { "#{project.tmux} new-session -d -s #{project.name} " } it "returns command to for new detached session without a window name" do expect(project.tmux_new_session_command).to eq command end end end describe "tmux_kill_session_command" do let(:command) { "#{executable} kill-session -t #{session}" } let(:executable) { project.tmux } let(:session) { project.name } context "when first window has a name" do it "returns command to start a new detached session" do expect(project.tmux_kill_session_command).to eq command end end end describe "::load" do let(:path) { File.expand_path("../../../fixtures/sample.yml", __FILE__) } let(:options) { {} } it "should raise if the project file doesn't parse" do bad_yaml = <<-Y name: "foo" subkey: Y expect(File).to receive(:read).with(path) { bad_yaml } expect do described_class.load(path, options) end.to raise_error RuntimeError, /\AFailed to parse config file: .+\z/ end it "should return an instance of the class if the file loads" do expect(described_class.load(path, options)).to be_a Tmuxinator::Project end end describe "::parse_settings" do let(:args) { ["one", "two=three"] } it "returns settings in a hash" do expect(described_class.parse_settings(args)["two"]).to eq("three") end it "removes settings from args" do described_class.parse_settings(args) expect(args).to eq(["one"]) end end describe "#validate!" do it "should raise if there are no windows defined" do nowindows_project = FactoryBot.build(:nowindows_project) expect do nowindows_project.validate! end.to raise_error RuntimeError, %r{should.include.some.windows} end it "should raise if there is not a project name" do expect do noname_project.validate! end.to raise_error RuntimeError, %r{didn't.specify.a.'project_name'} end end end tmuxinator-1.1.4/spec/lib/tmuxinator/util_spec.rb000066400000000000000000000001551360172565300221660ustar00rootroot00000000000000require "spec_helper" describe Tmuxinator::Util do let(:util) { Object.new.extend(Tmuxinator::Util) } end tmuxinator-1.1.4/spec/lib/tmuxinator/wemux_support_spec.rb000066400000000000000000000010451360172565300241510ustar00rootroot00000000000000require "spec_helper" describe Tmuxinator::WemuxSupport do let(:klass) { Class.new } let(:instance) { klass.new } before { instance.extend Tmuxinator::WemuxSupport } describe "#render" do it "renders the template" do expect(File).to receive(:read).at_least(:once) { "wemux ls 2>/dev/null" } expect(instance.render).to match %r{wemux.ls.2>\/dev\/null} end end describe "#name" do it { expect(instance.name).to eq "wemux" } end describe "#tmux" do it { expect(instance.tmux).to eq "wemux" } end end tmuxinator-1.1.4/spec/lib/tmuxinator/window_spec.rb000066400000000000000000000217251360172565300225260ustar00rootroot00000000000000require "spec_helper" describe Tmuxinator::Window do let(:project) { double } let(:panes) { ["vim", nil, "top"] } let(:window_name) { "editor" } let(:synchronize) { false } let(:yaml) do { window_name => { "pre" => [ "echo 'I get run in each pane. Before each pane command!'", nil ], "synchronize" => synchronize, "layout" => "main-vertical", "panes" => panes } } end let(:yaml_root) do { "editor" => { "root" => "/project/override", "root?" => true, "pre" => [ "echo 'I get run in each pane. Before each pane command!'", nil ], "layout" => "main-vertical", "panes" => panes } } end let(:window) { described_class.new(yaml, 0, project) } let(:window_root) { described_class.new(yaml_root, 0, project) } shared_context "window command context" do let(:project) { double(:project) } let(:window) { described_class.new(yaml, 0, project) } let(:root?) { true } let(:root) { "/project/tmuxinator" } before do allow(project).to receive_messages( name: "test", tmux: "tmux", root: root, root?: root?, base_index: 1 ) end let(:tmux_part) { project.tmux } end before do allow(project).to receive_messages( tmux: "tmux", name: "test", base_index: 1, pane_base_index: 0, root: "/project/tmuxinator", root?: true ) end describe "#initialize" do it "creates an instance" do expect(window).to be_a(Tmuxinator::Window) end end describe "#root" do context "without window root" do it "gets the project root" do expect(window.root).to include("/project/tmuxinator") end end context "with window root" do it "gets the window root" do expect(window_root.root).to include("/project/override") end end end describe "#panes" do context "with a three element Array" do let(:panes) { ["vim", "ls", "top"] } it "creates three panes" do expect(Tmuxinator::Pane).to receive(:new).exactly(3).times window.panes end it "returns three panes" do expect(window.panes).to all be_a_pane.with( project: project, tab: window ) expect(window.panes).to match( [ a_pane.with(index: 0).and_commands("vim"), a_pane.with(index: 1).and_commands("ls"), a_pane.with(index: 2).and_commands("top") ] ) end end context "with a String" do let(:panes) { "vim" } it "returns one pane in an Array" do expect(window.panes.first).to be_a_pane. with(index: 0).and_commands("vim") end end context "with nil" do let(:panes) { nil } it "returns an empty Array" do expect(window.panes).to be_empty end end context "nested collections" do let(:command1) { "cd /tmp/" } let(:command2) { "ls" } let(:panes) { ["vim", nested_collection] } context "with nested hash" do let(:nested_collection) { { pane2: [command1, command2] } } it "returns two panes in an Array" do expect(window.panes).to match [ a_pane.with(index: 0).and_commands("vim"), a_pane.with(index: 1).and_commands(command1, command2) ] end end context "with nested array" do let(:nested_collection) { [command1, command2] } it "returns two panes in an Array" do expect(window.panes).to match [ a_pane.with(index: 0).and_commands("vim"), a_pane.with(index: 1).and_commands(command1, command2) ] end end end end describe "#pre" do context "pre is a string" do before do yaml["editor"]["pre"] = "vim" end it "returns the pre command" do expect(window.pre).to eq "vim" end end context "pre is not present" do before do yaml["editor"].delete("pre") end it "returns nil" do expect(window.pre).to be_nil end end end describe "#build_commands" do context "command is an array" do before do yaml["editor"] = ["git fetch", "git status"] end it "returns the flattened command" do expect(window.commands).to eq [ "tmux send-keys -t test:1 git\\ fetch C-m", "tmux send-keys -t test:1 git\\ status C-m" ] end end context "command is a string" do before do yaml["editor"] = "vim" end it "returns the command" do expect(window.commands).to eq ["tmux send-keys -t test:1 vim C-m"] end end context "command is empty" do before do yaml["editor"] = "" end it "returns an empty array" do expect(window.commands).to be_empty end end context "command is a hash" do before do yaml["editor"] = { "layout" => "main-horizontal", "panes" => [nil] } end it "returns an empty array" do expect(window.commands).to be_empty end end end describe "#name_options" do context "with a name" do let(:window_name) { "editor" } it "specifies name with tmux name option" do expect(window.tmux_window_name_option).to eq "-n #{window_name}" end end context "without a name" do let(:window_name) { nil } it "specifies no tmux name option" do expect(window.tmux_window_name_option).to be_empty end end end describe "#synchronize_before?" do subject { window.synchronize_before? } context "synchronize is 'before'" do let(:synchronize) { "before" } it { is_expected.to be true } end context "synchronize is true" do let(:synchronize) { true } it { is_expected.to be true } end context "synchronize is 'after'" do let(:synchronize) { "after" } it { is_expected.to be false } end context "synchronization disabled" do let(:synchronize) { false } it { is_expected.to be false } end context "synchronization not specified" do it { is_expected.to be false } end end describe "#synchronize_after?" do subject { window.synchronize_after? } context "synchronization is 'after'" do let(:synchronize) { "after" } it { is_expected.to be true } end context "synchronization is true" do let(:synchronize) { true } it { is_expected.to be false } end context "synchronization is 'before'" do let(:synchronize) { "before" } it { is_expected.to be false } end context "synchronization disabled" do let(:synchronize) { false } it { is_expected.to be false } end context "synchronization not specified" do it { is_expected.to be false } end end describe "#tmux_synchronize_panes" do include_context "window command context" let(:window_option_set_part) { "set-window-option" } let(:target_part) { "-t #{window.tmux_window_target}" } let(:synchronize_panes_part) { "synchronize-panes" } context "synchronization enabled" do let(:synchronize) { true } let(:enabled) { "on" } let(:full_command) do "#{tmux_part} #{window_option_set_part} #{target_part} #{synchronize_panes_part} #{enabled}" end it "should set the synchronize-panes window option on" do expect(window.tmux_synchronize_panes).to eq full_command end end end describe "#tmux_new_window_command" do include_context "window command context" let(:window_part) { "new-window" } let(:name_part) { window.tmux_window_name_option } let(:target_part) { "-t #{window.tmux_window_target}" } let(:path_part) { "#{path_option} #{project.root}" } let(:path_option) { "-c" } let(:full_command) do "#{tmux_part} #{window_part} #{path_part} #{target_part} #{name_part}" end before do allow(Tmuxinator::Config).to receive(:default_path_option) { path_option } end it "constructs window command with path, target, and name options" do expect(window.tmux_new_window_command).to eq full_command end context "root not set" do let(:root?) { false } let(:root) { nil } let(:path_part) { nil } it "has an extra space instead of path_part" do expect(window.tmux_new_window_command).to eq full_command end end context "name not set" do let(:window_name) { nil } let(:full_command) do "#{tmux_part} #{window_part} #{path_part} #{target_part} " end it "does not set name option" do expect(window.tmux_new_window_command).to eq full_command end end end describe "#tmux_select_first_pane" do it "targets the pane based on the configured pane_base_index" do expect(window.tmux_select_first_pane).to eq("tmux select-pane -t test:1.0") end end end tmuxinator-1.1.4/spec/matchers/000077500000000000000000000000001360172565300164775ustar00rootroot00000000000000tmuxinator-1.1.4/spec/matchers/pane_matcher.rb000066400000000000000000000016771360172565300214650ustar00rootroot00000000000000RSpec::Matchers.alias_matcher :be_a_pane, :a_pane RSpec::Matchers.define :a_pane do attr_reader :commands match do result = is_pane result && attributes_match if @expected_attrs result &&= commands_match if commands result end failure_message do |actual| return "Expected #{actual} to be a Tmuxinator::Pane" unless is_pane msg = "Actual pane does not match expected" msg << "\n Expected #{@commands} but has #{actual.commands}" if @commands msg << "\n Expected pane to have #{@expected_attrs}" if @expected_attrs end chain :with do |attrs| @expected_attrs = attrs end chain :with_commands do |*expected| @commands = expected end alias_method :and_commands, :with_commands private def attributes_match expect(@actual).to have_attributes(@expected_attrs) end def commands_match @actual.commands == commands end def is_pane @actual.is_a? Tmuxinator::Pane end end tmuxinator-1.1.4/spec/spec_helper.rb000066400000000000000000000023461360172565300175140ustar00rootroot00000000000000require "coveralls" require "pry" require "simplecov" require "xdg" formatters = [ SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter ] SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(formatters) SimpleCov.start do add_filter "vendor/cache" end require "tmuxinator" require "factory_bot" FactoryBot.find_definitions # Custom Matchers require_relative "matchers/pane_matcher" RSpec.configure do |config| config.order = "random" end # Copied from minitest. def capture_io require "stringio" captured_stdout = StringIO.new captured_stderr = StringIO.new orig_stdout = $stdout orig_stderr = $stderr $stdout = captured_stdout $stderr = captured_stderr yield return captured_stdout.string, captured_stderr.string ensure $stdout = orig_stdout $stderr = orig_stderr end def tmux_config(options = {}) standard_options = [ "assume-paste-time 1", "bell-action any", "bell-on-alert off", ] if base_index = options.fetch(:base_index) { 1 } standard_options << "base-index #{base_index}" end if pane_base_index = options.fetch(:pane_base_index) { 1 } standard_options << "pane-base-index #{pane_base_index}" end "echo '#{standard_options.join("\n")}'" end tmuxinator-1.1.4/tmuxinator.gemspec000066400000000000000000000043231360172565300175200ustar00rootroot00000000000000# coding: utf-8 lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "tmuxinator/version" Gem::Specification.new do |s| s.name = "tmuxinator" s.version = Tmuxinator::VERSION s.authors = ["Allen Bargi", "Christopher Chow"] s.email = ["allen.bargi@gmail.com", "chris@chowie.net"] s.description = %q{Create and manage complex tmux sessions easily.} s.summary = %q{Create and manage complex tmux sessions easily.} s.homepage = "https://github.com/tmuxinator/tmuxinator" s.license = "MIT" s.files = Dir["lib/**/*", "spec/**/*", "bin/*", "completion/*"] s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) } s.test_files = s.files.grep(%r{^(test|spec|features)/}) s.require_paths = ["lib"] s.post_install_message = %q{ __________________________________________________________ .......................................................... Thank you for installing tmuxinator. Make sure that you've set these variables in your ENV: $EDITOR, $SHELL You can run `tmuxinator doctor` to make sure everything is set. Happy tmuxing with tmuxinator! .......................................................... __________________________________________________________ } s.required_rubygems_version = ">= 1.8.23" s.required_ruby_version = ">= 2.4.6" s.add_dependency "erubis", "~> 2.6" s.add_dependency "thor", "~> 1.0" s.add_dependency "xdg", "~> 2.2", ">= 2.2.5" s.add_development_dependency "activesupport", "< 5.0.0" # Please see issue #432 s.add_development_dependency "awesome_print", "~> 1.2" s.add_development_dependency "bundler", ">= 1.3" s.add_development_dependency "coveralls", "~> 0.8" s.add_development_dependency "factory_bot", "~> 4.8" s.add_development_dependency "pry", "~> 0.10" s.add_development_dependency "rake", "~> 10.4" s.add_development_dependency "rspec", "~> 3.3" s.add_development_dependency "rubocop", "~> 0.48.1" s.add_development_dependency "simplecov", "~> 0.16" # quiet "Gem.gunzip is deprecated" deprecation warning caused by rubocop s.add_development_dependency "unicode-display_width", "~> 1.3" end