COMMIT TEST

This commit is contained in:
tapframe 2025-09-17 23:22:01 +05:30
parent c767de12aa
commit 5372513142
340 changed files with 65322 additions and 77 deletions

3
.gitignore vendored
View file

@ -42,7 +42,7 @@ CHANGELOG.md
.env.local
android/
HEATING_OPTIMIZATIONS.md
ios
android
sliderreadme.md
.cursor/mcp.json
@ -53,3 +53,4 @@ hackintosh-emulator-fix.sh
/ota-builds
src/screens/xavio.md
/nuvio-providers
/KSPlayer

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController modalTransitionStyle="crossDissolve" id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" autoresizesSubviews="NO" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView autoresizesSubviews="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" image="BootSplashLogo-7d142f" translatesAutoresizingMaskIntoConstraints="NO" id="3lX-Ut-9ad">
<rect key="frame" x="127.5" y="273.5" width="120" height="120"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
</accessibility>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
<color key="backgroundColor" name="BootSplashBackground-7d142f"/>
<constraints>
<constraint firstItem="3lX-Ut-9ad" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="Fh9-Fy-1nT"/>
<constraint firstItem="3lX-Ut-9ad" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="nvB-Ic-PnI"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="0.0" y="0.0"/>
</scene>
</scenes>
<resources>
<image name="BootSplashLogo-7d142f" width="120" height="120"/>
<namedColor name="BootSplashBackground-7d142f">
<color red="0.00784313725490196" green="0.0156862745098039" blue="0.0156862745098039" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View file

@ -0,0 +1,20 @@
{
"colors": [
{
"idiom": "universal",
"color": {
"color-space": "srgb",
"components": {
"blue": "0.0156862745098039",
"green": "0.0156862745098039",
"red": "0.00784313725490196",
"alpha": "1.000"
}
}
}
],
"info": {
"author": "xcode",
"version": 1
}
}

View file

@ -0,0 +1,23 @@
{
"images": [
{
"idiom": "universal",
"filename": "logo-7d142f.png",
"scale": "1x"
},
{
"idiom": "universal",
"filename": "logo-7d142f@2x.png",
"scale": "2x"
},
{
"idiom": "universal",
"filename": "logo-7d142f@3x.png",
"scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

2
ios/.bundle/config Normal file
View file

@ -0,0 +1,2 @@
---
BUNDLE_PATH: "vendor/bundle"

30
ios/.gitignore vendored Normal file
View file

@ -0,0 +1,30 @@
# OSX
#
.DS_Store
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
.xcode.env.local
# Bundle artifacts
*.jsbundle
# CocoaPods
/Pods/

11
ios/.xcode.env Normal file
View file

@ -0,0 +1,11 @@
# This `.xcode.env` file is versioned and is used to source the environment
# used when running script phases inside Xcode.
# To customize your local environment, you can create an `.xcode.env.local`
# file that is not versioned.
# NODE_BINARY variable contains the PATH to the node executable.
#
# Customize the NODE_BINARY variable here.
# For example, to use nvm with brew, add the following line
# . "$(brew --prefix nvm)/nvm.sh" --no-use
export NODE_BINARY=$(command -v node)

3
ios/Gemfile Normal file
View file

@ -0,0 +1,3 @@
source 'https://rubygems.org'
gem 'xcodeproj', '~> 1.24'

30
ios/Gemfile.lock Normal file
View file

@ -0,0 +1,30 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.7)
base64
nkf
rexml
atomos (0.1.3)
base64 (0.3.0)
claide (1.1.0)
colored2 (3.1.2)
nanaimo (0.4.0)
nkf (0.2.0)
rexml (3.4.4)
xcodeproj (1.27.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
PLATFORMS
ruby
DEPENDENCIES
xcodeproj (~> 1.24)
BUNDLED WITH
1.17.2

View file

@ -0,0 +1,609 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
4A452C8AF8F8464E8F6C4499 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F4D4C2B75D490FA484DF2F /* noop-file.swift */; };
53A1AFA19BDA4A9CEC2C0758 /* KSPlayerViewManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F3DC5D187506A1702CBDCE7 /* KSPlayerViewManager.swift */; };
554BC3EE7AB3699FD2A5F6A6 /* KSPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94E57CA110F3B584C9EB54FF /* KSPlayerView.swift */; };
6A3072369CBE46F6A5531727 /* KSPlayerModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1859BB4D441C265493A728B0 /* KSPlayerModule.swift */; };
96905EF65AED1B983A6B3ABC /* libPods-Nuvio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Nuvio.a */; };
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
B1EEE41563DBC5313A090C06 /* KSPlayerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 677190A93C7E1E59AC68D165 /* KSPlayerManager.m */; };
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
C7F40B0D74A68CE28765494D /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 88706B115BE5800B1B31F65D /* PrivacyInfo.xcprivacy */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
02F4D4C2B75D490FA484DF2F /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "Nuvio/noop-file.swift"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* Nuvio.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Nuvio.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Nuvio/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = Nuvio/AppDelegate.mm; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Nuvio/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Nuvio/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Nuvio/main.m; sourceTree = "<group>"; };
1859BB4D441C265493A728B0 /* KSPlayerModule.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KSPlayerModule.swift; path = Nuvio/KSPlayerModule.swift; sourceTree = "<group>"; };
1F3DC5D187506A1702CBDCE7 /* KSPlayerViewManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KSPlayerViewManager.swift; path = Nuvio/KSPlayerViewManager.swift; sourceTree = "<group>"; };
1F4096E3944543A28521AEEE /* Nuvio-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "Nuvio-Bridging-Header.h"; path = "Nuvio/Nuvio-Bridging-Header.h"; sourceTree = "<group>"; };
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Nuvio.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Nuvio.a"; sourceTree = BUILT_PRODUCTS_DIR; };
677190A93C7E1E59AC68D165 /* KSPlayerManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = KSPlayerManager.m; path = Nuvio/KSPlayerManager.m; sourceTree = "<group>"; };
6C2E3173556A471DD304B334 /* Pods-Nuvio.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nuvio.debug.xcconfig"; path = "Target Support Files/Pods-Nuvio/Pods-Nuvio.debug.xcconfig"; sourceTree = "<group>"; };
7A4D352CD337FB3A3BF06240 /* Pods-Nuvio.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nuvio.release.xcconfig"; path = "Target Support Files/Pods-Nuvio/Pods-Nuvio.release.xcconfig"; sourceTree = "<group>"; };
88706B115BE5800B1B31F65D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Nuvio/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
94E57CA110F3B584C9EB54FF /* KSPlayerView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KSPlayerView.swift; path = Nuvio/KSPlayerView.swift; sourceTree = "<group>"; };
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Nuvio/SplashScreen.storyboard; sourceTree = "<group>"; };
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Nuvio/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
96905EF65AED1B983A6B3ABC /* libPods-Nuvio.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
13B07FAE1A68108700A75B9A /* Nuvio */ = {
isa = PBXGroup;
children = (
BB2F792B24A3F905000567C9 /* Supporting */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB71A68108700A75B9A /* main.m */,
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
02F4D4C2B75D490FA484DF2F /* noop-file.swift */,
1F4096E3944543A28521AEEE /* Nuvio-Bridging-Header.h */,
88706B115BE5800B1B31F65D /* PrivacyInfo.xcprivacy */,
94E57CA110F3B584C9EB54FF /* KSPlayerView.swift */,
1F3DC5D187506A1702CBDCE7 /* KSPlayerViewManager.swift */,
677190A93C7E1E59AC68D165 /* KSPlayerManager.m */,
1859BB4D441C265493A728B0 /* KSPlayerModule.swift */,
);
name = Nuvio;
sourceTree = "<group>";
};
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
isa = PBXGroup;
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Nuvio.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
);
name = Libraries;
sourceTree = "<group>";
};
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
13B07FAE1A68108700A75B9A /* Nuvio */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
D65327D7A22EEC0BE12398D9 /* Pods */,
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* Nuvio.app */,
);
name = Products;
sourceTree = "<group>";
};
92DBD88DE9BF7D494EA9DA96 /* Nuvio */ = {
isa = PBXGroup;
children = (
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */,
);
name = Nuvio;
sourceTree = "<group>";
};
BB2F792B24A3F905000567C9 /* Supporting */ = {
isa = PBXGroup;
children = (
BB2F792C24A3F905000567C9 /* Expo.plist */,
);
name = Supporting;
path = Nuvio/Supporting;
sourceTree = "<group>";
};
D65327D7A22EEC0BE12398D9 /* Pods */ = {
isa = PBXGroup;
children = (
6C2E3173556A471DD304B334 /* Pods-Nuvio.debug.xcconfig */,
7A4D352CD337FB3A3BF06240 /* Pods-Nuvio.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = {
isa = PBXGroup;
children = (
92DBD88DE9BF7D494EA9DA96 /* Nuvio */,
);
name = ExpoModulesProviders;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
13B07F861A680F5B00A75B9A /* Nuvio */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Nuvio" */;
buildPhases = (
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
1AF4057A2E2F0FE927F61E3C /* [Expo] Configure project */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
3F8163D8C03947BCA4B429B4 /* Upload Debug Symbols to Sentry */,
183E75605458C347967551DC /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Nuvio;
productName = Nuvio;
productReference = 13B07F961A680F5B00A75B9A /* Nuvio.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1130;
TargetAttributes = {
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = NLXTHANK2N;
LastSwiftMigration = 1250;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Nuvio" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 83CBB9F61A601CBA00E9B192;
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* Nuvio */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
13B07F8E1A680F5B00A75B9A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
C7F40B0D74A68CE28765494D /* PrivacyInfo.xcprivacy in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Bundle React Native code and images";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n/bin/sh `\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode.sh'\"` `\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n";
};
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Nuvio-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
183E75605458C347967551DC /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Nuvio/Pods-Nuvio-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/MobileVLCKit/MobileVLCKit.framework/MobileVLCKit",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MobileVLCKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Nuvio/Pods-Nuvio-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
1AF4057A2E2F0FE927F61E3C /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "[Expo] Configure project";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Nuvio/expo-configure-project.sh\"\n";
};
3F8163D8C03947BCA4B429B4 /* Upload Debug Symbols to Sentry */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Upload Debug Symbols to Sentry";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh `${NODE_BINARY:-node} --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode-debug-files.sh'\"`";
};
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Nuvio/Pods-Nuvio-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXNotifications/ExpoNotifications_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoLocalization/ExpoLocalization_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/KSPlayer/KSPlayer_KSPlayer.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift/ReachabilitySwift.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/Sentry/Sentry.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/LottiePrivacyInfo.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/lottie-react-native/Lottie_React_Native_Privacy.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoNotifications_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoLocalization_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/KSPlayer_KSPlayer.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ReachabilitySwift.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Sentry.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LottiePrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Lottie_React_Native_Privacy.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Nuvio/Pods-Nuvio-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
13B07F871A680F5B00A75B9A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */,
4A452C8AF8F8464E8F6C4499 /* noop-file.swift in Sources */,
554BC3EE7AB3699FD2A5F6A6 /* KSPlayerView.swift in Sources */,
53A1AFA19BDA4A9CEC2C0758 /* KSPlayerViewManager.swift in Sources */,
B1EEE41563DBC5313A090C06 /* KSPlayerManager.m in Sources */,
6A3072369CBE46F6A5531727 /* KSPlayerModule.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-Nuvio.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Nuvio/Nuvio.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = NLXTHANK2N;
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"FB_SONARKIT_ENABLED=1",
);
INFOPLIST_FILE = Nuvio/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-lc++",
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.app;
PRODUCT_NAME = Nuvio;
SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-Nuvio.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Nuvio/Nuvio.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = NLXTHANK2N;
INFOPLIST_FILE = Nuvio/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-lc++",
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.app;
PRODUCT_NAME = Nuvio;
SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
83CBBA201A601CBA00E9B192 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
USE_HERMES = true;
};
name = Debug;
};
83CBBA211A601CBA00E9B192 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Nuvio" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13B07F941A680F5B00A75B9A /* Debug */,
13B07F951A680F5B00A75B9A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Nuvio" */ = {
isa = XCConfigurationList;
buildConfigurations = (
83CBBA201A601CBA00E9B192 /* Debug */,
83CBBA211A601CBA00E9B192 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
}

View file

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1130"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "Nuvio.app"
BlueprintName = "Nuvio"
ReferencedContainer = "container:Nuvio.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "NuvioTests.xctest"
BlueprintName = "NuvioTests"
ReferencedContainer = "container:Nuvio.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "Nuvio.app"
BlueprintName = "Nuvio"
ReferencedContainer = "container:Nuvio.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "Nuvio.app"
BlueprintName = "Nuvio"
ReferencedContainer = "container:Nuvio.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Nuvio.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

7
ios/Nuvio/AppDelegate.h Normal file
View file

@ -0,0 +1,7 @@
#import <RCTAppDelegate.h>
#import <UIKit/UIKit.h>
#import <Expo/Expo.h>
@interface AppDelegate : EXAppDelegateWrapper
@end

62
ios/Nuvio/AppDelegate.mm Normal file
View file

@ -0,0 +1,62 @@
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTLinkingManager.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleName = @"main";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
return [self bundleURL];
}
- (NSURL *)bundleURL
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
// Linking API
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
}
// Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result;
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
@end

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

View file

@ -0,0 +1,14 @@
{
"images": [
{
"filename": "App-Icon-1024x1024@1x.png",
"idiom": "universal",
"platform": "ios",
"size": "1024x1024"
}
],
"info": {
"version": 1,
"author": "expo"
}
}

View file

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "expo"
}
}

View file

@ -0,0 +1,20 @@
{
"colors": [
{
"color": {
"components": {
"alpha": "1.000",
"blue": "0.0156862745098039",
"green": "0.0156862745098039",
"red": "0.00784313725490196"
},
"color-space": "srgb"
},
"idiom": "universal"
}
],
"info": {
"version": 1,
"author": "expo"
}
}

View file

@ -0,0 +1,23 @@
{
"images": [
{
"idiom": "universal",
"filename": "image.png",
"scale": "1x"
},
{
"idiom": "universal",
"filename": "image@2x.png",
"scale": "2x"
},
{
"idiom": "universal",
"filename": "image@3x.png",
"scale": "3x"
}
],
"info": {
"version": 1,
"author": "expo"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

99
ios/Nuvio/Info.plist Normal file
View file

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Nuvio</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>stremioexpo</string>
<string>com.nuvio.app</string>
</array>
</dict>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>exp+nuvio</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>12</string>
<key>LSMinimumSystemVersion</key>
<string>12.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSBonjourServices</key>
<array>
<string>_http._tcp</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>App uses the local network to discover and connect to devices.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app does not require microphone access.</string>
<key>RCTRootViewBackgroundColor</key>
<integer>4278322180</integer>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>SplashScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UIRequiresFullScreen</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDefault</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Dark</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

View file

@ -0,0 +1,41 @@
//
// KSPlayerManager.m
// Nuvio
//
// Created by KSPlayer integration
//
#import <React/RCTViewManager.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface RCT_EXTERN_MODULE(KSPlayerViewManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
RCT_EXPORT_VIEW_PROPERTY(paused, BOOL)
RCT_EXPORT_VIEW_PROPERTY(volume, NSNumber)
RCT_EXPORT_VIEW_PROPERTY(audioTrack, NSNumber)
RCT_EXPORT_VIEW_PROPERTY(textTrack, NSNumber)
// Event properties
RCT_EXPORT_VIEW_PROPERTY(onLoad, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onProgress, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onBuffering, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onEnd, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onBufferingProgress, RCTDirectEventBlock)
RCT_EXTERN_METHOD(seek:(nonnull NSNumber *)node toTime:(nonnull NSNumber *)time)
RCT_EXTERN_METHOD(setSource:(nonnull NSNumber *)node source:(nonnull NSDictionary *)source)
RCT_EXTERN_METHOD(setPaused:(nonnull NSNumber *)node paused:(BOOL)paused)
RCT_EXTERN_METHOD(setVolume:(nonnull NSNumber *)node volume:(nonnull NSNumber *)volume)
RCT_EXTERN_METHOD(setAudioTrack:(nonnull NSNumber *)node trackId:(nonnull NSNumber *)trackId)
RCT_EXTERN_METHOD(setTextTrack:(nonnull NSNumber *)node trackId:(nonnull NSNumber *)trackId)
@end
@interface RCT_EXTERN_MODULE(KSPlayerModule, RCTEventEmitter)
RCT_EXTERN_METHOD(getTracks:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@end

View file

@ -0,0 +1,36 @@
//
// KSPlayerModule.swift
// Nuvio
//
// Created by KSPlayer integration
//
import Foundation
import KSPlayer
import React
@objc(KSPlayerModule)
class KSPlayerModule: RCTEventEmitter {
override static func requiresMainQueueSetup() -> Bool {
return true
}
override func supportedEvents() -> [String]! {
return [
"KSPlayer-onLoad",
"KSPlayer-onProgress",
"KSPlayer-onBuffering",
"KSPlayer-onEnd",
"KSPlayer-onError"
]
}
@objc func getTracks(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
// This method can be expanded to get track information
// For now, return empty tracks
resolve([
"audioTracks": [],
"textTracks": []
])
}
}

View file

@ -0,0 +1,281 @@
//
// KSPlayerView.swift
// Nuvio
//
// Created by KSPlayer integration
//
import Foundation
import KSPlayer
import React
@objc(KSPlayerView)
class KSPlayerView: UIView {
private var playerView: IOSVideoPlayerView!
private var currentSource: NSDictionary?
private var isPaused = false
private var currentVolume: Float = 1.0
weak var viewManager: KSPlayerViewManager?
// Event blocks for Fabric
@objc var onLoad: RCTDirectEventBlock?
@objc var onProgress: RCTDirectEventBlock?
@objc var onBuffering: RCTDirectEventBlock?
@objc var onEnd: RCTDirectEventBlock?
@objc var onError: RCTDirectEventBlock?
@objc var onBufferingProgress: RCTDirectEventBlock?
// Property setters that React Native will call
@objc var source: NSDictionary? {
didSet {
if let source = source {
setSource(source)
}
}
}
@objc var paused: Bool = false {
didSet {
setPaused(paused)
}
}
@objc var volume: NSNumber = 1.0 {
didSet {
setVolume(volume.floatValue)
}
}
@objc var audioTrack: NSNumber = -1 {
didSet {
setAudioTrack(audioTrack.intValue)
}
}
@objc var textTrack: NSNumber = -1 {
didSet {
setTextTrack(textTrack.intValue)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupPlayerView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupPlayerView()
}
private func setupPlayerView() {
playerView = IOSVideoPlayerView()
playerView.translatesAutoresizingMaskIntoConstraints = false
addSubview(playerView)
NSLayoutConstraint.activate([
playerView.topAnchor.constraint(equalTo: topAnchor),
playerView.leadingAnchor.constraint(equalTo: leadingAnchor),
playerView.trailingAnchor.constraint(equalTo: trailingAnchor),
playerView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
// Set up player delegates and callbacks
setupPlayerCallbacks()
}
private func setupPlayerCallbacks() {
// Set up the player layer delegate
playerView.playerLayer?.delegate = self
// Configure KSOptions (use static defaults where required)
KSOptions.isAutoPlay = false
}
func setSource(_ source: NSDictionary) {
currentSource = source
guard let uri = source["uri"] as? String else {
print("KSPlayerView: No URI provided")
return
}
var headers: [String: String] = [:]
if let headersDict = source["headers"] as? [String: String] {
headers = headersDict
}
// Create KSPlayerResource
let url = URL(string: uri)!
let resource = KSPlayerResource(url: url, options: createOptions(with: headers), name: "Video")
print("KSPlayerView: Setting source: \(uri)")
playerView.set(resource: resource)
// Apply current state
if isPaused {
playerView.pause()
} else {
playerView.play()
}
setVolume(currentVolume)
}
private func createOptions(with headers: [String: String]) -> KSOptions {
let options = KSOptions()
options.hardwareDecode = KSOptions.hardwareDecode
if !headers.isEmpty {
options.appendHeader(headers)
if let referer = headers["Referer"] ?? headers["referer"] {
options.referer = referer
}
}
return options
}
func setPaused(_ paused: Bool) {
isPaused = paused
if paused {
playerView.pause()
} else {
playerView.play()
}
}
func setVolume(_ volume: Float) {
currentVolume = volume
playerView.playerLayer?.player.playbackVolume = volume
}
func seek(to time: TimeInterval) {
playerView.seek(time: time) { _ in }
}
func setAudioTrack(_ trackId: Int) {
if let player = playerView.playerLayer?.player {
let audioTracks = player.tracks(mediaType: .audio)
if trackId >= 0 && trackId < audioTracks.count {
// Enable only the selected track
for (index, track) in audioTracks.enumerated() {
track.isEnabled = (index == trackId)
}
}
}
}
func setTextTrack(_ trackId: Int) {
if let player = playerView.playerLayer?.player {
let textTracks = player.tracks(mediaType: .subtitle)
if trackId >= 0 && trackId < textTracks.count {
for (index, track) in textTracks.enumerated() {
track.isEnabled = (index == trackId)
}
} else if trackId == -1 {
// Disable all subtitles
for track in textTracks { track.isEnabled = false }
}
}
}
// Get current player state for React Native
func getCurrentState() -> [String: Any] {
guard let player = playerView.playerLayer?.player else {
return [:]
}
return [
"currentTime": player.currentPlaybackTime,
"duration": player.duration,
"buffered": player.playableTime,
"isPlaying": !isPaused,
"volume": currentVolume
]
}
}
extension KSPlayerView: KSPlayerLayerDelegate {
func player(layer: KSPlayerLayer, state: KSPlayerState) {
switch state {
case .readyToPlay:
// Send onLoad event to React Native
let p = layer.player
sendEvent("onLoad", [
"duration": p.duration,
"currentTime": p.currentPlaybackTime,
"naturalSize": [
"width": p.naturalSize.width,
"height": p.naturalSize.height
]
])
case .buffering:
sendEvent("onBuffering", ["isBuffering": true])
case .bufferFinished:
sendEvent("onBuffering", ["isBuffering": false])
case .playedToTheEnd:
sendEvent("onEnd", [:])
case .error:
// Error will be handled by the finish delegate method
break
default:
break
}
}
func player(layer: KSPlayerLayer, currentTime: TimeInterval, totalTime: TimeInterval) {
let p = layer.player
sendEvent("onProgress", [
"currentTime": currentTime,
"duration": totalTime,
"bufferTime": p.playableTime
])
}
func player(layer: KSPlayerLayer, finish error: Error?) {
if let error = error {
sendEvent("onError", ["error": error.localizedDescription])
}
}
func player(layer: KSPlayerLayer, bufferedCount: Int, consumeTime: TimeInterval) {
// Handle buffering progress if needed
sendEvent("onBufferingProgress", [
"bufferedCount": bufferedCount,
"consumeTime": consumeTime
])
}
}
extension KSPlayerView {
private func sendEvent(_ eventName: String, _ body: [String: Any]) {
DispatchQueue.main.async {
switch eventName {
case "onLoad":
self.onLoad?(body)
case "onProgress":
self.onProgress?(body)
case "onBuffering":
self.onBuffering?(body)
case "onEnd":
self.onEnd?([:])
case "onError":
self.onError?(body)
case "onBufferingProgress":
self.onBufferingProgress?(body)
default:
break
}
}
}
// Renamed to avoid clashing with React's UIView category method
private func findHostViewController() -> UIViewController? {
var responder: UIResponder? = self
while let nextResponder = responder?.next {
if let viewController = nextResponder as? UIViewController {
return viewController
}
responder = nextResponder
}
return nil
}
}

View file

@ -0,0 +1,88 @@
//
// KSPlayerViewManager.swift
// Nuvio
//
// Created by KSPlayer integration
//
import Foundation
import KSPlayer
import React
@objc(KSPlayerViewManager)
class KSPlayerViewManager: RCTViewManager {
// Not needed for RCTViewManager-based views; events are exported via RCT_EXPORT_VIEW_PROPERTY
override func view() -> UIView! {
let view = KSPlayerView()
view.viewManager = self
return view
}
override static func requiresMainQueueSetup() -> Bool {
return true
}
override func constantsToExport() -> [AnyHashable : Any]! {
return [
"EventTypes": [
"onLoad": "onLoad",
"onProgress": "onProgress",
"onBuffering": "onBuffering",
"onEnd": "onEnd",
"onError": "onError",
"onBufferingProgress": "onBufferingProgress"
]
]
}
// No-op: events are sent via direct event blocks on the view
@objc func seek(_ node: NSNumber, toTime time: NSNumber) {
DispatchQueue.main.async {
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
view.seek(to: TimeInterval(truncating: time))
}
}
}
@objc func setSource(_ node: NSNumber, source: NSDictionary) {
DispatchQueue.main.async {
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
view.setSource(source)
}
}
}
@objc func setPaused(_ node: NSNumber, paused: Bool) {
DispatchQueue.main.async {
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
view.setPaused(paused)
}
}
}
@objc func setVolume(_ node: NSNumber, volume: NSNumber) {
DispatchQueue.main.async {
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
view.setVolume(Float(truncating: volume))
}
}
}
@objc func setAudioTrack(_ node: NSNumber, trackId: NSNumber) {
DispatchQueue.main.async {
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
view.setAudioTrack(Int(truncating: trackId))
}
}
}
@objc func setTextTrack(_ node: NSNumber, trackId: NSNumber) {
DispatchQueue.main.async {
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
view.setTextTrack(Int(truncating: trackId))
}
}
}
}

View file

@ -0,0 +1,11 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
// React Native imports for bridge
#import <React/RCTBridgeModule.h>
#import <React/RCTViewManager.h>
#import <React/RCTEventEmitter.h>
#import <React/RCTUtils.h>
#import <React/RCTConvert.h>
// Avoid importing deprecated/private headers that can break module maps

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.associated-domains</key>
<array/>
</dict>
</plist>

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
<string>0A2A.1</string>
<string>3B52.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>E174.1</string>
<string>85F4.1</string>
</array>
</dict>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EXPO-VIEWCONTROLLER-1">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<scene sceneID="EXPO-SCENE-1">
<objects>
<viewController storyboardIdentifier="SplashScreenViewController" id="EXPO-VIEWCONTROLLER-1" sceneMemberID="viewController">
<view key="view" userInteractionEnabled="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="EXPO-ContainerView" userLabel="ContainerView">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView id="EXPO-SplashScreen" userLabel="SplashScreenLogo" image="SplashScreenLogo" contentMode="scaleAspectFit" clipsSubviews="true" userInteractionEnabled="false" translatesAutoresizingMaskIntoConstraints="false">
<rect key="frame" x="0" y="0" width="414" height="736"/>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="Rmq-lb-GrQ"/>
<constraints>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="top" secondItem="EXPO-ContainerView" secondAttribute="top" id="83fcb9b545b870ba44c24f0feeb116490c499c52"/>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="leading" secondItem="EXPO-ContainerView" secondAttribute="leading" id="61d16215e44b98e39d0a2c74fdbfaaa22601b12c"/>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="trailing" secondItem="EXPO-ContainerView" secondAttribute="trailing" id="f934da460e9ab5acae3ad9987d5b676a108796c1"/>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="bottom" secondItem="EXPO-ContainerView" secondAttribute="bottom" id="d6a0be88096b36fb132659aa90203d39139deda9"/>
</constraints>
<color key="backgroundColor" name="SplashScreenBackground"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="EXPO-PLACEHOLDER-1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="0.0" y="0.0"/>
</scene>
</scenes>
<resources>
<image name="SplashScreenLogo" width="414" height="736"/>
<namedColor name="SplashScreenBackground">
<color alpha="1.000" blue="0.0156862745098039" green="0.0156862745098039" red="0.00784313725490196" customColorSpace="sRGB" colorSpace="custom"/>
</namedColor>
</resources>
</document>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>EXUpdatesCheckOnLaunch</key>
<string>ERROR_RECOVERY_ONLY</string>
<key>EXUpdatesEnabled</key>
<true/>
<key>EXUpdatesLaunchWaitMs</key>
<integer>30000</integer>
<key>EXUpdatesRuntimeVersion</key>
<string>1.0.0</string>
<key>EXUpdatesURL</key>
<string>https://grim-reyna-tapframe-69970143.koyeb.app/api/manifest</string>
</dict>
</plist>

10
ios/Nuvio/main.m Normal file
View file

@ -0,0 +1,10 @@
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

View file

@ -0,0 +1,4 @@
//
// @generated
// A blank Swift file must be created for native modules with Swift files to work correctly.
//

72
ios/Podfile Normal file
View file

@ -0,0 +1,72 @@
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
require 'json'
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0'
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'
install! 'cocoapods',
:deterministic_uuids => false
prepare_react_native_project!
target 'Nuvio' do
use_expo_modules!
if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1'
config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
else
config_command = [
'node',
'--no-warnings',
'--eval',
'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))',
'react-native-config',
'--json',
'--platform',
'ios'
]
end
config = use_native_modules!(config_command)
use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
use_react_native!(
:path => config[:reactNativePath],
:hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
# An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/..",
:privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false',
)
# KSPlayer dependencies
pod 'KSPlayer',:git => 'https://github.com/kingslay/KSPlayer.git', :branch => 'main', :modular_headers => true
pod 'DisplayCriteria',:git => 'https://github.com/kingslay/KSPlayer.git', :branch => 'main', :modular_headers => true
pod 'FFmpegKit',:git => 'https://github.com/kingslay/FFmpegKit.git', :branch => 'main', :modular_headers => true
pod 'Libass',:git => 'https://github.com/kingslay/FFmpegKit.git', :branch => 'main', :modular_headers => true
post_install do |installer|
react_native_post_install(
installer,
config[:reactNativePath],
:mac_catalyst_enabled => false,
:ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true',
)
# This is necessary for Xcode 14, because it signs resource bundles by default
# when building for devices.
installer.target_installation_results.pod_target_installation_results
.each do |pod_name, target_installation_result|
target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
resource_bundle_target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
end
end
end

2957
ios/Podfile.lock Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,5 @@
{
"expo.jsEngine": "hermes",
"EX_DEV_CLIENT_NETWORK_INSPECTOR": "true",
"newArchEnabled": "true"
}

View file

@ -0,0 +1,43 @@
require 'xcodeproj'
require 'fileutils'
project_path = File.expand_path('../Nuvio.xcodeproj', __dir__)
project = Xcodeproj::Project.open(project_path)
app_dir = File.expand_path('..', __dir__)
target_name = 'Nuvio'
bridge_files = [
File.expand_path('Nuvio/KSPlayerView.swift', app_dir),
File.expand_path('Nuvio/KSPlayerViewManager.swift', app_dir),
File.expand_path('Nuvio/KSPlayerManager.m', app_dir),
File.expand_path('Nuvio/KSPlayerModule.swift', app_dir),
]
missing = bridge_files.reject { |p| File.exist?(p) }
if missing.any?
abort "Missing files: \n#{missing.join("\n")}"
end
target = project.targets.find { |t| t.name == target_name }
abort "Target '#{target_name}' not found" unless target
# Ensure group exists at the correct relative path
root_group = project.main_group
app_group = root_group['Nuvio'] || root_group.new_group('Nuvio', 'Nuvio')
added_files = []
bridge_files.each do |file_path|
relative_path = Pathname.new(file_path).relative_path_from(Pathname.new(project_path).dirname).to_s
ref = project.files.find { |f| f.path == relative_path }
unless ref
ref = app_group.new_file(relative_path)
added_files << relative_path
end
unless target.source_build_phase.files_references.include?(ref)
target.add_file_references([ref])
end
end
project.save
puts "Ensured bridge files are added to target '#{target_name}':\n - #{(added_files.empty? ? '(no new files)' : added_files.join("\n - "))}"

4
ios/sentry.properties Normal file
View file

@ -0,0 +1,4 @@
defaults.url=https://sentry.io/
defaults.org=tapframe
defaults.project=react-native
# Using SENTRY_AUTH_TOKEN environment variable

View file

@ -0,0 +1,27 @@
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'xcodeproj' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0.a"
str = ARGV.first
if str
str = str.b[/\A_(.*)_\z/, 1]
if str and Gem::Version.correct?(str)
version = str
ARGV.shift
end
end
if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('xcodeproj', 'xcodeproj', version)
else
gem "xcodeproj", version
load Gem.bin_path("xcodeproj", "xcodeproj", version)
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,16 @@
current directory: /Users/tapframe/Desktop/Project/Nuvio/ios/vendor/bundle/ruby/2.6.0/gems/nkf-0.2.0/ext/nkf
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby -I /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0 -r ./siteconf20250917-1505-1tafywa.rb extconf.rb
creating Makefile
current directory: /Users/tapframe/Desktop/Project/Nuvio/ios/vendor/bundle/ruby/2.6.0/gems/nkf-0.2.0/ext/nkf
make "DESTDIR=" clean
current directory: /Users/tapframe/Desktop/Project/Nuvio/ios/vendor/bundle/ruby/2.6.0/gems/nkf-0.2.0/ext/nkf
make "DESTDIR="
compiling nkf.c
linking shared-object nkf.bundle
ld: warning: search path '/AppleInternal/Library/BuildRoots/1c8f7852-1ca9-11f0-b28b-226177e5bb69/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.Internal.sdk/usr/local/lib' not found
current directory: /Users/tapframe/Desktop/Project/Nuvio/ios/vendor/bundle/ruby/2.6.0/gems/nkf-0.2.0/ext/nkf
make "DESTDIR=" install
/usr/bin/install -c -m 0755 nkf.bundle ./.gem.20250917-1505-18va8d4

View file

@ -0,0 +1,19 @@
Copyright (c) 2010 Christian Kruse, <cjk@wwwtech.de>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,79 @@
CFPropertyList implementation
class to read, manipulate and write both XML and binary property list
files (plist(5)) as defined by Apple. Have a look at CFPropertyList::List
for more documentation.
# Caution!
In version 3.0.0 we dropped Ruby 1.8 compatibility. If you are using
Ruby 1.8 consider to update Ruby; if you can't upgrade, don't upgrade
CFPropertyList.
# Installation
You could either use ruby gems and install it via
```bash
gem install CFPropertyList
```
or you could clone this repository and place it somewhere in your load path.
Example:
```ruby
require 'cfpropertylist'
```
If you're using Rails, you can add it into your Gemfile
```ruby
gem 'CFPropertyList'
```
# Usage
## create a arbitrary data structure of basic data types
```ruby
data = {
'name' => 'John Doe',
'missing' => true,
'last_seen' => Time.now,
'friends' => ['Jane Doe','Julian Doe'],
'likes' => {
'me' => false
}
}
```
## create CFPropertyList::List object
```ruby
plist = CFPropertyList::List.new
```
## call CFPropertyList.guess() to create corresponding CFType values
```ruby
plist.value = CFPropertyList.guess(data)
```
## write plist to file
```ruby
plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)
```
## … later, read it again
```ruby
plist = CFPropertyList::List.new(:file => "example.plist")
data = CFPropertyList.native_types(plist.value)
```
# Author and license
**Author:** Christian Kruse (mailto:cjk@wwwtech.de)
**Copyright:** Copyright (c) 2010
**License:** MIT License

View file

@ -0,0 +1,43 @@
CFPropertyList implementation
class to read, manipulate and write both XML and binary property list
files (plist(5)) as defined by Apple. Have a look at CFPropertyList::List
for more documentation.
== Installation
You could either use ruby gems and install it via
gem install CFPropertyList
or you could clone this repository and place it somewhere in your load path.
== Example
require 'cfpropertylist'
# create a arbitrary data structure of basic data types
data = {
'name' => 'John Doe',
'missing' => true,
'last_seen' => Time.now,
'friends' => ['Jane Doe','Julian Doe'],
'likes' => {
'me' => false
}
}
# create CFPropertyList::List object
plist = CFPropertyList::List.new
# call CFPropertyList.guess() to create corresponding CFType values
plist.value = CFPropertyList.guess(data)
# write plist to file
plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)
# … later, read it again
plist = CFPropertyList::List.new(:file => "example.plist")
data = CFPropertyList.native_types(plist.value)
Author:: Christian Kruse (mailto:cjk@wwwtech.de)
Copyright:: Copyright (c) 2010
License:: MIT License

View file

@ -0,0 +1,7 @@
Special thanks to:
Steve Madsen for providing a lot of performance patches and bugfixes!
Have a look at his Github account: <http://github.com/sjmadsen>

View file

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
require 'cfpropertylist/rbCFPropertyList'
# eof

View file

@ -0,0 +1,594 @@
# -*- coding: utf-8 -*-
require 'stringio'
module CFPropertyList
# Binary PList parser class
class Binary
# Read a binary plist file
def load(opts)
@unique_table = {}
@count_objects = 0
@object_refs = 0
@written_object_count = 0
@object_table = []
@object_ref_size = 0
@offsets = []
fd = nil
if(opts.has_key?(:file))
fd = File.open(opts[:file],"rb")
file = opts[:file]
else
fd = StringIO.new(opts[:data],"rb")
file = "<string>"
end
# first, we read the trailer: 32 byte from the end
fd.seek(-32,IO::SEEK_END)
buff = fd.read(32)
offset_size, object_ref_size, number_of_objects, top_object, table_offset = buff.unpack "x6CCx4Nx4Nx4N"
# after that, get the offset table
fd.seek(table_offset, IO::SEEK_SET)
coded_offset_table = fd.read(number_of_objects * offset_size)
raise CFFormatError.new("#{file}: Format error!") unless coded_offset_table.bytesize == number_of_objects * offset_size
@count_objects = number_of_objects
# decode offset table
if(offset_size != 3)
formats = ["","C*","n*","","N*"]
@offsets = coded_offset_table.unpack(formats[offset_size])
else
@offsets = coded_offset_table.unpack("C*").each_slice(3).map {
|x,y,z| (x << 16) | (y << 8) | z
}
end
@object_ref_size = object_ref_size
val = read_binary_object_at(file,fd,top_object)
fd.close
val
end
# Convert CFPropertyList to binary format; since we have to count our objects we simply unique CFDictionary and CFArray
def to_str(opts={})
@unique_table = {}
@count_objects = 0
@object_refs = 0
@written_object_count = 0
@object_table = []
@offsets = []
binary_str = "bplist00"
@object_refs = count_object_refs(opts[:root])
opts[:root].to_binary(self)
next_offset = 8
offsets = @object_table.map do |object|
offset = next_offset
next_offset += object.bytesize
offset
end
binary_str << @object_table.join
table_offset = next_offset
offset_size = Binary.bytes_needed(table_offset)
if offset_size < 8
# Fast path: encode the entire offset array at once.
binary_str << offsets.pack((%w(C n N N)[offset_size - 1]) + '*')
else
# Slow path: host may be little or big endian, must pack each offset
# separately.
offsets.each do |offset|
binary_str << "#{Binary.pack_it_with_size(offset_size,offset)}"
end
end
binary_str << [offset_size, object_ref_size(@object_refs)].pack("x6CC")
binary_str << [@object_table.size].pack("x4N")
binary_str << [0].pack("x4N")
binary_str << [table_offset].pack("x4N")
binary_str
end
def object_ref_size object_refs
Binary.bytes_needed(object_refs)
end
# read a „null” type (i.e. null byte, marker byte, bool value)
def read_binary_null_type(length)
case length
when 0 then 0 # null byte
when 8 then CFBoolean.new(false)
when 9 then CFBoolean.new(true)
when 15 then 15 # fill type
else
raise CFFormatError.new("unknown null type: #{length}")
end
end
protected :read_binary_null_type
# read a binary int value
def read_binary_int(fname,fd,length)
if length > 4
raise CFFormatError.new("Integer greater than 16 bytes: #{length}")
end
nbytes = 1 << length
buff = fd.read(nbytes)
CFInteger.new(
case length
when 0 then buff.unpack("C")[0]
when 1 then buff.unpack("n")[0]
when 2 then buff.unpack("N")[0]
# 8 byte integers are always signed
when 3 then buff.unpack("q>")[0]
# 16 byte integers are used to represent unsigned 8 byte integers
# where the unsigned value is stored in the lower 8 bytes and the
# upper 8 bytes are unused.
when 4 then buff.unpack("Q>Q>")[1]
end
)
end
protected :read_binary_int
# read a binary real value
def read_binary_real(fname,fd,length)
raise CFFormatError.new("Real greater than 8 bytes: #{length}") if length > 3
nbytes = 1 << length
buff = fd.read(nbytes)
CFReal.new(
case length
when 0 # 1 byte float? must be an error
raise CFFormatError.new("got #{length+1} byte float, must be an error!")
when 1 # 2 byte float? must be an error
raise CFFormatError.new("got #{length+1} byte float, must be an error!")
when 2 then
buff.reverse.unpack("e")[0]
when 3 then
buff.reverse.unpack("E")[0]
else
fail "unexpected length: #{length}"
end
)
end
protected :read_binary_real
# read a binary date value
def read_binary_date(fname,fd,length)
raise CFFormatError.new("Date greater than 8 bytes: #{length}") if length > 3
nbytes = 1 << length
buff = fd.read(nbytes)
CFDate.new(
case length
when 0 then # 1 byte CFDate is an error
raise CFFormatError.new("#{length+1} byte CFDate, error")
when 1 then # 2 byte CFDate is an error
raise CFFormatError.new("#{length+1} byte CFDate, error")
when 2 then
buff.reverse.unpack("e")[0]
when 3 then
buff.reverse.unpack("E")[0]
end,
CFDate::TIMESTAMP_APPLE
)
end
protected :read_binary_date
# Read a binary data value
def read_binary_data(fname,fd,length)
CFData.new(read_fd(fd, length), CFData::DATA_RAW)
end
protected :read_binary_data
def read_fd fd, length
length > 0 ? fd.read(length) : ""
end
# Read a binary string value
def read_binary_string(fname,fd,length)
buff = read_fd fd, length
@unique_table[buff] = true unless @unique_table.has_key?(buff)
CFString.new(buff)
end
protected :read_binary_string
# Convert the given string from one charset to another
def Binary.charset_convert(str,from,to="UTF-8")
return str.dup.force_encoding(from).encode(to) if str.respond_to?("encode")
Iconv.conv(to,from,str)
end
# Count characters considering character set
def Binary.charset_strlen(str,charset="UTF-8")
if str.respond_to?(:encode)
size = str.length
else
utf8_str = Iconv.conv("UTF-8",charset,str)
size = utf8_str.scan(/./mu).size
end
# UTF-16 code units in the range D800-DBFF are the beginning of
# a surrogate pair, and count as one additional character for
# length calculation.
if charset =~ /^UTF-16/
if str.respond_to?(:encode)
str.bytes.to_a.each_slice(2) { |pair| size += 1 if (0xd8..0xdb).include?(pair[0]) }
else
str.split('').each_slice(2) { |pair| size += 1 if ("\xd8".."\xdb").include?(pair[0]) }
end
end
size
end
# Read a unicode string value, coded as UTF-16BE
def read_binary_unicode_string(fname,fd,length)
# The problem is: we get the length of the string IN CHARACTERS;
# since a char in UTF-16 can be 16 or 32 bit long, we don't really know
# how long the string is in bytes
buff = fd.read(2*length)
@unique_table[buff] = true unless @unique_table.has_key?(buff)
CFString.new(Binary.charset_convert(buff,"UTF-16BE","UTF-8"))
end
protected :read_binary_unicode_string
def unpack_with_size(nbytes, buff)
format = ["C*", "n*", "N*", "N*"][nbytes - 1];
if nbytes == 3
buff = "\0" + buff.scan(/.{1,3}/).join("\0")
end
return buff.unpack(format)
end
# Read an binary array value, including contained objects
def read_binary_array(fname,fd,length)
ary = []
# first: read object refs
if(length != 0)
buff = fd.read(length * @object_ref_size)
objects = unpack_with_size(@object_ref_size, buff) #buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
# now: read objects
0.upto(length-1) do |i|
object = read_binary_object_at(fname,fd,objects[i])
ary.push object
end
end
CFArray.new(ary)
end
protected :read_binary_array
# Read a dictionary value, including contained objects
def read_binary_dict(fname,fd,length)
dict = {}
# first: read keys
if(length != 0) then
buff = fd.read(length * @object_ref_size)
keys = unpack_with_size(@object_ref_size, buff)
# second: read object refs
buff = fd.read(length * @object_ref_size)
objects = unpack_with_size(@object_ref_size, buff)
# read real keys and objects
0.upto(length-1) do |i|
key = read_binary_object_at(fname,fd,keys[i])
object = read_binary_object_at(fname,fd,objects[i])
dict[key.value] = object
end
end
CFDictionary.new(dict)
end
protected :read_binary_dict
# Read an object type byte, decode it and delegate to the correct
# reader function
def read_binary_object(fname,fd)
# first: read the marker byte
buff = fd.read(1)
object_length = buff.unpack("C*")
object_length = object_length[0] & 0xF
buff = buff.unpack("H*")
object_type = buff[0][0].chr
if(object_type != "0" && object_length == 15) then
object_length = read_binary_object(fname,fd)
object_length = object_length.value
end
case object_type
when '0' # null, false, true, fillbyte
read_binary_null_type(object_length)
when '1' # integer
read_binary_int(fname,fd,object_length)
when '2' # real
read_binary_real(fname,fd,object_length)
when '3' # date
read_binary_date(fname,fd,object_length)
when '4' # data
read_binary_data(fname,fd,object_length)
when '5' # byte string, usually utf8 encoded
read_binary_string(fname,fd,object_length)
when '6' # unicode string (utf16be)
read_binary_unicode_string(fname,fd,object_length)
when '8'
CFUid.new(read_binary_int(fname, fd, object_length).value)
when 'a' # array
read_binary_array(fname,fd,object_length)
when 'd' # dictionary
read_binary_dict(fname,fd,object_length)
end
end
protected :read_binary_object
# Read an object type byte at position $pos, decode it and delegate to the correct reader function
def read_binary_object_at(fname,fd,pos)
position = @offsets[pos]
fd.seek(position,IO::SEEK_SET)
read_binary_object(fname,fd)
end
protected :read_binary_object_at
# pack an +int+ of +nbytes+ with size
def Binary.pack_it_with_size(nbytes,int)
case nbytes
when 1 then [int].pack('c')
when 2 then [int].pack('n')
when 4 then [int].pack('N')
when 8
[int >> 32, int & 0xFFFFFFFF].pack('NN')
else
raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
end
end
def Binary.pack_int_array_with_size(nbytes, array)
case nbytes
when 1 then array.pack('C*')
when 2 then array.pack('n*')
when 4 then array.pack('N*')
when 8
array.map { |int| [int >> 32, int & 0xFFFFFFFF].pack('NN') }.join
else
raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
end
end
# calculate how many bytes are needed to save +count+
def Binary.bytes_needed(count)
case
when count < 2**8 then 1
when count < 2**16 then 2
when count < 2**32 then 4
when count < 2**64 then 8
else
raise CFFormatError.new("Data size too large: #{count}")
end
end
# Create a type byte for binary format as defined by apple
def Binary.type_bytes(type, length)
if length < 15
[(type << 4) | length].pack('C')
else
bytes = [(type << 4) | 0xF]
if length <= 0xFF
bytes.push(0x10, length).pack('CCC') # 1 byte length
elsif length <= 0xFFFF
bytes.push(0x11, length).pack('CCn') # 2 byte length
elsif length <= 0xFFFFFFFF
bytes.push(0x12, length).pack('CCN') # 4 byte length
elsif length <= 0x7FFFFFFFFFFFFFFF
bytes.push(0x13, length >> 32, length & 0xFFFFFFFF).pack('CCNN') # 8 byte length
else
raise CFFormatError.new("Integer too large: #{int}")
end
end
end
def count_object_refs(object)
case object
when CFArray
contained_refs = 0
object.value.each do |element|
if CFArray === element || CFDictionary === element
contained_refs += count_object_refs(element)
end
end
return object.value.size + contained_refs
when CFDictionary
contained_refs = 0
object.value.each_value do |value|
if CFArray === value || CFDictionary === value
contained_refs += count_object_refs(value)
end
end
return object.value.keys.size * 2 + contained_refs
else
return 0
end
end
def Binary.ascii_string?(str)
if str.respond_to?(:ascii_only?)
str.ascii_only?
else
str !~ /[\x80-\xFF]/mn
end
end
# Uniques and transforms a string value to binary format and adds it to the object table
def string_to_binary(val)
val = val.to_s
@unique_table[val] ||= begin
if !Binary.ascii_string?(val)
val = Binary.charset_convert(val,"UTF-8","UTF-16BE")
bdata = Binary.type_bytes(0b0110, Binary.charset_strlen(val,"UTF-16BE"))
val.force_encoding("ASCII-8BIT") if val.respond_to?("encode")
@object_table[@written_object_count] = bdata << val
else
bdata = Binary.type_bytes(0b0101,val.bytesize)
@object_table[@written_object_count] = bdata << val
end
@written_object_count += 1
@written_object_count - 1
end
end
# Codes an integer to binary format
def int_to_binary(value)
# Note: nbytes is actually an exponent. number of bytes = 2**nbytes.
nbytes = 0
nbytes = 1 if value > 0xFF # 1 byte unsigned integer
nbytes += 1 if value > 0xFFFF # 4 byte unsigned integer
nbytes += 1 if value > 0xFFFFFFFF # 8 byte unsigned integer
nbytes += 1 if value > 0x7FFFFFFFFFFFFFFF # 8 byte unsigned integer, stored in lower half of 16 bytes
nbytes = 3 if value < 0 # signed integers always stored in 8 bytes
Binary.type_bytes(0b0001, nbytes) <<
if nbytes < 4
[value].pack(["C", "n", "N", "q>"][nbytes])
else # nbytes == 4
[0,value].pack("Q>Q>")
end
end
# Codes a real value to binary format
def real_to_binary(val)
Binary.type_bytes(0b0010,3) << [val].pack("E").reverse
end
# Converts a numeric value to binary and adds it to the object table
def num_to_binary(value)
@object_table[@written_object_count] =
if value.is_a?(CFInteger)
int_to_binary(value.value)
else
real_to_binary(value.value)
end
@written_object_count += 1
@written_object_count - 1
end
def uid_to_binary(value)
nbytes = 0
nbytes = 1 if value > 0xFF # 1 byte integer
nbytes += 1 if value > 0xFFFF # 4 byte integer
nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
nbytes = 3 if value < 0 # 8 byte integer, since signed
@object_table[@written_object_count] = Binary.type_bytes(0b1000, nbytes) <<
if nbytes < 3
[value].pack(
if nbytes == 0 then "C"
elsif nbytes == 1 then "n"
else "N"
end
)
else
# 64 bit signed integer; we need the higher and the lower 32 bit of the value
high_word = value >> 32
low_word = value & 0xFFFFFFFF
[high_word,low_word].pack("NN")
end
@written_object_count += 1
@written_object_count - 1
end
# Convert date value (apple format) to binary and adds it to the object table
def date_to_binary(val)
val = val.getutc.to_f - CFDate::DATE_DIFF_APPLE_UNIX # CFDate is a real, number of seconds since 01/01/2001 00:00:00 GMT
@object_table[@written_object_count] =
(Binary.type_bytes(0b0011, 3) << [val].pack("E").reverse)
@written_object_count += 1
@written_object_count - 1
end
# Convert a bool value to binary and add it to the object table
def bool_to_binary(val)
@object_table[@written_object_count] = val ? "\x9" : "\x8" # 0x9 is 1001, type indicator for true; 0x8 is 1000, type indicator for false
@written_object_count += 1
@written_object_count - 1
end
# Convert data value to binary format and add it to the object table
def data_to_binary(val)
@object_table[@written_object_count] =
(Binary.type_bytes(0b0100, val.bytesize) << val)
@written_object_count += 1
@written_object_count - 1
end
# Convert array to binary format and add it to the object table
def array_to_binary(val)
saved_object_count = @written_object_count
@written_object_count += 1
#@object_refs += val.value.size
values = val.value.map { |v| v.to_binary(self) }
bdata = Binary.type_bytes(0b1010, val.value.size) <<
Binary.pack_int_array_with_size(object_ref_size(@object_refs),
values)
@object_table[saved_object_count] = bdata
saved_object_count
end
# Convert dictionary to binary format and add it to the object table
def dict_to_binary(val)
saved_object_count = @written_object_count
@written_object_count += 1
#@object_refs += val.value.keys.size * 2
keys_and_values = val.value.keys.map { |k| CFString.new(k).to_binary(self) }
keys_and_values.concat(val.value.values.map { |v| v.to_binary(self) })
bdata = Binary.type_bytes(0b1101,val.value.size) <<
Binary.pack_int_array_with_size(object_ref_size(@object_refs), keys_and_values)
@object_table[saved_object_count] = bdata
return saved_object_count
end
end
end
# eof

View file

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
#
# Exceptions used:
# CFPlistError:: General base exception
# CFFormatError:: Format error
# CFTypeError:: Type error
#
# Easy and simple :-)
#
# Author:: Christian Kruse (mailto:cjk@wwwtech.de)
# Copyright:: Copyright (c) 2010
# License:: MIT License
# general plist error. All exceptions thrown are derived from this class.
class CFPlistError < StandardError
end
# Exception thrown when format errors occur
class CFFormatError < CFPlistError
end
# Exception thrown when type errors occur
class CFTypeError < CFPlistError
end
# eof

View file

@ -0,0 +1,449 @@
# -*- coding: utf-8 -*-
require 'kconv'
require 'date'
require 'time'
#
# CFPropertyList implementation
#
# class to read, manipulate and write both XML and binary property list
# files (plist(5)) as defined by Apple. Have a look at CFPropertyList::List
# for more documentation.
#
# == Example
# require 'cfpropertylist'
#
# # create a arbitrary data structure of basic data types
# data = {
# 'name' => 'John Doe',
# 'missing' => true,
# 'last_seen' => Time.now,
# 'friends' => ['Jane Doe','Julian Doe'],
# 'likes' => {
# 'me' => false
# }
# }
#
# # create CFPropertyList::List object
# plist = CFPropertyList::List.new
#
# # call CFPropertyList.guess() to create corresponding CFType values
# # pass in optional :convert_unknown_to_string => true to convert things like symbols into strings.
# plist.value = CFPropertyList.guess(data)
#
# # write plist to file
# plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)
#
# # … later, read it again
# plist = CFPropertyList::List.new(:file => "example.plist")
# data = CFPropertyList.native_types(plist.value)
#
# Author:: Christian Kruse (mailto:cjk@wwwtech.de)
# Copyright:: Copyright (c) 2010
# License:: MIT License
module CFPropertyList
class << self
attr_accessor :xml_parser_interface
end
# interface class for PList parsers
class ParserInterface
# load a plist
def load(opts={})
return ""
end
# convert a plist to string
def to_str(opts={})
return true
end
end
class XMLParserInterface < ParserInterface
def new_node(name)
end
def new_text(val)
end
def append_node(parent, child)
end
end
end
dirname = File.dirname(__FILE__)
require dirname + '/rbCFPlistError.rb'
require dirname + '/rbCFTypes.rb'
require dirname + '/rbBinaryCFPropertyList.rb'
require dirname + '/rbPlainCFPropertyList.rb'
begin
require dirname + '/rbLibXMLParser.rb'
temp = LibXML::XML::Parser::Options::NOBLANKS # check if we have a version with parser options
temp = false # avoid a warning
try_nokogiri = false
CFPropertyList.xml_parser_interface = CFPropertyList::LibXMLParser
rescue LoadError, NameError
try_nokogiri = true
end
if try_nokogiri then
begin
require dirname + '/rbNokogiriParser.rb'
CFPropertyList.xml_parser_interface = CFPropertyList::NokogiriXMLParser
rescue LoadError
require dirname + '/rbREXMLParser.rb'
CFPropertyList.xml_parser_interface = CFPropertyList::ReXMLParser
end
end
module CFPropertyList
# Create CFType hierarchy by guessing the correct CFType, e.g.
#
# x = {
# 'a' => ['b','c','d']
# }
# cftypes = CFPropertyList.guess(x)
#
# pass optional options hash. Only possible value actually:
# +convert_unknown_to_string+:: Convert unknown objects to string calling to_str()
# +converter_method+:: Convert unknown objects to known objects calling +method_name+
#
# cftypes = CFPropertyList.guess(x,:convert_unknown_to_string => true,:converter_method => :to_hash, :converter_with_opts => true)
def guess(object, options = {})
case object
when Integer then CFInteger.new(object)
when UidFixnum then CFUid.new(object)
when Float then CFReal.new(object)
when TrueClass, FalseClass then CFBoolean.new(object)
when Blob
CFData.new(object, CFData::DATA_RAW)
when String, Symbol
CFString.new(object.to_s)
when Time, DateTime, Date
CFDate.new(object)
when Array, Enumerator
ary = Array.new
object.each do |o|
ary.push CFPropertyList.guess(o, options)
end
CFArray.new(ary)
when Hash
hsh = Hash.new
object.each_pair do |k,v|
k = k.to_s if k.is_a?(Symbol)
hsh[k] = CFPropertyList.guess(v, options)
end
CFDictionary.new(hsh)
else
case
when Object.const_defined?('BigDecimal') && object.is_a?(BigDecimal)
CFReal.new(object)
when object.respond_to?(:read)
raw_data = object.read
# treat the data as a bytestring (ASCII-8BIT) if Ruby supports it. Do this by forcing
# the encoding, on the assumption that the bytes were read correctly, and just tagged with
# an inappropriate encoding, rather than transcoding.
raw_data.force_encoding(Encoding::ASCII_8BIT) if raw_data.respond_to?(:force_encoding)
CFData.new(raw_data, CFData::DATA_RAW)
when options[:converter_method] && object.respond_to?(options[:converter_method])
if options[:converter_with_opts]
CFPropertyList.guess(object.send(options[:converter_method],options),options)
else
CFPropertyList.guess(object.send(options[:converter_method]),options)
end
when options[:convert_unknown_to_string]
CFString.new(object.to_s)
else
raise CFTypeError.new("Unknown class #{object.class.to_s}. Try using :convert_unknown_to_string if you want to use unknown object types!")
end
end
end
# Converts a CFType hiercharchy to native Ruby types
def native_types(object,keys_as_symbols=false)
return if object.nil?
if(object.is_a?(CFDate) || object.is_a?(CFString) || object.is_a?(CFInteger) || object.is_a?(CFReal) || object.is_a?(CFBoolean)) || object.is_a?(CFUid) then
return object.value
elsif(object.is_a?(CFData)) then
return CFPropertyList::Blob.new(object.decoded_value)
elsif(object.is_a?(CFArray)) then
ary = []
object.value.each do
|v|
ary.push CFPropertyList.native_types(v)
end
return ary
elsif(object.is_a?(CFDictionary)) then
hsh = {}
object.value.each_pair do
|k,v|
k = k.to_sym if keys_as_symbols
hsh[k] = CFPropertyList.native_types(v)
end
return hsh
end
end
module_function :guess, :native_types
# Class representing a CFPropertyList. Instantiate with #new
class List
# Format constant for binary format
FORMAT_BINARY = 1
# Format constant for XML format
FORMAT_XML = 2
# Format constant for the old plain format
FORMAT_PLAIN = 3
# Format constant for automatic format recognizing
FORMAT_AUTO = 0
@@parsers = [Binary, CFPropertyList.xml_parser_interface, PlainParser]
# Path of PropertyList
attr_accessor :filename
# the original format of the PropertyList
attr_accessor :format
# the root value in the plist file
attr_accessor :value
# default value for XML generation; if true generate formatted XML
attr_accessor :formatted
# initialize a new CFPropertyList, arguments are:
#
# :file:: Parse a file
# :format:: Format is one of FORMAT_BINARY or FORMAT_XML. Defaults to FORMAT_AUTO
# :data:: Parse a string
#
# All arguments are optional
def initialize(opts={})
@filename = opts[:file]
@format = opts[:format] || FORMAT_AUTO
@data = opts[:data]
@formatted = opts[:formatted]
load(@filename) unless @filename.nil?
load_str(@data) unless @data.nil?
end
# returns a list of registered parsers
def self.parsers
@@parsers
end
# set a list of parsers
def self.parsers=(val)
@@parsers = val
end
# Load an XML PropertyList
# filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
def load_xml(filename=nil)
load(filename,List::FORMAT_XML)
end
# read a binary plist file
# filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
def load_binary(filename=nil)
load(filename,List::FORMAT_BINARY)
end
# read a plain plist file
# filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
def load_plain(filename=nil)
load(filename,List::FORMAT_PLAIN)
end
# load a plist from a XML string
# str:: The string containing the plist
def load_xml_str(str=nil)
load_str(str,List::FORMAT_XML)
end
# load a plist from a binary string
# str:: The string containing the plist
def load_binary_str(str=nil)
load_str(str,List::FORMAT_BINARY)
end
# load a plist from a plain string
# str:: The string containing the plist
def load_plain_str(str=nil)
load_str(str,List::FORMAT_PLAIN)
end
# load a plist from a string
# str = nil:: The string containing the plist
# format = nil:: The format of the plist
def load_str(str=nil,format=nil)
str = @data if str.nil?
format = @format if format.nil?
@value = {}
case format
when List::FORMAT_BINARY, List::FORMAT_XML, List::FORMAT_PLAIN then
prsr = @@parsers[format-1].new
@value = prsr.load({:data => str})
when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
filetype = str[0..5]
version = str[6..7]
prsr = nil
if filetype == "bplist" then
raise CFFormatError.new("Wrong file version #{version}") unless version == "00"
prsr = Binary.new
@format = List::FORMAT_BINARY
else
if str =~ /^<(\?xml|!DOCTYPE|plist)/
prsr = CFPropertyList.xml_parser_interface.new
@format = List::FORMAT_XML
else
prsr = PlainParser.new
@format = List::FORMAT_PLAIN
end
end
@value = prsr.load({:data => str})
end
end
# Read a plist file
# file = nil:: The filename of the file to read. If nil, use +filename+ instance variable
# format = nil:: The format of the plist file. Auto-detect if nil
def load(file=nil,format=nil)
file = @filename if file.nil?
format = @format if format.nil?
@value = {}
raise IOError.new("File #{file} not readable!") unless File.readable? file
case format
when List::FORMAT_BINARY, List::FORMAT_XML, List::FORMAT_PLAIN then
prsr = @@parsers[format-1].new
@value = prsr.load({:file => file})
when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
magic_number = IO.read(file,12)
raise IOError.new("File #{file} is empty.") unless magic_number
filetype = magic_number[0..5]
version = magic_number[6..7]
prsr = nil
if filetype == "bplist" then
raise CFFormatError.new("Wrong file version #{version}") unless version == "00"
prsr = Binary.new
@format = List::FORMAT_BINARY
else
if magic_number =~ /^<(\?xml|!DOCTYPE|plist)/
prsr = CFPropertyList.xml_parser_interface.new
@format = List::FORMAT_XML
else
prsr = PlainParser.new
@format = List::FORMAT_PLAIN
end
end
@value = prsr.load({:file => file})
end
raise CFFormatError.new("Invalid format or parser error!") if @value.nil?
end
# Serialize CFPropertyList object to specified format and write it to file
# file = nil:: The filename of the file to write to. Uses +filename+ instance variable if nil
# format = nil:: The format to save in. Uses +format+ instance variable if nil
def save(file=nil,format=nil,opts={})
format = @format if format.nil?
file = @filename if file.nil?
if format != FORMAT_BINARY && format != FORMAT_XML && format != FORMAT_PLAIN
raise CFFormatError.new("Format #{format} not supported, use List::FORMAT_BINARY or List::FORMAT_XML")
end
if(!File.exist?(file)) then
raise IOError.new("File #{file} not writable!") unless File.writable?(File.dirname(file))
elsif(!File.writable?(file)) then
raise IOError.new("File #{file} not writable!")
end
opts[:root] = @value
opts[:formatted] = @formatted unless opts.has_key?(:formatted)
prsr = @@parsers[format-1].new
content = prsr.to_str(opts)
File.open(file, 'wb') {
|fd|
fd.write content
}
end
# convert plist to string
# format = List::FORMAT_BINARY:: The format to save the plist
# opts={}:: Pass parser options
def to_str(format=List::FORMAT_BINARY,opts={})
if format != FORMAT_BINARY && format != FORMAT_XML && format != FORMAT_PLAIN
raise CFFormatError.new("Format #{format} not supported, use List::FORMAT_BINARY or List::FORMAT_XML")
end
prsr = @@parsers[format-1].new
opts[:root] = @value
opts[:formatted] = @formatted unless opts.has_key?(:formatted)
return prsr.to_str(opts)
end
end
end
class Array
# convert an array to plist format
def to_plist(options={})
options[:plist_format] ||= CFPropertyList::List::FORMAT_BINARY
plist = CFPropertyList::List.new
plist.value = CFPropertyList.guess(self, options)
plist.to_str(options[:plist_format], options)
end
end
class Enumerator
# convert an array to plist format
def to_plist(options={})
options[:plist_format] ||= CFPropertyList::List::FORMAT_BINARY
plist = CFPropertyList::List.new
plist.value = CFPropertyList.guess(self, options)
plist.to_str(options[:plist_format], options)
end
end
class Hash
# convert a hash to plist format
def to_plist(options={})
options[:plist_format] ||= CFPropertyList::List::FORMAT_BINARY
plist = CFPropertyList::List.new
plist.value = CFPropertyList.guess(self, options)
plist.to_str(options[:plist_format], options)
end
end
# eof

View file

@ -0,0 +1,349 @@
# -*- coding: utf-8 -*-
#
# CFTypes, e.g. CFString, CFInteger
# needed to create unambiguous plists
#
# Author:: Christian Kruse (mailto:cjk@wwwtech.de)
# Copyright:: Copyright (c) 2009
# License:: MIT License
require 'base64'
module CFPropertyList
##
# Blob is intended to distinguish between a Ruby String instance that should
# be converted to a CFString type and a Ruby String instance that should be
# converted to a CFData type
class Blob < String
end
##
# UidFixnum is intended to distinguish between a Ruby Integer
# instance that should be converted to a CFInteger/CFReal type and a
# Ruby Integer instance that should be converted to a CFUid type.
class UidFixnum < Integer
end
# This class defines the base class for all CFType classes
#
class CFType
# value of the type
attr_accessor :value
def initialize(value=nil)
@value = value
end
def to_xml(parser)
end
def to_binary(bplist)
end
def to_plain(plist)
end
end
# This class holds string values, both, UTF-8 and UTF-16BE
# It will convert the value to UTF-16BE if necessary (i.e. if non-ascii char contained)
class CFString < CFType
# convert to XML
def to_xml(parser)
n = parser.new_node('string')
n = parser.append_node(n, parser.new_text(@value)) unless @value.nil?
n
end
# convert to binary
def to_binary(bplist)
bplist.string_to_binary(@value);
end
def to_plain(plist)
if @value =~ /^\w+$/
@value
else
quoted
end
end
def quoted
str = '"'
@value.each_char do |c|
str << case c
when '"'
'\\"'
when '\\'
'\\'
when "\a"
"\\a"
when "\b"
"\\b"
when "\f"
"\\f"
when "\n"
"\n"
when "\v"
"\\v"
when "\r"
"\\r"
when "\t"
"\\t"
else
c
end
end
str << '"'
end
end
# This class holds integer/fixnum values
class CFInteger < CFType
# convert to XML
def to_xml(parser)
n = parser.new_node('integer')
n = parser.append_node(n, parser.new_text(@value.to_s))
n
end
# convert to binary
def to_binary(bplist)
bplist.num_to_binary(self)
end
def to_plain(plist)
@value.to_s
end
end
# This class holds float values
class CFReal < CFType
# convert to XML
def to_xml(parser)
n = parser.new_node('real')
n = parser.append_node(n, parser.new_text(@value.to_s))
n
end
# convert to binary
def to_binary(bplist)
bplist.num_to_binary(self)
end
def to_plain(plist)
@value.to_s
end
end
# This class holds Time values. While Apple uses seconds since 2001,
# the rest of the world uses seconds since 1970. So if you access value
# directly, you get the Time class. If you access via get_value you either
# geht the timestamp or the Apple timestamp
class CFDate < CFType
TIMESTAMP_APPLE = 0
TIMESTAMP_UNIX = 1
DATE_DIFF_APPLE_UNIX = 978307200
# create a XML date strimg from a time object
def CFDate.date_string(val)
# 2009-05-13T20:23:43Z
val.getutc.strftime("%Y-%m-%dT%H:%M:%SZ")
end
# parse a XML date string
def CFDate.parse_date(val)
# 2009-05-13T20:23:43Z
val =~ %r{^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$}
year,month,day,hour,min,sec = $1, $2, $3, $4, $5, $6
return Time.utc(year,month,day,hour,min,sec).getlocal
end
# set value to defined state
def initialize(value = nil,format=CFDate::TIMESTAMP_UNIX)
if(value.is_a?(Time) || value.nil?) then
@value = value.nil? ? Time.now : value
elsif value.instance_of? Date
@value = Time.utc(value.year, value.month, value.day, 0, 0, 0)
elsif value.instance_of? DateTime
@value = value.to_time.utc
else
set_value(value,format)
end
end
# set value with timestamp, either Apple or UNIX
def set_value(value,format=CFDate::TIMESTAMP_UNIX)
if(format == CFDate::TIMESTAMP_UNIX) then
@value = Time.at(value)
else
@value = Time.at(value + CFDate::DATE_DIFF_APPLE_UNIX)
end
end
# get timestamp, either UNIX or Apple timestamp
def get_value(format=CFDate::TIMESTAMP_UNIX)
if(format == CFDate::TIMESTAMP_UNIX) then
@value.to_i
else
@value.to_f - CFDate::DATE_DIFF_APPLE_UNIX
end
end
# convert to XML
def to_xml(parser)
n = parser.new_node('date')
n = parser.append_node(n, parser.new_text(CFDate::date_string(@value)))
n
end
# convert to binary
def to_binary(bplist)
bplist.date_to_binary(@value)
end
def to_plain(plist)
@value.strftime("%Y-%m-%d %H:%M:%S %z")
end
end
# This class contains a boolean value
class CFBoolean < CFType
# convert to XML
def to_xml(parser)
parser.new_node(@value ? 'true' : 'false')
end
# convert to binary
def to_binary(bplist)
bplist.bool_to_binary(@value);
end
def to_plain(plist)
@value ? "true" : "false"
end
end
# This class contains binary data values
class CFData < CFType
# Base64 encoded data
DATA_BASE64 = 0
# Raw data
DATA_RAW = 1
# set value to defined state, either base64 encoded or raw
def initialize(value=nil,format=DATA_BASE64)
if(format == DATA_RAW)
@raw_value = value
else
@value = value
end
end
# get base64 encoded value
def encoded_value
@value ||= "\n#{Base64.encode64(@raw_value).gsub("\n", '').scan(/.{1,76}/).join("\n")}\n"
end
# get base64 decoded value
def decoded_value
@raw_value ||= Blob.new(Base64.decode64(@value))
end
# convert to XML
def to_xml(parser)
n = parser.new_node('data')
n = parser.append_node(n, parser.new_text(encoded_value()))
n
end
# convert to binary
def to_binary(bplist)
bplist.data_to_binary(decoded_value())
end
def to_plain(plist)
"<" + decoded_value.unpack("H*").join("") + ">"
end
end
# This class contains an array of values
class CFArray < CFType
# create a new array CFType
def initialize(val=[])
@value = val
end
# convert to XML
def to_xml(parser)
n = parser.new_node('array')
@value.each do |v|
n = parser.append_node(n, v.to_xml(parser))
end
n
end
# convert to binary
def to_binary(bplist)
bplist.array_to_binary(self)
end
def to_plain(plist)
ary = @value.map { |v| v.to_plain(plist) }
"( " + ary.join(", ") + " )"
end
end
# this class contains a hash of values
class CFDictionary < CFType
# Create new CFDictonary type.
def initialize(value={})
@value = value
end
# convert to XML
def to_xml(parser)
n = parser.new_node('dict')
@value.each_pair do |key, value|
k = parser.append_node(parser.new_node('key'), parser.new_text(key.to_s))
n = parser.append_node(n, k)
n = parser.append_node(n, value.to_xml(parser))
end
n
end
# convert to binary
def to_binary(bplist)
bplist.dict_to_binary(self)
end
def to_plain(plist)
str = "{ "
cfstr = CFString.new()
@value.each do |k,v|
cfstr.value = k
str << cfstr.to_plain(plist) + " = " + v.to_plain(plist) + "; "
end
str << "}"
end
end
class CFUid < CFType
def to_xml(parser)
CFDictionary.new({'CF$UID' => CFInteger.new(@value)}).to_xml(parser)
end
# convert to binary
def to_binary(bplist)
bplist.uid_to_binary(@value)
end
def to_plain(plist)
CFDictionary.new({'CF$UID' => CFInteger.new(@value)}).to_plain(plist)
end
end
end
# eof

View file

@ -0,0 +1,149 @@
# -*- coding: utf-8 -*-
require 'libxml'
module CFPropertyList
# XML parser
class LibXMLParser < XMLParserInterface
LibXML::XML::Error.set_handler(&LibXML::XML::Error::QUIET_HANDLER)
PARSER_OPTIONS = LibXML::XML::Parser::Options::NOBLANKS|LibXML::XML::Parser::Options::NONET
# read a XML file
# opts::
# * :file - The filename of the file to load
# * :data - The data to parse
def load(opts)
doc = nil
if(opts.has_key?(:file)) then
doc = LibXML::XML::Document.file(opts[:file],:options => PARSER_OPTIONS)
else
doc = LibXML::XML::Document.string(opts[:data],:options => PARSER_OPTIONS)
end
if doc
root = doc.root.first
return import_xml(root)
end
rescue LibXML::XML::Error => e
raise CFFormatError.new('invalid XML: ' + e.message)
end
# serialize CFPropertyList object to XML
# opts = {}:: Specify options: :formatted - Use indention and line breaks
def to_str(opts={})
doc = LibXML::XML::Document.new
doc.root = LibXML::XML::Node.new('plist')
doc.encoding = LibXML::XML::Encoding::UTF_8
doc.root['version'] = '1.0'
doc.root << opts[:root].to_xml(self)
# ugly hack, but there's no other possibility I know
str = doc.to_s(:indent => opts[:formatted])
str1 = String.new
first = false
str.each_line do |line|
str1 << line
unless(first) then
str1 << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" if line =~ /^\s*<\?xml/
end
first = true
end
str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
return str1
end
def new_node(name)
LibXML::XML::Node.new(name)
end
def new_text(val)
LibXML::XML::Node.new_text(val)
end
def append_node(parent, child)
parent << child
end
protected
# get the value of a DOM node
def get_value(n)
content = if n.children?
n.first.content
else
n.content
end
content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
content
end
# import the XML values
def import_xml(node)
ret = nil
case node.name
when 'dict'
hsh = Hash.new
key = nil
if node.children? then
node.children.each do |n|
next if n.text? # avoid a bug of libxml
next if n.comment?
if n.name == "key" then
key = get_value(n)
else
raise CFFormatError.new("Format error!") if key.nil?
hsh[key] = import_xml(n)
key = nil
end
end
end
if hsh['CF$UID'] and hsh.keys.length == 1
ret = CFUid.new(hsh['CF$UID'].value)
else
ret = CFDictionary.new(hsh)
end
when 'array'
ary = Array.new
if node.children? then
node.children.each do |n|
next if n.text? # avoid a bug of libxml
next if n.comment?
ary.push import_xml(n)
end
end
ret = CFArray.new(ary)
when 'true'
ret = CFBoolean.new(true)
when 'false'
ret = CFBoolean.new(false)
when 'real'
ret = CFReal.new(get_value(node).to_f)
when 'integer'
ret = CFInteger.new(get_value(node).to_i)
when 'string'
ret = CFString.new(get_value(node))
when 'data'
ret = CFData.new(get_value(node))
when 'date'
ret = CFDate.new(CFDate.parse_date(get_value(node)))
end
return ret
end
end
end
# eof

View file

@ -0,0 +1,152 @@
# -*- coding: utf-8 -*-
require 'nokogiri'
module CFPropertyList
# XML parser
class NokogiriXMLParser < ParserInterface
PARSER_OPTIONS = Nokogiri::XML::ParseOptions::NOBLANKS|Nokogiri::XML::ParseOptions::NONET
# read a XML file
# opts::
# * :file - The filename of the file to load
# * :data - The data to parse
def load(opts)
doc = nil
if(opts.has_key?(:file)) then
File.open(opts[:file], "rb") { |fd| doc = Nokogiri::XML::Document.parse(fd, nil, nil, PARSER_OPTIONS) }
else
doc = Nokogiri::XML::Document.parse(opts[:data], nil, nil, PARSER_OPTIONS)
end
if doc
root = doc.root.children.first
return import_xml(root)
end
rescue Nokogiri::XML::SyntaxError => e
raise CFFormatError.new('invalid XML: ' + e.message)
end
# serialize CFPropertyList object to XML
# opts = {}:: Specify options: :formatted - Use indention and line breaks
def to_str(opts={})
doc = Nokogiri::XML::Document.new
@doc = doc
doc.root = doc.create_element 'plist', :version => '1.0'
doc.encoding = 'UTF-8'
doc.root << opts[:root].to_xml(self)
# ugly hack, but there's no other possibility I know
s_opts = Nokogiri::XML::Node::SaveOptions::AS_XML
s_opts |= Nokogiri::XML::Node::SaveOptions::FORMAT if opts[:formatted]
str = doc.serialize(:save_with => s_opts)
str1 = String.new
first = false
str.each_line do |line|
str1 << line
unless(first) then
str1 << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" if line =~ /^\s*<\?xml/
end
first = true
end
str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
return str1
end
def new_node(name)
@doc.create_element name
end
def new_text(val)
@doc.create_text_node val
end
def append_node(parent, child)
parent << child
end
protected
# get the value of a DOM node
def get_value(n)
content = if n.children.empty?
n.content
else
n.children.first.content
end
content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
content
end
# import the XML values
def import_xml(node)
ret = nil
case node.name
when 'dict'
hsh = Hash.new
key = nil
children = node.children
unless children.empty? then
children.each do |n|
next if n.text? # avoid a bug of libxml
next if n.comment?
if n.name == "key" then
key = get_value(n)
else
raise CFFormatError.new("Format error!") if key.nil?
hsh[key] = import_xml(n)
key = nil
end
end
end
if hsh['CF$UID'] and hsh.keys.length == 1
ret = CFUid.new(hsh['CF$UID'].value)
else
ret = CFDictionary.new(hsh)
end
when 'array'
ary = Array.new
children = node.children
unless children.empty? then
children.each do |n|
next if n.text? # avoid a bug of libxml
next if n.comment?
ary.push import_xml(n)
end
end
ret = CFArray.new(ary)
when 'true'
ret = CFBoolean.new(true)
when 'false'
ret = CFBoolean.new(false)
when 'real'
ret = CFReal.new(get_value(node).to_f)
when 'integer'
ret = CFInteger.new(get_value(node).to_i)
when 'string'
ret = CFString.new(get_value(node))
when 'data'
ret = CFData.new(get_value(node))
when 'date'
ret = CFDate.new(CFDate.parse_date(get_value(node)))
end
return ret
end
end
end
# eof

View file

@ -0,0 +1,199 @@
# -*- coding: utf-8 -*-
require 'strscan'
module CFPropertyList
# XML parser
class PlainParser < XMLParserInterface
# read a XML file
# opts::
# * :file - The filename of the file to load
# * :data - The data to parse
def load(opts)
@doc = nil
if(opts.has_key?(:file)) then
File.open(opts[:file], :external_encoding => "ASCII") do |fd|
@doc = StringScanner.new(fd.read)
end
else
@doc = StringScanner.new(opts[:data])
end
if @doc
root = import_plain
raise CFFormatError.new('content after root object') unless @doc.eos?
return root
end
raise CFFormatError.new('invalid plist string or file not found')
end
SPACES_AND_COMMENTS = %r{((?:/\*.*?\*/)|(?://.*?$\n?)|(?:\s*))+}x
# serialize CFPropertyList object to XML
# opts = {}:: Specify options: :formatted - Use indention and line breaks
def to_str(opts={})
opts[:root].to_plain(self)
end
protected
def skip_whitespaces
@doc.skip SPACES_AND_COMMENTS
end
def read_dict
skip_whitespaces
hsh = {}
while not @doc.scan(/\}/)
key = import_plain
raise CFFormatError.new("invalid dictionary format") if !key
if key.is_a?(CFString)
key = key.value
elsif key.is_a?(CFInteger) or key.is_a?(CFReal)
key = key.value.to_s
else
raise CFFormatError.new("invalid key format")
end
skip_whitespaces
raise CFFormatError.new("invalid dictionary format") unless @doc.scan(/=/)
skip_whitespaces
val = import_plain
skip_whitespaces
raise CFFormatError.new("invalid dictionary format") unless @doc.scan(/;/)
skip_whitespaces
hsh[key] = val
raise CFFormatError.new("invalid dictionary format") if @doc.eos?
end
CFDictionary.new(hsh)
end
def read_array
skip_whitespaces
ary = []
while not @doc.scan(/\)/)
val = import_plain
return nil if not val or not val.value
skip_whitespaces
if not @doc.skip(/,\s*/)
if @doc.scan(/\)/)
ary << val
return CFArray.new(ary)
end
raise CFFormatError.new("invalid array format")
end
ary << val
raise CFFormatError.new("invalid array format") if @doc.eos?
end
CFArray.new(ary)
end
def escape_char
case @doc.matched
when '"'
'"'
when '\\'
'\\'
when 'a'
"\a"
when 'b'
"\b"
when 'f'
"\f"
when 'n'
"\n"
when 'v'
"\v"
when 'r'
"\r"
when 't'
"\t"
when 'U'
@doc.scan(/.{4}/).hex.chr('utf-8')
end
end
def read_quoted
str = ''
while not @doc.scan(/"/)
if @doc.scan(/\\/)
@doc.scan(/./)
str << escape_char
elsif @doc.eos?
raise CFFormatError.new("unterminated string")
else @doc.scan(/./)
str << @doc.matched
end
end
CFString.new(str)
end
def read_unquoted
raise CFFormatError.new("unexpected end of file") if @doc.eos?
if @doc.scan(/(\d\d\d\d)-(\d\d)-(\d\d)\s+(\d\d):(\d\d):(\d\d)(?:\s+(\+|-)(\d\d)(\d\d))?/)
year,month,day,hour,min,sec,pl_min,tz_hour, tz_min = @doc[1], @doc[2], @doc[3], @doc[4], @doc[5], @doc[6], @doc[7], @doc[8], @doc[9]
CFDate.new(Time.new(year, month, day, hour, min, sec, pl_min ? sprintf("%s%s:%s", pl_min, tz_hour, tz_min) : nil))
elsif @doc.scan(/-?\d+?\.\d+\b/)
CFReal.new(@doc.matched.to_f)
elsif @doc.scan(/-?\d+\b/)
CFInteger.new(@doc.matched.to_i)
elsif @doc.scan(/\b(true|false)\b/)
CFBoolean.new(@doc.matched == 'true')
else
CFString.new(@doc.scan(/\w+/))
end
end
def read_binary
@doc.scan(/(.*?)>/)
hex_str = @doc[1].gsub(/ /, '')
CFData.new([hex_str].pack("H*"), CFData::DATA_RAW)
end
# import the XML values
def import_plain
skip_whitespaces
ret = nil
if @doc.scan(/\{/) # dict
ret = read_dict
elsif @doc.scan(/\(/) # array
ret = read_array
elsif @doc.scan(/"/) # string
ret = read_quoted
elsif @doc.scan(/</) # binary
ret = read_binary
else # string w/o quotes
ret = read_unquoted
end
return ret
end
end
end
# eof

View file

@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
require 'rexml/document'
module CFPropertyList
# XML parser
class ReXMLParser < ParserInterface
# read a XML file
# opts::
# * :file - The filename of the file to load
# * :data - The data to parse
def load(opts)
doc = nil
if(opts.has_key?(:file)) then
File.open(opts[:file], "rb") { |fd| doc = REXML::Document.new(fd) }
else
doc = REXML::Document.new(opts[:data])
end
if doc
root = doc.root.elements[1]
return import_xml(root)
end
rescue REXML::ParseException => e
raise CFFormatError.new('invalid XML: ' + e.message)
end
# serialize CFPropertyList object to XML
# opts = {}:: Specify options: :formatted - Use indention and line breaks
def to_str(opts={})
doc = REXML::Document.new
@doc = doc
doc.context[:attribute_quote] = :quote
doc.add_element 'plist', {'version' => '1.0'}
doc.root << opts[:root].to_xml(self)
formatter = if opts[:formatted] then
f = REXML::Formatters::Pretty.new(2)
f.compact = true
f.width = Float::INFINITY
f
else
REXML::Formatters::Default.new
end
str = formatter.write(doc.root, "")
str1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + str + "\n"
str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
return str1
end
def new_node(name)
REXML::Element.new(name)
end
def new_text(val)
val
end
def append_node(parent, child)
if child.is_a?(String) then
parent.add_text child
else
parent.elements << child
end
parent
end
protected
# get the value of a DOM node
def get_value(n)
content = n.text
content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
content
end
# import the XML values
def import_xml(node)
ret = nil
case node.name
when 'dict'
hsh = Hash.new
key = nil
if node.has_elements? then
node.elements.each do |n|
next if n.name == '#text' # avoid a bug of libxml
next if n.name == '#comment'
if n.name == "key" then
key = get_value(n)
key = '' if key.nil? # REXML returns nil if key is empty
else
raise CFFormatError.new("Format error!") if key.nil?
hsh[key] = import_xml(n)
key = nil
end
end
end
if hsh['CF$UID'] and hsh.keys.length == 1
ret = CFUid.new(hsh['CF$UID'].value)
else
ret = CFDictionary.new(hsh)
end
when 'array'
ary = Array.new
if node.has_elements? then
node.elements.each do |n|
next if n.name == '#text' # avoid a bug of libxml
ary.push import_xml(n)
end
end
ret = CFArray.new(ary)
when 'true'
ret = CFBoolean.new(true)
when 'false'
ret = CFBoolean.new(false)
when 'real'
ret = CFReal.new(get_value(node).to_f)
when 'integer'
ret = CFInteger.new(get_value(node).to_i)
when 'string'
ret = CFString.new(get_value(node))
ret.value = '' if ret.value.nil? # REXML returns nil for empty elements' .text attribute
when 'data'
ret = CFData.new(get_value(node))
when 'date'
ret = CFDate.new(CFDate.parse_date(get_value(node)))
end
return ret
end
end
end
# eof

View file

@ -0,0 +1,11 @@
/.bundle/
/.yardoc
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
# rspec failure tracking
.rspec_status

View file

@ -0,0 +1,3 @@
--format documentation
--color
--require spec_helper

View file

@ -0,0 +1,2 @@
inherit_from: .rubocop_todo.yml

View file

@ -0,0 +1,32 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2018-02-02 08:32:23 -0800 using RuboCop version 0.52.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 1
# Configuration parameters: Include.
# Include: **/*.gemspec
Gemspec/RequiredRubyVersion:
Exclude:
- 'atomos.gemspec'
# Offense count: 1
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 14
# Offense count: 1
Style/Documentation:
Exclude:
- 'spec/**/*'
- 'test/**/*'
- 'lib/atomos.rb'
# Offense count: 7
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 97

View file

@ -0,0 +1,5 @@
sudo: false
language: ruby
rvm:
- 2.5.0
before_install: gem install bundler -v 1.16.1

View file

@ -0,0 +1,74 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at segiddins@squareup.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View file

@ -0,0 +1,8 @@
# frozen_string_literal: true
source 'https://rubygems.org'
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
# Specify your gem's dependencies in atomos.gemspec
gemspec

View file

@ -0,0 +1,51 @@
PATH
remote: .
specs:
atomos (0.1.3)
GEM
remote: https://rubygems.org/
specs:
ast (2.3.0)
diff-lcs (1.3)
parallel (1.12.1)
parser (2.4.0.2)
ast (~> 2.3)
powerpack (0.1.1)
rainbow (3.0.0)
rake (10.5.0)
rspec (3.7.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-support (3.7.0)
rubocop (0.52.1)
parallel (~> 1.10)
parser (>= 2.4.0.2, < 3.0)
powerpack (~> 0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.9.0)
unicode-display_width (1.3.0)
PLATFORMS
ruby
DEPENDENCIES
atomos!
bundler (~> 1.16)
rake (~> 10.0)
rspec (~> 3.0)
rubocop
BUNDLED WITH
1.16.3

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 Samuel Giddins
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,43 @@
# Atomos
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/atomos`. To experiment with that code, run `bin/console` for an interactive prompt.
TODO: Delete this and the text above, and describe your gem
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'atomos'
```
And then execute:
$ bundle
Or install it yourself as:
$ gem install atomos
## Usage
TODO: Write usage instructions here
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/atomos. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
## Code of Conduct
Everyone interacting in the Atomos projects codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/atomos/blob/master/CODE_OF_CONDUCT.md).

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
require 'bundler/gem_tasks'
require 'rspec/core/rake_task'
require 'rubocop/rake_task'
RSpec::Core::RakeTask.new
RuboCop::RakeTask.new
task default: %i[rubocop spec]

View file

@ -0,0 +1 @@
0.1.3

View file

@ -0,0 +1,26 @@
# frozen_string_literal: true
Gem::Specification.new do |spec|
spec.name = 'atomos'
spec.version = File.read(File.expand_path('../VERSION', __FILE__))
spec.authors = ['Samuel Giddins']
spec.email = ['segiddins@segiddins.me']
spec.summary = 'A simple gem to atomically write files'
spec.homepage = 'https://github.com/segiddins/atomos'
spec.license = 'MIT'
spec.files = `git ls-files -z`.split("\x0").reject do |f|
f.match(%r{^(test|spec|features)/})
end
spec.bindir = 'exe'
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']
spec.required_ruby_version = '>= 2.0'
spec.add_development_dependency 'bundler', '~> 1.16'
spec.add_development_dependency 'rake', '~> 10.0'
spec.add_development_dependency 'rspec', '~> 3.0'
spec.add_development_dependency 'rubocop'
end

View file

@ -0,0 +1,15 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'bundler/setup'
require 'atomos'
# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.
# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start
require 'irb'
IRB.start(__FILE__)

View file

@ -0,0 +1,29 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'rake' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
Pathname.new(__FILE__).realpath)
bundle_binstub = File.expand_path('../bundle', __FILE__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 150).match?(/This file was generated by Bundler/)
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path('rake', 'rake')

View file

@ -0,0 +1,29 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'rspec' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
Pathname.new(__FILE__).realpath)
bundle_binstub = File.expand_path('../bundle', __FILE__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 150).match?(/This file was generated by Bundler/)
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path('rspec-core', 'rspec')

View file

@ -0,0 +1,29 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'rubocop' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
Pathname.new(__FILE__).realpath)
bundle_binstub = File.expand_path('../bundle', __FILE__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 150).match?(/This file was generated by Bundler/)
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path('rubocop', 'rubocop')

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx
bundle install
# Do any other automated setup that you need to do here

View file

@ -0,0 +1,47 @@
# frozen_string_literal: true
require 'atomos/version'
module Atomos
module_function
# rubocop:disable Metrics/MethodLength
def atomic_write(dest, contents = nil, tmpdir: nil, &block)
unless contents.nil? ^ block.nil?
raise ArgumentError, 'must provide either contents or a block'
end
tmpdir = Atomos.default_tmpdir_for_file(dest, tmpdir)
require 'tempfile'
Tempfile.open(".atomos.#{File.basename(dest)}", tmpdir) do |tmpfile|
if contents
tmpfile << contents
else
retval = yield tmpfile
end
tmpfile.close
File.rename(tmpfile.path, dest)
retval
end
end
# rubocop:enable Metrics/MethodLength
def self.default_tmpdir_for_file(dest, tmpdir)
tmpdir ||= begin
require 'tmpdir'
Dir.tmpdir
end
# Ensure the destination is on the same device as tmpdir
if File.stat(tmpdir).dev != File.stat(File.dirname(dest)).dev
# If not, use the directory of the destination as the tmpdir.
tmpdir = File.dirname(dest)
end
tmpdir
end
end

View file

@ -0,0 +1,5 @@
# frozen_string_literal: true
module Atomos
VERSION = File.read(File.expand_path('../../../VERSION', __FILE__))
end

View file

@ -0,0 +1,22 @@
Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View file

@ -0,0 +1,56 @@
Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
You can redistribute it and/or modify it under either the terms of the
2-clause BSDL (see the file BSDL), or the conditions below:
1. You may make and give away verbatim copies of the source form of the
software without restriction, provided that you duplicate all of the
original copyright notices and associated disclaimers.
2. You may modify your copy of the software in any way, provided that
you do at least ONE of the following:
a. place your modifications in the Public Domain or otherwise
make them Freely Available, such as by posting said
modifications to Usenet or an equivalent medium, or by allowing
the author to include your modifications in the software.
b. use the modified software only within your corporation or
organization.
c. give non-standard binaries non-standard names, with
instructions on where to get the original software distribution.
d. make other distribution arrangements with the author.
3. You may distribute the software in object code or binary form,
provided that you do at least ONE of the following:
a. distribute the binaries and library files of the software,
together with instructions (in the manual page or equivalent)
on where to get the original distribution.
b. accompany the distribution with the machine-readable source of
the software.
c. give non-standard binaries non-standard names, with
instructions on where to get the original software distribution.
d. make other distribution arrangements with the author.
4. You may modify and include the part of the software into any other
software (possibly commercial). But some files in the distribution
are not written by the author, so that they are not under these terms.
For the list of those files and their copying conditions, see the
file LEGAL.
5. The scripts and library files supplied as input to or produced as
output from the software do not automatically fall under the
copyright of the software, but belong to whomever generated them,
and may be sold commercially, and may be aggregated with this
software.
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.

View file

@ -0,0 +1,60 @@
# -*- rdoc -*-
= LEGAL NOTICE INFORMATION
--------------------------
All the files in this distribution are covered under either the Ruby's
license (see the file COPYING) or public-domain except some files
mentioned below.
== MIT License
>>>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
== Old-style BSD license
>>>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the University nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
IMPORTANT NOTE::
From ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
paragraph 3 above is now null and void.

View file

@ -0,0 +1,48 @@
# Base64
The Base64 module provides for the encoding (`#encode64`, `#strict_encode64`,
`#urlsafe_encode64`) and decoding (`#decode64`, `#strict_decode64`,
`#urlsafe_decode64`) of binary data using a Base64 representation.
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'base64'
```
And then execute:
$ bundle install
Or install it yourself as:
$ gem install base64
## Usage
A simple encoding and decoding.
```ruby
require "base64"
enc = Base64.encode64('Send reinforcements')
# -> "U2VuZCByZWluZm9yY2VtZW50cw==\n"
plain = Base64.decode64(enc)
# -> "Send reinforcements"
```
The purpose of using base64 to encode data is that it translates any
binary data into purely printable characters.
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/base64.

View file

@ -0,0 +1,381 @@
# frozen_string_literal: true
#
# \Module \Base64 provides methods for:
#
# - \Encoding a binary string (containing non-ASCII characters)
# as a string of printable ASCII characters.
# - Decoding such an encoded string.
#
# \Base64 is commonly used in contexts where binary data
# is not allowed or supported:
#
# - Images in HTML or CSS files, or in URLs.
# - Email attachments.
#
# A \Base64-encoded string is about one-third larger that its source.
# See the {Wikipedia article}[https://en.wikipedia.org/wiki/Base64]
# for more information.
#
# This module provides three pairs of encode/decode methods.
# Your choices among these methods should depend on:
#
# - Which character set is to be used for encoding and decoding.
# - Whether "padding" is to be used.
# - Whether encoded strings are to contain newlines.
#
# Note: Examples on this page assume that the including program has executed:
#
# require 'base64'
#
# == \Encoding Character Sets
#
# A \Base64-encoded string consists only of characters from a 64-character set:
#
# - <tt>('A'..'Z')</tt>.
# - <tt>('a'..'z')</tt>.
# - <tt>('0'..'9')</tt>.
# - <tt>=</tt>, the 'padding' character.
# - Either:
# - <tt>%w[+ /]</tt>:
# {RFC-2045-compliant}[https://datatracker.ietf.org/doc/html/rfc2045];
# _not_ safe for URLs.
# - <tt>%w[- _]</tt>:
# {RFC-4648-compliant}[https://datatracker.ietf.org/doc/html/rfc4648];
# safe for URLs.
#
# If you are working with \Base64-encoded strings that will come from
# or be put into URLs, you should choose this encoder-decoder pair
# of RFC-4648-compliant methods:
#
# - Base64.urlsafe_encode64 and Base64.urlsafe_decode64.
#
# Otherwise, you may choose any of the pairs in this module,
# including the pair above, or the RFC-2045-compliant pairs:
#
# - Base64.encode64 and Base64.decode64.
# - Base64.strict_encode64 and Base64.strict_decode64.
#
# == Padding
#
# \Base64-encoding changes a triplet of input bytes
# into a quartet of output characters.
#
# <b>Padding in Encode Methods</b>
#
# Padding -- extending an encoded string with zero, one, or two trailing
# <tt>=</tt> characters -- is performed by methods Base64.encode64,
# Base64.strict_encode64, and, by default, Base64.urlsafe_encode64:
#
# Base64.encode64('s') # => "cw==\n"
# Base64.strict_encode64('s') # => "cw=="
# Base64.urlsafe_encode64('s') # => "cw=="
# Base64.urlsafe_encode64('s', padding: false) # => "cw"
#
# When padding is performed, the encoded string is always of length <em>4n</em>,
# where +n+ is a non-negative integer:
#
# - Input bytes of length <em>3n</em> generate unpadded output characters
# of length <em>4n</em>:
#
# # n = 1: 3 bytes => 4 characters.
# Base64.strict_encode64('123') # => "MDEy"
# # n = 2: 6 bytes => 8 characters.
# Base64.strict_encode64('123456') # => "MDEyMzQ1"
#
# - Input bytes of length <em>3n+1</em> generate padded output characters
# of length <em>4(n+1)</em>, with two padding characters at the end:
#
# # n = 1: 4 bytes => 8 characters.
# Base64.strict_encode64('1234') # => "MDEyMw=="
# # n = 2: 7 bytes => 12 characters.
# Base64.strict_encode64('1234567') # => "MDEyMzQ1Ng=="
#
# - Input bytes of length <em>3n+2</em> generate padded output characters
# of length <em>4(n+1)</em>, with one padding character at the end:
#
# # n = 1: 5 bytes => 8 characters.
# Base64.strict_encode64('12345') # => "MDEyMzQ="
# # n = 2: 8 bytes => 12 characters.
# Base64.strict_encode64('12345678') # => "MDEyMzQ1Njc="
#
# When padding is suppressed, for a positive integer <em>n</em>:
#
# - Input bytes of length <em>3n</em> generate unpadded output characters
# of length <em>4n</em>:
#
# # n = 1: 3 bytes => 4 characters.
# Base64.urlsafe_encode64('123', padding: false) # => "MDEy"
# # n = 2: 6 bytes => 8 characters.
# Base64.urlsafe_encode64('123456', padding: false) # => "MDEyMzQ1"
#
# - Input bytes of length <em>3n+1</em> generate unpadded output characters
# of length <em>4n+2</em>, with two padding characters at the end:
#
# # n = 1: 4 bytes => 6 characters.
# Base64.urlsafe_encode64('1234', padding: false) # => "MDEyMw"
# # n = 2: 7 bytes => 10 characters.
# Base64.urlsafe_encode64('1234567', padding: false) # => "MDEyMzQ1Ng"
#
# - Input bytes of length <em>3n+2</em> generate unpadded output characters
# of length <em>4n+3</em>, with one padding character at the end:
#
# # n = 1: 5 bytes => 7 characters.
# Base64.urlsafe_encode64('12345', padding: false) # => "MDEyMzQ"
# # m = 2: 8 bytes => 11 characters.
# Base64.urlsafe_encode64('12345678', padding: false) # => "MDEyMzQ1Njc"
#
# <b>Padding in Decode Methods</b>
#
# All of the \Base64 decode methods support (but do not require) padding.
#
# \Method Base64.decode64 does not check the size of the padding:
#
# Base64.decode64("MDEyMzQ1Njc") # => "01234567"
# Base64.decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.decode64("MDEyMzQ1Njc==") # => "01234567"
#
# \Method Base64.strict_decode64 strictly enforces padding size:
#
# Base64.strict_decode64("MDEyMzQ1Njc") # Raises ArgumentError
# Base64.strict_decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.strict_decode64("MDEyMzQ1Njc==") # Raises ArgumentError
#
# \Method Base64.urlsafe_decode64 allows padding in the encoded string,
# which if present, must be correct:
# see {Padding}[Base64.html#module-Base64-label-Padding], above:
#
# Base64.urlsafe_decode64("MDEyMzQ1Njc") # => "01234567"
# Base64.urlsafe_decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.urlsafe_decode64("MDEyMzQ1Njc==") # Raises ArgumentError.
#
# == Newlines
#
# An encoded string returned by Base64.encode64 or Base64.urlsafe_encode64
# has an embedded newline character
# after each 60-character sequence, and, if non-empty, at the end:
#
# # No newline if empty.
# encoded = Base64.encode64("\x00" * 0)
# encoded.index("\n") # => nil
#
# # Newline at end of short output.
# encoded = Base64.encode64("\x00" * 1)
# encoded.size # => 4
# encoded.index("\n") # => 4
#
# # Newline at end of longer output.
# encoded = Base64.encode64("\x00" * 45)
# encoded.size # => 60
# encoded.index("\n") # => 60
#
# # Newlines embedded and at end of still longer output.
# encoded = Base64.encode64("\x00" * 46)
# encoded.size # => 65
# encoded.rindex("\n") # => 65
# encoded.split("\n").map {|s| s.size } # => [60, 4]
#
# The string to be encoded may itself contain newlines,
# which are encoded as \Base64:
#
# # Base64.encode64("\n\n\n") # => "CgoK\n"
# s = "This is line 1\nThis is line 2\n"
# Base64.encode64(s) # => "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK\n"
#
module Base64
VERSION = "0.3.0"
module_function
# :call-seq:
# Base64.encode64(string) -> encoded_string
#
# Returns a string containing the RFC-2045-compliant \Base64-encoding of +string+.
#
# Per RFC 2045, the returned string may contain the URL-unsafe characters
# <tt>+</tt> or <tt>/</tt>;
# see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above:
#
# Base64.encode64("\xFB\xEF\xBE") # => "++++\n"
# Base64.encode64("\xFF\xFF\xFF") # => "////\n"
#
# The returned string may include padding;
# see {Padding}[Base64.html#module-Base64-label-Padding] above.
#
# Base64.encode64('*') # => "Kg==\n"
#
# The returned string ends with a newline character, and if sufficiently long
# will have one or more embedded newline characters;
# see {Newlines}[Base64.html#module-Base64-label-Newlines] above:
#
# Base64.encode64('*') # => "Kg==\n"
# Base64.encode64('*' * 46)
# # => "KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq\nKg==\n"
#
# The string to be encoded may itself contain newlines,
# which will be encoded as ordinary \Base64:
#
# Base64.encode64("\n\n\n") # => "CgoK\n"
# s = "This is line 1\nThis is line 2\n"
# Base64.encode64(s) # => "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK\n"
#
def encode64(bin)
[bin].pack("m")
end
# :call-seq:
# Base64.decode(encoded_string) -> decoded_string
#
# Returns a string containing the decoding of an RFC-2045-compliant
# \Base64-encoded string +encoded_string+:
#
# s = "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK\n"
# Base64.decode64(s) # => "This is line 1\nThis is line 2\n"
#
# Non-\Base64 characters in +encoded_string+ are ignored;
# see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above:
# these include newline characters and characters <tt>-</tt> and <tt>/</tt>:
#
# Base64.decode64("\x00\n-_") # => ""
#
# Padding in +encoded_string+ (even if incorrect) is ignored:
#
# Base64.decode64("MDEyMzQ1Njc") # => "01234567"
# Base64.decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.decode64("MDEyMzQ1Njc==") # => "01234567"
#
def decode64(str)
str.unpack1("m")
end
# :call-seq:
# Base64.strict_encode64(string) -> encoded_string
#
# Returns a string containing the RFC-2045-compliant \Base64-encoding of +string+.
#
# Per RFC 2045, the returned string may contain the URL-unsafe characters
# <tt>+</tt> or <tt>/</tt>;
# see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above:
#
# Base64.strict_encode64("\xFB\xEF\xBE") # => "++++\n"
# Base64.strict_encode64("\xFF\xFF\xFF") # => "////\n"
#
# The returned string may include padding;
# see {Padding}[Base64.html#module-Base64-label-Padding] above.
#
# Base64.strict_encode64('*') # => "Kg==\n"
#
# The returned string will have no newline characters, regardless of its length;
# see {Newlines}[Base64.html#module-Base64-label-Newlines] above:
#
# Base64.strict_encode64('*') # => "Kg=="
# Base64.strict_encode64('*' * 46)
# # => "KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg=="
#
# The string to be encoded may itself contain newlines,
# which will be encoded as ordinary \Base64:
#
# Base64.strict_encode64("\n\n\n") # => "CgoK"
# s = "This is line 1\nThis is line 2\n"
# Base64.strict_encode64(s) # => "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK"
#
def strict_encode64(bin)
[bin].pack("m0")
end
# :call-seq:
# Base64.strict_decode64(encoded_string) -> decoded_string
#
# Returns a string containing the decoding of an RFC-2045-compliant
# \Base64-encoded string +encoded_string+:
#
# s = "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK"
# Base64.strict_decode64(s) # => "This is line 1\nThis is line 2\n"
#
# Non-\Base64 characters in +encoded_string+ are not allowed;
# see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above:
# these include newline characters and characters <tt>-</tt> and <tt>/</tt>:
#
# Base64.strict_decode64("\n") # Raises ArgumentError
# Base64.strict_decode64('-') # Raises ArgumentError
# Base64.strict_decode64('_') # Raises ArgumentError
#
# Padding in +encoded_string+, if present, must be correct:
#
# Base64.strict_decode64("MDEyMzQ1Njc") # Raises ArgumentError
# Base64.strict_decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.strict_decode64("MDEyMzQ1Njc==") # Raises ArgumentError
#
def strict_decode64(str)
str.unpack1("m0")
end
# :call-seq:
# Base64.urlsafe_encode64(string) -> encoded_string
#
# Returns the RFC-4648-compliant \Base64-encoding of +string+.
#
# Per RFC 4648, the returned string will not contain the URL-unsafe characters
# <tt>+</tt> or <tt>/</tt>,
# but instead may contain the URL-safe characters
# <tt>-</tt> and <tt>_</tt>;
# see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above:
#
# Base64.urlsafe_encode64("\xFB\xEF\xBE") # => "----"
# Base64.urlsafe_encode64("\xFF\xFF\xFF") # => "____"
#
# By default, the returned string may have padding;
# see {Padding}[Base64.html#module-Base64-label-Padding], above:
#
# Base64.urlsafe_encode64('*') # => "Kg=="
#
# Optionally, you can suppress padding:
#
# Base64.urlsafe_encode64('*', padding: false) # => "Kg"
#
# The returned string will have no newline characters, regardless of its length;
# see {Newlines}[Base64.html#module-Base64-label-Newlines] above:
#
# Base64.urlsafe_encode64('*') # => "Kg=="
# Base64.urlsafe_encode64('*' * 46)
# # => "KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg=="
#
def urlsafe_encode64(bin, padding: true)
str = strict_encode64(bin)
str.chomp!("==") or str.chomp!("=") unless padding
str.tr!("+/", "-_")
str
end
# :call-seq:
# Base64.urlsafe_decode64(encoded_string) -> decoded_string
#
# Returns the decoding of an RFC-4648-compliant \Base64-encoded string +encoded_string+:
#
# +encoded_string+ may not contain non-Base64 characters;
# see {Encoding Character Set}[Base64.html#module-Base64-label-Encoding+Character+Sets] above:
#
# Base64.urlsafe_decode64('+') # Raises ArgumentError.
# Base64.urlsafe_decode64('/') # Raises ArgumentError.
# Base64.urlsafe_decode64("\n") # Raises ArgumentError.
#
# Padding in +encoded_string+, if present, must be correct:
# see {Padding}[Base64.html#module-Base64-label-Padding], above:
#
# Base64.urlsafe_decode64("MDEyMzQ1Njc") # => "01234567"
# Base64.urlsafe_decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.urlsafe_decode64("MDEyMzQ1Njc==") # Raises ArgumentError.
#
def urlsafe_decode64(str)
# NOTE: RFC 4648 does say nothing about unpadded input, but says that
# "the excess pad characters MAY also be ignored", so it is inferred that
# unpadded input is also acceptable.
if !str.end_with?("=") && str.length % 4 != 0
str = str.ljust((str.length + 3) & ~3, "=")
str.tr!("-_", "+/")
else
str = str.tr("-_", "+/")
end
strict_decode64(str)
end
end

View file

@ -0,0 +1,355 @@
# <!-- rdoc-file=lib/base64.rb -->
# Module Base64 provides methods for:
#
# * Encoding a binary string (containing non-ASCII characters) as a string of
# printable ASCII characters.
# * Decoding such an encoded string.
#
# Base64 is commonly used in contexts where binary data is not allowed or
# supported:
#
# * Images in HTML or CSS files, or in URLs.
# * Email attachments.
#
# A Base64-encoded string is about one-third larger that its source. See the
# [Wikipedia article](https://en.wikipedia.org/wiki/Base64) for more
# information.
#
# This module provides three pairs of encode/decode methods. Your choices among
# these methods should depend on:
#
# * Which character set is to be used for encoding and decoding.
# * Whether "padding" is to be used.
# * Whether encoded strings are to contain newlines.
#
# Note: Examples on this page assume that the including program has executed:
#
# require 'base64'
#
# ## Encoding Character Sets
#
# A Base64-encoded string consists only of characters from a 64-character set:
#
# * `('A'..'Z')`.
# * `('a'..'z')`.
# * `('0'..'9')`.
# * `=`, the 'padding' character.
# * Either:
# * `%w[+ /]`:
# [RFC-2045-compliant](https://datatracker.ietf.org/doc/html/rfc2045);
# *not* safe for URLs.
# * `%w[- _]`:
# [RFC-4648-compliant](https://datatracker.ietf.org/doc/html/rfc4648);
# safe for URLs.
#
# If you are working with Base64-encoded strings that will come from or be put
# into URLs, you should choose this encoder-decoder pair of RFC-4648-compliant
# methods:
#
# * Base64.urlsafe_encode64 and Base64.urlsafe_decode64.
#
# Otherwise, you may choose any of the pairs in this module, including the pair
# above, or the RFC-2045-compliant pairs:
#
# * Base64.encode64 and Base64.decode64.
# * Base64.strict_encode64 and Base64.strict_decode64.
#
# ## Padding
#
# Base64-encoding changes a triplet of input bytes into a quartet of output
# characters.
#
# **Padding in Encode Methods**
#
# Padding -- extending an encoded string with zero, one, or two trailing `=`
# characters -- is performed by methods Base64.encode64, Base64.strict_encode64,
# and, by default, Base64.urlsafe_encode64:
#
# Base64.encode64('s') # => "cw==\n"
# Base64.strict_encode64('s') # => "cw=="
# Base64.urlsafe_encode64('s') # => "cw=="
# Base64.urlsafe_encode64('s', padding: false) # => "cw"
#
# When padding is performed, the encoded string is always of length *4n*, where
# `n` is a non-negative integer:
#
# * Input bytes of length *3n* generate unpadded output characters of length
# *4n*:
#
# # n = 1: 3 bytes => 4 characters.
# Base64.strict_encode64('123') # => "MDEy"
# # n = 2: 6 bytes => 8 characters.
# Base64.strict_encode64('123456') # => "MDEyMzQ1"
#
# * Input bytes of length *3n+1* generate padded output characters of length
# *4(n+1)*, with two padding characters at the end:
#
# # n = 1: 4 bytes => 8 characters.
# Base64.strict_encode64('1234') # => "MDEyMw=="
# # n = 2: 7 bytes => 12 characters.
# Base64.strict_encode64('1234567') # => "MDEyMzQ1Ng=="
#
# * Input bytes of length *3n+2* generate padded output characters of length
# *4(n+1)*, with one padding character at the end:
#
# # n = 1: 5 bytes => 8 characters.
# Base64.strict_encode64('12345') # => "MDEyMzQ="
# # n = 2: 8 bytes => 12 characters.
# Base64.strict_encode64('12345678') # => "MDEyMzQ1Njc="
#
# When padding is suppressed, for a positive integer *n*:
#
# * Input bytes of length *3n* generate unpadded output characters of length
# *4n*:
#
# # n = 1: 3 bytes => 4 characters.
# Base64.urlsafe_encode64('123', padding: false) # => "MDEy"
# # n = 2: 6 bytes => 8 characters.
# Base64.urlsafe_encode64('123456', padding: false) # => "MDEyMzQ1"
#
# * Input bytes of length *3n+1* generate unpadded output characters of length
# *4n+2*, with two padding characters at the end:
#
# # n = 1: 4 bytes => 6 characters.
# Base64.urlsafe_encode64('1234', padding: false) # => "MDEyMw"
# # n = 2: 7 bytes => 10 characters.
# Base64.urlsafe_encode64('1234567', padding: false) # => "MDEyMzQ1Ng"
#
# * Input bytes of length *3n+2* generate unpadded output characters of length
# *4n+3*, with one padding character at the end:
#
# # n = 1: 5 bytes => 7 characters.
# Base64.urlsafe_encode64('12345', padding: false) # => "MDEyMzQ"
# # m = 2: 8 bytes => 11 characters.
# Base64.urlsafe_encode64('12345678', padding: false) # => "MDEyMzQ1Njc"
#
# **Padding in Decode Methods**
#
# All of the Base64 decode methods support (but do not require) padding.
#
# Method Base64.decode64 does not check the size of the padding:
#
# Base64.decode64("MDEyMzQ1Njc") # => "01234567"
# Base64.decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.decode64("MDEyMzQ1Njc==") # => "01234567"
#
# Method Base64.strict_decode64 strictly enforces padding size:
#
# Base64.strict_decode64("MDEyMzQ1Njc") # Raises ArgumentError
# Base64.strict_decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.strict_decode64("MDEyMzQ1Njc==") # Raises ArgumentError
#
# Method Base64.urlsafe_decode64 allows padding in `str`, which if present, must
# be correct: see [Padding](Base64.html#module-Base64-label-Padding), above:
#
# Base64.urlsafe_decode64("MDEyMzQ1Njc") # => "01234567"
# Base64.urlsafe_decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.urlsafe_decode64("MDEyMzQ1Njc==") # Raises ArgumentError.
#
# ## Newlines
#
# An encoded string returned by Base64.encode64 or Base64.urlsafe_encode64 has
# an embedded newline character after each 60-character sequence, and, if
# non-empty, at the end:
#
# # No newline if empty.
# encoded = Base64.encode64("\x00" * 0)
# encoded.index("\n") # => nil
#
# # Newline at end of short output.
# encoded = Base64.encode64("\x00" * 1)
# encoded.size # => 4
# encoded.index("\n") # => 4
#
# # Newline at end of longer output.
# encoded = Base64.encode64("\x00" * 45)
# encoded.size # => 60
# encoded.index("\n") # => 60
#
# # Newlines embedded and at end of still longer output.
# encoded = Base64.encode64("\x00" * 46)
# encoded.size # => 65
# encoded.rindex("\n") # => 65
# encoded.split("\n").map {|s| s.size } # => [60, 4]
#
# The string to be encoded may itself contain newlines, which are encoded as
# Base64:
#
# # Base64.encode64("\n\n\n") # => "CgoK\n"
# s = "This is line 1\nThis is line 2\n"
# Base64.encode64(s) # => "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK\n"
#
module Base64
# <!--
# rdoc-file=lib/base64.rb
# - decode64(str)
# -->
# Returns a string containing the decoding of an RFC-2045-compliant
# Base64-encoded string `str`:
#
# s = "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK\n"
# Base64.decode64(s) # => "This is line 1\nThis is line 2\n"
#
# Non-Base64 characters in `str` are ignored; see [Encoding Character
# Set](Base64.html#module-Base64-label-Encoding+Character+Sets) above: these
# include newline characters and characters `-` and `/`:
#
# Base64.decode64("\x00\n-_") # => ""
#
# Padding in `str` (even if incorrect) is ignored:
#
# Base64.decode64("MDEyMzQ1Njc") # => "01234567"
# Base64.decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.decode64("MDEyMzQ1Njc==") # => "01234567"
#
def self?.decode64: (String str) -> String
# <!--
# rdoc-file=lib/base64.rb
# - encode64(bin)
# -->
# Returns a string containing the RFC-2045-compliant Base64-encoding of `bin`.
#
# Per RFC 2045, the returned string may contain the URL-unsafe characters `+` or
# `/`; see [Encoding Character
# Set](Base64.html#module-Base64-label-Encoding+Character+Sets) above:
#
# Base64.encode64("\xFB\xEF\xBE") # => "++++\n"
# Base64.encode64("\xFF\xFF\xFF") # => "////\n"
#
# The returned string may include padding; see
# [Padding](Base64.html#module-Base64-label-Padding) above.
#
# Base64.encode64('*') # => "Kg==\n"
#
# The returned string ends with a newline character, and if sufficiently long
# will have one or more embedded newline characters; see
# [Newlines](Base64.html#module-Base64-label-Newlines) above:
#
# Base64.encode64('*') # => "Kg==\n"
# Base64.encode64('*' * 46)
# # => "KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq\nKg==\n"
#
# The string to be encoded may itself contain newlines, which will be encoded as
# ordinary Base64:
#
# Base64.encode64("\n\n\n") # => "CgoK\n"
# s = "This is line 1\nThis is line 2\n"
# Base64.encode64(s) # => "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK\n"
#
def self?.encode64: (String bin) -> String
# <!--
# rdoc-file=lib/base64.rb
# - strict_decode64(str)
# -->
# Returns a string containing the decoding of an RFC-2045-compliant
# Base64-encoded string `str`:
#
# s = "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK"
# Base64.strict_decode64(s) # => "This is line 1\nThis is line 2\n"
#
# Non-Base64 characters in `str` not allowed; see [Encoding Character
# Set](Base64.html#module-Base64-label-Encoding+Character+Sets) above: these
# include newline characters and characters `-` and `/`:
#
# Base64.strict_decode64("\n") # Raises ArgumentError
# Base64.strict_decode64('-') # Raises ArgumentError
# Base64.strict_decode64('_') # Raises ArgumentError
#
# Padding in `str`, if present, must be correct:
#
# Base64.strict_decode64("MDEyMzQ1Njc") # Raises ArgumentError
# Base64.strict_decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.strict_decode64("MDEyMzQ1Njc==") # Raises ArgumentError
#
def self?.strict_decode64: (String str) -> String
# <!--
# rdoc-file=lib/base64.rb
# - strict_encode64(bin)
# -->
# Returns a string containing the RFC-2045-compliant Base64-encoding of `bin`.
#
# Per RFC 2045, the returned string may contain the URL-unsafe characters `+` or
# `/`; see [Encoding Character
# Set](Base64.html#module-Base64-label-Encoding+Character+Sets) above:
#
# Base64.strict_encode64("\xFB\xEF\xBE") # => "++++\n"
# Base64.strict_encode64("\xFF\xFF\xFF") # => "////\n"
#
# The returned string may include padding; see
# [Padding](Base64.html#module-Base64-label-Padding) above.
#
# Base64.strict_encode64('*') # => "Kg==\n"
#
# The returned string will have no newline characters, regardless of its length;
# see [Newlines](Base64.html#module-Base64-label-Newlines) above:
#
# Base64.strict_encode64('*') # => "Kg=="
# Base64.strict_encode64('*' * 46)
# # => "KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg=="
#
# The string to be encoded may itself contain newlines, which will be encoded as
# ordinary Base64:
#
# Base64.strict_encode64("\n\n\n") # => "CgoK"
# s = "This is line 1\nThis is line 2\n"
# Base64.strict_encode64(s) # => "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK"
#
def self?.strict_encode64: (String bin) -> String
# <!--
# rdoc-file=lib/base64.rb
# - urlsafe_decode64(str)
# -->
# Returns the decoding of an RFC-4648-compliant Base64-encoded string `str`:
#
# `str` may not contain non-Base64 characters; see [Encoding Character
# Set](Base64.html#module-Base64-label-Encoding+Character+Sets) above:
#
# Base64.urlsafe_decode64('+') # Raises ArgumentError.
# Base64.urlsafe_decode64('/') # Raises ArgumentError.
# Base64.urlsafe_decode64("\n") # Raises ArgumentError.
#
# Padding in `str`, if present, must be correct: see
# [Padding](Base64.html#module-Base64-label-Padding), above:
#
# Base64.urlsafe_decode64("MDEyMzQ1Njc") # => "01234567"
# Base64.urlsafe_decode64("MDEyMzQ1Njc=") # => "01234567"
# Base64.urlsafe_decode64("MDEyMzQ1Njc==") # Raises ArgumentError.
#
def self?.urlsafe_decode64: (String str) -> String
# <!--
# rdoc-file=lib/base64.rb
# - urlsafe_encode64(bin, padding: true)
# -->
# Returns the RFC-4648-compliant Base64-encoding of `bin`.
#
# Per RFC 4648, the returned string will not contain the URL-unsafe characters
# `+` or `/`, but instead may contain the URL-safe characters `-` and `_`; see
# [Encoding Character
# Set](Base64.html#module-Base64-label-Encoding+Character+Sets) above:
#
# Base64.urlsafe_encode64("\xFB\xEF\xBE") # => "----"
# Base64.urlsafe_encode64("\xFF\xFF\xFF") # => "____"
#
# By default, the returned string may have padding; see
# [Padding](Base64.html#module-Base64-label-Padding), above:
#
# Base64.urlsafe_encode64('*') # => "Kg=="
#
# Optionally, you can suppress padding:
#
# Base64.urlsafe_encode64('*', padding: false) # => "Kg"
#
# The returned string will have no newline characters, regardless of its length;
# see [Newlines](Base64.html#module-Base64-label-Newlines) above:
#
# Base64.urlsafe_encode64('*') # => "Kg=="
# Base64.urlsafe_encode64('*' * 46)
# # => "KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg=="
#
def self?.urlsafe_encode64: (String bin, ?padding: boolish) -> String
end

View file

@ -0,0 +1,45 @@
name: ci
on:
pull_request:
push:
branches:
- master
- '*-stable'
jobs:
ci:
name: Ruby ${{ matrix.ruby.name }}
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
ruby:
- { name: "2.3", value: 2.3.8 }
- { name: "2.4", value: 2.4.10 }
- { name: "2.5", value: 2.5.9 }
- { name: "2.6", value: 2.6.9 }
- { name: "2.7", value: 2.7.5 }
- { name: "3.0", value: 3.0.3 }
- { name: "3.1", value: 3.1.0 }
steps:
- uses: actions/checkout@v2
- name: Setup ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby.value }}
bundler-cache: true
- name: Run Test
run: bundle exec rake spec
- name: Test & publish code coverage
uses: paambaati/codeclimate-action@v3.0.0
env:
CC_TEST_REPORTER_ID: 46c8b29dd6711f35704e7c5a541486cbbf2cff8b2df8ce755bfc09917d3c1cbb

View file

@ -0,0 +1,17 @@
*.gem
*.rbc
.bundle
.config
.yardoc
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
.idea

View file

@ -0,0 +1,30 @@
recipe :ruby
Kicker::Recipes::Ruby.runner_bin = 'bundle exec bacon --quiet'
process do |files|
specs = files.take_and_map do |file|
if file =~ %r{lib/[^/]*/(.+?)\.rb$}
s = Dir.glob("spec/**/#{File.basename(file, '.rb')}_spec.rb")
s.uniq unless s.empty?
end
end
Kicker::Recipes::Ruby.run_tests(specs)
end
# Have written this so many times, probably should make a recipe out of it.
process do |files|
files.each do |file|
case file
when 'Gemfile'
files.delete(file)
execute 'bundle install'
end
end
end
recipe :ignore
ignore(/.*\/?tags/)
ignore(/.*\/?\.git/)
ignore(/^tmp/)

View file

@ -0,0 +1,6 @@
require:
- rubocop-performance
inherit_from:
- .rubocop_todo.yml
- .rubocop_cocoapods.yml

View file

@ -0,0 +1,151 @@
AllCops:
Include:
- ./Rakefile
- ./Gemfile
- ./*.gemspec
Exclude:
- ./spec/fixtures/**/*
- ./vendor/bundle/**/*
# At the moment not ready to be used
# https://github.com/bbatsov/rubocop/issues/947
Style/Documentation:
Enabled: false
#- CocoaPods -----------------------------------------------------------------#
# We adopted raise instead of fail.
Style/SignalException:
EnforcedStyle: only_raise
# They are idiomatic
Lint/AssignmentInCondition:
Enabled: false
# Allow backticks
Style/AsciiComments:
Enabled: false
# Indentation clarifies logic branches in implementations
Style/IfUnlessModifier:
Enabled: false
# No enforced convention here.
Style/SingleLineBlockParams:
Enabled: false
# We only add the comment when needed.
Style/Encoding:
Enabled: false
# Having these make it easier to *not* forget to add one when adding a new
# value and you can simply copy the previous line.
Style/TrailingCommaInArguments:
EnforcedStyleForMultiline: comma
Style/TrailingCommaInArrayLiteral:
EnforcedStyleForMultiline: comma
Style/TrailingCommaInHashLiteral:
EnforcedStyleForMultiline: comma
Layout/MultilineOperationIndentation:
EnforcedStyle: indented
# Clashes with CLAide Command#validate!
Style/GuardClause:
Enabled: false
# Not always desirable: lib/claide/command/plugins_helper.rb:12:15
Style/Next:
Enabled: false
# Arbitrary max lengths for classes simply do not work and enabling this will
# lead to a never ending stream of annoyance and changes.
Metrics/ClassLength:
Enabled: false
# Arbitrary max lengths for modules simply do not work and enabling this will
# lead to a never ending stream of annoyance and changes.
Metrics/ModuleLength:
Enabled: false
# Arbitrary max lengths for methods simply do not work and enabling this will
# lead to a never ending stream of annoyance and changes.
Metrics/MethodLength:
Enabled: false
# No enforced convention here.
Metrics/BlockNesting:
Enabled: false
# It will be obvious which code is complex, Rubocop should only lint simple
# rules for us.
Metrics/AbcSize:
Enabled: false
# It will be obvious which code is complex, Rubocop should only lint simple
# rules for us.
Metrics/CyclomaticComplexity:
Enabled: false
# It will be obvious which code is complex, Rubocop should only lint simple
# rules for us.
Metrics/PerceivedComplexity:
Enabled: false
#- CocoaPods support for Ruby 1.8.7 ------------------------------------------#
Style/HashSyntax:
EnforcedStyle: hash_rockets
Style/Lambda:
Enabled: false
Layout/DotPosition:
EnforcedStyle: trailing
Style/EachWithObject:
Enabled: false
Style/SpecialGlobalVars:
Enabled: false
#- CocoaPods specs -----------------------------------------------------------#
# Allow for `should.match /regexp/`.
Lint/AmbiguousRegexpLiteral:
Exclude:
- spec/**/*
Performance/RedundantMatch:
Exclude:
- spec/**/*
# Allow `object.should == object` syntax.
Lint/Void:
Exclude:
- spec/**/*
Style/ClassAndModuleChildren:
Exclude:
- spec/**/*
Lint/UselessComparison:
Exclude:
- spec/**/*
Lint/RaiseException:
Enabled: false
Lint/StructNewOverride:
Enabled: false
Style/HashEachMethods:
Enabled: false
Style/HashTransformKeys:
Enabled: false
Style/HashTransformValues:
Enabled: false

View file

@ -0,0 +1,70 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2016-03-09 18:40:14 -0600 using RuboCop version 0.38.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 3
Lint/IneffectiveAccessModifier:
Exclude:
- 'lib/claide/command.rb'
# Offense count: 1
# Cop supports --auto-correct.
Lint/RedundantCopDisableDirective:
Exclude:
- 'spec/command/banner_spec.rb'
# Offense count: 1
Performance/FixedSize:
Exclude:
- 'lib/claide/command/banner.rb'
# Offense count: 1
# Cop supports --auto-correct.
Performance/StringReplacement:
Exclude:
- 'lib/claide/command/banner.rb'
# Offense count: 8
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: prefer_alias, prefer_alias_method
Style/Alias:
Exclude:
- 'lib/claide/argument.rb'
- 'lib/claide/command.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: SingleLineConditionsOnly.
Style/ConditionalAssignment:
Exclude:
- 'lib/claide/command/banner.rb'
# Offense count: 1
Style/IfInsideElse:
Exclude:
- 'lib/claide/command.rb'
# Offense count: 9
# Cop supports --auto-correct.
Style/MutableConstant:
Exclude:
- 'lib/claide/ansi.rb'
- 'lib/claide/argument.rb'
- 'lib/claide/command.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/ParallelAssignment:
Exclude:
- 'lib/claide/command/argument_suggester.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/RedundantInterpolation:
Exclude:
- 'lib/claide/command/argument_suggester.rb'

Some files were not shown because too many files have changed in this diff Show more