среда, 1 февраля 2012 г.

rabbitmq и ha для очереди

Rabbitmq передает сообщения. И сообщения частенько очень важные.
Что б не терять их, при отказе железки, можно использовать rabbitmq cluster.
Но есть один ньюанс - в кластере, по умолчанию, очередь хранится на одной ноде.
Добавляем в опции очереди x-ha-policy => all и теперь копии очереди есть на всех нодах и если одна упадет , сообщения не потеряются.
Клиентская программа должна знать куда переподключаться в случае падения и использовать подтверждение о получении сообщения.

суббота, 28 января 2012 г.

deploy spree to openshift

Instruction how to deploy spreecommerce to openshift
work with ruby 1.8.7 because openshift work with this version.
ruby 1.9. hash syntax will create problem in openshift cloud

1. create application on openshift
rhc-create-app -a spree -t rack-1.1
Создалось приложение в облаке редхата и папочка spree с простым rack приложением.
А нам надо создать rails приложение с гемами spree.
rm -f ./spree/README
rails new ./spree -d postgresql -f
Обратите внимание - используем постгрес(дело вкуса конечно,но все ж знают, что постгрес вкуснее)
2. add database(postgresql)
rhc-ctl-app -a spree -e add-postgresql-8.4
Если понадобится удалить подключенную базу
rhc-ctl-app -a spree -e remove-postgresql-8.4
По завершению мы получим данные для подключения к базе.
Их стоит записать в spree/config/database.yml
и не забудьте добавить поле host и вписать ip из подобной строки Connection URL: postgresql://127.1.13.1:5432
И настроить базу для development режима
development:
  adapter: sqlite3
  database: db/dev.sqlite3

И в Gemfile добавляем гем sqlite3 для группы development
теперь перемещаемся в папку spree
bundle install && bundle exec rails g spree:install
добавляем сприи в Gemfile
echo "gem 'spree', :git => 'git://github.com/spree/spree.git'" >> ./Gemfile
Gemfile.lock должен быть актуальным
bundle install
mkdir public/images && cp public/favicon.ico public/images/favicon.ico
RAILS_ENV=development bundle exec rake assets:precompile

добавить код в .openshift/action_hooks/deploy

pushd ${OPENSHIFT_REPO_DIR} > /dev/null
RAILS_ENV=production bundle exec rake db:migrate -y
RAILS_ENV=production bundle exec rake db:seed -y
popd > /dev/null


и теперь пушим git push
после удачного деплоя очищаем .openshift/action_hooks/deploy

first request will broken - it is problem on timeout parameter in proxy server.
but exist problem with timeout

P.S.
after few updates require use this database.yml


adapter: <%=ENV['OPENSHIFT_DB_TYPE']%>
encoding: utf8
host: <%=ENV['OPENSHIFT_DB_HOST']%>
port: <%=ENV['OPENSHIFT_DB_PORT']%>
socket: <%=ENV['OPENSHIFT_DB_SOCKET']%>
database: <%=ENV['OPENSHIFT_APP_NAME']%>
username: <%=ENV['OPENSHIFT_DB_USERNAME']%>
password: <%=ENV['OPENSHIFT_DB_PASSWORD']%>

воскресенье, 14 августа 2011 г.

Кеширование в мультидоменном веб приложении

добавляем в контроллер

before_filter { |c|
   c.class.page_cache_directory = "#{RAILS_ROOT}/public/#{c.request.host}"
}

в логах сразу видим как пишется

Write fragment views/sub.domain.com/s/test (2.6ms)

и при повторном запросе

Read fragment views/sub.domain.com/s/test (0.9ms)

Итого: с 300мс до 10 мс рендеринг страницы.

среда, 3 августа 2011 г.

Получение в SproutCore нескольких массивов данных одним запросом к бакэнду.

Получение нескольких массивов данных одним запросом к бакэнду.

Допустим, у нас есть приложение Blog, в котором есть модели Blog.Post и Blog.Quote. Пусть они выводятся сервером по адресу '/records.json' и в json выглядят вот так:
{posts: [{post1}, {post2}, {post3}], quotes: [{quote1}, {quote2}, {quote3}]}
Что бы уменьшить количество запросов к серверу и тем самым увеличить скорость получения данных, нам нужно немного изменить datasource приложения.
Шаг 1. Создать запрос:
Blog.QUERY_POSTS_AND_QUOTES = SC.Query.local([Blog.Post, Blog.Quote]);
В SC.Query.local можно передавать не только один тип записи, но и массив типов.
Шаг 2. Создать хэш-таблицу с ссылками на данные и типами записей:
Это шаг нужен что бы избежать огромного количества if в функции fetch()

Blog.Datasource = SC.DataSource.extend({
   urls: {
     posts_and_quotes: { url: '/records.json', records: ['posts', 'quotes'] }
   },
});

Так же нам нужно поправить шан запрос, установив ему атрибум id, соответствующий имени одного из элементов urls, в данном случае posts_and_quotes:
Blog.QUERY_POSTS_AND_QUOTES = SC.Query.local([Blog.Post,Blog.Quote]).set('id','posts_and_quotes');
Шаг 3. Пишем fetch():

fetch: function(store, query) {
   //Получаем id запроса и берём из urls ссылку для запроса в соответствии с этим id
   var id = query.get('id');
   //Если id не задан, значит запрос не должен обращаться к серверу
   if(!id) return NO;
   var url = this.urls[id]['url'],
     funcs = this.get('fetchDidComplete'),
     //Если нужна особая обработка какого нибудь запроса то в fetchDidComplete
     //нужно добавить функцию, имя которой совпадает с id (ещё одна хэш-таблица).
     //Если имя не указано, то по завершении запроса будет вызвана default функция

     func = funcs[id] || funcs['default'];
   SC.Request.getUrl(url).json().notify(this, func, store, query).send();
   return YES;
}



Шаг 4. Обрабатываем полученные данные:

fetchDidComplete: {
   'default': function(response, store, query) {
     if (SC.ok(response)) {
       //В зависимости от id берём нужные записи из полученного json
       var body = this.urls[query.get('id')]['records'];
       //Получаем тип записи
       var recordType = query.get('recordType');
       //Если тип не задан, значит их несколько и они хранятся в recordsTypes
       if (!recordType) {
       var recordTypes = query.get('recordTypes'); //Получаем типы записей
       jQuery.each(body, function(i) {
       //Загружаем в store записи разных типов. Порядок выдачи записей разных типов сервером
       //и порядок этих типов в запросе должны совпадать. Т.е.:
       //SC.Query.local[Scm.Product, Scm.Taxon] и {products: [{product1}, {product2}], taxons: [{taxon1}, {taxon2}]}

       store.loadRecords(recordTypes[i], response.get('body')[body[i]]); });
     }
     else {
       store.loadRecords(recordType, response.get('body'));
     }
     store.dataSourceDidFetchQuery(query);
     } else store.dataSourceDidErrorQuery(query, response);
   }
}


Шаг 5. Используем новый запрос в приложении:
var data = Blog.store.find(Blog.QUERY_POSTS_AND_QUOTES);
Теперь, что бы получить отдельно posts, а отдельно quotes используем scoped queries:

var posts = data.find(SC.Query.local(Blog.Post);
var quotes = data.find(SC.Query.local(Blog.Quote);

Ну и теперь можно заполнить ArrayController'ы приложения полученными данными:

Blog.postsController.set('content', posts);
Blog.quotesController.set('content', quotes);

Полностью datasource будет выглядеть так:
Blog.Datasource = SC.DataSource.extend({
urls: {
   posts_and_quotes: { url: '/records.json', records: ['posts', 'quotes'] }
},
fetch: function(store, query) {
   //Получаем id запроса и берём из urls ссылку для запроса в соответствии с этим id
   var id = query.get('id');
   //Если id не задан, значит запрос не должен обращаться к серверу
   if(!id) return NO;
   var url = this.urls[id]['url'],
     funcs = this.get('fetchDidComplete'),
     //Если нужна особая обработка какого нибудь запроса то в fetchDidComplete
     //нужно добавить функцию, имя которой совпадает с id (ещё одна хэш-таблица).
     //Если имя не указано, то по завершении запроса будет вызвана default функция
     func = funcs[id] || funcs['default'];
   SC.Request.getUrl(url).json().notify(this, func, store, query).send();
   return YES;
},
fetchDidComplete: {
   'default': function(response, store, query) {
   if (SC.ok(response)) {
     //В зависимости от id берём нужные записи из полученного json
     var body = this.urls[query.get('id')]['records'];
     //Получаем тип записи
     var recordType = query.get('recordType');
     //Если тип не задан, значит их несколько и они хранятся в recordsTypes
     if (!recordType) {
       var recordTypes = query.get('recordTypes'); //Получаем типы записей
       jQuery.each(body, function(i) {
       //Загружаем в store записи разных типов. Порядок выдачи записей разных типов сервером
       //и порядок этих типов в запросе должны совпадать. Т.е.:
       //SC.Query.local[Scm.Product, Scm.Taxon] и {products: [{product1}, {product2}], taxons: [{taxon1}, {taxon2}]}
       store.loadRecords(recordTypes[i], response.get('body')[body[i]]); });
     }
     else {
       store.loadRecords(recordType, response.get('body'));
     }
     store.dataSourceDidFetchQuery(query);
     } else store.dataSourceDidErrorQuery(query, response);
     }
   },
});

вторник, 2 августа 2011 г.

Tradefast & Sproutcore 1.6: Создание и использование тем

Tradefast & Sproutcore 1.6: Создание и использование тем
В этом небольшом руководстве описывается процесс создания и последующего использования своей темы для sproutcore-приложения, импользующего handlebars в качестве шаблонизатора.

В самом простом (и наиболее часто используемом) случае, тема - это набор css-стилей. Помимо этого темы могут заменять DOM, сгенерированный через views приложения.

Создадим новую тему командой sc-gen theme и назовём её tradefast-red:

sc-gen theme tradefast-red
~ Created directory at themes/tradefast_red
~ Created directory at themes/tradefast_red/resources
~ Created file at themes/tradefast_red/resources/theme_styles.css
~ Created file at themes/tradefast_red/theme.js
~ Created file at themes/tradefast_red/Buildfile
Your theme is now ready to use!


Sproutcore добавил в /themes (если этой папки нет, то она так же будет сгенерирована) папку /tradefast_red, в которой хранятся все необходимые для темы файлы. В main.js определяется новая тема и добавляется в приложение:

TradefastRed = SC.AceTheme.create({
name: 'tradefast-red'
});
SC.Theme.addTheme(TradefastRed);


По-умолчанию новая тема создаётся как под-тема темы Ace, которая не нужна, если приложение использует handlebars. Поэтому лучше удалить ‘Ace’ из определения темы:
TradefastRed = SC.Theme.create

Так же стоит убрать ace из tradefast_red/Buildfile:
Было: config :tradefast_red, :css_theme => 'ace.tradefast-red'
Стало: config :tradefast_red, :css_theme => 'tradefast-red'

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

Что бы использовать тему в приложении, нужно указать её в Buildfile самого приложения, который лежит на одном уровне с папками /themes и /apps:
config :all, :required => :sproutcore', :theme => "tradefast_red"
Обратите внимание, что имя темы совпадает с именем папки с темой в /themes/. Не перепутайте с именем, указанном в main.js. :theme => “tradefast-red” не сработает.

Остаётся только перезапустить сервер sc-server и посмотреть на новую тему в действии.

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

четверг, 14 июля 2011 г.

latest xen 4 and libvirt.

For connect libvirt to xen4 on fedora15 require install latest version of libvirt.
Today it is
rpm -Uvh http://kojipkgs.fedoraproject.org/packages/libvirt/0.9.3/2.fc16/x86_64/libvirt-0.9.3-2.fc16.x86_64.rpm http://kojipkgs.fedoraproject.org/packages/libvirt/0.9.3/2.fc16/x86_64/libvirt-python-0.9.3-2.fc16.x86_64.rpm http://kojipkgs.fedoraproject.org/packages/libvirt/0.9.3/2.fc16/x86_64/libvirt-client-0.9.3-2.fc16.x86_64.rpm http://kojipkgs.fedoraproject.org/packages/parted/3.0/2.fc16/x86_64/parted-3.0-2.fc16.x86_64.rpm http://kojipkgs.fedoraproject.org/packages/netcf/0.1.8/1.fc16/x86_64/netcf-libs-0.1.8-1.fc16.x86_64.rpm

But after tests: kvm is faster simpler and better.

воскресенье, 10 июля 2011 г.

capybara get html

For get html
find("ul.random_quotes").native.to_html
for get elements count
find("ul.random_quotes").native.search("li").size