You need to join this project to post message / question. See Help for details.

Blog

Ruby && and tip

Added by ruby 4 days ago

The following code comes from Puppet source (lib/puppet/type/cron.rb)

1   # Verify that a number is within the specified limits.  Return the
2   # number if it is, or false if it is not.
3   def limitcheck(num, lower, upper)
4     (num >= lower and num <= upper) && num
5   end

Take a look at the comments, and the last statement (&&num). The operator && is not used as a condition check. It is to return the value if the previous checks are passed: If the num is out-of-range, the whole statement returns false. Otherwise, the last value num is returned. This is also how the operator && works :)

The code can be rewritten as

1   def limitcheck(num, lower, upper)
2     if (num >= lower and num <= upper)
3       return num
4     else
5       return false
6     end
7   end

But it is too long, isn't it?

STDOUT: what's wrong with these lines? (1 comment)

Added by ruby 2 months ago  »  Votes: 2/2

The purpose of this portion of code is to print a star every one second

1 while true do
2   STDOUT.write("*")
3   sleep(1)
4 done

Do you think that it really works? and why?

Ruby plus

Added by ruby 5 months ago  »  Votes: 1/1

Let see the differences

1 irb> 65. + (20).chr                # some spaces before and after the operator
2 TypeError: String can't be coerced into Fixnum
3         from (irb):21:in `+'
4         from (irb):21
5         from /usr/bin/irb:12:in '<main>'
6 
7 irb> 65.+(20).chr                  # no space before/after the operator
8 => "U" 

Ruby is fun, isn't it ?

Array in Ruby is ordered

Added by icy 12 months ago  »  Votes: 1/1

1 >> %w{abc xyz} == %w{xyz abc}
2 => false

true and foobar (1 comment)

Added by icy 12 months ago  »  Votes: 2/2

and will return false, or the last object in the expression:

1 true and :foobar         # => :foobar
2 :foobar and true         # => true
3 :foo and :bar and :other # => :other

So, be careful when you're using and in the return value :)

are you.defined? (1 comment)

Added by icy about 1 year ago  »  Votes: 1/1

I was trying to use defined? as a function argument, and I found that I couldn't do that. That's weird. Let's see the first example

1 >> def foobar?; puts "this is a test"; end
2 nil
3 >> define?(foobar?)
4 "method" 
5 >> s = method("foobar?")
6 #<Method: Object#foobar?>
7 >> s.clall
8 this is a test

That's clear. But I can't do a same thing with defined?

1 >> define?(defined?)
2 SyntaxError: compile error
3 (irb):23: syntax error, unexpected ')'
4 >> s = method("defined?")
5 NameError: undefined method `defined?' for class `Object'
6 

Why? Oh, the answer is very simple: defined? isn't a method, it is an operator. I suddendly found the reason after reading this post http://kconrails.com/2010/12/20/rubys-defined-operator/. As an operator, it works like "+", "!", etc. The post also shows that we can't overwrite / redefine it :)

Strange! And fun!

ruby-1.8.7-head: chomp called for nil:NilClass

Added by icy about 1 year ago  »  Votes: 2/2

I am using ruby-1.8.7-head with helps of RVM . This is the first time I've used a head version of ruby. And the first try is often hard :P

$ ruby -e "require 'rubygems'; require 'net/ssh'" 
/home/pi/.rvm/rubies/ruby-1.8.7-head/lib/ruby/1.8/logger.rb:174: private method `chomp' called for nil:NilClass (NoMethodError)
        from /home/pi/.rvm/rubies/ruby-1.8.7-head/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require'
        from /home/pi/.rvm/rubies/ruby-1.8.7-head/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `require'
        from /home/pi/.rvm/gems/ruby-1.8.7-head/gems/net-ssh-2.1.4/lib/net/ssh.rb:5
        from /home/pi/.rvm/rubies/ruby-1.8.7-head/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:58:in `gem_original_require'
        from /home/pi/.rvm/rubies/ruby-1.8.7-head/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:58:in `require'
        from -e:1

There problem comes from logger.rb. Let's take a look at the 174th line of the file:

1 class Logger
2   VERSION = "1.2.6" 
3   id, name, rev = %w$Id$
4   ProgName = "#{name.chomp(",v")}/#{rev}" 

In the official version (logger.rb from ruby-1.8.7 stable version):

1 class Logger
2   VERSION = "1.2.6" 
3   id, name, rev = %w$Id: logger.rb 22285 2009-02-13 10:19:04Z shyouhei $
4   ProgName = "#{name.chomp(",v")}/#{rev}" 

So the author used SVN ID string to set some properties of the class. In HEAD version, such information wasn't substituted, as RVM forked the source from a git repository, not a subversion repository. As git simply ignores $Id$, it makes the head version stupid :))

As a work-around, I've just forced a phantom value for id,name,rev :)

bundler: no such file to load -- set.rb

Added by icy about 1 year ago  »  Votes: 1/1

This is a story.

I am using Bundler 1.0.10

1 $ gem list |grep bundler
2 bundler (1.0.10)

Bundler is required to set up boxrom. Unfortunately, I couldn't get "Rakefile" to work. When loading the boxrom scripts, I got the error "no such file to load -- set.rb"

1 $ rake db:migrate --trace
2 (in /home/pi/projects/boxroom)
3 Cannot load psych in /home/ruby/gems/1.8/gems/rake-0.8.7/bin, /home/ruby/gems/1.8/gems/rake-0.8.7/lib,... .
4 Cannot load set.rb in /home/ruby/gems/1.8/gems/bundler-1.0.10/lib
5 Cannot load /home/pi/projects/boxroom/config/boot in /home/ruby/gems/1.8/gems/bundler-1.0.10/lib
6 Cannot load /home/pi/projects/boxroom/config/application in /home/ruby/gems/1.8/gems/bundler-1.0.10/lib
7 rake aborted!
8 no such file to load -- set.rb

The file set.rb is located in /opt/ruby1.8/lib/ruby/1.8/set.rb; it defines the Set class, and it can be loaded from irb or ruby:

1 $ ruby -e "require 'set'; puts Set.class" 
2 Class

So what's problem that made the file unloadable by bundler? As you can see the logs, when bundler was trying to load 'set', the $LOAD_PATH was modified. This was really weird.

Cannot load psych in /home/ruby/gems/1.8/gems/rake-0.8.7/bin, /home/ruby/gems/1.8/gems/rake-0.8.7/lib,... .
   # full path
Cannot load set.rb in /home/ruby/gems/1.8/gems/bundler-1.0.10/lib
   # the path was modified

As the $LOAD_PATH was modified, I thought that bundler would do something strange. In the file bundler-1.0.10/lib/bundler.rb I saw that

 1   private
 2 
 3     def configure_gem_home_and_path
 4       if settings[:disable_shared_gems]
 5         ENV['GEM_PATH'] = ''
 6         ENV['GEM_HOME'] = File.expand_path(bundle_path, root)
 7       elsif Gem.dir != bundle_path.to_s
 8         paths = [Gem.dir, Gem.path].flatten.compact.uniq.reject{|p| p.empty? }
 9         ENV["GEM_PATH"] = paths.join(File::PATH_SEPARATOR)
10         ENV["GEM_HOME"] = bundle_path.to_s
11       end
12 
13       FileUtils.mkdir_p bundle_path.to_s
14       Gem.clear_paths
15     end

Uhm, it seems that I forgot to use the option disable_shared_gems. So I tried:

1 $ bundle install  --disable-shared-gems
2 Cannot load psych in /home/ruby/gems/1.8/gems/bundler-1.0.10/bin, ....
3 Cannot load Win32API in /home/ruby/gems/1.8/gems/bundler-1.0.10/lib/bundler/vendor,... .
4 The disable-shared-gem option is no longer available.

Errr... So... the code lies. I updated the script

 1     def configure_gem_home_and_path
 2       #if settings[:disable_shared_gems]
 3       #  ENV['GEM_PATH'] = ''
 4       #  ENV['GEM_HOME'] = File.expand_path(bundle_path, root)
 5       #if Gem.dir != bundle_path.to_s
 6         paths = [Gem.dir, Gem.path].flatten.compact.uniq.reject{|p| p.empty? }
 7         ENV["GEM_PATH"] = paths.join(File::PATH_SEPARATOR)
 8         ENV["GEM_HOME"] = bundle_path.to_s
 9       #end
10 
11       FileUtils.mkdir_p bundle_path.to_s
12       Gem.clear_paths
13     end

and things are working very well now :)

PS: I modified the file "custom_required.rb" to get the messages "Cannot load..."

 1 # File /opt/ruby1.8/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb 
 2 
 3   def require(path) # :doc:
 4     gem_original_require path
 5   rescue LoadError => load_error
 6     if load_error.message =~ /#{Regexp.escape path}\z/ and
 7        spec = Gem.searcher.find(path) then
 8       Gem.activate(spec.name, "= #{spec.version}")
 9       gem_original_require path
10     else
11       STDERR.write("Cannot load #{path} in #{$LOAD_PATH.join(", ")}\n")
12       raise load_error
13     end
14   end

so sánh hai phiên bản

Added by over 1 year ago  »  Votes: 2/2

Không biết có thư viện nào cho việc này không. Thôi đành viết cái hàm đơn giản sau. Hàm sẽ trả về true nếu phiên bản thứ nhất bằng hoặc mới hơn phiên bản thứ hai. Ví dụ,

1  __v1_ge_v2 1.2.0-1 1.1.9-9

sẽ trả về true. Ta bỏ qua việc kiểm tra sự hợp lệ của phiên bản, mà giả định rằng hai phiên bản đều có dạng a.b.c.x hoặc a.b.c-x. Về cách kiểm tra thì hai dòng cuối cùng của định nghĩa hàm gần như là một mẹo :)

 1   # Return true if v1 >= v2
 2   def __v1_ge_v2(v1,v2)
 3     av1 = v1.split(/[\.\-]/).map(&:to_i)
 4     av2 = v2.split(/[\.\-]/).map(&:to_i)
 5     ret = av1.size.times.map {|i| av1[i] > av2[i] ? 2 : (av1[i] == av2[i] ? 1 : 0)}.join()
 6     ii = ret.index("2")
 7     zero = ret.index("0")
 8     zero.nil? or (ii && ii < zero)
 9   end

Mảng: tính tổng và giá trị trung bình

Added by over 1 year ago  »  Votes: 2/2

Cho trước một mảng các số, Ruby không có sẵn hàm để tính tổng, giá trị trung bình của tất cả các phần tử của mảng. Trong nhiều trường hợp, ta sẽ cần viết bổ sung nhanh các hàm sum, average cho lớp mảng. Có thể dùng vòng lặp (for, foreach, while,...) để duyệt qua các phần tử của mảng rồi cộng chúng lại với nhau (theo cách quen thuộc khi học nhập môn các ngôn ngữ lập trình.) Tuy nhiên, Ruby cung cấp hàm inspect rất đơn giản

1 class Array
2   def sum
3     inject(0) {|s,i| s + i}
4   end
5 end

Trong biểu thức |s,i| của ví dụ trên, biến i đại diện cho phần tử của mảng, còn biến s sẽ thay đổi giá trị và sự thay đó sẽ lưu trữ lại cho lần lặp kế tiếp. Giá trị khởi đầu của s có thể cho bằng tham số bổ sung của hàm inject, như ở trên là 0. Kết quả trả về của toàn bộ biểu thứ inject là giá trị s được tính toán sau cùng. Lưu ý rằng, ta đã giả định mảng chỉ gồm các số nguyên. Việc xác nhận tính chất này có thể thay đổi tùy trường hợp, nên không được nêu ra ở đây.

Khi đã có hàm sum thì hàm average thật đơn giản

1   def average
2     size > 0 ? sum / size : 0
3   end

Một ví dụ khác: tính tổng tất cả các số dương trong một mảng số

1 class Array
2   def psum
3     inject(0) {|s,i| i > 0 ? s + i : s}
4   end
5 end

1 2 3 4 Next »

Also available in: Atom