Sunday, October 2, 2011

Using Ruby to Solve Problems

This past week I've been working through Learn Ruby the Hard Way. It's really a great resource (as I've mentioned a few times already) and I'm picking up lots of good habits. Here's a little example:

I work full-time doing SEO for an e-commerce site, and a while back we ran into a problem on our site with duplicate content. We needed a simple tool for entering two blocks of text and calculating how similar they were as a raw percentage. We eventually found some tools, but they weren't quite what we needed and weren't that great.

So I got the idea of building something using Ruby to do just this and spoke to my friend Eli about it. I still haven't actually BUILT this little program (and I'm sure it WOULD be little, at least at first) and part of the reason was that I didn't have a great idea of HOW to build it. I assumed I might have one model, say Text, with two textarea fields that could be compared, then spit out a percentage for similarity.

Obviously, I'd be using Ruby to make this calculation and that would be a matter of defining a word, counting instances of words, and then making a comparison between both blocks of text.

I've been doing a lot of work using Ruby to open and edit files, so it's obvious I could do similar operations to open and compare files, then generate a report based on that comparison. Not an elegant solution, since users would want to paste text into fields and compare them, not upload files to compare. But I'm sure it's doable. So that might be my next project.

I kind of tend to bounce around between projects, but I'm learning the entire time, so I don't feel bad about it. That's what these projects are for, right?

I also tend to think of an idea, and immediately think of ways to expand it even before I have the original idea working. For example, I'm already thinking of how this project could be expanded to do other things with text, such as identifying words that are used too frequently, identifying passive voice, identifying and suggesting replacements for adverbs and so on. In this case, I think it's because the original problem seemed to be "How can we identify content that's too similar in terms of SEO?" but I've begun thinking of it as "How can we write better content for the web?"

From my limited experience and understanding of web development, programming and computer science in general, it seems to be a matter of identifying a problem and then finding a solution. Am I far off the mark here?

Tuesday, September 27, 2011

Learn Ruby the Hard way

So I was all amped up and ready to get back to work refactoring Manticore tonight, but I ran across this site last night:

Learning Ruby the Hard Way

I've been meaning to beef up my Ruby skills, and although this site is largely review for me, I think it's GOOD review. I'm definitely getting in the habit of commenting on my code, which is something I've been meaning to do for a while now. I'm already through the first four exercises, so I don't anticipate this taking very long to get through.

When I first started learning with Beginning Ruby, I read through a bunch of Ruby code, but it wasn't very practical, since I had yet to get my filthy mitts on Rails. And now I've been hitting Rails pretty hard for a few months, and while I've used Ruby here and there, I've been feeling like I needed to get better acquainted with it. So here we are!

This is a good resource, and I wish I'd found it (or it had been available) when I got started. It's well written, engaging and doesn't hold your hand too much. The author assumes you can do some basic things, such as researching terms you aren't aware of and figuring out why an error happens when it does.

Monday, September 26, 2011

Updating to Rails 3.1 and Ruby 1.9.2

Yesterday evening I went over to @jqr's house for some Rails help and advice. Early into out discussion it became clear to me (and was probably already clear to Eli) that I should redo Manticore.

Here's where it stands now: A whopping 14 models. 14! Why, Tyler? Why did you do that?

Back when I first started working on this project (in April or May I believe) making things that belong_to a Character, such as Hit Points, Armor Class, Statistics and the like, into separate models made more sense to me because they would be easier to edit. It didn't occur to me that if I only wanted to edit a Character's Hit Points, that I could simply render a form that ONLY had that field on it, and edit the record that way.

So after about 3 hours of talking and eating delicious cinnamon buns, @jqr showed me how to think about the problem in a different way. Rather than having modifiers that are specifically called from Statistics and applied to things like Fortitude Saves, or Attack Rolls, I'll have one method for ALL Modifiers, not a thousand different kinds of modifiers. I'm still rolling this around, trying to get a grip on it, but now that I've begun thinking about Manticore in a different way, I think I'll be able to make much more progress going forward. I already GET how to define methods and call them, as well as how to render partials and views that do a variety of tasks, so I'm most of the way there already, right?

Most of my time tonight was spent trying to get Rails 3.1 and Ruby 1.9.2 installed and cooperating. I was getting a ton of BOGUS errors, about Gems that Rails couldn't find. Turns out you have to install a bunch of crazy stuff for 3.1, like Sass, Sass-Rails, Coffee-Script and so on. WEIRD, right?

One cool thing I noticed: I went ahead and created a scaffold for my Character model, and made statistics into attributes of Character. All the statistics are integers, so when it pops up on the form, you get an automatic drop down that just inputs numbers. I'm guessing this is to get around people entering non-integers, and I bet I can pretty easily limit the scope of these attributes.

I don't think updating to Rails 3.1 and Ruby 1.9.2 was strictly necessary, but this seems like a good time to make a change. Also seems like a good time to get more comfortable (re: comfortable at all) writing and running my own tests, considering that's something I've woefully neglected.

Wednesday, September 21, 2011

Instance variables and local variables in Rails

I've got a lot to learn about local and instance variables. I already knew 'character' meant a local instance of character, but I thought '@character' referred to another model, and not an instance of the Character model. I knew that sometimes if Rails was complaining about an unidentified local variable, putting an @ in front would often fix the problem, but I didn't know why. I think I've got a better handle on this, but I think the time has come to do some serious background reading to try and patch up some of the holes in what I know and what I think I know.

On the other hand, thanks to help from @jqr and a healthy does of trial and error, I've gotten that pesky statistic bonus problem figured out. This is what ended up working:

def total
  fortitude_base + character.statistic.con_modifier + magic + misc
end

And here's the con_modifier definition from my Statistic model:

def con_modifier
  (constitution.to_i - 10) / 2
end


I'm keeping it as .to_i now, as a fix for a case where a player tries to create a Fortitude save before a Constitution score has been entered. The .to_i translates a nil record to 0, so Rails does a temporary calculation (which isn't shown) rather than displaying an error. Not sure if there's a better way to handle this (perhaps simply checking that statistic.constitution.present? before performing any calculations) but it works for now.

More importantly than simply figuring this one instance of how to define logic in one model and then call it in the view of another is understanding HOW it works. I can extrapolate these methods and apply them to other things: skills, attack rolls, armor class, etc, etc. Once again, Manticore proves to be a useful learning tool! SUCCESS!

Sunday, September 18, 2011

Nil can't be coerced into Fixnum in Rails

Question time!

I'm trying to figure out a way to calculate bonuses or penalties from a Character's Statistics, and then apply that method in other Models' views to calculate things like Fortitude saves, attack bonuses and so on. I've been working with a modifier for Constitution and here's what I've got so far.

The logic for calculating a Statistic bonus/penalty is (statistic - 10) / 2. So for a score of 14, we get 2, and for a score of 8, we get -1. Not bad so far!

So I define this method in Statistic controller:

def con_modifier
  (constitution.to_i - 10) / 2
end

And since I want to see it in my Character view, I wire it up in the Show method in Characters controller as such:

@con_modifier = @character.statistic.con_modifier

So I can see it in my Character view (where most of this information is displayed) and I know it's working so far. I've got a Fortitude model for Fortitude saves, and I'd like to use the con_modifier in the logic to calculate a total. How do I call a foreign model correctly this way? I have:

Here's the logic for calculating a total in my Fortitude model right now:

def total
  fortitude_base.to_i + ability.to_i + magic.to_i + misc.to_i
end

And I tried changing it to:

def total
  fortitude_base.to_i + @con_modifier + magic.to_i + misc.to_i
end

But then I get this error:

nil can't be coerced into Fixnum

So obviously it isn't calling up the correct information. I've noticed if I chage @con_modifier to @con_modifier.to_i, it calculates it at 0, because it's nil. Any ideas on how to call the number that actually exists? Do I need to define it in my Fortitudes controller as well, or can I simply define it in the Fortitude model and call it in the view that way?

Tuesday, September 13, 2011

Validating integer ranges in Rails

Taking my friend Matt's advice, I redid the validation and logic for my models in Manticore tonight. Didn't take too long and the new way makes more sense than before.

Here's what I had before:

class Initiative < ActiveRecord::Base   belongs_to :character   validates_presence_of :dex, :misc, :speed   validates_numericality_of :dex, :misc, :speed   def total     dex + misc   end end


And I've changed it to:

class Initiative < ActiveRecord::Base   belongs_to :character   validates_numericality_of :dex, :misc, :speed   validates_inclusion_of :dex, :misc, :in => -10..20, :message => "must be between -10 and 20."
  validates_inclusion_of :speed, :in => 20..200, :message => "must be at least 20."

  def total
    dex.to_i + misc.to_i
  end
end


A pretty minor change, but here's what this is doing. First off, there's no need to validate both for presence_of and numericality_of an object. Validating numericality_of an object will in effect also validate_presence_of that object, since it must be an integer to pass validation.

Secondly, I've added a validates_inclusion_of to ensure that all integers make sense within Dungeons and Dragons rules. For example, :speed refers to how many feet a character can move per round. The vast majority of characters are going to be either 20 or 30, so I set 20 as the base. However, there are ways to get higher speeds, through class benefits, spells, magical items and so on, so I set the upper limit at 200. I don't use this object to make any calculations, but it's nice to have information that makes sense for a Dungeons and Dragons character, since Manticore is a Dungeons and Dragons character database program.

Lastly (and I think this might be a bit of overkill) I added .to_i to my logic, which will ensure all objects are integers before attempting to perform calculations. Am I right about this one? It also sets nil to 0, right? Pretty handy, considering I was wanting a way to set nil to 0 a while back.

I started to bang out a question, but as I was writing I got an idea of how to do what I want to do, so I'll give it a shot before asking for help

Monday, September 12, 2011

Code abstraction and models in Rails

I felt like I had a minor break through tonight, thanks in part to my friend Brent. Abstracting code using partials is something that's made sense to me for a long time. Basically, you're just cleaning up the code of a view by creating another bit of code that's called using a render method.

However, last night as I was writing some partials, I noticed I was loading quite a bit of logic into my view and it felt wrong, somehow. I felt...dirty. I was on the right track when I assumed there was a way to collect this logic into a method, then just call that method in the view, rather than having all the logic piling up, making an untidy mess of my code.

But I didn't quite get how to define a method in a model. Sure, I've defined methods such as:

def foo
  bar
end


But usually I got this code by following a tutorial, or reading through a book and so forth. It was never my code, and I think that was the reason I didn't really understand it. Now that a situation has arisen that I needed to learn how to write logic into a model and then call it in the view, I understand it. Funny how that works, huh?

Also, I don't think I'm quite as done with Manticore as I thought last night. I'm obviously still learning from working on it, and that's what I'm after. I've got some big ideas that are frankly a bit intimidating (user account creation and log-in, differing account types and permissions for dungeon masters and players, to name just two) but what's the best way to get over being intimidated by coding? By getting excited about coding. It's brought me this far, anyway!