require 'spec_helper'

describe Parametric do
  it 'should have a version number' do
    Parametric::VERSION.should_not be_nil
  end

  describe Parametric do

    let(:klass) do
      Class.new do
        include Parametric::Params
        param :name, 'User name'
        param :page, 'page number', default: 1
        param :per_page, 'items per page', default: 50
        param :status, 'status', options: ['one', 'two', 'three'], multiple: true
        param :piped_status, 'status with pipes', multiple: true, separator: '|'
        param :country, 'country', options: ['UK', 'CL', 'JPN']
        param :email, 'email', match: /\w+@\w+\.\w+/
        param :emails, 'emails', match: /\w+@\w+\.\w+/, multiple: true, default: 'default@email.com'
      end
    end

    describe '#params' do
      let(:subject) { klass.new(foo: 'bar', per_page: 20, status: 'four') }

      it 'ignores undeclared fields' do
        subject.params.has_key?(:foo).should be_false
      end

      it 'sets passed values' do
        subject.params[:per_page].should == 20
      end

      it 'uses defaults if no value passed' do
        subject.params[:page].should == 1
      end

      it 'does not set value if outside of declared options' do
        subject.params[:status].should == []
      end

      it 'does not set value if it does not :match' do
        klass.new(email: 'my@email').params[:email].should be_nil
      end

      it 'does set value if it does :match' do
        klass.new(email: 'my@email.com').params[:email].should == 'my@email.com'
      end

      it 'only sets value for :multiple values that :match' do
        klass.new(emails: 'my@email,your,her@email.com').params[:emails].should == ['her@email.com']
      end

      it 'returns :default wrapped in array if :multiple' do
        klass.new().params[:emails].should == ['default@email.com']
      end

      it 'turns :multiple comma-separated values into arrays' do
        klass.new(status: 'one,three').params[:status].should == ['one', 'three']
      end

      it ':multiple values can be arrays' do
        klass.new(status: ['one','three']).params[:status].should == ['one', 'three']
      end

      it 'turns :multiple separated values with custom separator into arrays' do
        klass.new(piped_status: 'one|three').params[:piped_status].should == ['one', 'three']
      end

      it 'does not turn non-multiple comma-separated values into arrays' do
        klass.new(name: 'foo,bar').params[:name].should == 'foo,bar'
      end

      it 'filters out undeclared options' do
        klass.new(status: 'one,three,fourteen').params[:status].should == ['one', 'three']
      end

      it 'defaults empty multiple options to empty array' do
        klass.new().params[:status].should == []
      end

      it 'wraps single multiple options in array' do
        klass.new(status: 'one').params[:status].should == ['one']
      end

      it 'does not accept comma-separated values outside of options unless :multiple == true' do
        klass.new(country: 'UK,CL').params[:country].should be_nil
      end

      it 'does accept single option' do
        klass.new(country: 'UK').params[:country].should == 'UK'
      end

      it 'does not accept single option if not in declared options' do
        klass.new(country: 'USA').params[:country].should be_nil
      end
    end

    describe '#available_params' do
      let(:subject) { klass.new(foo: 'bar', name: 'lala', per_page: 20, status: 'four', emails: 'one@email.com,two@email.com') }

      it 'only includes declared params with values or defaults' do
        subject.available_params.keys.sort.should == [:emails, :name, :page, :per_page]
        subject.available_params[:emails].should == ['one@email.com', 'two@email.com']
        subject.available_params[:name].should == 'lala'
        subject.available_params[:per_page].should == 20
        subject.available_params[:page].should == 1
      end

      describe '#flat' do
        it 'joins values back' do
          subject.available_params.flat[:emails].should == 'one@email.com,two@email.com'
        end
      end
    end

    describe '#schema' do
      let(:subject) { klass.new(foo: 'bar', name: 'lala', per_page: 20, status: 'four') }

      it 'returns full param definitions with populated value' do
        regexp = /\w+@\w+\.\w+/.to_s

        subject.schema[:name][:label].should == 'User name'
        subject.schema[:name][:value].should == 'lala'

        subject.schema[:page][:label].should == 'page number'
        subject.schema[:page][:value].should == 1

        subject.schema[:per_page][:label].should == 'items per page'
        subject.schema[:per_page][:value].should == 20

        subject.schema[:status][:label].should == 'status'
        subject.schema[:status][:value].should == ''
        subject.schema[:status][:options].should == ['one', 'two', 'three']
        subject.schema[:status][:multiple].should be_true

        subject.schema[:piped_status][:label].should == 'status with pipes'
        subject.schema[:piped_status][:value].should == ''
        subject.schema[:piped_status][:multiple].should be_true

        subject.schema[:country][:label].should == 'country'
        subject.schema[:country][:value].should == ''
        subject.schema[:country][:options].should == ['UK', 'CL', 'JPN']

        subject.schema[:email][:label].should == 'email'
        subject.schema[:email][:value].should == ''
        subject.schema[:email][:match].should == regexp

        subject.schema[:emails][:label].should == 'emails'
        subject.schema[:emails][:value].should == 'default@email.com'
        subject.schema[:emails][:multiple].should be_true
        subject.schema[:emails][:match].should == regexp
      end
    end
  end

  describe Parametric::Hash do
    let(:klass) do
      Class.new do
        include Parametric::Hash
        param :name, 'User name'
        param :page, 'page number', default: 1
        param :per_page, 'items per page', default: 50
      end
    end

    let(:subject) { klass.new(name: 'Ismael', page: 2) }

    it 'quacks like a hash' do
      subject[:name].should == 'Ismael'
      subject[:page].should == 2
      subject[:per_page].should == 50
      subject.map{|k,v| k}.sort.should == [:name, :page, :per_page]
      subject.keys.sort.should == [:name, :page, :per_page]
      subject.values.map(&:to_s).sort.should == ['2', '50', 'Ismael']
      subject.fetch(:page, 0).should == 2
      subject.fetch(:foo, 0).should == 0
      subject.merge(foo: 22).should == {name: 'Ismael', page: 2, per_page: 50, foo: 22}
      subject.select{|k,v| k == :name}.should == {name: 'Ismael'}
    end

    it 'has #available_params' do
      subject.available_params[:name].should == 'Ismael'
    end

    it 'has #schema' do
      subject.schema[:name][:label].should == 'User name'
      subject.schema[:name][:value].should == 'Ismael'
    end
  end
end