Ruby on Rails - Generating Bit.Ly Style Identifiers

Ruby on Rails - generating bit.ly style identifiers

You are confusing two different things here. A UUID is a universally unique identifier. It has a very high probability of being unique even if millions of them were being created all over the world at the same time. It is generally displayed as a 36 digit string. You can not chop off the first 8 characters and expect it to be unique.

Bitly, tinyurl et-al store links and generate a short code to represent that link. They do not reconstruct the URL from the code they look it up in a data-store and return the corresponding URL. These are not UUIDS.

Without knowing your application it is hard to advise on what method you should use, however you could store whatever you are pointing at in a data-store with a numeric key and then rebase the key to base32 using the 10 digits and 22 lowercase letters, perhaps avoiding the obvious typo problems like 'o' 'i' 'l' etc

EDIT

On further investigation there is a Ruby base32 gem available that implements Douglas Crockford's Base 32 implementation

A 5 character Base32 string can represent over 33 million integers and a 6 digit string over a billion.

Best way to generate bit.ly or twitpic style ids?

Here's an example on I did it in Sinatra.

https://github.com/chrisledet/shorty_url

Are there style checker for Ruby code as JSLint for javascript

There's this tool named rails_best_practices but it's Rails-specific, not general Ruby, and it acts more as a guideline.

bitly raise error exception rspec test

As you can see from the docs - https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/raise-error-matcher - the raise error matcher can take a second parameter of string or regex to match against the exceptions message

expect { whatever }.to raise_error(BitlyError, "ALREADY_A_BITLY_LINK")

Generating Post ID's like Facebook and similar sites

  1. What are some advantages/disadvantages to using either approach?

Using sequential numbers

Advantages: Easy to implement

Disadvantages: Possible vector for attack. See this video for a high-level overview.

Using random numbers

Advantages: Solves the problems outlined in the video re: sequential record attacks

Disadvantages: Since there's only 10 bits of entropy, ID's would have to be much longer if your app grows.

Base 64 (use this instead of hex)

Advantages: 64 bits of entropy means an ID 5 chars long would have 64^5 possible permutations. This allows for comparatively much shorter URLs. Use SecureRandom.urlsafe_base64 for this.

Disadvantages: None, really.


  1. Is there a good industry standard or best practice for generating synthetic ids for concepts like users, groups, posts, etc..

To my knowledge, no. Anything sufficiently random and of sufficient length should be fine. Within your model, you'd want to check if an ID is taken first so you don't have duplicates, but outside of that there's little to worry about.


  1. What's the best way in Ruby/Rails to generate each of these IDs? I know of SecureRandom.hex but that seems to generate a long hash.

Like I said above, I recommend using SecureRandom.urlsafe_base64

Getting a HAML button_to work with twitter bootstrap

If you're trying to get a button tag in HAML you can use the %button tag to generate it.

In order to change the class you would be able to do %button.class

ex.

%button.alert
Test

Would generate

<button class="alert">Test</button>

EDIT:

Just a bit extra, while I'm not experienced with using Bootstrap (I used Foundation), if you're interested in using button_to you're allowed to declare the class, though it won't generate the tag you're looking for so this part is not related to your question.

= button_to 'Example, {:controller => 'your_controller', :action => 'your_action'}, {:class => 'your button class', :method => 'post'}

However this will generate a

<input class="small button" type="submit" value="Example">

Associate different models in the same relation

I would suggest using combination of store_accessor and STI. The store accessors will allow you to add model specific attributes with support for validation. If you use PostgreSQL you can take advantage of GIN/GiST index to search custom fields efficiently.

class User < ActiveRecord::Base
has_many :friends
end

class Friend < ActiveRecord::Base
belongs_to :user

# exclude nil values from uniq check
validates_uniqueness_of :user_id, allow_nil: true

# The friends table should have a text column called
# custom_attrs.
# In Postgres you can use hstore data type. Then
# next line is not required.
store :custom_attrs

# Shared between all friend models
store_accessor [ :shared_interests ]
validates_presence_of :shared_interests

# bit of meta-programming magic to add helper
# associations in User model.
def self.inherited(child_class)
klass = child_class.name
name = klass.pluralize.underscore.to_sym
User.has_many name, -> {where(type: klass)}, class_name: klass
end
end

Define various friend models with model specific attributes.

class BestFriend < Friend
store_accessor [ :favourite_colour ]
validates_presence_of :favourite_colour

def to_s
"This is my best friend whos favourite colour is #{favourite_colour} ..."
end
end

class RegularFriend < Friend
store_accessor [ :address ]
validates_presence_of :address

def to_s
"This is a regular friend who lives at #{address} ..."
end
end

class CloseFriend < Friend
store_accessor [ :car_type ]
validates_presence_of :car_type

def to_s
"This is a close friend who drives a #{car_type} ..."
end
end

Usage

user = User.create!

user.close_friends.create!(
car_type: "Volvo",
shared_interests: "Diving"
)

user.regular_friends.create!(
country: "Norway",
shared_interests: "Diving"
)

user.best_friends.create!(
favourite_colour: "Blue",
shared_interests: "Diving"
)

# throws error
user.best_friends.create!(
shared_interests: "Diving"
)

# returns all friends
user.friends.page(4).per(10)

Reference

Store documentation

PostgreSQL Hstore documentation:

Rails loop: Show and hide element by id

Your problem is scoping: you're not providing a context to document.getElementById("categories");, meaning it just selects the first element on the page with the id 'categories'.

While we're here, id should be unique to a page, so you're better switching anything rendered multiple times to use class.

To use the scope to the click event you can pass the event to the function, which we can use to find which li it is toggling.

So in your html:

<li onclick="showHide(event)">

And your function:

function showHide(e) {
var parentCategory = e.currentTarget
var categoryList = parentCategory.querySelector(".categories"); // << assuming you switch to using classes
if(!categoryList) { return }
if (categoryList.style.display === "none") {
categoryList.style.display = "block";
} else {
categoryList.style.display = "none";
}
}

That way, you're looking for the categories within the clicked li, and you should see this toggle as expected.

You can also slim this down a little using a ternary if you're using es6:

function showHide(e) {
let categoryList = e.currentTarget.querySelector(".categories");
if(!categoryList) { return }

categoryList.style.display = categoryList.style.display === "none" ? "block" : "none"
}

Let me know how you get on, of if you have any questions.



Related Topics



Leave a reply



Submit