├── .gitignore ├── .gitmodules ├── Documentation ├── auth_man_pages.md ├── build-man.sh ├── building.md ├── install-man.sh ├── modulo-add.1 ├── modulo-add.1.md ├── modulo-add.1.ronn ├── modulo-init.1 ├── modulo-init.1.md ├── modulo-init.1.ronn ├── modulo-layout.1 ├── modulo-layout.1.md ├── modulo-layout.1.ronn ├── modulo-update.1 ├── modulo-update.1.md ├── modulo-update.1.ronn ├── modulo.1 ├── modulo.1.md └── modulo.1.ronn ├── Formula └── brew-howto.md ├── LICENSE ├── Makefile ├── Modules ├── .DS_Store ├── ELCLI │ ├── .DS_Store │ ├── .gitignore │ ├── ELCLI.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ ├── ELCLI │ │ ├── CLI.swift │ │ ├── Command.swift │ │ ├── Commands │ │ │ ├── HelpCommand.swift │ │ │ └── VersionCommand.swift │ │ ├── ELCLI.h │ │ ├── Info.plist │ │ ├── Options.swift │ │ └── Output.swift │ ├── ELCLITests │ │ ├── CommitCommand.swift │ │ ├── ELCLITests-Bridging-Header.h │ │ ├── ELCLITests.swift │ │ └── Info.plist │ ├── LICENSE │ └── README.md ├── ELCodable │ ├── .gitignore │ ├── .travis.yml │ ├── CHANGELOG.md │ ├── ELCodable.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── ELCodable.xcscheme │ ├── ELCodable │ │ ├── Decimal.swift │ │ ├── Decodable.swift │ │ ├── DecodableExtensions.swift │ │ ├── DecodeOperators.swift │ │ ├── ELCodable.h │ │ ├── Encodable.swift │ │ ├── EncodableExtensions.swift │ │ ├── EncodeOperators.swift │ │ ├── Info.plist │ │ └── JSON.swift │ ├── ELCodableTests │ │ ├── DecimalTests.swift │ │ ├── DynamicKeyTest.swift │ │ ├── DynamicKeyedData.json │ │ ├── ELCodableTests.swift │ │ ├── Info.plist │ │ ├── JSONTests.swift │ │ ├── KeyPathTests.swift │ │ ├── NestedErrorTesting.json │ │ ├── NestedErrorTesting.swift │ │ ├── OptionalTests.json │ │ ├── OptionalTests.swift │ │ └── jsontest_models.json │ ├── ELCodable_osx │ │ ├── ELCodable_osx.h │ │ └── Info.plist │ ├── ELCodable_osxTests │ │ ├── ELCodable_osxTests.swift │ │ └── Info.plist │ ├── LICENSE │ └── README.md └── ELFoundation │ ├── .gitignore │ ├── .travis.yml │ ├── CHANGELOG.md │ ├── ELFoundation copy-Info.plist │ ├── ELFoundation.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ ├── ELFoundation.xcscheme │ │ └── ELFoundation_osx.xcscheme │ ├── ELFoundation │ ├── ELFoundation.h │ ├── Extensions │ │ ├── Array.swift │ │ ├── NSBundle.swift │ │ ├── NSError.swift │ │ ├── NSObject.swift │ │ ├── NSThread.swift │ │ ├── NSURL.swift │ │ └── String.swift │ ├── Info.plist │ ├── TestExtensions │ │ ├── TestHelper.swift │ │ ├── XCTestCase+Exceptions.h │ │ └── XCTestCase+Exceptions.m │ └── Utilities │ │ ├── Exceptions.swift │ │ ├── ObjectAssociation.swift │ │ ├── Swizzling.swift │ │ └── Synchronization.swift │ ├── ELFoundationTests │ ├── ArrayTests.swift │ ├── ELFoundationTests.swift │ ├── Info.plist │ ├── NSBundleTests.swift │ ├── NSObjectTests.swift │ ├── NSThreadTests.swift │ ├── NSURLTests.swift │ └── StringTests.swift │ ├── ELFoundation_osx │ ├── ELFoundation_osx.h │ └── Info.plist │ ├── ELFoundation_osxTests │ └── Info.plist │ ├── LICENSE │ └── README.md ├── ModuloKit ├── Actions.swift ├── Commands │ ├── AddCommand.swift │ ├── DefaultsCommand.swift │ ├── InitCommand.swift │ ├── MapCommand.swift │ ├── RemoveCommand.swift │ ├── SetCommand.swift │ ├── StatusCommand.swift │ └── UpdateCommand.swift ├── ErrorCode.swift ├── Extensions.swift ├── Info.plist ├── Modulo.swift ├── ModuloKit.h ├── Reachability.swift ├── SCM │ ├── Git.swift │ └── SCM.swift ├── Semver.swift ├── SemverCodable.swift ├── Specs │ ├── DependencySpec.swift │ ├── ModuleSpec.swift │ └── OptionsSpec.swift ├── State.swift └── System │ ├── BridgingHeader.h │ ├── System.h │ └── System.m ├── ModuloKitTests ├── Info.plist ├── ModuloKitTests-Bridging-Header.h ├── ModuloKitTests.swift ├── SemverTests.swift ├── TestAdd.swift ├── TestCheckout.swift ├── TestDefaults.swift ├── TestDummyApp.swift ├── TestGit.swift ├── TestInit.swift ├── TestScenarios.swift ├── TestStatus.swift ├── TestUpdate.swift ├── Utils.swift ├── XCTestCase+Exceptions.h └── XCTestCase+Exceptions.m ├── README.md ├── modulo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── modulo.xcscmblueprint │ └── xcuserdata │ │ └── brandonsneed.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── modulo.xcscheme └── xcuserdata │ ├── brandon.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── ModuloKit.xcscheme │ │ ├── modulo.xcscheme │ │ └── xcschememanagement.plist │ ├── brandonsneed.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── ModuloKit.xcscheme │ │ ├── modulo.xcscheme │ │ └── xcschememanagement.plist │ └── brsneed.xcuserdatad │ └── xcschemes │ ├── ModuloKit.xcscheme │ └── xcschememanagement.plist └── modulo └── main.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Combined .gitignore from github/gitignore/master/Swift.gitignore and Xcode.gitignore 2 | 3 | ## Build generated 4 | build/ 5 | .build/ 6 | DerivedData/ 7 | 8 | ## Various settings 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1v3 12 | !default.mode1v3 13 | *.mode2v3 14 | !default.mode2v3 15 | *.perspectivev3 16 | !default.perspectivev3 17 | xcuserdata/ 18 | 19 | ## Other 20 | *.moved-aside 21 | *.xcuserstate 22 | *.xcscmblueprint 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | .DS_Store 34 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modulo-dm/modulo/b401f94ffee31a9359293222e2ec09974f9f475c/.gitmodules -------------------------------------------------------------------------------- /Documentation/auth_man_pages.md: -------------------------------------------------------------------------------- 1 | Using ronn: https://github.com/rtomayko/ronn/blob/master/README.md 2 | 3 | Example: http://rtomayko.github.io/ronn/ronn-format.7 4 | Source for example: https://github.com/rtomayko/ronn/blob/master/man/ronn-format.7.ronn 5 | 6 | See `modulo.1.ronn` for a starting point for new man pages. 7 | See `building.md` for how to build/install man pages. 8 | -------------------------------------------------------------------------------- /Documentation/build-man.sh: -------------------------------------------------------------------------------- 1 | DATE=`date +%Y-%m-%d` 2 | 3 | ronn --roff *.ronn --date="$DATE" --manual="Modulo manual" --organization="Modulo" 4 | 5 | # copy all the .ronn files to .md so they can be viewed/linked on github. 6 | for x in *.ronn; do n=${x/.ronn/.md}; cp $x $n; done 7 | -------------------------------------------------------------------------------- /Documentation/building.md: -------------------------------------------------------------------------------- 1 | ## Build Dependencies 2 | 3 | * \>= Xcode 8.1 4 | * Git 5 | * Ronn (sudo gem install ronn) 6 | 7 | ### Modulo binary, from Source 8 | 9 | Clone this repo, then ... 10 | 11 | ```bash 12 | $ cd $repo 13 | $ xcodebuild -project modulo.xcodeproj -scheme modulo -configuration Release SYMROOT=build 14 | ``` 15 | 16 | This will leave the modulo binary in `/tmp/modulo` 17 | 18 | ### Modulo documentation, via Ronn 19 | 20 | ```bash 21 | $ ... 22 | ``` 23 | -------------------------------------------------------------------------------- /Documentation/install-man.sh: -------------------------------------------------------------------------------- 1 | sudo cp *.1 /usr/local/share/man/man1/ 2 | -------------------------------------------------------------------------------- /Documentation/modulo-add.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "MODULO\-ADD" "1" "July 2017" "Modulo" "Modulo manual" 5 | . 6 | .SH "NAME" 7 | \fBmodulo\-add\fR \- Initialize a project for use with Modulo\. 8 | . 9 | .SH "SYNOPSIS" 10 | \fBmodulo add\fR [\-u] \fIrepo_url\fR 11 | . 12 | .br 13 | \fBmodulo add\fR \-\-tag [\-u] \fIsemver\fR \fIrepo_url\fR 14 | . 15 | .br 16 | \fBmodulo add\fR \-\-branch [\-u] \fIbranchname\fR \fIrepo_url\fR 17 | . 18 | .br 19 | \fBmodulo add\fR \-\-commit [\-u] \fIcommithash\fR \fIrepo_url\fR 20 | . 21 | .br 22 | . 23 | .SH "DESCRIPTION" 24 | This command adds a dependency to the current project\. When no tag/branch/commit is specified, \fBorigin/master\fR is assumed\. 25 | . 26 | .P 27 | No cloning of dependencies, etc\. takes place here unless \fB\-\-update\fR is specified\. 28 | . 29 | .P 30 | Any dependencies that are cloned (even branches) are in a detached\-head state\. This forces the same workflow to be used regardless of the checkout type\. 31 | . 32 | .SH "OPTIONS" 33 | . 34 | .TP 35 | \fB\-\-tag\fR \fIsemver\fR 36 | This option specifies that a semver tag or range should be checked out\. Modulo treats semver as \fBbreaking\.feature\.fix\fR since that is more meaningful to most\. 37 | . 38 | .IP 39 | Modulo\'s semver implementation follows the \fBnpm\fR implementation very closely\. To read more, visit: 40 | . 41 | .IP 42 | https://docs\.npmjs\.com/getting\-started/semantic\-versioning 43 | . 44 | .TP 45 | \fB\-\-branch\fR \fIbranchname\fR 46 | Specify the branch desired on checkedout\. It\'s preferrable to specify the remote, ie: \fBorigin/mybranch\fR\. If omitted, \fBorigin\fR will be the assumed remote\.\' 47 | . 48 | .TP 49 | \fB\-\-commit\fR \fIcommithash\fR 50 | Specify the commit hash to be used on checkout\. This accepts both short and long hashes\. 51 | . 52 | .TP 53 | \fB\-u, \-\-update\fR 54 | Immediately perform an \fBupdate\fR after adding the dependency\. This will clone if necessary, as well as perform any dependency compatibility checks\. 55 | . 56 | .TP 57 | \fB\-v, \-\-verbose\fR 58 | Prints verbose output\. Use this to see what underlying SCM commands are being used and any other important information\. 59 | . 60 | .TP 61 | \fB\-h, \-\-help\fR 62 | Prints the help for this command\. 63 | . 64 | .SH "EXAMPLES" 65 | \fBmodulo add \-\-tag ">=1\.1 < 2\.0\.0" \-\-update git@github\.com/something/yadda\.git\fR 66 | . 67 | .P 68 | This will do the following 69 | . 70 | .br 71 | * Add yadda\.git as a dependency\. 72 | . 73 | .br 74 | * Clone yadda\.git, because \-\-update was specified\. 75 | . 76 | .br 77 | * Checkout the latest tag that is greater than or equal to 1\.1\.0, but less than 2\.0\.0 78 | . 79 | .br 80 | . 81 | .P 82 | \fBmodulo add \-\-branch master git@github\.com/something/yadda\.git\fR 83 | . 84 | .P 85 | This will 86 | . 87 | .br 88 | * Add yadda\.git as a dependency\. 89 | . 90 | .br 91 | * Record that when performing update, the branch \fBorigin/master\fR is to be checked out\. 92 | . 93 | .br 94 | . 95 | .SH "SEE ALSO" 96 | modulo\-layout(1), modulo\-update(1), modulo\-remove(1) 97 | . 98 | .SH "REPORTING BUGS" 99 | Report bugs to the Github Project located at https://github\.com/modulo\-dm/modulo/\. You\'ll be able to see the status and track any issues you create\. 100 | . 101 | .SH "AUTHORS" 102 | . 103 | .nf 104 | 105 | Brandon Sneed 106 | Peat Bakke 107 | . 108 | .fi 109 | 110 | -------------------------------------------------------------------------------- /Documentation/modulo-add.1.md: -------------------------------------------------------------------------------- 1 | modulo-add(1) -- Initialize a project for use with Modulo. 2 | ==== 3 | 4 | ## SYNOPSIS 5 | 6 | `modulo add` [-u]
7 | `modulo add` --tag [-u]
8 | `modulo add` --branch [-u]
9 | `modulo add` --commit [-u]
10 | 11 | ## DESCRIPTION 12 | 13 | This command adds a dependency to the current project. When no tag/branch/commit is specified, `origin/master` is assumed. 14 | 15 | No cloning of dependencies, etc. takes place here unless `--update` is specified. 16 | 17 | Any dependencies that are cloned (even branches) are in a detached-head state. This forces the same workflow to be used regardless of the checkout type. 18 | 19 | ## OPTIONS 20 | 21 | * `--tag` : 22 | This option specifies that a semver tag or range should be checked out. Modulo treats semver as `breaking.feature.fix` since that is more meaningful to most. 23 | 24 | Modulo's semver implementation follows the `npm` implementation very closely. To read more, visit: 25 | 26 | https://docs.npmjs.com/getting-started/semantic-versioning 27 | 28 | * `--branch` : 29 | Specify the branch desired on checkedout. 30 | It's preferrable to specify the remote, ie: `origin/mybranch`. If omitted, `origin` will be the assumed remote.' 31 | 32 | * `--commit` : 33 | Specify the commit hash to be used on checkout. This accepts both short and long hashes. 34 | 35 | * `-u, --update`: 36 | Immediately perform an `update` after adding the dependency. This will clone if necessary, as well as perform any dependency compatibility checks. 37 | 38 | * `-v, --verbose`: 39 | Prints verbose output. Use this to see what underlying SCM commands are being used and any other important information. 40 | 41 | * `-h, --help`: 42 | Prints the help for this command. 43 | 44 | ## EXAMPLES 45 | 46 | `modulo add --tag ">=1.1 < 2.0.0" --update git@github.com/something/yadda.git` 47 | 48 | This will do the following
49 | * Add yadda.git as a dependency.
50 | * Clone yadda.git, because --update was specified.
51 | * Checkout the latest tag that is greater than or equal to 1.1.0, but less than 2.0.0
52 | 53 | `modulo add --branch master git@github.com/something/yadda.git` 54 | 55 | This will
56 | * Add yadda.git as a dependency.
57 | * Record that when performing update, the branch `origin/master` is to be checked out.
58 | 59 | ## SEE ALSO 60 | 61 | modulo-layout(1), modulo-update(1), modulo-remove(1) 62 | 63 | ## REPORTING BUGS 64 | 65 | Report bugs to the Github Project located at https://github.com/modulo-dm/modulo/. You'll be able to see the status and track any issues you create. 66 | 67 | ## AUTHORS 68 | 69 | Brandon Sneed 70 | Peat Bakke 71 | 72 | 73 | -------------------------------------------------------------------------------- /Documentation/modulo-add.1.ronn: -------------------------------------------------------------------------------- 1 | modulo-add(1) -- Initialize a project for use with Modulo. 2 | ==== 3 | 4 | ## SYNOPSIS 5 | 6 | `modulo add` [-u]
7 | `modulo add` --tag [-u]
8 | `modulo add` --branch [-u]
9 | `modulo add` --commit [-u]
10 | 11 | ## DESCRIPTION 12 | 13 | This command adds a dependency to the current project. When no tag/branch/commit is specified, `origin/master` is assumed. 14 | 15 | No cloning of dependencies, etc. takes place here unless `--update` is specified. 16 | 17 | Any dependencies that are cloned (even branches) are in a detached-head state. This forces the same workflow to be used regardless of the checkout type. 18 | 19 | ## OPTIONS 20 | 21 | * `--tag` : 22 | This option specifies that a semver tag or range should be checked out. Modulo treats semver as `breaking.feature.fix` since that is more meaningful to most. 23 | 24 | Modulo's semver implementation follows the `npm` implementation very closely. To read more, visit: 25 | 26 | https://docs.npmjs.com/getting-started/semantic-versioning 27 | 28 | * `--branch` : 29 | Specify the branch desired on checkedout. 30 | It's preferrable to specify the remote, ie: `origin/mybranch`. If omitted, `origin` will be the assumed remote.' 31 | 32 | * `--commit` : 33 | Specify the commit hash to be used on checkout. This accepts both short and long hashes. 34 | 35 | * `-u, --update`: 36 | Immediately perform an `update` after adding the dependency. This will clone if necessary, as well as perform any dependency compatibility checks. 37 | 38 | * `-v, --verbose`: 39 | Prints verbose output. Use this to see what underlying SCM commands are being used and any other important information. 40 | 41 | * `-h, --help`: 42 | Prints the help for this command. 43 | 44 | ## EXAMPLES 45 | 46 | `modulo add --tag ">=1.1 < 2.0.0" --update git@github.com/something/yadda.git` 47 | 48 | This will do the following
49 | * Add yadda.git as a dependency.
50 | * Clone yadda.git, because --update was specified.
51 | * Checkout the latest tag that is greater than or equal to 1.1.0, but less than 2.0.0
52 | 53 | `modulo add --branch master git@github.com/something/yadda.git` 54 | 55 | This will
56 | * Add yadda.git as a dependency.
57 | * Record that when performing update, the branch `origin/master` is to be checked out.
58 | 59 | ## SEE ALSO 60 | 61 | modulo-layout(1), modulo-update(1), modulo-remove(1) 62 | 63 | ## REPORTING BUGS 64 | 65 | Report bugs to the Github Project located at https://github.com/modulo-dm/modulo/. You'll be able to see the status and track any issues you create. 66 | 67 | ## AUTHORS 68 | 69 | Brandon Sneed 70 | Peat Bakke 71 | 72 | 73 | -------------------------------------------------------------------------------- /Documentation/modulo-init.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "MODULO\-INIT" "1" "July 2017" "Modulo" "Modulo manual" 5 | . 6 | .SH "NAME" 7 | \fBmodulo\-init\fR \- Initialize a project for use with Modulo\. 8 | . 9 | .SH "SYNOPSIS" 10 | \fBmodulo init\fR [\-\-app] 11 | . 12 | .br 13 | \fBmodulo init\fR [\-\-module] 14 | . 15 | .br 16 | . 17 | .SH "DESCRIPTION" 18 | This command initializes a project/directory for use with Modulo\. A \fB\.modulo\fR file is created to track dependencies, and other details\. The two modes of operation are documented below\. 19 | . 20 | .SH "OPTIONS" 21 | . 22 | .TP 23 | \fB\-\-app\fR 24 | Initializes modulo as an application\. Any dependencies will be located in \fB\.\emodules\fR upon add/update\. This option also adds an entry to SCM\'s ignore file to ignore the \fBmodules\fR directory\. 25 | . 26 | .TP 27 | \fB\-\-module\fR 28 | Initializes modulo as an module\. Any dependencies will be located in \fB\.\.\e\fR upon add/update\. 29 | . 30 | .TP 31 | \fB\-v, \-\-verbose\fR 32 | Prints verbose output\. Use this to see what underlying SCM commands are being used and any other important information\. 33 | . 34 | .TP 35 | \fB\-h, \-\-help\fR 36 | Prints the help for this command\. 37 | . 38 | .SH "SEE ALSO" 39 | modulo\-layout(1), modulo\-add(1) 40 | . 41 | .SH "REPORTING BUGS" 42 | Report bugs to the Github Project located at https://github\.com/modulo\-dm/modulo/\. You\'ll be able to see the status and track any issues you create\. 43 | . 44 | .SH "AUTHORS" 45 | . 46 | .nf 47 | 48 | Brandon Sneed 49 | Peat Bakke 50 | . 51 | .fi 52 | 53 | -------------------------------------------------------------------------------- /Documentation/modulo-init.1.md: -------------------------------------------------------------------------------- 1 | modulo-init(1) -- Initialize a project for use with Modulo. 2 | ==== 3 | 4 | ## SYNOPSIS 5 | 6 | `modulo init` [--app]
7 | `modulo init` [--module]
8 | 9 | ## DESCRIPTION 10 | 11 | This command initializes a project/directory for use with Modulo. A `.modulo` file is created to track dependencies, and other details. The two modes of operation are documented below. 12 | 13 | ## OPTIONS 14 | 15 | * `--app`: 16 | Initializes modulo as an application. Any dependencies will be located in `.\modules` upon add/update. This option also adds an entry to SCM's ignore file to ignore the `modules` directory. 17 | 18 | * `--module`: 19 | Initializes modulo as an module. Any dependencies will be located in `..\` upon add/update. 20 | 21 | * `-v, --verbose`: 22 | Prints verbose output. Use this to see what underlying SCM commands are being used and any other important information. 23 | 24 | * `-h, --help`: 25 | Prints the help for this command. 26 | 27 | ## SEE ALSO 28 | 29 | modulo-layout(1), modulo-add(1) 30 | 31 | ## REPORTING BUGS 32 | 33 | Report bugs to the Github Project located at https://github.com/modulo-dm/modulo/. You'll be able to see the status and track any issues you create. 34 | 35 | ## AUTHORS 36 | 37 | Brandon Sneed 38 | Peat Bakke 39 | 40 | 41 | -------------------------------------------------------------------------------- /Documentation/modulo-init.1.ronn: -------------------------------------------------------------------------------- 1 | modulo-init(1) -- Initialize a project for use with Modulo. 2 | ==== 3 | 4 | ## SYNOPSIS 5 | 6 | `modulo init` [--app]
7 | `modulo init` [--module]
8 | 9 | ## DESCRIPTION 10 | 11 | This command initializes a project/directory for use with Modulo. A `.modulo` file is created to track dependencies, and other details. The two modes of operation are documented below. 12 | 13 | ## OPTIONS 14 | 15 | * `--app`: 16 | Initializes modulo as an application. Any dependencies will be located in `.\modules` upon add/update. This option also adds an entry to SCM's ignore file to ignore the `modules` directory. 17 | 18 | * `--module`: 19 | Initializes modulo as an module. Any dependencies will be located in `..\` upon add/update. 20 | 21 | * `-v, --verbose`: 22 | Prints verbose output. Use this to see what underlying SCM commands are being used and any other important information. 23 | 24 | * `-h, --help`: 25 | Prints the help for this command. 26 | 27 | ## SEE ALSO 28 | 29 | modulo-layout(1), modulo-add(1) 30 | 31 | ## REPORTING BUGS 32 | 33 | Report bugs to the Github Project located at https://github.com/modulo-dm/modulo/. You'll be able to see the status and track any issues you create. 34 | 35 | ## AUTHORS 36 | 37 | Brandon Sneed 38 | Peat Bakke 39 | 40 | 41 | -------------------------------------------------------------------------------- /Documentation/modulo-layout.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "MODULO\-LAYOUT" "1" "July 2017" "Modulo" "Modulo manual" 5 | . 6 | .SH "NAME" 7 | \fBmodulo\-layout\fR \- Explains the file system layout 8 | . 9 | .SH "RELATIONSHIPS" 10 | Modulo understands relationships between \fImodules\fR and \fIapplications\fR\. Modules may be dependent as peers, whereas an application depends on a set of modules: 11 | . 12 | .IP "" 4 13 | . 14 | .nf 15 | 16 | Module \-> [Module, Module, \.\.\.] 17 | Application \-> [Module, Module, \.\.\.] 18 | . 19 | .fi 20 | . 21 | .IP "" 0 22 | . 23 | .P 24 | (Note: nothing can be dependent on an application) 25 | . 26 | .P 27 | As an example, let\'s consider an application named "Fancy App" that depends on some shared components and media assets\. To make it a little more complicated, one of the shared components also depends on a utility library\. 28 | . 29 | .P 30 | In other words, there are two sets of dependencies to be managed: 31 | . 32 | .IP "" 4 33 | . 34 | .nf 35 | 36 | Fancy App \-> [Component A, Component B, Assets] 37 | Component A \-> [Utilities] 38 | . 39 | .fi 40 | . 41 | .IP "" 0 42 | . 43 | .P 44 | These dependencies will get arranged directory structure as such: 45 | . 46 | .IP "" 4 47 | . 48 | .nf 49 | 50 | Fancy App/ 51 | modules/ 52 | Component A/ 53 | Component B/ 54 | Utilities/ 55 | Assets/ 56 | . 57 | .fi 58 | . 59 | .IP "" 0 60 | . 61 | .P 62 | Note that "Fancy App" is the root, and that all of the modules are checked out into the \fBmodules/\fR directory, including the \fBUtilities\fR dependency\. 63 | . 64 | .P 65 | To put a twist on this scenario, if you\'re developing on \fBComponent A\fR in isolation, and you only want to check it out with it\'s dependencies, you would end up with a directory structure like this: 66 | . 67 | .IP "" 4 68 | . 69 | .nf 70 | 71 | Component A/ 72 | Utilities/ 73 | . 74 | .fi 75 | . 76 | .IP "" 0 77 | . 78 | .P 79 | \&\.\.\. Where \fBComponent A\fR and \fBUtilities\fR are peers in the file system\. 80 | . 81 | .SH "SEE ALSO" 82 | modulo(1), modulo\-init(1), modulo\-update(1) 83 | . 84 | .SH "REPORTING BUGS" 85 | Report bugs to the Github Project located at https://github\.com/modulo\-dm/modulo/\. You\'ll be able to see the status and track any issues you create\. 86 | . 87 | .SH "AUTHORS" 88 | . 89 | .nf 90 | 91 | Brandon Sneed 92 | Peat Bakke 93 | . 94 | .fi 95 | 96 | -------------------------------------------------------------------------------- /Documentation/modulo-layout.1.md: -------------------------------------------------------------------------------- 1 | modulo-layout(1) -- Explains the file system layout 2 | ==== 3 | 4 | ## RELATIONSHIPS 5 | 6 | Modulo understands relationships between _modules_ and _applications_. Modules may be dependent as peers, whereas an application depends on a set of modules: 7 | 8 | Module -> [Module, Module, ...] 9 | Application -> [Module, Module, ...] 10 | 11 | (Note: nothing can be dependent on an application) 12 | 13 | As an example, let's consider an application named "Fancy App" that depends on some shared components and media assets. To make it a little more complicated, one of the shared components also depends on a utility library. 14 | 15 | In other words, there are two sets of dependencies to be managed: 16 | 17 | Fancy App -> [Component A, Component B, Assets] 18 | Component A -> [Utilities] 19 | 20 | These dependencies will get arranged directory structure as such: 21 | 22 | Fancy App/ 23 | modules/ 24 | Component A/ 25 | Component B/ 26 | Utilities/ 27 | Assets/ 28 | 29 | Note that "Fancy App" is the root, and that all of the modules are checked out into the `modules/` directory, including the `Utilities` dependency. 30 | 31 | To put a twist on this scenario, if you're developing on `Component A` in isolation, and you only want to check it out with it's dependencies, you would end up with a directory structure like this: 32 | 33 | Component A/ 34 | Utilities/ 35 | 36 | ... Where `Component A` and `Utilities` are peers in the file system. 37 | 38 | ## SEE ALSO 39 | 40 | modulo(1), modulo-init(1), modulo-update(1) 41 | 42 | ## REPORTING BUGS 43 | 44 | Report bugs to the Github Project located at https://github.com/modulo-dm/modulo/. You'll be able to see the status and track any issues you create. 45 | 46 | ## AUTHORS 47 | 48 | Brandon Sneed 49 | Peat Bakke 50 | 51 | 52 | -------------------------------------------------------------------------------- /Documentation/modulo-layout.1.ronn: -------------------------------------------------------------------------------- 1 | modulo-layout(1) -- Explains the file system layout 2 | ==== 3 | 4 | ## RELATIONSHIPS 5 | 6 | Modulo understands relationships between _modules_ and _applications_. Modules may be dependent as peers, whereas an application depends on a set of modules: 7 | 8 | Module -> [Module, Module, ...] 9 | Application -> [Module, Module, ...] 10 | 11 | (Note: nothing can be dependent on an application) 12 | 13 | As an example, let's consider an application named "Fancy App" that depends on some shared components and media assets. To make it a little more complicated, one of the shared components also depends on a utility library. 14 | 15 | In other words, there are two sets of dependencies to be managed: 16 | 17 | Fancy App -> [Component A, Component B, Assets] 18 | Component A -> [Utilities] 19 | 20 | These dependencies will get arranged directory structure as such: 21 | 22 | Fancy App/ 23 | modules/ 24 | Component A/ 25 | Component B/ 26 | Utilities/ 27 | Assets/ 28 | 29 | Note that "Fancy App" is the root, and that all of the modules are checked out into the `modules/` directory, including the `Utilities` dependency. 30 | 31 | To put a twist on this scenario, if you're developing on `Component A` in isolation, and you only want to check it out with it's dependencies, you would end up with a directory structure like this: 32 | 33 | Component A/ 34 | Utilities/ 35 | 36 | ... Where `Component A` and `Utilities` are peers in the file system. 37 | 38 | ## SEE ALSO 39 | 40 | modulo(1), modulo-init(1), modulo-update(1) 41 | 42 | ## REPORTING BUGS 43 | 44 | Report bugs to the Github Project located at https://github.com/modulo-dm/modulo/. You'll be able to see the status and track any issues you create. 45 | 46 | ## AUTHORS 47 | 48 | Brandon Sneed 49 | Peat Bakke 50 | 51 | 52 | -------------------------------------------------------------------------------- /Documentation/modulo-update.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "MODULO\-UPDATE" "1" "July 2017" "Modulo" "Modulo manual" 5 | . 6 | .SH "NAME" 7 | \fBmodulo\-update\fR \- Update the project based on the current set of dependencies\. 8 | . 9 | .SH "SYNOPSIS" 10 | \fBmodulo update\fR [\-\-all] 11 | . 12 | .br 13 | \fBmodulo update\fR \fIdependencyname\fR 14 | . 15 | .br 16 | . 17 | .SH "DESCRIPTION" 18 | This command updates any previously specified dependencies\. Updates can consist of the following: 19 | . 20 | .IP "\(bu" 4 21 | Cloning any dependencies that don\'t exist in the filesystem yet\. 22 | . 23 | .IP "\(bu" 4 24 | Performing a fetch on dependencies to get any new tags/branches/commits 25 | . 26 | .IP "\(bu" 4 27 | Verifying that it\'s safe to check out the specified tag/branch/commit\. 28 | . 29 | .IP "\(bu" 4 30 | Checking out the specified tag/branch/commit for each dependency\. 31 | . 32 | .IP "" 0 33 | . 34 | .SH "OPTIONS" 35 | . 36 | .TP 37 | \fB\-a, \-\-all\fR 38 | This will iterate through all dependencies and perform an update\. 39 | . 40 | .TP 41 | \fIdependencyname\fR 42 | Instructs Modulo to just perform an update on the specified dependency\. See \fBmap\fR for a list of dependencies\. 43 | . 44 | .TP 45 | `\-\-nonzero 46 | Returns a non\-zero result if modulo actually cloned dependencies\. This is useful for CI build systems\. 47 | . 48 | .TP 49 | \fB\-\-host \fR 50 | Checks for ability to connect to host name before continuing\. This allows for developers to work off\-line without being interrupted\. 51 | . 52 | .TP 53 | \fB\-\-meh\fR 54 | Update will perform a no\-op if modulo isn\'t being used on this project\. Useful for build system integration\. 55 | . 56 | .TP 57 | \fB\-v, \-\-verbose\fR 58 | Prints verbose output\. Use this to see what underlying SCM commands are being used and any other important information\. 59 | . 60 | .TP 61 | \fB\-h, \-\-help\fR 62 | Prints the help for this command\. 63 | . 64 | .SH "SEE ALSO" 65 | modulo\-layout(1), modulo\-map(1) 66 | . 67 | .SH "REPORTING BUGS" 68 | Report bugs to the Github Project located at https://github\.com/modulo\-dm/modulo/\. You\'ll be able to see the status and track any issues you create\. 69 | . 70 | .SH "AUTHORS" 71 | Brandon Sneed \fIbrandon@redf\.net\fR 72 | . 73 | .br 74 | Peat Bakke \fIpeat@peat\.org\fR 75 | . 76 | .br 77 | 78 | -------------------------------------------------------------------------------- /Documentation/modulo-update.1.md: -------------------------------------------------------------------------------- 1 | modulo-update(1) -- Update the project based on the current set of dependencies. 2 | ==== 3 | 4 | ## SYNOPSIS 5 | 6 | `modulo update` [--all]
7 | `modulo update`
8 | 9 | ## DESCRIPTION 10 | 11 | This command updates any previously specified dependencies. Updates can consist of the following: 12 | 13 | * Cloning any dependencies that don't exist in the filesystem yet. 14 | * Performing a fetch on dependencies to get any new tags/branches/commits 15 | * Verifying that it's safe to check out the specified tag/branch/commit. 16 | * Checking out the specified tag/branch/commit for each dependency. 17 | 18 | ## OPTIONS 19 | 20 | * `-a, --all`: 21 | This will iterate through all dependencies and perform an update. 22 | 23 | * : 24 | Instructs Modulo to just perform an update on the specified dependency. See `map` for a list of dependencies. 25 | 26 | * `--nonzero: 27 | Returns a non-zero result if modulo actually cloned dependencies. This is useful for CI build systems. 28 | 29 | * `--host `: 30 | Checks for ability to connect to host name before continuing. This allows for developers to work off-line without being interrupted. 31 | 32 | * `--meh`: 33 | Update will perform a no-op if modulo isn't being used on this project. Useful for build system integration. 34 | 35 | * `-v, --verbose`: 36 | Prints verbose output. Use this to see what underlying SCM commands are being used and any other important information. 37 | 38 | * `-h, --help`: 39 | Prints the help for this command. 40 | 41 | ## SEE ALSO 42 | 43 | modulo-layout(1), modulo-map(1) 44 | 45 | ## REPORTING BUGS 46 | 47 | Report bugs to the Github Project located at https://github.com/modulo-dm/modulo/. You'll be able to see the status and track any issues you create. 48 | 49 | ## AUTHORS 50 | 51 | Brandon Sneed
52 | Peat Bakke
53 | 54 | 55 | -------------------------------------------------------------------------------- /Documentation/modulo-update.1.ronn: -------------------------------------------------------------------------------- 1 | modulo-update(1) -- Update the project based on the current set of dependencies. 2 | ==== 3 | 4 | ## SYNOPSIS 5 | 6 | `modulo update` [--all]
7 | `modulo update`
8 | 9 | ## DESCRIPTION 10 | 11 | This command updates any previously specified dependencies. Updates can consist of the following: 12 | 13 | * Cloning any dependencies that don't exist in the filesystem yet. 14 | * Performing a fetch on dependencies to get any new tags/branches/commits 15 | * Verifying that it's safe to check out the specified tag/branch/commit. 16 | * Checking out the specified tag/branch/commit for each dependency. 17 | 18 | ## OPTIONS 19 | 20 | * `-a, --all`: 21 | This will iterate through all dependencies and perform an update. 22 | 23 | * : 24 | Instructs Modulo to just perform an update on the specified dependency. See `map` for a list of dependencies. 25 | 26 | * `--nonzero: 27 | Returns a non-zero result if modulo actually cloned dependencies. This is useful for CI build systems. 28 | 29 | * `--host `: 30 | Checks for ability to connect to host name before continuing. This allows for developers to work off-line without being interrupted. 31 | 32 | * `--meh`: 33 | Update will perform a no-op if modulo isn't being used on this project. Useful for build system integration. 34 | 35 | * `-v, --verbose`: 36 | Prints verbose output. Use this to see what underlying SCM commands are being used and any other important information. 37 | 38 | * `-h, --help`: 39 | Prints the help for this command. 40 | 41 | ## SEE ALSO 42 | 43 | modulo-layout(1), modulo-map(1) 44 | 45 | ## REPORTING BUGS 46 | 47 | Report bugs to the Github Project located at https://github.com/modulo-dm/modulo/. You'll be able to see the status and track any issues you create. 48 | 49 | ## AUTHORS 50 | 51 | Brandon Sneed
52 | Peat Bakke
53 | 54 | 55 | -------------------------------------------------------------------------------- /Documentation/modulo.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "MODULO" "1" "July 2017" "Modulo" "Modulo manual" 5 | . 6 | .SH "NAME" 7 | \fBmodulo\fR \- A source\-only dependency manager 8 | . 9 | .SH "SYNOPSIS" 10 | \fBmodulo\fR [\-\-version] [\-\-help] 11 | . 12 | .SH "DESCRIPTION" 13 | Modulo is a source\-only dependency manager\. Its primary goal is to orchestrate repositories and filesystem assets for large, modular projects\. For example, collections of services or libraries or components that have versioned dependencies between themselves\. 14 | . 15 | .P 16 | Modulo manages collections of dependent, versioned repositories\. It leaves build concerns to build systems (eg: Xcode, Xcodebuild, Maven, make, etc), and is designed to be agnostic about source code management systems (eg: Git, Subversion*)\. 17 | . 18 | .P 19 | A formatted and hyperlinked copy of the latest Modulo documentation can be viewed at https://github\.com/modulo\-dm/modulo 20 | . 21 | .P 22 | * only git is currently supported at the moment, others to follow 23 | . 24 | .SH "OPTIONS" 25 | . 26 | .TP 27 | \fB\-\-version\fR 28 | Prints the Modulo version number\. 29 | . 30 | .TP 31 | \fB\-h, \-\-help\fR 32 | Prints the synopsis and a list of the most commonly used commands\. 33 | . 34 | .SH "MODULO COMMANDS" 35 | We divide modulo into high level commands, each with their own \fB\-\-help\fR information\. 36 | . 37 | .TP 38 | \fBmodulo\-init(1)\fR 39 | Initialize Modulo for a given project\. 40 | . 41 | .TP 42 | \fBmodulo\-add(1)\fR 43 | Add a dependency to a given project\. 44 | . 45 | .TP 46 | \fBmodulo\-update(1)\fR 47 | Update dependencies on a given project\. 48 | . 49 | .TP 50 | \fBmodulo\-remove(1)\fR 51 | Remove dependency from a given project\. 52 | . 53 | .TP 54 | \fBmodulo\-status(1)\fR 55 | Shows the overall status of a project and it\'s dependencies\. 56 | . 57 | .TP 58 | \fBmodulo\-map(1)\fR 59 | Display a map of the dependency tree for a given project\. 60 | . 61 | .SH "FILE/DIRECTORY STRUCTURE" 62 | Please see \fBmodulo\-layout(1)\fR 63 | . 64 | .SH "REPORTING BUGS" 65 | Report bugs to the Github Project located at https://github\.com/modulo\-dm/modulo/\. You\'ll be able to see the status and track any issues you create\. 66 | . 67 | .SH "AUTHORS" 68 | Brandon Sneed \fIbrandon@redf\.net\fR 69 | . 70 | .br 71 | Peat Bakke \fIpeat@peat\.org\fR 72 | -------------------------------------------------------------------------------- /Documentation/modulo.1.md: -------------------------------------------------------------------------------- 1 | modulo(1) -- A source-only dependency manager 2 | ==== 3 | 4 | ## SYNOPSIS 5 | 6 | `modulo` [--version] [--help] 7 | 8 | ## DESCRIPTION 9 | 10 | Modulo is a source-only dependency manager. Its primary goal is to orchestrate repositories and filesystem assets for large, modular projects. For example, collections of services or libraries or components that have versioned dependencies between themselves. 11 | 12 | Modulo manages collections of dependent, versioned repositories. It leaves build concerns to build systems (eg: Xcode, Xcodebuild, Maven, make, etc), and is designed to be agnostic about source code management systems (eg: Git, Subversion*). 13 | 14 | A formatted and hyperlinked copy of the latest Modulo documentation can be viewed at https://github.com/modulo-dm/modulo 15 | 16 | \* only git is currently supported at the moment, others to follow 17 | 18 | ## OPTIONS 19 | 20 | * `--version`: 21 | Prints the Modulo version number. 22 | 23 | * `-h, --help`: 24 | Prints the synopsis and a list of the most commonly used commands. 25 | 26 | ## MODULO COMMANDS 27 | 28 | We divide modulo into high level commands, each with their own `--help` information. 29 | 30 | * `modulo-init(1)`: 31 | Initialize Modulo for a given project. 32 | 33 | * `modulo-add(1)`: 34 | Add a dependency to a given project. 35 | 36 | * `modulo-update(1)`: 37 | Update dependencies on a given project. 38 | 39 | * `modulo-remove(1)`: 40 | Remove dependency from a given project. 41 | 42 | * `modulo-status(1)`: 43 | Shows the overall status of a project and it's dependencies. 44 | 45 | * `modulo-map(1)`: 46 | Display a map of the dependency tree for a given project. 47 | 48 | ## FILE/DIRECTORY STRUCTURE 49 | 50 | Please see `modulo-layout(1)` 51 | 52 | ## REPORTING BUGS 53 | 54 | Report bugs to the Github Project located at https://github.com/modulo-dm/modulo/. You'll be able to see the status and track any issues you create. 55 | 56 | ## AUTHORS 57 | 58 | Brandon Sneed 59 | Peat Bakke 60 | 61 | 62 | -------------------------------------------------------------------------------- /Documentation/modulo.1.ronn: -------------------------------------------------------------------------------- 1 | modulo(1) -- A source-only dependency manager 2 | ==== 3 | 4 | ## SYNOPSIS 5 | 6 | `modulo` [--version] [--help] 7 | 8 | ## DESCRIPTION 9 | 10 | Modulo is a source-only dependency manager. Its primary goal is to orchestrate repositories and filesystem assets for large, modular projects. For example, collections of services or libraries or components that have versioned dependencies between themselves. 11 | 12 | Modulo manages collections of dependent, versioned repositories. It leaves build concerns to build systems (eg: Xcode, Xcodebuild, Maven, make, etc), and is designed to be agnostic about source code management systems (eg: Git, Subversion*). 13 | 14 | A formatted and hyperlinked copy of the latest Modulo documentation can be viewed at https://github.com/modulo-dm/modulo 15 | 16 | \* only git is currently supported at the moment, others to follow 17 | 18 | ## OPTIONS 19 | 20 | * `--version`: 21 | Prints the Modulo version number. 22 | 23 | * `-h, --help`: 24 | Prints the synopsis and a list of the most commonly used commands. 25 | 26 | ## MODULO COMMANDS 27 | 28 | We divide modulo into high level commands, each with their own `--help` information. 29 | 30 | * `modulo-init(1)`: 31 | Initialize Modulo for a given project. 32 | 33 | * `modulo-add(1)`: 34 | Add a dependency to a given project. 35 | 36 | * `modulo-update(1)`: 37 | Update dependencies on a given project. 38 | 39 | * `modulo-remove(1)`: 40 | Remove dependency from a given project. 41 | 42 | * `modulo-status(1)`: 43 | Shows the overall status of a project and it's dependencies. 44 | 45 | * `modulo-map(1)`: 46 | Display a map of the dependency tree for a given project. 47 | 48 | ## FILE/DIRECTORY STRUCTURE 49 | 50 | Please see `modulo-layout(1)` 51 | 52 | ## REPORTING BUGS 53 | 54 | Report bugs to the Github Project located at https://github.com/modulo-dm/modulo/. You'll be able to see the status and track any issues you create. 55 | 56 | ## AUTHORS 57 | 58 | Brandon Sneed 59 | Peat Bakke 60 | 61 | 62 | -------------------------------------------------------------------------------- /Formula/brew-howto.md: -------------------------------------------------------------------------------- 1 | - Build latest documentation via build-man.sh, commit. 2 | - Make a new tagged modulo release. 3 | - "curl -O " 4 | - update the sha256 5 | - "brew tests" -- make sure tests are installed. 6 | - "brew install --build-from-source " 7 | - "brew test " 8 | - "brew audit --strict " 9 | - PR changes to formula to homebrew-core. 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Brandon Sneed, Peat Bakke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # By default export all variables 2 | export 3 | 4 | .PHONY: install release debug build setup clean 5 | 6 | PROJECT ?= 'modulo.xcodeproj' 7 | SCHEME ?= 'modulo' 8 | SYMROOT ?= 'build' 9 | CONFIGURATION ?= 'Debug' 10 | 11 | # Build for debugging 12 | debug: build 13 | 14 | # Install `modulo` to `/usr/local/bin` 15 | install: release 16 | cp $(SYMROOT)/Release/modulo /usr/local/bin/ 17 | 18 | # Build for release 19 | release: CONFIGURATION = 'Release' 20 | release: build 21 | 22 | 23 | # Build modulo 24 | # This will build the `PROJECT` with the given `SCHEME` 25 | # to the `SYMROOT` with a given `CONFIGURATION` 26 | # Defaults for these values are 27 | # `PROJECT` - `modulo.xcodeproj` 28 | # `SCHEME` - `modulo` 29 | # `SYMROOM` - `build` 30 | # `CONFIGURATION` - `Debug` 31 | # 32 | # These can be overwritten via ENV variables. 33 | build: setup 34 | xcodebuild -project $(PROJECT) -scheme $(SCHEME) -configuration $(CONFIGURATION) SYMROOT=$(SYMROOT) 35 | 36 | # Setup the environment 37 | setup: 38 | mkdir -p $(SYMROOT) 39 | 40 | clean: 41 | rm -rfv $(SYMROOT) 42 | -------------------------------------------------------------------------------- /Modules/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modulo-dm/modulo/b401f94ffee31a9359293222e2ec09974f9f475c/Modules/.DS_Store -------------------------------------------------------------------------------- /Modules/ELCLI/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modulo-dm/modulo/b401f94ffee31a9359293222e2ec09974f9f475c/Modules/ELCLI/.DS_Store -------------------------------------------------------------------------------- /Modules/ELCLI/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | 28 | # Carthage 29 | # 30 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 31 | # Carthage/Checkouts 32 | 33 | Carthage/Build 34 | xcshareddata 35 | -------------------------------------------------------------------------------- /Modules/ELCLI/ELCLI.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Modules/ELCLI/ELCLI/Commands/HelpCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HelpCommand.swift 3 | // ELCLI 4 | // 5 | // Created by Brandon Sneed on 7/27/15. 6 | // Copyright (c) 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | open class HelpCommand: Command { 12 | fileprivate let cli: CLI 13 | 14 | open var name: String { return "--help" } 15 | open var shortHelpDescription: String { return "" } 16 | open var longHelpDescription: String { return "" } 17 | open var failOnUnrecognizedOptions: Bool { return false } 18 | 19 | open var verbose: Bool = false 20 | open var quiet: Bool = false 21 | 22 | open func configureOptions() { 23 | // do nothing 24 | } 25 | 26 | @discardableResult 27 | open func execute(_ otherParams: Array?) -> Int { 28 | write(.stdout, "usage: ") 29 | write(.stdout, "\(ProcessInfo.processInfo.processName) ") 30 | writeln(.stdout, " []\n") 31 | 32 | writeln(.stdout, "The most commonly used \(cli.appName) commands are:") 33 | 34 | let commands = cli.commands 35 | for index in 0..?) -> Int { 27 | writeln(.stdout, "\(cli.appName) version \(cli.appVersion), \(cli.appDescription)") 28 | 29 | return 0 30 | } 31 | 32 | init(cli: CLI) { 33 | self.cli = cli 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Modules/ELCLI/ELCLI/ELCLI.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELCLI.h 3 | // ELCLI 4 | // 5 | // Created by Brandon Sneed on 7/27/15. 6 | // Copyright (c) 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ELCLI. 12 | FOUNDATION_EXPORT double ELCLIVersionNumber; 13 | 14 | //! Project version string for ELCLI. 15 | FOUNDATION_EXPORT const unsigned char ELCLIVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Modules/ELCLI/ELCLI/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015 WalmartLabs. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Modules/ELCLI/ELCLI/Options.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Options.swift 3 | // ELCLI 4 | // 5 | // Created by Brandon Sneed on 7/27/15. 6 | // Copyright (c) 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias OptionClosure = (_ option: String?, _ value: String?) -> Void 12 | 13 | public struct Option { 14 | let usage: String? 15 | let flags: Array? 16 | let valueSignatures: Array? 17 | let closure: OptionClosure 18 | 19 | init(flags: Array?, usage: String?, valueSignatures: Array?, closure: @escaping OptionClosure) { 20 | self.flags = flags 21 | self.usage = usage 22 | self.valueSignatures = valueSignatures 23 | self.closure = closure 24 | } 25 | 26 | init(flags: Array?, usage: String?, closure: @escaping OptionClosure) { 27 | self.flags = flags 28 | self.usage = usage 29 | self.valueSignatures = nil 30 | self.closure = closure 31 | } 32 | 33 | init(valueSignatures: Array?, closure: @escaping OptionClosure) { 34 | self.flags = nil 35 | self.usage = nil 36 | self.valueSignatures = valueSignatures 37 | self.closure = closure 38 | } 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Modules/ELCLI/ELCLI/Output.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Output.swift 3 | // ELCLI 4 | // 5 | // Created by Brandon Sneed on 8/13/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if NOFRAMEWORKS 12 | #else 13 | import ELFoundation 14 | #endif 15 | 16 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 17 | // Consider refactoring the code to use the non-optional operators. 18 | fileprivate func < (lhs: T?, rhs: T?) -> Bool { 19 | switch (lhs, rhs) { 20 | case let (l?, r?): 21 | return l < r 22 | case (nil, _?): 23 | return true 24 | default: 25 | return false 26 | } 27 | } 28 | 29 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 30 | // Consider refactoring the code to use the non-optional operators. 31 | fileprivate func > (lhs: T?, rhs: T?) -> Bool { 32 | switch (lhs, rhs) { 33 | case let (l?, r?): 34 | return l > r 35 | default: 36 | return rhs < lhs 37 | } 38 | } 39 | 40 | public enum Output { 41 | case stdin 42 | case stdout 43 | case stderr 44 | 45 | func fileHandle() -> FileHandle { 46 | switch self { 47 | case .stdin: 48 | return FileHandle.standardInput 49 | case .stderr: 50 | return FileHandle.standardError 51 | case .stdout: 52 | fallthrough 53 | default: 54 | return FileHandle.standardOutput 55 | } 56 | } 57 | } 58 | 59 | public func write(_ destination: Output, _ data: String) { 60 | var str = data 61 | var finalDestination = destination 62 | if destination == .stderr { 63 | str = "error: " + data 64 | if isInUnitTest() { 65 | // if we're debugging unit tests, data won't show if it's spit otu to stderr. 66 | finalDestination = .stdout 67 | } 68 | } 69 | 70 | if let outputData = str.data(using: String.Encoding.utf8) { 71 | finalDestination.fileHandle().write(outputData) 72 | } 73 | } 74 | 75 | public func writeln(_ destination: Output, _ data: String) { 76 | write(destination, data + "\n") 77 | } 78 | 79 | public func exitSuccess() { 80 | if isInUnitTest() { 81 | exceptionFailure("") 82 | } else { 83 | exit(0) 84 | } 85 | } 86 | 87 | public func exit(_ data: String, closure: (() -> Void)? = nil) { 88 | writeln(.stderr, data) 89 | if let closure = closure { 90 | closure() 91 | } 92 | 93 | if isInUnitTest() { 94 | exceptionFailure(data) 95 | } else { 96 | exit(1) 97 | } 98 | } 99 | 100 | public func printOption(_ option: Option) { 101 | if option.flags == nil || option.valueSignatures?.count > 1 { 102 | return 103 | } 104 | 105 | var flagData = " " 106 | 107 | if let flags = option.flags { 108 | for index in 0.. 0 { 119 | flagData += " " + sigs[0] 120 | } 121 | } 122 | 123 | flagData = flagData.padBack(26) 124 | 125 | let usageData = option.usage! 126 | 127 | if flagData.count > 26 { 128 | flagData += "\n" 129 | flagData += usageData.padFront(27 + usageData.count) 130 | } else { 131 | flagData += " " + usageData 132 | } 133 | 134 | writeln(.stdout, flagData) 135 | 136 | } 137 | 138 | public func printCommand(_ command: Command) { 139 | if command.name.hasPrefix("-") { 140 | return 141 | } 142 | 143 | var commandData = " " 144 | 145 | commandData += command.name.padBack(14) 146 | commandData += " " + command.shortHelpDescription 147 | 148 | writeln(.stdout, commandData) 149 | } 150 | -------------------------------------------------------------------------------- /Modules/ELCLI/ELCLITests/CommitCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommitCommand.swift 3 | // ELCLI 4 | // 5 | // Created by Brandon Sneed on 8/12/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ELCLI 11 | 12 | public class CommitCommand: NSObject, Command { 13 | // Internal properties 14 | public var all: Bool = false 15 | public var patch: Bool = false 16 | public var commit: String? = nil 17 | public var message: String? = nil 18 | 19 | public var nonFlagValues = Array() 20 | 21 | // Protocol conformance 22 | public var name: String { return "commit" } 23 | public var helpDescription: String { return "Record changes to the repository" } 24 | public var failOnUnrecognizedOptions: Bool { return true } 25 | 26 | public var verbose: Bool = false 27 | public var quiet: Bool = false 28 | 29 | public func configureOptions() { 30 | addOption(["-a", "--all"], usage: "commit all changed files") { (option, value) -> Void in 31 | self.all = true 32 | } 33 | 34 | addOption(["-p", "--patch"], usage: "interactively add changes") { (option, value) -> Void in 35 | self.patch = true 36 | } 37 | 38 | addOptionValue(["-c", "-C", "--commit"], usage: "reuse message from specified commit", valueSignature: "") { (option, value) -> Void in 39 | self.commit = value 40 | } 41 | 42 | addOptionValue(["-m", "--message"], usage: "commit message", valueSignature: "") { (option, value) -> Void in 43 | self.message = value 44 | } 45 | 46 | addFlaglessOptionValues(["", ""]) { (option, value) -> Void in 47 | if let value = value { 48 | self.nonFlagValues.append(value) 49 | } 50 | } 51 | } 52 | 53 | public func execute(otherParams: Array?) -> CLIResult { 54 | var result = CLIResult() 55 | 56 | result.resultCode = 0 57 | result.resultDescription = "Success" 58 | result.executedCommand = self 59 | 60 | return result 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Modules/ELCLI/ELCLITests/ELCLITests-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "XCTestCase+Exceptions.h" -------------------------------------------------------------------------------- /Modules/ELCLI/ELCLITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Modules/ELCLI/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 WalmartLabs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Modules/ELCLI/README.md: -------------------------------------------------------------------------------- 1 | # ELCLI 2 | 3 | A framework for parsing command lines in Swift. 4 | 5 | ## Requirements 6 | 7 | ELCLI requires Swift 2.1, Xcode 7.2 and depends on [`ELFoundation.framework`](https://github.com/Electrode-iOS/ELFoundation). 8 | 9 | [Electrode-iOS](https://github.com/Electrode-iOS/) frameworks are designed to live side-by-side in the file system, like so: 10 | 11 | * \MyProject 12 | * \MyProject\ELFoundation 13 | 14 | ## Installation 15 | 16 | Install by adding `ELCLI.xcodeproj` to your project and configuring your target to link `ELCLI.framework`. 17 | 18 | ## Usage 19 | 20 | TODO 21 | -------------------------------------------------------------------------------- /Modules/ELCodable/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcscmblueprint 19 | *.xcsettings 20 | *.xcuserstate 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /Modules/ELCodable/.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode8 3 | script: 4 | - xcodebuild -project ELCodable.xcodeproj -scheme ELCodable -sdk iphonesimulator test -destination 'platform=iOS Simulator,name=iPhone 6,OS=10.0' 5 | -------------------------------------------------------------------------------- /Modules/ELCodable/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [2.0.0](https://github.com/Electrode-iOS/ELCodable/releases/tag/v2.0.0) 2 | 3 | ## New Features 4 | 5 | - Added subscript to retrieve values for a given key index or array indexes. 6 | - Added an OS X target. 7 | - Made `data()` public. 8 | 9 | ## Removals 10 | 11 | - Remove return values from compound operators 12 | - Remove extra `inout` from `*` operator 13 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable/Decimal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Decimal.swift 3 | // Decimal 4 | // 5 | // Created by Brandon Sneed on 11/7/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Decimal { 12 | public var value: NSDecimalNumber { 13 | get { 14 | return (self as NSDecimalNumber) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable/Decodable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Decodable.swift 3 | // Codable 4 | // 5 | // Created by Brandon Sneed on 11/2/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum ELDecodeError: Error { 12 | case emptyJSON 13 | case undecodable 14 | case validationUnimplemented 15 | case validationFailed 16 | case notFound(key: String) 17 | } 18 | 19 | public protocol ELDecodable { 20 | static func decode(_ json: JSON?) throws -> Self 21 | func validate() throws -> Self 22 | } 23 | 24 | public extension ELDecodable { 25 | func validate() throws -> Self { 26 | // do nothing. user to override. 27 | throw ELDecodeError.validationUnimplemented 28 | //return self 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable/DecodableExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DecodableExtensions.swift 3 | // Codable 4 | // 5 | // Created by Brandon Sneed on 11/4/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String: ELDecodable { 12 | public static func decode(_ json: JSON?) throws -> String { 13 | if let value = json?.string { 14 | return value 15 | } 16 | if json != nil { 17 | throw ELDecodeError.undecodable 18 | } else { 19 | throw ELDecodeError.emptyJSON 20 | } 21 | } 22 | } 23 | 24 | extension Float: ELDecodable { 25 | public static func decode(_ json: JSON?) throws -> Float { 26 | if let value = json?.float { 27 | return value 28 | } 29 | throw ELDecodeError.undecodable 30 | } 31 | } 32 | 33 | extension Double: ELDecodable { 34 | public static func decode(_ json: JSON?) throws -> Double { 35 | if let value = json?.double { 36 | return value 37 | } 38 | throw ELDecodeError.undecodable 39 | } 40 | } 41 | 42 | extension Int: ELDecodable { 43 | public static func decode(_ json: JSON?) throws -> Int { 44 | if let value = json?.int { 45 | return value 46 | } 47 | throw ELDecodeError.undecodable 48 | } 49 | } 50 | 51 | extension Int64: ELDecodable { 52 | public static func decode(_ json: JSON?) throws -> Int64 { 53 | if let value = json?.int64 { 54 | return value 55 | } 56 | throw ELDecodeError.undecodable 57 | } 58 | } 59 | 60 | extension UInt: ELDecodable { 61 | public static func decode(_ json: JSON?) throws -> UInt { 62 | if let value = json?.uInt { 63 | return value 64 | } 65 | throw ELDecodeError.undecodable 66 | } 67 | } 68 | 69 | extension UInt64: ELDecodable { 70 | public static func decode(_ json: JSON?) throws -> UInt64 { 71 | if let value = json?.uInt64 { 72 | return value 73 | } 74 | throw ELDecodeError.undecodable 75 | } 76 | } 77 | 78 | extension Bool: ELDecodable { 79 | public static func decode(_ json: JSON?) throws -> Bool { 80 | if let value = json?.bool { 81 | return value 82 | } 83 | throw ELDecodeError.undecodable 84 | } 85 | } 86 | 87 | extension Decimal: ELDecodable { 88 | public static func decode(_ json: JSON?) throws -> Decimal { 89 | if let value = json?.decimal { 90 | return value.decimalValue 91 | } 92 | throw ELDecodeError.undecodable 93 | } 94 | } 95 | 96 | extension Array where Element: ELDecodable { 97 | public static func decode(_ json: JSON?) throws -> [Element] { 98 | guard let items = json?.array else { 99 | throw ELDecodeError.undecodable 100 | } 101 | 102 | var decodedItems = [Element]() 103 | 104 | for item in items { 105 | let decodedItem = try Element.decode(item) 106 | decodedItems.append(decodedItem) 107 | } 108 | 109 | return decodedItems 110 | } 111 | } 112 | 113 | extension Dictionary where Key: ExpressibleByStringLiteral, Value: ELDecodable { 114 | public static func decode(_ json: JSON?) throws -> [String: JSON] { 115 | guard let value = json?.dictionary else { 116 | throw ELDecodeError.undecodable 117 | } 118 | 119 | return value 120 | } 121 | } 122 | 123 | 124 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable/DecodeOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DecodeOperators.swift 3 | // Codable 4 | // 5 | // Created by Brandon Sneed on 11/3/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | //infix operator ==> { associativity right precedence 150 } 12 | 13 | infix operator ==> : DecodingPrecedence 14 | 15 | precedencegroup DecodingPrecedence { 16 | associativity: right 17 | higherThan: CastingPrecedence 18 | } 19 | 20 | public func ==> (lhs: JSON?, rhs: String) throws -> T { 21 | guard let json = lhs else { 22 | throw ELDecodeError.emptyJSON 23 | } 24 | 25 | do { 26 | let value: T? = try? T.decode(json[rhs]) 27 | if let value = value { 28 | return value 29 | } else { 30 | throw ELDecodeError.notFound(key: rhs) 31 | } 32 | } catch let error { 33 | throw error 34 | } 35 | } 36 | 37 | public func ==> (lhs: JSON?, rhs: String) throws -> [T] { 38 | guard let json = lhs else { 39 | throw ELDecodeError.emptyJSON 40 | } 41 | 42 | guard let array = json[rhs]?.array else { 43 | throw ELDecodeError.notFound(key: rhs) 44 | } 45 | 46 | var results = [T]() 47 | 48 | for json in array { 49 | // will throw a NotFound() if this decode fails. 50 | let value = try T.decode(json) 51 | results.append(value) 52 | } 53 | 54 | return results 55 | } 56 | 57 | public func ==> (lhs: JSON?, rhs: String) throws -> T? { 58 | guard let json = lhs else { 59 | throw ELDecodeError.emptyJSON 60 | } 61 | 62 | let value = try? T.decode(json[rhs]) 63 | if let value = value { 64 | return value 65 | } else { 66 | return nil 67 | } 68 | } 69 | 70 | public func ==> (lhs: JSON?, rhs: String) throws -> [T]? { 71 | guard let json = lhs else { 72 | throw ELDecodeError.emptyJSON 73 | } 74 | 75 | guard let array = json[rhs]?.array else { 76 | return nil 77 | } 78 | 79 | var results = [T]() 80 | 81 | for json in array { 82 | if let value = try? T.decode(json) { 83 | results.append(value) 84 | } 85 | } 86 | 87 | return results 88 | } 89 | 90 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable/ELCodable.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELCodable.h 3 | // ELCodable 4 | // 5 | // Created by Brandon Sneed on 11/12/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ELCodable. 12 | FOUNDATION_EXPORT double ELCodableVersionNumber; 13 | 14 | //! Project version string for ELCodable. 15 | FOUNDATION_EXPORT const unsigned char ELCodableVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable/Encodable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Encodable.swift 3 | // Codable 4 | // 5 | // Created by Brandon Sneed on 11/9/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum ELEncodeError: Error { 12 | case unencodable 13 | case validationUnumplemented 14 | case validationFailed 15 | } 16 | 17 | public protocol ELEncodable { 18 | func encode() throws -> JSON 19 | } 20 | 21 | public typealias ELEncodeFormat = Array<(String, JSON)> 22 | 23 | public extension ELEncodable { 24 | func validateEncode() throws -> Self { 25 | // do nothing. user to override. 26 | throw ELEncodeError.validationUnumplemented 27 | } 28 | 29 | func encodeToJSON(_ format: ELEncodeFormat) throws -> JSON { 30 | var json = JSON() 31 | for tuple in format { 32 | json[tuple.0] = tuple.1 33 | } 34 | return json 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable/EncodableExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EncodableExtensions.swift 3 | // Codable 4 | // 5 | // Created by Brandon Sneed on 11/10/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String: ELEncodable { 12 | public func encode() throws -> JSON { 13 | return JSON(self as AnyObject?) 14 | } 15 | } 16 | 17 | extension Float: ELEncodable { 18 | public func encode() throws -> JSON { 19 | return JSON(self as AnyObject?) 20 | } 21 | } 22 | 23 | extension Double: ELEncodable { 24 | public func encode() throws -> JSON { 25 | return JSON(self as AnyObject?) 26 | } 27 | } 28 | 29 | extension Int: ELEncodable { 30 | public func encode() throws -> JSON { 31 | return JSON(self as AnyObject?) 32 | } 33 | } 34 | 35 | extension Int64: ELEncodable { 36 | public func encode() throws -> JSON { 37 | return JSON(NSNumber(value: self as Int64)) 38 | } 39 | } 40 | 41 | extension UInt: ELEncodable { 42 | public func encode() throws -> JSON { 43 | return JSON(self as AnyObject?) 44 | } 45 | } 46 | 47 | extension UInt64: ELEncodable { 48 | public func encode() throws -> JSON { 49 | return JSON(NSNumber(value: self as UInt64)) 50 | } 51 | } 52 | 53 | extension Bool: ELEncodable { 54 | public func encode() throws -> JSON { 55 | return JSON(self as AnyObject?) 56 | } 57 | } 58 | 59 | extension Decimal: ELEncodable { 60 | public func encode() throws -> JSON { 61 | return JSON(self.value) 62 | } 63 | } 64 | 65 | extension Array where Element: ELEncodable { 66 | public func encode() throws -> JSON { 67 | var array = [Any]() 68 | for item in self { 69 | let jsonItem = try item.encode() 70 | if let object = jsonItem.object { 71 | array.append(object) 72 | } 73 | } 74 | return JSON(array as AnyObject?) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable/EncodeOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EncodeOperators.swift 3 | // Codable 4 | // 5 | // Created by Brandon Sneed on 11/10/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | //infix operator <== { associativity right precedence 150 } 12 | 13 | infix operator <== : ELEncodingPrecedence 14 | 15 | precedencegroup ELEncodingPrecedence { 16 | associativity: right 17 | higherThan: CastingPrecedence 18 | } 19 | 20 | public func <== (lhs: String, rhs: T) throws -> (String, JSON) { 21 | let value = try? rhs.encode() 22 | if let value = value { 23 | return (lhs, value) 24 | } else { 25 | throw ELEncodeError.unencodable 26 | } 27 | } 28 | 29 | public func <== (lhs: String, rhs: [T]) throws -> (String, JSON) { 30 | let value = try? rhs.encode() 31 | if let value = value { 32 | return (lhs, value) 33 | } else { 34 | throw ELEncodeError.unencodable 35 | } 36 | } 37 | 38 | public func <== (lhs: String, rhs: T?) throws -> (String, JSON) { 39 | if rhs == nil { 40 | return (lhs, JSON()) 41 | } 42 | 43 | let value = try? rhs?.encode() 44 | if let value = value { 45 | return (lhs, value!) 46 | } else { 47 | return (lhs, JSON()) 48 | } 49 | } 50 | 51 | public func <== (lhs: String, rhs: [T]?) throws -> (String, JSON) { 52 | if rhs == nil { 53 | return (lhs, JSON()) 54 | } 55 | 56 | let value = try? rhs?.encode() 57 | if let value = value { 58 | return (lhs, value!) 59 | } else { 60 | return (lhs, JSON()) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodableTests/DecimalTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DecimalTests.swift 3 | // ELCodable 4 | // 5 | // Created by Brandon Sneed on 1/15/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELCodable 11 | 12 | class DecimalTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testEquality() { 25 | let doubleValue: Double = 264.91 26 | let jsonValue = JSON(doubleValue) 27 | 28 | let subtotal = Decimal(jsonValue.decimal!) 29 | 30 | XCTAssertEqual(subtotal, Decimal(doubleValue)) 31 | } 32 | 33 | } 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodableTests/DynamicKeyTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicKeyTest.swift 3 | // ELCodable 4 | // 5 | // Created by Brandon Sneed on 7/21/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELCodable 11 | 12 | struct MyData { 13 | let availableInStore: Int 14 | let format: String 15 | let identifier: String 16 | let location: LocationData 17 | let name: String 18 | let packagePrice: Decimal 19 | let unitPrice: Decimal 20 | } 21 | 22 | extension MyData: Decodable { 23 | static func decode(json: JSON?) throws -> MyData { 24 | let buriedJson = json?["data"]?[0] 25 | 26 | return try MyData( 27 | availableInStore: buriedJson ==> "availabilityInStore", 28 | format: buriedJson ==> "format", 29 | identifier: buriedJson ==> "identifier", 30 | location: buriedJson ==> "location", 31 | name: buriedJson ==> "name", 32 | packagePrice: buriedJson ==> "packagePrice", 33 | unitPrice: buriedJson ==> "unitPrice" 34 | ) 35 | } 36 | } 37 | 38 | struct LocationData { 39 | let aisle: String 40 | let section: String 41 | let zone: String 42 | } 43 | 44 | extension LocationData: Decodable { 45 | static func decode(json: JSON?) throws -> LocationData { 46 | return try LocationData( 47 | aisle: json ==> "aisle", 48 | section: json ==> "section", 49 | zone: json ==> "zone" 50 | ) 51 | } 52 | } 53 | 54 | 55 | 56 | class DynamicKeyTest: XCTestCase { 57 | 58 | override func setUp() { 59 | super.setUp() 60 | // Put setup code here. This method is called before the invocation of each test method in the class. 61 | } 62 | 63 | override func tearDown() { 64 | // Put teardown code here. This method is called after the invocation of each test method in the class. 65 | super.tearDown() 66 | } 67 | 68 | func testRandomKeyExtraction() { 69 | guard let json = JSON(bundleClass: ELCodableTests.self, filename: "DynamicKeyedData.json") else { 70 | assertionFailure("the json is missing.") 71 | return 72 | } 73 | 74 | var thrownError: ErrorType? = nil 75 | 76 | do { 77 | let model = try MyData.decode(json) 78 | XCTAssertTrue(model.availableInStore == 15) 79 | XCTAssertTrue(model.format == "EAN13") 80 | XCTAssertTrue(model.location.zone == "A") 81 | XCTAssertTrue(model.unitPrice == Decimal(3.32)) 82 | } catch let error { 83 | thrownError = error 84 | } 85 | 86 | XCTAssertTrue(thrownError == nil) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodableTests/DynamicKeyedData.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "data": { 4 | "5260": { 5 | "availabilityInStore": 15, 6 | "format": "EAN13", 7 | "identifier": "0078742079905", 8 | "location": { 9 | "aisle": "3", 10 | "section": "20", 11 | "zone": "A" 12 | }, 13 | "name": "GV N-STIR CRMY 26.5Z", 14 | "packagePrice": 3.32, 15 | "unitPrice": 3.32 16 | } 17 | }, 18 | "meta": { 19 | "isExtruded": true, 20 | "pluginVersion": "v1.0.0" 21 | } 22 | } -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodableTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodableTests/JSONTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONTests.swift 3 | // Codable 4 | // 5 | // Created by Brandon Sneed on 10/27/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ELCodable 11 | 12 | class JSONTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testReadingFromJSON() { 25 | let json = JSON(bundleClass: ELCodableTests.self, filename: "jsontest_models.json") 26 | 27 | // types 28 | /* 29 | case Number 30 | case String 31 | case Bool 32 | case Array 33 | case Dictionary 34 | case Null 35 | case Unknown 36 | */ 37 | 38 | XCTAssertTrue(json?["mystring"]?.type == .String) 39 | XCTAssertTrue(json?["decimalNumber"]?.type == .Number) 40 | XCTAssertTrue(json?["bool"]?.type == .Bool) 41 | XCTAssertTrue(json?["double"]?.type == .Number) 42 | XCTAssertTrue(json?["int"]?.type == .Number) 43 | XCTAssertTrue(json?["myarray1"]?.type == .Array) 44 | XCTAssertTrue(json?["mydictionary"]?.type == .Dictionary) 45 | XCTAssertTrue(json?["null"]?.type == .Null) 46 | } 47 | 48 | func testWritingToJSON() { 49 | let dictData = ["key1": "value1", "key2": 1234] 50 | let arrayData = ["1", "2", "3", "4"] 51 | let stringData = "true" 52 | let numberData = 123456789 53 | 54 | var json = JSON() 55 | json["stringData"] = JSON(stringData) 56 | json["numberData"] = JSON(numberData) 57 | json["arrayData"] = JSON(arrayData) 58 | json["dictData"] = JSON(dictData) 59 | 60 | print(json) 61 | } 62 | 63 | func testCollectionStuff() { 64 | let dictData = ["key1": "value1", "key2": 1234] 65 | let arrayData = ["1", "2", "3", "4"] 66 | let stringData = "true" 67 | let numberData = 123456789 68 | 69 | var json = JSON() 70 | json["stringData"] = JSON(stringData) 71 | json["numberData"] = JSON(numberData) 72 | json["arrayData"] = JSON(arrayData) 73 | json["dictData"] = JSON(dictData) 74 | 75 | XCTAssertTrue(json["arrayData"]!.array! == JSON(arrayData).array!) 76 | XCTAssertTrue(json["dictData"]!.dictionary! == JSON(dictData).dictionary!) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodableTests/KeyPathTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyPathTests.swift 3 | // ELCodable 4 | // 5 | // Created by Brandon Sneed on 3/15/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELCodable 11 | 12 | struct VersionModel { 13 | let minVersion: String 14 | let url: String 15 | let version: String 16 | } 17 | 18 | extension VersionModel: Decodable { 19 | static func decode(json: JSON?) throws -> VersionModel { 20 | return try VersionModel( 21 | minVersion: json?["appVersion"]?["iOS"] ==> "minVersion", 22 | url: json?["appVersion"]?["iOS"] ==> "url", 23 | version: json?["appVersion"]?["iOS"] ==> "version" 24 | ) 25 | } 26 | } 27 | 28 | class KeyPathTests: XCTestCase { 29 | 30 | override func setUp() { 31 | super.setUp() 32 | // Put setup code here. This method is called before the invocation of each test method in the class. 33 | } 34 | 35 | override func tearDown() { 36 | // Put teardown code here. This method is called after the invocation of each test method in the class. 37 | super.tearDown() 38 | } 39 | 40 | func testUglyKeyPath() { 41 | guard let json = JSON(bundleClass: ELCodableTests.self, filename: "jsontest_models.json") else { 42 | assertionFailure("the json is missing.") 43 | return 44 | } 45 | 46 | var thrownError: ErrorType? = nil 47 | 48 | do { 49 | let model = try VersionModel.decode(json) 50 | XCTAssertTrue(model.minVersion == "0.1.4") 51 | XCTAssertTrue(model.url == "https://walmart.com/change-me") 52 | XCTAssertTrue(model.version == "0.1.4") 53 | } catch let error { 54 | thrownError = error 55 | } 56 | 57 | XCTAssertTrue(thrownError == nil) 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodableTests/NestedErrorTesting.json: -------------------------------------------------------------------------------- 1 | { 2 | "description" : "Walmart", 3 | "iD" : "2280", 4 | "hoursOfOperation" : [ 5 | { 6 | "day" : "Sun - Sat", 7 | "time" : "07:00AM - 10:00PM" 8 | } 9 | ], 10 | "phone" : "(650) 917-0796", 11 | "storeNumber" : "2280", 12 | "storeType" : "2", 13 | "storeServices" : [ 14 | ], 15 | "longitude" : "-122.10944299", 16 | "storeClosedTemporarily" : "false", 17 | "latitude" : "37.40088999", 18 | "mscoEnabled" : "false", 19 | "rxFiltered" : "false", 20 | "openDate" : "11\/1\/1995", 21 | "address" : { 22 | "registry" : "false", 23 | "country" : "United States", 24 | "state" : "CA", 25 | "city" : "Mountain View", 26 | "street1" : "600 Showers Dr", 27 | "zip" : "94040" 28 | }, 29 | "reOpenDate" : "1\/12\/2016", 30 | "localAdAvailable" : "true", 31 | "availableForS2S" : "true", 32 | "storeOpeningSoon" : "false" 33 | } 34 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodableTests/NestedErrorTesting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NestedErrorTesting.swift 3 | // ELCodable 4 | // 5 | // Created by Brandon Sneed on 1/12/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELCodable 11 | import CoreLocation 12 | 13 | // MARK: WMSNGAddress 14 | 15 | struct WMSNGAddress: Equatable { 16 | let street1: String 17 | let street2: String? 18 | let city: String 19 | let state: String 20 | let zip: String 21 | let country: String 22 | } 23 | 24 | func ==(lhs: WMSNGAddress, rhs: WMSNGAddress) -> Bool { 25 | return lhs.street1 == rhs.street1 && 26 | lhs.street2 == rhs.street2 && 27 | lhs.city == rhs.city && 28 | lhs.state == rhs.state && 29 | lhs.zip == rhs.zip && 30 | lhs.country == rhs.country 31 | } 32 | 33 | extension WMSNGAddress: Decodable { 34 | static func decode(json: JSON?) throws -> WMSNGAddress { 35 | return try WMSNGAddress(street1: json ==> "street1", 36 | street2: json ==> "street2", 37 | city: json ==> "cityjij", 38 | state: json ==> "state", 39 | zip: json ==> "zip", 40 | country: json ==> "country") 41 | } 42 | 43 | func validate() throws -> WMSNGAddress { 44 | // Possibly validate the data here 45 | return self 46 | } 47 | } 48 | 49 | // MARK: WMSNGStore 50 | 51 | struct WMSNGStore: Equatable { 52 | let storeId: String 53 | let location: CLLocation 54 | let phone: String 55 | let description: String 56 | let address: WMSNGAddress 57 | } 58 | 59 | func ==(lhs: WMSNGStore, rhs: WMSNGStore) -> Bool { 60 | return lhs.storeId == rhs.storeId && 61 | lhs.location == rhs.location && 62 | lhs.phone == rhs.phone && 63 | lhs.description == rhs.description && 64 | lhs.address == rhs.address 65 | } 66 | 67 | extension WMSNGStore: Decodable { 68 | static func decode(json: JSON?) throws -> WMSNGStore { 69 | let store = try WMSNGStore(storeId: json ==> "storeNumber", 70 | location: CLLocation(latitude: json ==> "latitude", longitude: json ==> "longitude"), 71 | phone: json ==> "phone", 72 | description: json ==> "description", 73 | address: json ==> "address" 74 | ) 75 | return store 76 | } 77 | 78 | func validate() throws -> WMSNGStore { 79 | // Possibly validate the data here 80 | return self 81 | } 82 | } 83 | 84 | 85 | class NestedErrorTesting: XCTestCase { 86 | 87 | override func setUp() { 88 | super.setUp() 89 | // Put setup code here. This method is called before the invocation of each test method in the class. 90 | } 91 | 92 | override func tearDown() { 93 | // Put teardown code here. This method is called after the invocation of each test method in the class. 94 | super.tearDown() 95 | } 96 | 97 | func testNestedFailure() { 98 | 99 | let json = JSON(bundleClass: NestedErrorTesting.self, filename: "NestedErrorTesting.json") 100 | 101 | var thrownError: ErrorType? = nil 102 | 103 | do { 104 | let store = try WMSNGStore.decode(json) 105 | print(store) 106 | } catch DecodeError.EmptyJSON { 107 | print("JSON was empty.") 108 | } catch let error { 109 | thrownError = error 110 | } 111 | 112 | XCTAssertTrue(thrownError != nil) 113 | XCTAssertTrue(thrownError.debugDescription == "Optional(ELCodable.DecodeError.NotFound(\"address\"))") 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodableTests/OptionalTests.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "storeId": 2280, 4 | "items": [ 5 | { 6 | "format": "UPC", 7 | "identifier": "887276062013", 8 | "quantity": 1, 9 | "itemId": 554069138, 10 | "departmentId": 72, 11 | "subCategoryId": 42, 12 | "name": "SAM 55 LED 55J6200", 13 | "normalizedUpc": "88727606201", 14 | "unitPrice": 748, 15 | "upc": "88727606201", 16 | "packagePrice": 748, 17 | "isWeighted": false, 18 | "price": 748, 19 | "isProductFee": false, 20 | "taxRates": { 21 | "tax1": 8.75 22 | }, 23 | "tax": 65.45 24 | }, 25 | { 26 | "format" : "UPC", 27 | "identifier": "0068113180661", 28 | "quantity": 1, 29 | "itemId": 593908, 30 | "departmentId": 72, 31 | "subCategoryId": 85, 32 | "name": "CA RECYCLE FEE", 33 | "normalizedUpc": "68113180661", 34 | "unitPrice": 5, 35 | "upc": "0068113180661", 36 | "packagePrice": 5, 37 | "isWeighted": false, 38 | "price": 5, 39 | "isProductFee": true, 40 | "taxRates": { 41 | "tax12": 0 42 | }, 43 | "tax": 0 44 | } 45 | ], 46 | "subtotal": 748, 47 | "productFees": 5, 48 | "tax": 65.45, 49 | "taxAmounts": { 50 | "tax1": 65.45, 51 | "tax12": 0 52 | }, 53 | "total": 818.45 54 | } 55 | } -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodableTests/OptionalTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OptionalTests.swift 3 | // ELCodable 4 | // 5 | // Created by Brandon Sneed on 2/23/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELCodable 11 | 12 | struct Data { 13 | var cart: Cart 14 | } 15 | 16 | extension Data: Decodable { 17 | static func decode(json: JSON?) throws -> Data { 18 | return try Data( 19 | cart: json ==> "data" 20 | ) 21 | } 22 | } 23 | 24 | struct Cart { 25 | var total: Decimal 26 | var clientTransactionId: String? 27 | var recordSaleTransactionId: String? 28 | var approvalNumber: String? 29 | var authorizerId: String? 30 | } 31 | 32 | extension Cart: Decodable { 33 | static func decode(json: JSON?) throws -> Cart { 34 | return try Cart( 35 | total: json ==> "total", 36 | clientTransactionId: json ==> "clientTransactionId", 37 | recordSaleTransactionId: json ==> "recordSaleTransactionId", 38 | approvalNumber: json ==> "approvalNumber", 39 | authorizerId: json ==> "authorizerId" 40 | ) 41 | } 42 | } 43 | 44 | class OptionalTests: XCTestCase { 45 | 46 | override func setUp() { 47 | super.setUp() 48 | // Put setup code here. This method is called before the invocation of each test method in the class. 49 | } 50 | 51 | override func tearDown() { 52 | // Put teardown code here. This method is called after the invocation of each test method in the class. 53 | super.tearDown() 54 | } 55 | 56 | func testBasicOptionals() { 57 | /*guard let json = JSON(bundleClass: ELCodableTests.self, filename: "OptionalTests.json") else { 58 | assertionFailure("the json is missing.") 59 | return 60 | } 61 | 62 | var model: Cart? = nil 63 | 64 | guard let json = JSON(responseJson) else { 65 | 66 | } 67 | 68 | guard let data = json["data"] 69 | 70 | guard let jsonDict = json.object as? NSDictionary else { 71 | assertionFailure("the json is jacked up.") 72 | return 73 | } 74 | 75 | guard let data = jsonDict["data"] as? NSDictionary else { 76 | assertionFailure("the data object is missing") 77 | return 78 | } 79 | 80 | var thrownError: ErrorType? = nil 81 | 82 | do { 83 | model = try Cart.decode(JSON(data)) 84 | } catch let error { 85 | thrownError = error 86 | } 87 | 88 | if thrownError != nil { 89 | print(thrownError) 90 | } 91 | 92 | XCTAssertTrue(model != nil, "Cart model is nil!") 93 | XCTAssertTrue(model?.total == Decimal(818.45), "Total doesn't have the right value!") 94 | XCTAssertTrue(model?.clientTransactionId == nil)*/ 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodableTests/jsontest_models.json: -------------------------------------------------------------------------------- 1 | { 2 | "mystring": "hello", 3 | "myuint": 1234, 4 | "mynumber": 3.14, 5 | "myarray1": [ 6 | "1", 7 | "2", 8 | "3", 9 | "4" 10 | ], 11 | "myarray2": [ 12 | "1", 13 | "2", 14 | "3", 15 | "4" 16 | ], 17 | "myobject": { 18 | "mystring": "sub-hello" 19 | }, 20 | "myobjectarray": [ 21 | { 22 | "mystring": "value1", 23 | }, 24 | { 25 | "mystring": "value2", 26 | }, 27 | { 28 | "mystring": "value3", 29 | }, 30 | { 31 | "mystring": "value4", 32 | }, 33 | ], 34 | "mydictionary": { 35 | "someItem": { 36 | "mystring": "someItem" 37 | }, 38 | "someOtherItem": { 39 | "mystring": "someOtherItem" 40 | } 41 | }, 42 | "null": null, 43 | "string": "a string", 44 | "stringNumber": "1234", 45 | "bool": true, 46 | "int": -1234, 47 | "int32": -2147483647, 48 | "int64": -9223372036854775807, 49 | "uint": 4294967295, 50 | "uint32": 4294967295, 51 | "uint64": 18446744073709551615, 52 | "float": 0.08251977, 53 | "double": 0.333333333333333314829616256247390992939, 54 | "boolString": "yes", 55 | "intString": "-1234", 56 | "int32String": "-2147483647", 57 | "int64String": "-9223372036854775807", 58 | "uintString": "1234", 59 | "uint32String": "4294967295", 60 | "uint64String": "18446744073709551615", 61 | "floatString": "0.00000011920928955078125", 62 | "doubleString": "0.99999999999999994444444444444444444444333333333333333314829616256247390992939", 63 | "decimalNumber": 3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 64 | "appVersion": { 65 | "android": { 66 | "minSDK": "5.1", 67 | "minVersion": "0.1.4", 68 | "url": "https://walmart.com/change-me", 69 | "version": "0.1.4" 70 | }, 71 | "iOS": { 72 | "minVersion": "0.1.4", 73 | "url": "https://walmart.com/change-me", 74 | "version": "0.1.4" 75 | } 76 | }, 77 | "storeIdWhitelist": [ 5260 ] 78 | } -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable_osx/ELCodable_osx.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELCodable_osx.h 3 | // ELCodable_osx 4 | // 5 | // Created by Brandon Sneed on 6/16/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ELCodable_osx. 12 | FOUNDATION_EXPORT double ELCodable_osxVersionNumber; 13 | 14 | //! Project version string for ELCodable_osx. 15 | FOUNDATION_EXPORT const unsigned char ELCodable_osxVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable_osx/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2016 WalmartLabs. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable_osxTests/ELCodable_osxTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ELCodable_osxTests.swift 3 | // ELCodable_osxTests 4 | // 5 | // Created by Brandon Sneed on 6/16/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ELCodable_osx 11 | 12 | class ELCodable_osxTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Modules/ELCodable/ELCodable_osxTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Modules/ELCodable/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Walmart, WalmartLabs, and other Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Modules/ELFoundation/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcscmblueprint 19 | *.xcuserstate 20 | 21 | # CocoaPods 22 | # 23 | # We recommend against adding the Pods directory to your .gitignore. However 24 | # you should judge for yourself, the pros and cons are mentioned at: 25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 26 | # 27 | # Pods/ 28 | .DS_Store 29 | -------------------------------------------------------------------------------- /Modules/ELFoundation/.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode8 3 | script: 4 | - xcodebuild -project ELFoundation.xcodeproj -scheme ELFoundation -sdk iphonesimulator test -destination 'platform=iOS Simulator,name=iPhone 6,OS=10.0' CODE_SIGNING_REQUIRED=NO 5 | -------------------------------------------------------------------------------- /Modules/ELFoundation/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [1.1.0](https://github.com/Electrode-iOS/ELFoundation/releases/tag/v1.1.0) 2 | 3 | - Add support for Xcode 8, Swift 2.3, and iOS SDK 10 4 | 5 | # [1.0.2](https://github.com/Electrode-iOS/ELFoundation/releases/tag/v1.0.2) 6 | 7 | - Update schemes to support Xcode 7.3 8 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation copy-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation.xcodeproj/xcshareddata/xcschemes/ELFoundation_osx.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/ELFoundation.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELFoundation.h 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 2/19/15. 6 | // Copyright (c) 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ELFoundation. 12 | FOUNDATION_EXPORT double ELFoundationVersionNumber; 13 | 14 | //! Project version string for ELFoundation. 15 | FOUNDATION_EXPORT const unsigned char ELFoundationVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/Extensions/NSBundle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle.swift 3 | // THGDispatch 4 | // 5 | // Created by Brandon Sneed on 2/16/15. 6 | // Copyright (c) 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Bundle { 12 | /** 13 | Returns the reverse DNS style bundle identifier 14 | 15 | - returns: The reverse DNS style bundle identifier 16 | 17 | Example: com.walmartlabs.thgfoundation 18 | */ 19 | public func reverseBundleIdentifier() -> String? { 20 | if let id = bundleIdentifier { 21 | let components: [String] = id.components(separatedBy: ".") 22 | let reverseComponents = Array(components.reversed()) 23 | let result = reverseComponents.joined(separator: ".") 24 | return result 25 | } 26 | 27 | return nil 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/Extensions/NSError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSError.swift 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 3/25/15. 6 | // Copyright (c) 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | Protocol to make using NSError's in swift easier to use. 13 | 14 | Example: 15 | 16 | enum ELMyError: Int, NSErrorEnum { 17 | case FileNotFound 18 | case LostACoconut 19 | 20 | public var domain: String { 21 | return "com.walmartlabs.ELMyError" 22 | } 23 | 24 | public var errorDescription: String { 25 | case FileNotFound: 26 | return "File not found." 27 | case LostACoconut: 28 | return "An african swallow stole a coconut." 29 | } 30 | } 31 | */ 32 | public protocol NSErrorEnum { 33 | /// Returns the raw value of the enum. The enum MUST be an Int. 34 | var rawValue: Int { get } 35 | /// Returns the domain of the error enum. ie: "com.walmartlabs.ELMyError" 36 | var domain: String { get } 37 | /// Returns an error description string representing the enum's value. 38 | var errorDescription: String { get } 39 | } 40 | 41 | extension NSError { 42 | /** 43 | Convenience init that takes an enum conforming to the NSErrorEnum protocol to build an NSError object. 44 | 45 | Example: 46 | 47 | let error = NSError(ELMyError.LostACoconut) 48 | */ 49 | convenience public init(_ code: NSErrorEnum) { 50 | self.init(domain:code.domain, code: code.rawValue, userInfo: [NSLocalizedDescriptionKey: code.errorDescription]) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/Extensions/NSObject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject.swift 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 12/8/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension NSObject { 12 | /** 13 | Returns the NSBundle containing self's class. 14 | */ 15 | public static func bundle() -> Bundle { 16 | return Bundle(for: self) 17 | } 18 | 19 | /** 20 | Returns the NSBundle containing self's class. 21 | */ 22 | public func bundle() -> Bundle { 23 | return type(of: self).bundle() 24 | } 25 | 26 | /** 27 | Returns a plugin ID based on bundleID. 28 | */ 29 | public static func pluginIdentifier() -> String { 30 | let bundleID = self.bundle().bundleIdentifier! 31 | return bundleID 32 | } 33 | 34 | /** 35 | Returns a plugin ID based on bundleID. 36 | */ 37 | public func pluginIdentifier() -> String { 38 | return type(of: self).pluginIdentifier() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/Extensions/NSThread.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSThread.swift 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 3/16/15. 6 | // Copyright (c) 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Thread { 12 | fileprivate static let formatterCacheKey = "ELFoundation.dateFormatter" 13 | 14 | /** 15 | Creates and returns a date formatter for the format and locale. The first time a format is passed in 16 | the date formatter is created and cached per thread. This ensures that the costly creation of a date formatter is 17 | only done once. Formatters are also created per thread as their underlying implementation hasn't been thread-safe 18 | until recent versions of iOS. This last requirement may be removed in a future version of this method. 19 | 20 | - parameter format: The date format for the creating the date formatter. 21 | - parameter locale: The locale for the date formatter. 22 | - returns: The date formatter. 23 | */ 24 | public class func dateFormatter(_ format: String, locale: Locale? = Locale.current) -> DateFormatter { 25 | let threadDictionary = Thread.current.threadDictionary 26 | 27 | var cache: Dictionary? = threadDictionary.object(forKey: formatterCacheKey) as? Dictionary 28 | if cache == nil { 29 | cache = Dictionary() 30 | } 31 | 32 | let formatKey = format + "_" + locale!.identifier 33 | if let existing = cache?[formatKey] { 34 | return existing 35 | } 36 | 37 | let result = DateFormatter() 38 | result.locale = locale 39 | result.dateFormat = format 40 | cache?[formatKey] = result 41 | 42 | threadDictionary[formatterCacheKey] = cache 43 | 44 | return result; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/Extensions/NSURL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSURL.swift 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 4/15/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension URL { 12 | /** 13 | Breaks down a query string (ie: "") into it's decoded parts. 14 | */ 15 | public var queryDictionary: [String: String]? { 16 | if let queryString = query { 17 | var result = [String: String]() 18 | 19 | let components = queryString.components(separatedBy: "&") 20 | for item in components { 21 | let pair = item.components(separatedBy: "=") 22 | let key = pair[0] 23 | let value = pair[1] 24 | 25 | let decodedKey = key.removingPercentEncoding 26 | let decodedValue = value.removingPercentEncoding 27 | 28 | // if we can't get non-optionals, it's unable to be decoded. 29 | if let key = decodedKey, let value = decodedValue { 30 | result[key] = value 31 | } 32 | } 33 | 34 | if result.count > 0 { 35 | return result 36 | } 37 | } 38 | // if we didn't return prior to this, send back nil. 39 | return nil 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/Extensions/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String.swift 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 3/16/15. 6 | // Copyright (c) 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension String { 12 | /** 13 | Returns a GUID in the form of "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". 14 | 15 | - returns: A unique string identifier. 16 | */ 17 | static public func GUID() -> String { 18 | return UUID().uuidString 19 | } 20 | 21 | } 22 | 23 | public extension String { 24 | public func padFront(_ maxLength: Int) -> String { 25 | var spaces = "" 26 | if maxLength > self.count { 27 | for _ in 0..<(maxLength - self.count) { 28 | spaces += " " 29 | } 30 | } 31 | 32 | return "\(spaces)\(self)" 33 | } 34 | 35 | public func padBack(_ maxLength: Int) -> String { 36 | var spaces = "" 37 | if maxLength > self.count { 38 | for _ in 0..<(maxLength - self.count) { 39 | spaces += " " 40 | } 41 | } 42 | 43 | return "\(self)\(spaces)" 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/TestExtensions/TestHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestHelper.swift 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 8/13/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | Determines if a given block of code is being run within the context of 13 | a unit test. 14 | */ 15 | public func isInUnitTest() -> Bool { 16 | if ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil { 17 | return true 18 | } 19 | return false 20 | } 21 | 22 | private enum WaitConditionError: Error { 23 | case timeout 24 | } 25 | 26 | extension NSObject { 27 | /** 28 | Pumps the run loop while waiting for the given conditions check to return true, or the timeout has 29 | expired. This function should only be used within unit tests, and will throw an exception if not. 30 | - parameter timeout: The timeout, in seconds. 31 | - parameter conditionsCheck: A block that performs the condition check and returns true/false. 32 | */ 33 | public func waitForConditionsWithTimeout(_ timeout: TimeInterval, conditionsCheck: () -> Bool) throws { 34 | if isInUnitTest() { 35 | var condition = false 36 | let startTime = Date() 37 | 38 | while (!condition) { 39 | RunLoop.current.run(until: Date.distantPast) 40 | condition = conditionsCheck() 41 | let currentTime = Date().timeIntervalSince(startTime) 42 | if currentTime > timeout { 43 | throw WaitConditionError.timeout 44 | } 45 | } 46 | } else { 47 | exceptionFailure("waitForConditionsWithTimeout should only be used in unit tests.") 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/TestExtensions/XCTestCase+Exceptions.h: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | These files are not to be included in any targets except test case targets. 4 | Simply drag them to your project and make sure only the Tests target is checked. 5 | You'll be prompted to create an objc-bridging header. Say yes, then add 6 | this to the newly created header: 7 | 8 | #import "XCTestCase+Exceptions.h" 9 | 10 | These methods should now be accessible from within Swift without doing 11 | anything additional. 12 | 13 | */ 14 | 15 | @import Foundation; 16 | @import XCTest; 17 | 18 | @interface XCTestCase (Exceptions) 19 | 20 | /** 21 | Replacement for the stock objc XCTAssertThrows, which is unavailable in Swift. 22 | 23 | :param: block The block to execute. 24 | :param: message The message to be displayed on failure to throw an exception. 25 | 26 | Example (Swift): XCTAssertThrows({ testThrow() }, "This method should've thrown an exception!") 27 | */ 28 | - (void)XCTAssertThrows:(void (^)(void))block :(NSString *)message; 29 | 30 | /** 31 | Replacement for the stock objc XCTAssertThrowsSpecific, which is unavailable in Swift. 32 | 33 | :param: block The block to execute. 34 | :param: name The name of the assertion to look for. 35 | :param: message The message to be displayed on failure to throw an exception. 36 | 37 | Example (Swift): XCTAssertThrowsSpecific({ testThrow() }, "THG", "This method should've thrown a THG exception!") 38 | */ 39 | - (void)XCTAssertThrowsSpecific:(void (^)(void))block :(NSString *)name :(NSString *)message; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/TestExtensions/XCTestCase+Exceptions.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | These files are not to be included in any targets except test case targets. 4 | Simply drag them to your project and make sure only the Tests target is checked. 5 | You'll be prompted to create an objc-bridging header. Say yes, then add 6 | this to the newly created header: 7 | 8 | #import "XCTestCase+Exceptions.h" 9 | 10 | These methods should now be accessible from within Swift without doing 11 | anything additional. 12 | 13 | */ 14 | 15 | #import "XCTestCase+Exceptions.h" 16 | 17 | @implementation XCTestCase (Exceptions) 18 | 19 | - (void)XCTAssertThrows:(void (^)(void))block :(NSString *)message { 20 | XCTAssertThrows(block(), @"%@", message); 21 | } 22 | 23 | - (void)XCTAssertThrowsSpecific:(void (^)(void))block :(NSString *)exceptionName :(NSString *)message { 24 | BOOL __didThrow = NO; 25 | @try { 26 | block(); 27 | } 28 | @catch (NSException *exception) { 29 | __didThrow = YES; 30 | XCTAssertEqualObjects(exception.name, exceptionName, @"%@", message); 31 | } 32 | @catch (...) { 33 | __didThrow = YES; 34 | XCTFail(@"%@", message); 35 | } 36 | 37 | if (!__didThrow) { 38 | XCTFail(@"%@", message); 39 | } 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/Utilities/Exceptions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Exceptions.swift 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 2/19/15. 6 | // Copyright (c) 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | The name of the exception thrown by 'exceptionFailture(...)'. 13 | */ 14 | public let ELExceptionFailure = "ELExceptionFailure" 15 | 16 | /** 17 | This function is intended to be used to catch programming errors and undefined code 18 | paths. To handle unrecoverable errors, see 'assertionFailure'. 19 | 20 | Raises an exception and can be used on testable code. This is to be used as an 21 | alternative to assertionFailure(), which blows up tests. 22 | 23 | - parameter message: Message string to be used. 24 | 25 | Example: exceptionFailure("This object is invalid. \(obj)") 26 | */ 27 | public func exceptionFailure(_ message: String) { 28 | let args: [CVarArg] = [] 29 | if isInUnitTest() { 30 | NSException.raise(NSExceptionName(rawValue: ELExceptionFailure), format: message, arguments: getVaList(args)) 31 | } else { 32 | #if DEBUG 33 | NSException.raise(NSExceptionName(rawValue: ELExceptionFailure), format: message, arguments: getVaList(args)) 34 | #endif 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/Utilities/ObjectAssociation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectAssociation.swift 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 8/11/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final private class Wrapper: NSObject { 12 | var value: T? 13 | init(_ x: T) { 14 | value = x 15 | } 16 | } 17 | 18 | /** 19 | Sets an value to be associated with 'object'. Be careful when using swift types 20 | like Arrays and whatnot where mutability is involved. 21 | */ 22 | public func setAssociatedObject(_ object: AnyObject, value: T, associativeKey: UnsafeRawPointer, policy: objc_AssociationPolicy) { 23 | //print("set, T = \(T.self)") 24 | objc_setAssociatedObject(object, associativeKey, Wrapper(value), policy) 25 | } 26 | 27 | /** 28 | Gets a value associated with 'object'. Be careful when using swift types 29 | like Arrays and whatnot where mutability is involved. 30 | */ 31 | public func getAssociatedObject(_ object: AnyObject, associativeKey: UnsafeRawPointer) -> T! { 32 | let result = objc_getAssociatedObject(object, associativeKey) 33 | //print("get, T = \(T.self)") 34 | if let v = result as? Wrapper { 35 | return v.value 36 | } else { 37 | return nil 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/Utilities/Swizzling.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Swizzling.swift 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 12/9/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | For the love of pete and the Flying Spaghetti Monster... 13 | 14 | Do not use this stuff unless you've inquired with no less than a Rabbi, a Priest, 15 | and a Shaman... and a few other engineers to figure out if there is a better way. 16 | 17 | */ 18 | public extension NSObject { 19 | /// Swizzles a class method on an Objective-C object. 20 | public class func swizzleClassMethod(_ originalSelector: Selector, swizzledSelector:Selector) { 21 | guard let c: AnyClass = object_getClass(self), 22 | let originalMethod = class_getClassMethod(c, originalSelector), 23 | let swizzledMethod = class_getClassMethod(c, swizzledSelector) else { 24 | print("Error replacing \(originalSelector) on \(String(describing: object_getClass(self))) with \(swizzledSelector).") 25 | return 26 | } 27 | 28 | let didAddMethod = class_addMethod(c, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) 29 | 30 | if didAddMethod { 31 | class_replaceMethod(c, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)) 32 | } else { 33 | method_exchangeImplementations(originalMethod, swizzledMethod); 34 | } 35 | } 36 | 37 | 38 | /// Swizzles an instance method on an Objective-C object. 39 | public class func swizzleInstanceMethod(_ originalSelector: Selector, swizzledSelector:Selector) { 40 | guard let originalMethod = class_getInstanceMethod(self, originalSelector), 41 | let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else { 42 | print("Error replacing \(originalSelector) with \(swizzledSelector).") 43 | return 44 | } 45 | 46 | let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) 47 | 48 | if didAddMethod { 49 | class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)) 50 | } else { 51 | method_exchangeImplementations(originalMethod, swizzledMethod); 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation/Utilities/Synchronization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Synchronization.swift 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 2/19/15. 6 | // Copyright (c) 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | Mimics @synchronized(x) in Objective-C. Synchronizes around the given object 13 | and executes the supplied closure. 14 | 15 | - parameter lock: Object to lock around. 16 | - parameter closure: Closure to execute inside of the lock. 17 | 18 | Example: synchronized(self) { doSomething() } 19 | */ 20 | public func synchronized(_ lock: AnyObject, closure: () -> Void) { 21 | objc_sync_enter(lock) 22 | closure() 23 | objc_sync_exit(lock) 24 | } 25 | 26 | /** 27 | Mimics @synchronized(x) in Objective-C. Synchronizes around the given object 28 | and executes the supplied closure, returning the type T. 29 | 30 | - parameter lock: Object to lock around. 31 | - parameter closure: Closure to execute inside of the lock. 32 | - returns: The result of the closure. 33 | 34 | Example: let running = synchronized(self) { return true } 35 | */ 36 | public func synchronized(_ lock: AnyObject, closure: () -> T) -> T { 37 | objc_sync_enter(lock) 38 | let result: T = closure() 39 | objc_sync_exit(lock) 40 | return result 41 | } 42 | 43 | /** 44 | OS Level Spin Lock class. Wraps the OSSpinLock* functions to allow for 45 | synchronization around a specified closure. This is very useful for properties 46 | where get/set need to be thread-safe. 47 | */ 48 | final public class Spinlock { 49 | public init() { 50 | 51 | } 52 | 53 | /** 54 | Tries to acquire the lock, and if successful executes the specified closure. 55 | 56 | - parameter closure: Closure to execute inside of the lock. 57 | - returns: False if it failed to acquire the lock, otherwise true. 58 | */ 59 | public func tryaround(_ closure: () -> Void) -> Bool { 60 | let held = OSSpinLockTry(&spinlock) 61 | if !held { 62 | closure() 63 | OSSpinLockUnlock(&spinlock) 64 | } 65 | return held 66 | } 67 | 68 | /** 69 | Runs the specified closure within the spin lock. 70 | 71 | - parameter closure: Closure to execute inside of the lock. 72 | */ 73 | public func around(_ closure: () -> Void) { 74 | OSSpinLockLock(&spinlock) 75 | closure() 76 | OSSpinLockUnlock(&spinlock) 77 | } 78 | 79 | /** 80 | Runs the specified closure within the spin lock, returning the type T. 81 | 82 | - parameter closure: Closure to execute inside of the lock. 83 | - returns: The result of the closure. 84 | */ 85 | public func around(_ closure: () -> T) -> T { 86 | OSSpinLockLock(&spinlock) 87 | let result: T = closure() 88 | OSSpinLockUnlock(&spinlock) 89 | return result 90 | } 91 | 92 | public func lock() { 93 | OSSpinLockLock(&spinlock) 94 | } 95 | 96 | public func trylock() -> Bool { 97 | return OSSpinLockTry(&spinlock) 98 | } 99 | 100 | public func unlock() { 101 | OSSpinLockUnlock(&spinlock) 102 | } 103 | 104 | fileprivate var spinlock: OSSpinLock = OS_SPINLOCK_INIT 105 | } 106 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundationTests/ELFoundationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ELFoundationTests.swift 3 | // ELFoundationTests 4 | // 5 | // Created by Brandon Sneed on 2/19/15. 6 | // Copyright (c) 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELFoundation 11 | 12 | class Foo: NSObject { 13 | dynamic func returnsOne() -> Int { 14 | return 1 15 | } 16 | 17 | dynamic class func returnsThree() -> Int { 18 | return 3 19 | } 20 | } 21 | 22 | extension Foo { 23 | dynamic func returnsTwo() -> Int { 24 | return 2 25 | } 26 | 27 | dynamic class func returnsFour() -> Int { 28 | return 4 29 | } 30 | } 31 | 32 | class Bar { 33 | 34 | } 35 | 36 | class ELFoundationTests: XCTestCase { 37 | 38 | override func setUp() { 39 | super.setUp() 40 | // Put setup code here. This method is called before the invocation of each test method in the class. 41 | } 42 | 43 | override func tearDown() { 44 | // Put teardown code here. This method is called after the invocation of each test method in the class. 45 | super.tearDown() 46 | } 47 | 48 | func test_swizzleInstanceMethod_replacesImplementationWithSwizzledSelector() { 49 | let foo = Foo() 50 | XCTAssertTrue(foo.returnsOne() == 1) 51 | 52 | Foo.swizzleInstanceMethod(#selector(Foo.returnsOne), swizzledSelector: #selector(Foo.returnsTwo)) 53 | 54 | XCTAssertTrue(foo.returnsOne() == 2) 55 | } 56 | 57 | func test_swizzleClassMethod_replacesImplementationWithSwizzledSelector() { 58 | XCTAssertTrue(Foo.returnsThree() == 3) 59 | 60 | Foo.swizzleClassMethod(#selector(Foo.returnsThree), swizzledSelector: #selector(Foo.returnsFour)) 61 | 62 | XCTAssertTrue(Foo.returnsThree() == 4) 63 | } 64 | 65 | func testObjectAssociation() { 66 | let bar = Bar() 67 | let storedValue = "O'HAI" 68 | setAssociatedObject(bar, value: storedValue, associativeKey: "AnAssociationkey", policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) 69 | let retrievedValue = getAssociatedObject(bar, associativeKey: "AnAssociationkey") as String! 70 | XCTAssertTrue(retrievedValue == storedValue) 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundationTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundationTests/NSBundleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundleTests.swift 3 | // ELFoundation 4 | // 5 | // Created by Steven Riggins on 7/7/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELFoundation 11 | 12 | class NSBundleTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testReverseBundleIdentifier() { 25 | let bundle = Bundle(identifier: "com.walmartlabs.ELFoundation") 26 | let reverseIdentifier = bundle?.reverseBundleIdentifier() 27 | 28 | XCTAssertTrue(reverseIdentifier == "ELFoundation.walmartlabs.com") 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundationTests/NSObjectTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSObjectTests.swift 3 | // ELFoundation 4 | // 5 | // Created by Sam Grover on 2/1/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELFoundation 11 | 12 | class NSObjectTests: XCTestCase { 13 | 14 | class Foo: NSObject { 15 | 16 | } 17 | 18 | override func setUp() { 19 | super.setUp() 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | override func tearDown() { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | super.tearDown() 26 | } 27 | 28 | func testStaticBundle() { 29 | XCTAssertTrue("com.walmartlabs.ELFoundationTests" == Foo.bundle().bundleIdentifier!) 30 | } 31 | 32 | func testBundle() { 33 | let foo = Foo() 34 | XCTAssertTrue("com.walmartlabs.ELFoundationTests" == foo.bundle().bundleIdentifier!) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundationTests/NSThreadTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSThreadTests.swift 3 | // ELFoundation 4 | // 5 | // Created by Sam Grover on 2/2/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class NSThreadTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testDateFormatter() { 24 | let RFC3339TestDate = "1996-12-19T16:39:57-08:00" 25 | let RFC3339TestDateDescription = "1996-12-20 00:39:57 +0000" 26 | let RFC3339DateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" 27 | 28 | let df = Thread.dateFormatter(RFC3339DateFormat) 29 | let date = df.date(from: RFC3339TestDate) 30 | XCTAssertTrue(date!.description == RFC3339TestDateDescription) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundationTests/NSURLTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLTests.swift 3 | // ELFoundation 4 | // 5 | // Created by Brandon Sneed on 4/15/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELFoundation 11 | 12 | class NSURLTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testQueryDictionary() { 25 | let url = URL(string: "http://blah.com/something?key1=value1&key2=value2&key3=this%20be%20value%203%2C%20y0") 26 | 27 | let dict = url!.queryDictionary! 28 | 29 | XCTAssertTrue(dict["key1"] == "value1") 30 | XCTAssertTrue(dict["key2"] == "value2") 31 | XCTAssertTrue(dict["key3"] == "this be value 3, y0") 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundationTests/StringTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringTests.swift 3 | // ELFoundation 4 | // 5 | // Created by Sam Grover on 2/1/16. 6 | // Copyright © 2016 WalmartLabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class StringTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testGUID() { 24 | do { 25 | let regex = try NSRegularExpression(pattern: "^.{8}-.{4}-.{4}-.{4}-.{12}$", options: []) 26 | let guid = String.GUID() 27 | let numMatches = regex.numberOfMatches(in: String.GUID(), options: [], range: NSRange(location: 0, length: guid.characters.count)) 28 | XCTAssertTrue(numMatches == 1) 29 | } catch { 30 | XCTAssertTrue(false) 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation_osx/ELFoundation_osx.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELFoundation_osx.h 3 | // ELFoundation_osx 4 | // 5 | // Created by Brandon Sneed on 8/11/15. 6 | // Copyright © 2015 WalmartLabs. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ELFoundation_osx. 12 | FOUNDATION_EXPORT double ELFoundation_osxVersionNumber; 13 | 14 | //! Project version string for ELFoundation_osx. 15 | FOUNDATION_EXPORT const unsigned char ELFoundation_osxVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation_osx/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015 WalmartLabs. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Modules/ELFoundation/ELFoundation_osxTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Modules/ELFoundation/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Walmart, WalmartLabs, and other Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Modules/ELFoundation/README.md: -------------------------------------------------------------------------------- 1 | # ELFoundation 2 | 3 | [![Version](https://img.shields.io/badge/version-v1.1.0-blue.svg)](https://github.com/Electrode-iOS/ELFoundation/releases/latest) 4 | [![Build Status](https://travis-ci.org/Electrode-iOS/ELFoundation.svg?branch=master)](https://travis-ci.org/Electrode-iOS/ELFoundation) 5 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 6 | 7 | ELFoundation is a collection of Swift utilities for iOS development. 8 | 9 | ## Requirements 10 | 11 | ELFoundation requires Swift 3 and Xcode 8. 12 | 13 | ## Installation 14 | 15 | ### Carthage 16 | 17 | Install with [Carthage](https://github.com/Carthage/Carthage) by adding the framework to your project's [Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile). 18 | 19 | ``` 20 | github "Electrode-iOS/ELFoundation" ~> 1.1.0 21 | ``` 22 | 23 | ### Manual 24 | 25 | Install by adding `ELFoundation.xcodeproj` to your project and configuring your target to link `ELFoundation.framework`. 26 | 27 | ## Usage 28 | 29 | * `exceptionFailure` - A replacement for assertionFailure, usable in tests. 30 | * `synchronized` - Akin to @synchronized() in Objective-C. 31 | * `Spinlock` - A basic spinlock implementation for synchronization. 32 | * `Object Association` - Objective-C style object association. 33 | * `Swizzling` - Objective-C style swizzling. 34 | * `String (extensions)` - Handy extensions. 35 | * `Array (extensions)` - Handy extensions. 36 | * `NSObject (extensions)` - Handy extensions. 37 | * `NSThread (extensions)` - Handy extensions. 38 | * `NSError (extensions)` - Handy extensions. 39 | * `NSBundle (extensions)` - Handy extensions. 40 | * `XCTestCase (extensions)` - Gets XCTAssertThrows working in Swift. 41 | 42 | ## Some Examples 43 | 44 | Synchronized property access: 45 | 46 | ```Swift 47 | public var suspended: Bool { 48 | get { 49 | return lock.around { 50 | self.suspended 51 | } 52 | } 53 | 54 | set(value) { 55 | lock.around { 56 | self.suspended = value 57 | } 58 | } 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /ModuloKit/Commands/AddCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddCommand.swift 3 | // modulo 4 | // 5 | // Created by Brandon Sneed on 6/17/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if NOFRAMEWORKS 12 | #else 13 | import ELCLI 14 | #endif 15 | 16 | open class AddCommand: NSObject, Command { 17 | // internal properties 18 | fileprivate var version: SemverRange? = nil 19 | fileprivate var repositoryURL: String! = nil 20 | fileprivate var shouldUpdate: Bool = false 21 | fileprivate var unmanaged: Bool = false 22 | 23 | // Protocol conformance 24 | open var name: String { return "add" } 25 | open var shortHelpDescription: String { return "Adds a module dependency" } 26 | open var longHelpDescription: String { 27 | return "Add the given repository as a module to the current project.\n\n" + 28 | "In unmanaged mode, it is up to the user to manage what is checked out.\n" + 29 | "In this case, the update command will simply do a pull.\n\n" + 30 | "More information on version ranges can be found at https://docs.npmjs.com/misc/semver" 31 | } 32 | open var failOnUnrecognizedOptions: Bool { return true } 33 | 34 | open var verbose: Bool = State.instance.options.alwaysVerbose 35 | open var quiet: Bool = false 36 | 37 | open func configureOptions() { 38 | addOptionValue(["--version"], usage: "specify the version or range to use", valueSignature: "") { (option, value) -> Void in 39 | if let value = value { 40 | self.version = SemverRange(value) 41 | } 42 | } 43 | 44 | addOption(["--unmanaged"], usage: "specifies that this module will be unmanaged") { (option, value) in 45 | self.unmanaged = true 46 | } 47 | 48 | addOption(["-u", "--update"], usage: "performs the update command after adding a module") { (option, value) in 49 | self.shouldUpdate = true 50 | } 51 | 52 | addFlaglessOptionValues([""]) { (option, value) -> Void in 53 | self.repositoryURL = value 54 | } 55 | } 56 | 57 | open func execute(_ otherParams: Array?) -> Int { 58 | let actions = Actions() 59 | 60 | if version == nil && unmanaged == false { 61 | writeln(.stderr, "A version or range must be specified via --version, or --unmanaged must be used.") 62 | return ErrorCode.commandError.rawValue 63 | } 64 | 65 | if let version = version { 66 | if version.valid == false { 67 | writeln(.stderr, "The range or version specified is not valid. Please see: https://docs.npmjs.com/misc/semver") 68 | return ErrorCode.commandError.rawValue 69 | } 70 | } 71 | 72 | let result = actions.addDependency(repositoryURL, version: version, unmanaged: unmanaged) 73 | if result == .success { 74 | if shouldUpdate { 75 | writeln(.stdout, "Added \(String(describing: repositoryURL)).") 76 | if let spec = ModuleSpec.workingSpec(), let dep = spec.dependencyForURL(repositoryURL) { 77 | let actions = Actions() 78 | _ = actions.updateDependencies([dep], explicit: true) 79 | } 80 | } else { 81 | writeln(.stdout, "Added \(String(describing: repositoryURL)). Run the `update` command to complete the process.") 82 | } 83 | } 84 | 85 | 86 | return result.rawValue 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /ModuloKit/Commands/DefaultsCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultsCommand.swift 3 | // ModuloKit 4 | // 5 | // Created by Daniel Miedema on 9/25/18. 6 | // Copyright © 2018 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if NOFRAMEWORKS 11 | #else 12 | import ELCLI 13 | #endif 14 | 15 | open class DefaultsCommand: NSObject, Command { 16 | // Internal Properties 17 | fileprivate var toggleVerbose: Bool = false 18 | fileprivate var verboseValue: String? = nil 19 | fileprivate var moduleFolderPath: String? = nil 20 | fileprivate var setValue: Bool = false 21 | 22 | // Protocol Conformance 23 | public var name: String { return "defaults" } 24 | 25 | public var shortHelpDescription: String { 26 | return "Set default arguments/configuration properties for this repository" 27 | } 28 | 29 | public var longHelpDescription: String { 30 | return """ 31 | Set default argument values for all commands to be run. 32 | This will make changes to the `.modulo` file reflecting the 33 | new defaults that have been set 34 | """ 35 | } 36 | 37 | public var failOnUnrecognizedOptions: Bool { return true } 38 | 39 | public var verbose: Bool = State.instance.options.alwaysVerbose 40 | public var quiet: Bool = false 41 | 42 | public func execute(_ otherParams: Array?) -> Int { 43 | guard var spec = ModuleSpec.workingSpec() else { 44 | exit(ErrorCode.notInitialized) 45 | return ErrorCode.notInitialized.rawValue 46 | } 47 | 48 | if setValue { 49 | if toggleVerbose { 50 | let newValue: Bool 51 | switch verboseValue { 52 | case "true": 53 | newValue = true 54 | case "false": 55 | newValue = false 56 | default: 57 | writeln(.stderr, "\(verboseValue ?? "") is not `true` or `false`. Interpretting as `false`.") 58 | newValue = false 59 | } 60 | 61 | spec.options.alwaysVerbose = newValue 62 | State.instance.options.alwaysVerbose = newValue 63 | } 64 | if let moduleFolderPath = moduleFolderPath, 65 | !moduleFolderPath.isEmpty { 66 | spec.options.depdencyInstallationPath = moduleFolderPath 67 | State.instance.options.depdencyInstallationPath = moduleFolderPath 68 | } 69 | 70 | if !toggleVerbose && moduleFolderPath == nil { 71 | writeln(.stderr, """ 72 | When `--set` is passed its assumed you want to set a default. 73 | Please specify one of the options 74 | --alwaysVerbose 75 | --moduleFolder 76 | """) 77 | } 78 | spec.save() 79 | } else { 80 | writeln(.stdout, "alwaysVerbose - \(spec.options.alwaysVerbose)") 81 | writeln(.stdout, "depdencyInstallationPath - \(spec.options.depdencyInstallationPath)") 82 | } 83 | 84 | return ErrorCode.success.rawValue 85 | } 86 | 87 | open func configureOptions() { 88 | addOption(["--set"], usage: "set a new value for the given") { (option, value) in 89 | self.setValue = true 90 | } 91 | 92 | addOptionValue(["--alwaysVerbose"], 93 | usage: "specify `verbose` for all commands that are run", 94 | valueSignature: "<[true|false]>") { (option, value) in 95 | self.toggleVerbose = true 96 | self.verboseValue = value 97 | } 98 | 99 | addOptionValue(["--moduleFolder"], 100 | usage: "specify the desired dependency path", 101 | valueSignature: "") { (option, value) in 102 | self.moduleFolderPath = value ?? "" 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /ModuloKit/Commands/InitCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InitCommand.swift 3 | // ModuloKit 4 | // 5 | // Created by Brandon Sneed on 6/16/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if NOFRAMEWORKS 11 | #else 12 | import ELCLI 13 | #endif 14 | 15 | open class InitCommand: NSObject, Command { 16 | // Internal properties 17 | open var isModule: Bool = false 18 | 19 | // Protocol conformance 20 | open var name: String { return "init" } 21 | open var shortHelpDescription: String { return "Initialize modulo" } 22 | open var longHelpDescription: String { 23 | return "This command initializes modulo and creates a .modulo file\n" + 24 | "containing module dependency information." 25 | } 26 | open var failOnUnrecognizedOptions: Bool { return true } 27 | 28 | open var verbose: Bool = State.instance.options.alwaysVerbose 29 | open var quiet: Bool = false 30 | 31 | open func configureOptions() { 32 | addOption(["--app"], usage: "init's the working path as an application (default)") { (option, value) in 33 | self.isModule = false 34 | } 35 | 36 | addOption(["--module"], usage: "init's the working path as a module") { (option, value) in 37 | self.isModule = true 38 | } 39 | } 40 | 41 | open func execute(_ otherParams: Array?) -> Int { 42 | //let scm = currentSCM() 43 | let workingPath = FileManager.workingPath() 44 | 45 | // Already nested in a Modules/ directory? Init as a module. 46 | if isValidModuleDirectory(path: workingPath) { 47 | isModule = true 48 | writeln(.stdout, "Initializing as a module, since you're already in the Modules directory ...") 49 | } 50 | 51 | if ModuleSpec.exists() { 52 | exit(.alreadyInitialized) 53 | } 54 | 55 | let specPath = workingPath.appendPathComponent(specFilename) 56 | let spec = ModuleSpec(name: FileManager.directoryName(), module: isModule, sourcePath: nil, dependencies: [], options: OptionsSpec(), path: specPath) 57 | let success = spec.save() 58 | 59 | if !success { 60 | exit(ErrorCode.specNotWritable) 61 | } else { 62 | writeln(.stdout, "Modulo has been initialized.") 63 | } 64 | 65 | return ErrorCode.success.rawValue 66 | } 67 | 68 | open func isValidModuleDirectory(path: String) -> Bool { 69 | let relativeParentPath = path.appendPathComponent("..") 70 | let absolutePath = NSString(string: relativeParentPath).standardizingPath // normalizes relative path segments 71 | let parentDirectoryName = NSString(string: absolutePath).lastPathComponent 72 | 73 | return parentDirectoryName == State.instance.modulePathName 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ModuloKit/Commands/RemoveCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RemoveCommand.swift 3 | // modulo 4 | // 5 | // Created by Brandon Sneed on 7/18/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if NOFRAMEWORKS 11 | #else 12 | import ELCLI 13 | #endif 14 | 15 | open class RemoveCommand: NSObject, Command { 16 | // Protocol conformance 17 | open var name: String { return "remove" } 18 | open var shortHelpDescription: String { return "Removes a module from the list of dependencies" } 19 | open var longHelpDescription: String { 20 | return "Removes a module from the list of dependencies and performs checking to see if it is used elsewhere. The filesystem is left intact for you to delete it manually at your convenience." 21 | } 22 | open var failOnUnrecognizedOptions: Bool { return true } 23 | 24 | open var verbose: Bool = State.instance.options.alwaysVerbose 25 | open var quiet: Bool = false 26 | 27 | open func configureOptions() { 28 | 29 | } 30 | 31 | open func execute(_ otherParams: Array?) -> Int { 32 | let actions = Actions() 33 | let result = actions.checkDependenciesStatus() 34 | return result.rawValue 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ModuloKit/Commands/StatusCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatusCommand.swift 3 | // modulo 4 | // 5 | // Created by Brandon Sneed on 7/11/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if NOFRAMEWORKS 12 | #else 13 | import ELCLI 14 | #endif 15 | 16 | open class StatusCommand: NSObject, Command { 17 | // Protocol conformance 18 | open var name: String { return "status" } 19 | open var shortHelpDescription: String { return "Gathers status about the module tree" } 20 | open var longHelpDescription: String { 21 | return "Gathers status about the module tree. Uncommitted, unpushed, branch or tag mismatches, etc." 22 | } 23 | open var failOnUnrecognizedOptions: Bool { return true } 24 | open var ignoreMain: Bool = false 25 | 26 | open var verbose: Bool = State.instance.options.alwaysVerbose 27 | open var quiet: Bool = false 28 | 29 | open func configureOptions() { 30 | addOption(["--ignoremain"], usage: "ignores the main project, scans modules only") { (option, value) in 31 | self.ignoreMain = true 32 | } 33 | 34 | } 35 | 36 | open func execute(_ otherParams: Array?) -> Int { 37 | let actions = Actions() 38 | let result = actions.checkDependenciesStatus(ignoreMain: ignoreMain) 39 | return result.rawValue 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ModuloKit/Commands/UpdateCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UpdateCommand.swift 3 | // modulo 4 | // 5 | // Created by Brandon Sneed on 6/21/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if NOFRAMEWORKS 12 | #else 13 | import ELCLI 14 | #endif 15 | 16 | open class UpdateCommand: NSObject, Command { 17 | // internal properties 18 | fileprivate var updateAll: Bool = true 19 | fileprivate var dependencyName: String! = nil 20 | fileprivate var failSilentlyIfUnused: Bool = false 21 | fileprivate var nonzero: Bool = false 22 | fileprivate var hostname: String? = nil 23 | 24 | // Protocol conformance 25 | open var name: String { return "update" } 26 | open var shortHelpDescription: String { return "Updates module dependencies" } 27 | open var longHelpDescription: String { 28 | return "Updates module dependencies if needed. This command may clone sub dependencies, update the checked out versions, etc." 29 | } 30 | open var failOnUnrecognizedOptions: Bool { return true } 31 | 32 | open var verbose: Bool = State.instance.options.alwaysVerbose 33 | open var quiet: Bool = false 34 | 35 | open func configureOptions() { 36 | addOption(["-a", "--all"], usage: "update all dependencies (default)") { (option, value) in 37 | self.updateAll = true 38 | } 39 | 40 | addOption(["--nonzero"], usage: "return a non-zero result code if clones occurred") { (option, value) in 41 | self.nonzero = true 42 | } 43 | 44 | addOptionValue(["--host"], usage: "checks for host availablility, ie: github.com", valueSignature: "") { (option, value) in 45 | self.hostname = value 46 | } 47 | 48 | addOption(["--meh"], usage: "return success if modulo isn't being used or is uninitialized") { (option, value) in 49 | self.failSilentlyIfUnused = true 50 | } 51 | 52 | addFlaglessOptionValues([""]) { (option, value) -> Void in 53 | self.dependencyName = value 54 | self.updateAll = false 55 | } 56 | 57 | addOption(["--verbose"], usage: "verbose logging on") { (option, value) in 58 | self.verbose = true 59 | } 60 | } 61 | 62 | open func execute(_ otherParams: Array?) -> Int { 63 | let actions = Actions() 64 | actions.scm.verbose = verbose 65 | 66 | if let hostname = hostname { 67 | if canConnect(hostname: hostname) == false { 68 | writeln(.stdout, "Connection to \(hostname) unavailable. Exiting.") 69 | return ErrorCode.success.rawValue 70 | } 71 | } 72 | 73 | var deps = [DependencySpec]() 74 | if updateAll { 75 | if let workingSpec = ModuleSpec.workingSpec() { 76 | deps = workingSpec.dependencies 77 | } else { 78 | return ErrorCode.specNotFound.rawValue 79 | } 80 | } else if dependencyName != nil { 81 | if let dep = ModuleSpec.workingSpec()?.dependencyForName(dependencyName) { 82 | deps.append(dep) 83 | } 84 | } else { 85 | showHelp() 86 | return ErrorCode.commandError.rawValue 87 | } 88 | 89 | if deps.count == 0 { 90 | if failSilentlyIfUnused { 91 | return ErrorCode.success.rawValue 92 | } else { 93 | return ErrorCode.noMatchingDependencies.rawValue 94 | } 95 | } else { 96 | _ = actions.updateDependencies(deps, explicit: true) 97 | 98 | // if we actually cloned something, and the nonzero flag was used, 99 | if nonzero && State.instance.dependenciesWereCloned() { 100 | return 1 101 | } 102 | 103 | return ErrorCode.success.rawValue 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /ModuloKit/ErrorCode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorCodes.swift 3 | // ModuloKit 4 | // 5 | // Created by Brandon Sneed on 6/16/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if NOFRAMEWORKS 11 | #else 12 | import ELCLI 13 | import ELFoundation 14 | #endif 15 | 16 | public enum ErrorCode: Int { 17 | case success = 0 18 | case unknownError 19 | case commandError 20 | case specNotFound 21 | case specNotWritable 22 | case scmNotFound 23 | case scmNotInitialized 24 | case alreadyInitialized 25 | case notInitialized 26 | case noMatchingDependencies 27 | case dependencyAlreadyExists 28 | case dependencyUnclean 29 | case dependencyUnknown 30 | 31 | var description: String { 32 | var result: String = "" 33 | switch self { 34 | case .success: 35 | break 36 | case .unknownError: 37 | result = "An unknown error occurred." 38 | case .commandError: 39 | result = "There was an error in the command line used." 40 | case .specNotFound: 41 | result = ".modulo file not found." 42 | case .specNotWritable: 43 | result = ".modulo cannot be written to, check permissions." 44 | case .scmNotFound: 45 | result = "No supported SCM was found." 46 | case .scmNotInitialized: 47 | result = "An SCM has not been initialized in this directory." 48 | case .alreadyInitialized: 49 | result = "Modulo has already been initialized." 50 | case .notInitialized: 51 | result = "Modulo has not been initialized." 52 | case .noMatchingDependencies: 53 | result = "No matching dependencies were found." 54 | case .dependencyAlreadyExists: 55 | result = "The dependency already exists." 56 | case .dependencyUnclean: 57 | result = "The dependency is not clean." 58 | case .dependencyUnknown: 59 | result = "The specified dependency is unknown." 60 | } 61 | return result 62 | } 63 | } 64 | 65 | internal func exit(_ code: ErrorCode, closure: (() -> Void)? = nil) { 66 | if code != .success { 67 | writeln(.stderr, code.description) 68 | } 69 | 70 | if let closure = closure { 71 | closure() 72 | } 73 | 74 | if isInUnitTest() { 75 | exceptionFailure(code.description) 76 | } else { 77 | exit(Int32(code.rawValue)) 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /ModuloKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2016 TheHolyGrail. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ModuloKit/Modulo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Modulo.swift 3 | // ModuloKit 4 | // 5 | // Created by Brandon Sneed on 6/15/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if NOFRAMEWORKS 12 | #else 13 | import ELCLI 14 | import ELFoundation 15 | #endif 16 | 17 | @objc 18 | open class Modulo: NSObject { 19 | 20 | public static func run() { 21 | let error = run([]) 22 | exit(Int32(error.rawValue)) 23 | } 24 | 25 | public static func run(_ args: [String]) -> ErrorCode { 26 | let cli = CLI(name: "modulo", version: "0.7.0", description: "A simple dependency manager") 27 | 28 | // before we do anything make sure our options are applied to our 29 | // current state. If we don't have a working spec the defaults will do fine 30 | if let options = ModuleSpec.workingSpec()?.options { 31 | State.instance.options = options 32 | } 33 | 34 | if args.count > 0 { 35 | cli.allArgumentsToExecutable = args 36 | } 37 | 38 | cli.addCommands([InitCommand(), AddCommand(), UpdateCommand(), StatusCommand(), MapCommand(), SetCommand(), DefaultsCommand()]) 39 | 40 | if let error = ErrorCode(rawValue: cli.run()) { 41 | if error == .success { 42 | State.instance.showFinalInformation() 43 | } 44 | 45 | return error 46 | } 47 | 48 | return ErrorCode.unknownError 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ModuloKit/ModuloKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // ModuloKit.h 3 | // ModuloKit 4 | // 5 | // Created by Brandon Sneed on 6/15/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ModuloKit. 12 | FOUNDATION_EXPORT double ModuloKitVersionNumber; 13 | 14 | //! Project version string for ModuloKit. 15 | FOUNDATION_EXPORT const unsigned char ModuloKitVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | -------------------------------------------------------------------------------- /ModuloKit/Reachability.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Reachability.swift 3 | // modulo 4 | // 5 | // Created by Sneed, Brandon on 7/10/17. 6 | // Copyright © 2017 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SystemConfiguration 11 | 12 | func canConnect(hostname: String) -> Bool { 13 | guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else { 14 | return false 15 | } 16 | 17 | var flags = SCNetworkReachabilityFlags() 18 | let gotFlags = SCNetworkReachabilityGetFlags(ref, &flags) 19 | 20 | let result = gotFlags && flags.contains(.reachable) && !flags.contains(.connectionRequired) 21 | 22 | return result 23 | } 24 | 25 | /*func internetIsReachable() -> Bool { 26 | var zeroAddress = sockaddr() 27 | zeroAddress.sa_len = UInt8(MemoryLayout.size) 28 | zeroAddress.sa_family = sa_family_t(AF_INET) 29 | 30 | guard let ref: SCNetworkReachability = withUnsafePointer(to: &zeroAddress, { 31 | SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)) 32 | }) else { 33 | return false 34 | } 35 | 36 | var reachabilityFlags = SCNetworkReachabilityFlags() 37 | let flags = withUnsafeMutablePointer(to: &reachabilityFlags) { 38 | SCNetworkReachabilityGetFlags(ref, UnsafeMutablePointer($0)) 39 | } 40 | 41 | /* 42 | guard isReachableFlagSet else { return false } 43 | 44 | if isConnectionRequiredAndTransientFlagSet { 45 | return false 46 | } 47 | 48 | if isRunningOnDevice { 49 | if isOnWWANFlagSet && !reachableOnWWAN { 50 | // We don't want to connect when on 3G. 51 | return false 52 | } 53 | } 54 | 55 | return true 56 | */ 57 | 58 | 59 | let reachable = reachabilityFlags.contains(.reachable) 60 | let connRequired = reachabilityFlags.intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection] 61 | 62 | }*/ 63 | -------------------------------------------------------------------------------- /ModuloKit/SemverCodable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SemverCodable.swift 3 | // modulo 4 | // 5 | // Created by Sneed, Brandon on 6/1/17. 6 | // Copyright © 2017 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if NOFRAMEWORKS 11 | #else 12 | import ELCodable 13 | #endif 14 | 15 | extension SemverRange: ELEncodable { 16 | public func encode() throws -> JSON { 17 | if self.valid { 18 | return JSON(self.original) 19 | } else { 20 | throw ELEncodeError.unencodable 21 | } 22 | } 23 | } 24 | 25 | extension SemverRange: ELDecodable { 26 | public static func decode(_ json: JSON?) throws -> SemverRange { 27 | if let value = json?.string { 28 | let range = SemverRange(value) 29 | if range.valid { 30 | return range 31 | } 32 | } 33 | 34 | throw ELDecodeError.undecodable 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ModuloKit/Specs/DependencySpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DependencySpec.swift 3 | // ModuloKit 4 | // 5 | // Created by Brandon Sneed on 6/16/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if NOFRAMEWORKS 11 | #else 12 | import ELCodable 13 | #endif 14 | 15 | public struct DependencySpec { 16 | // repository url to fetch the dep from 17 | var repositoryURL: String 18 | // version or version range 19 | var version: SemverRange? 20 | 21 | var unmanaged: Bool { 22 | get { 23 | return (version == nil) 24 | } 25 | } 26 | } 27 | 28 | extension DependencySpec: ELDecodable { 29 | public static func decode(_ json: JSON?) throws -> DependencySpec { 30 | return try DependencySpec( 31 | repositoryURL: json ==> "repositoryURL", 32 | version: json ==> "version" 33 | ) 34 | } 35 | 36 | public func validate() throws -> DependencySpec { 37 | return self 38 | } 39 | } 40 | 41 | extension DependencySpec: ELEncodable { 42 | public func encode() throws -> JSON { 43 | return try encodeToJSON([ 44 | "repositoryURL" <== repositoryURL, 45 | "version" <== version 46 | ]) 47 | } 48 | } 49 | 50 | extension DependencySpec { 51 | public func name() -> String { 52 | return repositoryURL.nameFromRemoteURL() 53 | } 54 | } 55 | 56 | extension DependencySpec: Hashable { 57 | public var hashValue: Int { 58 | return repositoryURL.hashValue 59 | } 60 | } 61 | 62 | public func ==(lhs: DependencySpec, rhs: DependencySpec) -> Bool { 63 | return lhs.repositoryURL == rhs.repositoryURL 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /ModuloKit/Specs/OptionsSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OptionsSpec.swift 3 | // ModuloKit 4 | // 5 | // Created by Daniel Miedema on 9/25/18. 6 | // Copyright © 2018 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if NOFRAMEWORKS 11 | #else 12 | import ELCodable 13 | #endif 14 | 15 | public struct OptionsSpec { 16 | /// Should we have `verbose` on all commands 17 | var alwaysVerbose: Bool = false 18 | /// Path to store our 'modules'/dependencies in 19 | var depdencyInstallationPath: String = "modules" 20 | } 21 | 22 | extension OptionsSpec: ELDecodable { 23 | public static func decode(_ json: JSON?) throws -> OptionsSpec { 24 | return try OptionsSpec( 25 | alwaysVerbose: json ==> "alwaysVerbose", 26 | depdencyInstallationPath: json ==> "depdencyInstallationPath" 27 | ) 28 | } 29 | } 30 | 31 | extension OptionsSpec: ELEncodable { 32 | public func encode() throws -> JSON { 33 | return try encodeToJSON([ 34 | "alwaysVerbose" <== alwaysVerbose, 35 | "depdencyInstallationPath" <== depdencyInstallationPath 36 | ]) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ModuloKit/System/BridgingHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // BridgingHeader.h 3 | // modulo 4 | // 5 | // Created by Sneed, Brandon on 12/13/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | #ifndef BridgingHeader_h 10 | #define BridgingHeader_h 11 | 12 | #import "System.h" 13 | 14 | #endif /* BridgingHeader_h */ 15 | -------------------------------------------------------------------------------- /ModuloKit/System/System.h: -------------------------------------------------------------------------------- 1 | // 2 | // System.h 3 | // modulo 4 | // 5 | // Created by Sneed, Brandon on 12/13/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NSInteger modulo_system(NSString *command); 12 | -------------------------------------------------------------------------------- /ModuloKit/System/System.m: -------------------------------------------------------------------------------- 1 | // 2 | // System.m 3 | // modulo 4 | // 5 | // Created by Sneed, Brandon on 12/13/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | #import "System.h" 10 | 11 | NSInteger modulo_system(NSString *command) { 12 | return system(command.UTF8String); 13 | } 14 | -------------------------------------------------------------------------------- /ModuloKitTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ModuloKitTests/ModuloKitTests-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "XCTestCase+Exceptions.h" 6 | -------------------------------------------------------------------------------- /ModuloKitTests/ModuloKitTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModuloKitTests.swift 3 | // ModuloKitTests 4 | // 5 | // Created by Brandon Sneed on 6/17/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ModuloKit 11 | 12 | class ModuloKitTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ModuloKitTests/TestAdd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestAdd.swift 3 | // modulo 4 | // 5 | // Created by Brandon Sneed on 2/1/16. 6 | // Copyright © 2016 Modulo. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELCLI 11 | import ELFoundation 12 | @testable import ModuloKit 13 | 14 | 15 | class TestAdd: XCTestCase { 16 | let modulo = Modulo() 17 | 18 | override func setUp() { 19 | super.setUp() 20 | moduloReset() 21 | 22 | print("working path = \(FileManager.workingPath())") 23 | } 24 | 25 | override func tearDown() { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | super.tearDown() 28 | } 29 | 30 | func testBasicAddModuleToModule() { 31 | let status = Git().clone("git@github.com:modulo-dm/test-add.git", path: "test-add") 32 | XCTAssertTrue(status == .success) 33 | 34 | FileManager.setWorkingPath("test-add") 35 | 36 | let result = Modulo.run(["add", "git@github.com:modulo-dm/test-add-update.git", "--version", "1.0", "-v"]) 37 | XCTAssertTrue(result == .success) 38 | 39 | let spec = ModuleSpec.load(contentsOfFile: specFilename) 40 | XCTAssertTrue(spec!.dependencies.count > 0) 41 | XCTAssertTrue(spec!.dependencyForURL("git@github.com:modulo-dm/test-add-update.git") != nil) 42 | 43 | FileManager.setWorkingPath("..") 44 | 45 | Git().remove("test-add") 46 | } 47 | 48 | func testBasicAddModuleAlreadyExists() { 49 | let status = Git().clone("git@github.com:modulo-dm/test-add.git", path: "test-add") 50 | XCTAssertTrue(status == .success) 51 | 52 | let status2 = Git().clone("git@github.com:modulo-dm/test-init.git", path: "test-init") 53 | XCTAssertTrue(status2 == .success) 54 | 55 | FileManager.setWorkingPath("test-add") 56 | 57 | let result = Modulo.run(["add", "git@github.com:modulo-dm/test-init.git", "--version", "1.0", "-v", "--update"]) 58 | XCTAssertTrue(result == .dependencyAlreadyExists) 59 | } 60 | 61 | 62 | func testBasicAddModuleToModuleAndUpdate() { 63 | let status = Git().clone("git@github.com:modulo-dm/test-add.git", path: "test-add") 64 | XCTAssertTrue(status == .success) 65 | 66 | FileManager.setWorkingPath("test-add") 67 | 68 | let result = Modulo.run(["add", "git@github.com:modulo-dm/test-add-update.git", "--version", "1.0", "-v", "--update"]) 69 | XCTAssertTrue(result == .success) 70 | 71 | let spec = ModuleSpec.load(contentsOfFile: specFilename) 72 | XCTAssertTrue(spec!.dependencies.count > 0) 73 | XCTAssertTrue(spec!.dependencies[2].repositoryURL == "git@github.com:modulo-dm/test-add-update.git") 74 | 75 | XCTAssertTrue(FileManager.fileExists("../test-add-update/README.md")) 76 | XCTAssertTrue(FileManager.fileExists("../test-dep1/README.md")) 77 | XCTAssertTrue(FileManager.fileExists("../test-dep2/README.md")) 78 | 79 | FileManager.setWorkingPath("..") 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ModuloKitTests/TestCheckout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestCheckout.swift 3 | // modulo 4 | // 5 | // Created by Sneed, Brandon on 1/15/17. 6 | // Copyright © 2017 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ModuloKit 11 | 12 | class TestCheckout: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | moduloReset() 18 | print("working path = \(FileManager.workingPath())") 19 | } 20 | 21 | override func tearDown() { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | super.tearDown() 24 | } 25 | 26 | func testTagCheckout() { 27 | runCommand("mkdir checkout-test") 28 | 29 | FileManager.setWorkingPath("checkout-test") 30 | 31 | runCommand("git init") 32 | 33 | var result = Modulo.run(["init", "--app"]) 34 | XCTAssertTrue(result == .success) 35 | 36 | result = Modulo.run(["add", "git@github.com:modulo-dm/test-checkout.git", "--version", "v2.0.0", "-u", "-v"]) 37 | XCTAssertTrue(result == .success) 38 | 39 | XCTAssertTrue(FileManager.fileExists("modules/test-checkout")) 40 | 41 | let tags = Git().headTagsAtPath("modules/test-checkout") 42 | XCTAssertTrue(tags.contains("v2.0.0")) 43 | } 44 | 45 | func testTagRangeCheckout() { 46 | runCommand("mkdir checkout-test") 47 | 48 | FileManager.setWorkingPath("checkout-test") 49 | 50 | runCommand("git init") 51 | 52 | var result = Modulo.run(["init", "--app"]) 53 | XCTAssertTrue(result == .success) 54 | 55 | result = Modulo.run(["add", "git@github.com:modulo-dm/test-checkout.git", "--version", ">0.0.2 <=2.0.1", "-u", "-v"]) 56 | XCTAssertTrue(result == .success) 57 | 58 | XCTAssertTrue(FileManager.fileExists("modules/test-checkout")) 59 | 60 | let tags = Git().headTagsAtPath("modules/test-checkout") 61 | XCTAssertTrue(tags.contains("v2.0.1")) 62 | } 63 | 64 | func testTagNonSemverFails() { 65 | runCommand("mkdir checkout-test") 66 | 67 | FileManager.setWorkingPath("checkout-test") 68 | 69 | runCommand("git init") 70 | 71 | var result = Modulo.run(["init", "--app"]) 72 | XCTAssertTrue(result == .success) 73 | 74 | result = Modulo.run(["add", "git@github.com:modulo-dm/test-checkout.git", "--version", "nosemver", "-u", "-v"]) 75 | XCTAssertTrue(result == .commandError) 76 | 77 | XCTAssertTrue(!FileManager.fileExists("modules/test-checkout")) 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /ModuloKitTests/TestDummyApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestDummyApp.swift 3 | // modulo 4 | // 5 | // Created by Sneed, Brandon on 12/12/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ModuloKit 11 | 12 | class TestDummyApp: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | moduloReset() 17 | print("working path = \(FileManager.workingPath())") 18 | } 19 | 20 | override func tearDown() { 21 | super.tearDown() 22 | } 23 | 24 | func testFreshStart() { 25 | runCommand("mkdir test-dummy") 26 | 27 | FileManager.setWorkingPath("test-dummy") 28 | 29 | runCommand("git init") 30 | 31 | var result = Modulo.run(["init", "--app"]) 32 | XCTAssertTrue(result == .success) 33 | 34 | result = Modulo.run(["add", "git@github.com:modulo-dm/test-add-update.git", "--unmanaged", "-u", "-v"]) 35 | XCTAssertTrue(result == .success) 36 | 37 | let spec = ModuleSpec.load(contentsOfFile: specFilename) 38 | XCTAssertTrue(spec!.module == false) 39 | XCTAssertTrue(spec!.name == "test-dummy") 40 | XCTAssertTrue(spec!.dependencies.count > 0) 41 | XCTAssertTrue(spec!.dependencyForURL("git@github.com:modulo-dm/test-add-update.git") != nil) 42 | 43 | let checkedOut = Git().branchName("modules/test-add-update") 44 | XCTAssertTrue(checkedOut == "master") 45 | 46 | XCTAssertTrue(FileManager.fileExists("modules/test-add-update")) 47 | XCTAssertTrue(FileManager.fileExists("modules/test-dep1")) 48 | XCTAssertTrue(FileManager.fileExists("modules/test-dep2")) 49 | } 50 | 51 | func testClonedAppStart() { 52 | let status = Git().clone("git@github.com:modulo-dm/test-dummy.git", path: "test-dummy") 53 | XCTAssertTrue(status == .success) 54 | 55 | FileManager.setWorkingPath("test-dummy") 56 | 57 | let result = Modulo.run(["update", "--all", "-v"]) 58 | XCTAssertTrue(result == .success) 59 | 60 | let spec = ModuleSpec.load(contentsOfFile: specFilename) 61 | XCTAssertTrue(spec!.module == false) 62 | XCTAssertTrue(spec!.name == "test-dummy") 63 | XCTAssertTrue(spec!.dependencies.count > 0) 64 | XCTAssertTrue(spec!.dependencyForURL("git@github.com:modulo-dm/test-add-update.git") != nil) 65 | 66 | XCTAssertTrue(FileManager.fileExists("modules/test-add-update")) 67 | XCTAssertTrue(FileManager.fileExists("modules/test-dep1")) 68 | XCTAssertTrue(FileManager.fileExists("modules/test-dep2")) 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /ModuloKitTests/TestGit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestGit.swift 3 | // modulo 4 | // 5 | // Created by Brandon Sneed on 12/3/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELCLI 11 | import ELFoundation 12 | @testable import ModuloKit 13 | 14 | class TestGit: XCTestCase { 15 | 16 | override func setUp() { 17 | super.setUp() 18 | moduloReset() 19 | print("working path = \(FileManager.workingPath())") 20 | } 21 | 22 | override func tearDown() { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | super.tearDown() 25 | } 26 | 27 | func testGettingGitTags() { 28 | let status = Git().clone("git@github.com:modulo-dm/test-checkout.git", path: "test-checkout") 29 | XCTAssertTrue(status == .success) 30 | 31 | let tags = Git().tags("test-checkout").map { 32 | $0.stringValue 33 | } 34 | print(tags) 35 | 36 | Git().remove("test-checkout") 37 | } 38 | 39 | func testGettingBranches() { 40 | let status = Git().clone("git@github.com:modulo-dm/test-checkout.git", path: "test-checkout") 41 | XCTAssertTrue(status == .success) 42 | 43 | let branches = Git().branches("test-checkout") 44 | print(branches) 45 | 46 | Git().remove("test-checkout") 47 | } 48 | 49 | func testAddingToIgnoreFile() { 50 | let status = Git().clone("git@github.com:modulo-dm/test-checkout.git", path: "test-checkout") 51 | XCTAssertTrue(status == .success) 52 | 53 | FileManager.setWorkingPath("test-checkout") 54 | 55 | let localModulesPath = State.instance.modulePathName 56 | let pattern = "testModule" 57 | let textBlob = "\n# Ignore \(pattern) for Modulo.\n\(localModulesPath)/\(pattern)" 58 | 59 | let ignoreFile = "*.*\n*.DS_Store\n*.m\n*.mm" // haha, ignore all the objc's. i kill me. 60 | try! ignoreFile.write(toFile: ".gitignore", atomically: true, encoding: .utf8) 61 | 62 | _ = Git().adjustIgnoreFile(pattern: pattern, removing: false) 63 | 64 | let resultingFile = try! String(contentsOfFile: ".gitignore") 65 | 66 | let found = resultingFile.contains(textBlob) 67 | XCTAssertTrue(found) 68 | 69 | FileManager.setWorkingPath("..") 70 | 71 | Git().remove("test-checkout") 72 | } 73 | 74 | func testRemovingFromIgnoreFile() { 75 | let status = Git().clone("git@github.com:modulo-dm/test-checkout.git", path: "test-checkout") 76 | XCTAssertTrue(status == .success) 77 | 78 | FileManager.setWorkingPath("test-checkout") 79 | 80 | let localModulesPath = State.instance.modulePathName 81 | let pattern = "testModule" 82 | let textBlob = "\n# Ignore \(pattern) for Modulo.\n\(localModulesPath)/\(pattern)" 83 | 84 | let ignoreFile = "*.*\n*.DS_Store\n*.m\n*.mm\n# Ignore testModule for Modulo.\nmodules/testModule" 85 | try! ignoreFile.write(toFile: ".gitignore", atomically: true, encoding: .utf8) 86 | 87 | _ = Git().adjustIgnoreFile(pattern: pattern, removing: true) 88 | 89 | let resultingFile = try! String(contentsOfFile: ".gitignore") 90 | 91 | let found = resultingFile.contains(textBlob) 92 | XCTAssertFalse(found) 93 | 94 | FileManager.setWorkingPath("..") 95 | 96 | Git().remove("test-checkout") 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /ModuloKitTests/TestStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestStatus.swift 3 | // modulo 4 | // 5 | // Created by Brandon Sneed on 7/18/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELCLI 11 | import ELFoundation 12 | @testable import ModuloKit 13 | 14 | 15 | class TestStatus: XCTestCase { 16 | let modulo = Modulo() 17 | 18 | override func setUp() { 19 | super.setUp() 20 | moduloReset() 21 | print("working path = \(FileManager.workingPath())") 22 | } 23 | 24 | override func tearDown() { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | super.tearDown() 27 | } 28 | 29 | func testStatusDepDirty() { 30 | let status = Git().clone("git@github.com:modulo-dm/test-add.git", path: "test-add") 31 | XCTAssertTrue(status == .success) 32 | 33 | FileManager.setWorkingPath("test-add") 34 | 35 | var result = Modulo.run(["update", "--all", "-v"]) 36 | XCTAssertTrue(result == .success) 37 | 38 | touchFile("../test-dep1/blah.txt") 39 | 40 | result = Modulo.run(["status", "-v"]) 41 | XCTAssertTrue(result == .dependencyUnclean) 42 | 43 | FileManager.setWorkingPath("..") 44 | 45 | _ = Git().remove("test-add") 46 | } 47 | 48 | func testStatusMainDirty() { 49 | let status = Git().clone("git@github.com:modulo-dm/test-add.git", path: "test-add") 50 | XCTAssertTrue(status == .success) 51 | 52 | FileManager.setWorkingPath("test-add") 53 | 54 | var result = Modulo.run(["update", "--all", "-v"]) 55 | XCTAssertTrue(result == .success) 56 | 57 | touchFile("blah.txt") 58 | 59 | result = Modulo.run(["status", "-v"]) 60 | XCTAssertTrue(result == .dependencyUnclean) 61 | 62 | FileManager.setWorkingPath("..") 63 | 64 | _ = Git().remove("test-add") 65 | } 66 | 67 | func testStatusMainUnpushed() { 68 | let status = Git().clone("git@github.com:modulo-dm/test-add.git", path: "test-add") 69 | XCTAssertTrue(status == .success) 70 | 71 | FileManager.setWorkingPath("test-add") 72 | 73 | var result = Modulo.run(["update", "--all", "-v"]) 74 | XCTAssertTrue(result == .success) 75 | 76 | touchFile("blah.txt") 77 | runCommand("git add blah.txt") 78 | testCommit("test") 79 | 80 | result = Modulo.run(["status", "-v"]) 81 | XCTAssertTrue(result == .dependencyUnclean) 82 | 83 | FileManager.setWorkingPath("..") 84 | 85 | _ = Git().remove("test-add") 86 | } 87 | 88 | func testStatusDepUnpushed() { 89 | let status = Git().clone("git@github.com:modulo-dm/test-add.git", path: "test-add") 90 | XCTAssertTrue(status == .success) 91 | 92 | FileManager.setWorkingPath("test-add") 93 | 94 | var result = Modulo.run(["update", "--all", "-v"]) 95 | XCTAssertTrue(result == .success) 96 | 97 | FileManager.setWorkingPath("../test-dep1") 98 | 99 | touchFile("blah.txt") 100 | runCommand("git add blah.txt") 101 | testCommit("test") 102 | 103 | FileManager.setWorkingPath("../test-add") 104 | 105 | result = Modulo.run(["status", "-v"]) 106 | XCTAssertTrue(result == .dependencyUnclean) 107 | 108 | FileManager.setWorkingPath("..") 109 | 110 | _ = Git().remove("test-add") 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /ModuloKitTests/TestUpdate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestUpdate.swift 3 | // modulo 4 | // 5 | // Created by Brandon Sneed on 6/27/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import ELCLI 11 | import ELFoundation 12 | @testable import ModuloKit 13 | 14 | 15 | class TestUpdate: XCTestCase { 16 | let modulo = Modulo() 17 | 18 | override func setUp() { 19 | super.setUp() 20 | moduloReset() 21 | print("working path = \(FileManager.workingPath())") 22 | } 23 | 24 | override func tearDown() { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | super.tearDown() 27 | } 28 | 29 | func testUpdateAll() { 30 | let status = Git().clone("git@github.com:modulo-dm/test-add.git", path: "test-add") 31 | XCTAssertTrue(status == .success) 32 | 33 | FileManager.setWorkingPath("test-add") 34 | 35 | let result = Modulo.run(["update", "--all", "-v"]) 36 | XCTAssertTrue(result == .success) 37 | 38 | let spec = ModuleSpec.load(contentsOfFile: specFilename) 39 | XCTAssertTrue(spec!.dependencies.count > 0) 40 | XCTAssertTrue(spec!.dependencies[0].repositoryURL == "git@github.com:modulo-dm/test-init.git") 41 | 42 | XCTAssertTrue(FileManager.pathExists("../test-init")) 43 | XCTAssertTrue(FileManager.pathExists("../test-dep1")) 44 | XCTAssertTrue(FileManager.pathExists("../test-dep2")) 45 | 46 | FileManager.setWorkingPath("..") 47 | 48 | Git().remove("test-add") 49 | } 50 | 51 | func testUpdateOneModule() { 52 | let status = Git().clone("git@github.com:modulo-dm/test-add.git", path: "test-add") 53 | XCTAssertTrue(status == .success) 54 | 55 | FileManager.setWorkingPath("test-add") 56 | 57 | let result = Modulo.run(["update", "test-init"]) 58 | XCTAssertTrue(result == .success) 59 | 60 | let spec = ModuleSpec.load(contentsOfFile: specFilename) 61 | XCTAssertTrue(spec!.dependencies.count > 0) 62 | XCTAssertTrue(spec!.dependencies[0].repositoryURL == "git@github.com:modulo-dm/test-init.git") 63 | 64 | XCTAssertTrue(FileManager.pathExists("../test-init")) 65 | 66 | FileManager.setWorkingPath("..") 67 | 68 | Git().remove("test-add") 69 | } 70 | 71 | func testUpdatePull() { 72 | let status = Git().clone("git@github.com:modulo-dm/test-add.git", path: "test-add") 73 | XCTAssertTrue(status == .success) 74 | 75 | FileManager.setWorkingPath("test-add") 76 | 77 | var result = Modulo.run(["update", "-v", "--all"]) 78 | XCTAssertTrue(result == .success) 79 | 80 | let spec = ModuleSpec.load(contentsOfFile: specFilename) 81 | XCTAssertTrue(spec!.dependencies.count > 0) 82 | XCTAssertTrue(spec!.dependencies[0].repositoryURL == "git@github.com:modulo-dm/test-init.git") 83 | 84 | XCTAssertTrue(FileManager.pathExists("../test-init")) 85 | 86 | // now run it all again and make sure it does a fetch/pull on the branches 87 | State.instance.clear() 88 | result = Modulo.run(["update", "-v", "--all"]) 89 | XCTAssertTrue(status == .success) 90 | 91 | FileManager.setWorkingPath("..") 92 | 93 | Git().remove("test-add") 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /ModuloKitTests/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // modulo 4 | // 5 | // Created by Brandon Sneed on 7/11/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ELCLI 11 | import ELFoundation 12 | @testable import ModuloKit 13 | 14 | func moduloReset() { 15 | FileManager.setWorkingPath("/private/tmp") 16 | Git().remove("test-dummy") 17 | Git().remove("test-add") 18 | Git().remove("test-init") 19 | Git().remove("test-add-update") 20 | Git().remove("test-dep1") 21 | Git().remove("test-dep2") 22 | Git().remove("test-checkout") 23 | Git().remove("checkout-test") 24 | Git().remove("test-simeon") 25 | 26 | State.instance.clear() 27 | } 28 | 29 | func touchFile(_ path: String) { 30 | try! path.write(toFile: path, atomically: true, encoding: String.Encoding.utf8) 31 | } 32 | 33 | func runCommand(_ command: String) { 34 | Git().runCommand(command) 35 | } 36 | 37 | func testCommit(_ message: String) { 38 | Git().runCommand("git commit -m \"\(message)\" --no-verify") 39 | } 40 | -------------------------------------------------------------------------------- /ModuloKitTests/XCTestCase+Exceptions.h: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | These files are not to be included in any targets except test case targets. 4 | Simply drag them to your project and make sure only the Tests target is checked. 5 | You'll be prompted to create an objc-bridging header. Say yes, then add 6 | this to the newly created header: 7 | 8 | #import "XCTestCase+Exceptions.h" 9 | 10 | These methods should now be accessible from within Swift without doing 11 | anything additional. 12 | 13 | */ 14 | 15 | @import Foundation; 16 | @import XCTest; 17 | 18 | @interface XCTestCase (Exceptions) 19 | 20 | /** 21 | Replacement for the stock objc XCTAssertThrows, which is unavailable in Swift. 22 | 23 | :param: block The block to execute. 24 | :param: message The message to be displayed on failure to throw an exception. 25 | 26 | Example (Swift): XCTAssertThrows({ testThrow() }, "This method should've thrown an exception!") 27 | */ 28 | - (void)XCTAssertThrows:(void (^)(void))block :(NSString *)message; 29 | 30 | /** 31 | Replacement for the stock objc XCTAssertThrowsSpecific, which is unavailable in Swift. 32 | 33 | :param: block The block to execute. 34 | :param: name The name of the assertion to look for. 35 | :param: message The message to be displayed on failure to throw an exception. 36 | 37 | Example (Swift): XCTAssertThrowsSpecific({ testThrow() }, "THG", "This method should've thrown a THG exception!") 38 | */ 39 | - (void)XCTAssertThrowsSpecific:(void (^)(void))block :(NSString *)name :(NSString *)message; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /ModuloKitTests/XCTestCase+Exceptions.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | These files are not to be included in any targets except test case targets. 4 | Simply drag them to your project and make sure only the Tests target is checked. 5 | You'll be prompted to create an objc-bridging header. Say yes, then add 6 | this to the newly created header: 7 | 8 | #import "XCTestCase+Exceptions.h" 9 | 10 | These methods should now be accessible from within Swift without doing 11 | anything additional. 12 | 13 | */ 14 | 15 | #import "XCTestCase+Exceptions.h" 16 | 17 | @implementation XCTestCase (Exceptions) 18 | 19 | - (void)XCTAssertThrows:(void (^)(void))block :(NSString *)message { 20 | XCTAssertThrows(block(), @"%@", message); 21 | } 22 | 23 | - (void)XCTAssertThrowsSpecific:(void (^)(void))block :(NSString *)exceptionName :(NSString *)message { 24 | BOOL __didThrow = NO; 25 | @try { 26 | block(); 27 | } 28 | @catch (NSException *exception) { 29 | __didThrow = YES; 30 | XCTAssertEqualObjects(exception.name, exceptionName, @"%@", message); 31 | } 32 | @catch (...) { 33 | __didThrow = YES; 34 | XCTFail(@"%@", message); 35 | } 36 | 37 | if (!__didThrow) { 38 | XCTFail(@"%@", message); 39 | } 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /modulo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /modulo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /modulo.xcodeproj/project.xcworkspace/xcshareddata/modulo.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "CDCC524B3D0D75DAC1D1CE8037A686D7006F68B7", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "8793D34A7BF94DA191D8F935B1E0425A8C1B2681" : 0, 8 | "CDCC524B3D0D75DAC1D1CE8037A686D7006F68B7" : 0, 9 | "3AA9B528BC4DFC86D9B300333E6A7EF9B619AC2B" : 0, 10 | "B78D931F23AF2B103B07F472BC9BDF733D7C9BCF" : 0 11 | }, 12 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "4B054CB3-2BAA-4373-AFFD-D86A0889C2A3", 13 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 14 | "8793D34A7BF94DA191D8F935B1E0425A8C1B2681" : "modulo\/modules\/ELFoundation\/", 15 | "CDCC524B3D0D75DAC1D1CE8037A686D7006F68B7" : "modulo\/", 16 | "3AA9B528BC4DFC86D9B300333E6A7EF9B619AC2B" : "modulo\/modules\/ELCLI\/", 17 | "B78D931F23AF2B103B07F472BC9BDF733D7C9BCF" : "modulo\/modules\/ELCodable\/" 18 | }, 19 | "DVTSourceControlWorkspaceBlueprintNameKey" : "modulo", 20 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 21 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "modulo.xcodeproj", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 23 | { 24 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:Electrode-iOS\/ELCLI.git", 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "3AA9B528BC4DFC86D9B300333E6A7EF9B619AC2B" 27 | }, 28 | { 29 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:Electrode-iOS\/ELFoundation.git", 30 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 31 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8793D34A7BF94DA191D8F935B1E0425A8C1B2681" 32 | }, 33 | { 34 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:Electrode-iOS\/ELCodable.git", 35 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 36 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "B78D931F23AF2B103B07F472BC9BDF733D7C9BCF" 37 | }, 38 | { 39 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:modulo-dm\/modulo.git", 40 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 41 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "CDCC524B3D0D75DAC1D1CE8037A686D7006F68B7" 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /modulo.xcodeproj/project.xcworkspace/xcuserdata/brandonsneed.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modulo-dm/modulo/b401f94ffee31a9359293222e2ec09974f9f475c/modulo.xcodeproj/project.xcworkspace/xcuserdata/brandonsneed.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /modulo.xcodeproj/xcuserdata/brandon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /modulo.xcodeproj/xcuserdata/brandon.xcuserdatad/xcschemes/ModuloKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /modulo.xcodeproj/xcuserdata/brandon.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ModuloKit.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | modulo.xcscheme 13 | 14 | isShown 15 | 16 | orderHint 17 | 0 18 | 19 | 20 | SuppressBuildableAutocreation 21 | 22 | CAE55C401D14928B00411727 23 | 24 | primary 25 | 26 | 27 | CAE55C4F1D1492A500411727 28 | 29 | primary 30 | 31 | 32 | CAE55C581D1492A500411727 33 | 34 | primary 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /modulo.xcodeproj/xcuserdata/brandonsneed.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /modulo.xcodeproj/xcuserdata/brandonsneed.xcuserdatad/xcschemes/modulo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /modulo.xcodeproj/xcuserdata/brandonsneed.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ModuloKit.xcscheme 8 | 9 | orderHint 10 | 4 11 | 12 | modulo.xcscheme 13 | 14 | orderHint 15 | 3 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | CAE55C401D14928B00411727 21 | 22 | primary 23 | 24 | 25 | CAE55C4F1D1492A500411727 26 | 27 | primary 28 | 29 | 30 | CAE55C581D1492A500411727 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /modulo.xcodeproj/xcuserdata/brsneed.xcuserdatad/xcschemes/ModuloKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /modulo.xcodeproj/xcuserdata/brsneed.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ModuloKit.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | modulo.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | CAE55C401D14928B00411727 21 | 22 | primary 23 | 24 | 25 | CAE55C4F1D1492A500411727 26 | 27 | primary 28 | 29 | 30 | CAE55C581D1492A500411727 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /modulo/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // modulo 4 | // 5 | // Created by Brandon Sneed on 6/17/16. 6 | // Copyright © 2016 TheHolyGrail. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // such complexity! :D 12 | 13 | /* 14 | 15 | The general idea is that we build with source, but we set ModuloKit as a target 16 | dependency. That'll make sure it builds before modulo does. Modulo then includes 17 | the source files from ModuloKit and the frameworks it depends on. This will 18 | ensure that frameworks continue to build as changes are made in both source-only 19 | and framework form. 20 | 21 | ModuloKit then gives us a place by which we can write unit tests. 22 | 23 | */ 24 | 25 | Modulo.run() 26 | 27 | --------------------------------------------------------------------------------