• Часто ли вы используете интерфейсы?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Интерфейсы использую, если это необходимо :-) Как я понимаю, что это необходимо, сказать сложно. Все зависит от задачи. Если рассматривать вопрос с позиции создания интерфейсов, то иногда это может быть очевидно, а иногда приходится подумать, стоит использовать интерфейсы или нет. Пихать их где попало - плохая идея.

    Готовые интерфейсы, да, часто используются. Самым популярным в .NET наверное будет IDisposable :-)

    Из общедоступных практических примеров использования собственных интерфейсов:

    • Простой интерфейс, описывающий всего один метод.

      interface ILoginForm
      {
      
        void WebDocumentLoaded(System.Windows.Forms.WebBrowser webBrowser, Uri url);
      
      }


      В проекте 100500 форм. Для некоторых форм может потребоваться индивидуальная обработка результатов, как здесь. А для других - нет. Все формы наследуются от базового класса, в котором я могу проверить, реализует текущий экземпляр интерфейс с индивидуальным обработчиком результатов или нет, и вызвать его.

      if (typeof(ILoginForm).IsAssignableFrom(this.GetType()))
      {
        this.CanLogin = false;
        Debug.WriteLine("ILoginForm", "LoginForm");
        ((ILoginForm)this).WebDocumentLoaded(this.webBrowser1, e.Url);
      }


    • Вот другой пример интерфейса.

      export interface ILocalization {
      
        Loading: string;
      
        LoadingFileContents: string;
      
        // ...
      }

      Файлы с ресурсами локализации реализуют этот интерфейс.

      export class RU implements ILocalization {
      
        public Loading: string = 'Загрузка...';
      
        public LoadingFileContents: string = 'Получение содержимого файла...';
      
        // ...
      }

      Это просто упрощает работу с кодом и больше ничего.

      6d84933efd7246e6adbeeed114ef8bbe.png

      Можно было бы использовать обычные текстовые ключи, но тогда программистам пришлось бы постоянно смотреть, какие есть ключи, делать много лишних движений и вероятность ошибок возрастает.


    • Еще пример интерфейса для вспомогательных классов работы с базами данных. Он просто описывает, каким должен быть класс.

      interface IDBClient
      {
          
        public function ExecuteNonQuery();
        public function ExecuteScalar();
        public function GetRow();
        // ...
      
      }

      А толку от этого никакого.

      Интерфейсы для слоев взаимодействия с БД я делаю частенько, с замахом на замену СУБД, но сделать это красиво в любом случае не получится, все равно придется вносить корректировки при изменении источника данных, так что практической пользы от таких интерфейсов мало, но иногда она есть, только для других целей.

    • А вот тут я реализовал множество готовых интерфейсов, чтобы получить нужные свойства и поведение класса. Например, интерфейс IConvertible позволяет адекватно реагировать на Convert.To*. Интерфейс ISerializable позволяет описать правила сериализации экземпляра класса. И т.п.

      // реализация метода ToInt32
      public int ToInt32(IFormatProvider provider)
      {
        // если пусто, возвращаем ноль
        if (!this.HasValue) { return 0; }
        // если что-то есть, извлекаем числа и пробуем вернуть int32
        return Convert.ToInt32(OAuthUtility.GetNumber(this.Data));
      }


    • Вот пример, где можно было использовать интерфейсы, но я принял решение в пользу базовых классов (на раннем этапе). Некоторые поставщики OAuth позволяют обновлять маркер доступа и/или отзывать его, но не все. В базовом классе нижнего уровня для определениях этих особенностей я сделал два свойства, причем даже не абстрактных.

      public bool SupportRevokeToken { get; protected set; }
      public bool SupportRefreshToken { get; protected set; }

      Клиенты, которым нужны эти свойства, задают их в конструкторе, выглядит это так.

      public GoogleClient(string clientId, string clientSecret) : base(/*...*/)
      {
        // ...
        base.SupportRevokeToken = true;
        base.SupportRefreshToken = true;
      }

      Не очень красиво, но ошибкой это назвать нельзя. Если бы я использовал интерфейсы, то для отзыва и обновления маркера мне бы пришлось делать два разных интерфейса, в каждом по одному bool свойству, либо пустых (но с явно определенным свойством для других программистов все было бы очевидней; это тоже спорный вопрос). Можно было пойти дальше и измельчить на интерфейсы другие особенности, но это было бы плохим решением, т.к. программистам пришлось бы указывать портянку интерфейсов, а потом писать много кода. С базовыми классами код писать не нужно, даже не нужно вникать в то, как это работает; в большинстве случаев достаточно просто выполнить простую конфигурацию дочернего класса в конструкторе. Но я не могу утверждать на 100%, что это лучшее решение для данного случая.

    • Пример, где ReactJS и TypeScript заставляют клепать интерфейсы и я этому совсем не рад.

      export interface ILoginState {
      
        Username?: string;
        Password?: string;
      
        // ...
      
      }
      
      export default class Index extends Page<any, ILoginState> {
      
        constructor() {
      
          this.state = {
            Username: '',
            Password: ''
          };
      
        }
      
        // ...
      
      }

      Можно забить и использовать анонимные типы, но для порядка и удобства работы с кодом придется открывать собственный завод по производству интерфейсов :-)

    Ответ написан
    Комментировать
  • Так sql, ...count(*)... и ...count(id)... в чем отличие?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    SELECT COUNT(*) FROM - перебрать все строки.
    SELECT COUNT(id) FROM - перебрать все строки, в которых указанное поле (в данном случае id) имеет значение отличное от NULL.

    Что использовать - вопрос производительности и потребностей. В случае с primary key, это больше вопрос производительности, которая будет зависеть от конкретной СУБД.
    Ответ написан
    2 комментария
  • Изображение из базы данных в представление?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    При отправке формы, если пользователем не была отправлена другая картинка, вам придется получать предыдущую картинку из базы повторно и использовать при сохранении.

    В принципе, если у вас флажки небольшого размера, их можно и в представление интегрировать, в виде base64 в скрытом (hidden) поле :-) Тогда не придется делать запрос к базе. Но в реальных условиях это мало пригодно, поскольку есть риск подделки данных формы или потери актуальности данных, что нужно проверять, получая редактируемые данные из базы перед внесением в них изменений.
    Ответ написан
    Комментировать
  • Как корректно разделить проект на слои?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Берем проект, смотрим на класс модели, затем смотрим на слои. Берем лист бумаги и шариковую ручку с синими чернилами. Каллиграфическим почерком пишем:

    "Уважаемый В,

    Прошу Вас срочно выслать мне два ящика ядерных боеголовок и банку сгущенки для расщепления моего проекта на слои, ибо я не хочу больше видеть весь этот ужас.

    С надеждой, _подпись_ /_расшифровка_/

    PS: Люблю сгущенку."


    ---

    Если серьезно, то ваш проект - вам виднее :-)

    Можно изначально все проанализировать, взвесить, написать подробный план и потом попытаться сделать. Но для этого нужно обладать исчерпывающей информацией о том, что нужно делать. Это отдельная задача, которая потребует отдельного времени. В идеале времени должно быть не меньше, чем при разработке плана отправки первого космонавта на Марс.

    Либо делать и вносить корректировки в процессе. Этот вариант используется, если не известно, что нужно сделать и как это делать, и если в процессе все может перевернуться с ног на голову, в общем сплошная импровизация. При наличии достаточного количества опыта, в плане временных затрат, этот вариант может быть экономичней.

    Во всех случаях, без хорошей реализации, не имеет значения, был план или все придумалось и эволюционировало в процессе. Космический корабль все равно упадет в океан, не добравшись даже до верхних слоев атмосферы Земли, в лучшем случае безнадежно зависнет где-то там :-) Вариант с постепенным развитием будет лучшим, если смотреть на вопрос с позиции обучения и получения опыта. Стройте как вам удобно для достижения стоящих целей. Вписывать проект в неподходящий шаблон, в некоторых случаях, может быть даже хуже хаоса и неразберихи. Когда сделаете, посмотрите, в какой шаблон более ли менее вписались, сделаете выводы, насколько это хорошо или плохо для конкретной задачи и как это можно сделать лучше/проще/быстрее, как выписаться полностью (если этот вариант окажется приемлемым) и уменьшить количество коллизий. Если в процессе у вас возникают какие-то сомнения, то всегда можно задать узкий вопрос, наверняка вам ответят.

    Если задача коммерческая, то такого вопроса не должно было возникать, по крайней мере иметь воплощения на подобных ресурсах.
    Ответ написан
  • Как загружать CSS assets из NPM используя Webpack?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Для Bootstrap и Font Awesome есть отдельные загрузчики: bootstrap-loader и font-awesome-loader.

    Достаточно просто добавить их в секцию devDependencies в package.json.

    В коде инициализации приложения подключить, например так:

    require('bootstrap-loader');
    require('font-awesome-loader');

    Для шрифтов, в webpack.config.js, прописать примерно такие правила:

    module {
      loaders: [
        {
          test: /\.woff(.*)$/,
          loader: 'url',
          query: {
            limit: 10000,
            mimetype: 'application/font-woff',
            name: 'fonts/[name].[ext]' // путь output, куда будут скопированы файлы
          }
        },
        {
          test: /\.woff2(.*)$/,
          loader: 'url',
          query: {
            limit: 10000,
            mimetype: 'application/font-woff',
            name: 'fonts/[name].[ext]'
          }
        },
        {
          test: /\.ttf(.*)$/,
          loader: 'url',
          query: {
            limit: 10000,
            mimetype: 'application/octet-stream',
            name: 'fonts/[name].[ext]'
          }
        },
        {
          test: /\.eot(.*)$/,
          loader: 'file',
          query: {
            limit: 10000,
            name: 'fonts/[name].[ext]'
          }
        },
        {
          test: /\.svg(.*)$/,
          loader: 'url',
          query: {
            limit: 10000,
            mimetype: 'image/svg+xml',
            name: 'fonts/[name].[ext]'
          }
        }
      ]
    }


    Если понадобится выделить CSS из JavaScript, то можно использовать extract-text-webpack-plugin.

    webpack.config.js:
    var extractTextPlugin = require('extract-text-webpack-plugin');
    // ...
    module.exports = {
      // ...
      module: {
        loaders: [
          {
            test: /\.scss$/, // для scss
            loader: extractTextPlugin.extract('style', 'css!sass')
          }
        ]
      },
    
      plugins: [
        new extractTextPlugin('bundle.css') // вынести css в файл bundle.css в папку output
      ]
    }


    Для копирования других статичных файлов можно использовать copy-webpack-plugin.
    Ответ написан
    Комментировать
  • Как интерпретировать this в данном примере?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    function CoffeeMachine(power) {
      //...
    
      var self = this; // запоминаем ссылку на текущий объект
    
      //...
    
      function getBoilTime() {
        // здесь this будет ссылкой на window, 
        // т.к. вызов метода getBoilTime() происходит из другого контекста
        // доступ к исходному объекту возможен через ссылку, 
        // которая была передана в переменную self
        console.log('this', this);
        console.log('self', self);
        return self.waterAmount * WATER_HEAT_CAPACITY * 80 / power;
      }
    
      // ...
    
      this.run = function() {
        setTimeout(onReady, getBoilTime());
      };
    }


    Привязка контекста и карринг: "bind"

    С передачей контекста код будет примерно таким:

    function CoffeeMachine(power) {
      this.waterAmount = 0;
    
      var WATER_HEAT_CAPACITY = 4200;
    
      function getBoilTime() {
          console.log('getBoilTime', this);
          return this.waterAmount * WATER_HEAT_CAPACITY * 80 / power;
        }
    
      function onReady() {
        alert( 'Кофе готово!' );
      }
    
      this.run = function() {
        console.log('run', this);
        var interval = getBoilTime.apply(this, null);
        setTimeout(onReady, interval);
      };
    
    }
    
    var coffeeMachine = new CoffeeMachine(100000);
    coffeeMachine.waterAmount = 200;
    
    coffeeMachine.run.apply(coffeeMachine, null);
    Ответ написан
    3 комментария
  • Reactjs плагины и requirejs, как?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Не переписывать же плагины, верно? Кто как с этим борется?


    Использую TypeScript и Visual Studio. Итоговый JavaScript сам собирается в нужном виде, достаточно выбрать систему модулей в свойствах проекта.

    Но с некоторыми библиотеками React в AMD возникали проблемы. И да, единственным решением было внесение изменений в код этих компонентов, что не очень хорошо и не факт, что можно будет отделаться парой простых изменений.

    В итоге пришлось использовать Webpack. Опыт работы с RequireJS сильно усложнил миграцию и в какие-то моменты я даже пытался прикрутить RequireJS в Webpack :-) Так что про RequireJS лучше полностью забыть.

    Реализация с Webpack мне нравится больше, но настроек придется написать много и иногда все это дело глючит. С использованием загрузчиков, на выходе можно получить все что угодно из чего угодно.
    Ответ написан
    Комментировать
  • Запрос к Apche минуя Nginx?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    server {
      server_name localhost;
      disable_symlinks if_not_owner;
      listen 80;
      include /etc/nginx/vhosts-includes/*.conf;
    
      location /extplorer/ {
        proxy_set_header X-Real-IP  $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:8080; // указать номер порта, который слушает Apache
      }
    }

    А без Nginx, видимо придется указывать номер порта в URL.
    Например, http://localhost:8080/extplorer.
    Ответ написан
    Комментировать
  • Как получить все каталоги в массив и пройтись по ним аналогом foreach?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Можно использовать find:
    find /etc/* | while read -r path; do 
      if [[ -f "$path" ]]; then 
        printf "Файл: %s\n" "$path"; 
      elif [[ -d "$path" ]]; then
        printf "Мамка: %s\n" "$path"; 
      fi
    done

    /etc/* - путь поиска. В данном случае будут получены все файлы и папки каталога /etc.

    При помощи дополнительных параметров, можно ограничить выборку.
    Например, ограничиться текущим уровнем: -maxdepth 0.
    Найти только каталоги: -type d.
    Или только файлы: -type f.
    find /etc/* -maxdepth 0 | while read -r path; do 
      if [[ -f "$path" ]]; then 
        printf "Файл: %s\n" "$path"; 
      elif [[ -d "$path" ]]; then
        printf "Папка: %s\n" "$path"; 
      fi
    done

    Для получения подробностей см. find --help.

    Если все еще нужен будет массив, то можно сформировать в цикле:
    declare -a files
    
    find /etc/* -maxdepth 0 -type f | while read -r path; do
      files+=("$path")
    done
    Ответ написан
  • Зачем нужна отладка с брейкпоинтами?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Редко работаю с PHP. Механизмы отладки ужасные, зачастую их нужно самих отлаживать :-)

    Использование точек останова (breakpoints) существенно упрощает процесс отладки, поиска ошибок. В C# я иногда пишу код в режиме отладки, останавливая выполнение программы в нужном месте, вношу изменения или даже пишу существенные объемы кода с нуля, перемещаю управление в нужное место и могу тут же посмотреть, что получится, без лишних движений. Не видел подобного в PHP и сомневаюсь, что это возможно. Но даже обычный просмотр состояния переменных и объектов поможет сэкономить время. Без этого придется писать код вывода состояния через var_dump, print_r или echo, засорять вывод, ломать глаза. Придется прыгать между редактором кода и браузером, тратить драгоценные доли секунд за всякую ерунду с риском допустить еще большее количество ошибок.

    Из реального практического примера, только что проводил отладку функции для обработки текстовых данных. Функция рекурсивная, процесс обработки данных относительно сложный.

    Поставил точки останова, посмотрел какое значение на каждом шаге имеет переменная с обработанным текстом. Определил потенциальное место ошибки. Поставил больше точек в этом месте и посмотрел детально. Оказалось, что проблема в работе функции preg_replace. Поскольку с PHP я работаю редко и все забыл, то не смог бы сразу написать правильный шаблон, так что пришлось провести эксперименты непосредственно в точке останова. Сделал остановку в проблемном месте, открыл окно быстрой проверки (у меня Visual Studio) и прям в режиме реального времени опробовал разные варианты использования функции preg_replace с теми данными (переменными), которые находились в памяти. Получив нужный результат, заменил проблемный фрагмент кода. Всё, проблема решена. Быстро и просто, без лишних движений в коде.
    Ответ написан
    6 комментариев
  • Вопрос про IE6 ??

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Странный вопрос для нашего времени. Internet Explorer 6 был выпущен в 2001 году. Когда он помер, уже никто не помнит :-) Веб-сайты с тех времен сильно изменились, и, как следствие, браузеры тоже.

    Если взглянуть на статистику, то можно разглядеть пользователей с IE6, коих меньше процента, и те скорее всего случайные, а может и вовсе роботы.

    Я бы и Internet Explorer 8 не стал поддерживать и вообще на весь этот зоопарк, по возможности лучше забивать. Уже давно пришло время, когда Internet Explorer и всяким Edge нужно самим адаптироваться под сайты, а не наоборот, дабы усилить утраченные во времена IE6 позиции, хотя бы на йоту. Нет смысла зря время на заплатки переводить.

    Раньше, иногда бывало, поддержку IE7/8 заказывали для коммерческих проектов. Обычно решения/исправлений косяков ждать приходилось несколько месяцев, и это с пинками :-) Сейчас нижний предел IE9. Ниже просто смысла нет, придется либо жертвовать функционалом, либо делать двойную, а то и тройную работу. Либо использовать древние технологии, которые через пару лет станут доисторическими и придется обновляться, видимо опять, до древних :-)

    Новые авторские проекты под IE я уже давно даже не проверяю.

    Если основной долей среди посетителей сайта будут корпоративные пользователи, у которых кроме IE ничего нет, то конечно, придется это учитывать. Крупные компании могут себе позволить свежее ПО. Если пользователи до сих пор сидят под Windows XP, то они вполне могут использовать Internet Explorer 8, но никак не шестую версию.
    Ответ написан
    Комментировать
  • Почему не парсятся директивы в ng-bind-html?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    angular-bind-html-compile
    (function (angular) {
        'use strict';
    
        var module = angular.module('angular-bind-html-compile', []);
    
        module.directive('bindHtmlCompile', ['$compile', function ($compile) {
            return {
                restrict: 'A',
                link: function (scope, element, attrs) {
                    scope.$watch(function () {
                        return scope.$eval(attrs.bindHtmlCompile);
                    }, function (value) {
                        // In case value is a TrustedValueHolderType, sometimes it
                        // needs to be explicitly called into a string in order to
                        // get the HTML string.
                        element.html(value && value.toString());
                        // If scope is provided use it, otherwise use parent scope
                        var compileScope = scope;
                        if (attrs.bindHtmlScope) {
                            compileScope = scope.$eval(attrs.bindHtmlScope);
                        }
                        $compile(element.contents())(compileScope);
                    });
                }
            };
        }]);
    }(window.angular));

    Использование:
    var app = angular.module('myApp',['ngMaterial', 'ngSanitize', 'angular-bind-html-compile']);
    app.controller("My", ['$scope', '$http', function ($scope, $http) {
      $scope.banner = '<md-progress-circular md-diameter="96"></md-progress-circular>'
    }]);

    bind-html-compile вместо ng-bind-html.

    Посмотреть
    Ответ написан
    Комментировать
  • Как убрать символ переноса строки?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Пропустить через trim.

    $command_script = "create_first_file.sh $this->server_user_name " .
                      trim($id_app) . " index$type_ex_file";


    Откуда берется лишний перевод строки, сказать сложно. Нужно в живую смотреть :-) Не припомню подобного поведения с формами в PHP. Может проблема во входящих данных ($data->id). Кстати, точка с запятой там лишняя, достаточно: <?=$data->id?>.

    Или же данные меняются где-то между отправкой и передачей в $id_app.

    Точки останова можно расставить и посмотреть, что происходит.
    Ответ написан
    2 комментария
  • Как правильно создать кнопку "поделиться" в facebook?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Адрес нужно указывать. В вашем примере, вместо адреса передается текст "URL".

    Можно сделать передачу параметров через объект, для гибкости:
    Share = {
      getParams: function(params) {
        // используем полученные параметры, 
        // либо создаем пустой объект, чтобы не было ошибок
        params = params || {};
        // в качестве url используем params.url,
        // либо адрес текущей страницы (window.location.href), если params.url не указан
        params.url = params.url || window.location.href;
        // используем params.title, либо заголовок документа
        params.title = params.title || document.title;
        // и т.п.
        params.description = params.description || '';
        params.img = params.img || '';
    
        return params;
      },
    
      vkontakte: function(params) {
        params = Share.getParams(params);
        url = 'http://vkontakte.ru/share.php?';
        url += 'url=' + encodeURIComponent(params.url);
        url += '&title=' + encodeURIComponent(params.title);
        url += '&description=' + encodeURIComponent(params.description);
        url += '&image=' + encodeURIComponent(params.img);
        url += '&noparse=true';
        Share.popup(url);
      },
    
      facebook: function(params) {
      	params = Share.getParams(params);
        url = 'http://www.facebook.com/sharer.php?s=100';
        url += '&p[title]=' + encodeURIComponent(params.title);
        url += '&p[summary]=' + encodeURIComponent(params.description);
        url += '&p[url]=' + encodeURIComponent(params.url);
        url += '&p[images][0]=' + encodeURIComponent(params.img);
        Share.popup(url);
      },
    
      twitter: function(params) {
        params = Share.getParams(params);
        url = 'http://twitter.com/share?';
        url += 'text=' + encodeURIComponent(params.description);
        url += '&url=' + encodeURIComponent(params.img);
        url += '&counturl=' + encodeURIComponent(params.img);
        Share.popup(url);
      },
    
      popup: function(url) {
        window.open(url, '', 'toolbar=0,status=0,width=626,height=436');
      }
    };

    <span title="Поделиться в Facebook">
      <a onclick="Share.facebook({url: 'https://toster.ru/q/294480'})">
        <i class="facebook square icon">Facebook</i>
      </a>
    </span>
    <span title="Поделиться в Twitter">
      <a onclick="Share.twitter({description: 'Hello world!'})">
        <i class="twitter square icon">Twitter</i>
      </a>
    </span>
    <span title="Поделиться ВКонтакте">
      <a onclick="Share.vkontakte({url: 'https://toster.ru/q/294480', description: 'Ответ на вопрос', 
    title: 'Как правильно создать кнопку «поделиться» в facebook?'})">
      <i class="vk icon">VK</i>
      </a>
    </span>

    Посмотреть.

    Если память не изменяет, Facebook с недавних пор (или уже давних) игнорирует передаваемые описания и заголовки страниц и извлекает необходимую информацию самостоятельно, непосредственно с указанной ссылки (преимущественно из тегов Open Graph).
    Ответ написан
    1 комментарий
  • Как авторизоваться с помощью одноклассников?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Ключ доступа (access_token) будет передан на страницу возврата, после авторизации пользователя.

    Этот ключ необходимо выдернуть из адресной строки браузера и использовать для работы с API.

    Вот на этой странице описан весь процесс.

    1. Необходимо сформировать адрес для авторизации на основе шаблона (в одну строку, я разбил на несколько, чтобы влезло на один экран):
    https://connect.ok.ru/oauth/authorize?client_id={clientId}&scope={scope}&
    response_type=token&redirect_uri={redirectUri}&
    layout={layout}&state={state}

    response_type=token, чтобы сервер Одноклассники вернул access_token в дополнительных параметрах (hash) страницы возврата.

    Вместо {clientId} необходимо указать идентификатор своего приложения.

    {scope} - права доступа, можно не указывать. Как минимум: VALUABLE_ACCESS;PHOTO_CONTENT, чтобы иметь возможность получить данные профиля пользователя.

    {redirectUri} - страница возврата, на которой должен быть код (в данном случае код JavaScript), который извлечет из адреса полученный ключ доступа, ну или обработает ситуацию, если в процессе авторизации произойдет какая-нибудь ошибка или пользователь откажется давать права приложению.

    {layout} - тип окна, скорее всего подойдет m - модальное окно, или a - упрощенное.

    {state} - можно указать любое свое значение, которое будет передано в исходном виде на страницу возврата. Обычно используется для передачи собственного идентификатора сессии авторизации, либо идентификатора локального пользователя, чтобы можно было понять, какой именно собственный (локальный) пользователь проходит авторизацию. Этот параметр использовать не обязательно.

    В итоге адрес страницы авторизации может быть примерно таким (в одну строку):
    https://connect.ok.ru/oauth/authorize?client_id=000000000&
    scope=VALUABLE_ACCESS;PHOTO_CONTENT&response_type=token&
    redirect_uri=https://localhost/auth_result.html&layout=m

    На локальном сервере можно создать страницу https://localhost/auth_result.html с кодом обработки результатов (см. п2).

    2. Сделать страницу возврата (redirect_uri) и написать код извлечения ключа доступа и обработку ошибок.

    При клиентской авторизации (response_type=token), сервер Одноклассники вернет параметры ответа в дополнительных параметрах документа (hash) в URL, которые можно найти в свойстве window.location.hash:
    <script>
      alert(window.location.hash);
      // берем hash из url и разбиваем на массив по символу &
      var params = window.location.hash.substr(1).split('&');
      // перебираем массив
      for (var i = 0; i < params.length; i++)
      {
        // разбиваем текущий элемент массив на новый массив по знаку =
        var p = params[i].split('=');
        // на выходе будет два элемента ключ-значение
        // проверяем имя ключа
        if (p[0] == 'access_token')
        {
          // показываем значение
          alert('Нашли ключ доступа: ' + p[1]); 
        }
      }
    </script>
    Ответ написан
  • Не могу вернуть значение C#. В чем ошибка?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Параметр в метод не передан:
    int h = obj.Met(--> ???? <--);
    Вызываем метод Met, в первом параметре указываем значение 123, результат передаем в переменную h:
    int h = obj.Met(123);
    Console.WriteLine("Получен результат: {0}", h);
    Ответ написан
    1 комментарий
  • Как заменить только первое вхождение в строке?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    -- строка, в которой нужно провести поиск
    SET @parent = '14 11 10 11 52';
    -- строка, которую нужно найти
    SET @search = '11';
    
    -- найти начальную позицию нужной строки
    SELECT INSTR(@parent, @search);
    
    -- зная расположение и размер подстроки, можно вырезать фрагмент текста
    -- первая часть
    SELECT SUBSTR(@parent, 1, INSTR(@parent, @search) - 1);
    -- хвост
    SELECT SUBSTR(@parent, INSTR(@parent, @search) + LENGTH(@search));
    
    -- все вместе
    SELECT CONCAT
    (
      SUBSTR(@parent, 1, INSTR(@parent, @search) - 1), 
      SUBSTR(@parent, INSTR(@parent, @search) + LENGTH(@search))
    );


    Грубый вариант с UPDATE будет примерно таким (лучше процедуру сделать и/или передавать строку поиска через параметр):

    UPDATE example SET parent = CONCAT
    (
      SUBSTR(parent, 1, INSTR(parent, '11') - 1), 
      SUBSTR(parent, INSTR(parent, '11') + LENGTH('11'))
    );
    Ответ написан
    Комментировать
  • Как в RichTextBox игнорировать/блокировать режим "замена" через insert?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Добавить к экземпляру richTextBox обработчик события KeyDown и отменить нажатие Insert:
    private void richTextBox1_KeyDown(object sender, KeyEventArgs e)
    {
      if (e.KeyCode == Keys.Insert)
      {
        e.Handled = true;
        return;
      }
    }
    Ответ написан
  • С# уменьшение кода?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    label.Content = (Convert.ToInt32(textBox.Text) + Convert.ToInt32(textBox1.Text)).ToString();

    Или функцию написать и использовать её:
    private static string Sum(string a, string b)
    {
      return (Convert.ToInt32(a) + Convert.ToInt32(b)).ToString();
    }

    Или даже так:
    label.Content = Sum(textBox.Text, textBox1.Text);
    label.Content = Sum(textBox.Text, textBox1.Text, textBox2.Text, textBox4.Text);
    
    private static string Sum(params string[] n)
    {
      return n.Sum(itm => Convert.ToInt32(itm)).ToString();
    }


    Еще можно расширение написать, но это только на случай, если сложение чисел в TextBox являются частыми в проекте :-)
    public static class TextBoxExtension
    {
    
      public static string SumWith(this TextBox value, params TextBox[] n)
      {
        return (Convert.ToInt32(value.Text) + n.Sum(itm => Convert.ToInt32(itm.Text))).ToString();
      }
    
    }

    label.Content = textBox.SumWith(textBox1, textBox2, textBox3);

    Либо расширить string:
    public static class StringExtension
    {
    
      public static string SumWith(this string value, params string [] n)
      {
        return (Convert.ToInt32(value) + n.Sum(itm => Convert.ToInt32(itm))).ToString();
      }
    
    }

    label.Content = textBox.Text.SumWith(textBox1.Text);
    Ответ написан
    Комментировать
  • Пауза в цикле Foreach?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    for($i = 1; $i < 100; $i++)
    {
      echo "элемент_$i<br />";
    
      // делаем паузу, если текущий индекс делится на 10 без остатка
      // т.е. каждый 10 элемент будет пауза
      if (($i % 10) == 0)
      {
        echo "пауза 3 секунды :)";
        sleep(3);
      }
    
    }

    Для foreach придется счетчик делать (типа $i). Но если такая необходимость возникнет, то по возможности лучше использовать обычный цикл.

    UPD: В соответствии с обновлением текста вопроса:
    for($i = 0; $i < 100; $i++)
    {
      // каждые два элемента помещаем в div
      if (($i % 2) == 0)
      {
        if ($i != 0) 
        {
          // закрываем предыдущий блок, если это не первый блок
          echo "</div>";
        }
        // открываем блок
        echo "<div class='exmpl'>";
      }
      // выводим элемент
      echo "<span> элемент_массива_$i</span><br />";
    }
    // закрываем последний блок
    echo "</div>";

    Возможно, через переменную формировать вывод будет проще:
    $output = "";
    for($i = 0; $i < 100; $i++)
    {
      // каждые два элемента помещаем в div
      if ($i != 0 && ($i % 2) == 0)
      {
        // выводим
        echo "<div class='exmpl'>$output</div>";
        // обнуляем
        $output = "";
      }
      // добавляем элемент в очередь на вывод
      $output .= "<span> элемент_массива_$i</span><br />";
    }
    // выводим остатки
    echo "<div class='exmpl'>$output</div>";
    Ответ написан
    4 комментария