Rails4 + devise + cancancan + rails_adminが最強すぎ
Rails4 + devise + cancancan + rails_adminが最強過ぎたので自分用にメモ。
deviseで認証をして
cancancanでロール毎のパーミッションを割り当てて
rails_adminをdeviseとcancancanでadminユーザのみに使わせる
また、自分はrvmを使っているのでその辺りの設定も入っているが、使っていない場合はその辺りを無視で。
今回のプロジェクト(sample)用にrvmの設定
rvmのgemsetを作成してrailsをインストール
rvm gemset create sample rvm 2.1@sample gem install rails
Gemfile
Gemfileを編集
gem 'devise' gem 'cancancan' gem 'rails_admin'
を追加
bundle installする
bundle install
deviseの設定
devise関連のファイルをgenerate
rails g devise:install
config/environments/development.rbを編集
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
を追加
app/views/layouts/application.html.erbを編集
<% if notice %> <p class="alert alert-notice"><%= notice %></p> <% end %> <% if alert %> <p class="alert alert-error"><%= alert %></p> <% end %>
を<%= yield %>の前あたりに追加。
deviceのユーザ管理モデルとロール、それらを結ぶ連結モデルの追加
rails g devise user rails g model role name rails g migration CreateJoinTableUserRole user role rake db:migrate
app/models/user.rbを編集
has_and_belongs_to_many :roles def has_role?(name) self.roles.where(name: name).length > 0 end
app/models/role.rbを編集
has_and_belongs_to_many :users
を追加。
cancancanの設定
cancancan関連のファイルをgenerate
rails g cancan:ability
app/models/ability.rbを編集
if user.has_role?('admin') can :read, :all can :access, :rails_admin can :dashboard if user.has_role?('superadmin') can :manage, :all else can :manage, [] # A end else can :read, [] # B can :create, [] # C end
を追加。
- superadminは全てのモデルの管理が可能
- adminユーザは全てのモデルのreadと、Aで指定されたも出るの管理が可能
- それ以外のユーザはBで指定されたモデルのreadと、Cで指定されたモデルのcreateが可能
アプリケーションの用途等に合わせて上記を変更する
初期データ(管理者ユーザ)の作成
railsサーバの起動
rails s
http://localhost:3000/adminにアクセスして
Roleで
- admin
- superadmin
を作成して
Userでadminとsuperadminを関連付けた管理者ユーザを作成する。
終わったらCtrl+cでサーバを終了しておく。
※ スクリーンショットではログインしている状態だけどこの時点ではまだなので気にしない。
rails_adminの認証設定
config/initializers/rails_admin.rbを編集
# config.authenticate_with do # warden.authenticate! scope: :user # end # config.current_user_method(&:current_user)
と
# config.authorize_with :cancan
となっているコメントを外す。
動作確認
railsサーバの起動
rails s
して
- http://localhost:3000/adminにアクセスしてログイン画面が出てくる
- 先ほど作った管理者ユーザでログイン出来る
- 全てのオブジェクトの管理が出来る
- 管理者以外のユーザを作成して狙い通りに動く事
を確認する
おまけ
メール送信関連の設定
config/environments/development.rbを編集
config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { :address => 'smtp.gmail.com', :port => 587, :authentication => :plain, :user_name => 'メールアドレス', :password => 'パスワード' }
ログアウトをGETに変更
config/initializers/rails_admin.rbを編集
デフォルトではdeleteになっているので
config.sign_out_via = :delete
getに変更しておくといいかも?
config.sign_out_via = :get
ユーザSign up時にメール確認
db/migrate/*_devise_create_users.rbを編集
## Confirmable # t.string :confirmation_token # t.datetime :confirmed_at # t.datetime :confirmation_sent_at # t.string :unconfirmed_email # Only if using reconfirmable
のコメントアウトを外す。
app/models/user.rbを編集
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable
:confirmableを追加
与えられたぷよぷよフィールドが19連鎖であることを証明する
とあるHP*1にて発見した問題。
久しぶりにCで書いたらSEGVで落ちまくり結局1時間弱かかってしまった。。。
問題
ゲーム「ぷよぷよ」で、フィールドの状態がテキストで与えられたとき、消える「ぷよ」を消して次のフィールドの状態を出力するプログラムを書け。 たとえば、色をG/Y/Rで表すとき(Green/Yellow/Red)、 GGR YGG であればGが消えて Y R になります。 また、このプログラムを使って次のフィールドを与えると19連鎖ののちすべてのぷよが消えることを確認し、消える途中の様子をあわせて提出すること。 GYRR RYYGYG GYGYRR RYGYRG YGYRYG GYRYRG YGYRYR YGYRYR YRRGRG RYGYGG GRYGYR GRYGYR GRYGYR ※1行目の"GYRR"の前には半角スペースが2つ入っている
バージョン1(思いつくがままに書いたバージョン)
塗りつぶしアルゴリズム(mark関数)が糞
#include <stdio.h> #include <stdlib.h> #include <string.h> char **input(int *w, int *h); void free_board(char **board, int h); void show(char **board, int w, int h); int mark(char **board, int w, int h); void del(char **board, int w, int h); char **input(int *w, int *h) { int ww =0, hh = 0, alloc_h = 10; char *p, **board = (char **)malloc(sizeof (char *) * alloc_h); char buf[256]; while (fgets(buf, 256, stdin) != NULL) { while (buf[strlen(buf) - 1] == '\n' || buf[strlen(buf) - 1] == '\r') buf[strlen(buf) - 1] = '\0'; if (ww == 0) { ww = strlen(buf); } else if (ww != strlen(buf)) { free_board(board, hh); return NULL; } board[hh] = strcpy((char *)malloc(ww + 1), buf); hh++; if (hh >= alloc_h) { alloc_h += 10; board = (char **)realloc(board, sizeof (char *) * alloc_h); } } *w = ww; *h = hh; return board; } void free_board(char **board, int h) { int i; for (i = 0; i < h; i++) { free(board[i]); } free(board); } void show(char **board, int w, int h) { int i; for (i = 0; i < w + 2; i++) putchar('-'); printf("\n"); for (i = 0; i < h; i++) { printf("|%s|\n", board[i]); } for (i = 0; i < w + 2; i++) putchar('-'); printf("\n"); } int mark(char **board, int w, int h) { int i, j, k, l, dels, count, total = 0; char c; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { c = board[i][j]; if (c == ' ' || c == '*') continue; board[i][j] = '%'; dels = 1; do { count = 0; for (k = 0; k < h; k++) { for (l = 0; l < w; l++) { if (board[k][l] != c) continue; if ( (k - 1 >= 0 && board[k - 1][l] == '%') || (k + 1 < h && board[k + 1][l] == '%') || (l - 1 >= 0 && board[k][l - 1] == '%') || (l + 1 < w && board[k][l + 1] == '%') ) { board[k][l] = '%'; count++; } } } dels += count; } while (count > 0); if (dels >= 4) { c = '*'; total += dels; } for (k = 0; k < h; k++) { for (l = 0; l < w; l++) { if (board[k][l] == '%') board[k][l] = c; } } } } return total; } void del(char **board, int w, int h) { int i, j, k, l; for (i = h - 1; i >= 0; i--) { for (j = 0; j < w; j++) { if (board[i][j] == '*') board[i][j] = ' '; if (board[i][j] == ' ') { for (k = i - 1; k >= 0; k--) { if (board[k][j] != ' ' && board[k][j] != '*') { board[i][j] = board[k][j]; if (board[i][j] == '*') board[i][j] = ' '; board[k][j] = ' '; break; } } } } } } int main(int argc, char **argv) { int i = 0, w, h; char **board = input(&w, &h); if (board == NULL) { exit(1); } show(board, w, h); while (mark(board, w, h) > 0) { printf("%d:\n", ++i); show(board, w, h); del(board, w, h); show(board, w, h); } free_board(board, h); return 0; }
塗りつぶしアルゴリズムをスタックを使ったバージョンにしたもの。5重ループが3重ループになりすっきり。108連鎖*2を与えた場合には10倍程度早くなった。
int mark(char **board, int w, int h) { int i, j, k, dels, total = 0, x, y; int *stack = (int *)malloc(sizeof (int) * w * h), sp; char c; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { c = board[i][j]; if (c == ' ' || c == '*') continue; dels = sp = 0; stack[dels++] = i * w + j; while (sp < dels) { x = stack[sp] % w; y = stack[sp] / w; board[y][x] = '%'; if (x - 1 >= 0 && board[y][x - 1] == c) stack[dels++] = y * w + x - 1; if (x + 1 < w && board[y][x + 1] == c) stack[dels++] = y * w + x + 1; if (y - 1 >= 0 && board[y - 1][x] == c) stack[dels++] = (y - 1) * w + x; if (y + 1 < h && board[y + 1][x] == c) stack[dels++] = (y + 1) * w + x; sp++; } if (dels >= 4) { c = '*'; total += dels; } for (k = 0; k < dels; k++) { board[stack[k] / w][stack[k] % w] = c; } } } free(stack); return total; }
最初からこれを書けなくなっているのは衰えている証拠かな。。
RubyでOAuth1.0のrequest tokenを取得
テスト用に使ったコード。
REQUEST_TOKEN_URI = 'https://api.twitter.com/oauth/request_token' CALLBACK_URI = 'http://localhost/callback' CONSUMER_KEY = '**********************' CONSUMER_SECRET = '**********************'
や場合によってはいろいろを書き換えるとTwitter以外にも使えると思います。
#!/usr/bin/env ruby # -*- coding: utf-8 -*- require 'uri' require 'cgi' require 'openssl' require 'net/https' class OAuthTest NONCE_CHARS = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a NONCE_LENGTH = 32 REQUEST_TOKEN_URI = 'https://api.twitter.com/oauth/request_token' CALLBACK_URI = 'https://c.na1.visual.force.com/apex/OAuthComplete' CONSUMER_KEY = 'xxxxxxxxxxxxxxxxxxxxxx' CONSUMER_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' def create_nonce NONCE_LENGTH.times.inject('') {|str| str + NONCE_CHARS[rand(NONCE_CHARS.size)]} end def create_signature secret, params signature_base_string = [ 'POST', REQUEST_TOKEN_URI, params.map{|k, v| "#{k}=#{CGI.escape(v)}"}.join('&'), ].map{|s| CGI.escape(s)}.join('&') signature_digest = OpenSSL::HMAC::digest(OpenSSL::Digest::SHA1.new, secret, signature_base_string) [signature_digest].pack('m').gsub!(/\n/u, '') end def ger_request_token params request_token_uri = URI.split(REQUEST_TOKEN_URI) if request_token_uri[0] == 'https' http = Net::HTTP.new(request_token_uri[2], 443) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE http.verify_depth = 5 else http = Net::HTTP.new(request_token_uri[2]) end header = { 'Content-type' => 'application/x-www-form-urlencoded', 'Authorization' => 'OAuth ' + params.map{|k, v| "#{k}=#{CGI.escape(v)}"}.select{|s| s =~ /^oauth_/}.join(',') } http.post(request_token_uri[5], nil, header).body end def request_token params = { 'oauth_callback' => CALLBACK_URI, 'oauth_consumer_key' => CONSUMER_KEY, 'oauth_nonce' => create_nonce, 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_timestamp' => (Time.now - Time.utc(1970, 1, 1)).to_i.to_s, 'oauth_version' => '1.0', } params['oauth_signature'] = create_signature("#{CONSUMER_SECRET}&", params) ger_request_token(params) end end puts OAuthTest.new.request_token
C言語でKaratsuba開平を利用して整数の平方根と余りを求める
math.hのsqrt(f)を使わずに整数の平方根を求めてみた。(intが4byteであるマシンでのみ動作)
sqrtmod0はアルゴリズム上0x40000000以上の値の平方根を計算でようなので、0x40000000以上の値はKaratsuba開平を利用して求めている。
↓は乱数で平方根と余りを求めてsqrtで求めたものとassertをし続けるプログラムです。
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <math.h> #define BASE (sizeof (unsigned int) * 8 / 4) int sqrtmod(unsigned int n, unsigned int *pmod); int sqrtmod_karatsuba(unsigned int n, unsigned int *pmod); int sqrtmod0(unsigned int n, unsigned int *pmod); int sqrtmod(unsigned int n, unsigned int *pmod) { return sqrtmod_karatsuba(n, pmod); } int sqrtmod_karatsuba(unsigned int n, unsigned int *pmod) { int s, s1, s0, r, r1, r0; if ((0xc0000000 & n) == 0) { return sqrtmod0(n, pmod); } s1 = sqrtmod0(n >> (BASE * 2), &r1); r0 = (r1 << BASE) | ((n >> BASE) & 0x000000ff); s0 = r0 / (s1 << 1); r0 -= s0 * (s1 << 1); s = (s1 << BASE) + s0; r = ((r0 << BASE) | (n & 0x000000ff)) - s0 * s0; if (r < 0) { r += (s << 1) - 1; s--; } if (pmod) *pmod = r; assert(s * s + r == n); return s; } int sqrtmod0(unsigned int n, unsigned int *pmod) { int p = 0, q = 1, r = n, h = 0; while (q <= n) { q = q << 2; } while (1 != q) { q = q >> 2; h = p + q; p = p >> 1; if (r >= h) { p = p + q; r = r - h; } } if (pmod) *pmod = r; assert(p * p + r == n); return p; } int main(int argc, char **argv) { unsigned int n, s1, r1, s2, r2; srand((unsigned)time(NULL)); while (1) { n = rand(); s1 = sqrtmod(n, &r1); s2 = sqrt(n); r2 = n - s2 * s2; assert(s1 == s2 && r1 == r2); } return 0; }
とりあえずassertエラーが出ないので、実装は出来ているらしい。。
参考:
2 Karatsuba$B7ONs$N%"%k%4%j%:%`(B
GNU MP 6.1.2: Square Root Algorithm
Rubyでn桁の円周率を求める
検証用に使っていた物です。
len = ARGV[0].to_i B = 10 ** len B2 = B << 1 pi = (len * 8 + 1).step(3, -2).inject(B) {|a, i| (i >> 1) * (a + B2) / i} - B puts "3.#{pi}"
# time ruby pi.rb 100 3.141592653589793238462643383279502884197169399375105820974944592307816406286208 9986280348253421170679 real 0m0.045s user 0m0.027s sys 0m0.007s
10万桁位なら2分前後で行けるはずです。
※ウチのPCはショボイのでもっと早いかも
ASCIIコード表見て今頃気づいたこと
toupperとtolowerって、cが英字であることが担保されていればこれでいい
#define TOUPPER(c) ((c) & ~0x20) #define TOLOWER(c) ((c) | 0x20)
Linuxでユーザ管理関係のコマンドまとめ
ユーザ追加
useradd ユーザ名
グループ、パスワードも一気に
useradd -g グループ名 -p パスワード ユーザ名
ユーザに利用有効期限を付ける
useradd -e yyyy-mm-dd ユーザ名
ログインシェルをnologinにして、TelnetやSSHでログインさせないようなユーザを作る
メールとHPは許可
useradd -s /sbin/nologin ユーザ名
同様に、メールしか利用できないようなユーザを作る
useradd -s /sbin/false ユーザ名
発効から指定日数までにログインしないと、無効になるユーザを作る
useradd -f 日数 ユーザ名
パスワード変更
自分のパスワードなら
passwd の後、パスワード入力。
管理者が他のユーザのパスワードを変更する場合は、
passwd ユーザ名
グループを追加
groupadd グループ名
グループは、
cat /etc/group
で確認できる。
グループ名変更
groupmod -l 旧グループ名 新グループ名
グループを削除
groupdel グループ名