Skip to content

Instantly share code, notes, and snippets.

@assembler
Created October 13, 2014 11:06
Show Gist options
  • Save assembler/6ebafc69d1b92148381a to your computer and use it in GitHub Desktop.
Save assembler/6ebafc69d1b92148381a to your computer and use it in GitHub Desktop.

Revisions

  1. assembler created this gist Oct 13, 2014.
    283 changes: 283 additions & 0 deletions monads.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,283 @@
    # https://www.youtube.com/watch?v=uTR__8RvgvM
    # ---------------------------------------------------------------------------- #

    Project = Struct.new(:creator)
    Person = Struct.new(:address)
    Address = Struct.new(:country)
    Country = Struct.new(:capital)
    City = Struct.new(:weather)

    class Optional
    attr_accessor :value

    def initialize(value)
    @value = value
    end

    def self.from_value(value)
    new(value)
    end

    def and_then(&block)
    if value.nil?
    Optional.new(nil)
    else
    block.call(value)
    end
    end

    def method_missing(*args, &block)
    and_then do |value|
    Optional.new(value.public_send(*args, &block))
    end
    end
    end

    def get_project_weather(project)
    # Optional.new(project)
    # .and_then { |project| Optional.new(project.creator) }
    # .and_then { |creator| Optional.new(creator.address) }
    # .and_then { |address| Optional.new(address.country) }
    # .and_then { |country| Optional.new(country.capital) }
    # .and_then { |capital| Optional.new(capital.weather) }
    # .value
    Optional.new(project).creator.address.country.capital.weather.value
    end


    project = Project.new(Person.new(Address.new(Country.new(City.new("sunny")))))
    p get_project_weather(project)

    project = Project.new(Person.new)
    p get_project_weather(project)


    # ---------------------------------------------------------------------------- #

    Blog = Struct.new(:categories)
    Category = Struct.new(:posts)
    Post = Struct.new(:comments)

    class Many
    attr_accessor :values

    def initialize(values)
    @values = values
    end

    def self.from_value(value)
    new([value])
    end

    def and_then(&block)
    Many.new(values.map(&block).flat_map(&:values))
    end

    def method_missing(*args, &block)
    and_then do |value|
    Many.new(value.public_send(*args, &block))
    end
    end
    end

    def words_in(blogs)
    # Many.new(blogs)
    # .and_then { |blog| Many.new(blog.categories) }
    # .and_then { |category| Many.new(category.posts) }
    # .and_then { |post| Many.new(post.comments) }
    # .and_then { |comment| Many.new(comment.split(/\s+/)) }
    # .values
    Many.new(blogs).categories.posts.comments.split(/\s+/).values
    end

    blogs = [
    Blog.new([
    Category.new([
    Post.new(['I love cats', 'I love dogs']),
    Post.new(['I love mice', 'I love pigs']),
    ]),
    Category.new([
    Post.new(['I hate cats', 'I hate dogs']),
    Post.new(['I hate mice', 'I hate pigs']),
    ]),
    ]),
    Blog.new([
    Category.new([
    Post.new(['Red is better than blue']),
    ]),
    Category.new([
    Post.new(['Blue is better than red']),
    ]),
    ]),
    ]

    p words_in(blogs)

    # ---------------------------------------------------------------------------- #

    Thread.abort_on_exception = true

    require "rubygems"
    gem "uri_template"

    require "uri"
    require "net/http"
    require "json"
    require "uri_template"

    def get_json(url, &success)
    Thread.new do
    puts "[GET] #{url}..."

    uri = URI.parse(url)
    json = Net::HTTP.get(uri)
    value = JSON.parse(json)
    success.call(value)
    end
    end

    # get_json("https://api.github.com") do |urls|
    # org_url_template = URITemplate.new(urls["organization_url"])
    # org_url = org_url_template.expand(org: "ruby")
    # get_json(org_url) do |org|
    # repos_url = org["repos_url"]

    # get_json(repos_url) do |repos|
    # most_popular_repo = repos.max_by { |repo| repo["watchers_count"] }
    # repo_url = most_popular_repo["url"]

    # get_json(repo_url) do |repo|
    # contributors_url = repo["contributors_url"]

    # get_json(contributors_url) do |users|
    # most_profilic_user = users.max_by { |user| user["contributions"] }
    # user_url = most_profilic_user["url"]

    # get_json(user_url) do |user|
    # puts "The most influential Rubyist is #{user["name"]} (#{user["login"]})"
    # end
    # end
    # end
    # end
    # end
    # end

    class Eventually
    attr_accessor :block

    def initialize(&block)
    @block = block
    end

    def self.from_value(value)
    new { |s| s.call(value) }
    end

    def and_then(&block)
    Eventually.new do |success|
    run do |value|
    block.call(value).run(&success)
    end
    end
    end

    def run(&success)
    block.call(success)
    end
    end

    def get_github_api_urls
    github_root_url = "https://api.github.com"
    Eventually.new{ |success| get_json(github_root_url, &success) }
    end

    def get_org(urls, name)
    org_url_template = URITemplate.new(urls["organization_url"])
    org_url = org_url_template.expand(org: name)

    Eventually.new{ |success| get_json(org_url, &success) }
    end

    def get_repos(org)
    repos_url = org["repos_url"]

    Eventually.new { |success| get_json(repos_url, &success) }
    end

    def get_most_popular_repo(repos)
    most_popular_repo = repos.max_by { |repo| repo["watchers_count"] }
    repo_url = most_popular_repo["url"]

    Eventually.new { |success| get_json(repo_url, &success) }
    end

    def get_contributors(repo)
    contributors_url = repo["contributors_url"]

    Eventually.new { |success| get_json(contributors_url, &success) }
    end

    def get_most_profilic_user(users)
    most_profilic_user = users.max_by { |user| user["contributions"] }
    user_url = most_profilic_user["url"]

    Eventually.new { |success| get_json(user_url, &success) }
    end


    get_github_api_urls
    .and_then { |urls | get_org(urls, 'ruby') }
    .and_then { |org | get_repos(org) }
    .and_then { |repos| get_most_popular_repo(repos) }
    .and_then { |repo | get_contributors(repo) }
    .and_then { |users| get_most_profilic_user(users) }
    .run do |user|
    puts "The most influential Rubyist is #{user["name"]} (#{user["login"]})"
    end

    # ---------------------------------------------------------------------------- #

    module Monad
    def within(&block)
    and_then do |value|
    self.class.from_value(block.call(value))
    end
    end
    end

    Optional.include Monad
    Many.include Monad
    Eventually.include Monad


    def description_from(containing_json)
    containing_json.within do |json|
    JSON.parse(json)
    end.within do |hash|
    "%s (%s)" % hash.values_at("name", "login")
    end
    end

    optional_nil = Optional.new(nil)
    p description_from(optional_nil)

    optional_json = Optional.new(%|{ "login": "nobu", "name": "Nobuyoshi Nakada" }|)
    p description_from(optional_json)

    many_jsons = Many.new([
    %|{ "login": "nobu", "name": "Nobuyoshi Nakada" }|,
    %|{ "login": "matz", "name": "Yukihiro Matsumoto" }|,
    ])
    p description_from(many_jsons)

    eventually_json = Eventually.new do |success|
    Thread.new do
    uri = URI.parse("https://api.github.com/users/nobu")
    json = Net::HTTP.get(uri)
    success.call(json)
    end
    end
    description_from(eventually_json).run { |description| p(description) }

    puts "\n press any key to exit \n"
    gets