<<<第五回 まとめ>>>
業務の中で端末構築を経験して苦労したのは、
・がちがちに端末の操作を固めてしまわないといけない
・認証に関連する動作確認
・プラットフォームを移植したアプリの検証
・動きがおかしいときの調査
・サーバに引きずられる仕様の確認
といったようなものだった。
MSに聞かないと分からないもの(広報されていないバグ的な動き)が幾つか発生した。
MSに聞ける窓口を持たないと、構築時の不具合をきれいに解消することは難しいと感じた。
Ex:starup関連など
以上。
<<<第五回 まとめ>>>
業務の中で端末構築を経験して苦労したのは、
・がちがちに端末の操作を固めてしまわないといけない
・認証に関連する動作確認
・プラットフォームを移植したアプリの検証
・動きがおかしいときの調査
・サーバに引きずられる仕様の確認
といったようなものだった。
MSに聞かないと分からないもの(広報されていないバグ的な動き)が幾つか発生した。
MSに聞ける窓口を持たないと、構築時の不具合をきれいに解消することは難しいと感じた。
Ex:starup関連など
以上。
こんばんは。ゆんぼうです。
今回は、Backbone.jsで、
静的HTMLでViewを作成します。
前回までは、htmlのbodyにBackbone.Viewのelをappendして、Webページを表示しておりましたが、
今回は、htmlのbodyに要素を直接記述して、要素のidをBackbone.Viewのelに紐付けて表示します。
今回使用する環境は下記の通りです。
Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/
JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/
index.html のbody に下記の内容を記述します。
<div id="id_buttons">
<input id="id_button_update" type="button" value="更新">
<input id="id_button_delete" type="button" value="削除">
</div>
$(function() {
var BaseView = Backbone.View.extend({
el : '#id_buttons', // (1)
events : {
'click #id_button_update' : 'onClickUpdateButton',
'click #id_button_delete' : 'onClickDeleteeButton'
},
onClickUpdateButton : function(event) {
console.log('onClickUpdateButton');
},
onClickDeleteeButton : function(event) {
console.log('onClickDeleteeButton');
}
});
var baseView = new BaseView();
});
$(function() {
var ButtonView = Backbone.View.extend({
events : {
'click' : 'onClick'
},
onClick : function(event) {
console.log(event.target.id + '#onClick');
}
});
var buttonView1 = new ButtonView({ // (1)
'el' : '#id_button_update'
});
var buttonView2 = new ButtonView({ // (2)
'el' : '#id_button_delete'
});
});
こんばんは。ゆんぼうです。
今回は、Backbone.jsのBackbone.ModelとBackbone.Collectionを使ったViewを作成します。
Modelは、データを表します。
Backbone.jsでは、データの更新や検証、Ajax通信などの機能を提供します。
Collectionは、Modelの配列を表します。
Backbone.jsでは、配列の追加、削除、並び替えなどの機能を提供します。
今回使用する環境は下記の通りです。
Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/
JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/
<script type="text/template" id="template-programing-subview">
<label for="language"><%= title %></label>
<select id="language"></select>
</script>
$(function() {
var ProgramingLanguageModel = Backbone.Model.extend({ // (1)
defaults : { // (2)
'name' : '',
'value' : 0
}
});
var ProgramingLanguageCollection = Backbone.Collection.extend({ // (3)
model : ProgramingLanguageModel // (4)
});
var SampleView = Backbone.View.extend({
events : {
'change #language' : 'onChange'
},
initialize : function() {
this.compiledTemplate = _.template($('#template-programing-subview').text());
this.collection = new ProgramingLanguageCollection([ { // (5)
'name' : 'JavaScript',
'value' : 1
}, {
'name' : 'Java',
'value' : 2
}, {
'name' : 'C言語',
'value' : 3
}, {
'name' : 'Ruby',
'value' : 4
}, {
'name' : 'Python',
'value' : 5
} ]);
},
render : function() {
var data = {
'title' : 'プログラミング言語を選択してください'
};
this.$el.append(this.compiledTemplate(data));
_.each(this.collection.models, function(model) { // (6)
this.$el.find('#language').append((new OptionView(model)).render().el);
}, this);
return this;
},
onChange : function(event) {
console.log('onChange:' + event.target.value);
}
});
var OptionView = Backbone.View.extend({
tagName : 'option',
initialize : function(model) {
this.label = model.get('name'); // (7)
this.attributes = {
'value' : model.get('value') // (8)
};
},
render : function() {
this.$el.html(this.label);
return this;
}
});
var sampleView = new SampleView();
$('body').append(sampleView.render().el);
});
こんばんは。ゆんぼうです。
今回は、Backbone.jsを使用して、リストと登録ボタンを作成します。
今回使用する環境は下記の通りです。
Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/
JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/
<script type="text/template" id="template-user-list-item">
<%= label.name %>
</script>
<script type="text/template" id="template-user-register">
<label for="id_input_name"><%= label.name %></label>
<input id="id_input_name" type="text">
<br>
<input id="id_button_regist" type="button" value="<%= label.registButton %>">
<hr>
</script>
$(function() {
var UserModel = Backbone.Model.extend({ // (1)
defaults : {
'name' : ''
}
});
var UserCollection = Backbone.Collection.extend({ // (2)
model : UserModel
});
var UserListView = Backbone.View.extend({
name : 'UserListView',
tagName : 'ul',
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
},
render : function() {
this.$el.html('');
_.each(this.collection.models, function(model) {
this.$el.append((new UserListItemView({
'model' : model
})).render().el);
}, this);
return this;
},
onAdd : function() { // (5)
console.log(this.name + '#onAdd');
this.render();
}
});
var UserListItemView = Backbone.View.extend({
name : 'UserListItemView',
tagName : 'li',
initialize : function(args) {
this.model = args.model;
this.compiledTemplate = _.template($('#template-user-list-item').text());
},
render : function() {
var templateData = {
'label' : {
'name' : this.model.get('name')
}
};
this.$el.append(this.compiledTemplate(templateData));
return this;
}
});
var UserRegisterView = Backbone.View.extend({
name : 'UserRegisterView',
events : {
'click #id_button_regist' : 'onClickRegistButton'
},
initialize : function(args) {
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-register').text());
},
render : function() {
var templateData = {
'label' : {
'name' : '名前 : ',
'registButton' : '登録'
}
};
this.$el.append(this.compiledTemplate(templateData))
return this;
},
onClickRegistButton : function() { // (4)
console.log(this.name + '#onClickRegistButton');
this.collection.add({
'name' : this.$el.find('#id_input_name').val()
});
}
});
var BaseView = Backbone.View.extend({
name : 'BaseView',
initialize : function() { // (3)
this.collection = new UserCollection();
this.userListView = new UserListView({
'collection' : this.collection
});
this.userRegisterView = new UserRegisterView({
'collection' : this.collection
});
},
render : function() {
this.$el.append(this.userRegisterView.render().el).append(this.userListView.render().el);
return this;
}
});
var baseView = new BaseView();
$('body').append(baseView.render().el);
});
var UserModel = Backbone.Model.extend({
defaults : {
'name' : ''
},
validate : function(attribuite) { // (1)
if (_.isEmpty(attribuite.name)) {
return "名前が入力されていません";
}
}
});
var UserRegisterView = Backbone.View.extend({
name : 'UserRegisterView',
events : {
'click #id_button_regist' : 'onClickRegistButton'
},
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'invalid', this.onInvalid);
this.compiledTemplate = _.template($('#template-user-register').text());
},
render : function() {
var templateData = {
'label' : {
'name' : '名前 : ',
'registButton' : '登録'
}
};
this.$el.append(this.compiledTemplate(templateData))
return this;
},
onClickRegistButton : function() {
console.log(this.name + '#onClickRegistButton');
this.collection.add({
'name' : this.$el.find('#id_input_name').val(),
}, {
validate : true // (1)
});
},
onInvalid : function(collection, message) { // (2)
console.log(this.name + '#onInvalid');
alert(message);
}
});
こんばんは。ゆんぼうです。
今回は、Backbone.jsを使用して、リストと削除ボタンを作成します。
今回使用する環境は下記の通りです。
Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/
JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/
<script type="text/template" id="template-user-list-item">
<%= label.name %>
<input type="button" class="cls_button_delete" value="<%= label.deleteButton %>">
</script>
<script type="text/template" id="template-user-register">
<label for="id_input_name"><%= label.name %></label>
<input id="id_input_name" type="text">
<br>
<input id="id_button_regist" type="button" value="<%= label.registButton %>">
<hr>
</script>
var UserListView = Backbone.View.extend({
name : 'UserListView',
tagName : 'ul',
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.listenTo(this.collection, 'remove', this.onRemove);
},
render : function() {
this.$el.html('');
_.each(this.collection.models, function(model) {
this.$el.append((new UserListItemView({
'model' : model,
'collection' : this.collection
})).render().el);
}, this);
return this;
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.render();
},
onRemove : function() { // (3)
console.log(this.name + '#onRemove');
this.render();
}
});
var UserListItemView = Backbone.View.extend({
name : 'UserListItemView',
tagName : 'li',
events : {
'click .cls_button_delete' : 'onClickDeleteButton'
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-list-item').text());
},
render : function() {
var templateData = {
'label' : {
'name' : this.model.get('name'),
'deleteButton' : '削除'
}
};
this.$el.append(this.compiledTemplate(templateData)); // (1)
return this;
},
onClickDeleteButton : function() {
console.log(this.name + '#onClickDeleteButton');
this.collection.remove(this.model); // (2)
}
});
<script type="text/template" id="template-user-list">
<input id="id_button_delete" type="button" value="<%= label.deleteButton %>">
<hr>
<ul id="id_ul_user_list"></ul>
</script>
<script type="text/template" id="template-user-list-item">
<input class="cls_checkbox" type="checkbox"> <%= label.name %>
</script>
<script type="text/template" id="template-user-register">
<label for="id_input_name"><%= label.name %></label>
<input id="id_input_name" type="text">
<br>
<input id="id_button_regist" type="button" value="<%= label.registButton %>">
<hr>
</script>
var UserModel = Backbone.Model.extend({
defaults : {
'name' : ''
},
isSelected : false, // (2)
validate : function(attribuite) {
if (_.isEmpty(attribuite.name)) {
return "名前が入力されていません";
}
}
});
var UserCollection = Backbone.Collection.extend({
model : UserModel,
removeSelectedModel : function() { // (5)
var selectedCollection = this.models.filter(function(model) {
return model.isSelected;
});
this.remove(selectedCollection);
}
});
var UserListView = Backbone.View.extend({
name : 'UserListView',
events : {
'click #id_button_delete' : 'onClickDeleteButton'
},
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.listenTo(this.collection, 'remove', this.onRemove);
this.compiledTemplate = _.template($('#template-user-list').text());
},
render : function() {
var templateData = {
'label' : {
'deleteButton' : '削除'
}
};
this.$el.append(this.compiledTemplate(templateData)) // (1)
return this;
},
renderItem : function() {
this.$el.find('#id_ul_user_list').html('');
_.each(this.collection.models, function(model) {
this.$el.find('#id_ul_user_list').append((new UserListItemView({
'model' : model,
'collection' : this.collection
})).render().el);
}, this);
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.renderItem();
},
onRemove : function() {
console.log(this.name + '#onRemove');
this.renderItem(); // (6)
},
onClickDeleteButton : function() {
console.log(this.name + '#onClickDeleteButton');
this.collection.removeSelectedModel(); // (4)
}
});
var UserListItemView = Backbone.View.extend({
name : 'UserListItemView',
tagName : 'li',
events : {
'click .cls_checkbox' : 'onClickCheckBox'
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-list-item').text());
},
render : function() {
var templateData = {
'label' : {
'name' : this.model.get('name')
}
};
this.$el.append(this.compiledTemplate(templateData));
return this;
},
onClickCheckBox : function(event) {
console.log(this.name + '#onClickCheckBox');
this.model.isSelected = event.target.checked; // (3)
}
});
こんばんは。ゆんぼうです。
今回は、Backbone.jsを使用して、リストと詳細ボタンを作成します。
今回使用する環境は下記の通りです。
Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/
JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/
<script type="text/template" id="template-user-list-item">
<%= label.name %>
<input type="button" class="cls_button_detail" value="<%= label.detailButton %>">
<input type="button" class="cls_button_delete" value="<%= label.deleteButton %>">
</script>
<script type="text/template" id="template-user-register">
<label for="id_input_name"><%= label.name %></label>
<input id="id_input_name" type="text">
<br>
<label for="id_input_age"><%= label.age %></label>
<input id="id_input_age" type="number" min="0" max="99">
<br>
<label for="id_select_gender"><%= label.gender %></label>
<select id="id_select_gender">
<option><%= label.male %></option>
<option><%= label.female %></option>
</select>
<br>
<label for="id_select_blood"><%= label.blood %></label>
<select id="id_select_blood">
<option>A</option>
<option>B</option>
<option>O</option>
<option>AB</option>
</select>
<br>
<label for="id_input_mail"><%= label.mail %></label>
<input id="id_input_mail" type="text">
<br>
<input id="id_button_regist" type="button" value="<%= label.registButton %>">
<hr>
</script>
var UserListItemView = Backbone.View.extend({
name : 'UserListItemView',
tagName : 'li',
events : {
'click .cls_button_detail' : 'onClickDetailButton',
'click .cls_button_delete' : 'onClickDeleteButton'
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection
this.compiledTemplate = _.template($('#template-user-list-item').text());
},
render : function() {
var templateData = {
'label' : {
'name' : this.model.get('name'),
'detailButton' : '詳細',
'deleteButton' : '削除'
}
};
this.$el.append(this.compiledTemplate(templateData));
return this;
},
onClickDetailButton : function() {
console.log(this.name + '#onClickDetailButton');
var message = '';
message += '\n名前 : ' + this.model.get('name'); // (1)
message += '\n年齢 : ' + this.model.get('age');
message += '\n性別 : ' + this.model.get('gender');
message += '\n血液型 : ' + this.model.get('blood');
message += '\nメールアドレス : ' + this.model.get('mailAddress');
alert(message); // (2)
},
onClickDeleteButton : function() {
console.log(this.name + '#onClickDeleteButton');
this.collection.remove(this.model);
}
});
こんばんは。ゆんぼうです。
前回までは、登録ボタンを押すと、リストが追加されていきました。
今回は、Backbone.jsでテーブルを作成します。
今回使用する環境は下記の通りです。
Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/
JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/
<script type="text/template" id="template-user-table">
<table class="cls_table">
<thead>
<tr>
<th><%= header.name %></th>
</tr>
</thead>
<tbody id="id_tbody">
</tbody>
</table>
</script>
<script type="text/template" id="template-user-table-item">
<td><%= data.name %></td>
</script>
var UserTableView = Backbone.View.extend({
name : 'UserTableView',
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.compiledTemplate = _.template($('#template-user-table').text());
},
render : function() {
var tempData = {
'header' : {
'name' : '名前'
}
};
this.$el.append(this.compiledTemplate(tempData)); // (1)
return this;
},
renderItem : function() {
this.$el.find('#id_tbody').html('');
_.each(this.collection.models, function(model) { // (3)
this.$el.find('#id_tbody').append((new UserTableItemView({
'model' : model
})).render().el);
}, this);
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.renderItem(); // (2)
}
});
<script type="text/template" id="template-user-table">
<table class="cls_table">
<thead>
<tr>
<th><%= header.name %></th>
<th><%= header.age %></th>
<th><%= header.gender %></th>
<th><%= header.blood %></th>
<th><%= header.mailAddress %></th>
</tr>
</thead>
<tbody id="id_tbody">
</tbody>
</table>
</script>
<script type="text/template" id="template-user-table-item">
<td><%= data.name %></td>
<td><%= data.age %></td>
<td><%= data.gender %></td>
<td><%= data.blood %></td>
<td><%= data.mailAddress %></td>
</script>
こんばんは。ゆんぼうです。
今回は、Backbone.jsを使用して、ヘッダーを押す度にソートを行うテーブルを作成します。
今回使用する環境は下記の通りです。
Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/
JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/
<script type="text/template" id="template-user-table">
<table class="cls_table">
<thead>
<tr>
<th><a href="javascript:void(0)" class="cls_header" name="age"><%= header.age %><span class="cls_sort_icon"></span></a></th>
</tr>
</thead>
<tbody id="id_tbody">
</tbody>
</table>
</script>
<script type="text/template" id="template-user-table-item">
<td><%= data.age %></td>
</script>
var UserCollection = Backbone.Collection.extend({
model : UserModel,
sortAttribute : '',
sortDirection : 1,
sortToggle : function(attribute) {
this.sortDirection *= -1; // (3)
this.sortAttribute = attribute;
this.sort(); // (4)
},
comparator : function(model) {
return model.get(this.sortAttribute) * this.sortDirection; // (5)
}
});
var UserTableView = Backbone.View.extend({
name : 'UserTableView',
events : {
'click .cls_header' : 'onClickHeader'
},
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.listenTo(this.collection, 'sort', this.onSort);
this.compiledTemplate = _.template($('#template-user-table').text());
this.currentHeader = null;
},
render : function() {
var tempData = {
'header' : {
'age' : '年齢'
}
};
this.$el.append(this.compiledTemplate(tempData));
return this;
},
renderItem : function() {
this.$el.find('#id_tbody').html('');
_.each(this.collection.models, function(model) {
this.$el.find('#id_tbody').append((new UserTableItemView({
'model' : model
})).render().el);
}, this);
},
renderClearSortIcon : function() {
_.each([ 'cls_sort_up', 'cls_sort_down' ], function(sortClass) {
this.$el.find("a[name='" + this.collection.sortAttribute + "'] span").removeClass(sortClass);
}, this);
},
renderSortIcon : function() {
var sortClass = null;
if (this.collection.sortDirection == 1) {
sortClass = 'cls_sort_up';
} else {
sortClass = 'cls_sort_down';
}
this.$el.find("a[name='" + this.collection.sortAttribute + "'] span").addClass(sortClass);
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.renderItem();
},
onSort : function() {
console.log(this.name + '#onSort');
this.renderItem(); // (6)
this.renderSortIcon(); // (7)
},
onClickHeader : function(event) {
console.log(this.name + '#onClickHeader');
this.renderClearSortIcon(); // (1)
this.collection.sortToggle(event.target.name); // (2)
}
});
var UserCollection = Backbone.Collection.extend({
model : UserModel,
sortAttribute : '',
sortDirection : 1,
sortToggle : function(attribute) {
this.sortDirection *= -1;
this.sortAttribute = attribute;
this.sort();
},
comparator : function(a, b) { // (1)
var a = a.get(this.sortAttribute), b = b.get(this.sortAttribute);
if (a == b) {
return 0;
}
if (this.sortDirection == 1) {
return b < a ? 1 : -1;
} else {
return a < b ? 1 : -1;
}
}
})
var UserCollection = Backbone.Collection.extend({
model : UserModel,
sortAttribute : '',
sortDirection : 1,
initialize : function() {
var that = this;
this.sortComparators = { // (3)
'name' : that.comparatorString,
'age' : that.comparatorInteger,
'gender' : that.comparatorString,
'blood' : that.comparatorString,
'mailAddress' : that.comparatorString
}
},
sortToggle : function(attribute) {
if (this.sortAttribute == attribute) {
this.sortDirection *= -1;
} else {
this.sortDirection = 1;
}
this.sortAttribute = attribute;
this.comparator = this.sortComparators[attribute];
this.sort();
},
comparatorString : function(a, b) { // (1)
var a = a.get(this.sortAttribute), b = b.get(this.sortAttribute);
if (a == b) {
return 0;
}
if (this.sortDirection == 1) {
return b < a ? 1 : -1;
} else {
return a < b ? 1 : -1;
}
},
comparatorInteger : function(model) { // (2)
return model.get(this.sortAttribute) * this.sortDirection;
}
});
こんばんは。ゆんぼうです。
今回は、Backbone.jsを使用して、ラジオボタンを表示するテーブルを作成します。
今回使用する環境は下記の通りです。
Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/
JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/
<script type="text/template" id="template-user-table-item">
<th>
<input class="cls_radio_button" type="radio" name="item">
</th>
<td><%= data.name %></td>
</script>
var UserCollection = Backbone.Collection.extend({
model : UserModel,
removeSelectedModel : function() {
this.remove(this.selectedModel); // (5)
},
selectedModel : null
});
var UserTableView = Backbone.View.extend({
name : 'UserTableView',
events : {
'click #id_button_delete' : 'onClickDeleteButton' // (3)
},
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.listenTo(this.collection, 'remove', this.onRemove);
this.compiledTemplate = _.template($('#template-user-table').text());
},
render : function() {
var tempData = {
'header' : {
'name' : '名前'
},
'label' : {
'deleteButton' : '削除'
}
};
this.$el.append(this.compiledTemplate(tempData));
return this;
},
renderItem : function() {
this.$el.find('#id_tbody').html('');
_.each(this.collection.models, function(model) {
this.$el.find('#id_tbody').append((new UserTableItemView({
'model' : model,
'collection' : this.collection
})).render().el);
}, this);
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.renderItem();
},
onRemove : function() {
console.log(this.name + '#onRemove');
this.renderItem();
},
onClickDeleteButton : function(event) {
console.log(this.name + '#onClickDeleteButton');
this.collection.removeSelectedModel(); // (4)
}
});
var UserTableItemView = Backbone.View.extend({
name : 'UserTableItemView',
tagName : 'tr',
events : {
'click .cls_radio_button' : 'onClickRadioButton' // (1)
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-table-item').text());
},
render : function() {
var templateData = {
'data' : this.model.toJSON()
};
this.$el.append(this.compiledTemplate(templateData));
return this;
},
onClickRadioButton : function(event) {
console.log(this.name + '#onClickRadioButton:' + event.target.checked);
this.collection.selectedModel = this.model; // (2)
}
});
var UserCollection = Backbone.Collection.extend({
model : UserModel,
sortAttribute : '',
sortDirection : 1,
initialize : function() {
var that = this;
this.sortComparators = {
'name' : that.comparatorString,
'age' : that.comparatorInteger,
'gender' : that.comparatorString,
'blood' : that.comparatorString,
'mailAddress' : that.comparatorString
}
},
sortToggle : function(attribute) {
if (this.sortAttribute == attribute) {
this.sortDirection *= -1;
} else {
this.sortDirection = 1;
}
this.sortAttribute = attribute;
this.comparator = this.sortComparators[attribute];
this.sort();
},
comparatorString : function(a, b) {
var a = a.get(this.sortAttribute), b = b.get(this.sortAttribute);
if (a == b) {
return 0;
}
if (this.sortDirection == 1) {
return b < a ? 1 : -1;
} else {
return a < b ? 1 : -1;
}
},
comparatorInteger : function(model) {
return model.get(this.sortAttribute) * this.sortDirection;
},
removeSelectedModel : function() {
this.remove(this.selectedModel);
},
selectModel : function(model) {
_.each(this.models, function(model) { // (2)
model.isSelected = false;
}, this);
model.isSelected = true;
this.selectedModel = model;
}
});
var UserTableItemView = Backbone.View.extend({
name : 'UserTableItemView',
tagName : 'tr',
events : {
'click .cls_radio_button' : 'onClickRadioButton'
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-table-item').text());
},
render : function() {
var templateData = {
'data' : this.model.toJSON()
};
this.$el.append(this.compiledTemplate(templateData));
if (this.model.isSelected) { // (3)
this.$el.find('.cls_checkbox').prop('checked', true)
}
return this;
},
onClickRadioButton: function(event) {
console.log(this.name + '#onClickRadioButton:' + event.target.checked);
this.collection.selectModel(this.model); // (1)
}
});
こんばんは。ゆんぼうです。
今回は、Backbone.jsを使用して、チェックボックスを表示するテーブルを作成します。
今回使用する環境は下記の通りです。
Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/
JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/
<script type="text/template" id="template-user-table">
<input id="id_button_delete" type="button" value="<%= label.deleteButton %>">
<hr>
<table class="cls_table">
<thead>
<tr>
<th><input class="cls_all_checkbox" type="checkbox"></th>
<th><%= header.name %></th>
</tr>
</thead>
<tbody id="id_tbody">
</tbody>
</table>
</script>
<script type="text/template" id="template-user-table-item">
<th>
<input class="cls_checkbox" type="checkbox">
</th>
<td><%= data.name %></td>
</script>
var UserCollection = Backbone.Collection.extend({
model : UserModel,
removeSelectedModel : function() {
var selectedCollection = this.models.filter(function(model) { // (17)
return model.isSelected;
});
this.remove(selectedCollection); // (18)
},
isSelectedAll : false,
selectModelAll : function(isSelected) {
this.isSelectedAll = isSelected;
_.each(this.models, function(model) { // (10)
model.isSelected = isSelected;
});
this.trigger('selectAll'); // (11)
},
selectModel : function() {
this.isSelectedAll = this.models.every(function(model) { // (3)
return model.isSelected
});
this.trigger('select'); // (4)
}
});
var UserTableView = Backbone.View.extend({
name : 'UserTableView',
events : {
'click .cls_all_checkbox' : 'onClickAllCheckbox', // (8)
'click #id_button_delete' : 'onClickDeleteButton' // (15)
},
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.listenTo(this.collection, 'remove', this.onRemove); // (19)
this.listenTo(this.collection, 'select', this.onSelect); // (5)
this.listenTo(this.collection, 'selectAll', this.onSelectAll); // (12)
this.compiledTemplate = _.template($('#template-user-table').text());
this.currentHeader = null;
},
render : function() {
var tempData = {
'header' : {
'name' : '名前'
},
'label' : {
'deleteButton' : '削除'
}
};
this.$el.append(this.compiledTemplate(tempData));
return this;
},
renderItem : function() {
this.$el.find('#id_tbody').html('');
_.each(this.collection.models, function(model) {
this.$el.find('#id_tbody').append((new UserTableItemView({
'model' : model,
'collection' : this.collection
})).render().el);
}, this);
},
renderSelectAll : function() {
this.$el.find('.cls_all_checkbox').prop('checked', this.collection.isSelectedAll) // (7)
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.renderItem();
},
onRemove : function() {
console.log(this.name + '#onRemove');
this.renderItem(); // (20)
},
onSelect : function() {
console.log(this.name + '#onSelect');
this.renderSelectAll(); // (6)
},
onSelectAll : function() {
console.log(this.name + '#onSelectAll');
this.renderItem(); // (13)
},
onClickAllCheckbox : function(event) {
console.log(this.name + '#onClickAllCheckbox');
this.collection.selectModelAll(event.target.checked); // (9)
},
onClickDeleteButton : function(event) {
console.log(this.name + '#onClickDeleteButton');
this.collection.removeSelectedModel(); // (16)
}
});
var UserTableItemView = Backbone.View.extend({
name : 'UserTableItemView',
tagName : 'tr',
events : {
'click .cls_checkbox' : 'onClickCheckbox' // (1)
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-table-item').text());
},
render : function() {
var templateData = {
'data' : this.model.toJSON()
};
this.$el.append(this.compiledTemplate(templateData));
if (this.model.isSelected) {
this.$el.find('.cls_checkbox').prop('checked', true) // (14)
}
return this;
},
onClickCheckbox : function(event) {
console.log(this.name + '#onClickCheckbox:' + event.target.checked);
this.model.isSelected = event.target.checked; // (2)
this.collection.selectModel();
}
});
どうも、ちかです
最近めっちゃ忙しい。。
Kibana ってこんなの
Elasticsearch のデータを可視化するツールです。
こういうブログ記事では
まず導入として
「Kibana ってこんなの」っていう話をするものですが
すでに知ってる人向けの記事ということで。
Kibana プラグイン
Kibana 4.2 からプラグインで機能を追加できるようになりました。
サーバー側は Node.js のフレームワーク hapi ベースで
クライアント側は AngularJS ベースで
とりあえずなんでもできます。
特に Elasticsearch へのアクセスは
サーバーから手軽にできるようになっています。
サーバー側でプロキシを作ればクライアントからも簡単にアクセスできます。
# Elasticsearch への GET メソッドのクチはデフォルトで開いてます。
# elasticsearch.js の API を
# サーバーでは server.plugins.elasticsearch.client として使えます。
# クライアントでは es という名前で依存性注入できます。
Kibana プラグインの作り方
Qiita に
『Kibanaプラットフォームでアプリケーションをつくってみよう』
という濃いぃ記事があるのでご参考に。
ミニマム構成では index.js と package.json があれば OK。
認証プラグイン
で
Kibana を使ってみて気になったのが
認証機能がないこと。
Shield という Elasticsearch プラグインがあるらしいのですが
有償らしいので (セキュリティ向けプラグインShieldのリリース(日本語訳))
とりあえずあきらめて
Authentication という Issue ページも立っているのですが
4.2 ⇒ 4.3 ⇒ 4.4 と先延ばし先延ばしにされているので
とりあえずあきらめて
作ってみました。
const Boom = require('boom');var users = { chica: { password: 'secret' }};module.exports = kibana => new kibana.Plugin({ init(server, options) { server.auth.scheme('basic', basic); server.auth.strategy('default', 'basic'); server.auth.default('default'); }});function basic(server, options) { return { authenticate(request, reply) { const authorization = request.raw.req.headers.authorization; if (!authorization) { return reply(Boom.unauthorized('Authentication required', 'Basic')); } const parts = authorization.split(/\s+/); if (parts.length !== 2 || parts[0].toLowerCase() !== 'basic') { return reply(Boom.unauthorized('Authentication failed.', 'Basic')); } const credentials = new Buffer(parts[1], 'base64').toString().split(':', 2); const user = users[credentials[0]]; if (!user || user.password !== credentials[1]) { return reply(Boom.unauthorized('Authentication failed.', 'Basic')); } return reply.continue({ credentials: { user: user } }); } };}
{ "name": "basic-authentication", "version": "0.0.1"}
index.js と package.json を tar.gz に固めて
kibana/bin/kibana plugin -i basic-authentication -u file://path/to/plugin.tar.gz
こんなので
一応ちゃんと認証が求められるようになります。
まぁでも
これを洗練するより
4.4 が出るのを待つかな。(本末転倒)
今回はこんなところで。
はじめに
ちかです
毎度のことですが個人の見解です。
会社の見解ではありません。
むしろ他の社員からは反対意見が出そうな話です。
Web ページの構成について「控えめな JavaScript (Unobtrusive JavaScript)」という慣行があります。
太古の昔、 JavaScript は馬に乗って行列を作って HTML 上を走り回っていたそうです。
「控えめな JavaScript」は革命を起こし、横柄な JavaScript とは対極の立場を取りました。
『インラインスクリプトは絶対に書きません!』
でもこれはあまりに極端では?? というのが今回の記事です。
「控えめな JavaScript」信奉者の主張
『Rails で JavaScript を使用する』の例を見てみます。
最もシンプルなJavaScriptを例にとって考えてみましょう。以下のような書き方は'インラインJavaScript'と呼ばれています。
<a href="#" onclick="this.style.backgroundColor='#990000'">Paint it red</a>
このリンクをクリックすると、背景が赤くなります。しかし早くもここで問題が生じ始めます。クリックした時にJavaScriptでもっといろんなことをさせるとどうなるでしょうか。
<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';">Paint it green</a>
だいぶ乱雑になってきました。ではここで関数定義をclickハンドラの外に追い出し、CoffeeScriptで書き換えてみましょう。
paintIt = (element, backgroundColor, textColor) -> element.style.backgroundColor = backgroundColor if textColor? element.style.color = textColor
ページの内容は以下のとおりです。
<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>
これでコードがだいぶ良くなりました。しかし、同じ効果を複数のリンクに与えるとどうなるでしょうか。
<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a><a href="#" onclick="paintIt(this, '#009900', '#FFFFFF')">Paint it green</a><a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a>
これではDRYとは言えません。今度はイベントを活用して改良してみましょう。最初にdata-*属性をリンクに追加しておきます。続いて、この属性を持つすべてのリンクで発生するクリックイベントにハンドラをバインドします。
paintIt = (element, backgroundColor, textColor) -> element.style.backgroundColor = backgroundColor if textColor? element.style.color = textColor $ -> $("a[data-background-color]").click (e) -> e.preventDefault() backgroundColor = $(this).data("background-color") textColor = $(this).data("text-color") paintIt(this, backgroundColor, textColor)
<a href="#" data-background-color="#990000">Paint it red</a><a href="#" data-background-color="#009900" data-text-color="#FFFFFF">Paint it green</a><a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>
私たちはこの手法を「控えめなJavaScript」と呼んでいます。この名称は、HTMLの中にJavaScriptを混入させないという意図に由来しています。JavaScriptを正しく分離することができたので、今後の変更が容易になりました。今後は、このdata-*属性をリンクタグに追加するだけでこの動作を簡単に追加できます。
直接 onclick とは書いていないだけ
> JavaScriptを正しく分離することができたので、今後の変更が容易になりました。今後は、このdata-*属性をリンクタグに追加するだけでこの動作を簡単に追加できます。
とおっしゃっておられますが
分離前でも onclick="paintIt(this, '#990000')" のような記述を追加するだけでこの動作を簡単に追加できていました。
なにをもって分離後の方が「変更が容易」と言えるのでしょう?
「JavaScriptを正しく分離することができたので」では全く理由になっていません。
(「これではDRYとは言えません。」という部分も何が DRY でないのか自明でなく説明もありません。。)
コンテンツ (HTML) と振る舞い (JavaScript) と見た目 (CSS) とを別ファイルにする目的は
関心を分離して互いを抽象化することにあると思うのですが
マークアップには振る舞いを表現するためのクラスや data-* 属性が必要で
抽象化は実現できておらず
何が変わったかと言えば単に「直接 onclick とは書いていない」だけです。
実際には関心は分離できていないのです。
grep しないと理解できないコード
それどころか
無視できない大きなデメリットがあります。
関係のある記述が散らばることです。
端的にビフォーアフターで比較しましょう。
レンダリング結果が予測しやすいのはどちらでしょうか?
<a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a>
<a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>
コードを書いているときはまだいいのですが
後になって HTML を読んでみると
data-background-color 属性が何を表しているのか
見た目にどう影響するのか
振る舞いにどう影響するのか
grep しなければ分かりません。
やがて HTML は得体の知れないクラス名や属性で埋め尽くされます。
そして JavaScript はイベントリスナーの登録処理で埋め尽くされます。
ビジネスロジックに集中できない!
『間違ったコードは間違って見えるようにする』 の言葉を少し借りると、
関係するものを隣り合わせにしておくことでコードを改善できる例はたくさんある。コーディング規則の多くは以下のような規則を含んでいる:
これらのルールに共通しているのは、1行のコードが実際にすることに関連した情報を可能な限り物理的に近づけるということだ。そうすることによって、あなたの目玉が何が起きているのか理解できる可能性が高くなる。
(だからといってハンガリアン記法はオススメしませんが。。)
もっと地味な問題: コードの恣意性
もっと地味ぃ~な問題もあります。
コードの恣意性が強くなることです。
例えば:
div 要素 A ではクリック時に処理 1 の結果をもとに処理 2 を行いたいとします。
div 要素 B ではクリック時に処理 1 の結果をもとに処理 3 を行いたいとします。
インラインスクリプトで実現するならこうです。
<div onclick="procedure2(procedure1('arg1'));"> ... </div><div onclick="procedure3(procedure1('arg2'));"> ... </div>
インラインスクリプトを使わないコードを想像してみてください。
しっくり来るでしょうか?
その「しっくり来たコード」は誰が考えても似たようになるでしょうか?
しっくり来るまでにかかる時間はインラインスクリプトで実現するのに比べて長くならないでしょうか?
(この例ではインラインスクリプトが見えているのでアレですが)
これらは「やり方がいろいろある」ことの弊害です。
スクリプトを分離しようと考えると、コードの恣意性が高まります。
(恣意性が高くなる原因についてはパッと説明できませんが。。)
やり方がいろいろあると
思いつく限りの方法の良し悪しを比較する必要があり
「しっくり来る」のに時間がかかるのです。
間違えようのないやり方がひとつだけあるのがいいと考えるのは私だけではないはずです。
React
コンテンツとその振る舞いは切っても切れません。
コンテンツとその振る舞いは同じ場所に記述した方がメンテナンスしやすくなります。
などと私ごときが言っても伝わらないかもしれませんが...
React の JSX コードがどうして次のようになるのか (次のように書けると何が嬉しいのか)
考えてみてはいかがでしょうか。
var Box = React.createClass({ getInitialState: function() { return {windowWidth: window.innerWidth}; }, handleResize: function(e) { this.setState({windowWidth: window.innerWidth}); }, componentDidMount: function() { window.addEventListener('resize', this.handleResize); }, componentWillUnmount: function() { window.removeEventListener('resize', this.handleResize); }, render: function() { return <div>Current window width: {this.state.windowWidth}</div>; }});ReactDOM.render(<Box />, mountNode);
だいたい以上です
インラインスクリプト禁止の弊害について書いてきました。
やたらめったらインラインスクリプトを使えと言うつもりはありませんし
プロジェクトのポリシーとしてインラインスクリプト禁止と言われれば私もインラインスクリプトは使いません。
ただ
思考を停止してインラインスクリプトを悪とする風潮に対しては冷静になりたいと思った次第です。
p.s. インラインスタイルについて
インラインスタイルが悪かどうかについては検討できていません。
いろいろ思うところはありますが
ノーコメントにしておきます。
ちかです
最近は TypeScript に浸かっています。
もう何年も Python に触れていなかったのですが
Python 3.5 には型ヒントやらコルーチンのための構文 (async/await) やら追加されて
少し気になっていました。
型ヒントについては Python と型ヒント (Type Hints) を読んで
構造的部分型の型チェック (TypeScript みたいな) ができるようになることに期待しつつ放置。
async/await は (少なくとも他の言語では神機能だし) 使ってみようかと。
特に非同期イテレーション専用の構文 async for がおもしろそう!
ということで
以前 Go 言語と D 言語でスリープソートを実装してみたのとの比較も込めて
スリープソートを実装してみました。
import asynciofrom contextlib import closingclass ArrivalOrderedIterator: ''' コルーチン (または Future) のリストを受け取り、結果の到着順に結果値を返す非同期イテレーターを表します。 ''' def __init__(self, coroutines): self.completed = set() self.incomplete = [asyncio.ensure_future(co) for co in coroutines] async def __aiter__(self): return self async def __anext__(self): if self.completed: return self.completed.pop().result() if not self.incomplete: raise StopAsyncIteration() self.completed, self.incomplete = await asyncio.wait(self.incomplete, return_when=asyncio.FIRST_COMPLETED) return self.completed.pop().result()class sleepsort: ''' 数値のリストをスリープソートする非同期シーケンスを表します。 ''' def __init__(self, values): self.values = values async def __aiter__(self): return ArrivalOrderedIterator(asyncio.sleep(x, x) for x in self.values)if __name__ == '__main__': async def main(): async for value in sleepsort([5, 2, 19, 11, 7, 13, 3, 17]): print(value) with closing(asyncio.get_event_loop()) as loop: loop.run_until_complete(main())
故意に async for 文を使っています (31 行目)。
そのせいで必要以上に長いコードになっているかもしれません。
async for 文を使うことで非同期のイテレーションを読みやすく記述できます。
...利用側では。
async for value in async_iterable: do_something(value)
非同期イテレーターの実装側では
__anext__() メソッドで「次の値」を返すか StopAsyncIteration を発生させてループを終了させます。
基本的にはクラスを作成して現在の状態をフィールドに持たせることになると思います。
ジェネレーター関数みたいに記述できたらラクなのですが。。
また
Reactive Extensions (Rx) を彷彿とさせますが (私だけ?)
内包表記や map/filter などは使えないようで
必要なら自前で用意する必要がありそうです。。
((i * i async for value in sleepsort(values) if i < 10) みたいな構文はないみたいです。)
参考: python - how can I asynchronously map/filter an asynchronous iterable? - Stack Overflow
ということで Python 3.5 の非同期イテレーション構文の紹介でした。
追記: ちなみに async for を使わず素直にコールバック形式で書くなら:
import asynciofrom contextlib import closingasync def sleepsort(values, callback): ''' 数値のリストをスリープソートします。 ''' incomplete = [asyncio.ensure_future(asyncio.sleep(x, x)) for x in values] while incomplete: completed, incomplete = await asyncio.wait(incomplete, return_when=asyncio.FIRST_COMPLETED) while completed: callback(completed.pop().result())if __name__ == '__main__': with closing(asyncio.get_event_loop()) as loop: loop.run_until_complete(sleepsort([5, 2, 19, 11, 7, 13, 3, 17], print))
追々記: map/filter を実装してみました。
import asynciofrom functools import partialfrom contextlib import closingclass AsyncIterator: ''' 「次の値を取得する関数」から非同期イテレーターを構築します。 ''' def __init__(self, anext): self.anext = anext async def __aiter__(self): return self async def __anext__(self): return await self.anext()def async_iterable(next_getter_creator): ''' 「「次の値を取得する関数」を取得する関数」から非同期シーケンスを構築します。 ''' class AsyncIterable: def __init__(self, *args, **kwds): self.args = args self.kwds = kwds async def __aiter__(self): return AsyncIterator(await next_getter_creator(*self.args, **self.kwds)) return AsyncIterable@async_iterableasync def async_map(selector, source): ''' 非同期シーケンスを射影します。 ''' iterator = await source.__aiter__() async def next(): return selector(await iterator.__anext__()) return next@async_iterableasync def async_filter(predicate, source): ''' 非同期シーケンスを絞り込みます。 ''' iterator = await source.__aiter__() async def next(): while True: value = await iterator.__anext__() if predicate(value): return value return next@async_iterableasync def sleepsort(values): ''' 数値のリストをスリープソートする非同期シーケンスを表します。 ''' # tasks = (completed, incomplete) tasks = [set(), [asyncio.ensure_future(asyncio.sleep(x, x)) for x in values]] async def next(): if tasks[0]: return tasks[0].pop().result() if not tasks[1]: raise StopAsyncIteration() tasks[0], tasks[1] = await asyncio.wait(tasks[1], return_when=asyncio.FIRST_COMPLETED) return tasks[0].pop().result() return nextdef chain(source, *selectors): ''' f(g(h(x))) を chain(x, h, g, f) と記述するメソッドチェイン機構を提供します。 ''' for selector in selectors: source = selector(source) return sourceif __name__ == '__main__': async def main(): async for value in chain( sleepsort([5, 2, 19, 11, 7, 13, 3, 17]), partial(async_map, lambda x: x * x), partial(async_filter, lambda x: 10 < x < 200) ): print(value) with closing(asyncio.get_event_loop()) as loop: loop.run_until_complete(main())
出力結果
2549121169
(開始 5 秒後に 25, 7 秒後に 49, ... が出力されます)
サービスマネージャで任意のサービスを開始した時、
開始したサービスの状態欄がOSによって下記のように
異なりますが、同じ状態を表しています。
・Windows2012→「実行中」
・Windows2008→「開始」
サービスの状態は、コマンドプロンプトで所定の場所で
以下のコマンドを打つことでも確認できます。
C:\Windows\System32 > sc query <任意のサービス名>
※sc queryについては下記参照
https://technet.microsoft.com/ja-jp/en-vs/library/dd228922.aspx
こんにちは、中村です。
Railsの5.0がリリースされた(http://weblog.rubyonrails.org/2016/6/30/Rails-5-0-final/)ので、作成中のRailsアプリを早速5.0にアップデートしてみました。
↓のコマンドを叩くと設定ファイル諸々のファイルを更新してくれました。
(書き換えているファイルはその都度マージが必要です)$ rails app:update
その後、RubyGemsに上がっているdraperがRails
-- before5.0
に未対応ということがわかり、Gemfileを↓のように修正して bundle 実行。gem 'draper'
-- after
gem 'draper', github: 'audionerd/draper', branch: 'rails5'
gem 'activemodel-serializers-xml'
とりあえずはこれで動くようになりました。
けど、個人的にはdraperのようなメジャーなライブラリが5.0に対応するまではアップデートを少し見送って、しばらくは4.2を使おうかなと思います。
こんにちは、中村です。
前回からかなり時間が空きましたが、最近のRails案件ではRails5.0を積極的に採用するようになりました。
弊社のホームページもRails4.2から5.0へアップデートし、この機会にフロントエンドのフレームワークもFoundationからBootstrapに乗り換えてみました。
(あまり見た目は変えていませんが)
また、案件によってはWebSocketが便利に使えるActionCableの機能も使っています。
ActionCableを使ってみての感触ですが、やはりPub/Subの機能を簡単に実現したい時には便利だと思います。
RSpecの最新版(3.5)がActionCableには対応していないようなので、テストをどうしようか迷っているところではありますが。
それに加えて、調べていく中でパフォーマンスを求められるところでは(Go言語やElixirなどの)別言語のフレームワークを考える必要があるのかなというのが今のところの感触でした。
Sikuliとは
画像認識を利用した、UI操作を自動化するソフトウェアです。
インストール前提条件
SikuliXの動作には、Java7 or Java8が必要となります。インストールが必要となります。
SikuliX(Python) インストール
ダウンロード
1.http://www.sikuli.org/ へアクセス
「SikuliX Package – Learn More and Get It」を選択
2.「Get Version 1.1.0」を選択
3.「sikulixsetup-1.1.0.jar」をダウンロード
インストール
1.ダウンロードした、「sikulixsetup-1.1.0.jar」を任意のフォルダに配置
(「C:\sikuliX」を作成し実施しています。)
2.「sikulixsetup-1.1.0.jar」を実行
「1-Pack1:~」を選択 → 「Setup Now」をクリック
3.ダウンロード確認
はい」を選択(※「jython-standalone-2.7.0.jar」も同様に「はい」選択)
4.インストール中 ⇒しばらく待つと完了です
SikuliX起動方法
1.インストールフォルダに作成された「runsikulix.cmd」を実行