Обертка для итератора


#1

Работаю с API сервиса через rails-интерфейс примерно вот таким незамысловатым образом:

if request.GET[:tweet].present?
@array = params[:tweet].split(/[\r\n]+/)
@array.each do |i|
@client.update(i)
sleep rand(1..10)
end
end

Далее страничка висит энное количество времени, необходимое на публикацию твитов и sleep rand, после чего перезагружается и становятся доступными ответы:

#<Twitter::Tweet:0x007f5b8c7b0838> #<Twitter::Tweet:0x007f5b8c769258> #<Twitter::Tweet:0x007f5b8c72d618> #<Twitter::Tweet:0x007f5b8c6c8b28> #<Twitter::Tweet:0x007f5b8c66ff78> #<Twitter::Tweet:0x007f5b8c632f10>

Вопрос, как мне сейчас кажется, сугубо к рельсоводам; подскажите, если возможно, метод, либо ту или иную логику, следуя которой можно “облагородить” показанный порядок действий: хотелось бы ajax, вероятно, ну и preloader, пока скрипт отрабатывает свое. Возможно, посоветуете тот или иной гем, обертку, которые пришлись бы в тему этой задачи? - чтоб не кодить с чистого листа и не изобретать велосипед, рельсы ведь все-таки, спс.


(Kvokka) #2

ну так ты, навернео хочешь чтобы это все делалось в фоне. для этого хотябы activejob, ну или sidekiq в помощь
если с этой очередью может иметь дело еще какой-то микросервис, либо ты подразумеваешь что таких очередей будет много, то что-то типа rabbitmq в помощь.
безусловно, этот малекький клочек кода превратится в некоторый монстрик, но это ведь весело :wink:


#3

Засунул все токены в db, создав scaffold, как-то так:

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Key</th>
      <th>Secret</th>
      <th>Token</th>
      <th>Token secret</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @twitter_postings.each do |twitter_posting| %>
      <tr>
        <td><%= twitter_posting.name %></td>
        <td><%= @a = twitter_posting.key %></td>
        <td><%= @b = twitter_posting.secret %></td>
        <td><%= @c = twitter_posting.token %></td>
        <td><%= @d = twitter_posting.token_secret %></td>
        <td><%= link_to 'Show', twitter_posting %></td>
        <td><%= link_to 'Edit', edit_twitter_posting_path(twitter_posting) %></td>
        <td><%= link_to 'Destroy', twitter_posting, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

, теперь очень несложно, соответственно, постить в твиттер, равно как и менять на страничке ключи-токены:

<% @client = Twitter::REST::Client.new do |config| %>
 <% config.consumer_key = @a %>
 <% config.consumer_secret = @b %>
  <% config.access_token = @c %>
  <% config.access_token_secret = @d %>
    <% end %>

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

<% @twitter_postings.each do |twitter_posting| %>
<%= select_tag "id", options_for_select([ "#{@a} #{@b} #{@c} #{@d}"]) %>
<% end %>

Т.е. примитивная идея в том, чтобы выбирать нужную четверку ключей и отправлять их через ту же самую форму, подхватывая затем из парамсов. А как еще? Хотелось бы реализовать селектами возможность выбора из нескольких введенных через веб-страничку ключей аккаунтов. Подскажи, @kvokka . Или же тег селект невозможно использовать в паре с each? рыщу экзамплы, но че-то пока не нахожу.


(Kvokka) #4

по-первых, если у тебя дофига инстанс-переменных, то это уже запах.
И лечить это придется каким-нить презентером(декоратором, если там еще и логики навалено) этого треша.
тоесть какой-нить class TwitterPresenter, куда ты запихиваешь все это, и его инстанс уже ты передаешь во вьюху. и там уже @presenter.foo и прочая. ибо даже такое вроде бы примитивное дело как "#{@a} #{@b} #{@c}" уже будет плохо рефакториться и вообще саппортиться.
так что как результат, там где надо ты уже будешь вызывать как-то типа @presenter.key ну или вроде того. а для селектов ты уже приделываешь для этого презентера (походу, выросшему до декоратора), имя или id или uuid по вкусу и выбираешь тот презентер, что тебе по нраву.
Вооще возьми за правило- если 2 переменных, это еще терпимо, если 3+ то это уже новый класс.


#5

Погодь, не так быстро. Вводные: создав для этой цели scaffold, пишем в db ряд стрингов, являющихся токенами-кеями первого акка, затем то же самое второго, третьего, etc. Вопрос: ты видишь еще какой-то адекватный способ для аппликухи поймать переменные/токены того или иного акка (по выбору пользователя) при выполнении, кроме как итерировать их, достав из bd, в селекты, и послав их в хандлер посредством формы? Это действительно оптимальный путь? я как-то сомневаюсь пока что.


(Kvokka) #6

а какая проблема в том, что будет сразу несколько акков выведено?
обычный сценарий, что у юзера 1 твиттер. редко 2. если же 100500 то будет работать хреново и быть может даже совсем уныло, но разве это вообще нужно рассматривать? ограничие искусственное врубить и все.
если тебя прет сделать все красиво, то тогда ты даешь каждому комплекту токенов отдельные имя и оно имеют скоуп на какого-то юзера, чтобы по user.tokens это все лежало. и там уже ты в форме по 1 или все сразу ты их выводишь.

для первого описанного случая токены можно и не именовать и не давать ид, так проще и нацелено на то, что наврятли у кого-то будет 3+ наборов токенов. если же токенов будет как глязи, то тогда идти придется по пути 2. и да, для второго пути все это уже по идее будет запитано на js чуть менее, чем полностью.


#7

не, в корне пагубная и ошибочная логика. :slight_smile:
У кого 1 твиттер, тому отложенный кросспостинг явно не нужен.
А у меня их пять штук, и число фолловеров в каждом от 10К до 20К, что-то вроде того… и это еще более чем скромно.

ну наверное ты прав.
Вот так чуть более пристойно вроде бы получилось… и без js пока что, зацени, и презентер, думаю, не особо нужен, если вот что-то типа так? ну подчистить еще, select_tag там и прочее, что-то в контроллер всяко уберется, но принцип такой: из db первоначально вытаскиваем дропдаун-список названий аккаунтов, и по нему же потом запитываем аппликуху ключами:

<form action='/twitter_postings' method='get'>
  <fieldset><legend>Twitter Tweet</legend>
    <textarea type='text' placeholder='Tweet to post ...' name='tweet' ></textarea><br>
    <p><select name="select" >
    <% @twitter_postings.each do |twitter_posting| %>
        <option value="<%= twitter_posting.key %> <%= twitter_posting.secret %> <%= twitter_posting.token %> <%= twitter_posting.token_secret %>"><%= twitter_posting.name %></option>
    <% end %>
    </select></p>
    <input type="submit" value = "tweet" />
  </fieldset>
</form>


<% if request.GET[:tweet].present? %>

<% @client = Twitter::REST::Client.new do |config| %>
 <% config.consumer_key = params['select'].split.first %>
 <% config.consumer_secret = params['select'].split.second %>
  <% config.access_token = params['select'].split.third %>
  <% config.access_token_secret = params['select'].split.fourth %>
    <% end %>


<% @array = params[:tweet].split(/[\r\n]+/) %>
  <% @array.each do |i| %>
    <%= @client.update(i) %>
  <% sleep rand(1..10) %>
  <% end %>
   <% end %>

(Kvokka) #8

ну, деталей твоей приложухи я не знаю, так что тут уже ой )

  • твиттер у меня только для “галочки”, как многие другие социальные сети. все это для меня read_only, как-то не нужны мне фоловеры для прикола, а зарабатывать на этом я не готов.

теперь по существу. все эти обращения к params во вью это говно. нет, это не плохо, это именно говно. такой вот выкрутас request.GET[:tweet].present? вообще странен. это же вью! тут ты только отображаешь то, что уже давно известно. никакой логики. просто вывести наружу.
далее- @client = Twitter::REST::Client.new do |config| тоже трешняк. меняется синтаксис гема и ты лезешь во вью. хотя, по сути, вью не изменился. изменилась логика. тоесть должна меняться модель, но у тебя- вьюха. потенциальный геморой. нет, не сегодня или завтра, а через годик, когда ты об этом наглухо забудешь. и ты или кто-то другой протрахается с обновлением несколько часов, вместо нескольких минут.

  • если гем где-то сделает raise, то это будет во вью. опять же, это проблема модели, а у тебя это проблема отображения.

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

ну а по сути- если по скорости тебе канает так, то и нихай так и будет. наведи только мурафет перед деплоем, и в бой )


#9

дык не, ща разложим все по полочкам, в смысле по MVC раскидаем.


#10

@kvokka , этот блок кода без никаких изменений переносится в def index и работает там совершенно без проблем, равно как и “выкрутас” можно записать короче и без всяких там get или post, разумеется. Я просто не ожидал, что акцентируешься на этом. Иных возражений по данной логике нет?


(Kvokka) #11

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


#12

Насчет воркера - да, возможно. Но что ты понимаешь под термином “неконкурентная рассылка”, я, признаться, не понял.


(Kvokka) #13

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


#14

одновременно можно, но потенциально даст прирост гемора, а не скорости; твиттер не одобряет сингулярности, в качестве ответки очень легко может воспоследовать бан аккаунта.

И все же. Больше кода. Typhoeus позволяет многопоточность ( hydra = Typhoeus::Hydra.hydra ), ты его имел в виду, или что-то другое?


(Kvokka) #15

ну так я и говорил сразу, что если со скоростью нет вопроса, то нафиг этот гемор )

я имел ввиду больше воркеров, пусть они парятся. то есть юзер что-то там сделал, получил свое законное “все будет” и тем временем очередь воркеров пошла. если апа еще не особо лютая, то прям локально, если побольше, то в какую-нить очередь (bunny например) и оттуда уже в рассыльные автоматы. это если уже к процессу подходить по-взрослому. ведь никто и не говорит, что у тебя должен быть 1 хаб рассылки. но это так, если уже упороться по скорости и это реально надо.

а лепить самому нечто на Typhoeus или Celluloid, конечно, можно… но скорее всего за такое дадут пиздюлей.


#16

Добавил materialize и webpacker, совсем уже понтово получилось… еще ajax и preloader прикрутил, соответственно. Как думаешь, @kvokka , может, в rails можно было как-то короче, чем я по старинке сделал? или вьюха она и есть вьюха? -

<div class="progress" style="display:none;">
      <div class="indeterminate"></div>
  </div>

  <script type="text/javascript" language="javascript">
 	function call() {
 	  var msg   = $('#twitsform').serialize();
        $.ajax({
          type: 'GET',
          url: '/tweets',
          data: msg,
          beforeSend: function(){
       $('.progress').show()
   },
          success: function(data) {
            alert('Твиты опубликованы'),
            $('.progress').hide();
          },
          error:  function(xhr, str){
	    alert('Стоп, херня возникла: ' + xhr.responseCode),
	    $('.progress').hide();
          }
        });
 
    }
</script>

(Kvokka) #17

с js у меня только 1 сложность. всегда когда на нем пишу получается какое-то говно.
в лучшем случае оно работает.

как по мне ванильный es5, или 6- одна ебола.
я не умею его готовить красиво. и всячески стараюсь откреститься от этой напасти.

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


#18

Планирую двигаться дальше по пути создания комбайна для управления акками твиттера, а-ля Твидиум Леонида Кофмана (проект закрылся де-факто год назад, Твиттер, сцуко, дожал; сперва ходили упорные слухи о судебном процессе, но хватило, по-видимому, технических средств; очень не любит Твиттер, когда вместо реальных пользователей - скрипты). Хотя бы для домашнего использования пока что сделаю, проект ведь некоммерческий, в свободное время лабаю. А дальше будем поглядеть.

Постинг, походу, получился, materialize и webpacker неплохо смотрятся, добавил к вышеописанному еще devise и active admin, вообще круть. )) Для коммерческих целей, вероятно, в таком виде не годится, но в качестве демки для подробного исследования реакций Твиттера - вполне.

На очереди инструментарий анфолловинга. Кто не в теме, это примерно так: если некий твиттер-акк у тебя во френдах, а ты у него - нет, то такой аккаунт надо убрать из своих френдов, т.е. анфолловить. Технически все несложно, но вот архитектура приложения мне пока совершенно не ясна… хотелось бы уложиться в лимиты бесплатного herokuapp прежде всего, это первое. Кое-что попробовал, походу, процесс способен оказаться ресурсоемким. И второе: посоветуйте, пожалуйста, некую логику… делать, что ли, два массива (фолловинги и фолловеры), и сравнивать их? или сначала записывать в базу, а потом сравнивать? или помещать в db уже результат сравнения, и затем по этому списку анфолловить? Или?.. буду благодарен, короче, за разумные идеи: спрашивал Кофмана, не собирается ли передавать проект в open source, коли уж закрылся, получил в ответ короткое “нет”. :frowning:


(Kvokka) #19

pet-project это вообще зашибись )

относительно хранения- я как всегда за то, чтобы данные хранить в максимально первозданном виде и перерабатывать ух уже по мере необходимости. если переработка постоянная и ресурсоёмкая- можно ее кешировать и даже хранить в базе (с должными ограничениями).
про массивы еще- есть 2 клевый метода (стандартных, кстати), это | и & . рекомендую :wink:

в продакшене такое в текущем виде не взлетит и будет инста забанено, но для себя от можно

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

опенсорс- это нечто, где совместная выгода, а от публикации дохлого проекта в опенсорс нет выгоды его создателю, как итог нет мотивации


#20

Ну не знаю, всякое бывает:

11 ноября 2009 года Samsung объявил о том, что с 2010 года отказывается от использования Symbian, а вместо неё наряду с Android и Windows Mobile будет использовать ОС Bada собственной разработки.
4 февраля 2010 года Symbian Foundation объявила, что ОС Symbian становится полностью открытой (Open Source) и бесплатной (Freeware).