diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 27bcee3fb..8cf8ccf63 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,3 +1,8 @@ +# See https://github.com/release-drafter/release-drafter#configuration +categories: + - title: 'Enhancements' + labels: + - enhancement template: | ## What’s Changed diff --git a/.github/workflows/bundler.yml b/.github/workflows/bundler.yml index cf11cde12..72486fbf0 100644 --- a/.github/workflows/bundler.yml +++ b/.github/workflows/bundler.yml @@ -1,24 +1,23 @@ name: Bundler -on: - create: - branches: - - 'release/*' +on: create jobs: autocommit: + name: Update to stable dependencies + if: startsWith(github.ref, 'refs/heads/release/') runs-on: ubuntu-latest container: image: atk4/image:latest # https://github.com/atk4/image steps: - uses: actions/checkout@master - - run: | - sed -i -e '/atk4\/schema/s/dev-develop/\*/' composer.json - sed -i -e '/atk4\/dsql/d' composer.json - sed -i -e '/atk4\/core/d' composer.json - cat composer.json - composer require atk4/core atk4/dsql - composer update + - run: echo ${{ github.ref }} + - name: Update to stable dependencies + run: | + jq 'del(.require["atk4/core"]) | del(.require["atk4/dsql"]) | del(.["require-dev"]["atk4/schema"])' < composer.json > tmp && mv tmp composer.json + composer require --no-progress --no-suggest --prefer-dist --optimize-autoloader atk4/core atk4/dsql + composer require --no-progress --no-suggest --prefer-dist --optimize-autoloader --dev atk4/schema + composer update --no-progress --no-suggest --prefer-dist --optimize-autoloader - uses: teaminkling/autocommit@master with: @@ -27,3 +26,21 @@ jobs: with: branch: ${{ github.ref }} github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: pull-request + uses: repo-sync/pull-request@v2 + with: + source_branch: "" # If blank, default: triggered branch + destination_branch: "master" # If blank, default: master + pr_title: "Releasing ${{ github.ref }} into master" + pr_body: | + - [ ] Review changes (must include stable dependencies) + - [ ] Merge this PR into master (will delete ${{ github.ref }}) + - [ ] Go to Releases and create TAG from master + + ---------- + + Do not merge master into develop + pr_reviewer: "romaninsh" + pr_assignee: "romaninsh" + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 8e2f42968..e725bcc65 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -2,18 +2,21 @@ name: Unit Testing on: pull_request: + branches: '*' + push: branches: - - 'feature/*' + - master + - develop jobs: unit-test: name: Unit Testing runs-on: ubuntu-latest container: - image: atk4/image:latest # https://github.com/atk4/image + image: atk4/image:${{ matrix.php }} # https://github.com/atk4/image strategy: matrix: - php: ['7.3'] + php: ['7.2', '7.3', 'latest'] services: mysql: image: mysql:5.7 @@ -29,6 +32,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v1 + - run: php --version - name: Get Composer Cache Directory id: composer-cache run: | @@ -62,62 +66,3 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} file: build/logs/cc.xml - - -# -# build: -# runs-on: ubuntu-linux -# container: -# image: php:${{ matrix.php }}-alpine -# strategy: -# #max-parallel: 6 -# matrix: -# #operating-system: [ubuntu-latest] -# #operating-system: [ubuntu-latest, windows-latest, macOS-latest] -# php: ['7.3'] -# -# env: -# DB_DATABASE: db -# DB_USERNAME: root -# DB_PASSWORD: password -# -# services: -# mysql: -# image: mysql:5.7 -# env: -# MYSQL_ALLOW_EMPTY_PASSWORD: false -# MYSQL_ROOT_PASSWORD: password -# MYSQL_DATABASE: db -# options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 -# postgres: -# image: postgres:10-alpine -# env: -# POSTGRES_PASSWORD: password -# POSTGRES_USER: root -# options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 -# -# -# steps: -# - uses: actions/checkout@v1 -# - run: | -# apk add \ -# unzip -# echo 'memory_limit=-1' >> /usr/local/etc/php/php.ini -# wget -qO - https://raw.githubusercontent.com/composer/getcomposer.org/fb22b78362d31c0d2bf516d1f8cdfd2745caa431/web/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet -# -# - name: Install dependencies -# run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader -# -# - name: Test with phpunit and SQLite -# run: vendor/bin/phpunit --configuration phpunit.xml --coverage-text -# -# - name: Test with phpunit and MySQL -# run: vendor/bin/phpunit --configuration phpunit-mysql-workflow.xml --coverage-text -# -# - name: Merge logs -# run: vendor/bin/phpcov merge build/logs/ --clover build/logs/cc.xml; -# -# - name: Upload coverage to CodeCov -# uses: codecov/codecov-action@v1 -# with: -# token: ${{ secrets.CODECOV_TOKEN }} #required diff --git a/.travis.yml b/.old.travis.yml similarity index 100% rename from .travis.yml rename to .old.travis.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index e0449de45..a00c61882 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,7 @@ # 2.0 -First major version update of ATK Data now includes support for "User Actions". - - - TBC +see https://github.com/atk4/data/releases for further releases. # 1.4 diff --git a/composer.json b/composer.json index 69643a6d0..5b0df8cb7 100644 --- a/composer.json +++ b/composer.json @@ -1,37 +1,53 @@ { - "name": "atk4/data", - "type": "library", - "description": "Agile Data - Database access abstraction framework", - "keywords": ["framework", "orm", "query", "active record", "sql", "builder", "nosql", "mongodb", "mysql", "postgresql"], - "homepage": "https://github.com/atk4/data", - "license": "MIT", - "authors": [ - { - "name": "Romans Malinovskis", - "email": "romans@agiletoolkit.org", - "homepage": "https://nearly.guru/" - } - ], - "require": { - "php": ">=7.2.0", - "ext-intl": "*", - "atk4/core": "^1.3", - "atk4/dsql": "^1.2" - }, - "require-dev": { - "atk4/schema": "*", - "phpunit/phpunit": "<6", - "phpunit/dbunit": ">=1.2", - "phpunit/phpcov": "*", - "codeclimate/php-test-reporter": "*" - }, - "autoload": { - "psr-4": {"atk4\\data\\":"src/"} - }, - "autoload-dev": { - "psr-4": { - "atk4\\data\\tests\\":"tests/", - "atk4\\data\\tests\\smbo\\":["tests/smbo","tests/smbo/lib"] - } + "name": "atk4/data", + "type": "library", + "description": "Agile Data - Database access abstraction framework", + "keywords": [ + "framework", + "orm", + "query", + "active record", + "sql", + "builder", + "nosql", + "mongodb", + "mysql", + "postgresql" + ], + "homepage": "https://github.com/atk4/data", + "license": "MIT", + "authors": [ + { + "name": "Romans Malinovskis", + "email": "romans@agiletoolkit.org", + "homepage": "https://nearly.guru/" } + ], + "require": { + "php": ">=7.2.0", + "ext-intl": "*", + "atk4/core": "^2.0", + "atk4/dsql": "^2.0" + }, + "require-dev": { + "phpunit/phpunit": "<6", + "phpunit/dbunit": ">=1.2", + "phpunit/phpcov": "*", + "codeclimate/php-test-reporter": "*", + "atk4/schema": "^2.0" + }, + "autoload": { + "psr-4": { + "atk4\\data\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "atk4\\data\\tests\\": "tests/", + "atk4\\data\\tests\\smbo\\": [ + "tests/smbo", + "tests/smbo/lib" + ] + } + } } diff --git a/locale/en/atk.php b/locale/en/atk.php index 205255973..840c3256b 100644 --- a/locale/en/atk.php +++ b/locale/en/atk.php @@ -17,4 +17,5 @@ 'Unable to serialize field value on save' => 'Unable to serialize field value on save ({{field}})', 'Unable to typecast field value on load' => 'Unable to typecast field value on load ({{field}})', 'Unable to typecast field value on save' => 'Unable to typecast field value on save ({{field}})', + 'Record with specified ID was not found' => 'Record with specified ID was not found', ]; diff --git a/locale/ru/atk.php b/locale/ru/atk.php index 36ca0082c..d2387db9d 100644 --- a/locale/ru/atk.php +++ b/locale/ru/atk.php @@ -15,4 +15,5 @@ 'Unable to serialize field value on save' => 'Невозможно сериализовать значение поля при сохранении ({{field}})', 'Unable to typecast field value on load' => 'Невозможно установить тип поля при загрузке ({{field}})', 'Unable to typecast field value on save' => 'Невозможно сериализовать значение поля при сохранении ({{field}})', + 'Record with specified ID was not found' => 'Запись с данным ID не найдена', ]; diff --git a/src/Field.php b/src/Field.php index ed2be4ca0..f5fd13bd7 100644 --- a/src/Field.php +++ b/src/Field.php @@ -5,6 +5,7 @@ namespace atk4\data; use atk4\core\DIContainerTrait; +use atk4\core\ReadableCaptionTrait; use atk4\core\TrackableTrait; use atk4\dsql\Expression; use atk4\dsql\Expressionable; @@ -16,6 +17,7 @@ class Field implements Expressionable { use TrackableTrait; use DIContainerTrait; + use ReadableCaptionTrait; // {{{ Properties @@ -516,8 +518,7 @@ public function isHidden() */ public function getCaption() { - return $this->caption ?: (isset($this->ui['caption']) ? $this->ui['caption'] : - ucwords(str_replace('_', ' ', $this->short_name))); + return $this->caption ?? $this->ui['caption'] ?? $this->readableCaption($this->short_name); } // }}} diff --git a/src/Model.php b/src/Model.php index 1de6accb4..14e9e20d3 100644 --- a/src/Model.php +++ b/src/Model.php @@ -14,6 +14,7 @@ use atk4\core\HookTrait; use atk4\core\InitializerTrait; use atk4\core\NameTrait; +use atk4\core\ReadableCaptionTrait; use atk4\data\UserAction\Generic; use atk4\dsql\Query; use IteratorAggregate; @@ -36,6 +37,7 @@ class Model implements ArrayAccess, IteratorAggregate use FactoryTrait; use AppScopeTrait; use CollectionTrait; + use ReadableCaptionTrait; // {{{ Properties of the class @@ -898,23 +900,13 @@ public function get($field = null) /** * Return (possibly localized) $model->caption. + * If caption is not set, then generate it from model class name. * * @return string */ public function getModelCaption() { - if ($this->caption) { - return $this->caption; - } - - // if caption is not set, then generate it from model class name - $s = strtolower(get_class($this)); - //$s = str_replace('model', '', $s); - $s = preg_split('/[\\\\_]/', $s, -1, PREG_SPLIT_NO_EMPTY); - $s = array_map('trim', $s); - $s = ucwords(implode(' ', $s)); - - return $s; + return $this->caption ?: $this->readableCaption(get_class($this)); } /** @@ -1040,13 +1032,7 @@ public function addAction($name, $defaults = []) : UserAction\Generic } if (!isset($defaults['caption'])) { - $s = $name; - - $s = preg_split('/[\\\\_]/', $s, -1, PREG_SPLIT_NO_EMPTY); - $s = array_map('trim', $s); - $s = ucwords(implode(' ', $s)); - - $defaults['caption'] = $s; + $defaults['caption'] = $this->readableCaption($name); } /** @var UserAction\Generic $action */ diff --git a/src/UserAction/Generic.php b/src/UserAction/Generic.php index 382420ff6..7c95c0fd2 100644 --- a/src/UserAction/Generic.php +++ b/src/UserAction/Generic.php @@ -113,7 +113,7 @@ public function execute(...$args) } } elseif ($this->fields !== false) { throw new Exception([ - 'Arguments fields for the action must be either array or `false`.', + 'Argument `fields` for the action must be either array or `false`.', 'fields'=> $this->fields, ]); } diff --git a/tests/DeepCopyTest.php b/tests/DeepCopyTest.php index ce2163a9c..87a4c788e 100644 --- a/tests/DeepCopyTest.php +++ b/tests/DeepCopyTest.php @@ -88,7 +88,7 @@ public function init() $this->addField('vat', ['type'=>'numeric', 'default'=>0.21]); // total is calculated with VAT - $this->addExpression('total', '[qty]*[price]*(1+vat)'); + $this->addExpression('total', '[qty]*[price]*(1+[vat])'); } } diff --git a/tests/FieldTest.php b/tests/FieldTest.php index 9bb84a53f..5f5862aea 100644 --- a/tests/FieldTest.php +++ b/tests/FieldTest.php @@ -177,6 +177,15 @@ public function testCaption() $f = $m->addField('foo3', ['ui'=>['caption'=>'My Foo']]); $this->assertEquals('My Foo', $f->getCaption()); + + $f = $m->addField('userDefinedEntity'); + $this->assertEquals('User Defined Entity', $f->getCaption()); + + $f = $m->addField('newNASA_module'); + $this->assertEquals('New NASA Module', $f->getCaption()); + + $f = $m->addField('this\\ _isNASA_MyBigBull shit_123\Foo'); + $this->assertEquals('This Is NASA My Big Bull Shit 123 Foo', $f->getCaption()); } /** diff --git a/tests/LocaleTest.php b/tests/LocaleTest.php index 123bf16da..d62570296 100644 --- a/tests/LocaleTest.php +++ b/tests/LocaleTest.php @@ -2,8 +2,11 @@ namespace atk4\data\tests; +use atk4\core\Translator\Translator; use atk4\data\Exception; use atk4\data\Locale; +use atk4\data\Model; +use atk4\data\Persistence; class LocaleTest extends \PHPUnit_Framework_TestCase { @@ -18,4 +21,31 @@ public function testGetPath() $path = implode(DIRECTORY_SEPARATOR, [dirname(__DIR__), 'src', '..', 'locale']).DIRECTORY_SEPARATOR; $this->assertEquals($path, Locale::getPath()); } + + public function testLocaleIntegration() + { + $a = [ + 'user' => [ + 1 => ['name' => 'John', 'surname' => 'Smith'], + 2 => ['name' => 'Sarah', 'surname' => 'Jones'], + ], + ]; + + $trans = Translator::instance(); + $trans->setDefaultLocale('ru'); + + try { + $p = new Persistence\Array_($a); + $m = new Model($p, 'user'); + $m->addField('name'); + $m->addField('surname'); + $m->load(4); + } catch (Exception $e) { + $this->assertContains('Запись', json_decode($e->getJSON(), true)['message']); + + return; + } + + $this->fail('Expected exception'); + } } diff --git a/tests/RandomTest.php b/tests/RandomTest.php index 824e37ca5..bf6dabc39 100644 --- a/tests/RandomTest.php +++ b/tests/RandomTest.php @@ -347,13 +347,13 @@ public function testNonSQLFieldClass() $this->assertEquals('y2', $m['x2']); } - public function testCaption() + public function testModelCaption() { $db = new Persistence\SQL($this->db->connection); $m = new Model($db, 'user'); // caption is not set, so generate it from class name \atk4\data\Model - $this->assertEquals('Atk4 Data Model', $m->getModelCaption()); + $this->assertEquals('Atk 4 Data Model', $m->getModelCaption()); // caption is set $m->caption = 'test';