keikokuc 0.7 → 0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,12 +11,16 @@ class Keikokuc::Client
11
11
  InvalidNotification = Class.new
12
12
  Unauthorized = Class.new
13
13
 
14
- attr_accessor :producer_api_key, :user, :password
14
+ attr_accessor :username, :api_key
15
15
 
16
+ # Internal: Initialize a Client
17
+ #
18
+ # opts = a hash containing two possible attributes:
19
+ # api_key - the user's or producer's API key (required)
20
+ # username - the producer's username (only required for publishers)
16
21
  def initialize(opts = {})
17
- @producer_api_key = opts[:producer_api_key]
18
- @user = opts[:user]
19
- @password = opts[:password]
22
+ @api_key = opts.fetch(:api_key)
23
+ @username = opts[:username]
20
24
  end
21
25
 
22
26
  # Internal: posts a new notification to keikoku
@@ -25,7 +29,8 @@ class Keikokuc::Client
25
29
  #
26
30
  # Examples
27
31
  #
28
- # client = Keikokuc::Client.new(producer_api_key: 'abcd')
32
+ # client = Keikokuc::Client.new(username: 'heroku-postgres',
33
+ # api_key: 'abcd')
29
34
  # response, error = client.post_notification(message: 'hello')
30
35
  #
31
36
  # Returns
@@ -42,7 +47,7 @@ class Keikokuc::Client
42
47
  # * `Client::Unauthorized` if API key auth fails
43
48
  def post_notification(attributes)
44
49
  begin
45
- response = notifications_api.post(encode_json(attributes), {'X-KEIKOKU-AUTH' => producer_api_key})
50
+ response = notifications_api.post(encode_json(attributes))
46
51
  [parse_json(response), nil]
47
52
  rescue RestClient::UnprocessableEntity => e
48
53
  [parse_json(e.response), InvalidNotification]
@@ -56,7 +61,7 @@ class Keikokuc::Client
56
61
  #
57
62
  # Examples
58
63
  #
59
- # client = Keikokuc::Client.new(user: 'user@example.com', password: 'pass')
64
+ # client = Keikokuc::Client.new(api_key: 'api-key')
60
65
  # response, error = client.get_notifications
61
66
  #
62
67
  # Returns
@@ -83,6 +88,8 @@ class Keikokuc::Client
83
88
  #
84
89
  # remote_id - the keikoku id for the notification to mark as read
85
90
  #
91
+ # Returns
92
+ #
86
93
  # two objects:
87
94
  # The response as a hash
88
95
  # The error if any (nil if no error)
@@ -95,7 +102,6 @@ class Keikokuc::Client
95
102
  begin
96
103
  response = notifications_api["/#{remote_id}/read"].post ''
97
104
  parsed_response = parse_json(response)
98
- parsed_response[:read_at] = DateTime.parse(parsed_response[:read_at])
99
105
  [parsed_response, nil]
100
106
  rescue RestClient::Unauthorized
101
107
  [{}, Unauthorized]
@@ -103,12 +109,13 @@ class Keikokuc::Client
103
109
  end
104
110
  handle_timeout :read_notification
105
111
 
106
-
107
112
  private
108
113
  def notifications_api # :nodoc:
109
- @notifications_api ||= RestClient::Resource.new(api_url,
110
- :user => user,
111
- :password => password)
114
+ @notifications_api ||= RestClient::Resource.new(
115
+ api_url,
116
+ :user => username || '',
117
+ :password => api_key
118
+ )
112
119
  end
113
120
 
114
121
  def api_url # :nodoc:
@@ -7,7 +7,8 @@
7
7
  # notification = Keikokuc::Notification.new(message: 'hello',
8
8
  # severity: 'info',
9
9
  # target_name: 'sunny-skies-42'
10
- # producer_api_key: 'abcd')
10
+ # producer_password: 'abcd',
11
+ # username: 'heroku-postgres')
11
12
  # if notification.publish
12
13
  # # persist notification
13
14
  # else
@@ -17,8 +18,8 @@
17
18
  class Keikokuc::Notification
18
19
  attr_accessor :message, :url, :severity,
19
20
  :target_name, :account_email,
20
- :producer_api_key, :remote_id,
21
- :errors, :read_at, :account_sequence
21
+ :producer_password, :username,
22
+ :remote_id, :errors, :account_sequence
22
23
 
23
24
  # Public: Initialize a notification
24
25
  #
@@ -30,17 +31,17 @@ class Keikokuc::Notification
30
31
  #
31
32
  # All keys on the attr_accessor list will be set
32
33
  def initialize(opts = {})
33
- @message = opts[:message]
34
- @url = opts[:url]
35
- @severity = opts[:severity]
36
- @target_name = opts[:target_name]
37
- @account_email = opts[:account_email]
38
- @producer_api_key = opts[:producer_api_key]
39
- @remote_id = opts[:remote_id]
40
- @errors = opts[:errors]
41
- @read_at = opts[:read_at]
42
- @account_sequence = opts[:account_sequence]
43
- @client = opts[:client]
34
+ @message = opts[:message]
35
+ @url = opts[:url]
36
+ @severity = opts[:severity]
37
+ @target_name = opts[:target_name]
38
+ @account_email = opts[:account_email]
39
+ @producer_password = opts[:producer_password]
40
+ @username = opts[:username]
41
+ @remote_id = opts[:remote_id]
42
+ @errors = opts[:errors]
43
+ @account_sequence = opts[:account_sequence]
44
+ @client = opts[:client]
44
45
  end
45
46
 
46
47
  # Public: publishes this notification to keikoku
@@ -70,39 +71,31 @@ class Keikokuc::Notification
70
71
  # Returns a boolean set to true if marking as read succeeded
71
72
  def read
72
73
  response, error = client.read_notification(remote_id)
73
- if error.nil?
74
- self.read_at = response[:read_at]
75
- end
76
74
  error.nil?
77
75
  end
78
76
 
79
- # Public: whether this notification is marked as read by this user
80
- #
81
- # Returns true if the user has marked this notification as read
82
- def read?
83
- !!@read_at
84
- end
85
-
86
77
  # Internal: coerces this notification to a hash
87
78
  #
88
79
  # Returns this notification's attributes as a hash
89
80
  def to_hash
90
81
  {
91
- :message => @message,
92
- :url => @url,
93
- :severity => @severity,
94
- :target_name => @target_name,
95
- :account_email => @account_email,
96
- :producer_api_key => @producer_api_key,
97
- :remote_id => @remote_id,
98
- :errors => @errors,
99
- :read_at => @read_at,
100
- :account_sequence => @account_sequence,
101
- :client => @client
82
+ :message => @message,
83
+ :url => @url,
84
+ :severity => @severity,
85
+ :target_name => @target_name,
86
+ :account_email => @account_email,
87
+ :producer_password => @producer_password,
88
+ :remote_id => @remote_id,
89
+ :errors => @errors,
90
+ :account_sequence => @account_sequence,
91
+ :client => @client
102
92
  }
103
93
  end
104
94
 
105
95
  def client # :nodoc:
106
- @client ||= Keikokuc::Client.new(:producer_api_key => producer_api_key)
96
+ @client ||= Keikokuc::Client.new(
97
+ :username => username,
98
+ :api_key => producer_password
99
+ )
107
100
  end
108
101
  end
@@ -8,8 +8,7 @@
8
8
  #
9
9
  # Examples
10
10
  #
11
- # notifications = Keikokuc::NotificationList.new(user: 'user@example.com',
12
- # api_key: 'abcd')
11
+ # notifications = Keikokuc::NotificationList.new(api_key: 'abcd')
13
12
  # if notifications.fetch
14
13
  # notifications.each do |notification|
15
14
  # puts notification.inspect
@@ -20,18 +19,16 @@
20
19
  class Keikokuc::NotificationList
21
20
  include Enumerable
22
21
 
23
- attr_accessor :user, :password
22
+ attr_accessor :api_key
24
23
 
25
24
  # Public: Initializes a NotificationList
26
25
  #
27
26
  # opts - options hash containing attribute values for the object
28
27
  # being constructed accepting the following three keys:
29
- # user - the heroku account's email (required)
30
- # password - the heroku account's password (required)
28
+ # api_key - the heroku account's api_key (required)
31
29
  # client - the client, used for DI in tests
32
30
  def initialize(opts)
33
- @user = opts.fetch(:user)
34
- @password = opts.fetch(:password)
31
+ @api_key = opts.fetch(:api_key)
35
32
  @client = opts[:client]
36
33
  @notifications = []
37
34
  end
@@ -95,7 +92,7 @@ class Keikokuc::NotificationList
95
92
 
96
93
  private
97
94
  def client # :nodoc:
98
- @client ||= Keikokuc::Client.new(:user => user,
99
- :password => password)
95
+ @client ||= Keikokuc::Client.new(:user => '',
96
+ :api_key => api_key)
100
97
  end
101
98
  end
@@ -1,3 +1,3 @@
1
1
  module Keikokuc
2
- VERSION = "0.7"
2
+ VERSION = "0.8"
3
3
  end
@@ -11,8 +11,8 @@ module Keikokuc
11
11
 
12
12
  it 'publishes a new notification' do
13
13
  ShamRack.mount(fake_keikoku, "keikoku.herokuapp.com", 443)
14
- fake_keikoku.register_publisher({:api_key => 'abc'})
15
- client = Client.new(:producer_api_key => 'abc')
14
+ fake_keikoku.register_producer({:api_key => 'abc', :username => 'heroku-postgres'})
15
+ client = Client.new(:api_key => 'abc', :username => 'heroku-postgres')
16
16
  result, error = client.post_notification(:message => 'hello',
17
17
  :severity => 'info')
18
18
  expect(result[:id]).not_to be_nil
@@ -24,15 +24,15 @@ module Keikokuc
24
24
  [422, {}, StringIO.new(OkJson.encode({ 'errors' => 'srorre' }))]
25
25
  end
26
26
 
27
- response, error = Client.new.post_notification({})
27
+ response, error = Client.new(:api_key => 'key').post_notification({})
28
28
  expect(error).to be Client::InvalidNotification
29
29
  expect(response[:errors]).to eq('srorre')
30
30
  end
31
31
 
32
32
  it 'handles authentication failures' do
33
33
  ShamRack.mount(fake_keikoku, "keikoku.herokuapp.com", 443)
34
- fake_keikoku.register_publisher({:api_key => 'abc'})
35
- client = Client.new(:producer_api_key => 'bad one')
34
+ fake_keikoku.register_producer({:api_key => 'abc', :username => 'heroku-postgres'})
35
+ client = Client.new(:api_key => 'bad one', :username => 'heroku-postgres')
36
36
  result, error = client.post_notification(:message => 'hello',
37
37
  :severity => 'info')
38
38
  expect(result[:id]).to be_nil
@@ -41,7 +41,7 @@ module Keikokuc
41
41
 
42
42
  it 'handles timeouts' do
43
43
  RestClient::Resource.any_instance.stub(:post).and_raise Timeout::Error
44
- response, error = Client.new.post_notification({})
44
+ response, error = Client.new(:api_key => 'key').post_notification({})
45
45
  expect(response).to be_nil
46
46
  expect(error).to eq(Client::RequestTimeout)
47
47
  end
@@ -52,12 +52,14 @@ module Keikokuc
52
52
 
53
53
  it 'gets all notifications for a user' do
54
54
  ShamRack.mount(fake_keikoku, "keikoku.herokuapp.com", 443)
55
- fake_keikoku.register_publisher(:api_key => 'abc')
56
- fake_keikoku.register_user(:email => 'harold@heroku.com', :password => 'pass')
57
- build_notification(:account_email => 'harold@heroku.com', :message => 'find me!', :producer_api_key => 'abc').publish
58
- build_notification(:account_email => 'another@heroku.com', :producer_api_key => 'abc').publish
55
+ fake_keikoku.register_producer(:username => 'heroku-postgres', :api_key => 'abc')
56
+ fake_keikoku.register_user(:api_key => 'api-key', :account_email => 'harold@heroku.com')
57
+ build_notification(:account_email => 'harold@heroku.com', :message => 'find me!',
58
+ :producer_password => 'abc', :username => 'heroku-postgres').publish
59
+ build_notification(:account_email => 'another@heroku.com', :producer_password => 'abc',
60
+ :username => 'heroku-postgres').publish
59
61
 
60
- client = Client.new(:user => 'harold@heroku.com', :password => 'pass')
62
+ client = Client.new(:api_key => 'api-key')
61
63
 
62
64
  notifications, error = client.get_notifications
63
65
 
@@ -69,15 +71,15 @@ module Keikokuc
69
71
 
70
72
  it 'handles timeouts' do
71
73
  RestClient::Resource.any_instance.stub(:get).and_raise Timeout::Error
72
- response, error = Client.new.get_notifications
74
+ response, error = Client.new(:api_key => 'key').get_notifications
73
75
  expect(response).to be_nil
74
76
  expect(error).to eq(Client::RequestTimeout)
75
77
  end
76
78
 
77
79
  it 'handles authentication failures' do
78
80
  ShamRack.mount(fake_keikoku, "keikoku.herokuapp.com", 443)
79
- fake_keikoku.register_user(:email => 'harold@heroku.com', :password => 'pass')
80
- client = Client.new(:user => 'harold@heroku.com', :password => 'bad-pass')
81
+ fake_keikoku.register_user(:api_key => 'api-key', :account_email => 'harold@heroku.com')
82
+ client = Client.new(:api_key => 'bad-api-key')
81
83
 
82
84
  response, error = client.get_notifications
83
85
 
@@ -88,28 +90,28 @@ module Keikokuc
88
90
 
89
91
  describe Client, '#read_notification' do
90
92
  include_context 'client specs'
93
+
91
94
  it 'marks the notification as read' do
92
95
  ShamRack.mount(fake_keikoku, "keikoku.herokuapp.com", 443)
93
- fake_keikoku.register_publisher(:api_key => 'abc')
94
- fake_keikoku.register_user(:email => 'harold@heroku.com', :password => 'pass')
95
- client = Client.new(:user => 'harold@heroku.com', :password => 'pass')
96
- notification = build_notification(:account_email => 'harold@heroku.com',
97
- :producer_api_key => 'abc')
96
+
97
+ fake_keikoku.register_producer(:username => 'heroku-postgres', :api_key => 'abc')
98
+ fake_keikoku.register_user(:api_key => 'api-key', :account_email => 'harold@heroku.com')
99
+ client = Client.new(:api_key => 'api-key')
100
+ notification = build_notification(:account_email => 'harold@heroku.com',
101
+ :producer_password => 'abc',
102
+ :username => 'heroku-postgres')
98
103
  notification.publish or raise "Notification publish error"
99
104
 
100
105
  response, error = client.read_notification(notification.remote_id)
101
106
  expect(error).to be_nil
102
107
 
103
- expect(response[:read_by]).to eq('harold@heroku.com')
104
- expect(response[:read_at]).to be_within(1).of(DateTime.now)
108
+ expect(response[:status]).to eq('ok')
105
109
  end
106
110
 
107
111
  it 'handles authentication errors' do
108
112
  ShamRack.mount(fake_keikoku, "keikoku.herokuapp.com", 443)
109
- fake_keikoku.register_user(:email => 'harold@heroku.com',
110
- :password => 'pass')
111
- client = Client.new(:user => 'harold@heroku.com',
112
- :password => 'bad-pass')
113
+ fake_keikoku.register_user(:api_key => 'api-key', :account_email => 'harold@heroku.com')
114
+ client = Client.new(:api_key => 'bad-api-key')
113
115
  response, error = client.read_notification(1)
114
116
  expect(response).to be_empty
115
117
  expect(error).to eq(Client::Unauthorized)
@@ -117,7 +119,7 @@ module Keikokuc
117
119
 
118
120
  it 'handles timeouts' do
119
121
  RestClient::Resource.any_instance.stub(:post).and_raise Timeout::Error
120
- response, error = Client.new.read_notification(1)
122
+ response, error = Client.new(:api_key => 'key').read_notification(1)
121
123
  expect(response).to be_nil
122
124
  expect(error).to eq(Client::RequestTimeout)
123
125
  end
@@ -65,11 +65,6 @@ module Keikokuc
65
65
  list.fetch or raise "error fetching"
66
66
 
67
67
  expect(list.read_all).to be_true
68
-
69
- list.each do |notification|
70
- expect(notification).to be_read
71
- expect(notification.read_at).to eq(now)
72
- end
73
68
  end
74
69
 
75
70
  it 'returns false if any notification fails to be marked as read' do
@@ -51,15 +51,12 @@ module Keikokuc
51
51
 
52
52
  fake_client.should_receive(:read_notification).
53
53
  with('1234').
54
- and_return([{:read_at => Time.now}, nil])
54
+ and_return([{'status' => 'ok'}, nil])
55
55
 
56
56
  notification = Notification.new(:remote_id => '1234', :client => fake_client)
57
57
 
58
58
  result = notification.read
59
59
  expect(result).to be_true
60
-
61
- expect(notification.read_at).to be_within(1).of(Time.now)
62
- expect(notification).to be_read
63
60
  end
64
61
 
65
62
  it 'handles errors' do
@@ -73,28 +70,15 @@ module Keikokuc
73
70
 
74
71
  result = notification.read
75
72
  expect(result).to be_false
76
-
77
- expect(notification.read_at).to be_nil
78
- expect(notification).not_to be_read
79
- end
80
- end
81
-
82
- describe Notification, '#read?' do
83
- it 'is true if the read_at is known' do
84
- notification = build_notification(:read_at => nil)
85
- expect(notification.read?).to be_false
86
-
87
- notification.read_at = Time.now
88
-
89
- expect(notification.read?).to be_true
90
73
  end
91
74
  end
92
75
 
93
76
  describe Notification, '#client' do
94
77
  it 'defaults to a properly constructer Keikokuc::Client' do
95
- notification = build_notification(:producer_api_key => 'fake-api-key')
78
+ notification = build_notification(:producer_password => 'fake-api-key', :username => 'heroku-postgres')
96
79
  expect(notification.client).to be_kind_of(Keikokuc::Client)
97
- expect(notification.client.producer_api_key).to eq('fake-api-key')
80
+ expect(notification.client.api_key).to eq('fake-api-key')
81
+ expect(notification.client.username).to eq('heroku-postgres')
98
82
  end
99
83
 
100
84
  it 'can be injected' do
@@ -11,8 +11,7 @@ module Factories
11
11
 
12
12
  def build_notification_list(opts = {})
13
13
  defaults = {
14
- :user => 'user@example.com',
15
- :password => 'pass'
14
+ :api_key => 'api-key'
16
15
  }
17
16
  Keikokuc::NotificationList.new(defaults.merge(opts))
18
17
  end
@@ -1,24 +1,28 @@
1
1
  class FakeKeikoku
2
2
  def initialize
3
- @publishers = []
3
+ @producers = []
4
4
  @notifications = []
5
5
  @users = []
6
6
  end
7
7
 
8
- def register_publisher(opts)
9
- @publishers << opts
8
+ def register_producer(opts)
9
+ @producers << opts
10
10
  end
11
11
 
12
12
  def register_user(opts)
13
13
  @users << opts
14
14
  end
15
15
 
16
- def publisher_by_api_key(api_key)
17
- @publishers.detect { |p| p[:api_key] == api_key }
16
+ def producer_by_api_key(api_key)
17
+ @producers.detect { |p| p[:api_key] == api_key }
18
18
  end
19
19
 
20
- def find_user(user, pass)
21
- @users.detect { |u| u[:email] == user && u[:password] == pass }
20
+ def find_user(api_key)
21
+ @users.detect { |u| u[:api_key] == api_key }
22
+ end
23
+
24
+ def find_producer(user, api_key)
25
+ @producers.detect { |p| p[:username] == user && p[:api_key] == api_key }
22
26
  end
23
27
 
24
28
  def notifications_for_user(email)
@@ -30,7 +34,7 @@ class FakeKeikoku
30
34
  def call(env)
31
35
  with_rack_env(env) do
32
36
  if request_path == '/api/v1/notifications' && request_verb == 'POST'
33
- if publisher_by_api_key(request_api_key)
37
+ if authenticate_producer
34
38
  notification = Notification.new({:id => next_id}.merge(request_body))
35
39
  @notifications << notification
36
40
  [200, { }, [Keikokuc::OkJson.encode({'id' => notification.id})]]
@@ -38,8 +42,8 @@ class FakeKeikoku
38
42
  [401, { }, ["Not authorized"]]
39
43
  end
40
44
  elsif request_path == '/api/v1/notifications' && request_verb == 'GET'
41
- if current_user = authenticate_consumer
42
- notifications = notifications_for_user(current_user).map(&:to_hash)
45
+ if api_key = authenticate_consumer
46
+ notifications = notifications_for_user(api_key).map(&:to_hash)
43
47
  [200, { }, [Keikokuc::OkJson.encode(notifications)]]
44
48
  else
45
49
  [401, { }, ["Not authorized"]]
@@ -50,7 +54,7 @@ class FakeKeikoku
50
54
  notification.to_hash['id'].to_s == $1.to_s
51
55
  end
52
56
  notification.mark_read_by!(current_user)
53
- [200, {}, [Keikokuc::OkJson.encode({'read_by' => current_user, 'read_at' => Time.now.to_s})]]
57
+ [200, {}, [Keikokuc::OkJson.encode({'status' => 'ok'})]]
54
58
  else
55
59
  [401, { }, ["Not authorized"]]
56
60
  end
@@ -85,16 +89,21 @@ private
85
89
  Keikokuc::OkJson.decode(raw_body)
86
90
  end
87
91
 
88
- def request_api_key
89
- rack_env["HTTP_X_KEIKOKU_AUTH"]
92
+ def authenticate_producer
93
+ auth = Rack::Auth::Basic::Request.new(rack_env)
94
+ if auth.provided? && auth.basic? && creds = auth.credentials
95
+ if find_producer(*creds)
96
+ creds.first
97
+ end
98
+ end
90
99
  end
91
100
 
92
101
  def authenticate_consumer
93
102
  auth = Rack::Auth::Basic::Request.new(rack_env)
94
103
  if auth.provided? && auth.basic? && creds = auth.credentials
95
- # creds looks like [user, password]
96
- if find_user(*creds)
97
- creds.first
104
+ # creds looks like [user, password] (technically ['', api_key])
105
+ if user = find_user(creds[-1])
106
+ user[:account_email]
98
107
  end
99
108
  end
100
109
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keikokuc
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.7'
4
+ version: '0.8'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-20 00:00:00.000000000 Z
12
+ date: 2013-01-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
16
- requirement: &70175173062000 !ruby/object:Gem::Requirement
16
+ requirement: &70304476052160 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70175173062000
24
+ version_requirements: *70304476052160
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70175173061580 !ruby/object:Gem::Requirement
27
+ requirement: &70304475972260 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70175173061580
35
+ version_requirements: *70304475972260
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sham_rack
38
- requirement: &70175173061160 !ruby/object:Gem::Requirement
38
+ requirement: &70304475971620 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70175173061160
46
+ version_requirements: *70304475971620
47
47
  description: Keikoku client
48
48
  email:
49
49
  - harold.gimenez@gmail.com
@@ -104,3 +104,4 @@ test_files:
104
104
  - spec/spec_helper.rb
105
105
  - spec/support/factories.rb
106
106
  - spec/support/fake_keikoku.rb
107
+ has_rdoc: