ana 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cd8798e8dc1b5f9cafcd02c3c90d65271d432460
4
+ data.tar.gz: a9f1e9fd4335b522314822e09b6c3f518085ad82
5
+ SHA512:
6
+ metadata.gz: 359d326b8bf0b9651cb5b28dafbeb5632e136243196b2bcf11fe9f69448d2d29a385f2718a671b4475cae1fe0e30379eb2252dd941941d8d5e700601be3ab3c8
7
+ data.tar.gz: 34d4741eadf16b74748feeeb2b1ee989fee1f0a643d4e92bb40e0443dea28a0eee9c2968e5795ff8687e1630ac8e1c34f8f36db9d88fb00aa1398b6115660cd3
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ .ruby-version
2
+ .ruby-gemset
3
+ *.gem
4
+ *.rbc
5
+ .bundle
6
+ .config
7
+ .yardoc
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ana.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Juanito Fatas
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # Ana
2
+
3
+ Ana knows a lot of things about RubyGems.
4
+
5
+ Yet another Ruby wrapper for the [RubyGems.org][1] API.
6
+
7
+ ## Installation
8
+
9
+ $ gem install ana
10
+
11
+ ## Usage
12
+
13
+ Type `ana` to get some help:
14
+
15
+ $ ana
16
+
17
+ Type `ana help COMMANDS` to get help for specific command:
18
+
19
+ $ ana help version
20
+
21
+ ### Available commands of Ana
22
+
23
+ #### List infomation of Given Gem
24
+
25
+ ```
26
+ $ ana info rails
27
+ Ruby on Rails is a full-stack web framework optimized for programmer happiness and sustainable productivity. It encourages beautiful code by favoring convention over configuration.
28
+ Has been downloaded for 29911559 times!
29
+ The Latest version is 4.0.1.
30
+ Respectful Author(s): David Heinemeier Hansson.
31
+ LICENSE under MIT
32
+ ```
33
+
34
+ #### List Runtime or Development Dependencies of Given Gem
35
+
36
+ ```bash
37
+ $ ana deps rails runtime
38
+ actionmailer = 4.0.1
39
+ actionpack = 4.0.1
40
+ activerecord = 4.0.1
41
+ activesupport = 4.0.1
42
+ bundler < 2.0, >= 1.3.0
43
+ railties = 4.0.1
44
+ sprockets-rails ~> 2.0.0
45
+ ```
46
+
47
+ #### Get latest version of a Given Gem
48
+
49
+ ```bash
50
+ $ ana lv rails
51
+ Latest version is 4.0.1.
52
+ ```
53
+
54
+
55
+ #### Get latest version of a Given Gem
56
+
57
+ ```bash
58
+ ana vs rails
59
+ Last 10 versions of rails are...
60
+ 2013-11-01 : 4.0.1
61
+ 2013-10-30 : 4.0.1.rc4
62
+ 2013-10-23 : 4.0.1.rc3
63
+ 2013-10-21 : 4.0.1.rc2
64
+ 2013-10-17 : 4.0.1.rc1
65
+ 2013-10-16 : 3.2.15
66
+ 2013-10-11 : 3.2.15.rc3
67
+ 2013-10-04 : 3.2.15.rc2
68
+ 2013-10-03 : 3.2.15.rc1
69
+ 2013-07-22 : 3.2.14
70
+ ```
71
+
72
+ #### Find specific version of a given gem
73
+
74
+ ```bash
75
+ $ ana fv rails 4.1
76
+ This version could not be found.
77
+ ```
78
+
79
+ #### (Exact) Search of a given gem name
80
+
81
+ ```bash
82
+ $ ana s rails
83
+ Latest version is 4.0.1.
84
+ ```
85
+
86
+ #### (Fuzzy) Search of a given gem name
87
+
88
+ ```bash
89
+ $ ana fs rails
90
+
91
+ *** REMOTE GEMS ***
92
+ ...
93
+ zurui-sass-rails (0.0.4)
94
+ ```
95
+
96
+ Via `gem search -r`.
97
+
98
+ #### Open a Gem's URI page
99
+
100
+ Available URI: Project Page, Homepage, Wiki, Documentation, Mailing List,
101
+ Source Code, Bug Tracker.
102
+
103
+ ```bash
104
+ $ ana o rails doc
105
+ will open the documentation of rails in your default browser.
106
+ ```
107
+
108
+ via [launchy][2] gem.
109
+
110
+ #### Download a Gem
111
+
112
+ Available URI: Project Page, Homepage, Wiki, Documentation, Mailing List,
113
+ Source Code, Bug Tracker.
114
+
115
+ ```bash
116
+ $ ana dl rails
117
+ will open the browser and download rails gem.
118
+ ```
119
+
120
+ ## Requirements
121
+
122
+ Ruby >= 2.0
123
+
124
+ ## TODO
125
+
126
+ * Add tests.
127
+ * Download via wget/curl.
128
+ * Download gem and its runtime dependencies in a zip/tar or in batch.
129
+
130
+ ## Contributing
131
+
132
+ 1. Fork it ( http://github.com/<my-github-username>/ana/fork )
133
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
134
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
135
+ 4. Push to the branch (`git push origin my-new-feature`)
136
+ 5. Create new Pull Request
137
+
138
+ [1]: https://rubygems.org/
139
+ [2]: https://github.com/copiousfreetime/launchy
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task default: :test
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.pattern = "test/**/*_test.rb"
8
+ end
data/ana.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ana/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'ana'
8
+ spec.version = Ana::VERSION::STRING
9
+ spec.authors = ['Juanito Fatas']
10
+ spec.email = ['katehuang0320@gmail.com']
11
+ spec.summary = %q{Ana knows a lot about RubyGems.}
12
+ spec.description = %q{Yet another RubyGems.org API wrapper in Ruby.}
13
+ spec.homepage = 'https://github.com/JuanitoFatas/Ana'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.required_ruby_version = '>= 2.0.0'
22
+
23
+ spec.add_dependency 'thor', '~> 0.18.1'
24
+ spec.add_dependency 'launchy', '~> 2.4.2'
25
+
26
+ spec.add_development_dependency 'bundler', '>= 1.4'
27
+ spec.add_development_dependency 'rake', '>= 10.1.0'
28
+ end
data/bin/ana ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require __dir__ + "/../lib/ana"
4
+ Ana::Command.start
data/lib/ana.rb ADDED
@@ -0,0 +1,7 @@
1
+ require_relative "ana/version"
2
+ require_relative "ana/core_ext"
3
+ require_relative "ana/constants"
4
+ require_relative "ana/command"
5
+
6
+ module Ana
7
+ end
@@ -0,0 +1,214 @@
1
+ require 'thor'
2
+ require 'net/http'
3
+ require 'json'
4
+ require 'launchy'
5
+
6
+ module Ana
7
+ class Command < Thor
8
+ include Ana::Scalars
9
+ include Thor::Actions
10
+
11
+ # Command Mapping
12
+ map '-v' => :version
13
+ map '--version' => :version
14
+ # Meta
15
+ map 'info' => :gem_infos
16
+ map 'deps' => :gem_dependencies
17
+ # Version
18
+ map 'lv' => :latest_version
19
+ map 'vs' => :versions
20
+ map 'fv' => :find_version
21
+ # Search
22
+ map 's' => :search
23
+ map 'fs' => :fuzzy_search
24
+ # Open URI
25
+ map 'o' => :open
26
+ map 'dl' => :download
27
+
28
+ # Create an empty diretory at ~/.gemjsons
29
+ # All later queried JSON files will be saved here.
30
+ desc 'init', 'Ana needs to setup some stuff in your home directory.'
31
+ def init
32
+ empty_directory full_path('~/.gemjsons')
33
+ end
34
+
35
+ desc 'version (-v, --version)', 'Display version of Ana.'
36
+ def version
37
+ say "#{Ana::VERSION::STRING}"
38
+ end
39
+
40
+ # Print Given Gem's Short description, download counts, latest version,
41
+ # author(s), and license information.
42
+ desc 'gem_infos (info)', 'Print Gem Informations'
43
+ def gem_infos(gem)
44
+ gem_hash = check_if_gem_exist_and_get_json!(gem, type: 'gems')
45
+ say gem_hash['info']
46
+ say "Has been downloaded for #{colorize(gem_hash['downloads'], RED)} times!"
47
+ say "The Latest version is #{colorize(gem_hash['version'], BLUE)}."
48
+ say "Respectful Author(s): #{colorize(gem_hash['authors'], YELLOW)}."
49
+ if gem_hash['licenses'][0].nil?
50
+ say "NO LICENSE :P"
51
+ else
52
+ say "LICENSE under #{colorize(gem_hash['licenses'][0], MAGENTA)}"
53
+ end
54
+ end
55
+
56
+ # List dependencies of a given Gem.
57
+ # You could pass 'runtime' / 'development' to see different dependencies.
58
+ # type: 'runtime', 'development'
59
+ desc 'gem_dependencies (deps)', 'Print Gem Dependencies (runtime / development).'
60
+ def gem_dependencies(gem, type='runtime')
61
+ gem_hash = check_if_gem_exist_and_get_json!(gem, type: 'gems')
62
+ gem_hash['dependencies'][type].each do |arr|
63
+ puts "#{arr['name'].ljust(20)} #{arr['requirements']}"
64
+ end
65
+ end
66
+
67
+ # Return latest version of given gem.
68
+ desc 'latest_version (v)', 'latest version of a gem.'
69
+ def latest_version(gem)
70
+ gem_hash = check_if_gem_exist_and_get_json!(gem, type: 'gems')
71
+ say("Latest version is #{gem_hash['version']}.", :blue)
72
+ end
73
+
74
+ # List versions of a given Gem, default will only list last 10 versions.
75
+ # You can pass all or a fairly large number to display all versions.
76
+ desc 'versions (vs)', 'List versions of a gem.'
77
+ def versions(gem, count=10)
78
+ gem_hash = check_if_gem_exist_and_get_json!(gem, type: 'versions')
79
+ if count == 'all' || count.to_i > gem_hash.count
80
+ count = gem_hash.count
81
+ end
82
+ say("Last #{count} versions of #{gem} are...")
83
+ [*0..count-1].each do |n|
84
+ say("#{gem_hash[n]['built_at'][0..9]} : #{gem_hash[n]['number']}")
85
+ end
86
+ end
87
+
88
+ # Find if a given version of Gem exists.
89
+ desc 'version exist?', 'Find if a given version exists.'
90
+ def find_version(gem, ver)
91
+ gem_hash = check_if_gem_exist_and_get_json!(gem, type: 'versions')
92
+ versions = gem_hash.collect { |x| x['number'] }
93
+ if versions.include? ver
94
+ gem_infos gem
95
+ else
96
+ print_gem_version_not_found!
97
+ end
98
+ end
99
+
100
+ # Search if a given Gem exists? If exists, return the latest version of it.
101
+ desc 'search (s)', '(Exact) Search for a gem.'
102
+ def search(gem)
103
+ latest_version(gem) if gem_exist?(gem)
104
+ end
105
+
106
+ # Invoke system `gem search -r`
107
+ desc 'fuzzy_search (fs)', 'Fuzzy search for a Gem'
108
+ def fuzzy_search(gem)
109
+ system("gem search -r #{Shellwords.escape gem}")
110
+ end
111
+
112
+ # Open the URIs of a given Gem.
113
+ # Available URI types could be found in Ana::Scalars::URI_TYPE.
114
+ desc 'open (o)', 'Open gem doc directly via open command.'
115
+ def open(gem, open_type='doc')
116
+ gem_hash = check_if_gem_exist_and_get_json!(gem, type: 'gems')
117
+ url = if URI_TYPE.keys.include? open_type
118
+ skip = false
119
+ gem_hash[URI_TYPE[open_type]]
120
+ else
121
+ say "#{open_type} is not a valid type :( \n"
122
+ print_valid_uri_open_types!
123
+ skip = true
124
+ gem_hash[URI_TYPE['doc']]
125
+ end
126
+ Launchy.open(url) if url.present? && skip == false
127
+ end
128
+
129
+ # Download a given Gem.
130
+ desc 'download (dl)', 'Download a Gem'
131
+ def download(gem)
132
+ open gem, 'lib'
133
+ end
134
+
135
+ private
136
+
137
+ # Add color to terminal text.
138
+ # Available colors could be found in Ana::Scalars.
139
+ # \033 is 100% POSIX compatible. Use \e is also fine.
140
+ def colorize(text, color_code)
141
+ "\033[#{color_code}m#{text}\033[0m"
142
+ end
143
+
144
+ # Print all valid URI types.
145
+ def print_valid_uri_open_types!
146
+ say "Available types are: "
147
+ URI_TYPE.keys.each do |key|
148
+ next if key == 'lib'
149
+ print "#{key} "
150
+ end
151
+ print "\n"
152
+ end
153
+
154
+ # Check if a Gem exists, if exists load the json and return.
155
+ # The real load will execute every TTL (900) seconds.
156
+ # type: 'versions', 'gems'
157
+ # @return [Hash]
158
+ def check_if_gem_exist_and_get_json!(gem, type: 'versions')
159
+ if gem_exist?(gem)
160
+ gem_uri = URI("https://rubygems.org/api/v1/#{type}/#{gem}.json")
161
+ gem_json_file_path = full_path("~/.gemjsons/#{gem}/#{type}.json")
162
+ unless File.exist?(gem_json_file_path) && fresh?(gem_json_file_path)
163
+ remove_and_create_file!(gem_json_file_path, Net::HTTP.get(gem_uri))
164
+ end
165
+ else
166
+ print_gem_not_found!
167
+ end
168
+ return JSON.parse(IO.read(gem_json_file_path))
169
+ end
170
+
171
+ # Check if a given gem exists?
172
+ def gem_exist?(gem)
173
+ uri = URI "https://rubygems.org/api/v1/gems/#{gem}.json"
174
+ return false if GEM_NOT_FOUND == Net::HTTP.get(uri)
175
+ return true
176
+ end
177
+
178
+ # Print Gem version not found message.
179
+ def print_gem_version_not_found!
180
+ say(GEM_VER_NOT_FOUND, :red)
181
+ end
182
+
183
+ # Print Gem not found message.
184
+ def print_gem_not_found!
185
+ say(GEM_NOT_FOUND, :red)
186
+ end
187
+
188
+ # Remove and create (update) a file.
189
+ def remove_and_create_file!(file_path, data)
190
+ if File.exist? file_path
191
+ remove_file(file_path, verbose: false)
192
+ create_file(file_path, data, verbose: false)
193
+ say_status(:update, file_path)
194
+ else
195
+ create_file(file_path, data)
196
+ end
197
+ end
198
+
199
+ # Expand the full path of given relative path.
200
+ def full_path(relative_path)
201
+ File.expand_path(relative_path)
202
+ end
203
+
204
+ # Check if a file is has been changed within 900s?
205
+ def fresh?(file_path)
206
+ access_time(file_path) < TTL
207
+ end
208
+
209
+ # Return a file's change time with respect to current time.
210
+ def access_time(file_path)
211
+ Time.now - File.ctime(file_path)
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,24 @@
1
+ module Ana
2
+ module Scalars
3
+ TTL = 900 # Every 900s updates the json.
4
+ GEM_NOT_FOUND = 'This rubygem could not be found.'
5
+ GEM_VER_NOT_FOUND = 'This version could not be found.'
6
+
7
+ # Terminal Colour Codes
8
+ # http://en.wikipedia.org/wiki/ANSI_escape_code
9
+ BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = [*30..37]
10
+
11
+ # $ curl https://rubygems.org/api/v1/gems/rails.json
12
+ # uris of above json
13
+ URI_TYPE = {
14
+ 'proj' => 'project_uri',
15
+ 'lib' => 'gem_uri',
16
+ 'home' => 'homepage_uri',
17
+ 'wiki' => 'wiki_uri',
18
+ 'doc' => 'documentation_uri',
19
+ 'mail' => 'mailing_list_uri',
20
+ 'src' => 'source_code_uri',
21
+ 'bug' => 'bug_tracker_uri',
22
+ }
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ class String
2
+ def present?
3
+ !blank?
4
+ end
5
+
6
+ # A string is blank if it's empty or contains whitespaces only:
7
+ #
8
+ # ''.blank? # => true
9
+ # ' '.blank? # => true
10
+ # ' '.blank? # => true
11
+ # ' something here '.blank? # => false
12
+ def blank?
13
+ self =~ /\A[[:space:]]*\z/
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ module Ana
2
+ module VERSION
3
+ MAJOR = 0
4
+ MINOR = 9
5
+ TINY = 0
6
+ PRE = nil
7
+
8
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ require 'test_helper'
2
+
3
+ class AnaTest < Minitest::Unit::TestCase
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'bundler/setup'
2
+ Bundler.require(:default)
3
+ require 'minitest/autorun'
4
+ require 'minitest/pride'
5
+ require_relative '../lib/ana'
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ana
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Juanito Fatas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.18.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.18.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: launchy
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 2.4.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 2.4.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '1.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '1.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 10.1.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: 10.1.0
69
+ description: Yet another RubyGems.org API wrapper in Ruby.
70
+ email:
71
+ - katehuang0320@gmail.com
72
+ executables:
73
+ - ana
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - .gitignore
78
+ - Gemfile
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - ana.gemspec
83
+ - bin/ana
84
+ - lib/ana.rb
85
+ - lib/ana/command.rb
86
+ - lib/ana/constants.rb
87
+ - lib/ana/core_ext.rb
88
+ - lib/ana/version.rb
89
+ - test/ana/ana_test.rb
90
+ - test/test_helper.rb
91
+ homepage: https://github.com/JuanitoFatas/Ana
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: 2.0.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.1.11
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Ana knows a lot about RubyGems.
115
+ test_files:
116
+ - test/ana/ana_test.rb
117
+ - test/test_helper.rb