Androidケータイで、radikoを聴く
パソコンやスマートフォンで聴けるラジオ「radiko」のアプリをインストールしてみました。
radikoとは
radiko.jpは、パソコンがそのままラジオ受信機となる「IP(Internet Protocol)サイマルラジオ」の配信サービスです。
独自コンテンツ、エリア制限なしという通常のインターネットラジオサービスとは異なり、地上波ラジオ放送をCMも含め、そのまま同時に放送エリアに準じた地域に 配信するサイマルサービスです。
radiko.jpについて
公式アプリ
radiko.jp for Android/株式会社radiko
説明
パソコンがそのままラジオ受信機となる「radiko.jp」のラジオ音声を再生できる公式アプリです。
radiko.jpは、CMも含め、そのまま同時に放送エリアに準じた地域に配信するサイマルサービスです。
エリア
radikoは電波のラジオと同じエリアでしか受信できないらしく、起動時に基地局やGPS,Wi-Fiを使って地域を判定します。
沖縄はエリア外の様です*1。
しかし、あきらめるのはまだ早かった!
raziko
Raziko/Nagamatsu Technology Center JAPAN
説明
radiko.jpを聞くなら Raziko
RazikoはIPサイマルラジオ radiko.jpおよび NHK らじる★らじるを受信する為の専用アプリケーションです。
再生
局を選びます。今回はFM OSAKAを選びました。放送受信準備中。
しばらくすると再生されました。
上のスクリーンショットではWi-Fi環境下でしたが、
3Gでもラジオを聴けました。
FM OSAKAを10分以上聴いてましたが、問題なく聴けました。
iPhoneの場合
iPhoneを持ってないのでアレですが、
http://app.iwire.jp/apps/364918717/%E3%83%A9%E3%82%B8%E6%9C%97%20-%20radiko%20client%20for%20iPhone/
というアプリがあるそうです。
パソコンやMacなど
ブラウザからradiko(ラジコ) | ラジオがインターネット(アプリやパソコン)で無料で聴けるにアクセスしますが、IPアドレスから(?)地域を判定しているらしく、エリア外の場合、やはり聴けません。
*1:2012年3月21日現在
(2012年の公休日とか)JTableのフィルタリング
これはJava Advent Calendar 2011の7番目のエントリーです。
≪前のエントリー#6:JUnit のセカイ:shuji_w6eさん*1
≫次のエントリー#8:AnnotationProcessorを利用して楽してintrefaceを徹底活用したプログラミングをしようぜ:t_yanoさん*2
facebookでJava Advent Calendar 2011の告知に「いいね!」を押したら、ブログ書いてねというありがたいお言葉をいただき、よった勢いでJava Advent Calendar 2011 : ATNDに参加表明しちゃったはいいけど、ネタをどうしようとか思ってますw。
酔った勢いではありましたが、狙ったとおりに12/7*3をゲットできました。
2012年の日本の公休日
いきなりJavaぢゃないですけど、そろそろ2012年の準備もしなきゃいけないので。
名称 | 年月日 | 曜日 |
---|---|---|
元日 | 2012/1/1 | 日 |
振替 | 2012/1/2 | 月 |
成人の日 | 2012/1/9 | 月 |
建国記念の日 | 2012/2/11 | 土 |
春分の日 | 2012/3/20 | 火 |
昭和の日 | 2012/4/29 | 日 |
振替 | 2012/4/30 | 月 |
憲法記念日 | 2012/5/3 | 木 |
みどりの日 | 2012/5/4 | 金 |
こどもの日 | 2012/5/5 | 土 |
海の日 | 2012/7/16 | 月 |
敬老の日 | 2012/9/17 | 月 |
秋分の日 | 2012/9/22 | 土 |
体育の日 | 2012/10/8 | 月 |
文化の日 | 2012/11/3 | 土 |
勤労感謝の日 | 2012/11/23 | 金 |
天皇誕生日 | 2012/12/23 | 日 |
振替 | 2012/12/24 | 月 |
国民の祝日について - 内閣府
これに某社の年末年始を加えて、日付のみを正規表現にすると、
'2012(0101|0102|0103|0104|0109|0211|0320|0429|0430|0503|0504|0505|0716|0917|0922|1008|1103|1123|1223|1224|1231)'
でしょうか。
redmine*4で日本の公休日などを表示
またまたJavaぢゃn(ry
徒然さめざめ Redmine hack! -カレンダーに休日色を-を参考にしました。
以下/usr/share/redmine/がインストール先として。
作成/修正ファイル一覧。
公休日の判定をRubyとJavaScriptでやっているんですね。>Redmine
lib/date2.rb
public/javascripts/calendar/holiday.js
app/views/common/_calendar.rhtml
public/stylesheets/application.css
public/stylesheets/calendar.css
public/javascripts/calendar/calendar.js
public/themes/farend_basic/stylesheets/application.css
Rubyの方はカレンダーで、JavaScriptの方はDatePicker的なやつ。
公休日の正規表現をハードコーディングしています。ymlなファイルで外に出せたらよかったんですが…
スクリーンショットはないです。ごめんなさい。
あと、ガントチャートには公休日を表示しないポリシーということで。(^^;
lib/date2.rbを新規。
# require "date" class Date2 < Date COMPANY_HOLIDAY = [ [ "%a", 'Sun' ], #日曜はデフォルトで [ "%a", 'Sat' ], [ "%Y%m%d", '2011(0101|0103|0104|0110|0211|0321|0429|0503|0504|0505|0718|0919|0923|1010|1103|1123|1223|1230)'], [ "%Y%m%d", '2012(0101|0102|0103|0104|0109|0211|0320|0429|0430|0503|0504|0505|0716|0917|0922|1008|1103|1123|1223|1224|1231)'], ] def holiday? COMPANY_HOLIDAY.each do |fil| return true if self.strftime(fil.first) =~ Regexp.new(fil.last) end return false end end
public/javascripts/calendar/holiday.jsを新規
var holidays = [ '2011(0101|0103|0104|0110|0211|0321|0429|0503|0504|0505|0718|0919|0923|1010|1103|1123|1223|1230)', '2012(0101|0102|0103|0104|0109|0211|0320|0429|0430|0503|0504|0505|0716|0917|0922|1008|1103|1123|1223|1224|1231)', ]; var ymd = function(date) { return date.getFullYear() + ('00' + (date.getMonth()+1)).slice(-2) + ('00' + date.getDate() ).slice(-2) }; var isholiday = function(date) { var s = ymd(date); for (i=0; i<holidays.length; i++) { if (holidays[i] === undefined) { continue; } if (s.match(new RegExp(holidays[i]))) { return true; } } return false; };
app/views/common/_calendar.rhtml を編集
*** app/views/common/_calendar.rhtml.old 2011-10-12 15:33:52.663388381 +0900 --- app/views/common/_calendar.rhtml 2011-10-12 16:25:00.933356562 +0900 *************** *** 5,13 **** <tbody> <tr> <% day = calendar.startdt ! while day <= calendar.enddt %> <%= "<td class='week-number' title='#{ l(:label_week) }'>#{(day+(11-day.cwday)%7).cweek}</td>" if day.cwday == calendar.first_wday %> ! <td class="<%= day.month==calendar.month ? 'even' : 'odd' %><%= ' today' if Date.today == day %>"> <p class="day-num"><%= day.day %></p> <% calendar.events_on(day).each do |i| %> <% if i.is_a? Issue %> --- 5,15 ---- <tbody> <tr> <% day = calendar.startdt ! while day <= calendar.enddt ! ddd = Date2.new(day.year, day.month, day.day) ! %> <%= "<td class='week-number' title='#{ l(:label_week) }'>#{(day+(11-day.cwday)%7).cweek}</td>" if day.cwday == calendar.first_wday %> ! <td class="<%= day.month==calendar.month ? 'even' : 'odd' %><%= ' today' if Date.today == day %><%= ' holiday' if ddd.holiday? %>"> <p class="day-num"><%= day.day %></p> <% calendar.events_on(day).each do |i| %> <% if i.is_a? Issue %>
public/stylesheets/application.cssを修正
*** public/stylesheets/application.css.old 2011-10-12 15:32:23.453586046 +0900 --- public/stylesheets/application.css 2011-10-12 16:55:49.854312806 +0900 *************** *** 550,555 **** --- 550,556 ---- table.cal td p.day-num {font-size: 1.1em; text-align:right;} table.cal td.odd p.day-num {color: #bbb;} table.cal td.today {background:#ffffdd;} + .holiday {background-color: #ffe4e1; color: red; } table.cal td.today p.day-num {font-weight: bold;} table.cal .starting a, p.cal.legend .starting {background: url(../images/bullet_go.png) no-repeat -1px -2px; padding-left:16px;} table.cal .ending a, p.cal.legend .ending {background: url(../images/bullet_end.png) no-repeat -1px -2px; padding-left:16px;}
public/stylesheets/calendar.cssを編集
*** public/stylesheets/calendar.css.old 2011-10-12 21:36:14.148784485 +0900 --- public/stylesheets/calendar.css 2011-10-12 21:37:57.427756286 +0900 *************** *** 125,130 **** --- 125,134 ---- color: #f00; } + div.calendar tbody td.holiday { /* Cell showing selected date */ + background-color: #FFE4E1; + } + div.calendar tbody .disabled { color: #999; } div.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */
public/javascripts/calendar/calendar.jsを編集
*** public/javascripts/calendar/calendar.js.old 2011-07-11 20:47:24.000000000 +0900 --- public/javascripts/calendar/calendar.js 2011-10-12 21:34:11.937288664 +0900 *************** *** 1177,1182 **** --- 1177,1185 ---- cell.className += " today"; cell.ttip += Calendar._TT["PART_TODAY"]; } + if (isholiday(date)) { + cell.className += " holiday"; + } if (weekend.indexOf(wday.toString()) != -1) cell.className += cell.otherMonth ? " oweekend" : " weekend"; }
テーマを使っている場合、
public/themes/farend_basic/stylesheets/application.cssなども編集。
*** public/themes/farend_basic/stylesheets/application.css.old 2011-10-12 16:57:55.483273982 +0900 --- public/themes/farend_basic/stylesheets/application.css 2011-10-12 16:58:25.624123111 +0900 *************** *** 582,587 **** --- 582,588 ---- table.cal td p.day-num {font-size: 1.1em; text-align:right;} table.cal td.odd p.day-num {color: #bbb;} table.cal td.today {background:#ffffdd;} + table.cal td.holiday {background:#FFE4E1;} table.cal td.today p.day-num {font-weight: bold;} table.cal .starting a, p.cal.legend .starting {background: url(../../../images/bullet_go.png) no-repeat -1px -2px; padding-left:16px;} table.cal .ending a, p.cal.legend .ending {background: url(../../../images/bullet_end.png) no-repeat -1px -2px; padding-left:16px;}
JTableのソートやフィルタリング
上の公休日やredmineと無関係ですが。
(やっとJavaの話題です。)
JavaSE6から、JTableのソートやフィルタリングができる様になったので、業務で使ってます。
さすがにそっちは公開できないので、できたてほやほやのソースを晒します。
テキストフィールドの
java.awt.event.KeyListener#keyReleased(KeyEvent)で、フィルターを設定しているので、インクリメンタルサーチぽい動きをします。
JavaAdventCalendar2011.zip
「Java SE 6完全攻略」第24回 JTableクラスでのフィルタリング | 日経 xTECH(クロステック)を参考にしました。
以下は実行中のスクリーンショット。
起動直後。
テキストフィールドに「^\d\d\d$」*5を入力したところ。
テキストフィールドに「ほし」と入力し、インプットメソッドで変換候補「☆」を選んだところ。ただしまだ確定はしていない。
あと、見出しをクリックすると並べ替えもしてくれますね。
いちど並べ替えると、並べ替えないにできないのでリセットボタンをでっちあげました。
動作環境は
$ java -version java version "1.7.0" Java(TM) SE Runtime Environment (build 1.7.0-b147) $ uname -a Linux *** 2.6.38-13-generic #52-Ubuntu SMP Tue Nov 8 16:53:51 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux $ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=11.04 DISTRIB_CODENAME=natty DISTRIB_DESCRIPTION="Ubuntu 11.04"
さいごにソースを晒しますね。
package jp.ne.hatena.d.ttmmrr.javaadventcalendar2011; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.regex.PatternSyntaxException; import java.awt.BorderLayout; import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.RowFilter; import javax.swing.SwingUtilities; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import javax.swing.table.TableRowSorter; /** * @author ttmmrr */ @SuppressWarnings("serial") public class WishList extends JFrame { public WishList() { super(); final ItemTable table = new ItemTable(); final KeywordPanel pnlKeyword = new KeywordPanel(table); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final Container pane = getContentPane(); pane.setLayout(new BorderLayout()); pane.add(pnlKeyword, BorderLayout.NORTH); pane.add(new JScrollPane(table), BorderLayout.CENTER); pack(); setVisible(true); } public static void main(final String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new WishList(); } }); } static class KeywordPanel extends JPanel { final ItemTable _table; final JTextField _txtKeyWord = new JTextField(30/*columns*/); final JButton _btnReset = new JButton("リセット"); public KeywordPanel(final ItemTable table) { super(); _table = table; setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); add(_txtKeyWord); add(_btnReset); _txtKeyWord.addKeyListener(new KeyAdapter() { @Override public void keyReleased(final KeyEvent e) { final String keyword = _txtKeyWord.getText(); _table.search(keyword); } }); _btnReset.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { _txtKeyWord.setText(""); _table.reset(); } }); } } // KeyWordPanel static class ItemTable extends JTable { final ItemModel _model = new ItemModel(); final TableRowSorter<ItemModel> _rowSorter; public ItemTable() { super(); setModel(_model); _rowSorter = new TableRowSorter<>(_model); setRowHeight(150); setCellSelectionEnabled(false); setColumnSelectionAllowed(false); setRowSelectionAllowed(true); reset(); } public void search(final String text) { if (null == text || "".equals(text)) { _rowSorter.setRowFilter(null); } else { RowFilter<Object, Object> regexFilter; try { regexFilter = RowFilter.regexFilter(text); // int...indicesを省略 } catch (final PatternSyntaxException pse) { regexFilter = null; } _rowSorter.setRowFilter(regexFilter); } } void reset() { resetTable(); resetWidths(); } void resetTable() { _rowSorter.setSortKeys(null); _rowSorter.setRowFilter(null); setRowSorter(_rowSorter); getTableHeader().repaint(); } void resetWidths() { final TableColumnModel cm = getColumnModel(); for (int i = 0; i < WIDTHS.length; i++) { final int[] widths = WIDTHS[i]; final TableColumn c = cm.getColumn(i); if (0 <= widths[0]) { c.setMinWidth(widths[0]); } if (0 <= widths[1]) { c.setPreferredWidth(widths[1]); } if (0 <= widths[2]) { c.setMaxWidth(widths[2]); } } } } // ItemTable static class ItemModel extends AbstractTableModel { final Map<Item, ImageIcon> _images = new HashMap<>(); public ItemModel() { super(); for (final Item item : ITEMS) { URL url = item.getImageURL(); ImageIcon image = null; if (null == url) { } else { image = new ImageIcon(url); } _images.put(item, image); } } /** {@inheritDoc} */ @Override public int getRowCount() { return ITEMS.length; } /** {@inheritDoc} */ @Override public int getColumnCount() { return 3; } /** {@inheritDoc} */ @Override public String getColumnName(final int column) { if (0 == column) { return "価格"; } else if (1 == column) { return "イメージ"; } else if (2 == column) { return "品名"; } return super.getColumnName(column); } /** {@inheritDoc} */ @Override public Object getValueAt(final int rowIndex, final int columnIndex) { if (rowIndex < 0 || ITEMS.length <= rowIndex) { return ""; } if (columnIndex < 0 || 3 <= columnIndex) { return ""; } if (0 == columnIndex) { return Integer.valueOf(ITEMS[rowIndex].getPrice()); } if (1 == columnIndex) { return _images.get(ITEMS[rowIndex]); } if (2 == columnIndex) { return ITEMS[rowIndex].getName(); } return ""; } /** {@inheritDoc} */ @Override public Class<?> getColumnClass(final int columnIndex) { if (0 == columnIndex) { return Integer.class; } else if (1 == columnIndex) { return ImageIcon.class; } else if (2 == columnIndex) { return String.class; } return Object.class; } } // ItemModel static final int[][] WIDTHS = { // min,pref,max {40, 120, 120}, {150, 150, 150}, {150, 600, Integer.MAX_VALUE}, }; static final Item[] ITEMS = new Item[] { new Item(34966, "住友スリーエム 3M ポケットプロジェクター ブラック MP180", "http://ecx.images-amazon.com/images/I/41gF9aI6o7L._SL500_SL135_.jpg"), new Item(46980, "aigo SiLK シリーズ 小型プロジェクター nano PT6216", "http://ecx.images-amazon.com/images/I/41UZhMjShzL._SL500_SL135_.jpg"), new Item(299, "【メール便 送料無料】 Hanwha ハイスピード HDMIケーブル 1m [3D/イーサネット対応] [HDMI Ver1.4] [1メートル] [PS3/Xbox360対応]​ UMA-HDMI10", "http://ecx.images-amazon.com/images/I/51zrMzW5n3L._SL500_SL135_.jpg"), new Item(5145, "魔法少女まどか☆マギカ 6 【完全生産限定版】 [Blu-ray]", "http://ecx.images-amazon.com/images/I/515W7cHVttL._SL500_SL135_.jpg"), new Item(5774, "魔法少女まどか☆マギカ 4 【完全生産限定版】 [Blu-ray]", "http://ecx.images-amazon.com/images/I/51Vk2uZjtTL._SL500_SL135_.jpg"), new Item(2300, "figma 魔法少女まどか☆マギカ 鹿目まどか", "http://ecx.images-amazon.com/images/I/41c3PopV8bL._SL500_SL135_.jpg"), new Item(2336, "figma 魔法少女まどか☆マギカ 巴マミ", "http://ecx.images-amazon.com/images/I/412351Rb2sL._SL500_SL135_.jpg"), new Item(5341, "魔法少女まどか☆マギカ 5 【完全生産限定版】 [Blu-ray]", "http://ecx.images-amazon.com/images/I/515jYWFP0YL._SL500_SL135_.jpg"), new Item(5900, "魔法少女まどか☆マギカ 3 【完全生産限定版】 [Blu-ray]", "http://ecx.images-amazon.com/images/I/51dFkKTKYiL._SL500_SL135_.jpg"), new Item(3630, "魔法少女まどか☆マギカ 2 【完全生産限定版】 [Blu-ray]", "http://ecx.images-amazon.com/images/I/51CS8vsUzZL._SL500_SL135_.jpg"), new Item(5900, "魔法少女まどか☆マギカ 1 【完全生産限定版】 [Blu-ray]", "http://ecx.images-amazon.com/images/I/51kDXEQIAnL._SL500_SL135_.jpg"), new Item(4980, "20世紀少年<第2章> 最後の希望 [Blu-ray]", "http://ecx.images-amazon.com/images/I/51-RUew9nvL._SL500_SL135_.jpg"), new Item(3444, "20世紀少年 第1章 終わりの始まり [Blu-ray]", "http://ecx.images-amazon.com/images/I/51Go1f2a9HL._SL500_SL135_.jpg"), new Item(620, "数学ガール 下 (MFコミックス フラッパーシリーズ)", "http://ecx.images-amazon.com/images/I/518-%2BGvqJ1L._SL500_SL135_.jpg"), new Item(1890, "数学ガール/フェルマーの最終定​理", "http://ecx.images-amazon.com/images/I/51CkaKAKglL._SL500_PIsitb-sticker-arrow-big,TopRight,35,-73_OU09_SL135_.jpg"), new Item(1890, "数学ガール/ゲーデルの不完全性​定理", "http://ecx.images-amazon.com/images/I/41wtOpDHFSL._SL500_PIsitb-sticker-arrow-big,TopRight,35,-73_OU09_SL135_.jpg"), new Item(620, "数学ガール 上 (MFコミックス フラッパーシリーズ)", "http://ecx.images-amazon.com/images/I/51AlFOZhUVL._SL500_SL135_.jpg"), }; static class Item { final int _price; final String _name; final URL _imageUrl; Item(final int price, final String name, final String image) { super(); _price = price; _name = name; URL temp; try { temp = new URL(image); } catch (final MalformedURLException e) { temp = null; } _imageUrl = temp; } public int getPrice() { return _price; } public String getName() { return _name; } public URL getImageURL() { return _imageUrl; } /** {@inheritDoc} */ @Override public int hashCode() { return _price * _name.hashCode(); } /** {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (null == obj) { return false; } if (this == obj) { return true; } if (!(obj instanceof Item)) { return false; } final Item that = (Item) obj; boolean rtn; rtn = getPrice() == that.getPrice(); if (!rtn) { return rtn; } rtn = getName().equals(that.getName()); if (!rtn) { return rtn; } return true; } /** {@inheritDoc} */ @Override public String toString() { return _price + "," + _name; } } // Item } // WishList
これはJava Advent Calendar 2011の7番目のエントリーです。
≪前のエントリー#6:JUnit のセカイ:shuji_w6eさん
≫次のエントリー#8:AnnotationProcessorを利用して楽してintrefaceを徹底活用したプログラミングをしようぜ:t_yanoさん
いよいよ本日!津田大介(@tsuda)氏講演会 in 沖縄
先日お知らせした
津田大介(@tsuda)氏講演会 in 沖縄 - ttmmrr(@o_tmr)の日記
いよいよ本日です。
琉球新報本社2階多目的ホール(那覇市天久)
(泉崎ではありません)
- 駐車場ないのでバス・タクシーなどの公共交通機関をご利用ください
- 開場:18:00 / 開演:19:00
- 入場料:2000円(先着100名さま)
- なるべくお釣りが出ない様にご準備ください
- 懇親会:4000円up!(計6000円)(先着30名さま)
- こちらもなるべくお釣りが出ない様にご準備ください
GroovyでSwingX
いまさらながらgroovy始めました。
Java で Swing で GUI なアプリを組んでいるんですが、
手続的なレイアウト構築はもう嫌だなぁと思っていて、
Java FX あたりに注目してましたが、script はなくなるし、Linux対応はだいぶ先な感じだし。
で、groovyでほぼ宣言的なレイアウトが組めそうだと知り、試しています。
groovyには標準でSwingBuilderというクラスがありますが、
SwingXに対応したSwingXBuilderもあるとのことで、
id:skrb さんのJava技術最前線:ITproの記事
Yet Another Swing - SwingX 第2回 Swingには存在しないコンポーネント
のタスクペインをgroovyで書き直して見ました。
import groovy.swing.SwingXBuilder import java.awt.event.KeyEvent import javax.swing.WindowConstants as WC class TaskPaneDemo { def static SwingXBuilder swing = new SwingXBuilder() TaskPaneDemo() { def frame1 = swing.frame(title:"TaskPane Demo", size:[200,300], defaultCloseOperation:WC.EXIT_ON_CLOSE) { scrollPane() { taskPaneContainer() { taskPane(title:"ファイル", collapsed:true, mnemonic:KeyEvent.VK_F) { button(text:"Open", actionPerformed: { println "open" } ) // ★ button(text:"Save", actionPerformed: { println "save" } ) } taskPane(title:"オプション", mnemonic:KeyEvent.VK_O) { bg1 = buttonGroup() checkBox(text:"Option 1", selected:true, buttonGroup:bg1) checkBox(text:"Option 2", buttonGroup:bg1) checkBox(text:"Option 3", buttonGroup:bg1) } } } } frame1.visible = true } static void main(args) { swing.edt { new TaskPaneDemo() } } }
コメントで★となっているあたりをbuttonで書いちゃいましたが、元記事では AbstractAction を使ってました。
groovy+SwingXBuilderでAbstractActionをどう扱うか分からなかったので、buttonにしちゃいました。(^^;
Ubuntuでの実行結果
subversionのコミットメールをrubyで送信する
subversionのコミットメールをrubyで送信する方法を見つけたけど、ちょっと使いづらかったので直してみた。
直した点は
- 複数の宛先
- それをファイルから読み込む
だ。
ファイルは三つ
- post-commit
- commit-email.to.yml
- commit-email.rb
- post-commit
#!/bin/sh export LANG="ja_JP.UTF8" REPOS="$1" REV="$2" ${REPOS}/hooks/commit-email.rb "$REPOS" "$REV"
- commit-email.rb
#!/usr/bin/ruby -Ke require 'net/smtp' require 'kconv' require 'yaml' REPOS=ARGV[0] REV=ARGV[1].to_i svnauthor=%x{svnlook author #{REPOS} -r #{REV} }.chomp svndate=%x{svnlook date #{REPOS} -r #{REV} }.chomp svnchanged=%x{svnlook changed #{REPOS} -r #{REV} }.chomp svnlog=%x{svnlook log #{REPOS} -r #{REV} }.chomp svndiff=%x{svnlook diff #{REPOS} -r #{REV} }.chomp toaddr =YAML.load_file('./commit-email.to.yml') fromaddr=['ttmmrr@example.com'] # from svnlog=svnlog.kconv(Kconv::UTF8, Kconv::ASCII) body = <<END_OF_BODY Subversion committed to #{REPOS} #{REV} Updated by #{svnauthor} Modified #{svndate} Log: -------------------------------------------------------- #{svnlog} Changed: [U:UPDATE A:APPEND D:DELETE] -------------------------------------------------------- #{svnchanged} Diff: -------------------------------------------------------- #{svndiff} END_OF_BODY message = <<END_OF_MESSAGE From: Subversion Admin <#{fromaddr}> To: #{toaddr.join(',')}, Subject: [SVN-#{REV}] Commit by #{svnauthor} MIME-Version: 1.0 Content-Type: text/plain; charset = ISO-2022-JP X-Mailer: /var/lib/svn/hooks/post-commit #{body.tojis} END_OF_MESSAGE Net::SMTP.start('ns.alp.co.jp', 25) { |smtp| smtp.send_mail(message, fromaddr, toaddr) }
- commit-email.to.yml
- aaa@example.com - bbb@example.com
参考
http://f29.aaa.livedoor.jp/~yamakan/index.php?LinuxSettingMemo%2F%A5%C7%A5%A3%A5%B9%A5%C8%A5%EA%A5%D3%A5%E5%A1%BC%A5%B7%A5%E7%A5%F3%B6%A6%C4%CC%2FSVN%28Subversion%29%A5%B3%A5%DF%A5%C3%A5%C8%A5%E1%A1%BC%A5%EB%C0%DF%C4%EA
http://www12.atpages.jp/~nekomike/blog/page/4/?cat=3
http://d.hatena.ne.jp/turipat/20090704/range_and_slice
第6回 YAMLファイルの扱い方 - WebデザイナーのためのRuby on Rails - Ruby on Rails with OIAX