Wrong declaration of new Template. Backtrace:
/var/www/barzha.top/controller/Controller.php:1154 Template->__construct(view/modern, )<br>/var/www/barzha.top/controller/Controller_User.php:356 Controller->getTariffs(Template Object
(
    [srctemplate] => <div class="user_channel_block">
    <div class="container">
        <div class="row">
            <div class="col-sm-2"></div>
            <div class="col-sm-10">
                <div class="chs_info">
 <div class="chs_line">
  <div class="row">
   <div class="col-sm-6">
    <div class="h1">Информация о канале</div>
   </div>
   <!--
    <div class="col-sm-6 right">
     <button type="button" class="admpen btn btn-danger"><span class="glyphicon glyphicon-pencil"></span>Написать администратору</button>    
    </div>
    -->
  </div>
 </div>
 <div class="chs_line">
  <div class="col-sm-2">
   <img class="ch_img img-circle" src="%PhotoBig%" alt="%Title%">
  </div>
  <div class="col-sm-10">
   <div class="ch_name">
    <a href="">%Title%</a> 
    <block_link><a class="link" href="http://cflink.ru/%Link%" target="_blank">@%Link%</a></block_link>
   </div>
   <div class="row">
    <div class="col-sm-7">
     <div style="display:none;" class="ch_cat">
      <a href="">%Type%</a>
     </div>
     <div class=clrln></div>
     <block_cat>
      <div class="cat_cat">
       Категория: <b><a href="/channel/%Link%">%Cat%</a></b>
      </div>
     </block_cat>
     <div class="ch_col">
      Подписчиков: <b>%Members%</b>
     </div>
     <div class="ch_col">
      Стоимость рекламы: <b>%Cost%</b> руб.
     </div>
    </div>
    <div class="col-sm-5 opiskanala">
     <block_description>
      <b>Описание канала:</b>
      %Description%
     </block_description>
    </div>
   </div>
  </div>
  <!--
   <div class="col-sm-3 right">
    <a class="zametka" href=""><span class="glyphicon glyphicon-bookmark"></span> Оставить заметку</a>
   </div>
   -->
  <div class="clear"></div>
 </div>
 <block_sysmsgs>
  <div class="chs_line">
   %SystemMessages%
  </div>
 </block_sysmsgs>
 
 
 
 
 
 
 
 
 
 
 <div class="hidden">
  <div class="chs_line">
   <div class="variants_rek">
    <div class="line_chan">
     Реклама на Telegram канале <a class="link" href="http://cflink.ru/%Link%" target="_blank">@%Link%</a>
    </div>
    <div class="variant_list">
     <div class="row">
      <div class="col-sm-2">
       <span>Размещение</span>
      </div>
      <div class="col-sm-1">
       <span><b>%Cost%</b> Р</span>
      </div>
      <div class="col-sm-2">
       <span>1 час / 2 дня</span>
      </div>
      <div class="col-sm-3">
       <span>Просмотров: <b>1400+</b></span>
      </div>
      <div class="col-sm-2">
       <span>CPM: <b>595.83 Р</b></span>
      </div>
      <div class="col-sm-2"><button class="btn btn-success">Выбрать</button></div>
     </div>
     <div class="row">
      <div class="col-sm-2">
       <span>Размещение</span>
      </div>
      <div class="col-sm-1">
       <span><b>%Cost%</b> Р</span>
      </div>
      <div class="col-sm-2">
       <span>5 час / 1 дня</span>
      </div>
      <div class="col-sm-3">
       <span>Просмотров: <b>2400+</b></span>
      </div>
      <div class="col-sm-2">
       <span>CPM: <b>595.83 Р</b></span>
      </div>
      <div class="col-sm-2"><button class="btn btn-success">Выбрать</button></div>
     </div>
    </div>
   </div>
  </div>
  <div class="chs_line">
   <div class="alert alert-warning">
    После того как юзер выбрал вариант размещения ему показываем что может составить пост и отправить рекламу на модерацию. <br><br>
    кстати после выбора варианта - мы его подсвечиваем, либо фоном либо цвет кнопки, хз подумаем <br><br>
    Когда выбираем дату можем кликнуть на сам календарь и выбрать дату и время, а можем выбрать из предложенных вариантов администратора (это он указывает у себя в настройках канала) <br><br>
    Также я бы вывел блок с уже отправленными этому каналу заявками со статусами заявок
   </div>
  </div>
  <div class="chs_line">
   <div class="publ-box">
    <div class="row">
     <div class="col-sm-3">
      <b>Статус предложения</b><br>
      <span>Составление</span>
     </div>
     <div class="col-sm-3">
      <b>Дата публикации</b><br>
      <span><a data-toggle="modal" data-target="#addDate">Январь 9, 10:35</a></span>
     </div>
     <div class="col-sm-3">
      <b>Ответ не позже</b><br>
      <span><a data-toggle="modal" data-target="#addAnswer">Январь 10, 10:35</a></span>
     </div>
    </div>
   </div>
  </div>
 </div>
 <block_requestallowed>
  <block_notenoughmoney>
   <div class="chs_line">
    <div class="alert alert-danger">
     <ru>У вас недостаточно средств, чтобы оставить заявку на рекламу в этом канале! Для продолжения следует <a href="/pay">пополнить баланс</a>.</ru>
     <en>You don't have enough money to send request to this channel! You should <a href="/pay">add money</a> to continue.</en>
    </div>
   </div>
  </block_notenoughmoney>
  <div class="chs_line">
   <div class="ch_ads">
    <div class="add_ads">
     <div class="col-sm-4">
      <!--<img src="%imgfolder%/calendar.jpg" alt="">-->
      <div style="overflow:hidden;">
       <div class="form-group">
        <div class="row">
         <div class="col-sm-12">
          <div id="datetimepicker12"></div>
         </div>
        </div>
       </div>
       <script type="text/javascript">
        $(
         function () {
          $('#datetimepicker12').datetimepicker(
           {
            inline: true,
            format: 'DD.MM.YYYY HH:mm:ss'
           }
          );
         }
        );
       </script>
      </div>
      <block_comment>
       <div class="adm_say">
        <div class="alert alert-warning">
         %Comment%
        </div>
       </div>
      </block_comment>
     </div>
     <div class="col-sm-8">
      <div class="alert alert-info">
       Выберите дату слева, составьте рекламный пост и отправьте на утверждение.
      </div>
      <div class="ch_post">
       <ul class="nav nav-tabs">
        <li class="active"><a data-toggle="tab" href="#post">Пост</a></li>
        <!--<li><a data-toggle="tab" href="#repost">Перепост</a></li>-->
       </ul>
       <div class="tab-content">
        <div id="post" class="tab-pane fade in active">
         <div class="post_line">
          <b>Какой канал рекламируете?</b>
          <select id="ChannelID">
           <option value="0">
            <ru>Никакой</ru>
            <en>No one</en>
           </option>
           <loop_channels>
            <option id="%ID%">%Title% (@%Link%)</option>
           </loop_channels>
           <block_nochannels>
           </block_nochannels>
          </select>
          <br><br>
          <b>Напишите ваш рекламный материал</b>
          <textarea placeholder="Отправьте администратору канала заранее отформатированный текст, согласно правил форматирования которые представлены ниже. " id="Text"></textarea>
          <br><br>
          <div class="table_format">
           <table class="table table-striped">
            <thead>
             <tr>
              <th>Правило</th>
              <th>Результат</th>
             </tr>
            </thead>
            <tbody>
             <tr>
              <td>[Ссылка] (https://barzha.top)</td>
              <td><a href="javascript:;" rel="noopener" onclick="return false;">Ссылка</a></td>
             </tr>
             <tr>
              <td>*Жирный*</td>
              <td><b>Жирный</b></td>
             </tr>
             <tr>
              <td>_Курсив_</td>
              <td><em>Курсив</em></td>
             </tr>
             <tr>
              <td>`Выделение кода`</td>
              <td><code>Выделение кода</code></td>
             </tr>
            </tbody>
           </table>
          </div>
          <span class="btn-file">
           Выберите файл:<br>
           <form id='uploadform_%id%' name='uploadform' target='uploadbuffer' enctype='multipart/form-data' method='post' action='index.php'>
            <p>
             <input type='hidden' name='action'     id='action' value='%id%'>
             <input type='hidden' name='id'         id='id'     value=''>
             <input type='file'   name='fileupload' id='fileupload' onchange='doupload_v2("%id%");' accept_='application/vnd.ms-excel'>
             <!-- <button onclick='doupload_v2("%id%");'>Reload</button> -->
            </p>
           </form>
           <p>
           <div id='status_%id%'>
            <block_file>
             <a href="%rootdir%/%Image%" target="_blank">
             <img class="img" src="%rootdir%/%Image_pf%">
             </a>
             <a href="javascript:;" onclick="deleteFile('%Image%', '%id%');">
              <span class="fa fa-times"></span>
              <ru>Удалить файл</ru>
              <en>Delete file</en>
             </a>
            </block_file>
            <block_nofile>
             <ru>Нет файла</ru>
             <en>No file</en>
            </block_nofile>
            
           </div>
           <iframe name='uploadbuffer' class='uploadbuffer' id='uploadbuffer_%id%' onload='uploaded_v2("%id%");'></iframe>
           <p>
          </span>
          <b>Оставить комментарий:</b>
          <textarea placeholder="Напишите требование по рекламе или сразу задайте вопрос администратору канала" id="Comment"></textarea>
         </div>
        </div>
        <div id="repost" class="tab-pane fade">
         <div class="post_line">
          <p>Ссылка на репост</p>
          <input type="text" placeholder="http://cflink.ru/casebiz/3">
         </div>
        </div>
       </div>
      </div>
      <div class="post_app">
       <div class="row">
        <div class="ch_alert alert alert-danger center">
         Внимательно проверьте рекламный пост перед отправкой. После отправки поста правка невозможна. После отправки поста с вашего счёта будет списано <b>%Cost% рублей</b>, которые будут зачислены на счёт администратора канала после того, как вы одобрите публикацию.
        </div>
        <div class="center">
         <button class="btn btn-success" onclick="sendRequest();">Отправить предложение <span>%Cost% руб.</span></button>
        </div>
       </div>
      </div>
     </div>
    </div>
   </div>
   <div class="clear"></div>
  </div>
  <div class="chs_line hidden" id="sendRequestRslt_div">
   <div class="alert alert-danger" id="sendRequestRslt">
    Здесь будет блок отзывов о рекламе в этом канале, они появляются после того как реклама опубликовалась и пользователи обменялись отзывами.
   </div>
  </div>
 </block_requestallowed>
 <block_adminonly>
  <div class="chs_line">
   <div class="ch_rewievs">
    <div class="row">
     <div class="col-sm-6">
      <h3>Кто рекламировался:</h3>
      <div class="overflow_box">
       <loop_itemsWho>
        <div class="line_adv row">
         <div class="col-sm-2">
          <img class="img-responsive img-circle" src="%PhotoBig%" alt="%Title%">
         </div>
         <div class="col-sm-7">
          <a href="/channel/%Link%">%Title%</a>
         </div>
         <div class="col-sm-3 right">
          <span>17.11.17</span><b>17:45</b>
         </div>
        </div>
       </loop_itemsWho>
      </div>
     </div>
     <div class="col-sm-6">
      <h3>Где рекламировался:</h3>
      <div class="overflow_box">
       <loop_itemsWhere>
        <div class="line_adv row">
         <div class="col-sm-2">
          <img class="img-responsive img-circle" src="%PhotoBig%" alt="%Title%">
         </div>
         <div class="col-sm-7">
          <a href="/channel/%Link%">%Title%</a>
         </div>
         <div class="col-sm-3 right">
          <span>17.11.17</span><b>17:45</b>
         </div>
        </div>
       </loop_itemsWhere>
      </div>
     </div>
    </div>
    <block_reviews>
     <div class="row">
      <div class="col-sm-12">
       <h3>Отзывы о канале:</h3>
       <loop_reviews>
        <div class="line_review row">
         <div class="col-sm-1">
          <img class="img-responsive img-circle" src="%PhotoBig%" alt="Отзыв о канале %Title%">
         </div>
         <div class="col-sm-7">
          <div class="rev_user">
           <b>%Firstname% %Lastname%</b> %DateAddedFmt%<br>
           %Comment%
          </div>
         </div>
         <div class="col-sm-4">
          <ul class="right">
           <li>
            <b>Ответственность:</b>
            <i class="fa fa-star%Liability_1%"></i>
            <i class="fa fa-star%Liability_2%"></i>
            <i class="fa fa-star%Liability_3%"></i>
            <i class="fa fa-star%Liability_4%"></i>
            <i class="fa fa-star%Liability_5%"></i>
           </li>
           <li>
            <b>Сроки:</b>
            <i class="fa fa-star%Terms_1%"></i>
            <i class="fa fa-star%Terms_2%"></i>
            <i class="fa fa-star%Terms_3%"></i>
            <i class="fa fa-star%Terms_4%"></i>
            <i class="fa fa-star%Terms_5%"></i>
           </li>
           <li>
            <b>Адекватность:</b>
            <i class="fa fa-star%Adequacy_1%"></i>
            <i class="fa fa-star%Adequacy_2%"></i>
            <i class="fa fa-star%Adequacy_3%"></i>
            <i class="fa fa-star%Adequacy_4%"></i>
            <i class="fa fa-star%Adequacy_5%"></i>
           </li>
          </ul>
         </div>
        </div>
       </loop_reviews>
      </div>
     </div>
    </block_reviews>
   </div>
  </div>
 </block_adminonly>
 <div class="chs_line divstats">
  <!-- Styles -->
  <style>
   #chartdiv {
   width : 100%;
   height : 500px;
   }
  </style>
  <!-- Resources -->
  <script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
  <script src="https://www.amcharts.com/lib/3/serial.js"></script>
  <script src="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script>
  <link rel="stylesheet" href="https://www.amcharts.com/lib/3/plugins/export/export.css" type="text/css" media="all" />
  <script src="https://www.amcharts.com/lib/3/themes/light.js"></script>
  <!-- Chart code -->
  <script>
   var chart = AmCharts.makeChart(
    "chartdiv", {
     "type": "serial",
     "theme": "light",
     "marginRight": 40,
     "marginLeft": 40,
     "autoMarginOffset": 20,
     "mouseWheelZoomEnabled":true,
     "dataDateFormat": "YYYY-MM-DD",
     "valueAxes": [
      {
       "id": "v1",
       "axisAlpha": 0,
       "position": "left",
       "ignoreAxisWidth":true
      }
     ],
     "balloon": {
      "borderThickness": 1,
      "shadowAlpha": 0
     },
     "graphs": [
      {
         "id": "g1",
         "balloon":{
           "drop":true,
           "adjustBorderColor":false,
           "color":"#ffffff"
         },
         "bullet": "round",
         "bulletBorderAlpha": 1,
         "bulletColor": "#FFFFFF",
         "bulletSize": 5,
         "hideBulletsCount": 50,
         "lineThickness": 2,
         "title": "red line",
         "useLineColorForBulletBorder": true,
         "valueField": "value",
         "balloonText": "<span style='font-size:18px;'>[[value]]</span>"
      }
     ],
     "chartScrollbar": {
         "graph": "g1",
         "oppositeAxis":false,
         "offset":30,
         "scrollbarHeight": 80,
         "backgroundAlpha": 0,
         "selectedBackgroundAlpha": 0.1,
         "selectedBackgroundColor": "#888888",
         "graphFillAlpha": 0,
         "graphLineAlpha": 0.5,
         "selectedGraphFillAlpha": 0,
         "selectedGraphLineAlpha": 1,
         "autoGridCount":true,
         "color":"#AAAAAA"
     },
     "chartCursor": {
         "pan": true,
         "valueLineEnabled": true,
         "valueLineBalloonEnabled": true,
         "cursorAlpha":1,
         "cursorColor":"#258cbb",
         "limitToGraph":"g1",
         "valueLineAlpha":0.2,
         "valueZoomable":true
     },
     "valueScrollbar":{
       "oppositeAxis":false,
       "offset":50,
       "scrollbarHeight":10
     },
     "categoryField": "date",
     "categoryAxis": {
         "parseDates": true,
         "dashLength": 1,
         "minorGridEnabled": true
     },
     "export": {
         "enabled": true
     },
     "dataProvider": [
      %StatsDaily%
     ]
    }
   );
   
   chart.addListener("rendered", zoomChart);
   
   zoomChart();
   
   function zoomChart() {
    chart.zoomToIndexes(chart.dataProvider.length - 40, chart.dataProvider.length - 1);
   }
  </script>
  <!-- HTML -->
  <div id="chartdiv"></div>
  <!--
   <div class="h4">График ниже отобразим в нем кол-во рекламных постов канала</div>
   будет круговая диаграмма с процентным соотношением 
   -->
 </div>
 <div class="chs_line">
  <div class="row">
   <div class="col-sm-7">
    <div class="graph1">
     <div class="h4">Суточный охват записей <button class="btn primary_btn navbar-btn show_day_stat"><i class="fa fa-info-circle" aria-hidden="true"></i></button></div>
     <div style="display:none;" class="day_stat alert alert-warning">
      <p>В графике показаны данные суточного охвата по просмотрам за 24 часа с момента публикации.</p>
      <p>Если в день вышло более одной публикации - просмотры суммируются и делятся на кол-во публикаций в этот день. Таким образом мы получаем среднее арифметическое.</p>
     </div>
     <script>
      jQuery('.show_day_stat').on(
       'click',function(){
        jQuery('.day_stat').toggle(); 
       }
      );
     </script>
     <canvas id="myChart" width="400" height="150"></canvas>
     <script>
      var ctx = document.getElementById("myChart");
      var myChart = new Chart(ctx, 
       {
        type: 'line', data: {
         datasets: [
          {
           label: 'последние 14 дней',
           data: [%StatsDaily_views_data%]
          }, 
         ],
         labels: [%StatsDaily_views_labels%]
        },
        options: {
         scales: {
          yAxes: [
           {
            ticks: {
             beginAtZero: true
            }
           }
          ]
         }
        }
       }
      );
     </script>
    </div>
    <div class="graph1">
     <div class="h4">График изменения цен <button class="btn primary_btn navbar-btn show_day_stat2"><i class="fa fa-info-circle" aria-hidden="true"></i></button></div>
     <div style="display:none;" class="day_stat2 alert alert-warning">
      <p>В графике показан порядок изменения цен на рекламную запись, размещаемую на 24 часа.</p>
     </div>
     <script>
      jQuery('.show_day_stat2').on(
       'click',function(){
        jQuery('.day_stat2').toggle(); 
       }
      );
     </script>
     <canvas id="myChart2" width="400" height="150"></canvas>
     <script>
      var ctx2 = document.getElementById("myChart2");
      var myChart2 = new Chart(
       ctx2, {
        type: 'line',
        data: {
         datasets: [
          {
           label: 'Текущая неделя (руб.)',
           data: [%StatsDaily_cost_data%]
          }, 
         ],
         labels: [%StatsDaily_cost_labels%]
        },
        options: {
         scales: {
          yAxes: [
           {
            ticks: {
             beginAtZero: true
            }
           }
          ]
         }
        }
       }
      );
     </script>
    </div>
    
   </div>
   <div class="col-sm-5">
    <block_messages>
     <div class="chs_liners">
      <div class="h4">Записи канала</div>
      <div class="ch_desc">
       <loop_messages>
        <div class="post_blog">
         <div class="titletext">
          <div class="row">
           <div style="display:none;" class="col-sm-1">
            <img class="ch_img img-responsive img-circle" src="%PhotoBig%" alt="%Title%">
           </div>
           <div class="col-sm-12">
            <a href="">%Title%</a>
           </div>
           <div style="display:none;" class="col-sm-3 right">
            %DateAdded% 
           </div>
          </div>
         </div>
         <div class="posttext">%Text%</div>
         <div style="margin-top:10px;padding-top:10px;border-top:1px solid #cfd1d3;" class="row">
          <div class="col-sm-8">
           <div class="row">
            <div class="col-sm-6">
             <block_views1>
              <div class="views"><span data-toggle="tooltip" data-original-title="просмотры за 24 часа"><b>BAR1:</b> %Views1%</span></div>
             </block_views1>
            </div>
            <div class="col-sm-6">
             <block_views2>
              <div class="views"><span data-toggle="tooltip" data-original-title="просмотры за 48 часов"><b>BAR2:</b> %Views2%</span></div>
             </block_views2>
            </div>
            <div class="col-sm-6">
             <block_views3>
              <div class="views"><span data-toggle="tooltip" data-original-title="просмотры за 72 часа"><b>BAR3:</b> %Views3%</span></div>
             </block_views3>
            </div>
            <div class="col-sm-6">
             <block_views7>
              <div class="views"><span data-toggle="tooltip" data-original-title="просмотры за 7 дней"><b>BAR7:</b> %Views7%</span></div>
             </block_views7>
            </div>
            <div class="col-sm-6">
             <block_views30>
              <div class="views"><span data-toggle="tooltip" data-original-title="просмотры за 30 дней"><b>BAR30:</b> %Views30%</span></div>
             </block_views30>
            </div>
           </div>
          </div>
          <div class="col-sm-4">
           <block_views>
            <div class="views right"><i class="fa fa-eye" aria-hidden="true"></i>%Views%</div>
           </block_views>
          </div>
         </div>
        </div>
       </loop_messages>
      </div>
     </div>
    </block_messages>
   </div>
  </div>
 </div>
</div>
<!-- Modal -->
<div id="addDate" class="modal fade" role="dialog">
 <div class="modal-dialog">
  <!-- Modal content-->
  <div class="modal-content">
   <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" onclick="getChannels();">&times;</button>
    <h4 class="modal-title">Дата публикации</h4>
   </div>
   <div class="modal-body">
    <div class="row">
     <div class="col-sm-6">
      <div id="datepickermodal"></div>
      <script type="text/javascript">
       $(
        function () {
         $('#datepickermodal').datetimepicker(
          {
           locale: 'ru',
           inline: true,
           format: 'DD.MM.YYYY HH:mm:ss',
       
          }
         );
        }
       );
      </script>
     </div>
     <div class="col-sm-6">
      <p>Также вы можете выбрать свободную дату и время предложенные администратором канала</p>
      <button><b>10.01.18</b> (<span>17:59</span>)</button><button><b>13.01.18</b> (<span>17:59</span>)</button><button><b>15.01.18</b> (<span>17:59</span>)</button><button><b>17.01.18</b> (<span>17:59</span>)</button><button><b>19.01.18</b> (<span>17:59</span>)</button><button><b>22.01.18</b> (<span>17:59</span>)</button>
     </div>
    </div>
   </div>
  </div>
 </div>
</div>
<!-- Modal -->
<div id="addAnswer" class="modal fade" role="dialog">
 <div class="modal-dialog">
  <!-- Modal content-->
  <div class="modal-content">
   <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" onclick="getChannels();">&times;</button>
    <h4 class="modal-title">Ответ не позже</h4>
   </div>
   <div class="modal-body">
    <div id="datepickermodal"></div>
    <script type="text/javascript">
     $(
      function () {
       $('#datepickermodal').datetimepicker(
        {
         locale: 'ru',
         inline: true,
         format: 'DD.MM.YYYY HH:mm:ss',
        }
       );
      }
     );
    </script>
   </div>
  </div>
 </div>
</div>
            </div>
        </div>
    </div>
</div>
    [template] => <div class="user_channel_block">
    <div class="container">
        <div class="row">
            <div class="col-sm-2"></div>
            <div class="col-sm-10">
                <div class="chs_info">
 <div class="chs_line">
  <div class="row">
   <div class="col-sm-6">
    <div class="h1">Информация о канале</div>
   </div>
   <!--
    <div class="col-sm-6 right">
     <button type="button" class="admpen btn btn-danger"><span class="glyphicon glyphicon-pencil"></span>Написать администратору</button>    
    </div>
    -->
  </div>
 </div>
 <div class="chs_line">
  <div class="col-sm-2">
   <img class="ch_img img-circle" src="%PhotoBig%" alt="%Title%">
  </div>
  <div class="col-sm-10">
   <div class="ch_name">
    <a href="">%Title%</a> 
    <block_link><a class="link" href="http://cflink.ru/%Link%" target="_blank">@%Link%</a></block_link>
   </div>
   <div class="row">
    <div class="col-sm-7">
     <div style="display:none;" class="ch_cat">
      <a href="">%Type%</a>
     </div>
     <div class=clrln></div>
     <block_cat>
      <div class="cat_cat">
       Категория: <b><a href="/channel/%Link%">%Cat%</a></b>
      </div>
     </block_cat>
     <div class="ch_col">
      Подписчиков: <b>%Members%</b>
     </div>
     <div class="ch_col">
      Стоимость рекламы: <b>%Cost%</b> руб.
     </div>
    </div>
    <div class="col-sm-5 opiskanala">
     <block_description>
      <b>Описание канала:</b>
      %Description%
     </block_description>
    </div>
   </div>
  </div>
  <!--
   <div class="col-sm-3 right">
    <a class="zametka" href=""><span class="glyphicon glyphicon-bookmark"></span> Оставить заметку</a>
   </div>
   -->
  <div class="clear"></div>
 </div>
 <block_sysmsgs>
  <div class="chs_line">
   %SystemMessages%
  </div>
 </block_sysmsgs>
 
 
 
 
 
 
 
 
 
 
 <div class="hidden">
  <div class="chs_line">
   <div class="variants_rek">
    <div class="line_chan">
     Реклама на Telegram канале <a class="link" href="http://cflink.ru/%Link%" target="_blank">@%Link%</a>
    </div>
    <div class="variant_list">
     <div class="row">
      <div class="col-sm-2">
       <span>Размещение</span>
      </div>
      <div class="col-sm-1">
       <span><b>%Cost%</b> Р</span>
      </div>
      <div class="col-sm-2">
       <span>1 час / 2 дня</span>
      </div>
      <div class="col-sm-3">
       <span>Просмотров: <b>1400+</b></span>
      </div>
      <div class="col-sm-2">
       <span>CPM: <b>595.83 Р</b></span>
      </div>
      <div class="col-sm-2"><button class="btn btn-success">Выбрать</button></div>
     </div>
     <div class="row">
      <div class="col-sm-2">
       <span>Размещение</span>
      </div>
      <div class="col-sm-1">
       <span><b>%Cost%</b> Р</span>
      </div>
      <div class="col-sm-2">
       <span>5 час / 1 дня</span>
      </div>
      <div class="col-sm-3">
       <span>Просмотров: <b>2400+</b></span>
      </div>
      <div class="col-sm-2">
       <span>CPM: <b>595.83 Р</b></span>
      </div>
      <div class="col-sm-2"><button class="btn btn-success">Выбрать</button></div>
     </div>
    </div>
   </div>
  </div>
  <div class="chs_line">
   <div class="alert alert-warning">
    После того как юзер выбрал вариант размещения ему показываем что может составить пост и отправить рекламу на модерацию. <br><br>
    кстати после выбора варианта - мы его подсвечиваем, либо фоном либо цвет кнопки, хз подумаем <br><br>
    Когда выбираем дату можем кликнуть на сам календарь и выбрать дату и время, а можем выбрать из предложенных вариантов администратора (это он указывает у себя в настройках канала) <br><br>
    Также я бы вывел блок с уже отправленными этому каналу заявками со статусами заявок
   </div>
  </div>
  <div class="chs_line">
   <div class="publ-box">
    <div class="row">
     <div class="col-sm-3">
      <b>Статус предложения</b><br>
      <span>Составление</span>
     </div>
     <div class="col-sm-3">
      <b>Дата публикации</b><br>
      <span><a data-toggle="modal" data-target="#addDate">Январь 9, 10:35</a></span>
     </div>
     <div class="col-sm-3">
      <b>Ответ не позже</b><br>
      <span><a data-toggle="modal" data-target="#addAnswer">Январь 10, 10:35</a></span>
     </div>
    </div>
   </div>
  </div>
 </div>
 <block_requestallowed>
  <block_notenoughmoney>
   <div class="chs_line">
    <div class="alert alert-danger">
     У вас недостаточно средств, чтобы оставить заявку на рекламу в этом канале! Для продолжения следует <a href="/pay">пополнить баланс</a>.
     
    </div>
   </div>
  </block_notenoughmoney>
  <div class="chs_line">
   <div class="ch_ads">
    <div class="add_ads">
     <div class="col-sm-4">
      <!--<img src="%imgfolder%/calendar.jpg" alt="">-->
      <div style="overflow:hidden;">
       <div class="form-group">
        <div class="row">
         <div class="col-sm-12">
          <div id="datetimepicker12"></div>
         </div>
        </div>
       </div>
       <script type="text/javascript">
        $(
         function () {
          $('#datetimepicker12').datetimepicker(
           {
            inline: true,
            format: 'DD.MM.YYYY HH:mm:ss'
           }
          );
         }
        );
       </script>
      </div>
      <block_comment>
       <div class="adm_say">
        <div class="alert alert-warning">
         %Comment%
        </div>
       </div>
      </block_comment>
     </div>
     <div class="col-sm-8">
      <div class="alert alert-info">
       Выберите дату слева, составьте рекламный пост и отправьте на утверждение.
      </div>
      <div class="ch_post">
       <ul class="nav nav-tabs">
        <li class="active"><a data-toggle="tab" href="#post">Пост</a></li>
        <!--<li><a data-toggle="tab" href="#repost">Перепост</a></li>-->
       </ul>
       <div class="tab-content">
        <div id="post" class="tab-pane fade in active">
         <div class="post_line">
          <b>Какой канал рекламируете?</b>
          <select id="ChannelID">
           <option value="0">
            Никакой
            
           </option>
           <loop_channels>
            <option id="%ID%">%Title% (@%Link%)</option>
           </loop_channels>
           <block_nochannels>
           </block_nochannels>
          </select>
          <br><br>
          <b>Напишите ваш рекламный материал</b>
          <textarea placeholder="Отправьте администратору канала заранее отформатированный текст, согласно правил форматирования которые представлены ниже. " id="Text"></textarea>
          <br><br>
          <div class="table_format">
           <table class="table table-striped">
            <thead>
             <tr>
              <th>Правило</th>
              <th>Результат</th>
             </tr>
            </thead>
            <tbody>
             <tr>
              <td>[Ссылка] (https://barzha.top)</td>
              <td><a href="javascript:;" rel="noopener" onclick="return false;">Ссылка</a></td>
             </tr>
             <tr>
              <td>*Жирный*</td>
              <td><b>Жирный</b></td>
             </tr>
             <tr>
              <td>_Курсив_</td>
              <td><em>Курсив</em></td>
             </tr>
             <tr>
              <td>`Выделение кода`</td>
              <td><code>Выделение кода</code></td>
             </tr>
            </tbody>
           </table>
          </div>
          <span class="btn-file">
           Выберите файл:<br>
           <form id='uploadform_%id%' name='uploadform' target='uploadbuffer' enctype='multipart/form-data' method='post' action='index.php'>
            <p>
             <input type='hidden' name='action'     id='action' value='%id%'>
             <input type='hidden' name='id'         id='id'     value=''>
             <input type='file'   name='fileupload' id='fileupload' onchange='doupload_v2("%id%");' accept_='application/vnd.ms-excel'>
             <!-- <button onclick='doupload_v2("%id%");'>Reload</button> -->
            </p>
           </form>
           <p>
           <div id='status_%id%'>
            <block_file>
             <a href="%rootdir%/%Image%" target="_blank">
             <img class="img" src="%rootdir%/%Image_pf%">
             </a>
             <a href="javascript:;" onclick="deleteFile('%Image%', '%id%');">
              <span class="fa fa-times"></span>
              Удалить файл
              
             </a>
            </block_file>
            <block_nofile>
             Нет файла
             
            </block_nofile>
            
           </div>
           <iframe name='uploadbuffer' class='uploadbuffer' id='uploadbuffer_%id%' onload='uploaded_v2("%id%");'></iframe>
           <p>
          </span>
          <b>Оставить комментарий:</b>
          <textarea placeholder="Напишите требование по рекламе или сразу задайте вопрос администратору канала" id="Comment"></textarea>
         </div>
        </div>
        <div id="repost" class="tab-pane fade">
         <div class="post_line">
          <p>Ссылка на репост</p>
          <input type="text" placeholder="http://cflink.ru/casebiz/3">
         </div>
        </div>
       </div>
      </div>
      <div class="post_app">
       <div class="row">
        <div class="ch_alert alert alert-danger center">
         Внимательно проверьте рекламный пост перед отправкой. После отправки поста правка невозможна. После отправки поста с вашего счёта будет списано <b>%Cost% рублей</b>, которые будут зачислены на счёт администратора канала после того, как вы одобрите публикацию.
        </div>
        <div class="center">
         <button class="btn btn-success" onclick="sendRequest();">Отправить предложение <span>%Cost% руб.</span></button>
        </div>
       </div>
      </div>
     </div>
    </div>
   </div>
   <div class="clear"></div>
  </div>
  <div class="chs_line hidden" id="sendRequestRslt_div">
   <div class="alert alert-danger" id="sendRequestRslt">
    Здесь будет блок отзывов о рекламе в этом канале, они появляются после того как реклама опубликовалась и пользователи обменялись отзывами.
   </div>
  </div>
 </block_requestallowed>
 <block_adminonly>
  <div class="chs_line">
   <div class="ch_rewievs">
    <div class="row">
     <div class="col-sm-6">
      <h3>Кто рекламировался:</h3>
      <div class="overflow_box">
       <loop_itemsWho>
        <div class="line_adv row">
         <div class="col-sm-2">
          <img class="img-responsive img-circle" src="%PhotoBig%" alt="%Title%">
         </div>
         <div class="col-sm-7">
          <a href="/channel/%Link%">%Title%</a>
         </div>
         <div class="col-sm-3 right">
          <span>17.11.17</span><b>17:45</b>
         </div>
        </div>
       </loop_itemsWho>
      </div>
     </div>
     <div class="col-sm-6">
      <h3>Где рекламировался:</h3>
      <div class="overflow_box">
       <loop_itemsWhere>
        <div class="line_adv row">
         <div class="col-sm-2">
          <img class="img-responsive img-circle" src="%PhotoBig%" alt="%Title%">
         </div>
         <div class="col-sm-7">
          <a href="/channel/%Link%">%Title%</a>
         </div>
         <div class="col-sm-3 right">
          <span>17.11.17</span><b>17:45</b>
         </div>
        </div>
       </loop_itemsWhere>
      </div>
     </div>
    </div>
    <block_reviews>
     <div class="row">
      <div class="col-sm-12">
       <h3>Отзывы о канале:</h3>
       <loop_reviews>
        <div class="line_review row">
         <div class="col-sm-1">
          <img class="img-responsive img-circle" src="%PhotoBig%" alt="Отзыв о канале %Title%">
         </div>
         <div class="col-sm-7">
          <div class="rev_user">
           <b>%Firstname% %Lastname%</b> %DateAddedFmt%<br>
           %Comment%
          </div>
         </div>
         <div class="col-sm-4">
          <ul class="right">
           <li>
            <b>Ответственность:</b>
            <i class="fa fa-star%Liability_1%"></i>
            <i class="fa fa-star%Liability_2%"></i>
            <i class="fa fa-star%Liability_3%"></i>
            <i class="fa fa-star%Liability_4%"></i>
            <i class="fa fa-star%Liability_5%"></i>
           </li>
           <li>
            <b>Сроки:</b>
            <i class="fa fa-star%Terms_1%"></i>
            <i class="fa fa-star%Terms_2%"></i>
            <i class="fa fa-star%Terms_3%"></i>
            <i class="fa fa-star%Terms_4%"></i>
            <i class="fa fa-star%Terms_5%"></i>
           </li>
           <li>
            <b>Адекватность:</b>
            <i class="fa fa-star%Adequacy_1%"></i>
            <i class="fa fa-star%Adequacy_2%"></i>
            <i class="fa fa-star%Adequacy_3%"></i>
            <i class="fa fa-star%Adequacy_4%"></i>
            <i class="fa fa-star%Adequacy_5%"></i>
           </li>
          </ul>
         </div>
        </div>
       </loop_reviews>
      </div>
     </div>
    </block_reviews>
   </div>
  </div>
 </block_adminonly>
 <div class="chs_line divstats">
  <!-- Styles -->
  <style>
   #chartdiv {
   width : 100%;
   height : 500px;
   }
  </style>
  <!-- Resources -->
  <script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
  <script src="https://www.amcharts.com/lib/3/serial.js"></script>
  <script src="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script>
  <link rel="stylesheet" href="https://www.amcharts.com/lib/3/plugins/export/export.css" type="text/css" media="all" />
  <script src="https://www.amcharts.com/lib/3/themes/light.js"></script>
  <!-- Chart code -->
  <script>
   var chart = AmCharts.makeChart(
    "chartdiv", {
     "type": "serial",
     "theme": "light",
     "marginRight": 40,
     "marginLeft": 40,
     "autoMarginOffset": 20,
     "mouseWheelZoomEnabled":true,
     "dataDateFormat": "YYYY-MM-DD",
     "valueAxes": [
      {
       "id": "v1",
       "axisAlpha": 0,
       "position": "left",
       "ignoreAxisWidth":true
      }
     ],
     "balloon": {
      "borderThickness": 1,
      "shadowAlpha": 0
     },
     "graphs": [
      {
         "id": "g1",
         "balloon":{
           "drop":true,
           "adjustBorderColor":false,
           "color":"#ffffff"
         },
         "bullet": "round",
         "bulletBorderAlpha": 1,
         "bulletColor": "#FFFFFF",
         "bulletSize": 5,
         "hideBulletsCount": 50,
         "lineThickness": 2,
         "title": "red line",
         "useLineColorForBulletBorder": true,
         "valueField": "value",
         "balloonText": "<span style='font-size:18px;'>[[value]]</span>"
      }
     ],
     "chartScrollbar": {
         "graph": "g1",
         "oppositeAxis":false,
         "offset":30,
         "scrollbarHeight": 80,
         "backgroundAlpha": 0,
         "selectedBackgroundAlpha": 0.1,
         "selectedBackgroundColor": "#888888",
         "graphFillAlpha": 0,
         "graphLineAlpha": 0.5,
         "selectedGraphFillAlpha": 0,
         "selectedGraphLineAlpha": 1,
         "autoGridCount":true,
         "color":"#AAAAAA"
     },
     "chartCursor": {
         "pan": true,
         "valueLineEnabled": true,
         "valueLineBalloonEnabled": true,
         "cursorAlpha":1,
         "cursorColor":"#258cbb",
         "limitToGraph":"g1",
         "valueLineAlpha":0.2,
         "valueZoomable":true
     },
     "valueScrollbar":{
       "oppositeAxis":false,
       "offset":50,
       "scrollbarHeight":10
     },
     "categoryField": "date",
     "categoryAxis": {
         "parseDates": true,
         "dashLength": 1,
         "minorGridEnabled": true
     },
     "export": {
         "enabled": true
     },
     "dataProvider": [
      %StatsDaily%
     ]
    }
   );
   
   chart.addListener("rendered", zoomChart);
   
   zoomChart();
   
   function zoomChart() {
    chart.zoomToIndexes(chart.dataProvider.length - 40, chart.dataProvider.length - 1);
   }
  </script>
  <!-- HTML -->
  <div id="chartdiv"></div>
  <!--
   <div class="h4">График ниже отобразим в нем кол-во рекламных постов канала</div>
   будет круговая диаграмма с процентным соотношением 
   -->
 </div>
 <div class="chs_line">
  <div class="row">
   <div class="col-sm-7">
    <div class="graph1">
     <div class="h4">Суточный охват записей <button class="btn primary_btn navbar-btn show_day_stat"><i class="fa fa-info-circle" aria-hidden="true"></i></button></div>
     <div style="display:none;" class="day_stat alert alert-warning">
      <p>В графике показаны данные суточного охвата по просмотрам за 24 часа с момента публикации.</p>
      <p>Если в день вышло более одной публикации - просмотры суммируются и делятся на кол-во публикаций в этот день. Таким образом мы получаем среднее арифметическое.</p>
     </div>
     <script>
      jQuery('.show_day_stat').on(
       'click',function(){
        jQuery('.day_stat').toggle(); 
       }
      );
     </script>
     <canvas id="myChart" width="400" height="150"></canvas>
     <script>
      var ctx = document.getElementById("myChart");
      var myChart = new Chart(ctx, 
       {
        type: 'line', data: {
         datasets: [
          {
           label: 'последние 14 дней',
           data: [%StatsDaily_views_data%]
          }, 
         ],
         labels: [%StatsDaily_views_labels%]
        },
        options: {
         scales: {
          yAxes: [
           {
            ticks: {
             beginAtZero: true
            }
           }
          ]
         }
        }
       }
      );
     </script>
    </div>
    <div class="graph1">
     <div class="h4">График изменения цен <button class="btn primary_btn navbar-btn show_day_stat2"><i class="fa fa-info-circle" aria-hidden="true"></i></button></div>
     <div style="display:none;" class="day_stat2 alert alert-warning">
      <p>В графике показан порядок изменения цен на рекламную запись, размещаемую на 24 часа.</p>
     </div>
     <script>
      jQuery('.show_day_stat2').on(
       'click',function(){
        jQuery('.day_stat2').toggle(); 
       }
      );
     </script>
     <canvas id="myChart2" width="400" height="150"></canvas>
     <script>
      var ctx2 = document.getElementById("myChart2");
      var myChart2 = new Chart(
       ctx2, {
        type: 'line',
        data: {
         datasets: [
          {
           label: 'Текущая неделя (руб.)',
           data: [%StatsDaily_cost_data%]
          }, 
         ],
         labels: [%StatsDaily_cost_labels%]
        },
        options: {
         scales: {
          yAxes: [
           {
            ticks: {
             beginAtZero: true
            }
           }
          ]
         }
        }
       }
      );
     </script>
    </div>
    
   </div>
   <div class="col-sm-5">
    <block_messages>
     <div class="chs_liners">
      <div class="h4">Записи канала</div>
      <div class="ch_desc">
       <loop_messages>
        <div class="post_blog">
         <div class="titletext">
          <div class="row">
           <div style="display:none;" class="col-sm-1">
            <img class="ch_img img-responsive img-circle" src="%PhotoBig%" alt="%Title%">
           </div>
           <div class="col-sm-12">
            <a href="">%Title%</a>
           </div>
           <div style="display:none;" class="col-sm-3 right">
            %DateAdded% 
           </div>
          </div>
         </div>
         <div class="posttext">%Text%</div>
         <div style="margin-top:10px;padding-top:10px;border-top:1px solid #cfd1d3;" class="row">
          <div class="col-sm-8">
           <div class="row">
            <div class="col-sm-6">
             <block_views1>
              <div class="views"><span data-toggle="tooltip" data-original-title="просмотры за 24 часа"><b>BAR1:</b> %Views1%</span></div>
             </block_views1>
            </div>
            <div class="col-sm-6">
             <block_views2>
              <div class="views"><span data-toggle="tooltip" data-original-title="просмотры за 48 часов"><b>BAR2:</b> %Views2%</span></div>
             </block_views2>
            </div>
            <div class="col-sm-6">
             <block_views3>
              <div class="views"><span data-toggle="tooltip" data-original-title="просмотры за 72 часа"><b>BAR3:</b> %Views3%</span></div>
             </block_views3>
            </div>
            <div class="col-sm-6">
             <block_views7>
              <div class="views"><span data-toggle="tooltip" data-original-title="просмотры за 7 дней"><b>BAR7:</b> %Views7%</span></div>
             </block_views7>
            </div>
            <div class="col-sm-6">
             <block_views30>
              <div class="views"><span data-toggle="tooltip" data-original-title="просмотры за 30 дней"><b>BAR30:</b> %Views30%</span></div>
             </block_views30>
            </div>
           </div>
          </div>
          <div class="col-sm-4">
           <block_views>
            <div class="views right"><i class="fa fa-eye" aria-hidden="true"></i>%Views%</div>
           </block_views>
          </div>
         </div>
        </div>
       </loop_messages>
      </div>
     </div>
    </block_messages>
   </div>
  </div>
 </div>
</div>
<!-- Modal -->
<div id="addDate" class="modal fade" role="dialog">
 <div class="modal-dialog">
  <!-- Modal content-->
  <div class="modal-content">
   <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" onclick="getChannels();">&times;</button>
    <h4 class="modal-title">Дата публикации</h4>
   </div>
   <div class="modal-body">
    <div class="row">
     <div class="col-sm-6">
      <div id="datepickermodal"></div>
      <script type="text/javascript">
       $(
        function () {
         $('#datepickermodal').datetimepicker(
          {
           locale: 'ru',
           inline: true,
           format: 'DD.MM.YYYY HH:mm:ss',
       
          }
         );
        }
       );
      </script>
     </div>
     <div class="col-sm-6">
      <p>Также вы можете выбрать свободную дату и время предложенные администратором канала</p>
      <button><b>10.01.18</b> (<span>17:59</span>)</button><button><b>13.01.18</b> (<span>17:59</span>)</button><button><b>15.01.18</b> (<span>17:59</span>)</button><button><b>17.01.18</b> (<span>17:59</span>)</button><button><b>19.01.18</b> (<span>17:59</span>)</button><button><b>22.01.18</b> (<span>17:59</span>)</button>
     </div>
    </div>
   </div>
  </div>
 </div>
</div>
<!-- Modal -->
<div id="addAnswer" class="modal fade" role="dialog">
 <div class="modal-dialog">
  <!-- Modal content-->
  <div class="modal-content">
   <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" onclick="getChannels();">&times;</button>
    <h4 class="modal-title">Ответ не позже</h4>
   </div>
   <div class="modal-body">
    <div id="datepickermodal"></div>
    <script type="text/javascript">
     $(
      function () {
       $('#datepickermodal').datetimepicker(
        {
         locale: 'ru',
         inline: true,
         format: 'DD.MM.YYYY HH:mm:ss',
        }
       );
      }
     );
    </script>
   </div>
  </div>
 </div>
</div>
            </div>
        </div>
    </div>
</div>
    [filename] => view/modern/templates/user_channel.htt
    [viewroot] => view/modern/templates/
    [is_template] => 1
)
, stdClass Object
(
    [ID] => 3733
    [DateAdded] => 2018-01-09 06:41:19.75124
    [UserID] => 
    [Title] => 2pegramming
    [Link] => pepegramming
    [Cost] => 71.8
    [CategoryID] => 5
    [Members] => 719
    [Views] => 
    [TgUserID] => 
    [TgChatID] => -1001106477565
    [Type] => channel
    [Description] => Грустно о программировании. Все проблемы сюда: @davydovanton

Medium: https://medium.com/pepegramming

Ссылки на конкретные посты: http://telegra.ph/Pepegramming-Contents-03-11
Обратная связь: https://goo.gl/forms/iUd1Gufq6WnTsaO62
    [Comment] => 
    [CatID] => 
    [Cat] => 
    [DateUpdated] => 2018-12-18 07:04:21
    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
    [TgCreatorID] => 
    [IsVisible] => 1
    [IsMessagesVisible] => 1
    [IsMessagesIndexable] => 1
    [PhotoLastDateUpdated] => 2018-12-17 03:25:36
    [MinTgMsgID] => 1
    [MsgsScanned] => 1
    [MaxMsgLastDateUpdated] => 2018-12-17 14:45:48
    [Tags] => 
    [Source] => 
    [IsFound] => 1
    [MaxTgMsgID] => 116
    [NumMessages] => 91
    [CostSource] => members
    [Messages] => Array
        (
            [5402158] => stdClass Object
                (
                    [ID] => 5402158
                    [TgID] => 116
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-12-07 16:34:18
                    [Text] => Ссылка на ютуб канал со стримами (качество не очень вышло, постараюсь исправить)  https://www.youtube.com/playlist?list=PL6n1fvXhQN4kOm_HhBDJrynj6_dcl7VJP
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 711
                    [ViewsLastDateUpdated] => 2018-12-17 07:27:12
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 578
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [5402157] => stdClass Object
                (
                    [ID] => 5402157
                    [TgID] => 115
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-12-07 16:30:13
                    [Text] => Пятничное чтиво

Привет&#33
На неделе провел первый стрим, объяснил и показал как написать простейший парсер математических формул и объяснил как обратная польская нотация может помочь в вычислении такси строк. Ссылка на видео.

Следующий стрим планирую сделать через неделю. Будем делать валидатор для банковских карт. Если у есть идеи для проектов - пишите.

Так как 12 декабря буду рассказывать о граблях,  на которые наступил за последние полгода в выковыривали сервиса из монолита. Много боли, слез и крови.

—————————————

Four things about Pry – Runtime Revolution
Так сложилось, что у меня натянутые отношения с pry.rb. Не смог использовать на постоянной основе, а сейчас уже сложно переучиться. Об этом даже упоминал на прошлом стриме. Но, статьи с tips&amptrics об инструментах люблю.

После прочтения прокачаете чуть больше pry skill, а после сможете удивлять коллег и быстрее проверять код в REPL.

—————————————

Markov Chains
Статья с реалтайм примерами в браузере, в которой рассказывается о цепях маркова, что это и как работают. Я не писал такие цепи до этого, возможно стоит добавить цепи в список проектов для стрима (если хотели бы видеть разбор и код цепей стриме - пишите об этом).

—————————————

Личный опыт: как не копить закладки и успевать всё читать
Давно не было статей в закладки. Добавить нечего. Скажу честно, эту ссылку я пытался прочитать 5 раз и так и не осилил. Было бы круто устроить интерактив и собрать других советов. Поэтому, расскажите, как справляетесь с тысячами вкладками и покетом на несколько сотен статей? Для затравки поделюсь статьей @vanadium23 о том, как держать покет в чистоте:

Держи кармашек в чистоте
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 728
                    [ViewsLastDateUpdated] => 2018-12-17 07:27:11
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 617
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [5077012] => stdClass Object
                (
                    [ID] => 5077012
                    [TgID] => 107
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-11-16 16:45:14
                    [Text] => Пятничное чтиво

Functional architecture is Ports and Adapters
Статья пример того, как переплетаются функциональные подходы с архитектурными паттернами. Основная часть текста - рассуждение с примерами на тему того, как же использовать чистые и не чистые функции в хаскеле или F# и как это похоже на подход проектирования систем с помощью адаптеров и портов. Интересно будет для расширения кругозора и как пример взаимодействия и дополнения подходов в функциональных языках и архитектурах систем.

Русский перевод

—————————————

Cloud Diagrams &amp Notes

Список картинок о том, как работают сервисы AWS. Для меня это открытие недели, так как постоянно забываю что есть что или просто путаюсь в сервисах от амазона.

—————————————

How to teach yourself hard things

Учиться сложно, учиться продуктивно сложно вдвойне. Автор статьи делиться советами о том, как учиться продуктивнее. Совета четыре, но каждый может кардинально изменить подход к обучению.

Добавлю, что кроме упора на продуктивность, стоит не забывать о перерывах для отдыха. Иногда перерыв длится пару часов, иногда недели. Важно в такие моменты не корить себя, а дать голове переварить информацию.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1100
                    [ViewsLastDateUpdated] => 2018-12-17 12:47:33
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 1100
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [5040383] => stdClass Object
                (
                    [ID] => 5040383
                    [TgID] => 106
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-11-14 14:44:22
                    [Text] => так же, подсказали важную мысль на счет самоутверждения и доверия, дополнил тексты
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1000
                    [ViewsLastDateUpdated] => 2018-12-17 05:26:59
                    [Views1] => 470
                    [Views2] => 499
                    [Views3] => 696
                    [Views7] => 773
                    [Views30] => 991
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [5040382] => stdClass Object
                (
                    [ID] => 5040382
                    [TgID] => 105
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-11-14 14:15:05
                    [Text] => На работе попробовал новую для себя практику post review. Делюсь опытом.

Medium
Telegraph
MindNode
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 998
                    [ViewsLastDateUpdated] => 2018-12-17 05:27:00
                    [Views1] => 496
                    [Views2] => 539
                    [Views3] => 707
                    [Views7] => 783
                    [Views30] => 984
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [5040381] => stdClass Object
                (
                    [ID] => 5040381
                    [TgID] => 104
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-11-09 15:40:06
                    [Text] => Пятничное чтиво

Maintaining 65k open connections in a single Ruby process
Соеденив вместе puma, rack и hijacking получим развлечение на вечер и статью, в которой автор подробно рассказывает как с помощью этих вещей добиться обработки 65523 открытых коннекшенов. В конце статьи - ссылка на гист с полным кодом (всего 55 строк кода с комментариями).
Из проблем: такой код съедает 350 мб памяти, что много (nginx для примера потребляет 2.5 мб), race conditions проблемы (поправились с помощью использования http://amp.gs/vXFN и http://amp.gs/vXFA).
Если хотите разобраться как подобные штуки работают и как еще можно ускорить приложение - маст рид.

—————————————

Architecture of a high performance GraphQL to SQL engine
В первый раз, когда увидел graphQL подумал, что было бы круто получить сервис с 1 в 1 доступом к базе. Спустя 3 года появилась статья, которая описывает как работает Hasura, сервер-прослойка между клиентами и постгресом. Стоит обратить внимание, что сервис написан на хаскеле, по бенчмаркам быстрее prisma и postgraphile. Надеюсь что подобные сервисы смогут в будущем элегантно решить проблему кастомной логики.

Русский перевод

—————————————

Six Great Reasons to Build Service Architectures in Ruby with Eventide
За eventide слежу давно. Даже упоминал этот проект пару раз в выступлениях. Для тех кто не в курсе, eventide - тулкит для построения ES систем на руби. Много фич, огромная и полная документация. В статье выше описываются 6 причин, почему eventide стоит попробовать. Для меня причины спорные, но для ознакомления стоит прочитать.

Рад, что таких проектов становится больше. Так же стоит посмотреть на sequent и RailsEventStore. Круто, что библиотеки не просто появляются, но и развиваются. А так же, появляется больше ES ориентированных конференций в мире руби.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1400
                    [ViewsLastDateUpdated] => 2018-12-17 05:27:00
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 1100
                    [Views30] => 1400
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [5040380] => stdClass Object
                (
                    [ID] => 5040380
                    [TgID] => 103
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-11-02 15:45:06
                    [Text] => Пятничное чтиво

Microservices Are Something You Grow Into, Not Begin With — Nick Janetakis
Давно задумываюсь о том, что микросервисы - архитектурный блокчейн. Считаю, что это кейворд, который усложняет стартапы и полезен только гигантам вроде гугла или в системах, где распределенная архитектура архи важна. На деле микросервисы приносят много новой, асинхронной боли (привет distributed мир), о которой больше говорят. Как пример, segment, который мы используем на работе, поделился историей не обдуманного использования микросервисной архитектуры.

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

Перевод на русский.

—————————————

Why You Should Never Use MongoDB
Люблю истории, особенно истории провалов. В статье выше, ребята из диаспоры рассказывают о том, как начинался проект, почему выбор пал на монгу и как потом перенесли данные в постгресс. В истории идеально все. Понравился опыт хранения социальных данных и способы работы с ними.

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

Перевод на русский.

—————————————

Concurrent Ruby with Tasks · software is fun
Асинхронный код - боль. Использование concurent-ruby в бизнес логике - двойная боль. В функциональных языках для подобных вещей используется Task монада, в js - promises. @flash_gordon сделал реализацию подобной монады в dry-monads, а в статье выше описано зачем Task нужен, как пользоваться и приводится примеры работы с Do нотацией.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-12-17 05:27:00
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 1100
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4937544] => stdClass Object
                (
                    [ID] => 4937544
                    [TgID] => 102
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-10-26 15:45:02
                    [Text] => Пятничное чтиво

Seven Microservices Anti-patterns
В интернете слишком много информации о пользе микросервисов и почему это круто. Но лично мне не хватает информации об ошибках, которые совершаются в таких системах. Поэтому сегодня - 7 паттернов, которые помогут упростить создание и поддержку подобной архитектуры.

Совет о гейтвее (Building a gateway in every service) зарезонировал с внутренним архитектором. Причина в том, что в системах о которых читал или слышал, клиенты вызывали сервисы в рандомном порядке. Это приводит к сложности: один сервис  начинает вызывать неограниченное количество других сервисов, которые по цепочки вызывают друг друга. Сервисы начинают знать друг о друге, что может влиять на изоляцию и поддерживаемость кода. Элегантное решение проблемы - gateway прослойка, которая последовательно вызывает нужные сервисы и с которой обращаются другие клиенты. 

HTTP/2 for Ruby Web Development
Еще один “modern asynchronous HTTP/2 capable web server” для руби. Что понравилось: чистый руби, может в  http/2, поддерживает rack приложения. К тому же, ребята с ходу пишут, что продакшен код, использ хоть сейчас.
Но к сожалению к этой штуке хватает и вопросов: как быстро работает? Что с памятью? На сколько сервер готов к продакшену?

Поэтому будет интересно посмотреть, во что превратиться проект, а также, попробовать запустить Falcon в своих проектах.

Understanding user support systems in open source
Open source проект в первую очередь продукт, а не открытый код. Это значит, что у проектов есть пользователи. А где люди, там ответы на вопросы и решение проблем. В статье выше описывается как построить систему помощи пользователям.

Как мейнтейнер - круто, что таких статей становится больше, потому что, в мелких и средних OSS проектах не хватает людей, которые понимают, как улучшить жизнь проекта и людей вокруг него. Возможно в этом причина не оптимальных процессов релизов, работы и анбординга (если такие процессы есть). Или сложностей с получением помощи, а также неочевидные фичи, которые пытаются сделать мейнтейнеры. Так что, если хотите пойти в менеджеры, а на работе не пускают - попробуйте помочь open source проекту со внутренними процессами.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-12-01 11:41:09
                    [Views1] => 516
                    [Views2] => 540
                    [Views3] => 587
                    [Views7] => 686
                    [Views30] => 686
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4903067] => stdClass Object
                (
                    [ID] => 4903067
                    [TgID] => 101
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-10-19 15:20:11
                    [Text] => Пятничное чтиво

Целостность данных в микросервисной архитектуре 
Архитектурная статья из авито о том, как сохранить данные в микросервесной архитектуре с зоопарком баз данных.  Рассматривается ACID принцип в связке с микросервисами. Для обеспечения целостности предлагается использовать сага паттерн. Вообще, сага паттерн не заслуженно пропускается в мире руби разработчиков, хотя в некоторых моментах может сильно облегчить жизнь, когда приходится делать бизнес транзакции со сторонними сервисами. Ну и ссылка из ссылки о паттерне в придачу.

Am I doing it wrong?
Cервис объекты приходили в rails тяжело и вызывали много боли у людей привыкшим к rails way (исправте меня, если не прав). В гисте выше разработчик, которого уволили за сервис объект в проекте, спрашивает правильно ли он сделал и как с этим жить дальше. Из интересных комментаторов: Avdi Grimm, Mike Perham, Nick Sutterer, Piotr Solnica и DHH.

How to Read Source Code
Мне сложно читать чужой код. Это отнимает силы, время и мотивацию. К сожалению, заниматься этим приходиться часто, поэтому статьи с идеями, как упростить этот процесс - ❤️.

Добавлю два совета по чтению чужого кода:
- когда документация желает лучшего, смотрите в интеграционные тесты
- если нужно быстро разобраться что и как библиотека делает - дебаггер выручает. В руби открываем исходники (bundle open &ltgem name&gt) ставим дебагер и проходим, смотря что происходит
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-11-21 10:04:08
                    [Views1] => 543
                    [Views2] => 553
                    [Views3] => 599
                    [Views7] => 696
                    [Views30] => 1200
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4869949] => stdClass Object
                (
                    [ID] => 4869949
                    [TgID] => 100
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-10-12 15:20:11
                    [Text] => Пятничное чтиво

User-defined Order in SQL
Сортировка, заданная пользователем не тривиальная задача. На каждый случай в голову приходит тысячи вариантов решения проблемы. В тексте показано, как реализовать алгоритм пользовательской сортировки дешево, используя толкьо одно поле в базе. Если задумали сделать аналог трелло или понадобилась подобная сортировка - однозначный маст хев.

Что бы не спойлерить решение, предлагаю игру. Напишите на бумаге решение, а потом прочтите статью и сравните решения. Если решение кардинально отличается - присылайте ссылку на гист с описанием, опубликую.

Testing best practices
Обожаю гитлаб за открытость. Два года назад, забрал кучу полезных идей из плейбука, а после - сделал описание hanami-paybook по аналогии с гитлабовским. Сегодня - мануал о тестировании в рельсе. Зацепил fast&#092_spec&#092_helper.rb,  хочу попробовать использовать похожую практику в своих проектах. Так же, будет интересно тем, кто хочет узнать, как тестировать большие приложения и где можно срезать время выполнения тестов.

Why Funding Open Source is Hard
На RubyRussia в первый раз проводились круглые столы о сервисах, найме и OpenSource. При обсуждении OSS поднялась тема финансирования продуктов с открытым исходным кодом. Из-за этого, вспомнилась статья о том, почему так тяжело привлекать средства в опенсорс.

В статье подробно описываются возможные виды финансирования, приводится пример проекта Code Sponsor, а так же задаются вопросы, почему же это так сложно и что с этим делать.

Русский перевод на хабре.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1300
                    [ViewsLastDateUpdated] => 2018-11-18 00:53:23
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 784
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4869948] => stdClass Object
                (
                    [ID] => 4869948
                    [TgID] => 99
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-10-11 17:15:12
                    [Text] => После открытого стола об open source проектах на rubyrussia задался вопросом, а какие проблемы испытывают люди. Я нашел четыре разных группы лиц, проблемы которых сильно отличаются. Таким образом появился эта короткая форма. Буду рад  вашим ответам, а результаты опубликую в ближайшее время.

https://goo.gl/forms/LPHPIYWJzUUkjqF52
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1100
                    [ViewsLastDateUpdated] => 2018-11-18 00:53:24
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 829
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4751488] => stdClass Object
                (
                    [ID] => 4751488
                    [TgID] => 96
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-10-05 15:10:14
                    [Text] => Пятничное чтиво

В прошлый раз пропустил из-за розыгрыша. Сегодня исправляюсь.

Software Architecture Patterns Book
Бесплатная книга о различных архитектурных паттернов систем. Подробно расскажет о пяти видах архитектур:

- Layered architecture
- Event-driven architecture
- Microkernel architecture
- Microservices architecture
- Space-based architecture

Хочу обратить внимание на вторую главу, в которой описывается построение Event-Driven архитектуры. Приводится в пример две топологии, mediator и broker. Идея в том, что в mediator топологии кастомер кладет инициирующий эвент в общую очередь эвентов, а медиатор уже организует события по определенным каналам. В broker топологии используется обратный подход: эвент обрабатывается распределенно и при каждой обработке могут добавляться новые события в очередь эвентов.

DDD for Rails Developers. Part 1: Layered Architecture
DDD набирает обороты в мире руби. Слоистые архитектуры покоряют умы разработчиков. В статье доступное описание зачем это нужно, как сделать и что это такое. Много очевидных, но полезных советов (изоляция AR и бизнес логики)

Soft Skills: The software developer’s life manual
Список софт скилов, которые могут помочь разработчику (и не только) улучшить качество жизни. Из списка, хотелось бы обратить внимание на “Marketing yourself”. По моему, это один из навыков, которому в россии, к сожалению, нигде не учат (если не прав - напишите пожалуйста). На эту тему есть статья из лайвхакера), читать на свой страх и риск.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1700
                    [ViewsLastDateUpdated] => 2018-11-02 04:09:00
                    [Views1] => 689
                    [Views2] => 758
                    [Views3] => 810
                    [Views7] => 1400
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4718929] => stdClass Object
                (
                    [ID] => 4718929
                    [TgID] => 95
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-10-01 15:50:05
                    [Text] => Перед конференцией, хотелось бы добавить организационных моментов.

Мероприятия

4.10 - др Maciej Mensfeld (http://amp.gs/h7ia)), идем отмечать и пить пиво в RULE taproom в 20:00
5.10 - препати конференции, RULE taproom 20:00
6.10 - конференции и афтепати
7.10 - воркшопы (если найдутся силы - можем устроить афтепати для афтепати)

Нетворкинг
Хотелось бы организовать нетворкинг, чтобы людям проще было общаться на конференции. Если у вас есть идеи как это лучше сделать - пишите в личку @davydovanton

Доклады которые понравились
В этом году слушал прогоны некоторых докладов. Из того, что понравилось и что рекомендую к посещению

1. Эволюция rails архитектуры. Просто маст хев. Доклад не технический, но интересно послушать как github, cookpad и basecamp  живут с рельсой. Кроме того, на докладе будет розыгрыш (но это не точно)
2. Фичафлаги. Использую этот подход на работе и читал фаулера, но даже с таким багажом узнал много нового. Если испытываете проблемы с доставкой фич в прод - маст хев
3. Mruby, слежу за проектом уже 3 или 4 года. Искренне рад, что такие доклады появляются. Узнаете что это, зачем и где использовать
4. Мониторинг. Мониторинг не самая популярная тема в руби сообществе, узнаете на что смотреть в первую очередь, а так же будет анонс новой библиотеке марсиан

Что еще
Мой доклад будет сильно пересекаться с темами из Designing Data-Intensive Applications (DDIA). Поэтому хочу разыграть эту книгу. Подробности на докладе.

Стикеры&#33 С радостью возьму и раздам.

До встречи&#33
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-10-29 17:49:34
                    [Views1] => 484
                    [Views2] => 515
                    [Views3] => 536
                    [Views7] => 924
                    [Views30] => 1200
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4674033] => stdClass Object
                (
                    [ID] => 4674033
                    [TgID] => 92
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-28 14:24:14
                    [Text] => https://prnt.sc/kzrn0a
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 900
                    [ViewsLastDateUpdated] => 2018-10-21 05:51:47
                    [Views1] => 467
                    [Views2] => 502
                    [Views3] => 520
                    [Views7] => 687
                    [Views30] => 900
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4674034] => stdClass Object
                (
                    [ID] => 4674034
                    [TgID] => 93
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-28 14:24:14
                    [Text] => Всё куплено&#3311
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 911
                    [ViewsLastDateUpdated] => 2018-10-21 05:51:47
                    [Views1] => 463
                    [Views2] => 497
                    [Views3] => 515
                    [Views7] => 690
                    [Views30] => 911
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4674032] => stdClass Object
                (
                    [ID] => 4674032
                    [TgID] => 91
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-28 14:10:57
                    [Text] => Как и обещали, вместе с @wi11son разыграли 2 билета на конференцию. Наши победители: @hana1995 и @asmorozov. Организаторы напишут вам по поводу билетов.

Спасибо что поучаствовали и поделились тем, чего не хватает на конференции. Некоторые вещи уже добавлены (например воркшопы), а некоторые будут в следующем году&#33 

Полное видео с записью того, как и что делали тут:
https://youtu.be/FI7Fa55VrjQ
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1000
                    [ViewsLastDateUpdated] => 2018-10-21 05:51:46
                    [Views1] => 607
                    [Views2] => 653
                    [Views3] => 684
                    [Views7] => 816
                    [Views30] => 1000
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4674031] => stdClass Object
                (
                    [ID] => 4674031
                    [TgID] => 90
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-25 16:38:02
                    [Text] => Hапомню, что в эту пятницу мы разыгрываем два билета  на предстоящую конференцию. Если не успели записаться - самое время&#33

Пока учавствуют 100 человек, а это значит, что шансы получить билет высоки

http://amp.gs/hthb
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-10-21 05:51:46
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 890
                    [Views7] => 989
                    [Views30] => 1200
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4629042] => stdClass Object
                (
                    [ID] => 4629042
                    [TgID] => 89
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-24 15:30:06
                    [Text] => У меня больше 30 репозиториев в гитхабе, за которыми надо следить и оперативно закрывать ишю (или отвечать на них). Так как эти репозитории в разных организациях, то постоянно бегать между ними - гиблое дело. Могли бы спасти нотификации, но к сожалению с ними не увидить картину целиком.

9 месяцев назад я решил, что хватит это терпеть и начал делать прототип проекта, который помог бы решить описанную выше проблему. Но к сожалению 3 месяца назад я забросил это проект так и не положив его в публичный доступ. Поэтому проект теперь публичный и на heroku, жду фидбэк и предложения, что бы понимать, что с этим дальше делать. Куча не доделанных вещей, багов и вырвигразный дизайн прилогаются. 

Сам проект

Доска с моими прокетами и ишю, которые хотелось бы закрыть (можно с этим помочь)

Гитхаб - davydovanton/cookie&#092_box. В гитхабе примеры того как hanami дружит с dry-system и как работают do-notation на практике. А так же пример представления поддерживаемой архитектуры и пример использования веб хуков гитхаба.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1400
                    [ViewsLastDateUpdated] => 2018-10-17 03:09:38
                    [Views1] => 483
                    [Views2] => 921
                    [Views3] => 970
                    [Views7] => 1200
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4629041] => stdClass Object
                (
                    [ID] => 4629041
                    [TgID] => 88
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-21 15:40:07
                    [Text] => Пятничное чтиво


A guide to logging in Java
Полноценный гайд о том, как использовать логгер в приложении. В качестве примера используется java и разные виды логгеров. Меняем  java на Any Lang - получаем маст хев чтиво.

Operations For Developers
Я плохо разбираюсь в том, как администрировать и деплоить приложения. Если испытываете похожие проблемы - Operations For Developers расскажет о том, что такое контейнеры, как мониторить приложения, логгировать, обеспечить безопасность и другие полезные вещи.

Learning SQL Resources
Список ссылок для того, что бы изучить SQL, а так же PostgreSQL и MySQL. Добавлю, что PostgreSQL Exercises сильно помог разобраться в сложных запросах и на него стоит потратить время.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-10-17 03:09:52
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 1100
                    [Views7] => 1300
                    [Views30] => 1600
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4629040] => stdClass Object
                (
                    [ID] => 4629040
                    [TgID] => 87
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-18 15:34:50
                    [Text] => Благодаря ребятам из Saint P Ruby User Group (https://t.me/saintprug) количество билетов увеличивается в 2 раза (2 вместо одного). Что это значит? шансы на победу увеличиваются в 2 раза&#33
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-10-17 03:09:37
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 1300
                    [Views30] => 1600
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4629039] => stdClass Object
                (
                    [ID] => 4629039
                    [TgID] => 86
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-18 15:24:20
                    [Text] => Pepegramming второй год подряд является информационным партнером конференции http://amp.gs/yXWI (ex railsclub). Это значит, что в этом году опять разыгрываем билеты.

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

Хочется, что бы в этом году участников было больше, а условия — одинаковые для каждого. Поэтому в этом году нужно просто заполнить форму и я случайным образом выберу победителя (с пруфами в виде видео).

Условия:

- Заполнить форму
- Рассказать друзьям
- 28 сентября в 18:00, с помощью рандома выберу победителя
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1900
                    [ViewsLastDateUpdated] => 2018-10-17 03:09:38
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 1500
                    [Views30] => 1900
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4536629] => stdClass Object
                (
                    [ID] => 4536629
                    [TgID] => 81
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-14 12:21:57
                    [Text] => Ну а для тех, кто хочет поучиться на чужих ошибках - гитхаб репозиторий со списоком post-mortems разных компаний:

https://github.com/danluu/post-mortems
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-10-09 20:04:59
                    [Views1] => 
                    [Views2] => 526
                    [Views3] => 542
                    [Views7] => 1200
                    [Views30] => 1600
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4536628] => stdClass Object
                (
                    [ID] => 4536628
                    [TgID] => 80
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-14 12:13:12
                    [Text] => Пятничное чтиво.

В далеком 2015 году, грезил дочитать SICP до конца, но к сожалению, с 3 раза дочитал только до главы об environments, frames и bindings. (3.2, если правильно помню). Для того что бы хоть как-то понять что это и зачем - решил написать интерпретатор scheme. В интернете нашел аналог на питоне и сделал подобное на руби.

В итоге получилось страшное, но рабочее решение, которое помогло лучше понять главу и работу языков программирования.

Если вы застряли там же - надеюсь пост с примером реализации интерпретатора scheme сможет помочь понять тему лучше.

Write simple scheme interpreter on ruby
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-10-09 20:04:41
                    [Views1] => 
                    [Views2] => 456
                    [Views3] => 467
                    [Views7] => 897
                    [Views30] => 1200
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4536627] => stdClass Object
                (
                    [ID] => 4536627
                    [TgID] => 79
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-13 11:21:56
                    [Text] => Люблю открытые компании. Чем больше опыта получишь не работая в компании - тем лучше. Политика открытости (даже если говорить только о техническую часть) помогает инженерам получить бесплатный опыт, а это сразу вес в комьюнити и хорошее отношение к компании со стороны. В этом плане gitlab персональный фаворит, так как плейбук натолкнул на кучу идей, а код  платформы позволил найти решения  собственных проблем.

На днях, коллега поделился видео со внутренней презентации додо пиццы.

Стабильность Dodo IS

В презентации рассказывается о том, как решались проблемы, возникшие после распила монолита на сервисы. Доклад не претендует на сокральные знания, да и 5 минут не позволяют вставить кучу технических фактов. В докладе важно то, что после презентации хочется посмотреть какие технологии используется в додо. Спустя пару минут поиска был найдет инженеринговый сайт и могу сказать, что он цепляет:

О том, как устроено IT в Додо Пицца

Понравилась диаграмма сервисов с описанием основного стека и напоминание, что компания - продуктовая и продукт можно и нужно потрогать:

&gt Надоело писать код – можешь сходить в пиццерию, покрутить додстеры или постоять на кассе.

Ну а если интересно посмотреть на процессы работы в ресторанном бизнесе (заказ пиццы) стоит почитать серию статей о работе информационной системы в додо:

Dodo IS. Часть III. Всё о заказе - Сила ума
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1100
                    [ViewsLastDateUpdated] => 2018-10-09 20:04:42
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 761
                    [Views7] => 932
                    [Views30] => 1100
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [4536626] => stdClass Object
                (
                    [ID] => 4536626
                    [TgID] => 78
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-09-10 11:02:49
                    [Text] => В одной статье обратил внимание на упоминание the knowledge loop . Если кратко, то петля состоит из 3 элементов:

изучить -&gt создать -&gt поделиться

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

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

Из текущей работы:

* Data intensive applications и все что касается данных в продуктах. Год назад пришло осознание, что код это не важно, а бизнес логика - важно. Теперь пришел к тому, что данные и работа с данными &gt бизнес логика. Из-за этого, интересно как работать с данными, а так же  как хранить и как обеспечить оптимальную эволюцию в проекте
* По работе отковыриваю сервис из монолита. Это первый шаг к сервисной архитектуре, в котором хочется получить как можно больше шишек. В планах написать статьи в блог как документацию к проекту
* Из-за полного контроля отковыривая сервиса нашел у себя много пробелов не технических умениях. Из-за этого приходится читать много книг связанных с soft skills, бизнесом и головой
* В качестве опыта, решил попробовать написать реальный продукт. Хочу начать глубже понимать тех, на кого работаю. Планирую закончить mvp и сделать приватную бету на https://rubyrussia.club. Об этом тоже напишу, но позже
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1000
                    [ViewsLastDateUpdated] => 2018-10-09 20:04:40
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 807
                    [Views30] => 1000
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [3800026] => stdClass Object
                (
                    [ID] => 3800026
                    [TgID] => 77
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-06-08 19:23:24
                    [Text] => В последние время очень много путешествий (26 часов из японии в москву чего стоят), поэтому пишу мало. Зато, если вы 10 числа будете в питере на конференции (https://www.meetup.com/saintprug/events/249120635/) - говорите привет. К тому же у меня есть ограниченное количество крутых наклеек :)
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 754
                    [ViewsLastDateUpdated] => 2018-07-23 13:18:49
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 525
                    [Views30] => 673
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [3609736] => stdClass Object
                (
                    [ID] => 3609736
                    [TgID] => 76
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-05-21 12:06:21
                    [Text] => Написал о еще одном способе создания callable objects - Do notations из dry-monads. А так же, почему dry-transactions не вытягивают.

Medium
Telegraph
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-07-04 18:50:30
                    [Views1] => 434
                    [Views2] => 442
                    [Views3] => 474
                    [Views7] => 516
                    [Views30] => 1100
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [3557737] => stdClass Object
                (
                    [ID] => 3557737
                    [TgID] => 75
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-05-11 11:36:44
                    [Text] => Хорошие статьи - редкость, а хорошие статьи по тестированию - двойная редкость.

13 антипаттернов тестирования. Сложно выделить один антипаттерн, который понравился. Поэтому советую прочитать о каждом отдельно.

http://blog.codepipes.com/testing/software-testing-antipatterns.html

Русский перевод: https://habr.com/post/358178/
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1500
                    [ViewsLastDateUpdated] => 2018-06-05 00:36:54
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 610
                    [Views30] => 1500
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [3557736] => stdClass Object
                (
                    [ID] => 3557736
                    [TgID] => 74
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-05-08 13:19:04
                    [Text] => Тут ребята из @saintprug выложили запись hanami воркшопа. Пара причин почему стоит посмотреть (советую на 1.25-1.5):
- посмотреть на то, как работает фулстек ханами
- реальные примермы DI и почему это работает
- в конце второй части были вопросы на большое количество тем (эвенты, dry, архитектура)
- можно написать на почту и получить ссылки 😉

Код на то, что вышло в итоге: https://github.com/saintprug/spb_link_shortener

А по вопросам - пишите в личку или в @saintprug

Первая часть: https://youtu.be/KnSPR-F2nag
Вторая часть: https://youtu.be/DCMTyddUvio
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1700
                    [ViewsLastDateUpdated] => 2018-06-21 22:31:18
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 1100
                    [Views30] => 1600
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [3557735] => stdClass Object
                (
                    [ID] => 3557735
                    [TgID] => 73
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-05-07 13:38:54
                    [Text] => йо&#33 
По поводу последнего поста и планирования задач. У меня тоже адовый перегруз по задачам, поэтому я решил делать как делает Шон Бланк: 3 задачи которые сдеалют мой день успешным, 3 задачи которые сделают неделю успелшной + записываю мотивацию почему мне надо сделать именно эти задачи и что они принесут. 3 неделя пошла, в привычку ещё не вошло, но уже результаты есть небольше: проще стало немношк

http://macshifford.me/all-the-things-pro/ - тут чёта тоже написывал про это
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-06-18 14:23:12
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 1100
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [3557734] => stdClass Object
                (
                    [ID] => 3557734
                    [TgID] => 72
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-05-07 12:21:46
                    [Text] => Новая неделя (месяц) - новый текст, сегодня о не техническом но важном.

А что вы обычно делайте когда не прет?

Медиум
Телеграф
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 789
                    [ViewsLastDateUpdated] => 2018-06-18 14:23:12
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 738
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [3193515] => stdClass Object
                (
                    [ID] => 3193515
                    [TgID] => 71
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-04-12 15:05:02
                    [Text] => Решил собрать немного статистики, поэтому, если вам не сложно - пройдите пожалуйста опрос, 11 вопросов, которые помогут лучше понять на что обращать внимание в следующем проекте 🙂

https://goo.gl/forms/weQbVYOJGKpMmqG73
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-05-23 22:28:21
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 534
                    [Views30] => 1200
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [3193514] => stdClass Object
                (
                    [ID] => 3193514
                    [TgID] => 70
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-04-12 12:05:55
                    [Text] => Стоит ли использовать еще одну платформу для текстов, кроме медиума?

Да, телеграф – 90
👍👍👍👍👍👍👍 49%

Нет, медиума хватит – 86
👍👍👍👍👍👍👍 47%

Да, свое (напишу @davydovanton лично) – 5
▫️ 3%

Да, яндекс дзен – 3
▫️ 2%

👥 184 people voted so far.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-05-23 22:33:04
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 578
                    [Views30] => 1200
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [3193513] => stdClass Object
                (
                    [ID] => 3193513
                    [TgID] => 69
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-04-10 12:35:55
                    [Text] => На прошлой неделе провел вокшоп в питере, спасибо ребятам из @saintprug за теплый прием. Поэтому сегодня - мысли и советы, как сделать что-то подобное. Хочется верить, что в будущем в россии будет все чаще и чаще встречаться такой формат (организаторы конференций - это ваш шанс 😉).

https://medium.com/pepegramming/workshop-adbe753f2df4
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-05-23 22:33:04
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 1100
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [3193512] => stdClass Object
                (
                    [ID] => 3193512
                    [TgID] => 68
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-04-10 12:32:25
                    [Text] => Привет, на улице наконец-то стало тепло, поэтому радостные новости. Я наконец-то переехал на medium. Теперь все посты будут выходить там.
В течении ближайшего времени я перевезу все тексты туда, так что подписывайтесь и рассказывайте друзям:
https://medium.com/pepegramming
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 904
                    [ViewsLastDateUpdated] => 2018-05-23 22:33:05
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 835
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2874124] => stdClass Object
                (
                    [ID] => 2874124
                    [TgID] => 67
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-26 13:07:03
                    [Text] => Плюсы

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

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

Минусы

Главная проблема - трата времени на обсуждение задачи. Этого могут не понять менеджеры, которые считают “нет кода - нет работы”. Старайтесь говорить с менеджерами и объяснять смысл таких обсуждений. 

Такой подход не работает в команде из одного человека. А сам процесс груминга энергоемок, поэтому не ждите, что сможете загрумить все за один день.

И  главное - груминг требует командной дисциплины.

Советы

- Ведите записи. А в конце задачи выделяйте время на обновление документации. Это поможет в поддержке документации к проекту
- Начните с культуры и помощи коллегам. Не везде практикуются подобные обсуждения. Хотите исправить это - подойдите и предложите поговорить о задаче коллеги. После чего попросите помощи сами
- Один груминг - одна тема. Также, желательно ограничивать обсуждения по времени и количеству участников. Не тащите команду, хватит только тех, кто будет работать над задачей
- Делайте груминги по необходимости. Не тратьте время коллег в пустую

Запомнить

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

Ссылки
- MindNode со всеми идеями
- Product Backlog Grooming Best Practices
- Версия для покета
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1300
                    [ViewsLastDateUpdated] => 2018-05-05 01:18:02
                    [Views1] => 475
                    [Views2] => 483
                    [Views3] => 483
                    [Views7] => 579
                    [Views30] => 1200
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2874123] => stdClass Object
                (
                    [ID] => 2874123
                    [TgID] => 66
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-26 13:07:03
                    [Text] => #quality

Backlog Grooming - понятие из scrum, смысл которого в том, что команда собирается и решает что будет делать дальше и ставит эстимейты задачам. Проводится 1-2 раза в спринт в назначенное время. Но к сожалению, у нас, в какой-то момент возникли проблемы с PR:

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

Решение проблем оказалось банальным, появились “technical groomings” (название взято из головы, если вы знаете как этот процесс называется официально - пишите в личку).  Смысл в том, чтобы позволить технической стороне обсудить формальный алгоритм задачи, разбить задачи на атомарные задачи и заэстимейтить каждую из задач.

Формальный алгоритм

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

Разбивка на атомарные задачи
Правила, которых стараюсь придерживаться:

- 1 задача == 1 RP
- Работающий код, потом рефакторинг

Также, планируйте рефакторинг и закрытие технических долгов либо перед началом работы, либо после. Если одна задача из списка блокирует другую - стоит задуматься и попробовать вынести то, что блокирует - в отдельную задачу. Яркий пример такой задачи: добавить эндпоинт для фронтенда. Минимум два варианта, так решить проблему:
- 1 большой PR в котором будет и логика и эндпоинт. Много кода и сложно ревьювить
- 2 PR-а. быстро сделать “пустой” эндпоинт для фронтенда, а потом потратить время на логику. Так разработчик разблокирует коллег, потом сделает остальную работу
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 886
                    [ViewsLastDateUpdated] => 2018-05-05 01:18:09
                    [Views1] => 500
                    [Views2] => 505
                    [Views3] => 505
                    [Views7] => 577
                    [Views30] => 866
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2742969] => stdClass Object
                (
                    [ID] => 2742969
                    [TgID] => 65
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-19 12:29:56
                    [Text] => и ссылкой в браузер
https://my.mindnode.com/SfeToxQtx9dv2b2n8scQq6N7HqxWPAhzSTJweLBW
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 939
                    [ViewsLastDateUpdated] => 2018-04-29 19:06:53
                    [Views1] => 315
                    [Views2] => 357
                    [Views3] => 383
                    [Views7] => 474
                    [Views30] => 896
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2742968] => stdClass Object
                (
                    [ID] => 2742968
                    [TgID] => 64
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-19 12:26:47
                    [Text] => и майдмап для лучшего запоминания
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 881
                    [ViewsLastDateUpdated] => 2018-04-29 19:27:45
                    [Views1] => 327
                    [Views2] => 369
                    [Views3] => 396
                    [Views7] => 481
                    [Views30] => 836
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2742967] => stdClass Object
                (
                    [ID] => 2742967
                    [TgID] => 63
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-19 12:23:50
                    [Text] => ссылка для покета:
https://gist.github.com/davydovanton/bb504c423e958470ca639dbc086ad2dc
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 794
                    [ViewsLastDateUpdated] => 2018-04-29 19:27:47
                    [Views1] => 328
                    [Views2] => 367
                    [Views3] => 393
                    [Views7] => 476
                    [Views30] => 751
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2742965] => stdClass Object
                (
                    [ID] => 2742965
                    [TgID] => 60
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-19 12:23:06
                    [Text] => В программировании много абстракций, которые кажутся сложными на первый взгляд, но после практики становятся понятнее. Например - монады.

Я видел разработчиков, которые бояться использовать монады, а код с использованием монад - сжигают на месте. Основная причина - убеждение, что для этого необходимо знать теорию категорий и хаскель, да и зачем лишние абстракции в проекте. Другие не понимают зачем эту абстракцию тащить в руби.  При этом, люди используют монады каждый день и не подозревают этого (ссылки). Поэтому сегодня поговорим о практическом использовании монад в руби и зачем это нужно.

Не значит, что монады - серебряная пуля, которая решит любую проблему. Так же, это не значит, что использование монад - единственно верный способ написания кода.

Поэтому, цель текста - не объяснить что такое монада, а показать, как и где начать ее использовать . Не ждите функторов, аппликативных функторов, математических выкладок и подробного объяснения зачем каждая монада нужна. Только практика и императивное объяснение. Статьи для любопытных:

* Объяснение функторов и монад в картинках
* Объяснение в HaskellWiki
* A Fistful of Monads - Learn You a Haskell for Great Good&#33


Код

Встречаются места когда приходится работать с данными и состояниями, например валидны данные или нет, вернула бд данные и если да - какие это данные, etc. Например:

response = http.get(url, params)
if response[:status] == :success
  user_repository.create(response[:body])
end


Пример не выглядит сложным, но с бизнес логикой - вложенность выходит из под контроля:

response = http.get(url, params)
if response[:status] == :success
  validation_result = validator.call(response[:body])

  if validation_result.valid?
    if user = user_repository.create(response[:body])
      NotificationWorker.perform_async(user.id)
    end
  else
    validation_result.errors
  end
else
  response
end


Вариант с гардами мне показался сложнее для восприятия

Вспоминая railway programming, было бы здорово переписать наш пример с использованием последовательных шагов:

step :get_response # returns response or Failed result
step :validate # returns payload or error message
step :persist # returns user or nothing
step :notify # calls worker with user id


В случаях, когда данные из прошлого шага влияют на последовательность логики, приходят на помощь монады. Важно запомнить, что монада - объект с общим интерфейсом, в котором лежит значение. Ближайшая абстракция - коробка. В коробке лежит все, что поместиться. При этом, не открыв коробку - значение не получить. Коробки - разные, большие, маленькие, цветные, но каждая коробка открывается одинаково - просто подними крышку и посмотри что там.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 781
                    [ViewsLastDateUpdated] => 2018-04-29 19:27:51
                    [Views1] => 387
                    [Views2] => 419
                    [Views3] => 438
                    [Views7] => 515
                    [Views30] => 746
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2742966] => stdClass Object
                (
                    [ID] => 2742966
                    [TgID] => 61
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-19 12:23:06
                    [Text] => Монада - инструмент, который позволяет создавать цепочки вызовов функций или методов без лишних проверок данных, которые возвращаются в предыдущем шаге. Монад много, но рассмотрим только популярные - Maybe, Result и Try.

* Maybe - оборачивает значение в Some или возвращает None объект без значения. 

* Result - оборачивает значение в Success или Failure.

* Try - оборачивает вызов кода в Result если не было эксепшенов и в Error, если код упал с ошибкой (которая ловится)

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

* fmap - выполняет блок, если значение монады соответствует Success варианту, а результат выполнения блока оборачивает в ту же монаду, у которой он вызвался. Например: 

Some(1).fmap(&amp:to_s) # =&gt Some(1)
None().fmap(&amp:to_s) # =&gt Nothing


* bind - аналогичен fmap, только возвращается результат выполнения блока:

Some(1).bind(&amp:to_s) # =&gt 1
Some(1).bind { |value| Success(value) } # =&gt Success(1)
None().bind(&amp:to_s) # =&gt Nothing


В руби нет монад из коробки, но существуют гемы, которые реализуют монады:

* dry-monads
* kleisli
* tomstuart/monads

Советую dry, как единственную поддерживаемую. К тому же, при использовании dry-validation можно легко конвертировать результат валидации в монаду, воспользовавшись экстеншеном:

Dry::Validation.load_extensions(:monads)


Это минимум, который нужен, чтобы начать использовать монады в руби приложении. Для закрепления - перепишем изначальный пример с использованием монад:

http.get(url, params) # теперь клиент возвращается Result Monad
  # валидация возвращает Result, который используется для следующих вызовов
  .bind { |body| validator.call(body).to_result }
  # сохраняем в базу, если валидация вернула Success
  .bind { |payload| Maybe(user_repository.create(payload)) }
  # вызываем воркер, если сохранение вернет Some
  .fmap { |user| NotificationWorker.perform_async(user.id) }


Кроме использования монад в бизнес логике, попробуйте эту абстракцию для обработки результата, который возвращается из бизнес логики. Как пример - вызов operation из экшена и последующая обработка результата в этом же экшене: cookie_box/show.rb

Что делать с результатом

При использовании dry-monads можно:
- вызывать на прямую success?, failed? или value_or
- использовать "dry-matcher"
- мой любимый вариант, использовать "case"

Минусы

1. В отличии от условий (if, unless, etc) нельзя просто взять и использовать монаду. Если не знать в чем смысл абстракции и что значат bind и fmap - будет сложно понять код, который написан
2. Использование монад может сильно усложнить код. Спасает опыт, а опыт получается в практике
3.  Если хотите начать использовать монады в проекте, придется прорваться через ужас в глазах коллег (причина почему я написал этот текст)

Запомнить

* Монады - абстракция для чейна вызовов функций и следованию railway programming
* Для использования монад не нужно математическое образование. Главное понять, что монада оборачивает данные в объекты с единым интерфейсом
* Советую начать с  - Maybe, Result и Try
* fmap и bind - методы для чейна вызовов функций
* Чрезмерное использование монад усложняет код, будьте осторожны и подходите к написанию кода с умом

Полезное

* Как рефакторить руби код с монадами
* Algebraic Data Types &amp Monads in Ruby
* Monads and Ruby
* Railway Oriented Programming
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 877
                    [ViewsLastDateUpdated] => 2018-04-29 19:27:47
                    [Views1] => 379
                    [Views2] => 426
                    [Views3] => 452
                    [Views7] => 553
                    [Views30] => 831
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2742964] => stdClass Object
                (
                    [ID] => 2742964
                    [TgID] => 59
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-18 16:09:40
                    [Text] => @shiroginne поделился подробной воскресной статьей о вещах, которые стоит помнить во время ревью:
https://medium.com/@palantir/code-review-best-practices-19e02780015f

А @alexsubbotin поделился CONTRIBUTING.md файлом, дополненым после текста выше:
https://gist.github.com/KELiON/45cae7ac0eeeaa2a097116396c94b610
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 968
                    [ViewsLastDateUpdated] => 2018-04-24 13:13:07
                    [Views1] => 521
                    [Views2] => 565
                    [Views3] => 594
                    [Views7] => 642
                    [Views30] => 943
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2676509] => stdClass Object
                (
                    [ID] => 2676509
                    [TgID] => 58
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-12 13:23:33
                    [Text] => А каких правил вы придерживаетесь в ревью и на что смотрите в первую очередь? (обратная связь тут - @davydovanton)
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1100
                    [ViewsLastDateUpdated] => 2018-04-23 20:56:47
                    [Views1] => 
                    [Views2] => 454
                    [Views3] => 457
                    [Views7] => 823
                    [Views30] => 1000
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2676508] => stdClass Object
                (
                    [ID] => 2676508
                    [TgID] => 57
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-12 12:43:05
                    [Text] => А так же, версия для покета: https://medium.com/@anton_davydov/pepegramming-ревью-кода-e82794a8f05e
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1100
                    [ViewsLastDateUpdated] => 2018-04-23 20:56:47
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 477
                    [Views7] => 784
                    [Views30] => 994
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2676507] => stdClass Object
                (
                    [ID] => 2676507
                    [TgID] => 56
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-12 12:18:05
                    [Text] => Персональные выводы

Такой способ проверки кода трудозатратен, что значит дороже для бизнеса. Разработчик тратит больше времени на ревью, но при этом команда улучшает качество кода, увеличивается вовлеченность команды, из этого следует, что знания шарятся лучше (а это увеличение bus фактора).

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

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

Сложность ревью пропорциональна количеству кода. Чтобы справляться с этим, разбивайте работу на &quotатомарные&quot пулл реквесты, которые не превышают N строк (например - 200 для кода без тестов). Например, в опен сорс проектах, я стал просить людей разбивать работу над задачей на отдельные пулл реквесты

Для последнего шага ревью необходима внимательность, но с этим возникают проблемы у отдельных людей (ADHD напрмиер). При этом, базовые проверки, таких как стиль, грамматика и опечатки автоматизируются с помощью код стайла с rubocop, danger и codeclimate, который показывает, какие части пулл реквеста не покрыты тестами.

Как говорилось, главный минус такого подхода в повышенной энергозатратности для ревьюверов. Отсутствие настроения или желания, авралы и слабость - нормальные явления. Поэтому не стоит огорчаться, если не получается с первого, второго и даже десятого раза. Главное - регулярность и поддержка команды.

Не стоит писать комментарии на каждый из шагов разом. В идеале, если ревью не прошло шаг, стоит остановиться и подождать когда код &quotпойдет дальше&quot. Не стоит захламлять ПР, разработчик может не понять где проблемы, а где просто мысли или вторичные проблемы, которые опускаются.

# Запомнить

- понимание задачи &gt выполнение задачи &gt архитектура &gt текст
- работающий код &gt идеальный код
- поймите контекст и важность задачи, перед тем как смотреть код
- цените время разработчика, не просите исправлять то, что изменится
- меньше кода - меньше конгнетивная нагрузка для ревьюверов и быстрее процесс ревью
- автоматизируйте, если это возможно

# Полезное

- 11 советов, как улучшить ревью от IBM
- Еще советы, на этот раз от asana
- И еще советы
- Посмотрите, как Лука пишет описания к ПР-ам в ханами, у него есть чему научиться
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1000
                    [ViewsLastDateUpdated] => 2018-04-23 20:56:48
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 554
                    [Views7] => 751
                    [Views30] => 955
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [2676506] => stdClass Object
                (
                    [ID] => 2676506
                    [TgID] => 55
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2018-03-12 12:18:04
                    [Text] => #quality

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

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

Понимание задачи

Начните с главного, понимания задачи, которую пытается решить разработчик. Важно: поймите бизнес требование, а не текущую реализацию. Проверьте, разбиваются изменения на 2 и больше пулл реквеста или нет. Если сложно - попросите коллегу о помощи или подробном описании того, зачем изменения нужны. На этом этапе определяется насколько сложную и значимую задачу решал разработчик, а ревьюверы погружается в контекст проблемы. Правило, которого стараюсь придерживаться: 1 пулл реквест == 1 атомарная задача, которую просто понять и просто объяснить на каждом из уровней абстракции.

Проверка, что задача выполнена

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

Проверка архитектуры и способа решения

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

Проверка кода и грамматики

Последний шаг - скрупулезная проверка кода. Тут проверяется нейминг, стилистические особенности, проверяется перформанс. 

Подитожив, можно увидеть такую последовательность:


Понимание задачи -&gt проверка, что задача выполнена -&gt проверка архитектуры и способа решения -&gt проверка кода и грамматики
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1000
                    [ViewsLastDateUpdated] => 2018-04-23 20:56:48
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 586
                    [Views7] => 665
                    [Views30] => 958
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267670] => stdClass Object
                (
                    [ID] => 1267670
                    [TgID] => 54
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-09-19 12:30:05
                    [Text] => Конкурс завершен&#33 
В течение недели вы писали свои истории о проблемах, с которыми приходится сталкиваться в работе https://github.com/moscowrb/organisation/issues/3. Спасибо всем, кто поделился наболевшим и рассказал о своей проблеме.
Наибольшее количество лайков собрал рассказ Nikolay Shebanov (https://github.com/killthekitten). Николай получает бесплатный билет на RailsClub 2017. Поздравляем&#33
На 2 и 3 месте соответственно оказались Кирилл (https://github.com/mephody-bro) и domenic99 (https://github.com/domenic99). Пожалуйста, напишите мне в личку @davydovanton, чтобы забрать свой сувенир из Японии.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:53:00.967067
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267669] => stdClass Object
                (
                    [ID] => 1267669
                    [TgID] => 53
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-09-11 13:51:44
                    [Text] => Ребята из RailsClub из года в год делают самую крупную Ruby-конференцию в России с иностранными спикерами. В этом году ожидаются выступления авторов dry, rom, Hanami и Trailblaizer, а также крутых разработчиков со всей страны.

Мне предложили разыграть один бесплатный билет на конференцию. Чтобы розыгрыш был справедливым, надо будет выполнить небольшое задание.

Задача
Важно, чтобы участие в конкурсе принесло пользу не только победителям, но и всем остальным. Поэтому формат будет таким:
- В комментариях под ишью опишите технические проблемы, с которыми вам приходится сталкиваться в своей работе.
- Один комментарий — одна проблема, о которой хочется рассказать другим.
- Ставьте 👍 понравившемуся комментарию и выбирайте победителя.

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

Награда
Человек с наибольшим количеством 👍 получит бесплатный входной билет на конференцию RailsClub 2017, которая состоится 23 сентября в Москве.
Обладателям второго и третьего мест я привезу сувениры из Японии.

Условия
Конкурс завершается 18 сентября. Каждый участник может отправить неограниченное количество историй. Флуд и спам будут удаляться.

Полезное
- Напишите вашу историю как можно быстрее — так у вас будет больше шансов собрать максимальное количество голосов
- Почитайте пост о том, почему конференции — это круто и что вы можете получить от них помимо докладов
- И еще один старый пост — про то, что делать до, во время и после конференциии
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 2000
                    [ViewsLastDateUpdated] => 2018-02-10 07:53:00.068206
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267668] => stdClass Object
                (
                    [ID] => 1267668
                    [TgID] => 52
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-28 20:02:11
                    [Text] => Благодаря трекингу времени, проведенного за компьютером, обнаружил, что в последний месяц, трачу половину дня на соцсети и месенджеры. Из-за этого я захотел провести экспиремент и не пользоваться меcсенджерами (кроме рабочего слака) и социальными сетями на 7 дней.

Поэтому, к сожалению, в канале будет перерыв на эту неделю. А пока можно вспомнить старые сообщения или задать вопросы. А также прочитать пост Андрея Ситника о цифровом шаббате.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1300
                    [ViewsLastDateUpdated] => 2018-02-10 07:52:59.168851
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267667] => stdClass Object
                (
                    [ID] => 1267667
                    [TgID] => 50
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-25 18:25:57
                    [Text] => #закладки №1

Статьи

* www.blackbytes.info/2017/08/how-does-sinatra-work
Статья о том, как работает Sinatra и что происходит, когда в коде появляется require sinatra. Если хотите прокачаться, в процессе чтения попробуйте написать свою собственную реализацию этого фреймворка с таким же синтаксисом.

* jakeyesbeck.com/2016/03/13/the-power-of-arel
Arel — библиотека, которая вызывает либо отвращение, либо отвращение. Я не знаю ни одного человека, которому бы нравился код, написанный на ней. Так как Arel — low level библиотека для работы Active Record, в коде иногда приходится использовать этот гем. Обычно найти нормальную документацию на эту библиотеку сложно, но статья по ссылке хороша тем, что содержит примеры селектов, джойнов и сложных выборок.

* nicksda.apotomo.de/2011/07/are-class-methods-evil/
* andrzejonsoftware.blogspot.ru/2011/07/yes-nick-class-methods-are-evil.html
Я не фанат методов класса. В коде стараюсь использовать такие методы только по необходимости, либо для создания фабрик и других инициализаторов. В этих двух статьях на примерах рассказано о том, почему не стоит использовать методы классов. Советую также почитать комментарии — там много интересной информации.

* robots.thoughtbot.com/rebuilding-git-in-ruby
Лонгрид о том, как написать git на чистом Ruby. Постарайтесь не просто прочитать статью, но и написать код самостоятельно, без копипаста. Потом запустите программу и проверьте, что всё работает корректно.

* stillflowing.net/2014/12/21/an-experience-in-contributing-to-open-source
Автор рассказывает, почему не надо заниматься опенсорсом ради опенсорса. Ключевая идея: лучше просто пишите код, и во время этого процесса вы найдете проблему, которую можно решить с помощью Open Source.


Репозитории

* reqres-api/reqres_rspec
Интересное решение для того, чтобы генерировать документацию из тестов. Я не использовал эту библиотеку, но планирую испытать ее в деле с Hanami и dry-web приложениями. К сожалению, у библиотеки нет тестов и непонятно, будет ли гем работать с нерельсовыми проектами. Поэтому, если у вас есть время и желание помочь, велкам.

* donnemartin/system-design-primer
Материал о том, как работают разные части веб-приложений. Это популярный текст, который не раз попадал в рассылки или подкасты. У репозитория с этим текстом — 16 тысяч звезд, хотя в нём лежит довольно разрозненная информация. Есть ссылка на русский перевод, который можно дополнить, если есть желание. Вот вам готовая идея для PR: просто дополните гайд и помогите другим разработчикам.

* muan/dashboard
Расширение для Chrome, которое фильтрует информацию на главной странице Github. Скажу честно, мне не нравится Github feed и я раздумывал написать приложение, которое исправило бы эту ситуацию. Но плагин решает проблему: отфильтрованный фид выглядит намного информативнее.


Опенсорс

* slim-template/language-slim/issues/25
Автор Slim ищет помощников. Хотите помочь 10 миллионам человек (на самом деле это количество скачиваний гема)? Есть свободные пара часов в неделю? Напишите автору и предложите помощь. А если сомневаетесь — я уже писал, почему Open Source — это круто (https://t.me/pepegramming/43).

* hanami/events/issues
Если знаете, почему важен event sourcing, хотите прокачаться в асинхронщине и работе с Kafka, Postgres, Redis и другими крутыми штуками — велкам. По ссылке — много открытых ишью, которые ждут своих героев.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1500
                    [ViewsLastDateUpdated] => 2018-02-10 07:55:18.973212
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267666] => stdClass Object
                (
                    [ID] => 1267666
                    [TgID] => 49
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-24 18:30:22
                    [Text] => Недавно нашел статью (https://m.habrahabr.ru/post/158011/, оригинал: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/) про паттерны для моделей в Rails от 2012 года.  Подскажи, насколько эти паттерны актуальны и часто ли их приходится применять на практике. Заранее спасибо&#33

Я считаю, что эти паттерны актуальны даже сейчас. Если присмотреться, они об одном и том же: разделяй логику на изолированные объекты и радуйся жизни. Конечно, можно не следовать паттернам, но необходимо задавать самому себе вопрос: «А действительно ли код, который я написал, должен быть в этом месте, или нет?».

Получается, рельсовые параметры нам всё равно нужно постоянно пермитить? (вопрос по последнему примеру)

Мне не приходилось таким заниматься, но думаю, ответ легко найти в интернете. Например, на SO описывается один из вариантов, как отключить параметры:
https://stackoverflow.com/questions/18092475/in-rails-4-disable-strong-parameters-by-default

Думал, что "Dry::Validation.Schema" на выходе будет выкидывать все параметры, которые не описаны в схеме, это возможно? Просто получается, что если в запросе будет куча лишнего хлама, и описанные параметры проходят валидацию, то лишние параметры тоже остаются, и приходится их самому подчищать, иначе при попытке сохранить запись будет эксепшен, что переданы дополнительные параметры, о которых модель не в курсе, и запись не сохранится.

Я допустил ошибку в примере. Используйте form-валидацию. Эта валидация обрезает неуказанные параметры, что решает проблему.


schema = Dry::Validation.Form do
  required(:email).filled(:str?)
  required(:age).maybe(:int?, gt?: 18)
end

schema.call(email =&gt jane@doe.org, age =&gt )
# =&gt #&ltDry::Validation::Result output={:email=&gt&quotjane@doe.org&quot, :age=&gtnil} errors={}&gt

schema.call(email =&gt jane@doe.org, age =&gt , firstname =&gt anton)
# =&gt #&ltDry::Validation::Result output={:email=&gt&quotjane@doe.org&quot, :age=&gtnil} errors={}&gt
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1100
                    [ViewsLastDateUpdated] => 2018-02-10 07:55:18.57077
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267664] => stdClass Object
                (
                    [ID] => 1267664
                    [TgID] => 47
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-21 20:22:59
                    [Text] => Я люблю экшены в Hanami и сегодня расскажу, почему.

После рельсы и синатры разработчики привыкли, что экшены являются методом класса или чем-то похожим. Главное, что они — в одном классе.

У такого подхода есть плюсы:
- каждый экшен в одном месте, не надо «прыгать» между файлами
- легко представить экшен как один класс и инкапсулировать его в голове.

Но существуют и минусы:
- если экшены большие, то куча кода в одном месте превращается в хаос
- появится код, который будет общим для 1+ экшена (например, callback), что увеличивает связанность экшенов между собой
- такие экшены сложно тестировать. Нельзя просто взять и явно вызвать метод, для этого нужны интеграционные тесты
- permit params из Rails шарится на каждый экшен в классе. Это доставляет трудности, если нужно предоставлять разные параметры.

Тут в игру врывается Hanami, который переворачивает такой подход с ног на голову. Этот фреймворк утверждает обратное: каждый экшен — изолированный класс с единственным публичным методом #call. Но если задуматься, то получается, что мы работаем не с контроллером, а с отдельным экшеном. Поэтому логично изолировать не контроллеры между собой, а экшены.

Выглядит это следующим образом:

<br/>module Web::Controllers::Home<br/>  class Index<br/>    include Web::Action<br/><br/>    def call(params)<br/>      # your action code here<br/>    end<br/>  end<br/>end<br/>

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

<br/>Web::Controllers::Home::Index.new.call({ ... })<br/>

Этот подход добавляет гибкости при тестировании. Теперь можно писать не только интеграционные, но и юнит-тесты. А если у экшена есть сложная для тестирования зависимость в коде, то Hanami позволяет использовать DI вместо моков:

<br/>module Web::Controllers::Home<br/>  class Index<br/>    include Web::Action<br/><br/>    def initialize(repo: UserRepository.new)<br/>      @repo = repo<br/>    end<br/><br/>    def call(params)<br/>      # your action code here<br/>    end<br/>  end<br/>end<br/><br/>action = Web::Controllers::Home::Index.new(repo: EmptyRepository.new)<br/>

Кроме того, экшен предоставляет валидацию для работы с параметрами. Валидация основана на dry-validation, что позволяет вынести логику из модели. В результате модель получается «тоньше». Условные валидации пропадают как костыль, потому что валидируются только указанные в каждом экшене параметры.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1000
                    [ViewsLastDateUpdated] => 2018-02-10 07:55:17.745105
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267665] => stdClass Object
                (
                    [ID] => 1267665
                    [TgID] => 48
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-21 20:22:59
                    [Text] => Но и как у любой технологии, у экшенов Hanami есть минусы.

Код экшена под капотом — магия
Сделать действительно удобный для использования другими людьми код — непростая задача. Поэтому приходится сидеть на двух стульях. На эту тему можно найти отличный пост (советую также прочитать комментарии, там много ценной информации). От себя скажу, что эта магия не мешает во время разработки, но порой задаешься вопросом, почему иногда явный вызов возвращает неявные вещи.

Много файлов
Это палка о двух концах. Пока в системе маленькие экшены на 1-3 строчки кода, легче смотреть весь контроллер целиком — и тут будет удобен подход из Rails. Но когда экшены растут и сложность увеличивается, помогут идеи, заложенные в Hanami. Изолированный экшен позволяет разработчику сфокусироваться только над ним одним, а не над контроллером в целом. Поэтому если у вас будет приложение с простым CRUD без излишеств, рассчитывайте, что разом на весь контроллер вы вряд ли посмотрите.

Чем это полезно разработчикам, которые не будут использовать Hanami

Как я неоднократно говорил, Hanami интересен идеями, которые лежат в его основе. Например, фреймворк помогает задуматься, правильно ли валидировать данные в модели и создавать огромные контроллер-классы.

Запомнить
- В мире Ruby существует устоявшееся «правило», как должны выглядеть контроллеры.
- Hanami предлагает концептуально новый подход к написанию экшенов.
- Этот подход не является «серебряной пулей». Он решает только определенные проблемы.
- Если вы не планируете использовать Hanami, можете просто вдохновиться идеями из этого фреймворка.

Ссылки
- Документация на экшены в hanami
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1100
                    [ViewsLastDateUpdated] => 2018-02-10 07:55:18.137246
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267663] => stdClass Object
                (
                    [ID] => 1267663
                    [TgID] => 46
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-21 12:30:20
                    [Text] => Доброго времени, читаю канал, но интересно, почему не используется телеграф(telegraph.ph) для хранения статей? Интересно узнать ваше мнение

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

Оргвопросы

Обновил форму вопросов. Теперь можно задать вопрос или поделиться проблемой, и мы разберем её в канале. Пожалуйста, не воспринимайте это как SO или место, где можно получить ответ на любой вопрос. Придерживайтесь тематики.

Также появилась идея раз в неделю делиться ссылками на интересные посты. Что думаете?
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1100
                    [ViewsLastDateUpdated] => 2018-02-10 07:55:17.300632
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267662] => stdClass Object
                (
                    [ID] => 1267662
                    [TgID] => 45
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-15 00:19:34
                    [Text] => Я уже писал о dry-transaction. На работе мы начали использовать эту библиотеку в оперейшенах. Это такие сервис объекты, которые вызываются из транспорта (http, stream) и выполняют код завязанный на бизнес логику. Это может быть как просто манипуляция с бд (достать данные, сохранить данные), так и действительно сложная логика. Например процессинг платежа. Текущее решение содержит в себе  много магии и поэтому было решено начать использовать dry-transactions как основной каркас для построения оперейшенов.

Поэтому сегодня две ссылки.

Пример использования такого оперейшена в rails экшене:
https://gist.github.com/davydovanton/ec99546c3eb512599b65e5b27fe78bb0

Старая ссылка с примером в ханами:
https://gist.github.com/davydovanton/0a9e9dcaef75582e3c2fe9b4392b61d9
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1300
                    [ViewsLastDateUpdated] => 2018-02-10 07:55:16.849755
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267660] => stdClass Object
                (
                    [ID] => 1267660
                    [TgID] => 43
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-10 17:54:34
                    [Text] => «Как начать писать в opensource?»
Кажется, такой вопрос задавал себе каждый разработчик, в том числе и я сам. Сегодня мне хотелось бы поделиться опытом и советами для всем, кому интересна эта тема.

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

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

Прокачивайтесь как разработчик
Опенсорс-проектов много. Это значит, что найти задачу по душе не составит труда. Понравился elixir — найдите проект на нем и попробуйте помочь. Хотите прокачаться в асинхронном программировании — откройте гитхаб и найдите интересный репозиторий.

Занимаясь опенсорсом, можно лучше понять, как работают технологии, или узнать новые подходы. Например, можно помочь в работе над ORM — и понимание того, как функционируют базы и как лучше работать с ними, обеспечено. Есть проекты, в которых придерживаются кодстайла и пишут красивый код. Работая над популярными осс-проектами, можно получить фидбэк крутых разработчиков и перенять их опыт.

Учитесь коммуникации
Опенсорс — это прежде всего общение с людьми. Значит, придется продвигать идеи, объяснять, почему было выбрано именно такое решение и зачем оно вообще нужно.

Например, я год не мог объяснить, почему необходимо сделать ивентную библиотеку не на виспере. В итоге я все-таки смог донести свою идею, и теперь мы делаем отличный продукт.

Создаете себе имя
Крутые разработчики, которых знают другие, занимаются опенсорсом, пишут полезные статьи или же просто говорят о себе. Опенсорс может помочь сделать имя, что в будущем принесет бенефиты.

Я уже говорил, что опенсорс — такая же работа, как и та, за которую платят деньги. На нее нужны силы и время. Поэтому первое, что нужно сделать, чтобы начать писать в опенсорс — ответить самому себе на вопрос, зачем это нужно лично вам. Не стесняйтесь себя и отвечайте честно, потому что причин может быть несколько, и они могут быть даже эгоистичными. Подумайте, действительно ли вы готовы пожертвовать своим временем и силами ради этого, и если да, то ищите задачи.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-02-10 07:55:16.026687
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267661] => stdClass Object
                (
                    [ID] => 1267661
                    [TgID] => 44
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-10 17:54:34
                    [Text] => Как найти задачи
Я знаю четыре способа, которые могут помочь:

1. Использовать технологию, в которую хотите начать писать. Самый простой вариант.
Вы пишете проект на hanami, dry-web, rails или используете любую другую технологию. Видите проблемы, на решение которых нужно потратить время, и исправляете их, обновляете документацию или пишете новую библиотеку. Как пример: работая с rom, мне было сложно понять ассоциации между таблицами, поэтому я написал простой инструмент, который генерирует картинку с этой информацией.

2. Написать автору. Самый простой вариант. Просто находите автора библиотеки и предлагаете ему свою помощь. Все разработчики, которых я знаю, не отказываются от таких предложений и с удовольствием объясняют, куда и как можно написать.

3. Воспользоваться агрегаторами тасков. В интернете можно найти много сервисов со списком осс-задач (1, 2). Я сам написал такой. Проблема в том, что вам как новичку сложно будет понять, что и как нужно сделать. Поэтому я не фанат такого варианта.

4. Найти баг или фичу самому. Самый сложный и далеко не практичный вариант. Открываете код проекта, долго изучаете его и находите, как и что можно исправить. Советую не доходить до такого, ибо способ сильно убивает мотивацию.

Что можно делать, кроме кода

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

Советы

1. Лучше час каждый день, чем 5 часов один раз в неделю. За час можно многое успеть и получить много ❤️  от других людей. Регулярность важнее количества.
2. Не идеализируйте. Опенсорс — это прежде всего помощь другим, а не хардкорные таски. Порой важнее поправить сайт проекта, чем написать невероятно сложную и быструю систему.
3. Не отчаивайтесь. Все мы люди, все мы ошибаемся. У меня был пулл-реквест в Rails с 80 комментариями, который я не смог закончить. Такие ситуации нормальны. Не сдавайтесь, потому что все сообщество готово вам помочь.
4. Отдыхайте и не делайте через силу. Если чувствуете, что не идет — забейте. Хуже от этого никому не будет. Если чувствуете, что не можете доделать задачу — скажите это другим людям. Вам помогут и никто не обидится. Например, не так давно я попросил помощи у Луки (Luca...) с тестами, потому что я не знал, как их поправить, и он мне помог. 

Запомнить

- Опенсорс — такая же работа, с такими же людьми. Не думайте, что она для избранных или исключительно для профи. Это под силу каждому.
- Опенсорс — это не только про код, но и про общение. Не обязательно писать код, чтобы помочь.
- Не заставляйте себя, пользы от этого никому не будет.
- Не нервничайте и не сдавайтесь. Будьте вежливы и дружелюбны, и вам понравится&#33
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:55:16.441235
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267659] => stdClass Object
                (
                    [ID] => 1267659
                    [TgID] => 41
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-07 14:37:37
                    [Text] => и немного опыта моего коллеги, который будет полезен, если вы захотите использовать контейнеры
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1300
                    [ViewsLastDateUpdated] => 2018-02-10 07:55:15.605147
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267657] => stdClass Object
                (
                    [ID] => 1267657
                    [TgID] => 39
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-07 14:01:08
                    [Text] => DI containers

Представьте объект, который знает о зависимостях в приложении (или части приложения). Такой объект знает об инициализации и конфигурации каждой зависимости. Этим занимается DI containers.

Какие проблемы решает DI container:

* Зависимости находятся в одном месте. Не нужно писать пачку файлов в config/initializes/*, а потом пытаться контролировать флоу загрузки зависимостей.
* Каждую зависимость достаточно инициализировать один раз. Если нужна динамическая инициализация — не проблема, библиотеки это поддерживают.
* Не нужно держать конфигурации зависимостей в одно месте. Можно легко выделять конфиги в разные части приложения.

В руби есть библиотеки, которые реализуют подобный функционал:

* dry-containers
* micon
* encase
* dim
* hanami-components

Если ничего не понравилось — велосипед займет меньше 100 строк кода. Но я отдаю предпочтение dry-containers, и вот почему:

* Можно использовать класс или инстанс контейнер.
* Легко пишется кастомный регистратор и резолвер зависимостей контейнера.
* Register options.
* Поддерживаются стабы.

Но у контейнеров можно найти минусы:
* Появляется глобальная переменная, которая ведет себя как синглтон. Это создает глобал стейт, поэтому будьте аккуратны.
* Может возникнуть соблазн создать мега-контейнер с множеством излишних зависимостей. Это принесет много проблем и негативно отразится на производительности.
* Придется писать конструктор в каждом сервисе/объекте, в который будет передаваться DI container.
* Если есть куча сервисов, которые резолвятся в контейнере, придется писать много кода.
* Что делать, если понадобится использовать DI и DI containers сразу в одном коде?

Хочется обратить внимание на последние 3 пункта. Это проблемы, которые не решаются через контейнер. Поэтому придется говнокодить. Например, так:
<br/>class Service<br/>  def initialize(twitter: Container[twitter])<br/>    @twitter = twitter<br/>  end<br/>end<br/>

А для автоматической регистрации сервисов придется писать сложный код с require в интеракторе.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-02-10 07:55:14.762259
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267656] => stdClass Object
                (
                    [ID] => 1267656
                    [TgID] => 38
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-07 14:01:08
                    [Text] => Я уже писал об использовании dependency injection в ruby. Но как и у всех подходов, у DI существуют свои минусы. 

Сложно управлять зависимостями
Например, есть папка с сервис-объектами. Для каждого из них нужен логгер. Также логгеры нужны для интеракторов, воркеров, репозиториев и экшенов.
 
Используя DI, придется каждый раз прокидывать один и тот же инстанс логгера в каждый объект. Это утомительно и заставляет писать много кода.

Сложная и дорогостоящая инициализация
Например: для инициализации клиента Amazon приходится писать много кода.

В случае с DI понадобится каждый раз создавать ресурсозатратный инстанс или использовать константу, которая объявленна где-то в коде.

Много кода
Например: сервис, которому нужен твиттер-клиент, логгер, репозиторий и еще пара зависимостей. Создается инициализатор с кучей переменных, а это уже code smell.
<br/>class MyObject<br/>  def initialize(repo:, twitter:, logger:, mailer: ,...)<br/>  end<br/>end<br/>

Конфигурирование зависимостей в одном месте
Конфиги зависимостей хранятся в одном месте, из-за чего такой файл сложно поддерживать.

Перечисленные проблемы заставляют задуматься об использовании DI в проекте. Но есть решение, которое позволяет контролировать зависимости.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:07.89457
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267658] => stdClass Object
                (
                    [ID] => 1267658
                    [TgID] => 40
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-07 14:01:08
                    [Text] => Ребята из dry-org подумали о разработчиках и сделали две библиотеки, которые решают эти проблемы: dry-auto_inject и dry-system).

dry-auto_inject нужен для того, чтобы легко и просто инжектить зависимости в объекты:
* Библиотека предоставляет сахар для инжектинга зависимостей из контейнера.
* Заинжекченные зависимости также инжектятся через классический DI.
* Работает с любыми контейнерами. Я проверял с ханами-контейнером и с обычным руби хешем.

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

Примеры:

* Все части ханами загружаются в DI контейнер, который уже резолвит что и как загружать.
* Пост из icelab о том, как правильно использовать DI.
* Пример блога на dry-web, можно посмотреть на DI containers в деле 

Tips:
Контейнер хорошо подходит для реализации фабрик. Можно создать класс, который будет загружать в память и возвращать только указанный объект. Именно так работает выбор адаптеров в hanami-events.

Links:
* Пост великолепного Jim Weirich, который поможет разобраться в теме
* Не путайте IoC и DI containers. Это разные вещи
* Статья, которая описывает когда лучше использовать DI containers
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1300
                    [ViewsLastDateUpdated] => 2018-02-10 07:55:15.206272
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267655] => stdClass Object
                (
                    [ID] => 1267655
                    [TgID] => 37
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-04 13:17:13
                    [Text] => Простите, ссылка на использование AR::Validation вне модели вела не на тот пост, вот правильный:
https://blog.ragnarson.com/2016/10/26/validation-outside-activemodel.html
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1400
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:07.479619
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267654] => stdClass Object
                (
                    [ID] => 1267654
                    [TgID] => 36
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-03 20:10:58
                    [Text] => Запомнить
- Rails way не панацея. Старайтесь думать, когда полезно его придерживаться, а когда — нет.
- Валидации и модель — разные вещи. Вынос валидаций в контроллер может сильно облегчить жизнь и процесс тестирования.
- У выноса валидаций есть минусы, выбирайте осознанно.

Интересное
- Валидация разных параметров в пользовательском приложении и в приложеини администрирования на примере hanami
- Пример большой валидации в моделе гитлаба

Почитать
- Hanami — отличный пример, когда вынос валидации из модели работает как надо.
- Такой же подход пропагандирует трейлблейзер.
- Облегчаем жизнь монадами.
- Как использовать AR::Validation вне модели.
- Используем dry-validation в rails приложении.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1500
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:07.069351
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267653] => stdClass Object
                (
                    [ID] => 1267653
                    [TgID] => 35
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-08-03 20:10:23
                    [Text] => Сегодня поговорим про валидации. Я не фанат rails way подхода, и вот почему.

На мой взгляд, главные проблемы заключаются в следующем:

1. Модель вмещает в себя много логики. Это мешает в тестах, а также увеличивает связанность между частями системы.
2. В одном файле описывается схема для всех возможных вызовов в коде. Получается гибрид с кучей кондишен-условий (привет if: :paidwithcard?)
3. Если говорить о rails, то заметно дублирование логики. Контроллер проверяет ключи (permit params), а модель проверяет значение ключей при сохранении.

Это ведет к сложностям и затратам усилий на борьбу с фреймворком вместо решения бизнес-задач.

Как с этим жить?

Вариант первый: забить и писать условные валидации в модели. Это будет просто, не наплодит лишних абстракций и соответствует rails way. Но такой вариант приведет к проблемам, о которых я писал выше:
- сложность в тестировании
- сложность в валидировании частей приложения. Например, валидация данных в админке для модели Post и в юзер-контроллере
- логика мешает, а модель растет. Это приводит к 1000-строчным моделям, что увеличивает сложность понимания кода.

Второй вариант: валидации происходят на транспортном уровне приложения. Получая данные от клиента, нужно быть уверенным, что они валидны и ничего не ломают. Только после этого данные сохраняются в базу. Схематически это выглядит так:
<br/>Получить данные → Провалидировать → Сохранить валидные данные в базу<br/>

Путь, который я предлагаю — вынести валидацию в отдельную абстракцию и использовать ее там, где это нужно. Это могут быть контроллеры, сервис-объекты или любые другие варианты.

Плюсы такого подхода:
- Схемы валидаций специфичны только для конкретного случая, поэтому не нужно думать о реализации, которая покроет все случаи.
- Легко тестировать. Тестируется только схема, а с помощью DI она пробрасывается в код. Такой подход позволит проще тестировать объекты, так как не нужно заморачиваться по поводу валидных данных в тестах.
- Модель не реализует логику валидирования, следовательно, уменьшается в размерах. Так проще найти нужную валидацию для определенного случая, не нужно разбираться, в каком случае кондишен валидации сработает, а в каком — нет.

Минусы:
- Неканоничный rails way. Новым разработчиками придется понимать, почему было сделано так, а не иначе.
- Появляется новая абстракция. Не имею ничего против этого, но абстракции текут.

Как это сделать в Ruby?

Я знаю три библиотеки, которые предоставляют валидированию данных:
- dry-validation
- ActiveModel::Validations
- hash_validator gem

Используйте любую, но я отдаю предпочтение dry-validation. Это гибкая библиотека, которая предоставляет удобный DSL для кастомных матчеров и проверяет опциональные поля. У нее также есть гайд о сравнении с AR::Validatoin. Эта библиотека используется в hanami, trailblazer и других гемах. Чтобы начать с ней работать, достаточно прочитать гайды.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1400
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:06.616588
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267652] => stdClass Object
                (
                    [ID] => 1267652
                    [TgID] => 34
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-31 15:35:08
                    [Text] => Я сделал простой проект, который поможет легко и быстро найти застаренный гитхаб-репозиторий. Поиск похож на таковой по полям в гитхабе.

Задача

Вместо множества полей и перегрузки интерфейса было решено сделать единственное поле для поиска. Такой поиск сделал гитхаб. Строка is:open is:issue позволяет получить список открытых issue. Мне захотелось сделать подобное и в своем проекте.

Реализация

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

1. Поиск в базе по ключам
2. Парсинг строки для созданием ключей поиска
3. Валидация ключей

Поиск по ключам

Поиск по ключам состоит из главного релейшена, который пробрасывается через методы для поиска. Каждый метод принимает query в виде релейшена и значение ключа для поиска и возвращает новый query-объект. В виде кода это выглядит следующим образом:
<br/>def find_by_account(account_id, search = {}, limit = 100)<br/>  query = projects.where(account_id: account_id)<br/>  query = text_search(query, search[:text])<br/>  # ...<br/><br/>  query.order{ starred_at.desc }.limit(limit).as(Project).to_a<br/>end<br/><br/>def text_search(query, text)<br/>  return query if text.nil?<br/><br/>  text = pattern(text)<br/>  query.where { name.ilike(text) | description.ilike(text) }<br/>end<br/>

Парсинг строки

Мне не хотелось заморачиваться с парсерами, поэтому выбор пал на стандартную библиотеку strscan. StringScanner посимвольно проходит по строке. Результатом выполнения метода будет либо совпадение, либо nil. В документации много подробных примеров. Хотелось бы добавить, что создание токенов в виде регулярных выражений и сканирование строки по этим токенам может сильно упростить логику и повысить читаемость кода:
<br/>require strscan<br/>OPTION_TOKEN = /&#092w+:&#092w+/<br/><br/>StringScanner.new(command:test).scan(OPTION_TOKEN)<br/># =&gt &quotcommand:test&quot<br/>StringScanner.new(other text).scan(OPTION_TOKEN)<br/># =&gt nil<br/>

Полный код доступен в репозитории. Парсер возвращает хеш вида { search_key =&gt value }, который валидируется, а потом передается в репозиторий.

Валидация ключей

Следующая проблема: как провалидировать параметры поиска? Хочется закрыть xss-уязвимость, а также для улучшения UX показать, что пользователь ввел неправильные данные. Для этого я написал сервис валидации и проверки параметров. Сервис знает допустимые ключи поиска и удаляет невалидные символы.

Что можно имправить

1. Главный экшен. Стоит вынести валидацию в отдельный сервис, а поиск в интерактор, который будет вызывать валидацию и метод репозитория.
2. Поиск в репозитории. Метод содержит много логики. Вынос логики в отдельный класс  упростит логику и поможет в тестировании.
3. Поиск в postgresql. Я люблю постгрес, в нем отличный поиск. Но возможно, вариант с эластиком будет проще и быстрее.

Запомнить

1. Если нужен простой поиск, а за SaaS платить не хочется — стоит подумать, может, проще написать велосипед.
2. StringScanner — рабочая библиотека, которая поможет сохранить силы и время.
3. Использование больше одного поля для поиска усложняет UX и код.

Ссылки

* Блог пост о том, как парсить текст в руби
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1500
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:06.204182
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267651] => stdClass Object
                (
                    [ID] => 1267651
                    [TgID] => 33
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-31 01:21:03
                    [Text] => Спасибо всем за вопросы&#33 Приятно видеть ваш интерес к постам. Надеюсь, что в будущем фидбека будет еще больше :)

&gt Привет&#33 Вижу, ты часто пишешь про Dependency Inversion, и поэтому хотел бы спросить: в чем для тебя принципиальная разница между di и паттерном strategy? Спрашиваю, т.к., на мой взгляд, разница между определениями в Ruby несколько размыта.

Мне нравится ответ SO на такой же вопрос.

Если в двух словах — strategy и DI работают одинаково. Однако различия тоже есть. В случае с DI изменение зависимости во время работы программы — редкий случай. Например, если используете DI с hanami экшенами, то в экшен пробрасывается интерактор или репозиторий. Вероятность того, что в рантайме экшен будет использовать другой репозиторий или интерактор, крайне мала.

В случае паттерна strategy вероятность передачи разных объектов выше. Например, использование разных форматов данных (http, pdf, xml, etc) в одном рантайме. 

&gt А как ты для себя решаешь, когда использовать service object, а когда interactor?

Функциональные объекты похожи между собой. Различие в деталях — и иногда эти детали важны, а иногда — легко опускаются. 

Немного теории: сервисы — объекты, которые принимают и возвращают данные. У таких объектов нет возвращаемого стейта, а для разработчика это выглядит как функция y = f(x).  Неважно, вернется success или failed, главное — получить результат. 

Интерактор — другое. Это тот же сервис, но вместо данных возвращается еще и стейт кода. Можно легко сделать «интерактор» из ~~говна и палок~~ подручных средств. Для этого  достаточно просто возвращать хеш со статусом кода и данными:
<br/>class Service<br/>  def call(payload)<br/>    validation = UserSchema.call(payload)<br/><br/>    if validation.success?<br/>      user = UserRepository.new.create(payload)<br/>      { success?: ture, user: user }<br/>    else<br/>      { success?: false, errors: validation.errors }<br/>    end<br/>  end<br/>end<br/><br/>result = Service.new.call(name: Anton)<br/>result[:success?] # =&gt true<br/>result[:user] # =&gt #&ltUser:xxx &gt<br/><br/>result = Service.new.call(name: nil)<br/>result[:success?] # =&gt false<br/>result[:errors] # =&gt { name: [should be not empty] }<br/>

Код выше идеологически не отличается от использования hanami-interactor или interactor gem. Но для себя я выработал правило: если стейт не важен, использую сервис. Если стейт важен — интерактор или оперейшен с монадой, хешем.

Возникает логичный вопрос: в чем различие между оперейшеном, интерактором и сервисом? Об этом поговорим в следующий раз :)
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1500
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:05.811228
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267650] => stdClass Object
                (
                    [ID] => 1267650
                    [TgID] => 31
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-28 14:18:33
                    [Text] => Организационные моменты

1. Чтобы было проще искать конкретные сообщения и понимать взаимосвязь некоторых тем, я добавил оглавление. Там же есть ссылка на mindmap по функциональным объектам.
http://telegra.ph/Pepegramming-Contents-07-16

2. Появилась форма обратной связи. Ответы на вопрсы — хороший способ глубже понять тему как вам, так и мне. Я считаю, что не бывает глупых вопросов — они все важны. Поэтому на любой фидбек я отвечу в течение пары дней.  Если вам хочется что-то спросить или посоветовать, воспользуйтесь анонимной формой или просто напишите мне в личку (@davydovanton).
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1500
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:05.343948
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267649] => stdClass Object
                (
                    [ID] => 1267649
                    [TgID] => 30
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-28 01:30:22
                    [Text] => Сегодня я расскажу о фиче ханами, над которой работаю сейчас и которую хотел бы видеть в следующей версии (1.1).

Для начала — теория. В ханами контейнерная архитектура. Это значит, что проект на ханами состоит из набора приложений. Разработчик волен выбирать сам, как называть приложения. Например: web, api, admin. Или доменные приложения (account, cart, posts, etc). Каждое из них — набор контроллеров и средств представления данных. Например, вью объекты + темплейты, или же сериализаторы для API. Эти приложения полностью изолированы и загружаются в память при старте сервера.

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

1. Запуск бэкграунд-процессинга, например, sidekiq, без приложений. Это нужно, чтобы не загружать в память ненужный код, который не будет выполняться.
2. Если существует web-приложение, на которое приходится большая часть нагрузки, можно легко заскейлиться. Поднимите кластер серверов только с web-приложением, опять же, не загружая в память лишнего.
3. Если необходимо защитить админки, это можно легко сделать, запустив конкретное приложение в приватной сети. При этом саму админку в открытой сети можно не запускать. В этом случае злоумышленник не сможет взломать auth и узнать эндпоинты админских приложений.
4. Кейс повышенной сложности: если есть главное приложение и куча админок или внутренних приложений, не нужно дублировать бизнес логику или модель данных. Просто поднимите внутренние сервера и на каждом запустите сервер с нужным приложением.

Последний пункт объясню подробнее. Много раз видел, как создают больше одного приложения с общей базой. На предыдущей работе было 3 приложения с одной базой и одинаковыми моделями, которые надо было как-то поддерживать. Каждый раз возникали одни и те же проблемы: начиная от копирования модели и кучи логики, связанной с доменной областью, заканчивая одинаковыми патчами 2+ проектов, которые надо сделать и проверить.  Конечно, один API-сервер решает часть проблем, но в моем случае это не работало.

Аналогичный функционал существует в dry-web. Его я использую в своей работе. К сожалению, я не знаю других руби-фреймворков, которые позволяют делать подобные вещи. Если вы знаете другие примеры — расскажите об этом. Для заинтересовавшихся — ишью на гитхабе: https://github.com/hanami/hanami/issues/778
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1500
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:04.913044
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267648] => stdClass Object
                (
                    [ID] => 1267648
                    [TgID] => 29
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-26 00:47:03
                    [Text] => Я работаю в американском healthcare стартапе. Медицина в Штатах отличается тем, что там много проверенных решений.

Каждый житель, имеющий страховку, приписан к определенной аптеке. Большинство лекарств доступно только по рецепту, и покупать их можно только в указанной в рецепте аптеке. Если владелец страховки захочет сменить аптеку, нужно предупредить новую аптеку, чтобы та получила его данные. Туда должен быть отправлен факс вида: «One of our members would like to transfer a prescription to your pharmacy» с полными данными этого пользователя.

Когда брал эту задачу, то ожидал, что будет весело — ведь не каждый день пишешь штуки для отправки факс-сообщений. На деле же веселья оказалось намного меньше.

Во-первых, для факсов существует специальный API у twilio. Есть и другие API, но twilio уже используется в нашем приложении. В документации говорится — чтобы послать факс, необходимо знать 3 вещи:
* телефон получателя
* телефон отправителя
* ссылку на *.pdf файл, который будет отправлен по факсу

Итого, чтобы послать факс, нужно:
Собрать данные → Сгенерировать pdf → Разместить файл в облачном сторадже → Передать телефоны и ссылку на файл в API.

Собрать данные
Стучим в базу и собираем данные. В моем случае это ROM repository, через который вытаскиваются данные об аккаунте из BD.

Сгенерировать pdf
Из всех гемов мне понравился prawn, который легко создает *.pdf файлы. Из плюсоов:
* нативный
* понятная документация с множеством примеров
* можно создать изолированный класс для генерации .pdf

Разместить файл в облачном сторадже
Тут мало подробностей. Я использовал s3, но если у вас не сервис от aws — проблем быть не должно. Главное — получить ссылку.

Передать телефоны и ссылку на файл в API
В документации к twilio указан пример отправки факса. Делаем POST-запрос на twilio URL и передаем параметры.

Собираем шаги вместе
Каждый из этих шагов написан в виде функциональных объектов (оперейшенов). А потом я объединил все объекты в одной транзакции:
<br/>class TransferMedicationToPharmasy<br/>  include Dry::Transaction(container: Application)<br/><br/>  step :read_user, with: operations.read_user<br/>  step :generate, with: operations.generate_pdf<br/>  step :send_to_s3, with: operations.send_to_s3<br/>  step :send_fax, with: operations.send_fax<br/>end<br/>

На выходе получил класс, который вызывается в бэкграунд-процессинге: TransferMedicationToPharmasy.new.call(account_id).

Выводы
* Отправлять факсы проще, чем кажется.
* Старые технологии, о которых забыли модные ребята, могут приносить деньги.
* Не все задачи, которые кажутся сложными на первый взгляд, на самом деле таковыми являются.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:04.479251
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267647] => stdClass Object
                (
                    [ID] => 1267647
                    [TgID] => 28
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-19 23:57:53
                    [Text] => Завтра состоится митап в москве. Последние пару дней мы слушали доклады спикеров: задавали ребятам вопросы и давали рекомендации по поводу слайдов. Расскажу здесь о правилах, которые, на мой взгляд, помогут сделать презентацию качественной.

Что узнает пользователь
Это вопрос, который необходимо себе задать с самого начала: «Что полезного узнает слушатель»? Именно с этого я сам начинаю работу над презентацией.
Мотивация спикера — это очень важно. Если вы идете выступать только из-за того, что заставило начальство, или чтобы показать себя, то качество вашего выступления и всего мероприятия может пострадать.

Расскажите историю
Это правило самое сложное для меня. Интересная презентация — опыт, а излагать этот опыт проще всего в виде истории. У любой истории есть начало, завязка, кульминация и ценность. Не рассказывайте личные истории, если не хотите. Важно, чтобы у доклада сохранялась последовательность и смысловые переходы между частями.
Под этот пункт подходит и другая проблема. Часто выступающие грешат тем, что пересказывают документацию или же рассказывают о совершенно абстрактных вещах.
Это тяжело для слушателя. Во-первых, зачем слушать такой доклад, если можно просто почитать документацию? Отнимать у слушателей 30 минут времени — некрасиво. Во-вторых, если люди не разбираются в практической области применения, им будет сложно понять абстрактную штуку.

Не бойтесь говорить о проблемах
Не будьте роботами, будьте людьми. А люди читают документацию для популярных методов, пишут кривой код и ошибаются. Эти ошибки интересны другим. Описание ошибок сложно найти, поэтому такие истории дорогого стоят. 

Меньше текста на слайдах
Часто замечаю, что люди пишут много текста на слайдах. Такие слайды выглядят перегруженно, отвлекают от речи докладчика. Возможно, это удобно для спикера: часть текста можно просто прочитать со слайда, а не запоминать. Но доклад — для слушателей, а не для спикеров. Разбейте текст на слайды (1 слайд — 1 термин), попробовать заменить текст картинками или же просто выкинуть лишнее.

Готовьтесь к докладу как минимум за неделю
Постарайтесь перебороть себя и начать готовиться минимум за неделю. Неподготовленные доклады видно сразу, и они мало кому нравятся. Так как выступить хорошо — в ваших же интересах, делайте слайды раньше, чем за 2 часа до выступления.

Оценивайте доклад честно
И напоследок — совет, который пару раз выручал. За пару дней до выступления запишите свою речь на видео. Обязательно со слайдами. Потом посмотрите и ответьте себе честно: было вам интересно на месте слушателя или нет? Также это отличная тренировка и способ отшлифовать свою речь.

Запомнить

- Сделайте доклад полезным для слушателя, а не для себя.
- Рассказывайте связанную историю вместо рандомных фактов.
- Рассказать о проблемах — способ поделиться опытом.
- Много текста на слайде — мало внимания к слайду.
- Подготовьтесь заранее и сделайте собственный прогон доклада с записью и последующим разбором.

Ссылки
- Советы от Scott Hanselman
- Советы от Zach Holman
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:04.054941
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267646] => stdClass Object
                (
                    [ID] => 1267646
                    [TgID] => 27
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-16 23:10:17
                    [Text] => Тут подсказали, что про railway programming в elixir есть лучше статья:
http://www.scottmessinger.com/2016/03/25/railway-development-in-elixir-using-with/
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1700
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:03.597612
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267644] => stdClass Object
                (
                    [ID] => 1267644
                    [TgID] => 25
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-16 22:45:19
                    [Text] => Вынести логику

Первое,что приходит в голову — создать еще одну абстракцию. Выносим туда условия и вызовы наших функциональных объектов. Например, вот так:
<br/>class UserOperation<br/>  def call(payload)<br/>    result = ValidateUser.new.call( ... )<br/>    result = result.success? ? CreateUser.new.call( ... ) : result<br/>    result<br/>  end<br/>end<br/><br/>if UserOperation.new.call({ ... }).success?<br/>  ...<br/>end<br/>
Плюсы:
  - процесс создания user находится в одном месте
  - легко переиспользовать объект

Минусы:
  - решение не решает проблему нескольких условий, а просто размазывает логику


Railway Oriented Programming

Не думайте, что это связано с Ruby on Rails, потому что это подход из мира функционального программирования, который исправляет ситуацию с условиями.

Создается цепочка функциональных объектов, в которые передаются данные. Дальше есть только 2 пути, по которому эти данные будут идти. Либо по success-пути, что будет значить, что объекты вернули положительное значение. Либо, зафейлившись на одном из объектов, возвращают значение failed. В конце можно просто обработать значение, как в интеракторе, и выполнить необходимую логику.

Схематично это выглядит следующим образом.

Допустим, есть объект, который принимает данные и возвращает два разных вида данных — success и failed:
<br/>success data → first function object → success data<br/> failed data → first function object → failed data<br/><br/>success data → second function object → success data<br/>failed data → second function object → failed data<br/>
В таком случае можно сделать цепочку объектов:

success data → first function object → second function object → success data
failed data → first function object → second function object → failed data
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:02.731131
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267645] => stdClass Object
                (
                    [ID] => 1267645
                    [TgID] => 26
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-16 22:45:19
                    [Text] => Я знаю 2 способа применения Railway Oriented Programming в Ruby:

Waterfall

Гем, который чейнит функциональные объекты и работает с возвращаемым значением. Выглядит это так:

<br/>Wf.new<br/>  .chain(user1: :user) { FetchUser.new(1) }<br/>  .chain(user2: :user) { FetchUser.new(2) }<br/>  .chain  {|outflow| puts(outflow.user1, outflow.user2)  } # report success<br/>  .on_dam {|error|   puts(error)      }                    # report error<br/>

Использование бизнес-транзакций

Бизнес-транзакции выполняют заданную логику шаг за шагом. Вам нужно просто создать класс, объявить шаги, вызвать этот класс с нужными данными — и дело в шляпе.

Учтите, что придется использовать объект, который будет хранить в себе success/failed состояние и значение. Это нужно для передачи состояния между шагами транзакции. Выбирайте сами — подойдет все, что угодно, например, собственный класс, хеш или Either монада (о монадах в следующий раз).

В Ruby есть 3 библиотеки, которые реализуют транзакции:

* operation из trailblazer
* solid use case gem
* dry-transaction

Я фанат dry-transaction, так как библиотека позволяет:
* использовать матчер для возвращаемого значения
* работать с контейнерами (о контейнерах тоже поговорим позже)
* использовать DI в виде инжектинга операций
* использовать один из трех видов шагов (возвращать любой результат как успешный, возвращать failed при exception и передавать данные и не думать о возвращаемом результате)
* создавать кастомные адаптеры для шагов
* многое другое

Использование библиотеки выглядит следующим образом:

<br/>class CreateUser<br/> include Dry::Transaction<br/><br/>  step :validate<br/>  step :persist<br/><br/>  def validate(input)<br/>    # ...<br/>  end<br/><br/>  def persist(input)<br/>    # ...<br/>  end<br/>end<br/><br/>CreateUser.new.call(name: &quotJane&quot, email: &quotjane@doe.com&quot) do |m|<br/>  m.success do |value|<br/>    # ...<br/>  end<br/><br/>  m.failure do |error|<br/>    # ...<br/>  end<br/>end<br/>

Пример рефакторинга знакомого класса с использованием транзакций.

## Запомнить

* Проблема с чейнингом логики возникает чаще, чем кажется.
* Только лишь вынос логики не поможет справиться с хаосом условий.
* Railway Oriented Programming позволяет забыть о проблеме хаоса условий, а также использовать функциональный подход обработки данных в приложении.
* Места применения Railway Oriented Programming — экшены и любые другие места, где у вас есть «поток» данных.
* Забудьте о велосипедах, возьмите одну из готовых библиотек для работы с бизнес-транзакциями.
* dry-transactions — самая навороченная и гибкая из этих библиотек.

## Ссылки

* Главная страница railway programming где можно найти кучу ссылок и примеров
* Описание того, как чейнить объекты &quotправильно&quot
* Практическое руководство функционального программирования в rails
* Пример railway programming в elexir
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1800
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:03.159097
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267643] => stdClass Object
                (
                    [ID] => 1267643
                    [TgID] => 24
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-16 22:45:18
                    [Text] => #functional_objects

В прошлый раз мы говорили о функциональных объектах и интеракторах. Сегодня рассмотрим случай, когда больше одного функционального объекта выполняют код последовательно в одном месте. Простой пример: экшен, который валидирует данные, сохраняет объект, вызывает нотификации и обрабатывает результат для нужного http-ответа. Например:
<br/>result = ValidateUser.new.call( ... )<br/>result = result.success? ? CreateUser.new.call( ... ) : result<br/><br/>if result2.success?<br/> redirect_to<br/>else<br/>  render<br/>end<br/>
Или просто код, который валидирует две разные сущности:
<br/>validation_post_result = ValidatePost.new.call( ... )<br/>validation_comments_result = ValidateComments.new.call( ... )<br/><br/>if validation_post_result.success? &amp&amp validation_comments_result.success?<br/>  ...<br/>end<br/>
Или же код, который запускает цепочку сервис объектов:
<br/>UserCreator.call(...) &amp&amp CommentCreator.call(...) &amp&amp ...<br/>
В этих примерах много лишних условий, которые приводятся к общему правилу: выполни пачку действий и верни общий статус. Выглядит такое решение сложным для понимания и будущей отладки, поэтому давайте попробуем упростить код.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:02.350104
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267642] => stdClass Object
                (
                    [ID] => 1267642
                    [TgID] => 23
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-13 18:11:25
                    [Text] => #functional_objects

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

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

Итак, что такое функциональный объект и зачем он нужен?
Это объект, который выглядит как функция. Соответственно, он принимает и возвращает какие-то данные. Также в идеале он должен быть чистым (мы же за функциональное программирование&#33)
Простые примеры функциональных объектов выглядят следующим образом:
<br/>convert_to_string = :to_s.to_proc # =&gt proc<br/>convert_to_string.call(123)       # =&gt &quot123&quot<br/><br/>pov2 = -&gt (x) { x ** 2 } # =&gt proc<br/>pov2.call(5)             # =&gt 25<br/>

Плюсы таких объектов:
- Изолированность. Функциональный объект ничего не знает об окружающем мире, следовательно, его связанность с остальным приложением минимальна.
- Чистота. В идеале такой объект реализует чистую функцию. Это значит, что одни и те же входящие данные каждый раз возвращают один и тот же результат вне зависимости от количества повторений.
- Отсутствие мутаций. Если нет мутаций, тестировать и поддерживать код будет проще. Есть несколько исключений, но о них поговорим позже.


## Зачем нужны функциональные объекты?

1. Вы могли не задумываться об этом раньше, но такие объекты находятся во всех проектах. Например, класс с единственным публичным методом также является функциональным объектом.

Эти сервис-объекты будут выглядеть примерно так:
<br/>class ToString<br/>  def call(object)<br/>    String(object)<br/>  end<br/>end<br/><br/>to_string = ToString.new<br/>to_string.call(:one) # =&gt one<br/>

А теперь сравните этот код с функциональным объектом :to_s.to_proc. Так, интеракторы, оперейшены и сервисы — это тоже функциональные объекты.

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

2. Если вы знаете функциональные языки, например, Haskell, вы знаете, что одни функции объединяются в другие со сложной логикой. Например:
<br/>plusOne = (+1)<br/>square = (^2)<br/>squareAndPlusOne = plusOne . square<br/>squareAndPlusOne(4) # =&gt (4 ^ 2) + 1 == 17<br/>

Есть энтузиасты, которые сделали библиотеку transproc. Библиотека дает возможность работать с функциональными объектами как в функциональных языках:
<br/>convert_to_json = Transproc(:load_json) # создаем первый функциональный объект<br/>symbolize_keys = Transproc(:symbolize_keys) # второй<br/>symbolize_json = convert_to_json &gt&gt symbolize_keys # объединяем их в один<br/><br/>symbolize_json.call([{&quotname&quot:&quotJane&quot}])<br/># =&gt [{ :name =&gt &quotJane&quot }]<br/>

На этом все. Если хотите узнать больше, прочтите ссылки в конце статьи.


## Запомнить

* Объект, который ведет себя как функция, называется функциональным. Логика, которую реализует объект — не так важна.
* Такие объекты реализуют изолированную и чистую функцию, которая не мутирует данные.
* Сервис-объекты, интеракторы или оперейшены — функциональные объекты.
* Функциональные объекты — это просто, они используются каждый день.
* Соединяя функциональные объекты в цепочки, можно создавать сложную логику из небольших изолированных частей.


## Ссылки

* Пример использование функциональных объектов от icelab
* Пример использования функциональных объектов и чистых функций для json конвертера
* Примеры функциональных подходов в руби
* Пост солника о Transproc
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1700
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:01.919109
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267641] => stdClass Object
                (
                    [ID] => 1267641
                    [TgID] => 22
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-10 23:45:38
                    [Text] => Этот подход называется dependency injection (DI) wiki и он «прокидывает» изолированную часть логики извне в объект или метод, что дает нам больше контроля.

Когда использовать
Если чувствуете, что появилась сложность со сколь угодно большим или маленьким участком кода.
Или чувствуете, что код мешает тестировать или расширить функционал. 
В этих случаях DI — ваш выбор.

Плюсы
- Тестирование упрощается во много раз
- Классы и методы становятся гибкими, их легко переиспользовать
- Изоляция кода. Оборачиваете логику в абстракцию — в следующий раз не нужно думать, как она работает
- DI превосходно работает с легаси кодом во время рефакторинга, так как не требует лишних библиотек и не меняет функционал
- Работает в любом языке, фреймворке, библиотеке.

Минусы
- Бесконтрольное использование DI усложняет систему из-за количества сущностей и связей. Такой код сложнее поддерживать
- Не всегда понятно, что делает объект, который инжектится
- Как и все абстракции, может потечь.

Ссылки
- Отличное обсуждение о DI
- Пост Солника о DI в руби
- Пример замены наследования на DI
- Еще один пример рефакторинга код с использованием DI
- Обратная сторона, dhh рассказывает о том, почему DI это зло
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:01.460732
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267640] => stdClass Object
                (
                    [ID] => 1267640
                    [TgID] => 21
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-10 23:44:24
                    [Text] => 1. Eсть класс, который возвращает данные для текущего времени:

<br/>class Foo<br/>  def get_users_for_current_time<br/>    User.where(created_at: Time.now)<br/>    # ...<br/>  end<br/>end<br/>

Не забудем о тестах. В текущей реализации придется мокать текущее время, чтобы получить адекватный результат:

<br/>describe Foo do<br/>  before do<br/>    @time = Time.now<br/>    Time.stub(:now).and_return(@time)<br/>  end<br/>  ...<br/>end<br/>


Код крутится, тесты мутятся. Но можно обнаружить пару минусов. Например: мок ведет себя себя как угодно, тесты становятся неконтролируемыми. Приходится использовать лишние абстракции в виде мока.

2. Eсть класс, который отображает логи:

<br/>class Logger<br/>  def call(message)<br/>    # ...<br/><br/>    print_message(message)<br/>  end<br/>end<br/>

Предположим, что в какой-то момент понадобилось отображать лог в JSON. Наследование спасает:

<br/>class JSONLogger &lt Logger<br/>  def call(message)<br/>    # ...<br/><br/>    to_json(message)<br/>  end<br/>end<br/>

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

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

Поэтому давайте попробуем изолировать эту логику и вынесем ее из объекта:

<br/>class Foo<br/>  def get_users_for_current_time(time: Time.now)<br/>    User.where(created_at: time)<br/>    # ...<br/>  end<br/>end<br/><br/>describe Foo do<br/>  let(:time) { Time.now }<br/>  it { expect(Foo.get_users_for_current_time(time)).to eq ... }<br/>  # ...<br/>end<br/>

И для другого примера:


class Logger
  def call(message, formatter: BaseFormatter)
    # ...
    formatter.call(message)
  end
end

Logger.new.call(message, formatter: BaseFormatter)
Logger.new.call(message, formatter: JsonFormatter)
Logger.new.call(message, formatter: XmlFormatter)
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:01.067666
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267639] => stdClass Object
                (
                    [ID] => 1267639
                    [TgID] => 20
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-10 23:42:53
                    [Text] => #functional_objects

В прошлый раз мы смотрели, как изолировать часть логики. Сегодня же поговорим о ситуации, когда у изолированной логики есть сложные для тестирования или контроля части.
Возможно, вы слышали о таком подходе, и сегодняшняя тема вызывает ассоциацию с миром javaEE, но поверьте, это знание выручает меня каждый день. Начнем с примеров.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:00.630081
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267638] => stdClass Object
                (
                    [ID] => 1267638
                    [TgID] => 19
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-06 16:42:29
                    [Text] => Ссылка оказалась битой, вот работающая:
https://gist.github.com/davydovanton/5fc0f213b56da3baa3a1e2c7b378a6e9
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:57:00.198556
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267637] => stdClass Object
                (
                    [ID] => 1267637
                    [TgID] => 18
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-06 16:38:06
                    [Text] => ## Практика

Я знаю о двух работающих библиотеках, в которых реализован этот паттерн:
1. Hanami-interactor
2. Гем interactor

Что из этого выбрать, решайте сами.

Лично мне нравится подход в Hanami, так как колбэки только усложняют код, а context объект добавляет еще одну штуку, о которой нужно постоянно думать. С hanami можно использовать просто класс с минимумом методов.

Как пример — рассмотрим экшен из open source проекта. И постараемся зарефакторить код с помощью interactor.
https://gist.github.com/davydovanton/5fc0f213b56da3baa3a1e2c7b378a6e

## Ссылки
1. How to Reduce Controller Bloat with Interactors in Ruby
2. A couple of words about interactors in Rails
3. Why using Interactor Gem is a Very Bad Idea
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:56:59.771064
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267636] => stdClass Object
                (
                    [ID] => 1267636
                    [TgID] => 17
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-06 16:34:19
                    [Text] => # Зачем это нужно
В первую очередь интерактор нужен, чтобы скрыть бизнес-логику в объекте с единственным публичным методом call. Пока это выглядит как типичный сервис-объект, но прелесть интерактора в том, что возвращаемый им объект (data object) содержит в себе статус (success или failed) и указанные геттеры.

# Какие проблемы решаются
Интерактор поможет решить три распространенных проблемы:

Изоляция бизнес-логики в отдельную сущность
Изолированные и имутабельные объекты легко тестируются. Объекты легко вызвать (в отличии от контроллеров и вью), объекты возвращают значение и при использовании dependency injection (DI) можно подменять логику в тестах вместо стабов. Я не фанат стабов и моков, но это отдельная тема для отдельного разговора.

С интеракторами легко шарить код без дублирования
Это плюс функциональных объектов, но так как разговор о интеракторах, то не будем говорить о другом. Как пример: в admin и в web апликейшене создается пост. Это один и тот же код в двух разных экшенах. С функциональными объектами, и интеракторами в частности, можно просто вызывать код без его дублирования.

Контроль флоу в зависимости от бизнес логики
Я писал, что интерактор возвращает data object со стейтом. Как можно догадаться, это используется. Например, надо сохранить объект, и в зависимости от того, сохранится успешно или нет, редиректнуть пользователя на указанный путь. Вот так это выглядит в приложении:


def call(params)
  result = Interactors::CreateDrug.new(params).call

  if result.successful?
    redirect_to routes.drug_path(result.drug.id)
  else
    redirect_to routes.drugs_path
  end
end
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1500
                    [ViewsLastDateUpdated] => 2018-02-10 07:56:59.375265
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267635] => stdClass Object
                (
                    [ID] => 1267635
                    [TgID] => 16
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-06 16:33:03
                    [Text] => Получил предложение писать о том, что использую в ежедневной работе, и добавить больше юзкейсов. 

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

Я фанат идеи функциональных объектов. В Rails я использовал сервис-объекты, в текущем dry-web проекте — оперейшены, а в ханами — интеракторы.

Текст будет касаться исключительно Hanami и hanami-interactor, но подход возможно использовать в других фреймворках.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1600
                    [ViewsLastDateUpdated] => 2018-02-10 07:56:58.887448
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267634] => stdClass Object
                (
                    [ID] => 1267634
                    [TgID] => 15
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-03 18:21:03
                    [Text] => ## После конференции

Дайте фидбэк
Поделитесь впечатлениями с организаторами, спикерами или просто друзьями. Важно быть активным и делиться знанием, ведь ради этого вы пришли на конференцю. Но не давайте необоснованного негативного фидбэка. Если что-то не понравилось, не оставляйте отзыв в духе «конференция ужасна, организаторы дураки». Вместо этого расскажите подробно о том, что не понравилось и какие вы видите пути решения этих проблем. Организация конференций — это тяжелый труд. Уважайте людей, которые сделали ивент для вас.

Пересмотрите доклады в спокойной обстановке
Законспектируйте доклад дома, если вам это поможет. Изучите подробнее тему интересного выступления. Если хотите помочь другим — напишите пост по теме доклада для друзей и коллег, которые по каким-то причинам не пришли на конференцию.

Доклады &#33= Лекции в универе
Сложно успеть рассказать объемную и (или) хардкорную тему за 25 минут. Поэтому мой совет:  воспринимайте доклады не как лекции, а как мотивацию двигаться в этом направлении. Как пример — доклад Тима с прошлогоднего reddotrubyconf, где рассказывалось про микс ООП и функциональных объектов в вебе. Это сложная и объемная тема. Понятно, что непросто рассказать все и сразу. Но для меня это выступление стало стимулом глубже разобраться в теме и узнать много нового.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1500
                    [ViewsLastDateUpdated] => 2018-02-10 07:56:58.499145
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267633] => stdClass Object
                (
                    [ID] => 1267633
                    [TgID] => 14
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-03 18:20:00
                    [Text] => ## На конференции

Не бойтесь общаться
Вы здесь для того, чтобы общаться. Доклады останутся на ютубе, а вот возможность задать интересные и важные вопросы будет только здесь и сейчас. Поэтому первый и главный совет — общайтесь.

Общайтесь где угодно, как угодно, о чем угодно. Если сложно просто так заговорить, то придумайте себе задачу. Например, на railsclub в прошлом году раздавали купоны на бесплатное пиво. Я бросил себе вызов собрать 20 таких купонов https://twitter.com/anton_davydov/status/789848018107367424, а в итоге собрал больше 30. Благодаря этому я познакомился с интересными людьми и весело провел день.

Интересуйтесь
Вопросы — наше все. Задавая вопросы, вы приближаетесь к решению своих проблем, а авторы библиотек чувствуют, что делают полезные вещи. Более того — понимание концепций и работы технологий лежит через правильные вопросы.

Говорите спасибо
Если уверены, что встретите автора библиотеки, которую используете на работе или в собственном проекте, найдите автора и скажите спасибо за его работу. Если не хотите говорить спасибо — дайте фидбэк. Опенсорс — это сложная работа, которая забирает много сил, поэтому поддерживайте разработчиков, которые помогают остальным.

Не конспектируйте доклады
Запись выступлений де-факто проводится на большинстве конференций и митапов. Это значит, что позже у вас будет шанс законспектировать интересные моменты выступления в спокойной обстановке и комфортном темпе. А также глубже изучить вещи, о которых говорит спикер. Не тратье время и силы на то, что можно сделать позже. Лучше вместо этого задайте вопрос, познакомьтесь с докладчиком или поделитесь личным опытом с другими.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1400
                    [ViewsLastDateUpdated] => 2018-02-10 07:56:58.070773
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267632] => stdClass Object
                (
                    [ID] => 1267632
                    [TgID] => 13
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-03 18:17:29
                    [Text] => ## До конференции

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

Изучите вещи, о которых не слышали.
Если в программе встречаются термины или слова, которые вам неизвестны, советую бегло погуглить. Это поможет быть «в теме» и быстрее понять, что хочет рассказать спикер.

Узнайте, будет ли проводиться препати, и если да — будьте там
Часто на препати приходят спикеры, а вот других участников там мало. Это хороший способ поговорить с докладчиками в неформальной обстановке. Кроме того, на препати можно познакомиться с другими участниками: после этого вы будете чувствовать себя не так одиноко на конференции.

Приходите за полчаса до начала
Во-первых, так ваша регистрация пройдет без суеты — основная масса людей приходит за 5-15 минут до начала.
Во-вторых, это отличный шанс спокойно выпить кофе и поймать интересных людей первыми.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1500
                    [ViewsLastDateUpdated] => 2018-02-10 07:56:57.593545
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267631] => stdClass Object
                (
                    [ID] => 1267631
                    [TgID] => 12
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-07-03 18:15:52
                    [Text] => Немного советов о том, что делать до, во время и после конференции, чтобы получить максимум пользы и впечатлений.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1300
                    [ViewsLastDateUpdated] => 2018-02-10 07:56:57.208102
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267630] => stdClass Object
                (
                    [ID] => 1267630
                    [TgID] => 7
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-06-24 21:49:57
                    [Text] => ## Карьера (и в open source тоже)

Конференция — скопление людей. Это выгодно компаниям: так проще заманивать к себе. Разработчикам это полезно тем, что можно неформально пообщаться с будущими коллегами. Например, мой знакомый устроился в Toptal только потому, что пришел на конференцию, где пересекся с разработчиками из этой компании.
 
А еще можно познакомиться с людьми, которые помогут в работе. Прочтите пост моей коллеги Марион в блоге Hanami (https://github.com/hanami/hanami.github.io/pull/267/files). Марион познакомилась с автором Hanami Лукой на конференции, и это знакомство привело к неожиданному результату. Вчера её презентацию оценили слушатели, а работа с опенсорсом помогает ей в решении ежедневных задач.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-02-10 07:56:56.82134
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267629] => stdClass Object
                (
                    [ID] => 1267629
                    [TgID] => 6
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-06-24 21:49:49
                    [Text] => ## Обмен опытом и / или идеями

Идея мало чего стоит, пока о ней никто не знает. Поделитесь идеей или работой, получите обратную связь и решите, что делать дальше. Пару лет назад Майк Перхам сделал прототип Sidekick, о котором рассказал на локальном митапе и получил фидбэк. Возможно, благодаря этому Sidekick теперь выглядит так.
 
Найдите автора технологии и спросите, как правильно использовать его проект. Каждый раз так делает Ник: допрашивает Матца и других разработчиков, как решить очередную проблему в Ruby коде.
 
Попробуйте выйти из зоны комфорта и узнать, как разработчики используют обыденную технологию в нестандартных ситуациях. Так, например, я узнал, что Ruby используется для инвалидных колясок (https://twitter.com/hanmd82/status/877735708798066688).
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1200
                    [ViewsLastDateUpdated] => 2018-02-10 07:56:56.43538
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267628] => stdClass Object
                (
                    [ID] => 1267628
                    [TgID] => 5
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-06-24 21:49:42
                    [Text] => ## Тусовка

Со временем приходит понимание, что общение в нашей работе важно. Оффлайн-мероприятия помогают комьюнити общаться как о технологиях, так и о жизни. А еще петь в караоке, пить пиво, заедая дурианом, и веселиться.
 
Два дня назад я впервые встретился вживую с коллегой по Hanami, Марион. За эти 2 дня, попутно решив насущные проблемы, связанные с проектом, я узнал от неё, что вегетарианство в Швейцарии намного выгоднее. Причина в том, что в Японии мало блюд, где нет мяса или рыбы.
 
С Ником, создателем фреймворка Trailblazer, мы познакомились на прошлогодней конференции Euruko. Я спросил, как у него дела с гемом для Hanami, и мы разговорились. За последние 3 дня, успев обсудить варианты использования Trailblazer и Hanami, мы выпили пива с дурианом, чего, по словам местных, делать ни в коем случае нельзя.
 
Иногда конференции — единственное место, где встречаются удаленные разработчики. Например, Railsclub объединяет кор-разработчиков Toptal, Evil Martians и других компаний. Разработчики Gitlab собираются на Euruko. На прошлой работе я увидел вживую коллегу из Таиланда только один раз – на RedDotRubyConf 2016.
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1100
                    [ViewsLastDateUpdated] => 2018-02-10 07:56:56.02676
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

            [1267627] => stdClass Object
                (
                    [ID] => 1267627
                    [TgID] => 4
                    [TgUserID] => 
                    [TgChatID] => -1001106477565
                    [DateAdded] => 2017-06-24 21:49:34
                    [Text] => Только что прошла сингапурская конференция http://reddotrubyconf.com, которую я посетил два раза: в прошлом году как участник, а в этом – как спикер. Так как впечатления и эмоции еще свежи, было бы логично поделиться мнением, связанным с конференциями, митапами и другими похожими ивентами.
 
Есть люди, уверенные в том, что подобные ивенты — трата времени. Я так не считаю, и вот почему:
                    [IsVisible] => 1
                    [DateEdited] => 
                    [Views] => 1100
                    [ViewsLastDateUpdated] => 2018-02-10 07:56:55.630002
                    [Views1] => 
                    [Views2] => 
                    [Views3] => 
                    [Views7] => 
                    [Views30] => 
                    [IsViewsAvailable] => 1
                    [Photo] => 
                    [Firstname] => 
                    [Lastname] => 
                    [Title] => 2pegramming
                    [PhotoSmall] => https://barzha.top/data/photos/AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg.jpg
                    [PhotoBig] => https://barzha.top/data/photos/AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg.jpg
                    [ChanPhotoSmall] => AQADAgATD-S3DgAE3VTcp3WuDXYG4gACAg
                    [ChanPhotoBig] => AQADAgATD-S3DgAE-LXX1PXu7OsI4gACAg
                    [Link] => pepegramming
                )

        )

    [StatsDaily] => Array
        (
            [0] => stdClass Object
                (
                    [DateTarget] => 2018-02-02
                    [Members] => 457
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [1] => stdClass Object
                (
                    [DateTarget] => 2018-02-03
                    [Members] => 456
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [2] => stdClass Object
                (
                    [DateTarget] => 2018-02-04
                    [Members] => 456
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [3] => stdClass Object
                (
                    [DateTarget] => 2018-02-05
                    [Members] => 457
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [4] => stdClass Object
                (
                    [DateTarget] => 2018-02-06
                    [Members] => 457
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [5] => stdClass Object
                (
                    [DateTarget] => 2018-02-07
                    [Members] => 457
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [6] => stdClass Object
                (
                    [DateTarget] => 2018-02-08
                    [Members] => 457
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [7] => stdClass Object
                (
                    [DateTarget] => 2018-02-09
                    [Members] => 457
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [8] => stdClass Object
                (
                    [DateTarget] => 2018-02-10
                    [Members] => 456
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [9] => stdClass Object
                (
                    [DateTarget] => 2018-02-11
                    [Members] => 458
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [10] => stdClass Object
                (
                    [DateTarget] => 2018-02-12
                    [Members] => 464
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [11] => stdClass Object
                (
                    [DateTarget] => 2018-02-13
                    [Members] => 465
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [12] => stdClass Object
                (
                    [DateTarget] => 2018-02-14
                    [Members] => 465
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [13] => stdClass Object
                (
                    [DateTarget] => 2018-02-15
                    [Members] => 464
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [14] => stdClass Object
                (
                    [DateTarget] => 2018-02-16
                    [Members] => 464
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [15] => stdClass Object
                (
                    [DateTarget] => 2018-02-17
                    [Members] => 464
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [16] => stdClass Object
                (
                    [DateTarget] => 2018-02-18
                    [Members] => 464
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [17] => stdClass Object
                (
                    [DateTarget] => 2018-02-19
                    [Members] => 463
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [18] => stdClass Object
                (
                    [DateTarget] => 2018-02-20
                    [Members] => 466
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [19] => stdClass Object
                (
                    [DateTarget] => 2018-02-21
                    [Members] => 466
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [20] => stdClass Object
                (
                    [DateTarget] => 2018-02-22
                    [Members] => 466
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [21] => stdClass Object
                (
                    [DateTarget] => 2018-02-23
                    [Members] => 466
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [22] => stdClass Object
                (
                    [DateTarget] => 2018-02-24
                    [Members] => 471
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [23] => stdClass Object
                (
                    [DateTarget] => 2018-02-25
                    [Members] => 471
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [24] => stdClass Object
                (
                    [DateTarget] => 2018-02-26
                    [Members] => 471
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [25] => stdClass Object
                (
                    [DateTarget] => 2018-02-27
                    [Members] => 471
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [26] => stdClass Object
                (
                    [DateTarget] => 2018-02-28
                    [Members] => 472
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [27] => stdClass Object
                (
                    [DateTarget] => 2018-03-01
                    [Members] => 472
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [28] => stdClass Object
                (
                    [DateTarget] => 2018-03-02
                    [Members] => 473
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [29] => stdClass Object
                (
                    [DateTarget] => 2018-03-03
                    [Members] => 470
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [30] => stdClass Object
                (
                    [DateTarget] => 2018-03-04
                    [Members] => 470
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [31] => stdClass Object
                (
                    [DateTarget] => 2018-03-05
                    [Members] => 471
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [32] => stdClass Object
                (
                    [DateTarget] => 2018-03-06
                    [Members] => 471
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [33] => stdClass Object
                (
                    [DateTarget] => 2018-03-07
                    [Members] => 472
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [34] => stdClass Object
                (
                    [DateTarget] => 2018-03-08
                    [Members] => 472
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [35] => stdClass Object
                (
                    [DateTarget] => 2018-03-09
                    [Members] => 472
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [36] => stdClass Object
                (
                    [DateTarget] => 2018-03-10
                    [Members] => 474
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [37] => stdClass Object
                (
                    [DateTarget] => 2018-03-11
                    [Members] => 473
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [38] => stdClass Object
                (
                    [DateTarget] => 2018-03-12
                    [Members] => 477
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [39] => stdClass Object
                (
                    [DateTarget] => 2018-03-13
                    [Members] => 481
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [40] => stdClass Object
                (
                    [DateTarget] => 2018-03-14
                    [Members] => 482
                    [Cost] => 0
                    [Messages] => 44
                    [Views] => 
                )

            [41] => stdClass Object
                (
                    [DateTarget] => 2018-03-15
                    [Members] => 482
                    [Cost] => 0
                    [Messages] => 48
                    [Views] => 
                )

            [42] => stdClass Object
                (
                    [DateTarget] => 2018-03-16
                    [Members] => 482
                    [Cost] => 0
                    [Messages] => 48
                    [Views] => 
                )

            [43] => stdClass Object
                (
                    [DateTarget] => 2018-03-17
                    [Members] => 482
                    [Cost] => 0
                    [Messages] => 48
                    [Views] => 
                )

            [44] => stdClass Object
                (
                    [DateTarget] => 2018-03-18
                    [Members] => 482
                    [Cost] => 0
                    [Messages] => 48
                    [Views] => 
                )

            [45] => stdClass Object
                (
                    [DateTarget] => 2018-03-19
                    [Members] => 488
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [46] => stdClass Object
                (
                    [DateTarget] => 2018-03-20
                    [Members] => 492
                    [Cost] => 0
                    [Messages] => 54
                    [Views] => 
                )

            [47] => stdClass Object
                (
                    [DateTarget] => 2018-03-21
                    [Members] => 494
                    [Cost] => 0
                    [Messages] => 54
                    [Views] => 
                )

            [48] => stdClass Object
                (
                    [DateTarget] => 2018-03-22
                    [Members] => 498
                    [Cost] => 0
                    [Messages] => 54
                    [Views] => 
                )

            [49] => stdClass Object
                (
                    [DateTarget] => 2018-03-23
                    [Members] => 499
                    [Cost] => 0
                    [Messages] => 54
                    [Views] => 
                )

            [50] => stdClass Object
                (
                    [DateTarget] => 2018-03-24
                    [Members] => 500
                    [Cost] => 0
                    [Messages] => 54
                    [Views] => 
                )

            [51] => stdClass Object
                (
                    [DateTarget] => 2018-03-25
                    [Members] => 507
                    [Cost] => 0
                    [Messages] => 54
                    [Views] => 
                )

            [52] => stdClass Object
                (
                    [DateTarget] => 2018-03-26
                    [Members] => 508
                    [Cost] => 0
                    [Messages] => 54
                    [Views] => 
                )

            [53] => stdClass Object
                (
                    [DateTarget] => 2018-03-27
                    [Members] => 508
                    [Cost] => 0
                    [Messages] => 54
                    [Views] => 
                )

            [54] => stdClass Object
                (
                    [DateTarget] => 2018-03-28
                    [Members] => 508
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [55] => stdClass Object
                (
                    [DateTarget] => 2018-03-29
                    [Members] => 509
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [56] => stdClass Object
                (
                    [DateTarget] => 2018-03-30
                    [Members] => 510
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [57] => stdClass Object
                (
                    [DateTarget] => 2018-03-31
                    [Members] => 517
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [58] => stdClass Object
                (
                    [DateTarget] => 2018-04-01
                    [Members] => 519
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [59] => stdClass Object
                (
                    [DateTarget] => 2018-04-02
                    [Members] => 522
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [60] => stdClass Object
                (
                    [DateTarget] => 2018-04-03
                    [Members] => 523
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [61] => stdClass Object
                (
                    [DateTarget] => 2018-04-04
                    [Members] => 524
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [62] => stdClass Object
                (
                    [DateTarget] => 2018-04-05
                    [Members] => 525
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [63] => stdClass Object
                (
                    [DateTarget] => 2018-04-06
                    [Members] => 526
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [64] => stdClass Object
                (
                    [DateTarget] => 2018-04-07
                    [Members] => 529
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [65] => stdClass Object
                (
                    [DateTarget] => 2018-04-08
                    [Members] => 529
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [66] => stdClass Object
                (
                    [DateTarget] => 2018-04-09
                    [Members] => 529
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [67] => stdClass Object
                (
                    [DateTarget] => 2018-04-10
                    [Members] => 531
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [68] => stdClass Object
                (
                    [DateTarget] => 2018-04-11
                    [Members] => 533
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [69] => stdClass Object
                (
                    [DateTarget] => 2018-04-12
                    [Members] => 534
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [70] => stdClass Object
                (
                    [DateTarget] => 2018-04-13
                    [Members] => 535
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [71] => stdClass Object
                (
                    [DateTarget] => 2018-04-14
                    [Members] => 536
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [72] => stdClass Object
                (
                    [DateTarget] => 2018-04-15
                    [Members] => 536
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [73] => stdClass Object
                (
                    [DateTarget] => 2018-04-16
                    [Members] => 538
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [74] => stdClass Object
                (
                    [DateTarget] => 2018-04-17
                    [Members] => 539
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [75] => stdClass Object
                (
                    [DateTarget] => 2018-04-18
                    [Members] => 539
                    [Cost] => 0
                    [Messages] => 56
                    [Views] => 
                )

            [76] => stdClass Object
                (
                    [DateTarget] => 2018-04-19
                    [Members] => 551
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [77] => stdClass Object
                (
                    [DateTarget] => 2018-04-20
                    [Members] => 556
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [78] => stdClass Object
                (
                    [DateTarget] => 2018-04-21
                    [Members] => 558
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [79] => stdClass Object
                (
                    [DateTarget] => 2018-04-22
                    [Members] => 561
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [80] => stdClass Object
                (
                    [DateTarget] => 2018-04-23
                    [Members] => 560
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [81] => stdClass Object
                (
                    [DateTarget] => 2018-04-24
                    [Members] => 560
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [82] => stdClass Object
                (
                    [DateTarget] => 2018-04-25
                    [Members] => 560
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [83] => stdClass Object
                (
                    [DateTarget] => 2018-04-26
                    [Members] => 560
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [84] => stdClass Object
                (
                    [DateTarget] => 2018-04-27
                    [Members] => 560
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [85] => stdClass Object
                (
                    [DateTarget] => 2018-04-28
                    [Members] => 560
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [86] => stdClass Object
                (
                    [DateTarget] => 2018-04-29
                    [Members] => 560
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [87] => stdClass Object
                (
                    [DateTarget] => 2018-04-30
                    [Members] => 560
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [88] => stdClass Object
                (
                    [DateTarget] => 2018-05-01
                    [Members] => 560
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [89] => stdClass Object
                (
                    [DateTarget] => 2018-05-02
                    [Members] => 560
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [90] => stdClass Object
                (
                    [DateTarget] => 2018-05-03
                    [Members] => 560
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [91] => stdClass Object
                (
                    [DateTarget] => 2018-05-04
                    [Members] => 561
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [92] => stdClass Object
                (
                    [DateTarget] => 2018-05-05
                    [Members] => 561
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [93] => stdClass Object
                (
                    [DateTarget] => 2018-05-06
                    [Members] => 561
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [94] => stdClass Object
                (
                    [DateTarget] => 2018-05-07
                    [Members] => 561
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [95] => stdClass Object
                (
                    [DateTarget] => 2018-05-08
                    [Members] => 565
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [96] => stdClass Object
                (
                    [DateTarget] => 2018-05-09
                    [Members] => 565
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [97] => stdClass Object
                (
                    [DateTarget] => 2018-05-10
                    [Members] => 565
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [98] => stdClass Object
                (
                    [DateTarget] => 2018-05-11
                    [Members] => 564
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [99] => stdClass Object
                (
                    [DateTarget] => 2018-05-12
                    [Members] => 564
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [100] => stdClass Object
                (
                    [DateTarget] => 2018-05-13
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [101] => stdClass Object
                (
                    [DateTarget] => 2018-05-14
                    [Members] => 564
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [102] => stdClass Object
                (
                    [DateTarget] => 2018-05-15
                    [Members] => 565
                    [Cost] => 0
                    [Messages] => 60
                    [Views] => 
                )

            [103] => stdClass Object
                (
                    [DateTarget] => 2018-05-16
                    [Members] => 565
                    [Cost] => 0
                    [Messages] => 64
                    [Views] => 
                )

            [104] => stdClass Object
                (
                    [DateTarget] => 2018-05-17
                    [Members] => 565
                    [Cost] => 0
                    [Messages] => 64
                    [Views] => 
                )

            [105] => stdClass Object
                (
                    [DateTarget] => 2018-05-18
                    [Members] => 564
                    [Cost] => 0
                    [Messages] => 64
                    [Views] => 
                )

            [106] => stdClass Object
                (
                    [DateTarget] => 2018-05-19
                    [Members] => 565
                    [Cost] => 0
                    [Messages] => 64
                    [Views] => 
                )

            [107] => stdClass Object
                (
                    [DateTarget] => 2018-05-20
                    [Members] => 565
                    [Cost] => 0
                    [Messages] => 64
                    [Views] => 
                )

            [108] => stdClass Object
                (
                    [DateTarget] => 2018-05-21
                    [Members] => 566
                    [Cost] => 0
                    [Messages] => 64
                    [Views] => 
                )

            [109] => stdClass Object
                (
                    [DateTarget] => 2018-05-22
                    [Members] => 567
                    [Cost] => 0
                    [Messages] => 64
                    [Views] => 
                )

            [110] => stdClass Object
                (
                    [DateTarget] => 2018-05-23
                    [Members] => 568
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [111] => stdClass Object
                (
                    [DateTarget] => 2018-05-24
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [112] => stdClass Object
                (
                    [DateTarget] => 2018-05-25
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [113] => stdClass Object
                (
                    [DateTarget] => 2018-05-26
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [114] => stdClass Object
                (
                    [DateTarget] => 2018-05-27
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [115] => stdClass Object
                (
                    [DateTarget] => 2018-05-28
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [116] => stdClass Object
                (
                    [DateTarget] => 2018-05-29
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [117] => stdClass Object
                (
                    [DateTarget] => 2018-05-30
                    [Members] => 562
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [118] => stdClass Object
                (
                    [DateTarget] => 2018-05-31
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [119] => stdClass Object
                (
                    [DateTarget] => 2018-06-01
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [120] => stdClass Object
                (
                    [DateTarget] => 2018-06-02
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [121] => stdClass Object
                (
                    [DateTarget] => 2018-06-03
                    [Members] => 564
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [122] => stdClass Object
                (
                    [DateTarget] => 2018-06-04
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [123] => stdClass Object
                (
                    [DateTarget] => 2018-06-05
                    [Members] => 562
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [124] => stdClass Object
                (
                    [DateTarget] => 2018-06-06
                    [Members] => 562
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [125] => stdClass Object
                (
                    [DateTarget] => 2018-06-07
                    [Members] => 563
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [126] => stdClass Object
                (
                    [DateTarget] => 2018-06-08
                    [Members] => 565
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [127] => stdClass Object
                (
                    [DateTarget] => 2018-06-09
                    [Members] => 564
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [128] => stdClass Object
                (
                    [DateTarget] => 2018-06-10
                    [Members] => 568
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [129] => stdClass Object
                (
                    [DateTarget] => 2018-06-11
                    [Members] => 569
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [130] => stdClass Object
                (
                    [DateTarget] => 2018-06-12
                    [Members] => 568
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [131] => stdClass Object
                (
                    [DateTarget] => 2018-06-13
                    [Members] => 568
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [132] => stdClass Object
                (
                    [DateTarget] => 2018-06-14
                    [Members] => 570
                    [Cost] => 0
                    [Messages] => 65
                    [Views] => 
                )

            [133] => stdClass Object
                (
                    [DateTarget] => 2018-06-15
                    [Members] => 571
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [134] => stdClass Object
                (
                    [DateTarget] => 2018-06-16
                    [Members] => 571
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [135] => stdClass Object
                (
                    [DateTarget] => 2018-06-17
                    [Members] => 572
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [136] => stdClass Object
                (
                    [DateTarget] => 2018-06-18
                    [Members] => 571
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [137] => stdClass Object
                (
                    [DateTarget] => 2018-06-19
                    [Members] => 572
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [138] => stdClass Object
                (
                    [DateTarget] => 2018-06-20
                    [Members] => 573
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [139] => stdClass Object
                (
                    [DateTarget] => 2018-06-21
                    [Members] => 573
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [140] => stdClass Object
                (
                    [DateTarget] => 2018-06-22
                    [Members] => 573
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [141] => stdClass Object
                (
                    [DateTarget] => 2018-06-23
                    [Members] => 573
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [142] => stdClass Object
                (
                    [DateTarget] => 2018-06-24
                    [Members] => 573
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [143] => stdClass Object
                (
                    [DateTarget] => 2018-06-25
                    [Members] => 574
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [144] => stdClass Object
                (
                    [DateTarget] => 2018-06-26
                    [Members] => 574
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [145] => stdClass Object
                (
                    [DateTarget] => 2018-06-27
                    [Members] => 574
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [146] => stdClass Object
                (
                    [DateTarget] => 2018-06-28
                    [Members] => 574
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [147] => stdClass Object
                (
                    [DateTarget] => 2018-06-29
                    [Members] => 576
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [148] => stdClass Object
                (
                    [DateTarget] => 2018-06-30
                    [Members] => 577
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [149] => stdClass Object
                (
                    [DateTarget] => 2018-07-01
                    [Members] => 577
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [150] => stdClass Object
                (
                    [DateTarget] => 2018-07-02
                    [Members] => 577
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [151] => stdClass Object
                (
                    [DateTarget] => 2018-07-03
                    [Members] => 578
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [152] => stdClass Object
                (
                    [DateTarget] => 2018-07-04
                    [Members] => 578
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [153] => stdClass Object
                (
                    [DateTarget] => 2018-07-05
                    [Members] => 581
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [154] => stdClass Object
                (
                    [DateTarget] => 2018-07-06
                    [Members] => 582
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [155] => stdClass Object
                (
                    [DateTarget] => 2018-07-07
                    [Members] => 582
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [156] => stdClass Object
                (
                    [DateTarget] => 2018-07-08
                    [Members] => 582
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [157] => stdClass Object
                (
                    [DateTarget] => 2018-07-09
                    [Members] => 583
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [158] => stdClass Object
                (
                    [DateTarget] => 2018-07-10
                    [Members] => 584
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [159] => stdClass Object
                (
                    [DateTarget] => 2018-07-11
                    [Members] => 584
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [160] => stdClass Object
                (
                    [DateTarget] => 2018-07-12
                    [Members] => 583
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [161] => stdClass Object
                (
                    [DateTarget] => 2018-07-13
                    [Members] => 583
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [162] => stdClass Object
                (
                    [DateTarget] => 2018-07-14
                    [Members] => 587
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [163] => stdClass Object
                (
                    [DateTarget] => 2018-07-15
                    [Members] => 589
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [164] => stdClass Object
                (
                    [DateTarget] => 2018-07-16
                    [Members] => 589
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [165] => stdClass Object
                (
                    [DateTarget] => 2018-07-17
                    [Members] => 588
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [166] => stdClass Object
                (
                    [DateTarget] => 2018-07-18
                    [Members] => 588
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [167] => stdClass Object
                (
                    [DateTarget] => 2018-07-19
                    [Members] => 588
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [168] => stdClass Object
                (
                    [DateTarget] => 2018-07-20
                    [Members] => 588
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [169] => stdClass Object
                (
                    [DateTarget] => 2018-07-21
                    [Members] => 587
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [170] => stdClass Object
                (
                    [DateTarget] => 2018-07-22
                    [Members] => 587
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [171] => stdClass Object
                (
                    [DateTarget] => 2018-07-23
                    [Members] => 588
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [172] => stdClass Object
                (
                    [DateTarget] => 2018-07-24
                    [Members] => 589
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [173] => stdClass Object
                (
                    [DateTarget] => 2018-07-25
                    [Members] => 589
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [174] => stdClass Object
                (
                    [DateTarget] => 2018-07-26
                    [Members] => 588
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [175] => stdClass Object
                (
                    [DateTarget] => 2018-07-27
                    [Members] => 588
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [176] => stdClass Object
                (
                    [DateTarget] => 2018-07-28
                    [Members] => 588
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [177] => stdClass Object
                (
                    [DateTarget] => 2018-07-29
                    [Members] => 587
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [178] => stdClass Object
                (
                    [DateTarget] => 2018-07-30
                    [Members] => 588
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [179] => stdClass Object
                (
                    [DateTarget] => 2018-07-31
                    [Members] => 587
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [180] => stdClass Object
                (
                    [DateTarget] => 2018-08-01
                    [Members] => 593
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [181] => stdClass Object
                (
                    [DateTarget] => 2018-08-02
                    [Members] => 593
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [182] => stdClass Object
                (
                    [DateTarget] => 2018-08-03
                    [Members] => 593
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [183] => stdClass Object
                (
                    [DateTarget] => 2018-08-04
                    [Members] => 593
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [184] => stdClass Object
                (
                    [DateTarget] => 2018-08-05
                    [Members] => 593
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [185] => stdClass Object
                (
                    [DateTarget] => 2018-08-06
                    [Members] => 594
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [186] => stdClass Object
                (
                    [DateTarget] => 2018-08-07
                    [Members] => 595
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [187] => stdClass Object
                (
                    [DateTarget] => 2018-08-08
                    [Members] => 596
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [188] => stdClass Object
                (
                    [DateTarget] => 2018-08-09
                    [Members] => 599
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [189] => stdClass Object
                (
                    [DateTarget] => 2018-08-10
                    [Members] => 599
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [190] => stdClass Object
                (
                    [DateTarget] => 2018-08-11
                    [Members] => 599
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [191] => stdClass Object
                (
                    [DateTarget] => 2018-08-12
                    [Members] => 599
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [192] => stdClass Object
                (
                    [DateTarget] => 2018-08-13
                    [Members] => 600
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [193] => stdClass Object
                (
                    [DateTarget] => 2018-08-14
                    [Members] => 601
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [194] => stdClass Object
                (
                    [DateTarget] => 2018-08-15
                    [Members] => 600
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [195] => stdClass Object
                (
                    [DateTarget] => 2018-08-16
                    [Members] => 600
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [196] => stdClass Object
                (
                    [DateTarget] => 2018-08-17
                    [Members] => 600
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [197] => stdClass Object
                (
                    [DateTarget] => 2018-08-18
                    [Members] => 600
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [198] => stdClass Object
                (
                    [DateTarget] => 2018-08-19
                    [Members] => 599
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [199] => stdClass Object
                (
                    [DateTarget] => 2018-08-20
                    [Members] => 599
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [200] => stdClass Object
                (
                    [DateTarget] => 2018-08-21
                    [Members] => 598
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [201] => stdClass Object
                (
                    [DateTarget] => 2018-08-22
                    [Members] => 598
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [202] => stdClass Object
                (
                    [DateTarget] => 2018-08-23
                    [Members] => 599
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [203] => stdClass Object
                (
                    [DateTarget] => 2018-08-24
                    [Members] => 597
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [204] => stdClass Object
                (
                    [DateTarget] => 2018-08-25
                    [Members] => 595
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [205] => stdClass Object
                (
                    [DateTarget] => 2018-08-26
                    [Members] => 595
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [206] => stdClass Object
                (
                    [DateTarget] => 2018-08-27
                    [Members] => 593
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [207] => stdClass Object
                (
                    [DateTarget] => 2018-08-28
                    [Members] => 593
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [208] => stdClass Object
                (
                    [DateTarget] => 2018-08-29
                    [Members] => 591
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [209] => stdClass Object
                (
                    [DateTarget] => 2018-08-30
                    [Members] => 592
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [210] => stdClass Object
                (
                    [DateTarget] => 2018-08-31
                    [Members] => 591
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [211] => stdClass Object
                (
                    [DateTarget] => 2018-09-01
                    [Members] => 591
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [212] => stdClass Object
                (
                    [DateTarget] => 2018-09-02
                    [Members] => 591
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [213] => stdClass Object
                (
                    [DateTarget] => 2018-09-03
                    [Members] => 591
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [214] => stdClass Object
                (
                    [DateTarget] => 2018-09-04
                    [Members] => 590
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [215] => stdClass Object
                (
                    [DateTarget] => 2018-09-05
                    [Members] => 590
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [216] => stdClass Object
                (
                    [DateTarget] => 2018-09-06
                    [Members] => 590
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [217] => stdClass Object
                (
                    [DateTarget] => 2018-09-07
                    [Members] => 590
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [218] => stdClass Object
                (
                    [DateTarget] => 2018-09-08
                    [Members] => 590
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [219] => stdClass Object
                (
                    [DateTarget] => 2018-09-09
                    [Members] => 590
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [220] => stdClass Object
                (
                    [DateTarget] => 2018-09-10
                    [Members] => 589
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [221] => stdClass Object
                (
                    [DateTarget] => 2018-09-11
                    [Members] => 588
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [222] => stdClass Object
                (
                    [DateTarget] => 2018-09-12
                    [Members] => 588
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [223] => stdClass Object
                (
                    [DateTarget] => 2018-09-13
                    [Members] => 589
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [224] => stdClass Object
                (
                    [DateTarget] => 2018-09-14
                    [Members] => 592
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [225] => stdClass Object
                (
                    [DateTarget] => 2018-09-15
                    [Members] => 592
                    [Cost] => 0
                    [Messages] => 66
                    [Views] => 
                )

            [226] => stdClass Object
                (
                    [DateTarget] => 2018-09-16
                    [Members] => 591
                    [Cost] => 0
                    [Messages] => 70
                    [Views] => 
                )

            [227] => stdClass Object
                (
                    [DateTarget] => 2018-09-17
                    [Members] => 590
                    [Cost] => 0
                    [Messages] => 70
                    [Views] => 
                )

            [228] => stdClass Object
                (
                    [DateTarget] => 2018-09-20
                    [Members] => 615
                    [Cost] => 0
                    [Messages] => 70
                    [Views] => 
                )

            [229] => stdClass Object
                (
                    [DateTarget] => 2018-09-21
                    [Members] => 618
                    [Cost] => 0
                    [Messages] => 70
                    [Views] => 
                )

            [230] => stdClass Object
                (
                    [DateTarget] => 2018-09-22
                    [Members] => 619
                    [Cost] => 0
                    [Messages] => 70
                    [Views] => 
                )

            [231] => stdClass Object
                (
                    [DateTarget] => 2018-09-23
                    [Members] => 619
                    [Cost] => 0
                    [Messages] => 70
                    [Views] => 
                )

            [232] => stdClass Object
                (
                    [DateTarget] => 2018-09-24
                    [Members] => 625
                    [Cost] => 0
                    [Messages] => 70
                    [Views] => 
                )

            [233] => stdClass Object
                (
                    [DateTarget] => 2018-09-25
                    [Members] => 635
                    [Cost] => 0
                    [Messages] => 74
                    [Views] => 
                )

            [234] => stdClass Object
                (
                    [DateTarget] => 2018-09-26
                    [Members] => 636
                    [Cost] => 0
                    [Messages] => 74
                    [Views] => 
                )

            [235] => stdClass Object
                (
                    [DateTarget] => 2018-09-27
                    [Members] => 643
                    [Cost] => 0
                    [Messages] => 74
                    [Views] => 
                )

            [236] => stdClass Object
                (
                    [DateTarget] => 2018-09-28
                    [Members] => 644
                    [Cost] => 0
                    [Messages] => 74
                    [Views] => 
                )

            [237] => stdClass Object
                (
                    [DateTarget] => 2018-09-29
                    [Members] => 642
                    [Cost] => 0
                    [Messages] => 78
                    [Views] => 
                )

            [238] => stdClass Object
                (
                    [DateTarget] => 2018-09-30
                    [Members] => 643
                    [Cost] => 0
                    [Messages] => 78
                    [Views] => 
                )

            [239] => stdClass Object
                (
                    [DateTarget] => 2018-10-01
                    [Members] => 646
                    [Cost] => 0
                    [Messages] => 78
                    [Views] => 
                )

            [240] => stdClass Object
                (
                    [DateTarget] => 2018-10-02
                    [Members] => 647
                    [Cost] => 0
                    [Messages] => 79
                    [Views] => 
                )

            [241] => stdClass Object
                (
                    [DateTarget] => 2018-10-03
                    [Members] => 647
                    [Cost] => 0
                    [Messages] => 79
                    [Views] => 
                )

            [242] => stdClass Object
                (
                    [DateTarget] => 2018-10-04
                    [Members] => 647
                    [Cost] => 0
                    [Messages] => 79
                    [Views] => 
                )

            [243] => stdClass Object
                (
                    [DateTarget] => 2018-10-05
                    [Members] => 648
                    [Cost] => 0
                    [Messages] => 79
                    [Views] => 
                )

            [244] => stdClass Object
                (
                    [DateTarget] => 2018-10-06
                    [Members] => 671
                    [Cost] => 0
                    [Messages] => 80
                    [Views] => 
                )

            [245] => stdClass Object
                (
                    [DateTarget] => 2018-10-07
                    [Members] => 672
                    [Cost] => 0
                    [Messages] => 80
                    [Views] => 
                )

            [246] => stdClass Object
                (
                    [DateTarget] => 2018-10-08
                    [Members] => 653
                    [Cost] => 0
                    [Messages] => 80
                    [Views] => 
                )

            [247] => stdClass Object
                (
                    [DateTarget] => 2018-10-09
                    [Members] => 654
                    [Cost] => 0
                    [Messages] => 80
                    [Views] => 
                )

            [248] => stdClass Object
                (
                    [DateTarget] => 2018-10-10
                    [Members] => 659
                    [Cost] => 0
                    [Messages] => 80
                    [Views] => 
                )

            [249] => stdClass Object
                (
                    [DateTarget] => 2018-10-11
                    [Members] => 661
                    [Cost] => 0
                    [Messages] => 80
                    [Views] => 
                )

            [250] => stdClass Object
                (
                    [DateTarget] => 2018-10-12
                    [Members] => 663
                    [Cost] => 0
                    [Messages] => 80
                    [Views] => 
                )

            [251] => stdClass Object
                (
                    [DateTarget] => 2018-10-13
                    [Members] => 663
                    [Cost] => 0
                    [Messages] => 80
                    [Views] => 
                )

            [252] => stdClass Object
                (
                    [DateTarget] => 2018-10-14
                    [Members] => 663
                    [Cost] => 0
                    [Messages] => 80
                    [Views] => 
                )

            [253] => stdClass Object
                (
                    [DateTarget] => 2018-10-15
                    [Members] => 663
                    [Cost] => 0
                    [Messages] => 80
                    [Views] => 
                )

            [254] => stdClass Object
                (
                    [DateTarget] => 2018-10-16
                    [Members] => 663
                    [Cost] => 0
                    [Messages] => 80
                    [Views] => 
                )

            [255] => stdClass Object
                (
                    [DateTarget] => 2018-10-17
                    [Members] => 663
                    [Cost] => 0
                    [Messages] => 82
                    [Views] => 
                )

            [256] => stdClass Object
                (
                    [DateTarget] => 2018-10-18
                    [Members] => 663
                    [Cost] => 0
                    [Messages] => 82
                    [Views] => 
                )

            [257] => stdClass Object
                (
                    [DateTarget] => 2018-10-19
                    [Members] => 666
                    [Cost] => 0
                    [Messages] => 82
                    [Views] => 
                )

            [258] => stdClass Object
                (
                    [DateTarget] => 2018-10-20
                    [Members] => 667
                    [Cost] => 0
                    [Messages] => 82
                    [Views] => 
                )

            [259] => stdClass Object
                (
                    [DateTarget] => 2018-10-21
                    [Members] => 668
                    [Cost] => 0
                    [Messages] => 83
                    [Views] => 
                )

            [260] => stdClass Object
                (
                    [DateTarget] => 2018-10-22
                    [Members] => 669
                    [Cost] => 0
                    [Messages] => 83
                    [Views] => 
                )

            [261] => stdClass Object
                (
                    [DateTarget] => 2018-10-23
                    [Members] => 669
                    [Cost] => 0
                    [Messages] => 83
                    [Views] => 
                )

            [262] => stdClass Object
                (
                    [DateTarget] => 2018-10-24
                    [Members] => 669
                    [Cost] => 0
                    [Messages] => 83
                    [Views] => 
                )

            [263] => stdClass Object
                (
                    [DateTarget] => 2018-10-25
                    [Members] => 669
                    [Cost] => 0
                    [Messages] => 83
                    [Views] => 
                )

            [264] => stdClass Object
                (
                    [DateTarget] => 2018-10-26
                    [Members] => 678
                    [Cost] => 0
                    [Messages] => 83
                    [Views] => 
                )

            [265] => stdClass Object
                (
                    [DateTarget] => 2018-10-27
                    [Members] => 680
                    [Cost] => 0
                    [Messages] => 83
                    [Views] => 
                )

            [266] => stdClass Object
                (
                    [DateTarget] => 2018-10-28
                    [Members] => 682
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [267] => stdClass Object
                (
                    [DateTarget] => 2018-10-29
                    [Members] => 680
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [268] => stdClass Object
                (
                    [DateTarget] => 2018-10-30
                    [Members] => 679
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [269] => stdClass Object
                (
                    [DateTarget] => 2018-10-31
                    [Members] => 680
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [270] => stdClass Object
                (
                    [DateTarget] => 2018-11-01
                    [Members] => 680
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [271] => stdClass Object
                (
                    [DateTarget] => 2018-11-02
                    [Members] => 684
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [272] => stdClass Object
                (
                    [DateTarget] => 2018-11-03
                    [Members] => 687
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [273] => stdClass Object
                (
                    [DateTarget] => 2018-11-04
                    [Members] => 689
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [274] => stdClass Object
                (
                    [DateTarget] => 2018-11-05
                    [Members] => 689
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [275] => stdClass Object
                (
                    [DateTarget] => 2018-11-06
                    [Members] => 689
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [276] => stdClass Object
                (
                    [DateTarget] => 2018-11-07
                    [Members] => 689
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [277] => stdClass Object
                (
                    [DateTarget] => 2018-11-08
                    [Members] => 689
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [278] => stdClass Object
                (
                    [DateTarget] => 2018-11-09
                    [Members] => 689
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [279] => stdClass Object
                (
                    [DateTarget] => 2018-11-10
                    [Members] => 689
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [280] => stdClass Object
                (
                    [DateTarget] => 2018-11-11
                    [Members] => 689
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [281] => stdClass Object
                (
                    [DateTarget] => 2018-11-12
                    [Members] => 689
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [282] => stdClass Object
                (
                    [DateTarget] => 2018-11-13
                    [Members] => 689
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [283] => stdClass Object
                (
                    [DateTarget] => 2018-11-14
                    [Members] => 689
                    [Cost] => 0
                    [Messages] => 84
                    [Views] => 
                )

            [284] => stdClass Object
                (
                    [DateTarget] => 2018-11-15
                    [Members] => 693
                    [Cost] => 0
                    [Messages] => 88
                    [Views] => 
                )

            [285] => stdClass Object
                (
                    [DateTarget] => 2018-11-16
                    [Members] => 692
                    [Cost] => 0
                    [Messages] => 88
                    [Views] => 
                )

            [286] => stdClass Object
                (
                    [DateTarget] => 2018-11-17
                    [Members] => 692
                    [Cost] => 0
                    [Messages] => 88
                    [Views] => 
                )

            [287] => stdClass Object
                (
                    [DateTarget] => 2018-11-18
                    [Members] => 691
                    [Cost] => 0
                    [Messages] => 88
                    [Views] => 
                )

            [288] => stdClass Object
                (
                    [DateTarget] => 2018-11-19
                    [Members] => 691
                    [Cost] => 0
                    [Messages] => 88
                    [Views] => 
                )

            [289] => stdClass Object
                (
                    [DateTarget] => 2018-11-20
                    [Members] => 694
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [290] => stdClass Object
                (
                    [DateTarget] => 2018-11-21
                    [Members] => 693
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [291] => stdClass Object
                (
                    [DateTarget] => 2018-11-22
                    [Members] => 693
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [292] => stdClass Object
                (
                    [DateTarget] => 2018-11-23
                    [Members] => 693
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [293] => stdClass Object
                (
                    [DateTarget] => 2018-11-24
                    [Members] => 697
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [294] => stdClass Object
                (
                    [DateTarget] => 2018-11-25
                    [Members] => 697
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [295] => stdClass Object
                (
                    [DateTarget] => 2018-11-26
                    [Members] => 696
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [296] => stdClass Object
                (
                    [DateTarget] => 2018-11-27
                    [Members] => 698
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [297] => stdClass Object
                (
                    [DateTarget] => 2018-11-28
                    [Members] => 702
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [298] => stdClass Object
                (
                    [DateTarget] => 2018-11-29
                    [Members] => 703
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [299] => stdClass Object
                (
                    [DateTarget] => 2018-11-30
                    [Members] => 702
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [300] => stdClass Object
                (
                    [DateTarget] => 2018-12-01
                    [Members] => 702
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [301] => stdClass Object
                (
                    [DateTarget] => 2018-12-02
                    [Members] => 702
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [302] => stdClass Object
                (
                    [DateTarget] => 2018-12-03
                    [Members] => 702
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [303] => stdClass Object
                (
                    [DateTarget] => 2018-12-04
                    [Members] => 700
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [304] => stdClass Object
                (
                    [DateTarget] => 2018-12-05
                    [Members] => 700
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [305] => stdClass Object
                (
                    [DateTarget] => 2018-12-06
                    [Members] => 700
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [306] => stdClass Object
                (
                    [DateTarget] => 2018-12-07
                    [Members] => 707
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [307] => stdClass Object
                (
                    [DateTarget] => 2018-12-08
                    [Members] => 708
                    [Cost] => 0
                    [Messages] => 89
                    [Views] => 
                )

            [308] => stdClass Object
                (
                    [DateTarget] => 2018-12-09
                    [Members] => 709
                    [Cost] => 70
                    [Messages] => 89
                    [Views] => 
                )

            [309] => stdClass Object
                (
                    [DateTarget] => 2018-12-11
                    [Members] => 711
                    [Cost] => 71
                    [Messages] => 91
                    [Views] => 
                )

            [310] => stdClass Object
                (
                    [DateTarget] => 2018-12-12
                    [Members] => 719
                    [Cost] => 71
                    [Messages] => 91
                    [Views] => 
                )

            [311] => stdClass Object
                (
                    [DateTarget] => 2018-12-13
                    [Members] => 718
                    [Cost] => 71
                    [Messages] => 91
                    [Views] => 
                )

            [312] => stdClass Object
                (
                    [DateTarget] => 2018-12-14
                    [Members] => 717
                    [Cost] => 71
                    [Messages] => 91
                    [Views] => 
                )

            [313] => stdClass Object
                (
                    [DateTarget] => 2018-12-15
                    [Members] => 718
                    [Cost] => 71
                    [Messages] => 91
                    [Views] => 
                )

            [314] => stdClass Object
                (
                    [DateTarget] => 2018-12-16
                    [Members] => 719
                    [Cost] => 71
                    [Messages] => 91
                    [Views] => 
                )

            [315] => stdClass Object
                (
                    [DateTarget] => 2018-12-17
                    [Members] => 718
                    [Cost] => 71
                    [Messages] => 91
                    [Views] => 
                )

        )

    [SortBy] => DateAdded
)
)<br>/var/www/barzha.top/controller/Controller.php:294 Controller_User->process(-1, stdClass Object
(
)
, , )<br>/var/www/barzha.top/index.php:67 Controller->run()<br> БАРЖА - честная биржа рекламы в Telegram — barzha.top
Информация о канале
2pegramming
Подписчиков: 719
Стоимость рекламы: 78.98 руб.
Описание канала: Грустно о программировании. Все проблемы сюда: @davydovanton

Medium: https://medium.com/pepegramming

Ссылки на конкретные посты: http://telegra.ph/Pepegramming-Contents-03-11
Обратная связь: https://goo.gl/forms/iUd1Gufq6WnTsaO62

Кто рекламировался:

Где рекламировался: