"Reflecting" on my self.n00b

S

swille

I'm admittedly quite a novice with programming. I'm sort of playing
around with some ideas, and I'm wondering if what I'm doing is
completely stupid, and if not, is there a better way to do it? Also,
is there any way that I could do some type of type-ish validation on
the data?

Basically, I want to provide what I suppose would be abstract class
(?), which someone else could extend and add attributes if they'd
like. I'd like to be able to look into the class and pick out the
attributes to populate (which is the reason for all of the sp_set &
sp_get stuff). Basically, my thought is to be able to provide a web
interface at some point to populate the needed data of a class, and
then maybe Marshal.dump the object to a database and call it later
using that data as config data essentially. I'm sure I could learn a
ton about this from reading Rails code, but there's so much of it that
I don't know where to look. Output and code follows. Please feel
free to tell me if this is dumb :O)

Output:
Enter Username: name
Enter Url: url
Enter Extra: more
Enter Password: pass
Enter Owner: own
Enter Certificate: cert
name
url
more
pass
own
cert

Code:
<code>
class ServiceProfile
def initialize()
end

def sp_set_url(url)
@url =3D url
end

def sp_get_url()
@url
end

def sp_set_username(username)
@username =3D username
end

def sp_get_username()
@username
end

def sp_set_password(password)
@password =3D password
end

def sp_get_password()
@password
end

def sp_set_certificate(certificate)
@certificate =3D certificate
end

def sp_get_certificate()
@certificate
end

def sp_set_owner(owner)
@owner =3D owner
end

def sp_get_owner()
@owner
end

def load_certificate()
@certificate.read
end

def call_service(&block)
end
end

class MyService < ServiceProfile
def sp_set_extra(extra)
@extra =3D extra
end

def sp_get_extra()
@extra
end
end

svc_profile =3D MyService.new

set_re =3D Regexp.new('^sp_set_(\w+)')
get_re =3D Regexp.new('^sp_get_(\w+)')

svc_profile.methods.sort.reverse.each { |meth|
if meth.match(set_re)
print "Enter #{Regexp.last_match(1).capitalize}: "
value =3D $stdin.gets.chomp
svc_profile.send(meth, value)
end
}

svc_profile.methods.sort.reverse.each { |meth|
if meth.match(get_re)
puts svc_profile.send(meth)
end
}
</code>
 
G

Gregory Brown

here are a few suggestions:

instead of:

def sp_set_url(url)
@url = url
end

def sp_get_url()
@url
end

def sp_set_username(username)
@username = username
end

def sp_get_username()
@username
end

def sp_set_password(password)
@password = password
end

def sp_get_password()
@password
end

def sp_set_certificate(certificate)
@certificate = certificate
end

def sp_get_certificate()
@certificate
end

def sp_set_owner(owner)
@owner = owner
end

def sp_get_owner()
@owner
end
--
try:
--
def initialize()
@url, @username, @password, @certificate, @owner = nil
end
attr_accessor :url, :username, :password, :certificate, :eek:wner

You might also store these values in a Hash or use OpenStruct


For the validation part, you might check out HighLine:

http://highline.rubyforge.org
 
G

Gregory Brown

For a little more clarification on usage:

irb(main):027:0> class Foo
irb(main):028:1> def initialize()
irb(main):029:2> @url, @username, @password, @certificate, @owner = nil
irb(main):030:2> end
irb(main):031:1> attr_accessor :url, :username, :password,
:certificate, :eek:wner
irb(main):032:1> end
=> nil
irb(main):033:0> a = Foo.new
=> #<Foo:0xb7d3715c @certificate=nil, @password=nil, @username=nil,
@owner=nil, @url=nil>
irb(main):034:0> a.url = "http://newhavenrubyists.org"
=> "http://newhavenrubyists.org"

and if you need to extend something

irb(main):036:0> class Bar < Foo
irb(main):037:1> end
=> nil
irb(main):038:0> b = Bar.new
=> #<Bar:0xb7d82634 @certificate=nil, @password=nil, @username=nil,
@owner=nil, @url=nil>
irb(main):039:0> b.url = "http://stonecode.org/blog"
=> "http://stonecode.org/blog"
 
S

swille

For a little more clarification on usage:

irb(main):027:0> class Foo
irb(main):028:1> def initialize()
irb(main):029:2> @url, @username, @password, @certificate, @owner =3D nil
irb(main):030:2> end
irb(main):031:1> attr_accessor :url, :username, :password,
:certificate, :eek:wner
irb(main):032:1> end
=3D> nil
irb(main):033:0> a =3D Foo.new
=3D> #<Foo:0xb7d3715c @certificate=3Dnil, @password=3Dnil, @username=3Dni= l,
@owner=3Dnil, @url=3Dnil>
irb(main):034:0> a.url =3D "http://newhavenrubyists.org"
=3D> "http://newhavenrubyists.org"

and if you need to extend something

irb(main):036:0> class Bar < Foo
irb(main):037:1> end
=3D> nil
irb(main):038:0> b =3D Bar.new
=3D> #<Bar:0xb7d82634 @certificate=3Dnil, @password=3Dnil, @username=3Dni= l,
@owner=3Dnil, @url=3Dnil>
irb(main):039:0> b.url =3D "http://stonecode.org/blog"
=3D> "http://stonecode.org/blog"

How would I programatically find which instance variables I'm
concerned with? The naming convention and the methods() call was my
(probably weak) way of being able to find what needed to be populated.
I'm not aware of how to search for instance variables. Can I?
 
S

swille

How would I programatically find which instance variables I'm
concerned with? The naming convention and the methods() call was my
(probably weak) way of being able to find what needed to be populated.
I'm not aware of how to search for instance variables. Can I?

Doh! Nevermind, I just saw instance_variables() in ri. I didn't see
that one before. Thanks!
 
D

dave.burt

You can save yourself some RSI by using attr_accessor.

class ServiceProfile
attr_accessor :username, :url, :password, :eek:wner, :certificate
def load_certificate()
@certificate.read
end
def call
yield self
end
def each_attribute(&block)
(methods - Object.instance_methods).each &block
end
end
class MyService < ServiceProfile
attr_accessor :extra
end

svc_profile = MyService.new
svc_profile.each_attribute do |meth|
print "Enter #{meth}: "
svc_profile.send(meth, gets.chomp)
end
svc_profile.each_attribute {|meth| puts svc_profile.send(meth) }

If you want to use validation, too, you can easily write a method like
attr_accessor. See Dwemthy's Array for how.
http://poignantguide.net/dwemthy/

A type-checking attr_accessor-ish method might look something like
this:

def ServiceProfile.typed_attr(hash)
attr_reader *hash.keys
hash.each_pair do |attr, klass|
define_method("#{attr}=") do |value|
raise TypeError, "not a #{klass}: #{value.inspect}" unless
value.kind_of?(klass)
instance_variable_set "@#{attr}", value
end
end
end

Cheers,
Dave
 
S

swille

For a little more clarification on usage:

irb(main):027:0> class Foo
irb(main):028:1> def initialize()
irb(main):029:2> @url, @username, @password, @certificate, @owner =3D nil
irb(main):030:2> end
irb(main):031:1> attr_accessor :url, :username, :password,
:certificate, :eek:wner
irb(main):032:1> end
=3D> nil
irb(main):033:0> a =3D Foo.new
=3D> #<Foo:0xb7d3715c @certificate=3Dnil, @password=3Dnil, @username=3Dni= l,
@owner=3Dnil, @url=3Dnil>
irb(main):034:0> a.url =3D "http://newhavenrubyists.org"
=3D> "http://newhavenrubyists.org"

and if you need to extend something

irb(main):036:0> class Bar < Foo
irb(main):037:1> end
=3D> nil
irb(main):038:0> b =3D Bar.new
=3D> #<Bar:0xb7d82634 @certificate=3Dnil, @password=3Dnil, @username=3Dni= l,
@owner=3Dnil, @url=3Dnil>
irb(main):039:0> b.url =3D "http://stonecode.org/blog"
=3D> "http://stonecode.org/blog"

This seems to work great, thanks! I swear, this is the first way that
I thought of, but somehow I missed all of the instance_variable calls.
Mental note, look harder. :O)

Still though, I'm unsure of whether this is a smart thing to do. Any
thoughts on that? I'd definitely like to learn the right way to do
stuff.

class ServiceProfile
def initialize
@sp_url =3D nil
@sp_username =3D nil
@sp_password =3D nil
@sp_certificate =3D nil
@sp_owner =3D nil
end

def load_certificate
@sp_certificate.read
end

def call_service(&block)
end
end

class MyService < ServiceProfile
def initialize
@sp_extra =3D nil
super
end
end

svc_profile =3D MyService.new #ServiceProfile.new
sp_re =3D Regexp.new('^@sp_(\w+)')

svc_profile.instance_variables.sort.reverse.each { |var|
if var.match(sp_re)
print "Enter #{Regexp.last_match(1).capitalize}: "
value =3D $stdin.gets.chomp
svc_profile.instance_variable_set(var, value)
end
}

svc_profile.instance_variables.sort.reverse.each { |var|
if var.match(sp_re)
puts svc_profile.instance_variable_get(var)
end
}
 
S

swille

You can save yourself some RSI by using attr_accessor.

class ServiceProfile
attr_accessor :username, :url, :password, :eek:wner, :certificate
def load_certificate()
@certificate.read
end
def call
yield self
end
def each_attribute(&block)
(methods - Object.instance_methods).each &block
end
end

It seems that if I use attr_accessor, I can't use
instance_variables(). Is that the case? If so, how could I peruse
the instance variables to find the ones I need?

Sorry for replying to my own thread so much :O)
 
D

dave.burt

It seems that if I use attr_accessor, I can't use
instance_variables(). Is that the case?

No, it's not - attr_accessor defines methods.
... how could I peruse
the instance variables to find the ones I need?

I added the method "each_attribute" in my previous post - look at that,
and how it's used.

You can also get a list by using something like:
MyService.instance_methods - Object.instance_methods
Sorry for replying to my own thread so much :O)

You should keep replying until your question is answered.

Cheers,
Dave
 
D

David A. Black

Hi --

Still though, I'm unsure of whether this is a smart thing to do. Any
thoughts on that? I'd definitely like to learn the right way to do
stuff.

class ServiceProfile
def initialize
@sp_url = nil
@sp_username = nil
@sp_password = nil
@sp_certificate = nil
@sp_owner = nil
end

That looks kind of odd. Instance variables default to nil anyway. If
you're just trying to trigger them into existence, then I think the
program design is probably suspect.
def load_certificate
@sp_certificate.read
end

def call_service(&block)
end

What's that method for? (It's not actually doing anything.)
end

class MyService < ServiceProfile
def initialize
@sp_extra = nil
super
end
end

svc_profile = MyService.new #ServiceProfile.new
sp_re = Regexp.new('^@sp_(\w+)')

svc_profile.instance_variables.sort.reverse.each { |var|
if var.match(sp_re)
print "Enter #{Regexp.last_match(1).capitalize}: "
value = $stdin.gets.chomp
svc_profile.instance_variable_set(var, value)
end
}

svc_profile.instance_variables.sort.reverse.each { |var|
if var.match(sp_re)
puts svc_profile.instance_variable_get(var)
end
}

The thing with the instance variable names matching a regex looks very
fragile. I would recommend using real data structures. You could do
something like this:


class ServiceProfile
SP_ITEMS = %w{url username password certificate owner }

def sp_items
self.class::SP_ITEMS
end

def initialize
@sp_hash = {}
end

def save_service(service,value)
@sp_hash[service] = value
end
end

class MyService < ServiceProfile
SP_ITEMS = SP_ITEMS.dup
SP_ITEMS << "extra"
end

And then you could just use the data structures to get and save your
data:

svc_profile = MyService.new
svc_profile.sp_items.each do |item|
print "Enter #{item.capitalize}: "
value = gets.chomp
svc_profile.save_service(item,value)
end


David
 
A

Ara.T.Howard

I'm admittedly quite a novice with programming. I'm sort of playing around
with some ideas, and I'm wondering if what I'm doing is completely stupid,
and if not, is there a better way to do it? Also, is there any way that I
could do some type of type-ish validation on the data?

Basically, I want to provide what I suppose would be abstract class (?),
which someone else could extend and add attributes if they'd like. I'd like
to be able to look into the class and pick out the attributes to populate
(which is the reason for all of the sp_set & sp_get stuff). Basically, my
thought is to be able to provide a web interface at some point to populate
the needed data of a class, and then maybe Marshal.dump the object to a
database and call it later using that data as config data essentially. I'm
sure I could learn a ton about this from reading Rails code, but there's so
much of it that I don't know where to look. Output and code follows.
Please feel free to tell me if this is dumb :O)

i think what you want is traits:

harp:~ > cat a.rb
require 'traits'
require 'readline'

class ServiceProfile
trait 'url', 'type' => String, 'case' => %r|^http://|
trait 'username', 'type' => String
trait 'password', 'type' => String
trait 'certificate', 'munge' => 'to_i', 'case' => Fixnum
trait 'owner', 'type' => String

def inspect
"#{ self.class }( " << klass.reader_traits.map{|t| "#{ t }:#{ send(t).inspect}" }.join(', ') << " )"
end
def input io
klass::writer_traits.each{|t| send t, Readline::readline("#{ t } ")}
end
def klass
self.class
end
end

class MyService < ServiceProfile
trait 'extra', 'munge' => 'to_f', 'ducktypes' => %w( floor ceil )
end

my_service = MyService::new
p my_service

my_service.input STDIN
p my_service


harp:~ > ruby a.rb
MyService( extra:nil, certificate:nil, owner:nil, password:nil, url:nil, username:nil )
extra= 42.0
certificate= 42abcd
owner= me
password= tru5t
url= http://codeforpeople.com/lib/ruby/traits/
username= ahoward
MyService( extra:42.0, certificate:42, owner:"me", password:"tru5t", url:"http://codeforpeople.com/lib/ruby/traits/", username:"ahoward" )

regards.


-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| anything that contradicts experience and logic should be abandoned.
| -- h.h. the 14th dalai lama
===============================================================================
 
S

swille

i think what you want is traits:

harp:~ > cat a.rb
require 'traits'
require 'readline'

class ServiceProfile
trait 'url', 'type' =3D> String, 'case' =3D> %r|^http://|
trait 'username', 'type' =3D> String
trait 'password', 'type' =3D> String
trait 'certificate', 'munge' =3D> 'to_i', 'case' =3D> Fixnum
trait 'owner', 'type' =3D> String

def inspect
"#{ self.class }( " << klass.reader_traits.map{|t| "#{ t }:#{ send=
(t).inspect}" }.join(', ') << " )"
end
def input io
klass::writer_traits.each{|t| send t, Readline::readline("#{ t } "= )}
end
def klass
self.class
end
end

class MyService < ServiceProfile
trait 'extra', 'munge' =3D> 'to_f', 'ducktypes' =3D> %w( floor ceil = )
end

my_service =3D MyService::new
p my_service

my_service.input STDIN
p my_service


harp:~ > ruby a.rb
MyService( extra:nil, certificate:nil, owner:nil, password:nil, url:ni= l, username:nil )
extra=3D 42.0
certificate=3D 42abcd
owner=3D me
password=3D tru5t
url=3D http://codeforpeople.com/lib/ruby/traits/
username=3D ahoward
MyService( extra:42.0, certificate:42, owner:"me", password:"tru5t", u=
rl:"http://codeforpeople.com/lib/ruby/traits/", username:"ahoward" )
regards.


-a
--
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| anything that contradicts experience and logic should be abandoned.
| -- h.h. the 14th dalai lama
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D

Wow, that's really nice. All of these suggestions are great. I
definitely needed the guidance. I'm glad David posted. He really
made me realize how much I was overthinking this. For some reason, I
got stuck on some type of reflection for this. I really like this
traits thing though, and I'd like to explore that a bit. Where can I
get that from? I might have to study the source on that one.
 

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
474,184
Messages
2,570,979
Members
47,579
Latest member
CharaS3188

Latest Threads

Top