text:
type RefArray{R<:Integer,N}
    a::Array{R,N}
end

type PooledDataArray{T, R<:Integer, N} <: AbstractDataArray{T, N}
    refs::Array{R, N}
    pool::Vector{T}

    function PooledDataArray(rs::RefArray{R, N},
                             p::Vector{T})
        # refs mustn't overflow pool
        if length(rs.a) > 0 && maximum(rs.a) > prod(size(p))
            throw(ArgumentError("Reference array points beyond the end of the pool"))
        end
        new(rs.a,p)
    end
end
typealias PooledDataVector{T,R} PooledDataArray{T,R,1}
typealias PooledDataMatrix{T,R} PooledDataArray{T,R,2}

##############################################################################
##
## PooledDataArray constructors
##
# How do you construct a PooledDataArray from an Array?
# From the same sigs as a DataArray!
#
# Algorithm:
# * Start with:
#   * A null pool
#   * A pre-allocated refs
#   * A hash from T to Int
# * Iterate over d
#   * If value of d in pool already, set the refs accordingly
#   * If value is new, add it to the pool, then set refs
##############################################################################

# Echo inner constructor as an outer constructor
function PooledDataArray{T,R<:Integer,N}(refs::RefArray{R, N},
                                         pool::Vector{T})
    PooledDataArray{T,R,N}(refs, pool)
end

# A no-op constructor
PooledDataArray(d::PooledDataArray) = d

# Constructor from array, w/ pool, missingness, and ref type
function PooledDataArray{T,R<:Integer,N}(d::AbstractArray{T, N},
                                         pool::Vector{T},
                                         m::AbstractArray{Bool, N},
                                         r::Type{R} = DEFAULT_POOLED_REF_TYPE)
    if length(pool) > typemax(R)
        throw(ArgumentError("Cannot construct a PooledDataVector with type $R with a pool of size $(length(pool))"))
    end

    newrefs = Array(R, size(d))
    poolref = Dict{T, R}()

    # loop through once to fill the poolref dict
    for i = 1:length(pool)
        poolref[pool[i]] = i
    end

    # fill in newrefs
    for i = 1:length(d)
        if m[i]
            newrefs[i] = 0
        else
            newrefs[i] = get(poolref, d[i], 0)
        end
    end
    return PooledDataArray(RefArray(newrefs), pool)
end

# Constructor from array, w/ missingness and ref type
function PooledDataArray{T,R<:Integer,N}(d::AbstractArray{T, N},
                                         m::AbstractArray{Bool, N},
                                         r::Type{R} = DEFAULT_POOLED_REF_TYPE)
    pool = convert(Array, unique(d[!m]))
    if method_exists(isless, (T, T))
        sort!(pool)
    end
    PooledDataArray(d, pool, m, r)
end

# Construct an all-NA PooledDataVector of a specific type
PooledDataArray(t::Type, dims::@compat Tuple{Vararg{Int}}) = PooledDataArray(Array(t, dims), trues(dims))
PooledDataArray(t::Type, dims::Int...) = PooledDataArray(Array(t, dims), trues(dims))
PooledDataArray{R<:Integer}(t::Type, r::Type{R}, dims::@compat Tuple{Vararg{Int}}) = PooledDataArray(Array(t, dims), trues(dims), r)
PooledDataArray{R<:Integer}(t::Type, r::Type{R}, dims::Int...) = PooledDataArray(Array(t, dims), trues(dims), r)

# Construct an empty PooledDataVector of a specific type
PooledDataArray(t::Type) = PooledDataArray(similar(Array(t,1),0), trues(0))
PooledDataArray{R<:Integer}(t::Type, r::Type{R}) = PooledDataArray(similar(Array(t,1),0), trues(0), r)

# Convert a BitArray to an Array{Bool} (m = missingness)
# For some reason an additional method is needed but even that doesn't work
# For a BitArray a refs type of UInt8 will always be sufficient as the size of the pool is 0, 1 or 2
PooledDataArray{N}(d::BitArray{N}) = PooledDataArray(bitunpack(d), falses(size(d)), UInt8)
PooledDataArray{N}(d::BitArray{N}, m::AbstractArray{Bool, N}) = PooledDataArray(bitunpack(d), m, UInt8)

# Convert a DataArray to a PooledDataArray
PooledDataArray{T,R<:Integer}(da::DataArray{T},
                              r::Type{R} = DEFAULT_POOLED_REF_TYPE) = PooledDataArray(da.data, da.na, r)
PooledDataArray{T,R<:Integer}(da::DataArray{T},
                              pool::Vector{T},
                              r::Type{R} = DEFAULT_POOLED_REF_TYPE) = PooledDataArray(da.data, pool, da.na, r)

# Convert a Array{T} to a PooledDataArray
PooledDataArray{T,R<:Integer}(d::Array{T},
                              r::Type{R} = DEFAULT_POOLED_REF_TYPE) = PooledDataArray(d, falses(size(d)), r)
PooledDataArray{T,R<:Integer}(d::Array{T},
                              pool::Vector{T},
                              r::Type{R} = DEFAULT_POOLED_REF_TYPE) = PooledDataArray(d, pool, falses(size(d)), r)

# Explicitly convert Ranges into a PooledDataVector
PooledDataArray{R<:Integer}(rs::Range,
                            r::Type{R} = DEFAULT_POOLED_REF_TYPE) = PooledDataArray([rs], falses(length(rs)), r)

# Initialized constructors with 0's, 1's
for (f, basef) in ((:pdatazeros, :zeros), (:pdataones, :ones))
    @eval begin
        ($f)(dims::Int...) = PooledDataArray(($basef)(dims...), falses(dims...))
        ($f)(t::Type, dims::Int...) = PooledDataArray(($basef)(t, dims...), falses(dims...))
        ($f){R<:Integer}(t::Type, r::Type{R}, dims::Int...) = PooledDataArray(($basef)(t, dims...), falses(dims...), r)
    end
end

##############################################################################
##
## Basic size properties of all Data* objects
##
##############################################################################

Base.size(pda::PooledDataArray) = size(pda.refs)
Base.length(pda::PooledDataArray) = length(pda.refs)
Base.endof(pda::PooledDataArray) = endof(pda.refs)

##############################################################################
##
## Copying Data* objects
##
##############################################################################

Base.copy(pda::PooledDataArray) = PooledDataArray(RefArray(copy(pda.refs)),
                                                  copy(pda.pool))
# TODO: Implement copy_to()

function Base.resize!{T,R}(pda::PooledDataArray{T,R,1}, n::Int)
    oldn = length(pda.refs)
    resize!(pda.refs, n)
    pda.refs[oldn+1:n] = zero(R)
    pda
end

##############################################################################
##
## Predicates, including the new isna()
##
##############################################################################

function Base.isnan(pda::PooledDataArray)
    PooledDataArray(RefArray(copy(pda.refs)), isnan(pda.pool))
end

function Base.isfinite(pda::PooledDataArray)
    PooledDataArray(RefArray(copy(pda.refs)), isfinite(pda.pool))
end

isna(pda::PooledDataArray) = pda.refs .== 0

#' @description
#'
#' Safe and type-stable way to determine if element `i` of an
#' PooledDataArray is `NA`.
#'
#' @param pda::PooledDataArray The PooledDataArray whose missingness will
#'        be assessed.
#' @param i::Integer The index of the element to be checked for `NA`.
#'
#' @returns na::Bool Is the element `NA` or not?
#'
#' @examples
#'
#' a = @pdata([1, 2, 3])
#' isna(a, 1)
isna(pda::PooledDataArray, i::Real) = pda.refs[i] == 0 # -> Bool

#' @description
#'
#' Determine if any of the entries of an PooledDataArray are `NA`.
#'
#' @param pda::PooledDataArray The PooledDataArray whose elements will
#'        be assessed.
#'
#' @returns out::Bool Are any of the elements of `pda` an `NA` value?
#'
#' @examples
#'
#' pda = @pdata([1, 2, 3])
#' anyna(pda)
function anyna(pda::PooledDataArray)
    for ref in pda.refs
        ref == 0 && return true
    end
    return false
end

#' @description
#'
#' Determine if all of the entries of an PooledDataArray are `NA`.
#'
#' @param pda::PooledDataArray The PooledDataArray whose elements will
#'        be assessed.
#'
#' @returns out::Bool Are all of the elements of `pda` an `NA` value?
#'
#' @examples
#'
#' pda = @pdata([1, 2, 3])
#' allna(pda)
function allna(pda::PooledDataArray)
    for ref in pda.refs
        ref == 0 || return false
    end
    return true
end

##############################################################################
##
## PooledDataArray utilities
##
## TODO: Add methods with these names for DataArray's
##
##############################################################################

function compact{T,R<:Integer,N}(d::PooledDataArray{T,R,N})
    sz = length(d.pool)

    REFTYPE = sz <= typemax(UInt8)  ? UInt8 :
              sz <= typemax(UInt16) ? UInt16 :
              sz <= typemax(UInt32) ? UInt32 :
                                      UInt64

    if REFTYPE == R
        return d
    end

    newrefs = convert(Array{REFTYPE, N}, d.refs)
    PooledDataArray(RefArray(newrefs), d.pool)
end

#' @description
#'
#' Return a DataVector containing the unique values of a `PooledDataArray`,
#' in the order they appear in the data, including `NA` if any missing entries
#' are encountered. For `PooledDataArray`s, this function is much less efficient
#' than `levels`, which does not return the values in the same order.
#'
#' @param da::DataArray{T} `DataArray` whose unique values are desired.
#'
#' @returns dv::DataVector{T} `DataVector` containing the unique values
#'          from `pda`, in the order they appear, including `NA` if there are
#'          any missing entries in `pda`.
#'
#' @examples
#'
#' pdv = @pdata [1, -2, 1, NA, 4]
#' distinct_values = unique(pdv)
function Base.unique{T}(pda::PooledDataArray{T})
    n = length(pda)
    nlevels = length(pda.pool)
    unique_values = Array(T, 0)
    sizehint!(unique_values, nlevels)
    seen = Set{eltype(pda.refs)}()

    firstna = 0
    for i in 1:n
        if isna(pda, i)
            if firstna == 0
                firstna = length(unique_values) + 1
            end
        elseif !in(pda.refs[i], seen)
            push!(seen, pda.refs[i])
            push!(unique_values, pda.pool[pda.refs[i]])
        else
            continue
        end

        if firstna > 0 && length(unique_values) == nlevels
            break
        end
    end

    if firstna > 0
        res = DataArray(Array(T, nlevels + 1))
        i = 0
        for val in unique_values
            i += 1
            if i == firstna
                res.na[i] = true
                i += 1
            end
            res.data[i] = val
        end

        if firstna == nlevels + 1
            res.na[nlevels + 1] = true
        end

        return res
    else
        return DataArray(unique_values)
    end
end

#' @description
#'
#' Return a DataVector containing the levels of a `PooledDataArray`,
#' excluding `NA`. For `PooledDataArray`s, this function is much more
#' efficient than `unique`, and it returns the levels in the order used
#' when calling `setlevels!` rather than in the order of appearance.
#' Contrary to `unique`, it also returns levels which are not present in
#' the vector.
#'
#'
#' @param pda::PooledDataArray{T} PooledDataArray whose levels values are
#' desired.
#'
#' @returns dv::DataVector{T} DataVector containing the levels
#'          of `da`, excluding `NA`.
#'
#' @examples
#'
#' pdv = @pdata [1, -2, 1, NA, 4]
#' distinct_values = levels(pdv)
levels{T}(pda::PooledDataArray{T}) = copy(pda.pool)

function PooledDataArray{S,R,N}(x::PooledDataArray{S,R,N},
                                newpool::Vector{S})
    # QUESTION: should we have a ! version of this? If so, needs renaming?
    tidx::Array{R} = findat(newpool, x.pool)
    refs = zeros(R, length(x))
    for i in 1:length(refs)
        if x.refs[i] != 0
            refs[i] = tidx[x.refs[i]]
        end
    end
    PooledDataArray(RefArray(refs), newpool)
end

myunique(x::AbstractVector) = x[sort(unique(findat(x, x)))]  # gets the ordering right
myunique(x::AbstractDataVector) = myunique(dropna(x))   # gets the ordering right; removes NAs

function setlevels{T,R}(x::PooledDataArray{T,R}, newpool::AbstractVector)
    pool = myunique(newpool)
    refs = zeros(R, length(x))
    tidx::Array{R} = findat(pool, newpool)
    tidx[isna(newpool)] = 0
    for i in 1:length(refs)
        if x.refs[i] != 0
            refs[i] = tidx[x.refs[i]]
        end
    end
    return PooledDataArray(RefArray(refs), pool)
end

function setlevels!{T,R}(x::PooledDataArray{T,R}, newpool::AbstractVector{T})
    if newpool == myunique(newpool) # no NAs or duplicates
        x.pool = newpool
        return x
    else
        x.pool = myunique(newpool)
        tidx::Array{R} = findat(x.pool, newpool)
        tidx[isna(newpool)] = 0
        for i in 1:length(x.refs)
            if x.refs[i] != 0
                x.refs[i] = tidx[x.refs[i]]
            end
        end
        return x
    end
end

function setlevels(x::PooledDataArray, d::Dict)
    newpool = copy(DataArray(x.pool))
    # An NA in `v` is put in the pool; that will cause it to become NA
    for (k,v) in d
        idx = findin(newpool, [k])
        if length(idx) == 1
            newpool[idx[1]] = v
        end
    end
    setlevels(x, newpool)
end

function setlevels!{T,R}(x::PooledDataArray{T,R}, d::Dict{T,T})
    for (k,v) in d
        idx = findin(x.pool, [k])
        if length(idx) == 1
            x.pool[idx[1]] = v
        end
    end
    x
end

function setlevels!{T,R}(x::PooledDataArray{T,R}, d::Dict{T,Any}) # this version handles NAs in d's values
    newpool = copy(DataArray(x.pool))
    # An NA in `v` is put in the pool; that will cause it to become NA
    for (k,v) in d
        idx = findin(newpool, [k])
        if length(idx) == 1
            newpool[idx[1]] = v
        end
    end
    setlevels!(x, newpool)
end

reorder(x::PooledDataArray) = PooledDataArray(x, sort(levels(x)))  # just re-sort the pool

reorder(x::PooledDataArray, y::AbstractVector...) = reorder(mean, x, y...)

### FIXME: this can't work because we don't know about DataFrames
# reorder(fun::Function, x::PooledDataArray, y::AbstractVector...) =
#     reorder(fun, x, DataFrame({y...}))

Base.reverse(x::PooledDataArray) = PooledDataArray(RefArray(reverse(x.refs)), x.pool)

##############################################################################
##
## similar()
##
##############################################################################

function Base.similar{T,R}(pda::PooledDataArray{T,R}, S::Type, dims::Dims)
    PooledDataArray(RefArray(zeros(R, dims)), S[])
end

##############################################################################
##
## find()
##
##############################################################################

Base.find(pdv::PooledDataVector{Bool}) = find(convert(Vector{Bool}, pdv, false))

##############################################################################
##
## setindex!() definitions
##
##############################################################################

function getpoolidx{T,R<:Union(UInt8, UInt16, Int8, Int16)}(pda::PooledDataArray{T,R}, val::Any)
    val::T = convert(T,val)
    pool_idx = findfirst(pda.pool, val)
    if pool_idx <= 0
        push!(pda.pool, val)
        pool_idx = length(pda.pool)
        if pool_idx > typemax(R)
            throw(ErrorException(
                "You're using a PooledDataArray with ref type $R, which can only hold $(int(typemax(R))) values,\n",
                "and you just tried to add the $(typemax(R)+1)th reference.  Please change the ref type\n",
                "to a larger int type, or use the default ref type ($DEFAULT_POOLED_REF_TYPE)."
            ))
        end
    end
    return pool_idx
end

function getpoolidx{T,R}(pda::PooledDataArray{T,R}, val::Any)
    val::T = convert(T,val)
    pool_idx = findfirst(pda.pool, val)
    if pool_idx <= 0
        push!(pda.pool, val)
        pool_idx = length(pda.pool)
    end
    return pool_idx
end

getpoolidx{T,R<:Union(UInt8, UInt16, Int8, Int16)}(pda::PooledDataArray{T,R}, val::NAtype) = zero(R)
getpoolidx{T,R}(pda::PooledDataArray{T,R}, val::NAtype) = zero(R)

##############################################################################
##
## show() and similar methods
##
##############################################################################

function Base.string(x::PooledDataVector)
    tmp = join(x, ", ")
    return "[$tmp]"
end

# # Need setindex!()'s to make this work
# This is broken now because the inner show returns to the outer show.
# function show(io::IO, pda::PooledDataArray)
#     invoke(show, (Any, AbstractArray), io, pda)
#     print(io, "\nlevels: ")
#     print(io, levels(pda))
# end

##############################################################################
##
## Replacement operations
##
##############################################################################

function replace!(x::PooledDataArray{NAtype}, fromval::NAtype, toval::NAtype)
    NA # no-op to deal with warning
end
function replace!(x::PooledDataArray, fromval::NAtype, toval::NAtype)
    NA # no-op to deal with warning
end
function replace!{S, T}(x::PooledDataArray{S}, fromval::T, toval::NAtype)
    fromidx = findfirst(x.pool, fromval)
    if fromidx == 0
        throw(ErrorException("can't replace a value not in the pool in a PooledDataVector!"))
    end

    x.refs[x.refs .== fromidx] = 0

    return NA
end
function replace!{S, T}(x::PooledDataArray{S}, fromval::NAtype, toval::T)
    toidx = findfirst(x.pool, toval)
    # if toval is in the pool, just do the assignment
    if toidx != 0
        x.refs[x.refs .== 0] = toidx
    else
        # otherwise, toval is new, add it to the pool
        push!(x.pool, toval)
        x.refs[x.refs .== 0] = length(x.pool)
    end

    return toval
end
function replace!{R, S, T}(x::PooledDataArray{R}, fromval::S, toval::T)
    # throw error if fromval isn't in the pool
    fromidx = findfirst(x.pool, fromval)
    if fromidx == 0
        throw(ErrorException("can't replace a value not in the pool in a PooledDataArray!"))
    end

    # if toval is in the pool too, use that and remove fromval from the pool
    toidx = findfirst(x.pool, toval)
    if toidx != 0
        x.refs[x.refs .== fromidx] = toidx
        #x.pool[fromidx] = None    TODO: what to do here??
    else
        # otherwise, toval is new, swap it in
        x.pool[fromidx] = toval
    end

    return toval
end

##############################################################################
##
## Sorting can use the pool to speed things up
##
##############################################################################

function Base.sortperm(pda::PooledDataArray; alg::Base.Sort.Algorithm=Base.Sort.DEFAULT_UNSTABLE,
                       lt::Function=isless, by::Function=identity,
                       rev::Bool=false, order=Base.Sort.Forward)
    order = Base.ord(lt, by, rev, order)

    # TODO handle custom ordering efficiently
    if !isa(order, Base.Order.ForwardOrdering) && !isa(order, Base.Order.ReverseOrdering)
        return sort!(collect(1:length(pda)), alg, Base.Order.Perm(order,pda))
    end

    # TODO handle non-sorted keys without copying
    perm = issorted(pda.pool) ? groupsort_indexer(pda, true)[1] : sortperm(reorder(pda))
    isa(order, Base.Order.ReverseOrdering) && reverse!(perm)
    perm
end

Base.sort(pda::PooledDataArray; kw...) = pda[sortperm(pda; kw...)]

type FastPerm{O<:Base.Sort.Ordering,V<:AbstractVector} <: Base.Sort.Ordering
    ord::O
    vec::V
end
FastPerm{O<:Base.Sort.Ordering,V<:AbstractVector}(o::O,v::V) = FastPerm{O,V}(o,v)
Base.sortperm{V}(x::AbstractVector, a::Base.Sort.Algorithm, o::FastPerm{Base.Sort.ForwardOrdering,V}) = x[sortperm(o.vec)]
Base.sortperm{V}(x::AbstractVector, a::Base.Sort.Algorithm, o::FastPerm{Base.Sort.ReverseOrdering,V}) = x[reverse(sortperm(o.vec))]
Perm{O<:Base.Sort.Ordering}(o::O, v::PooledDataVector) = FastPerm(o, v)

##############################################################################
##
## PooledDataVecs: EXPLANATION SHOULD GO HERE
##
##############################################################################

function PooledDataVecs{S,Q<:Integer,R<:Integer,N}(v1::PooledDataArray{S,Q,N},
                                                   v2::PooledDataArray{S,R,N})
    pool = sort(unique([v1.pool, v2.pool]))
    sz = length(pool)

    REFTYPE = sz <= typemax(UInt8)  ? UInt8 :
              sz <= typemax(UInt16) ? UInt16 :
              sz <= typemax(UInt32) ? UInt32 :
                                      UInt64

    tidx1 = convert(Vector{REFTYPE}, findat(pool, v1.pool))
    tidx2 = convert(Vector{REFTYPE}, findat(pool, v2.pool))
    refs1 = zeros(REFTYPE, length(v1))
    refs2 = zeros(REFTYPE, length(v2))
    for i in 1:length(refs1)
        if v1.refs[i] != 0
            refs1[i] = tidx1[v1.refs[i]]
        end
    end
    for i in 1:length(refs2)
        if v2.refs[i] != 0
            refs2[i] = tidx2[v2.refs[i]]
        end
    end
    return (PooledDataArray(RefArray(refs1), pool),
            PooledDataArray(RefArray(refs2), pool))
end

function PooledDataVecs{S,R<:Integer,N}(v1::PooledDataArray{S,R,N},
                                        v2::AbstractArray{S,N})
    return PooledDataVecs(v1,
                          PooledDataArray(v2))
end
function PooledDataVecs{S,R<:Integer,N}(v1::PooledDataArray{S,R,N},
                                        v2::AbstractArray{S,N})
    return PooledDataVecs(v1,
                          PooledDataArray(v2))
end

####
function PooledDataVecs{S,R<:Integer,N}(v1::AbstractArray{S,N},
                                        v2::PooledDataArray{S,R,N})
    return PooledDataVecs(PooledDataArray(v1),
                          v2)
end

function PooledDataVecs(v1::AbstractArray,
                        v2::AbstractArray)

    ## Return two PooledDataVecs that share the same pool.

    ## TODO: allow specification of REFTYPE
    refs1 = Array(DEFAULT_POOLED_REF_TYPE, size(v1))
    refs2 = Array(DEFAULT_POOLED_REF_TYPE, size(v2))
    poolref = Dict{promote_type(eltype(v1), eltype(v2)), DEFAULT_POOLED_REF_TYPE}()
    maxref = 0

    # loop through once to fill the poolref dict
    for i = 1:length(v1)
        if !isna(v1[i])
            poolref[v1[i]] = 0
        end
    end
    for i = 1:length(v2)
        if !isna(v2[i])
            poolref[v2[i]] = 0
        end
    end

    # fill positions in poolref
    pool = sort(collect(keys(poolref)))
    i = 1
    for p in pool
        poolref[p] = i
        i += 1
    end

    # fill in newrefs
    zeroval = zero(DEFAULT_POOLED_REF_TYPE)
    for i = 1:length(v1)
        if isna(v1[i])
            refs1[i] = zeroval
        else
            refs1[i] = poolref[v1[i]]
        end
    end
    for i = 1:length(v2)
        if isna(v2[i])
            refs2[i] = zeroval
        else
            refs2[i] = poolref[v2[i]]
        end
    end

    return (PooledDataArray(RefArray(refs1), pool),
            PooledDataArray(RefArray(refs2), pool))
end

Base.convert{S,T,R1<:Integer,R2<:Integer,N}(::Type{PooledDataArray{S,R1,N}}, pda::PooledDataArray{T,R2,N}) =
    PooledDataArray(RefArray(convert(Array{R1,N}, pda.refs)), convert(Vector{S}, pda.pool))
Base.convert{S,T,R<:Integer,N}(::Type{PooledDataArray{S,R,N}}, pda::PooledDataArray{T,R,N}) =
    PooledDataArray(RefArray(copy(pda.refs)), convert(Vector{S}, pda.pool))
Base.convert{T,R<:Integer,N}(::Type{PooledDataArray{T,R,N}}, pda::PooledDataArray{T,R,N}) = pda
Base.convert{S,T,R1<:Integer,R2<:Integer,N}(::Type{PooledDataArray{S,R1}}, pda::PooledDataArray{T,R2,N}) =
    convert(PooledDataArray{S,R1,N}, pda)
Base.convert{S,T,R<:Integer,N}(::Type{PooledDataArray{S}}, pda::PooledDataArray{T,R,N}) =
    convert(PooledDataArray{S,R,N}, pda)
Base.convert{T,R<:Integer,N}(::Type{PooledDataArray}, pda::PooledDataArray{T,R,N}) = pda

Base.convert{T,R<:Integer,N}(::Type{PooledDataArray{T,R,N}}, a::DataArray{T,N}) =
    PooledDataArray(a.data, a.na, R)
Base.convert{S,T,R<:Integer,N}(::Type{PooledDataArray{S,R,N}}, a::DataArray{T,N}) =
    convert(PooledDataArray{S,R,N}, PooledDataArray(a.data, a.na, R))
Base.convert{S,T,R<:Integer,N}(::Type{PooledDataArray{S,R}}, a::DataArray{T,N}) =
    convert(PooledDataArray{S,R,N}, PooledDataArray(a.data, a.na, R))
Base.convert{S,T,N}(::Type{PooledDataArray{S}}, a::DataArray{T,N}) =
    convert(PooledDataArray{S}, PooledDataArray(a.data, a.na))
Base.convert(::Type{PooledDataArray}, a::DataArray) =
    PooledDataArray(a.data, a.na)

Base.convert{S,T,R<:Integer,N}(::Type{PooledDataArray{S,R,N}}, a::AbstractArray{T,N}) =
    PooledDataArray(convert(Array{S,N}, a), falses(size(a)), R)
Base.convert{S,T,R<:Integer,N}(::Type{PooledDataArray{S,R}}, a::AbstractArray{T,N}) =
    PooledDataArray(convert(Array{S,N}, a), falses(size(a)), R)
Base.convert{S,T,N}(::Type{PooledDataArray{S}}, a::AbstractArray{T,N}) =
    PooledDataArray(convert(Array{S,N}, a), falses(size(a)))
Base.convert(::Type{PooledDataArray}, a::AbstractArray) =
    PooledDataArray(a, falses(size(a)))

function Base.convert{S,T,R<:Integer,N}(::Type{DataArray{S,N}},
                                        pda::PooledDataArray{T,R,N})
    res = DataArray(Array(S, size(pda)), BitArray(size(pda)))
    for i in 1:length(pda)
        r = pda.refs[i]
        if r == 0 # TODO: Use zero(R)
            res.na[i] = true
        else
            res.na[i] = false
            res.data[i] = pda.pool[r]
        end
    end
    return res
end
Base.convert{S,T,R<:Integer,N}(::Type{DataArray{S}}, pda::PooledDataArray{T,R,N}) =
    convert(DataArray{S,N}, pda)
Base.convert{T,R<:Integer,N}(::Type{DataArray}, pda::PooledDataArray{T,R,N}) =
    convert(DataArray{T,N}, pda)

pdata(a::AbstractArray) = convert(PooledDataArray, a)

function Base.convert{S, T, R, N}(
    ::Type{Array{S, N}},
    pda::PooledDataArray{T, R, N}
)
    res = Array(S, size(pda))
    for i in 1:length(pda)
        if pda.refs[i] == zero(R)
            throw(NAException())
        else
            res[i] = pda.pool[pda.refs[i]]
        end
    end
    return res
end

function Base.convert{T, R}(::Type{Vector}, pdv::PooledDataVector{T, R})
    return convert(Array{T, 1}, pdv)
end

function Base.convert{T, R}(::Type{Matrix}, pdm::PooledDataMatrix{T, R})
    return convert(Array{T, 2}, pdm)
end

function Base.convert{T, R, N}(::Type{Array}, pda::PooledDataArray{T, R, N})
    return convert(Array{T, N}, pda)
end

function Base.convert{S, T, R, N}(
    ::Type{Array{S, N}},
    pda::PooledDataArray{T, R, N},
    replacement::Any
)
    res = Array(S, size(pda))
    replacementS = convert(S, replacement)
    for i in 1:length(pda)
        if pda.refs[i] == zero(R)
            res[i] = replacementS
        else
            res[i] = pda.pool[pda.refs[i]]
        end
    end
    return res
end

function Base.convert{T, R}(::Type{Vector}, pdv::PooledDataVector{T, R}, replacement::Any)
    return convert(Array{T, 1}, pdv, replacement)
end

function Base.convert{T, R}(::Type{Matrix}, pdm::PooledDataMatrix{T, R}, replacement::Any)
    return convert(Array{T, 2}, pdm, replacement)
end

function Base.convert{T, R, N}(::Type{Array}, pda::PooledDataArray{T, R, N}, replacement::Any)
    return convert(Array{T, N}, pda, replacement)
end

function dropna{T}(pdv::PooledDataVector{T})
    n = length(pdv)
    res = Array(T, n)
    total = 0
    for i in 1:n
        if pdv.refs[i] > 0
            total += 1
            res[total] = pdv.pool[pdv.refs[i]]
        end
    end
    resize!(res, total)
    return res
end


text:
<?php

namespace Respect\Validation\Rules\SubdivisionCode;

use Respect\Validation\Rules\AbstractSearcher;

/**
 * Validator for Jersey subdivision code.
 *
 * ISO 3166-1 alpha-2: JE
 *
 * @link http://www.geonames.org/JE/administrative-division-jersey.html
 */
class JeSubdivisionCode extends AbstractSearcher
{
    public $haystack = array(null, '');

    public $compareIdentical = true;
}

redpajama_set_name: RedPajamaGithub


text:
<title>Zend Framework API Documentation</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta><link rel="stylesheet" href="css/black-tie/jquery-ui-1.8.2.custom.css" type="text/css"></link><link rel="stylesheet" href="css/jquery.treeview.css" type="text/css"></link><link rel="stylesheet" href="css/theme.css" type="text/css"></link>

API Documentation

Pdf/Element/Object.php

Global

                      Classes

\Zend_Pdf_Element_Object

Zend Framework

LICENSE

This source file is subject to the new BSD license that is bundled
with this package in the file LICENSE.txt.
It is also available through the world-wide-web at this URL:
http://framework.zend.com/license/new-bsd
If you did not receive a copy of the license and are unable to
obtain it through the world-wide-web, please send an email
to license@zend.com so we can send you a copy immediately.

category

Zend

copyright

Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)

license

New BSD License

package

Zend_Pdf

version

$Id: Object.php 23775 2011-03-01 17:25:24Z ralph $

\Zend_Pdf_Element_Object
jump to top

PDF file 'indirect object' element implementation

Extends from

\Zend_Pdf_Element

category

Zend

copyright

Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)

license

New BSD License

package

Zend_Pdf

Properties

\Zend_Pdf_ElementFactory
                 $_factory= ''

Reference to the factory.

Details

Type

\Zend_Pdf_ElementFactory

integer
                 $_genNum= ''

Generation number

Details

Type

integer

integer
                 $_objNum= ''

Object number within PDF file

Details

Type

integer

\Zend_Pdf_Element
                 $_value= ''

Object value

Details

Type

\Zend_Pdf_Element

Methods

__call(
          string $method, array $args
          )

        :
          mixed

Call handler

Parameters

Name
Type
Description

$method
string

$args
array

Returns

Type
Description

mixed

__construct(
          \Zend_Pdf_Element $val, integer $objNum, integer $genNum, \Zend_Pdf_ElementFactory $factory
          )

        :
          void

Object constructor

Parameters

Name
Type
Description

$val
\Zend_Pdf_Element

$objNum
integer

$genNum
integer

$factory
\Zend_Pdf_ElementFactory

Throws

Exception
Description

\Zend_Pdf_Exception

__get(
          string $property
          )

        :
          mixed

Get handler

Parameters

Name
Type
Description

$property
string

Returns

Type
Description

mixed

__set(
          string $property, mixed $value
          )

        :
          void

Set handler

Parameters

Name
Type
Description

$property
string

$value
mixed

cleanUp(

          )

        :
          void

Clean up resources, used by object

dump(
          \Zend_Pdf_ElementFactory $factory
          )

        :
          string

Dump object to a string to save within PDF file.

$factory parameter defines operation context.

Parameters

Name
Type
Description

$factory
\Zend_Pdf_ElementFactory

Returns

Type
Description

string

getFactory(

          )

        :
          \Zend_Pdf_ElementFactory

Check, that object is generated by specified factory

Returns

Type
Description

\Zend_Pdf_ElementFactory

getGenNum(

          )

        :
          integer

Get generation number

Returns

Type
Description

integer

getObjNum(

          )

        :
          integer

Get object number

Returns

Type
Description

integer

getObject(

          )

        :
          \Zend_Pdf_Element_Object

Return object, which can be used to identify object and its references identity

Returns

Type
Description

\Zend_Pdf_Element_Object

getParentObject(

          )

        :
          \Zend_Pdf_Element_Object

Get top level parent indirect object.

Inherited from:
                    \Zend_Pdf_Element::getParentObject()

Returns

Type
Description

\Zend_Pdf_Element_Object

getType(

          )

        :
          integer

Return type of the element.

See ZPdfPDFConst for possible values

Returns

Type
Description

integer

makeClone(
          \Zend_Pdf_ElementFactory $factory, array $processed, integer $mode
          )

        :
          void

Detach PDF object from the factory (if applicable), clone it and attach to new factory.

Parameters

Name
Type
Description

$factory
\Zend_Pdf_ElementFactory
The factory to attach

$processed
array

&$processed List of already processed indirect objects, used to avoid objects duplication

$mode
integer

Cloning mode (defines filter for objects cloning)

Details

returns

Zend_Pdf_Element

phpToPdf(
          mixed $input
          )

        :
          \Zend_Pdf_Element

static
Convert PHP value into PDF element.

Inherited from:
                    \Zend_Pdf_Element::phpToPdf()

Parameters

Name
Type
Description

$input
mixed

Returns

Type
Description

\Zend_Pdf_Element

setParentObject(
          \Zend_Pdf_Element_Object $parent
          )

        :
          void

Set top level parent indirect object.

Inherited from:
                    \Zend_Pdf_Element::setParentObject()

Parameters

Name
Type
Description

$parent
\Zend_Pdf_Element_Object

toPhp(

          )

        :
          mixed

Convert PDF element to PHP type.

Returns

Type
Description

mixed

toString(
          \Zend_Pdf_Factory $factory
              =
              null
          )

        :
          string

Return reference to the object

$factory parameter defines operation context.

Parameters

Name
Type
Description

$factory
\Zend_Pdf_Factory

Returns

Type
Description

string

touch(

          )

        :
          void

Mark object as modified, to include it into new PDF file segment

We don't automate this action to keep control on PDF update process.
All new objects are treated as "modified" automatically.

Documentation was generated by DocBlox 0.13.3.


text:
https://docs.docker.com/desktop/

## :o: Installation

:pushpin: Windows

```
PS > choco install docker-desktop -y
```

:pushpin: MacOS

```
$ brew cask install docker-desktop
```


text:
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

goog.provide('goog.testing.fs.FileReaderTest');
goog.setTestOnly('goog.testing.fs.FileReaderTest');

goog.require('goog.Promise');
goog.require('goog.array');
goog.require('goog.events');
goog.require('goog.fs.Error');
goog.require('goog.fs.FileReader');
goog.require('goog.object');
goog.require('goog.testing.events.EventObserver');
goog.require('goog.testing.fs.FileReader');
goog.require('goog.testing.fs.FileSystem');
goog.require('goog.testing.jsunit');

var EventType = goog.fs.FileReader.EventType;
var ReadyState = goog.fs.FileReader.ReadyState;

/** @type {!goog.testing.fs.File} */
var file;

/** @type {!goog.testing.fs.FileReader} */
var reader;

/** @type {!goog.testing.events.EventObserver} */
var observer;

/** @const */
var hasArrayBuffer = goog.isDef(goog.global.ArrayBuffer);

function setUp() {
  var observedEvents = [];
  var fs = new goog.testing.fs.FileSystem();
  var fileEntry = fs.getRoot().createDirectorySync('foo').createFileSync('bar');

  file = fileEntry.fileSync();
  file.setDataInternal('test content');

  reader = new goog.testing.fs.FileReader();

  // Observe all file events fired by the FileReader.
  observer = new goog.testing.events.EventObserver();
  goog.events.listen(reader, goog.object.getValues(EventType), observer);
}

function tearDown() {
  goog.dispose(reader);
}

function testRead() {
  assertEquals(ReadyState.INIT, reader.getReadyState());
  assertUndefined(reader.getResult());

  return new goog.Promise(function(resolve, reject) {
    goog.events.listen(reader, EventType.LOAD_END, resolve);
    reader.readAsText(file);
    assertEquals(ReadyState.LOADING, reader.getReadyState());
  }).then(function(result) {
    assertEquals(file.toString(), reader.getResult());

    assertEquals(ReadyState.DONE, reader.getReadyState());
    assertArrayEquals([
      EventType.LOAD_START,
      EventType.LOAD,
      EventType.LOAD,
      EventType.LOAD,
      EventType.LOAD_END
    ], goog.array.map(observer.getEvents(), function(e) { return e.type; }));
  });
}

function testReadAsArrayBuffer() {
  if (!hasArrayBuffer) {
    // Skip if array buffer is not supported
    return;
  }

  return new goog.Promise(function(resolve, reject) {
    goog.events.listen(reader, EventType.LOAD_END, resolve);
    reader.readAsArrayBuffer(file);
    assertEquals(ReadyState.LOADING, reader.getReadyState());
  }).then(function(result) {
    assertElementsEquals(file.toArrayBuffer(), reader.getResult());

    assertEquals(ReadyState.DONE, reader.getReadyState());
    assertArrayEquals([
      EventType.LOAD_START,
      EventType.LOAD,
      EventType.LOAD,
      EventType.LOAD,
      EventType.LOAD_END
    ], goog.array.map(observer.getEvents(), function(e) { return e.type; }));
  });
}

function testReadAsDataUrl() {
  return new goog.Promise(function(resolve, reject) {
    goog.events.listen(reader, EventType.LOAD_END, resolve);
    reader.readAsDataUrl(file);
    assertEquals(ReadyState.LOADING, reader.getReadyState());
  }).then(function(result) {
    assertEquals(file.toDataUrl(), reader.getResult());

    assertEquals(ReadyState.DONE, reader.getReadyState());
    assertArrayEquals([
      EventType.LOAD_START,
      EventType.LOAD,
      EventType.LOAD,
      EventType.LOAD,
      EventType.LOAD_END
    ], goog.array.map(observer.getEvents(), function(e) { return e.type; }));
  });
}

function testAbort() {
  return new goog.Promise(function(resolve, reject) {
    goog.events.listen(reader, EventType.LOAD_END, resolve);
    reader.readAsText(file);
    assertEquals(ReadyState.LOADING, reader.getReadyState());
    reader.abort();
  }).then(function(result) {
    assertUndefined(reader.getResult());

    assertEquals(ReadyState.DONE, reader.getReadyState());
    assertArrayEquals([
      EventType.ERROR,
      EventType.ABORT,
      EventType.LOAD_END
    ], goog.array.map(observer.getEvents(), function(e) { return e.type; }));
  });
}

function testAbortBeforeRead() {
  var err = assertThrows(function() {
    reader.abort();
  });
  assertEquals(goog.fs.Error.ErrorCode.INVALID_STATE, err.code);
}

function testReadDuringRead() {
  var err = assertThrows(function() {
    reader.readAsText(file);
    reader.readAsText(file);
  });
  assertEquals(goog.fs.Error.ErrorCode.INVALID_STATE, err.code);
}


text:
<!DOCTYPE html>

  <meta charset="utf-8"/>
  <title>
    Speakers - SVCC-gen2
  </title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

Loading...


text:
require 'spec_helper'

describe GeoConcerns::FileSetActionsHelper do
  let(:helper) { TestingHelper.new }

  before do
    class TestingHelper
      include GeoConcerns::FileSetActionsHelper

      def render(partial, locals = {})
      end
    end
  end

  after do
    Object.send(:remove_const, :TestingHelper)
  end

  let(:presenter) { GeoConcerns::GeoConcernsShowPresenter.new(solr_document, nil) }
  let(:solr_document) { SolrDocument.new(geo_mime_type_ssim: [geo_mime_type]) }

  describe '#file_set_actions' do
    let(:geo_mime_type) { 'image/tiff' }

    before do
      allow(helper).to receive(:file_set_actions_partial).with(presenter)
        .and_return('geo_concerns/file_sets/actions/image_actions')
    end

    it "renders a partial" do
      expect(helper).to receive(:render)
        .with('geo_concerns/file_sets/actions/image_actions', file_set: presenter)
      helper.file_set_actions(presenter)
    end
    it "takes options" do
      expect(helper).to receive(:render)
        .with('geo_concerns/file_sets/actions/image_actions', file_set: presenter, bbox: '123')
      helper.file_set_actions(presenter, bbox: '123')
    end
  end

  describe '#file_set_actions_partial' do
    subject { helper.file_set_actions_partial(presenter) }

    context "with an image file" do
      let(:geo_mime_type) { 'image/tiff' }
      it { is_expected.to eq 'geo_concerns/file_sets/actions/image_actions' }
    end

    context "with a vector file" do
      let(:geo_mime_type) { 'application/vnd.geo+json' }
      it { is_expected.to eq 'geo_concerns/file_sets/actions/vector_actions' }
    end

    context "with a raster file" do
      let(:geo_mime_type) { 'text/plain; gdal-format=USGSDEM' }
      it { is_expected.to eq 'geo_concerns/file_sets/actions/raster_actions' }
    end

    context "with a metadata file" do
      let(:geo_mime_type) { 'application/xml; schema=fgdc' }
      it { is_expected.to eq 'geo_concerns/file_sets/actions/metadata_actions' }
    end

    context "with anything else" do
      let(:geo_mime_type) { 'application/binary' }
      it { is_expected.to eq 'geo_concerns/file_sets/actions/default_actions' }
    end
  end
end


text:
---
layout: post
title:  java 从零实现 rpc(二)客户端调用服务端
date:  2020-12-16 22:11:27 +0800
categories: [RPC]
tags: [rpc, micro service, sh]
published: true
---

# 回顾

大家好,我是老马。

我们在前面学习了 [5 分钟入门 spring cloud 实战笔记](https://www.toutiao.com/i6906136436756840968/) 和 [dubbo 2.7 的 3种入门案例实战](https://www.toutiao.com/i6906507244977127950/),
小伙伴肯定对 rpc 有了基本的认识。

系列目录:

[java 从零实现 RPC(一)服务端与客户端启动](https://www.toutiao.com/i6906867178591535630/)

# 服务对象调用

上一节为了便于大家理解,实际上只是启动了服务端和客户端,但是二者之间没有任何交互。

这一节让我们一起来学习下实现客户端调用服务端。

## 功能

实现 Client 端到服务端之间的固定服务 pojo 调用。

# 接口

## 服务

我们希望实现一个计算功能。

```java
public interface Calculator {

    /**
     * 计算加法
     * @param request 请求入参
     * @return 返回结果
     */
    CalculateResponse sum(final CalculateRequest request);

}
```

- 服务端实现

```java
public class CalculatorService implements Calculator {

    @Override
    public CalculateResponse sum(CalculateRequest request) {
        int sum = request.getOne()+request.getTwo();

        return new CalculateResponse(true, sum);
    }

}
```

## pojo 信息

入参和出参如下:

- CalculateRequest.java

```java
public class CalculateRequest implements Serializable {

    private static final long serialVersionUID = 6420751004355300996L;

    /**
     * 参数一
     */
    private int one;

    /**
     * 参数二
     */
    private int two;

    //Getter & Setter
}
```

- CalculateResponse.java

```java
public class CalculateResponse implements Serializable {

    private static final long serialVersionUID = -1972014736222511341L;

    /**
     * 是否成功
     */
   private boolean success;

    /**
     * 二者的和
     */
   private int sum;

    //Getter & Setter
}
```

# 服务端核心代码

## RpcServerHandler

主要处理客户端的调用请求。

```java
import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.rpc.common.model.CalculateRequest;
import com.github.houbb.rpc.common.model.CalculateResponse;
import com.github.houbb.rpc.common.service.Calculator;
import com.github.houbb.rpc.server.service.CalculatorService;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * @author 老马啸西风
 * @since 0.0.1
 */
public class RpcServerHandler extends SimpleChannelInboundHandler {

    private static final Log log = LogFactory.getLog(RpcServerHandler.class);

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        final String id = ctx.channel().id().asLongText();
        log.info("[Server] channel {} connected " + id);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        final String id = ctx.channel().id().asLongText();

        CalculateRequest request = (CalculateRequest)msg;
        log.info("[Server] receive channel {} request: {} from ", id, request);

        Calculator calculator = new CalculatorService();
        CalculateResponse response = calculator.sum(request);

        // 回写到 client 端
        ctx.writeAndFlush(response);
        log.info("[Server] channel {} response {}", id, response);
    }

}
```

## 编码器 & 解码器

可以使得我们 handler 中直接操作对象即可。

- CalculateResponseEncoder.java

```java
import com.github.houbb.rpc.common.model.CalculateResponse;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

/**
 * @author 老马啸西风
 * @since 0.0.3
 */
public class CalculateResponseEncoder extends MessageToByteEncoder<CalculateResponse> {

    @Override
    protected void encode(ChannelHandlerContext ctx, CalculateResponse msg, ByteBuf out) throws Exception {
        boolean success = msg.isSuccess();
        int result = msg.getSum();
        out.writeBoolean(success);
        out.writeInt(result);
    }

}
```

- CalculateRequestDecoder.java

```java
import com.github.houbb.rpc.common.model.CalculateRequest;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

/**
 * 请求参数解码
 * @author 老马啸西风
 * @since 0.0.3
 */
public class CalculateRequestDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int one = in.readInt();
        int two = in.readInt();

        CalculateRequest request = new CalculateRequest(one, two);
        out.add(request);
    }

}
```

## 处理类的指定

我们将上述的处理类,在服务端启动的时候进行指定:

```java
serverBootstrap.group(workerGroup, bossGroup)
                .channel(NioServerSocketChannel.class)
                // 打印日志
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(Channel ch) throws Exception {
                        ch.pipeline()
                                .addLast(new CalculateRequestDecoder())
                                .addLast(new CalculateResponseEncoder())
                                .addLast(new RpcServerHandler());
                    }
                })
```

可以看到 netty 的 initChannel() 方法,可以让我们非常方便的添加各种处理类。

# 客户端核心代码

## RpcClientHandler.java

向客户端发送请求,并且处理服务端响应结果。

```java
import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.rpc.client.core.RpcClient;
import com.github.houbb.rpc.common.model.CalculateRequest;
import com.github.houbb.rpc.common.model.CalculateResponse;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * <p> 客户端处理类 </p>
 *
 * <pre> Created: 2019/10/16 11:30 下午  </pre>
 * <pre> Project: rpc  </pre>
 *
 * @author 老马啸西风
 * @since 0.0.2
 */
public class RpcClientHandler extends SimpleChannelInboundHandler {

    private static final Log log = LogFactory.getLog(RpcClient.class);

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        CalculateRequest request = new CalculateRequest(1, 2);

        ctx.writeAndFlush(request);
        log.info("[Client] request is :{}", request);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        CalculateResponse response = (CalculateResponse)msg;
        log.info("[Client] response is :{}", response);
    }
}
```

## 编码器 & 解码器

- CalculateRequestEncoder.java

```java
import com.github.houbb.rpc.common.model.CalculateRequest;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

/**
 * @author 老马啸西风
 * @since 0.0.3
 */
public class CalculateRequestEncoder extends MessageToByteEncoder<CalculateRequest> {

    @Override
    protected void encode(ChannelHandlerContext ctx, CalculateRequest msg, ByteBuf out) throws Exception {
        int one = msg.getOne();
        int two = msg.getTwo();

        out.writeInt(one);
        out.writeInt(two);
    }

}
```

- CalculateResponseDecoder.java

```java
import com.github.houbb.rpc.common.model.CalculateResponse;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

/**
 * 响应参数解码
 * @author 老马啸西风
 * @since 0.0.3
 */
public class CalculateResponseDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        boolean success = in.readBoolean();
        int sum = in.readInt();

        CalculateResponse response = new CalculateResponse(success, sum);
        out.add(response);
    }
}
```

## 处理类的指定

我们在客户端启动的时候进行指定:

```java
ChannelFuture channelFuture = bootstrap.group(workerGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .handler(new ChannelInitializer<Channel>(){
                        @Override
                        protected void initChannel(Channel ch) throws Exception {
                            ch.pipeline()
                                    .addLast(new LoggingHandler(LogLevel.INFO))
                                    .addLast(new CalculateRequestEncoder())
                                    .addLast(new CalculateResponseDecoder())
                                    .addLast(new RpcClientHandler());
                        }
                    })
                    .connect(RpcConstant.ADDRESS, port)
                    .syncUninterruptibly();
```

这个和服务端类似,比较简单。

# 测试代码

## 服务端启动

- 测试代码

```java
new RpcServer().start();
```

- 日志

```
[INFO] [2020-12-20 11:24:54.160] [Thread-0] [c.g.h.r.s.c.RpcServer.run] - RPC 服务开始启动服务端
十二月 20, 2020 11:24:56 上午 io.netty.handler.logging.LoggingHandler channelRegistered
信息: [id: 0x89de1e73] REGISTERED
十二月 20, 2020 11:24:56 上午 io.netty.handler.logging.LoggingHandler bind
信息: [id: 0x89de1e73] BIND: 0.0.0.0/0.0.0.0:9527
十二月 20, 2020 11:24:56 上午 io.netty.handler.logging.LoggingHandler channelActive
信息: [id: 0x89de1e73, L:/0:0:0:0:0:0:0:0:9527] ACTIVE
[INFO] [2020-12-20 11:24:56.452] [Thread-0] [c.g.h.r.s.c.RpcServer.run] - RPC 服务端启动完成,监听【9527】端口
```

## 客户端启动

- 测试代码

```java
new RpcClient().start();
```

- 日志

客户端日志如下:

```
[INFO] [2020-12-20 11:25:35.196] [nioEventLoopGroup-2-1] [c.g.h.r.c.c.RpcClient.channelActive] - [Client] request is :CalculateRequest{one=1, two=2}
十二月 20, 2020 11:25:35 上午 io.netty.handler.logging.LoggingHandler channelRead
信息: [id: 0x1bef131c, L:/127.0.0.1:51336 - R:/127.0.0.1:9527] READ: 5B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 01 00 00 00 03                                  |.....           |
+--------+-------------------------------------------------+----------------+
十二月 20, 2020 11:25:35 上午 io.netty.handler.logging.LoggingHandler channelReadComplete
信息: [id: 0x1bef131c, L:/127.0.0.1:51336 - R:/127.0.0.1:9527] READ COMPLETE
[INFO] [2020-12-20 11:25:35.262] [nioEventLoopGroup-2-1] [c.g.h.r.c.c.RpcClient.channelRead0] - [Client] response is :CalculateResponse{success=true, sum=3}
```

服务端日志如下:

```
[INFO] [2020-12-20 11:25:35.177] [nioEventLoopGroup-2-1] [c.g.h.r.s.h.RpcServerHandler.channelActive] - [Server] channel {} connected 502b73fffec4485c-00002eb4-00000001-3b69b5a4d9ae582e-dd8f2794
[INFO] [2020-12-20 11:25:35.250] [nioEventLoopGroup-2-1] [c.g.h.r.s.h.RpcServerHandler.channelRead0] - [Server] receive channel 502b73fffec4485c-00002eb4-00000001-3b69b5a4d9ae582e-dd8f2794 request: CalculateRequest{one=1, two=2} from
[INFO] [2020-12-20 11:25:35.253] [nioEventLoopGroup-2-1] [c.g.h.r.s.h.RpcServerHandler.channelRead0] - [Server] channel 502b73fffec4485c-00002eb4-00000001-3b69b5a4d9ae582e-dd8f2794 response CalculateResponse{success=true, sum=3}
```

可以发现,客户端已经通过网络调用服务端,并且获得了客户端的响应。

# 小结

这一节我们演示了如何实现实现客户端和服务端之间的交互,小伙伴看了之后,也要自己动手写一下。

为了便于大家学习,所有源码均已开源:

> [https://github.com/houbb/rpc](https://github.com/houbb/rpc)

希望本文对你有所帮助,如果喜欢,欢迎点赞收藏转发一波。

我是老马,期待与你的下次相遇。

# 参考资料

https://mp.weixin.qq.com/s/r9F8qYw8PIcyjGR2yS0Jzg

* any list
{:toc}


text:
require 'faraday'
require 'forwardable'

module FaradayMiddleware
  # Public: Uses the simple_oauth library to sign requests according the
  # OAuth protocol.
  #
  # The options for this middleware are forwarded to SimpleOAuth::Header:
  # :consumer_key, :consumer_secret, :token, :token_secret. All these
  # parameters are optional.
  #
  # The signature is added to the "Authorization" HTTP request header. If the
  # value for this header already exists, it is not overriden.
  #
  # If no Content-Type header is specified, this middleware assumes that
  # request body parameters should be included while signing the request.
  # Otherwise, it only includes them if the Content-Type is
  # "application/x-www-form-urlencoded", as per OAuth 1.0.
  #
  # For better performance while signing requests, this middleware should be
  # positioned before UrlEncoded middleware on the stack, but after any other
  # body-encoding middleware (such as EncodeJson).
  class OAuth < Faraday::Middleware
    dependency 'simple_oauth'

    AUTH_HEADER = 'Authorization'.freeze
    CONTENT_TYPE = 'Content-Type'.freeze
    TYPE_URLENCODED = 'application/x-www-form-urlencoded'.freeze

    extend Forwardable
    parser_method = :parse_nested_query
    parser_module = ::Faraday::Utils.respond_to?(parser_method) ? 'Faraday::Utils' : 'Rack::Utils'
    def_delegator parser_module, parser_method

    def initialize(app, options)
      super(app)
      @options = options
    end

    def call(env)
      env[:request_headers][AUTH_HEADER] ||= oauth_header(env).to_s if sign_request?(env)
      @app.call(env)
    end

    def oauth_header(env)
      SimpleOAuth::Header.new env[:method],
                              env[:url].to_s,
                              signature_params(body_params(env)),
                              oauth_options(env)
    end

    def sign_request?(env)
      !!env[:request].fetch(:oauth, true)
    end

    def oauth_options(env)
      if extra = env[:request][:oauth] and extra.is_a? Hash and !extra.empty?
        @options.merge extra
      else
        @options
      end
    end

    def body_params(env)
      if include_body_params?(env)
        if env[:body].respond_to?(:to_str)
          parse_nested_query env[:body]
        else
          env[:body]
        end
      end || {}
    end

    def include_body_params?(env)
      # see RFC 5489, section 3.4.1.3.1 for details
      !(type = env[:request_headers][CONTENT_TYPE]) or type == TYPE_URLENCODED
    end

    def signature_params(params)
      params.empty? ? params :
        params.reject {|k,v| v.respond_to?(:content_type) }
    end
  end
end

# deprecated alias
Faraday::Request::OAuth = FaradayMiddleware::OAuth


text:
/*
 *  QBaseMap.h
 *  iStressLess
 *

 *
 */

#ifndef QBASEMAP_H
#define QBASEMAP_H

#include "QDefs.h"

#define MAP_FLAG_FREE_NAME 1
#define MAP_FLAG_FREE_VALUE 2

typedef struct map_entry_t {
	char *name;
	void *value;
	int flags;
} map_entry_t;

class QBaseMap {
protected:
	map_entry_t *entries;
	int entryCount;
public:

	QBaseMap();
	virtual ~QBaseMap();

	void *_get(const char *name);
	void _put(const char *name, void *value, int flags);
	virtual void deleteValue(void *value);
};

#endif


text:
<?php

namespace Prime\Html\Base;

/**
 * Descrição da Classe HTMLDiv
 *
 * @author Elton Luiz
 */
class HTMLDiv extends HTMLElement
{

    public function __construct($nameId = null)
    {
        parent::__construct('div');

        if (!is_null($nameId)) {
            $this->id = $nameId;
            $this->name = $nameId;
        }
    }

}


text:
import pytest

from .common import WpsTestClient, TESTDATA, assert_response_success

@pytest.mark.skip(reason="no way of currently testing this")
def test_wps_indices_simple():
    wps = WpsTestClient()
    datainputs = "[resource={0};resource={1};indices=SU]".format(
        TESTDATA['cordex_tasmax_2006_nc'], TESTDATA['cordex_tasmax_2007_nc'])
    resp = wps.get(service='wps', request='execute', version='1.0.0',
                   identifier='indices_simple',
                   datainputs=datainputs)
    assert_response_success(resp)


text:
<?php

namespace League\Glide\Responses;

use Mockery;

class PsrResponseFactoryTest extends \PHPUnit_Framework_TestCase
{
    public function testCreateInstance()
    {
        $response = Mockery::mock('Psr\Http\Message\ResponseInterface');
        $streamCallback = function () {
        };

        $this->assertInstanceOf(
            'League\Glide\Responses\PsrResponseFactory',
            new PsrResponseFactory($response, $streamCallback)
        );
    }

    public function testCreate()
    {
        $response = Mockery::mock('Psr\Http\Message\ResponseInterface', function ($mock) {
            $mock->shouldReceive('withBody')->andReturn($mock)->once();
            $mock->shouldReceive('withHeader')->andReturn($mock)->times(4);
        });

        $streamCallback = function ($stream) {
            return $stream;
        };

        $cache = Mockery::mock('League\Flysystem\FilesystemInterface', function ($mock) {
            $mock->shouldReceive('getMimetype')->andReturn('image/jpeg');
            $mock->shouldReceive('getSize')->andReturn(0);
            $mock->shouldReceive('readStream')->andReturn(
                Mockery::mock('Psr\Http\Message\StreamInterface')
            );
        });

        $factory = new PsrResponseFactory($response, $streamCallback);

        $this->assertInstanceOf(
            'Psr\Http\Message\ResponseInterface',
            $factory->create($cache, 'image.jpg')
        );
    }
}


text:
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.github.sixro</groupId>
	<artifactId>small-javas</artifactId>
	<version>0.1.5-SNAPSHOT</version>
	<packaging>jar</packaging>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>

		<!-- Release -->
		<tagNameFormat>v@{project.version}</tagNameFormat>
		<releaseProfiles>release</releaseProfiles>
		<arguments>-DskipITs</arguments>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.25</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.5</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>4.3.7.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.jmock</groupId>
			<artifactId>jmock-junit4</artifactId>
			<version>2.8.2</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-dbcp2</artifactId>
			<version>2.1.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>6.0.5</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.2</version>
			<scope>test</scope>
		</dependency>

	</dependencies>

	<scm>
		<connection>scm:git:git@github.com:sixro/small-javas.git</connection>
		<developerConnection>scm:git:git@github.com:sixro/small-javas.git</developerConnection>
		<url>git@github.com:sixro/small-javas.git</url>
		<tag>HEAD</tag>
	</scm>

	<issueManagement>
		<system>Github Issue Tracker</system>
		<url>https://github.com/sixro/small-javas</url>
	</issueManagement>

	<licenses>
		<license>
			<name>MIT License</name>
			<url>http://www.opensource.org/licenses/mit-license.php</url>
		</license>
	</licenses>
	<distributionManagement>
		<repository>
			<id>maven-repository</id>
			<url>file:///tmp</url>
		</repository>
	</distributionManagement>

	<developers>
		<developer>
			<id>sixro</id>
			<name>Roberto Simoni</name>
			<email>me@sixro.net</email>
			<url>http://github.com/sixro</url>
		</developer>
	</developers>

	<build>
		<plugins>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-failsafe-plugin</artifactId>
				<version>2.19.1</version>
				<executions>
					<execution>
						<goals>
							<goal>integration-test</goal>
							<goal>verify</goal>
						</goals>
					</execution>
				</executions>
			</plugin>

		</plugins>
	</build>

	<profiles>
		<profile>
			<id>release</id>
			<build>
				<plugins>
					<plugin>
						<groupId>org.apache.maven.plugins</groupId>
						<artifactId>maven-source-plugin</artifactId>
						<version>2.3</version>
						<executions>
							<execution>
								<id>attach-sources</id>
								<goals>
									<goal>jar-no-fork</goal>
								</goals>
							</execution>
						</executions>
					</plugin>

					<plugin>
						<groupId>org.apache.maven.plugins</groupId>
						<artifactId>maven-javadoc-plugin</artifactId>
						<version>2.10.4</version>
						<configuration>
							<linksource>true</linksource>
						</configuration>
						<executions>
							<execution>
								<id>attach-javadocs</id>
								<goals>
									<goal>jar</goal>
								</goals>
							</execution>
						</executions>
					</plugin>

				</plugins>
			</build>
		</profile>
	</profiles>

</project>


text:
import abc

class Access(metaclass=abc.ABCMeta):

    @classmethod
    @abc.abstractmethod
    def _get_impl(cls):
        pass

    @classmethod
    def get_by_id(cls, _id):
        return cls._get_impl().get_by_id(_id)

    @classmethod
    def get_all(cls, *args, **kwargs):
        return cls._get_impl().get_all(*args, **kwargs)

    @classmethod
    def add_or_update(cls, obj):
        return cls._get_impl().add_or_update(obj)

    @classmethod
    def query(cls, *args, **kwargs):
        return cls._get_impl().query(*args, **kwargs)

    @classmethod
    def delete(cls, obj):
        cls._get_impl().delete(obj)

    @classmethod
    def paginate(cls, *args, **kwargs):
        return cls._get_impl().paginate(*args, **kwargs)


text:
namespace blink {
namespace {

#if DCHECK_IS_ON()
TEST(DisplayItemTest, DebugStringsExist) {
  for (int type = 0; type <= DisplayItem::kTypeLast; type++) {
    String debug_string =
        DisplayItem::TypeAsDebugString(static_cast<DisplayItem::Type>(type));
    EXPECT_FALSE(debug_string.empty());
    EXPECT_NE("Unknown", debug_string);
  }
}
#endif  // DCHECK_IS_ON()

TEST(DisplayItemTest, AllZeroIsTombstone) {
  alignas(alignof(DisplayItem)) uint8_t buffer[sizeof(DisplayItem)] = {0};
  EXPECT_TRUE(reinterpret_cast<const DisplayItem*>(buffer)->IsTombstone());
}

}  // namespace
}  // namespace blink


text:
using System.Linq;
using System.Threading.Tasks;
using DatenMeister.Core.EMOF.Implementation;
using DatenMeister.Core.EMOF.Interface.Common;
using DatenMeister.Core.EMOF.Interface.Reflection;
using DatenMeister.Core.Functions.Queries;
using DatenMeister.Core.Helper;
using DatenMeister.Core.Models;
using DatenMeister.Core.Models.EMOF;
using DatenMeister.Core.Provider.InMemory;
using DatenMeister.Core.Runtime;
using NUnit.Framework;

namespace DatenMeister.Tests.Modules.Actions
{
    [TestFixture]
    public class MoveOrCopyActionTests
    {
        [Test]
        public async Task TestCopyAction()
        {
            var actionLogic = ActionSetTests.CreateActionLogic();
            var (source, target) = ActionSetTests.CreateExtents(actionLogic);

            var elementSource = source.element("#source1.1");
            var elementTargetNonExisting = target.element("#source1.1");
            var elementTarget = target.element("#target1.1");

            Assert.That(elementSource, Is.Not.Null);
            Assert.That(elementTarget, Is.Not.Null);
            Assert.That(elementTargetNonExisting, Is.Null);

            var action = new MofFactory(target).create(_DatenMeister.TheOne.Actions.__MoveOrCopyAction);
            action.set(_DatenMeister._Actions._MoveOrCopyAction.source, elementSource);
            action.set(_DatenMeister._Actions._MoveOrCopyAction.target, elementTarget);
            action.set(
                _DatenMeister._Actions._MoveOrCopyAction.actionType,
                _DatenMeister._Actions.___MoveOrCopyType.Copy);

            await actionLogic.ExecuteAction(action);

            var elementInSourceAfter = source.element("#source1.1");
            var elementInTargetAfter = target.elements().GetAllDescendants()
                .WhenPropertyHasValue("name", "source1.1")
                .FirstOrDefault();
            Assert.That(elementInSourceAfter, Is.Not.Null);
            Assert.That(elementInTargetAfter, Is.Not.Null);

            var inTargetCollection = elementTarget.getOrDefault<IReflectiveCollection>(
                    DefaultClassifierHints.GetDefaultPackagePropertyName(elementTarget!))
                .OfType<IElement>()
                .ToList();
            Assert.That(inTargetCollection.Count, Is.EqualTo(1));
            Assert.That(
                inTargetCollection.Any(x =>
                    x.getOrDefault<string>(_UML._CommonStructure._NamedElement.name) == "source1.1"),
                Is.True);
        }

        [Test]
        public async Task TestMoveAction()
        {
            var actionLogic = ActionSetTests.CreateActionLogic();
            var (source, target) = ActionSetTests.CreateExtents(actionLogic);

            var elementSource = source.element("#source1.1");
            var elementTarget = target.element("#target1.1");

            var action = new MofFactory(target).create(_DatenMeister.TheOne.Actions.__MoveOrCopyAction);
            action.set(_DatenMeister._Actions._MoveOrCopyAction.source, elementSource);
            action.set(_DatenMeister._Actions._MoveOrCopyAction.target, elementTarget);
            action.set(
                _DatenMeister._Actions._MoveOrCopyAction.actionType,
                _DatenMeister._Actions.___MoveOrCopyType.Move);

            await actionLogic.ExecuteAction(action);

            var elementInSourceAfter = source.element("#source1.1");
            var elementInTargetAfter = target.elements()
                .GetAllDescendants()
                .WhenPropertyHasValue("name", "source1.1")
                .FirstOrDefault();
            Assert.That(elementInSourceAfter, Is.Null);
            Assert.That(elementInTargetAfter, Is.Not.Null);

            var inTargetCollection = elementTarget.getOrDefault<IReflectiveCollection>(
                    DefaultClassifierHints.GetDefaultPackagePropertyName(elementTarget!))
                .OfType<IElement>()
                .ToList();

            Assert.That(inTargetCollection.Count, Is.EqualTo(1));
            Assert.That(
                inTargetCollection.Any(x =>
                    x.getOrDefault<string>(_UML._CommonStructure._NamedElement.name) == "source1.1"),
                Is.True);
        }

        [Test]
        public async Task TestMoveActionIntoExtent()
        {
            var actionLogic = ActionSetTests.CreateActionLogic();
            var (source, target) = ActionSetTests.CreateExtents(actionLogic);

            var elementSource = source.element("#source1.1");

            // Check, that element is not in target
            Assert.That(
                target.elements().OfType<IObject>().Any(x=>x.getOrDefault<string>("name") == "source1.1"),
                Is.False);

            var action = new MofFactory(target).create(_DatenMeister.TheOne.Actions.__MoveOrCopyAction);
            action.set(_DatenMeister._Actions._MoveOrCopyAction.source, elementSource);
            action.set(_DatenMeister._Actions._MoveOrCopyAction.target, target);
            action.set(
                _DatenMeister._Actions._MoveOrCopyAction.actionType,
                _DatenMeister._Actions.___MoveOrCopyType.Move);

            await actionLogic.ExecuteAction(action);

            // Check, if element is in target
            Assert.That(
                target.elements().OfType<IObject>().Any(x=>x.getOrDefault<string>("name") == "source1.1"),
                Is.True);
        }

        [Test]
        public async Task TestCopyActionIntoExtent()
        {
            var actionLogic = ActionSetTests.CreateActionLogic();
            var (source, target) = ActionSetTests.CreateExtents(actionLogic);

            var elementSource = source.element("#source1.1");

            // Check, that element is not in target
            Assert.That(
                target.elements().OfType<IObject>().Any(x=>x.getOrDefault<string>("name") == "source1.1"),
                Is.False);

            var action = new MofFactory(target).create(_DatenMeister.TheOne.Actions.__MoveOrCopyAction);
            action.set(_DatenMeister._Actions._MoveOrCopyAction.source, elementSource);
            action.set(_DatenMeister._Actions._MoveOrCopyAction.target, target);
            action.set(
                _DatenMeister._Actions._MoveOrCopyAction.actionType,
                _DatenMeister._Actions.___MoveOrCopyType.Copy);

            await actionLogic.ExecuteAction(action);

            // Check, if element is in target
            Assert.That(
                target.elements().OfType<IObject>().Any(x=>x.getOrDefault<string>("name") == "source1.1"),
                Is.True);
        }
    }
}


text:
package org.apache.nifi.controller.repository;

public final class StandardFlowFileEvent implements FlowFileEvent, Cloneable {

    private final String componentId;

    private int flowFilesIn;
    private int flowFilesOut;
    private int flowFilesRemoved;
    private long contentSizeIn;
    private long contentSizeOut;
    private long contentSizeRemoved;
    private long bytesRead;
    private long bytesWritten;
    private long processingNanos;
    private long aggregateLineageMillis;
    private int flowFilesReceived;
    private long bytesReceived;
    private int flowFilesSent;
    private long bytesSent;
    private int invocations;

    public StandardFlowFileEvent(final String componentId) {
        this.componentId = componentId;
    }

    public StandardFlowFileEvent(final String componentId,
            final int flowFilesIn, final long contentSizeIn,
            final int flowFilesOut, final long contentSizeOut,
            final int flowFilesRemoved, final long contentSizeRemoved,
            final long bytesRead, final long bytesWritten,
            final int flowFilesReceived, final long bytesReceived,
            final int flowFilesSent, final long bytesSent,
            final int invocations, final long averageLineageMillis, final long processingNanos) {
        this.componentId = componentId;
        this.flowFilesIn = flowFilesIn;
        this.contentSizeIn = contentSizeIn;
        this.flowFilesOut = flowFilesOut;
        this.contentSizeOut = contentSizeOut;
        this.flowFilesRemoved = flowFilesRemoved;
        this.contentSizeRemoved = contentSizeRemoved;
        this.bytesRead = bytesRead;
        this.bytesWritten = bytesWritten;
        this.invocations = invocations;
        this.flowFilesReceived = flowFilesReceived;
        this.bytesReceived = bytesReceived;
        this.flowFilesSent = flowFilesSent;
        this.bytesSent = bytesSent;
        this.aggregateLineageMillis = averageLineageMillis;
        this.processingNanos = processingNanos;
    }

    public StandardFlowFileEvent(final FlowFileEvent other) {
        this.componentId = other.getComponentIdentifier();
        this.flowFilesIn = other.getFlowFilesIn();
        this.contentSizeIn = other.getContentSizeIn();
        this.flowFilesOut = other.getFlowFilesOut();
        this.contentSizeOut = other.getContentSizeOut();
        this.flowFilesRemoved = other.getFlowFilesRemoved();
        this.contentSizeRemoved = other.getContentSizeRemoved();
        this.bytesRead = other.getBytesRead();
        this.bytesWritten = other.getBytesWritten();
        this.invocations = other.getInvocations();
        this.flowFilesReceived = other.getFlowFilesReceived();
        this.bytesReceived = other.getBytesReceived();
        this.flowFilesSent = other.getFlowFilesSent();
        this.bytesSent = other.getBytesSent();
        this.aggregateLineageMillis = other.getAggregateLineageMillis();
        this.processingNanos = other.getProcessingNanoseconds();
    }

    @Override
    public String getComponentIdentifier() {
        return componentId;
    }

    @Override
    public int getFlowFilesIn() {
        return flowFilesIn;
    }

    public void setFlowFilesIn(int flowFilesIn) {
        this.flowFilesIn = flowFilesIn;
    }

    @Override
    public int getFlowFilesOut() {
        return flowFilesOut;
    }

    public void setFlowFilesOut(int flowFilesOut) {
        this.flowFilesOut = flowFilesOut;
    }

    @Override
    public long getContentSizeIn() {
        return contentSizeIn;
    }

    public void setContentSizeIn(long contentSizeIn) {
        this.contentSizeIn = contentSizeIn;
    }

    @Override
    public long getContentSizeOut() {
        return contentSizeOut;
    }

    public void setContentSizeOut(long contentSizeOut) {
        this.contentSizeOut = contentSizeOut;
    }

    @Override
    public long getContentSizeRemoved() {
        return contentSizeRemoved;
    }

    public void setContentSizeRemoved(final long contentSizeRemoved) {
        this.contentSizeRemoved = contentSizeRemoved;
    }

    @Override
    public int getFlowFilesRemoved() {
        return flowFilesRemoved;
    }

    public void setFlowFilesRemoved(final int flowFilesRemoved) {
        this.flowFilesRemoved = flowFilesRemoved;
    }

    @Override
    public long getBytesRead() {
        return bytesRead;
    }

    public void setBytesRead(long bytesRead) {
        this.bytesRead = bytesRead;
    }

    @Override
    public long getBytesWritten() {
        return bytesWritten;
    }

    public void setBytesWritten(long bytesWritten) {
        this.bytesWritten = bytesWritten;
    }

    @Override
    public long getProcessingNanoseconds() {
        return processingNanos;
    }

    public void setProcessingNanos(final long processingNanos) {
        this.processingNanos = processingNanos;
    }

    @Override
    public int getInvocations() {
        return invocations;
    }

    public void setInvocations(final int invocations) {
        this.invocations = invocations;
    }

    @Override
    public int getFlowFilesReceived() {
        return flowFilesReceived;
    }

    public void setFlowFilesReceived(int flowFilesReceived) {
        this.flowFilesReceived = flowFilesReceived;
    }

    @Override
    public long getBytesReceived() {
        return bytesReceived;
    }

    public void setBytesReceived(long bytesReceived) {
        this.bytesReceived = bytesReceived;
    }

    @Override
    public int getFlowFilesSent() {
        return flowFilesSent;
    }

    public void setFlowFilesSent(int flowFilesSent) {
        this.flowFilesSent = flowFilesSent;
    }

    @Override
    public long getBytesSent() {
        return bytesSent;
    }

    public void setBytesSent(long bytesSent) {
        this.bytesSent = bytesSent;
    }

    @Override
    public long getAverageLineageMillis() {
        if (flowFilesOut == 0 && flowFilesRemoved == 0) {
            return 0L;
        }

        return aggregateLineageMillis / (flowFilesOut + flowFilesRemoved);
    }

    public void setAggregateLineageMillis(long lineageMilliseconds) {
        this.aggregateLineageMillis = lineageMilliseconds;
    }

    @Override
    public long getAggregateLineageMillis() {
        return aggregateLineageMillis;
    }

}


text:
namespace ch
{

MonotoneChain2D::MonotoneChain2D()
{
    name_ = "Monotone Chain";
}

Points2D& MonotoneChain2D::solve(const Points2D& input, Points2D& output)
{
    // return solveSequential(input, output);
    return solveParallel(input, output);
}

Points2D& MonotoneChain2D::solveSequential(const Points2D& input,
                                           Points2D& output)
{
    if (input.getSize() <= 2) {
        output = input;
        return output;
    }

    const data_t& inputData = input.getData();
    order_.clear();
    for (unsigned i = 0; i < inputData.size(); i++) {
        order_.push_back(i);
    }

    sortPtsDirect(inputData);

    unsigned * lower = new unsigned[input.getSize()],
             * upper = new unsigned[input.getSize()];

    unsigned lowerSize = scanLower(inputData, lower);
    unsigned upperSize = scanUpper(inputData, upper);

    // do not include last points to avoid duplicates
    for (unsigned i = 0; i < lowerSize - 1; i++) {
        output.add(inputData[lower[i]]);
    }
    delete[] lower;

    for (unsigned i = 0; i < upperSize - 1; i++) {
        output.add(inputData[upper[i]]);
    }
    delete[] upper;

    return output;
}

Points2D& MonotoneChain2D::solveParallel(const Points2D& input,
                                         Points2D& output)
{
    if (input.getSize() <= 2) {
        output = input;
        return output;
    }

    const data_t& inputData = input.getData();
    order_.clear();
    for (unsigned i = 0; i < inputData.size(); i++) {
        order_.push_back(i);
    }

    // double tA = omp_get_wtime();
    sortPtsParallel(inputData);
    // double tB = omp_get_wtime();
    // R("") R("sort time:  " << tB - tA << " ms") std::cout << "  total time: ";

    unsigned * lower = new unsigned[input.getSize()],
             * upper = new unsigned[input.getSize()];

    unsigned lowerSize, upperSize;
#pragma omp parallel
    {
#pragma omp sections
        {
#pragma omp section
            lowerSize = scanLower(inputData, lower);
#pragma omp section
            upperSize = scanUpper(inputData, upper);
        }
    }

    // do not include last points to avoid duplicates
    for (unsigned i = 0; i < lowerSize - 1; i++) {
        output.add(inputData[lower[i]]);
    }
    delete[] lower;

    for (unsigned i = 0; i < upperSize - 1; i++) {
        output.add(inputData[upper[i]]);
    }
    delete[] upper;

    return output;
    return output;
}

unsigned MonotoneChain2D::scanLower(const data_t& input, unsigned* lower)
{
    unsigned sSize = 0;

    for (unsigned i = 0; i < input.size(); i++) {
        while (sSize >= 2 && !ccw(input[lower[sSize - 2]][0],
                                  input[lower[sSize - 2]][1],
                                  input[lower[sSize - 1]][0],
                                  input[lower[sSize - 1]][1],
                                  input[order_[i]][0],
                                  input[order_[i]][1])) {
            sSize--;
        }
        lower[sSize++] = order_[i];
    }

    return sSize;
}

unsigned MonotoneChain2D::scanUpper(const data_t& input, unsigned* upper)
{
    unsigned sSize = 0;

    for (int i = (int) input.size() - 1; i >= 0; i--) {
        while (sSize >= 2 && !ccw(input[upper[sSize - 2]][0],
                                  input[upper[sSize - 2]][1],
                                  input[upper[sSize - 1]][0],
                                  input[upper[sSize - 1]][1],
                                  input[order_[i]][0],
                                  input[order_[i]][1])) {
            sSize--;
        }
        upper[sSize++] = order_[i];
    }

    return sSize;
}

void MonotoneChain2D::sortPtsDirect(const data_t& input)
{
    std::sort(order_.begin(), order_.end(), PointCmpDirect(input));
}

void MonotoneChain2D::sortPtsCache(const data_t& input)
{
    xar_.clear(); yar_.clear();
    for (unsigned i = 0; i < input.size(); i++) {
        xar_.push_back(input[i][0]);
        yar_.push_back(input[i][1]);
    }
    std::sort(order_.begin(), order_.end(), PointCmpCache(*this, input));
}

void MonotoneChain2D::sortPtsParallel(const data_t& input)
{
    xar_.clear(); yar_.clear();
    for (unsigned i = 0; i < input.size(); i++) {
        xar_.push_back(input[i][0]);
        yar_.push_back(input[i][1]);
    }
    __gnu_parallel::stable_sort(order_.begin(), order_.end(),
                                PointCmpCache(*this, input));
}

bool MonotoneChain2D::PointCmpDirect::operator()(const unsigned& a,
                                                const unsigned& b)
{
    double dif = data_[a][0] - data_[b][0];
    if (fabs(dif) < EPS) {
        return data_[a][1] > data_[b][1];
    }
    return dif > EPS;
}

bool MonotoneChain2D::PointCmpCache::operator()(const unsigned& a,
                                                const unsigned& b)
{
    double dif = part_.xar_[a] - part_.xar_[b];
    if (fabs(dif) < EPS) {
        return part_.yar_[a] > part_.yar_[b];
    }
    return dif > EPS;
}

}


text:
package com.nominanuda.apikey;

import com.nominanuda.zen.obj.wrap.ObjWrapper;

public interface Time extends ObjWrapper {
	long start();
	Time start(long start);

	long duration();
	Time duration(long dur);
}


text:
#ifndef __C_ELEMENT_TRANSFORM_H_INCLUDED__
#define __C_ELEMENT_TRANSFORM_H_INCLUDED__

#include "../../ext/MitsubaLoader/IElement.h"

namespace irr
{
namespace ext
{
namespace MitsubaLoader
{

class CElementTransform : public IElement
{
	public:
		CElementTransform() : IElement(""), matrix() {}
		virtual ~CElementTransform() {}

		bool addProperty(SNamedPropertyElement&& _property) override;
		bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CGlobalMitsubaMetadata* globalMetadata) override { return true; }
		IElement::Type getType() const override { return IElement::Type::TRANSFORM; }
		std::string getLogName() const override { return "transform"; }
		/*
		inline CElementTransform& operator=(const CElementTransform& other)
		{
			IElement::operator=(other);
			matrix = other.matrix;
			return *this;
		}
		*/

		core::matrix4SIMD matrix;
};

}
}
}

#endif


text:
from __future__ import unicode_literals

import sys
import platform

try:
    from yaml import CLoader as Loader
    from StringIO import cStringIO as StringIO
except ImportError:
    from yaml import Loader
    from io import BytesIO as StringIO

_ver = sys.version_info
is_python2 = (_ver[0] == 2)
is_python3 = (_ver[0] == 3)
is_python26 = (_ver[0] == 2) and (_ver[1] == 6)

is_windows = platform.system().lower() == 'windows'

stdin_encoding = sys.stdin.encoding or "UTF-8"
stdout_encoding = sys.stdout.encoding or "UTF-8"


text:
using System;
using System.ComponentModel.DataAnnotations;

namespace P01_HospitalDatabase.Data.Models
{
    public class Visitation
    {
        public int VisitationId { get; set; }

        public DateTime Date { get; set; }

        [MaxLength(250)]
        public string Comments { get; set; }

        public int PatientId { get; set; }
        public Patient Patient { get; set; }

        public int? DoctorId { get; set; }
        public Doctor Doctor { get; set; }
    }
}


text:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- From: file:/usr/local/google/buildbot/repo_clients/https___googleplex-android.googlesource.com_a_platform_manifest.git/lmp-mr1-dev/frameworks/support/v7/mediarouter/res/values-zh-rTW/strings.xml -->
    <eat-comment/>
    <string msgid="8327680881775995150" name="mr_media_route_button_content_description">"投放"</string>
    <string msgid="7553005460920830010" name="mr_media_route_chooser_searching">"正在搜尋裝置..."</string>
    <string msgid="7106830097177242655" name="mr_media_route_chooser_title">"連線至裝置"</string>
    <string msgid="109793632378378069" name="mr_media_route_controller_disconnect">"中斷連線"</string>
    <string msgid="8315773974194466049" name="mr_media_route_controller_pause">"暫停"</string>
    <string msgid="5214423499524760404" name="mr_media_route_controller_play">"播放"</string>
    <string msgid="379358765881274425" name="mr_media_route_controller_settings_description">"路由設定"</string>
    <string msgid="5398645111664294430" name="mr_media_route_controller_stop">"停止投放"</string>
    <string msgid="5441529851481176817" name="mr_system_route_name">"系統"</string>
    <string msgid="7498112907524977311" name="mr_user_route_category_name">"裝置"</string>
</resources>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <title>EVENT: Vortrag anlegen</title>

    <link rel="stylesheet" href="/event/css/bootstrap.min.css">
    <link rel="stylesheet" href="/event/css/font-awesome.min.css">
    <link rel="stylesheet" href="/event/css/dropzone.min.css">
    <link rel="stylesheet" href="/event/css/main.css">
    <link rel="icon" type="image/x-icon" href="/event/images/favicon.ico" />

Vortrag anlegen

Beim Verarbeiten des Formulars ist ein Fehler aufgetreten. Es wurden nicht alle benötigten Felder korrekt ausgefüllt.

            <fieldset class="form-group">
              <input type="text" name="title" class="form-control" id="sessionTitle" placeholder="Titel des Vortrags">
            </fieldset>

            <fieldset class="form-group">
              <input type="text" name="speaker" class="form-control autocomplete speaker" id="sessionSpeaker" data-hidden-field-id="speakerId" placeholder="Name des Sprechers">
              <input type="hidden" name="speakerId" id="speakerId" value="">
            </fieldset>

            <fieldset class="form-group">
              <textarea name="description" class="form-control optional" id="sessionDescription" rows="3" placeholder="Beschreibung"></textarea>
            </fieldset>

            <fieldset class="form-group">

                <input type="text" name="date" class="datepicker form-control" maxlength="10" value="" id="datepickerSessionDate" placeholder="Datum">

            </fieldset>

            <fieldset class="form-group">
              Präsentation
              <form action="/event/api/files" class="dropzone" id="sessionSlides" data-id-target="sessionSlidesId"></form>
              Die Präsentation muss im PDF- oder PowerPoint-Format vorliegen.
              <input type="hidden" name="sessionSlidesId" class="optional" value="">
            </fieldset>

            <fieldset class="form-group">
              Screenshot
              <form action="/event/api/files" class="dropzone" id="sessionScreenshot" data-id-target="sessionScreenshotId"></form>
              Am besten eignet sich ein Screenshot der Titelfolie oder aus dem Video.
              <input type="hidden" name="sessionScreenshotId" class="optional" value="">
            </fieldset>

            <fieldset class="form-group">
              Video
              <form action="/event/api/files" class="dropzone" id="sessionVideo" data-id-target="sessionVideoId"></form>
              Folgende Videoformate werden unterstützt: ...
              <input type="hidden" name="sessionVideoId" class="optional" value="">
            </fieldset>
            <button type="submit" class="btn btn-primary save-session">Speichern</button>
            Abbrechen

        Coded w/ style by The EVENT-Team during B::IT Innovation Week 2015

          (

)

✔

✘


text:
package galaxyspace.systems.SolarSystem.moons.enceladus.dimension;

import java.util.List;

import com.google.common.collect.Lists;

import asmodeuscore.core.astronomy.dimension.world.gen.ACBiome;
import asmodeuscore.core.astronomy.dimension.world.gen.ChunkProviderSpaceLakes;
import asmodeuscore.core.astronomy.dimension.world.gen.MapGenCaves;
import galaxyspace.core.GSBlocks;
import galaxyspace.systems.SolarSystem.moons.enceladus.world.gen.BiomeDecoratorEnceladus;
import galaxyspace.systems.SolarSystem.moons.enceladus.world.gen.MapGenRavineEnceladus;
import micdoodle8.mods.galacticraft.api.prefab.core.BlockMetaPair;
import micdoodle8.mods.galacticraft.api.prefab.world.gen.BiomeDecoratorSpace;
import micdoodle8.mods.galacticraft.api.prefab.world.gen.MapGenBaseMeta;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkPrimer;

public class ChunkProviderEnceladus extends ChunkProviderSpaceLakes {

    private List<MapGenBaseMeta> worldGenerators;

    private final MapGenCaves caveGenerator = new MapGenCaves(GSBlocks.ENCELADUS_BLOCKS.getStateFromMeta(0), GSBlocks.ENCELADUS_BLOCKS.getStateFromMeta(1));
    private final MapGenRavineEnceladus ravineGenerator = new MapGenRavineEnceladus();

	@Override
	protected List<MapGenBaseMeta> getWorldGenerators()
	{
		List<MapGenBaseMeta> generators = Lists.newArrayList();
		generators.add(this.caveGenerator);
		generators.add(this.ravineGenerator);
		return generators;
	}

	public ChunkProviderEnceladus(World par1World, long seed, boolean mapFeaturesEnabled)
	{
		super(par1World, seed, mapFeaturesEnabled);
	}

	@Override
	protected BiomeDecoratorSpace getBiomeGenerator() {
	    return new BiomeDecoratorEnceladus();
	}
	//This should be a custom biome for your mod, but I'm opting to go desert instead out of quickness
	//and the fact that biomes are outside the scope of this tutorial

	@Override
	protected Biome[] getBiomesForGeneration() {
	    return new Biome[]{ACBiome.ACSpace};
	}

	@Override
	public int getCraterProbability() {
	    return 500;
	}

	@Override
	public double getHeightModifier() {
	    return 20;
	}

	@Override
	public void onChunkProvider(int cX, int cZ, ChunkPrimer primer) {

	}

	@Override
	public void onPopulate(int cX, int cZ) {
	}

	@Override
	public double getMountainHeightModifier() {
	    return 10;
	}

	@Override
	public int getWaterLevel() {
	    return 90;
	}

	@Override
	public double getSmallFeatureHeightModifier() {
	    return 55;
	}

	@Override
	public double getValleyHeightModifier() {
	    return 50;
	}

	@Override
	protected BlockMetaPair getGrassBlock() {
		return new BlockMetaPair(GSBlocks.ENCELADUS_BLOCKS, (byte) 0);
	}

	@Override
	protected BlockMetaPair getDirtBlock() {
		return new BlockMetaPair(Blocks.PACKED_ICE, (byte) 0);
	}

	@Override
	protected BlockMetaPair getStoneBlock() {
		return new BlockMetaPair(GSBlocks.ENCELADUS_BLOCKS, (byte) 1);
	}

	@Override
	protected boolean enableBiomeGenBaseBlock() {
		return false;
	}

	@Override
	public boolean canGenerateWaterBlock() {
		return false;
	}

	@Override
	public boolean canGenerateIceBlock() {
		return true;
	}

	@Override
	protected BlockMetaPair getWaterBlock() {
		return null;
	}

	@Override
	protected GenType getGenType() {
		return GenType.GC;
	}

	@Override
	public void recreateStructures(Chunk chunkIn, int x, int z) {

	}

}


text:
package main

import (
	"bytes"
	"go/format"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"text/template"
)

var packageTemplate = template.Must(template.New("").Parse(`
// Code generated by quota/redis/redistb/gen.go. DO NOT EDIT.
// source: {{ .Filename }}

package redistb

import (
	"github.com/go-redis/redis"
)

// contents of the '{{ .Prefix }}' Redis Lua script
const {{ .Prefix }}ScriptContents = {{ .Content }}

// Redis Script type for the '{{ .Prefix }}' Redis lua script
var {{ .Prefix }}Script = redis.NewScript({{ .Prefix }}ScriptContents)
`))

type templateData struct {
	Prefix   string
	Filename string
	Content  string
}

func main() {
	if len(os.Args) != 4 {
		log.Fatalf("usage: %s prefix file.lua output.go", os.Args[0])
	}

	data, err := ioutil.ReadFile(os.Args[2])
	if err != nil {
		log.Fatalf("error opening input file: %v", err)
	}

	vars := templateData{
		Prefix:   os.Args[1],
		Filename: os.Args[2],
		Content:  strconv.Quote(string(data)),
	}

	var buf bytes.Buffer
	if err := packageTemplate.Execute(&buf, vars); err != nil {
		log.Fatalf("error rendering template: %v", err)
	}

	data, err = format.Source(buf.Bytes())
	if err != nil {
		log.Fatalf("error formatting source: %v", err)
	}

	out, err := os.Create(os.Args[3])
	if err != nil {
		log.Fatalf("error opening output file: %v", err)
	}
	defer out.Close()

	if _, err := out.Write(data); err != nil {
		log.Fatalf("error writing output file: %v", err)
	}
}


text:
module Cryptoexchange::Exchanges
  module Xfutures
    module Services
      class Market < Cryptoexchange::Services::Market
        class << self
          def supports_individual_ticker_query?
            false
          end
        end

        def fetch
          output = super(ticker_url)
          adapt_all(output)
        end

        def ticker_url
          "#{Cryptoexchange::Exchanges::Xfutures::Market::API_URL}/spot/v3/instruments/ticker"
        end

        def adapt_all(output)
          output.map do |pair|
            base, target = pair["instrument_id"].split('-')
            market_pair  = Cryptoexchange::Models::MarketPair.new(
              base:   base,
              target: target,
              market: Xfutures::Market::NAME
            )
            adapt(market_pair, pair)
          end
        end

        def adapt(market_pair, output)
          ticker           = Cryptoexchange::Models::Ticker.new
          ticker.base      = market_pair.base
          ticker.target    = market_pair.target
          ticker.market    = Xfutures::Market::NAME
          ticker.last      = NumericHelper.to_d(output["last"])
          ticker.high      = NumericHelper.to_d(output["high_24h"])
          ticker.low       = NumericHelper.to_d(output["low_24h"])
          ticker.volume    = NumericHelper.to_d(output["base_volume_24h"])
          ticker.ask       = NumericHelper.to_d(output["ask"])
          ticker.bid       = NumericHelper.to_d(output["bid"])
          ticker.timestamp = nil
          ticker.payload   = output
          ticker
        end
      end
    end
  end
end


text:
Because Live Wires is built using Sass and CoffeeScript, you're going to need a preprocesser application to render your code into CSS and JavaScript.

[Mixture](http://mixture.io) is above and beyond the best tool we've used for this. It's polished, well documented, and the simplest to set up. The only other software you need is a text editor and browser.

There are a number of pretty good alternatives like CodeKit or Prepros that will also work just fine.

### Easy

This method requires little more than installing Mixture:

#### Step 1

Install [Mixture](http://mixture.io#download).

#### Step 2

Choose Live Wires from the included boilerplates list.

#### Step 3

Save the project and start coding. That's it.

Once you're up and running, there are a few settings you may want to customize in in the mixture.json and /models/_global.json files. Read more about that on the [Mixture](http://docs.mixture.io) site.

### Standard

If you're using a different preprocessor, you have a few more steps, but they're pretty easy too:

#### Step 1

Create a folder for your project.

#### Step 2

Download or clone Live Wires from the GitHub repository and save the contents in your project folder.

#### Step 3

Point your preferred Sass & CoffeeScript preprocessor app to the project folder.

#### Step 4

Configure the preprocessor to compile /source/scss/ to /httpdocs/css/ and /source/coffee/ to /httpdocs/js/.

#### Step 5

Run the preprocesser and start coding.

Note: You may also want to setup a local MAMP server for this types of install so you can use PHP or other server-side languages to build your templates. In these cases, you'll want to point your server to the /httpdocs/ directory.

### Advanced

This plugin requires Grunt `~0.4.2`

If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:

```shell
npm install livewires --save-dev
```

Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:

```js
grunt.loadNpmTasks('livewires');
```

## The "livewires" task

### Overview
In your project's Gruntfile, add a section named `livewires` to the data object passed into `grunt.initConfig()`.

```js
grunt.initConfig({
  livewires: {
    options: {
      // Task-specific options go here.
    },
    your_target: {
      // Target-specific file lists and/or options go here.
    },
  },
});
```

### Options

#### options.separator
Type: `String`
Default value: `',  '`

A string value that is used to do something with whatever.

#### options.punctuation
Type: `String`
Default value: `'.'`

A string value that is used to do something else with whatever else.

### Usage Examples

#### Default Options
In this example, the default options are used to do something with whatever. So if the `testing` file has the content `Testing` and the `123` file had the content `1 2 3`, the generated result would be `Testing, 1 2 3.`

```js
grunt.initConfig({
  livewires: {
    options: {},
    files: {
      'dest/default_options': ['src/testing', 'src/123'],
    },
  },
});
```

#### Custom Options
In this example, custom options are used to do something else with whatever else. So if the `testing` file has the content `Testing` and the `123` file had the content `1 2 3`, the generated result in this case would be `Testing: 1 2 3 !!!`

```js
grunt.initConfig({
  livewires: {
    options: {
      separator: ': ',
      punctuation: ' !!!',
    },
    files: {
      'dest/default_options': ['src/testing', 'src/123'],
    },
  },
});
```

## Contributing
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/).

## Release History
_(Nothing yet)_