The real Ruby vs. Python.

  • Thread starter Abe Vionas_MailingList
  • Start date
V

vruz

First of all, this email is written by me, Horacio López and doesn't
necessarily reflect the opinions of the RPA team.
We added it because we wanted the user to have the choice to remove a
package even if it might screw something up. I see it as being in the
same vein as allowing a programmer to pass a String into a method that may
usually expect something else.

I see these comments in the same vein as comparing oranges to apples.
"Here's some rope...you can use it to tie
knots, secure things, etc. or you can hang yourself with it.

it could read more like:
"here's a nice tool, you can use it to run production installations
without breaking stuff, streamlining the process and behaving in a
predictable manner... or you can use your former package management
system of choice"
We trust that you'll make the right decision, because we don't think you're an
idiot."

it could read more like:
"we ease the job because our focus is quality, and not everyone is
willing or have time to spend building decent packages that work
together well"

We don't call people "idiots".

Some people believe just a little policy is good for production
environments, nobody is going to die because of it, and you can
happily continue to design another system that has another policy (if
any) that suits your needs better.

I would still like to know what's the Rubygems manifesto, set of
guidelines, what's the purpose, big idea, right now, based on your
comments and diverging opinions from other Rubygems developers,
features that appear and disappear in the next release, it's hard to
tell and it feels like a bit of a moving target.

It's way much more difficult to start a decent analysis of a system if
you don't know what it will be like in 10 seconds from now, perhaps
it's not even worth the hassle.

On the other hand, it's very easy to drop FUD about a clearly defined
roadmap, for bad or good.

best,
vruz
 
G

Guillaume Marcais

--=-XN87+79wxRM1KhQCmcRq
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

Besides which: Ruby/DL is often touted as a great way to write extension
libraries. Are there any examples of libraries that have been written
using Ruby/DL instead of C? I've never seen any, much to my own
frustration when I would like a good example of Ruby/DL usage. :(

I wouldn't dare to brag about 'good example', but it at least is a
(mostly) working one. I say mostly as a good number of the settings for
a user are undocumented and I had to guess the return value type.
Anyway, the following code is a binding to the Merak mail server
library. It allowed may to automate the transition from our old mail
server to Merak. It is now a nice way to get the password in clear that
the stupid GUI shows as stars. The creation and basic settings of a
Domain and a User should work. The more exotic settings are untested.

Here is an example of a session using the created merak shell:

$ ssh Administrator@fakedomain irb -r merak_shell
Administrator@fakedomain's password:
Warning: Remote host denied X11 forwarding.
Merak 1:0> toto = Domain["toto.net"]
toto = Domain["toto.net"]
=> #<Domain name=toto.net; index=15>
Merak 2:0> user = toto["toto"]
user = toto["toto"]
=> #<User domain=toto.net index=0>
Merak 3:0> user.password
user.password
=> "coucou"
Merak 4:0> user.password = "hello"
user.password = "hello"
=> "hello"
Merak 5:0> user.save
user.save
=> #<User domain=toto.net index=0>

In short, I would never have written it if I had to drop down to C. One
reason being that I don't have a compiler installed on any Windows
machine yet.

Guillaume.


--=-XN87+79wxRM1KhQCmcRq
Content-Disposition: attachment; filename=merak.rb
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=ISO-8859-1

require 'Win32API'

module Merak
class Error < ScriptError; end
class APIError < Error; end
class Failure < APIError; CODE =3D -1; end
class License < APIError; CODE =3D -2; end
class Params < APIError; CODE =3D -3; end
class Paths < APIError; CODE =3D -4; end
class Config < APIError; CODE =3D -5; end

ERRORS =3D [nil, Failure, License, Params, Paths, Config]

# Call function in the DLL. Return an error object if an error occures.
class APIClass
attr_reader :winapis
DLL =3D 'API.dll'
ARGS =3D {
:Init =3D> ["P"],=20
:GetDomainCount =3D> [],
:GetDomainList =3D> ["P", "L"],
:AddDomain =3D> ["P", "P", "L"],
:DeleteDomain =3D> ["L"],
:GetDomainIndex =3D> ["P"],
:GetDomainName =3D> ["L", "P", "L"],
:LoadDomain =3D> ["L", "P", "L"],
:SaveDomain =3D> ["L", "P", "L"],
:SetDomainDefaults =3D> ["P", "L"],
:GetDomainIP =3D> ["L", "P", "L"],
:SetDomainIP =3D> ["L", "P", "L"],
:GetDomainSetting =3D> ["P", "L", "L", "P", "L"],
:SetDomainSetting =3D> ["P", "L", "L", "P", "L"],
:GetUserCount =3D> ["P"],
:GetUserList =3D> ["P", "P", "L"],
:GetUserIndex =3D> ["P", "P"],
:AddUser =3D> ["P", "P", "L"],
:DeleteUser =3D> ["P", "L"],
:SetUserDefaults =3D> ["P", "L"],
:LoadUser =3D> ["P", "L", "P", "L"],
:SaveUser =3D> ["P", "L", "P", "L"],
:GetUserSetting =3D> ["P", "L", "L", "P", "L"],
:SetUserSetting =3D> ["P", "L", "L", "P", "L"],
}

def initialize; @winapis =3D {}; end

def method_missing(call, *args)
winapi =3D @winapis[call]
if winapi.nil?
api_args =3D ARGS[call]
raise Error, "Unknown API call '#{call}'" if api_args.nil?
winapi =3D @winapis[call] =3D Win32API.new(DLL, call.to_s, api_args=
, "L")
end
param =3D args.last.kind_of?(Hash) ? args.pop : nil
r =3D winapi.call(*args)
if r < 0
msg =3D "Error calling #{call}(#{args.join(", ")})"
pmsg =3D param ? param[("err_%d" % (-r)).intern] : nil
msg <<=3D "\n#{pmsg}" if pmsg
raise ERRORS[-r], msg
end
r
end
end # API
=20
# Different types to store in Merak fields
# Should implement pack, unpack, size and base_size
# The virtual type must be instanciate with a base_size argument
class BoolT
def self.pack(b); [b ? 1 : 0].pack("C"); end
def self.unpack(s); s.unpack("C")[0] !=3D 0; end
def self.size(s); 1; end
def self.base_size; 1; end
end
=20
class NumT
def self.pack(n); [n].pack("L"); end
def self.unpack(s); s.unpack("L")[0]; end
def self.size(s); 4; end
def self.base_size; 4; end
end
=20
class TimeT
def self.pack(n); nil; end
def self.unpack(s); nil; end
def self.size(s); 8; end
def self.base_size; 8; end
end
=20
class StringTVirtual
attr_accessor :base_size
def initialize(base_size); @base_size =3D base_size; end
def pack(s); s; end
def unpack(s); s; end
def size(s); s.size; end
end
=20
class ListTVirtual
attr_accessor :base_size
def initialize(base_size); @base_size =3D base_size; end
def pack(l); l.kind_of?(Array) ? l.join('; ') : l; end
def unpack(s); s.split(/[,;]/).collect { |x| x.strip }; end
def size(s); s.size; end
end

class TypeTVirtual
attr_reader :category
def initialize(category); @category =3D category; end
def pack(t); [@category.index(t)].pack("L"); end
def unpack(s); @category[s.unpack("L")[0]]; s.unpack("L")[0]; end
def size(s); 4; end
def base_size; 4; end
end

# Actual type of virtual definition
String32T =3D StringTVirtual.new(32)
String128T =3D StringTVirtual.new(128)
String1024T =3D StringTVirtual.new(1024)
List128T =3D ListTVirtual.new(128)
List1024T =3D ListTVirtual.new(1024)
=20
# Get/Set settings for object
module Settings
class << self
alias :__append_features :append_features
def append_features(mod)
__append_features(mod)
mod.const_get:)SETTINGS_DESC).each_key do |k|
mod.module_eval("def #{k}; get_setting:)#{k}); end")
mod.module_eval("def #{k}=3D(val); set_setting:)#{k}, val); end")
end
end
end

def get_setting(setting, b_size =3D nil)
s_desc =3D settings_desc[setting]
raise Error, "Unknown setting '#{setting}'" if s_desc.nil?
get_setting =3D settings_calls[:get_setting]
=20
args =3D [@buffer, @buffer.size, s_desc[:val], :buffer, :size]
r, buffer, args =3D call_retry(get_setting, s_desc[:type].base_size, =
args)
return s_desc[:type].unpack(buffer[0, r])
end
private :get_setting

def set_setting(setting, value)
s_desc =3D settings_desc[setting]
raise Error, "Unknown setting '#{setting}'" if s_desc.nil?
set_setting =3D settings_calls[:set_setting]

s_buf =3D s_desc[:type].pack(value)
r =3D API.send(set_setting, @buffer, @buffer.size, s_desc[:val],
s_buf, s_buf.size)
return value
end
private :set_setting
end # module Settings

module Utils
# Call an API methods, max attempts time, increasing the size of a buff=
er
# The buffer is located at :buffer.
# Return: [result, buffer, args]
def call_retry(call, size_base, args, attempts =3D 5)
b_index =3D args.index:)buffer)
s_index =3D args.index:)size)
return nil if b_index.nil?
=20
i =3D 1
begin
args[b_index] =3D buffer =3D " " * (i * size_base)
args[s_index] =3D buffer.size if s_index
r =3D API.__send__(call, *args)
rescue Params =3D> e
i +=3D 1
raise e if i > attempts
retry
end
=20
return [r, buffer, args]
end
module_function :call_retry
end

class Domain
include Utils

class << self
private :new
=20
def names
count =3D API.GetDomainCount
base =3D count * 50
r, buf, args =3D Utils.call_retry:)GetDomainList, base, [:buffer, :=
size])
buf[0, r].split("\0")
end

# Get by index or name
def get(ident)
case ident
when Integer
begin
r, name, a =3D Utils.call_retry:)GetDomainName, 50,=20
[ident, :buffer, :size])
name =3D name[0, r]
rescue Params
raise Error, "No such domain with index '#{ident}'"
end
index =3D ident
when String
index =3D API.GetDomainIndex(ident,=20
:err_1 =3D> "No such domain '#{ident}'=
")
name =3D ident
end
=20
res =3D new(index, nil, name)
res.reload
res
end=0D
alias :[] :get

def create(name)
r, buffer, args =3D Utils.call_retry:)SetDomainDefaults, 4192,=20
[:buffer, :size])
API.AddDomain(name, buffer, buffer.size)
index =3D API.GetDomainIndex(name)
new(index, buffer, name)
end

def delete(ident); get(ident).delete; end
end # class << Domain
=20
attr_reader :index
def initialize(index, buffer =3D nil, name =3D nil)
@index, @buffer, @name =3D index, buffer, name
end
=20
# Returns Virtual Binding IP, or nil if none
def ip
ip =3D " " * 16
r =3D API.GetDomainIP(@index, ip, ip.size)
return (r =3D=3D 0) ? nil : ip[0, r]
end

def ip=3D(ip)
ip ||=3D ""
API.SetDomainIP(@index, ip, ip.size,
:err_1 =3D> "Bad IP '#{ip}'")
ip
end

def save
return true unless @buffer
API.SaveDomain(@index, @buffer, @buffer.size)
self
end

def delete; API.DeleteDomain(@index); self; end

def name
return @name if @name
r, name, args =3D call_retry:)GetDomainName, 50, [@index, :buffer, :s=
ize])
@name =3D name[0, r]
@name
end

def inspect; "#<Domain name=3D#{name}; index=3D#{@index}>"; end

SETTINGS_DESC =3D {
:description =3D> { :val =3D> 0, :type =3D> String1024T },
:type =3D> { :val =3D> 1, :type =3D> NumT },
:domain_value =3D> { :val =3D> 3, :type =3D> String1024T },
:postmaster =3D> { :val =3D> 4, :type =3D> List1024T },
:admin_forward =3D> { :val =3D> 5, :type =3D> String1024T },
:unknown_users_forward =3D> { :val =3D> 6, :type =3D> BoolT },
:unknown_forward_to =3D> { :val =3D> 7, :type =3D> List1024T },
:info_to_admin =3D> { :val =3D> 8, :type =3D> BoolT },
}
def settings_desc; SETTINGS_DESC; end
private :settings_desc
SETTINGS_CALLS =3D {=20
:get_setting =3D> :GetDomainSetting, :set_setting =3D> :SetDomainSett=
ing=20
}
def settings_calls; SETTINGS_CALLS; end
private :settings_calls
include Settings
=20
def reload
args =3D [@index, :buffer, :size]
r, buffer, args =3D call_retry:)LoadDomain, 4192, args)
@buffer =3D buffer
r
end

# User management
def users
count =3D API.GetUserCount(@name)
base =3D count * 50
r, buf, args =3D call_retry:)GetUserList, base, [@name, :buffer, :siz=
e])
buf[0, r].split("\0")
end

def get_user(ident)
case ident
when Integer
index =3D ident
when String
index =3D API.GetUserIndex(@name, ident)
end

r, buf, args =3D call_retry:)LoadUser, 4192,=20
[@name, index, :buffer, :size])
User.new(self, index, buf)
end=0D
alias :[] :get_user

def create_user(*aliases)
r, buffer, args =3D Utils.call_retry:)SetUserDefaults, 4192,=20
[:buffer, :size])
u =3D User.new(self, nil, buffer)
u.alias =3D aliases unless aliases.empty?
u
end
alias :add_user :create_user
end # Domain

class User
include Utils
=0D
attr_reader :index, :buffer, :domain
def initialize(domain, index, buf)
@index, @buffer, @domain =3D index, buf, domain
end

def inspect
"#<User domain=3D#{@domain ? @domain.name : @domain_index} index=3D#{=
@index}>"
end

# class POP3; end
# class IMAP; end
# class IMAPandPOP3; end
# UserTypeT =3D TypeTVirtual.new([POP3, IMAP, IMAPandPOP3])
SETTINGS_DESC =3D {
:type =3D> { :val =3D> 0, :type =3D> NumT },
:anti_spam_index =3D> { :val =3D> 1, :type =3D> NumT },
:name =3D> { :val =3D> 2, :type =3D> String32T },
:alias =3D> { :val =3D> 3, :type =3D> List128T },
:mailbox =3D> { :val =3D> 16, :type =3D> String32T },
:account_disabled =3D> { :val =3D> 17, :type =3D> BoolT },
:account_valid =3D> { :val =3D> 18, :type =3D> BoolT },
:account_valid_till =3D> { :val =3D> 19, :type =3D> TimeT },
:check_virus =3D> { :val =3D> 20, :type =3D> BoolT },
:allow_remote =3D> { :val =3D> 21, :type =3D> BoolT },
:validity_report =3D> { :val =3D> 22, :type =3D> BoolT },
:validity_report_days =3D> { :val =3D> 23, :type =3D> NumT },
:nt_Password =3D> { :val =3D> 24, :type =3D> BoolT },
:imap =3D> { :val =3D> 25, :type =3D> BoolT },
:imap_mailbox =3D> { :val =3D> 26, :type =3D> String32T },
:max_message_size =3D> { :val =3D> 27, :type =3D> NumT },
:dont_show_messages =3D> { :val =3D> 28, :type =3D> BoolT },
:any_password =3D> { :val =3D> 29, :type =3D> BoolT },
:etrn =3D> { :val =3D> 30, :type =3D> BoolT },
:delete_expire =3D> { :val =3D> 31, :type =3D> BoolT },
:null =3D> { :val =3D> 32, :type =3D> BoolT },
:password =3D> { :val =3D> 33, :type =3D> String32T },
:nt_password_value =3D> { :val =3D> 34, :type =3D> String32T },
:domain_admin_index =3D> { :val =3D> 35, :type =3D> NumT },
:domain_admin =3D> { :val =3D> 36, :type =3D> BoolT },
:mail_box_path =3D> { :val =3D> 37, :type =3D> String128T },
:admin =3D> { :val =3D> 38, :type =3D> BoolT },
:max_box =3D> { :val =3D> 39, :type =3D> NumT },
:max_box_size =3D> { :val =3D> 40, :type =3D> NumT },
:force_from =3D> { :val =3D> 41, :type =3D> String32T },
:respond =3D> { :val =3D> 42, :type =3D> NumT },
:eek:nly_local_domain =3D> { :val =3D> 43, :type =3D> BoolT },
:use_remote_address =3D> { :val =3D> 44, :type =3D> String32T },
:forward_to =3D> { :val =3D> 45, :type =3D> String32T },
:respond_with =3D> { :val =3D> 46, :type =3D> String1024T },
:mail_in =3D> { :val =3D> 47, :type =3D> String32T },
:mail_out =3D> { :val =3D> 48, :type =3D> String32T },
:valid_report =3D> { :val =3D> 49, :type =3D> BoolT },
:delete_older =3D> { :val =3D> 50, :type =3D> BoolT },
:delete_older_days =3D> { :val =3D> 51, :type =3D> NumT },
:forward_older =3D> { :val =3D> 52, :type =3D> BoolT },
:forward_older_days =3D> { :val =3D> 53, :type =3D> NumT },
:forward_older_to =3D> { :val =3D> 54, :type =3D> String32T },
:remote_address =3D> { :val =3D> 55, :type =3D> String32T },
:force_from_address =3D> { :val =3D> 56, :type =3D> BoolT },
:megabyte_send_limit =3D> { :val =3D> 57, :type =3D> NumT },
:number_send_limit =3D> { :val =3D> 58, :type =3D> NumT },
}
def settings_desc; SETTINGS_DESC; end
private :settings_desc
SETTINGS_CALLS =3D {
:get_setting =3D> :GetUserSetting, :set_setting =3D> :SetUserSetting=20
}
def settings_calls; SETTINGS_CALLS; end
private :settings_calls
include Settings

def save
return true unless @buffer
if @index
API.SaveUser(@domain.name, @index, @buffer, @buffer.size)
else
r =3D API.AddUser(@domain.name, @buffer, @buffer.size)
@index =3D r
end
self
end
=20
def delete; API.DeleteUser(@domain.name, @index); end
end # User
=20
def self.init(path)
const_set("API", APIClass.new)
API.Init(path)
end
end


--=-XN87+79wxRM1KhQCmcRq
Content-Disposition: attachment; filename=merak_shell.rb
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=ISO-8859-1

prompt =3D "Merak %3n:%i"
IRB.conf[:pROMPT][:Merak_Prompt] =3D {
:pROMPT_I =3D> "#{prompt}> ",
:pROMPT_C =3D> "#{prompt}* ",
:pROMPT_S =3D> "#{prompt}* ",
:RETURN =3D> "=3D> %s\n",
}
IRB.conf[:pROMPT_MODE] =3D :Merak_Prompt

require 'merak'
include Merak
Merak.init('C:\Program Files\Merak')

--=-XN87+79wxRM1KhQCmcRq
Content-Disposition: attachment; filename="Merak Shell.bat"
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=ISO-8859-1

@echo OFF=0D
cd \cygwin\home\ADMINI~1=0D
irb -r merak_shell=0D

--=-XN87+79wxRM1KhQCmcRq--
 
E

Eivind Eklund

An example of something that I believe is
broken from an end-user perspective in rpa-base is that when I remove a
package, it removes all of the dependencies. We _don't_ want to do that
with RubyGems. I have Rails installed, and I've written scripts to use
ActiveRecord, which was installed with Rails. If I decide I don't want
Rails anymore, I don't want to have to go manually install ActiveRecord
afterward.

Heh. This seems to be a preference issue: I strongly prefer that
dependencies are removed. This is especially important when libraries
are used for application support. I see extra dependencies being left
around as a significant problem when my use is scaled a little bit.

As an example, on one of my FreeBSD systems where I've experiemented
with different software packages and dependencies are not
automatically removed, I presently have 399 software packages
installed. I guess over 100 of these are presently unused
dependencies. These complicate my enviornment, but removing them is
work, and risk breaking things that have added implict dependencies on
them.

In my ideal world (and the design I originally described for RPA), the
dependencies are *hidden* from the user until they are explictly
requested. Thus, your case above cannot happen.
As for tracking reverse dependencies, it doesn't seem difficult to me:

spec.dependent_gems

The manual user intervention you were talking about is not _necessary_.

I don't get this. Do you mean there is a way to deinstall the
dependencies that the install of a gem auto-installed without manual
intervention? If so, how? I couldn't find it in the documentation -
I'll add it in the places I looked if there is a way.
We added it because we wanted the user to have the choice to remove a
package even if it might screw something up.

I don't see how this relate to the above - may we be talking past each other?
I see it as being in the
same vein as allowing a programmer to pass a String into a method that may
usually expect something else. "Here's some rope...you can use it to tie
knots, secure things, etc. or you can hang yourself with it. We trust
that you'll make the right decision, because we don't think you're an
idiot."

I'm an idiot, so I tend to presume that there are others out there, too :)

I work day-to-day with a package system (FreeBSD's) that do track
reverse dependencies and prevent dropping packages that has a
dependency, but do allow a force switch to override it.

I regularly try to de-install libraries that are in use. Roughly 70%
of those attempted de-installs are by accident, while 30% are ones
that I want to go through with anyway. So I definately appreciate
being caught by the safety-net.

Eivind.
 
M

Mauricio Fernández

when a package is installed - do you store dags of the dependancies
somewhere
then? i guess it would need to be a single dag (at least logically) so you
could detect problems on uninstall huh?

It is easy to rebuild the graph on uninstall, based on the stored
dependency information. rpa-base also has a mark and sweep garbage
collector (like Ruby :) to reclaim unneeded packages. It is very useful
for "build dependencies", but that concept doesn't exist in RubyGems so
they're not in dire need of that.
 
C

Chad Fowler

On Fri, 29 Oct 2004, Bill Guindon wrote:

#
# > We trust that you'll make the right decision,
# > because we don't think you're an idiot."
#
# ... unlike Mauricio (and the rest of the RPA team).
#
# That last part didn't add much to the discussion, but it does add to
# the belief that the two teams are antagonistic.
#
# Perhaps I just read it the wrong way.
#

Yea, either that or I wrote it in the wrong way. I was expressing the
philosophy of RubyGems--not implying that the opposite was true of any
other specific developer.

Chad
 
D

David Ross

Curt said:
David Ross wrote:



I am a big proponent of being cross-platform *and* making things as easy as
possible for end users to install and use. So, this is not intended to start
any kind of competition or flame-war, mostly I'm just curious...

When I see the statement "most of us are unix users, there are some windows
users" it makes me wonder whether or not this is really true. I'm involved
in two major cross-platform Ruby projects: FreeRIDE and wxRuby. In both
cases the windows downloads are higher than all the other platforms
combined.

It would be interesting if there was a more reliable way to gauge Ruby's use
on various platforms, but I don't know how that could be done.

Curt
mmm... Perhaps we can have a long list to a wiki, I'd like to get an
actual headcount. The Ruby Window QA team is all setup. I'm curious if
there are more people who could possibly donate some time or hardware
for the progress of Windows QA support.

David Ross
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,782
Latest member
ThomasGex

Latest Threads

Top