text:
describe ChargebackVm do
  shared_examples_for "ChargebackVm" do
    include Spec::Support::ChargebackHelper

    let(:admin) { FactoryGirl.create(:user_admin) }
    let(:base_options) do
      {:interval_size       => 2,
       :end_interval_offset => 0,
       :tag                 => '/managed/environment/prod',
       :ext_options         => {:tz => 'UTC'},
       :userid              => admin.userid}
    end
    let(:hourly_rate)               { 0.01 }
    let(:count_hourly_rate)         { 1.00 }
    let(:cpu_count)                 { 1.0 }
    let(:memory_available)          { 1000.0 }
    let(:vm_allocated_disk_storage) { 4.0 }
    let(:starting_date) { Time.parse('2012-09-01 23:59:59Z').utc }
    let(:ts) { starting_date.in_time_zone(Metric::Helper.get_time_zone(base_options[:ext_options])) }
    let(:report_run_time) { month_end }
    let(:month_beginning) { ts.beginning_of_month.utc }
    let(:month_end) { ts.end_of_month.utc }
    let(:hours_in_month) { Time.days_in_month(month_beginning.month, month_beginning.year) * 24 }
    let(:ems) { FactoryGirl.create(:ems_vmware) }

    let(:hourly_variable_tier_rate)       { {:variable_rate => hourly_rate.to_s} }
    let(:count_hourly_variable_tier_rate) { {:variable_rate => count_hourly_rate.to_s} }

    let(:detail_params) do
      {
          :chargeback_rate_detail_cpu_used           => {:tiers => [hourly_variable_tier_rate]},
          :chargeback_rate_detail_cpu_allocated      => {:tiers => [count_hourly_variable_tier_rate]},
          :chargeback_rate_detail_memory_allocated   => {:tiers => [hourly_variable_tier_rate]},
          :chargeback_rate_detail_memory_used        => {:tiers => [hourly_variable_tier_rate]},
          :chargeback_rate_detail_disk_io_used       => {:tiers => [hourly_variable_tier_rate]},
          :chargeback_rate_detail_net_io_used        => {:tiers => [hourly_variable_tier_rate]},
          :chargeback_rate_detail_storage_used       => {:tiers => [count_hourly_variable_tier_rate]},
          :chargeback_rate_detail_storage_allocated  => {:tiers => [count_hourly_variable_tier_rate]},
          :chargeback_rate_detail_fixed_compute_cost => {:tiers => [hourly_variable_tier_rate]},
          :chargeback_rate_detail_metering_used      => {:tiers => [count_hourly_variable_tier_rate]}
      }
    end

    let!(:chargeback_rate) do
      FactoryGirl.create(:chargeback_rate, :detail_params => detail_params)
    end

    let(:metric_rollup_params) do
      {
          :tag_names             => "environment/prod",
          :parent_host_id        => @host1.id,
          :parent_ems_cluster_id => @ems_cluster.id,
          :parent_ems_id         => ems.id,
          :parent_storage_id     => @storage.id,
      }
    end

    before do
      # TODO: remove metering columns form specs
      described_class.set_columns_hash(:metering_used_metric => :integer, :metering_used_cost => :float)

      MiqRegion.seed
      ChargebackRateDetailMeasure.seed
      ChargeableField.seed
      ManageIQ::Showback::InputMeasure.seed
      MiqEnterprise.seed

      EvmSpecHelper.create_guid_miq_server_zone
      cat = FactoryGirl.create(:classification, :description => "Environment", :name => "environment", :single_value => true, :show => true)
      c = FactoryGirl.create(:classification, :name => "prod", :description => "Production", :parent_id => cat.id)
      @tag = Tag.find_by(:name => "/managed/environment/prod")

      temp = {:cb_rate => chargeback_rate, :tag => [c, "vm"]}
      ChargebackRate.set_assignments(:compute, [temp])

      Timecop.travel(report_run_time)
    end

    after do
      Timecop.return
    end

    context 'with metric rollups' do
      before do
        @vm1 = FactoryGirl.create(:vm_vmware, :name => "test_vm", :evm_owner => admin, :ems_ref => "ems_ref",
                                  :created_on => month_beginning)
        @vm1.tag_with(@tag.name, :ns => '*')

        @host1   = FactoryGirl.create(:host, :hardware => FactoryGirl.create(:hardware, :memory_mb => 8124, :cpu_total_cores => 1, :cpu_speed => 9576), :vms => [@vm1])
        @storage = FactoryGirl.create(:storage_target_vmware)
        @host1.storages << @storage

        @ems_cluster = FactoryGirl.create(:ems_cluster, :ext_management_system => ems)
        @ems_cluster.hosts << @host1
      end

      let(:report_static_fields) { %w(vm_name) }

      it "uses static fields" do
        expect(described_class.report_static_cols).to match_array(report_static_fields)
      end

      it "succeeds without a userid" do
        options = base_options.except(:userid)
        expect { ChargebackVm.build_results_for_report_ChargebackVm(options) }.not_to raise_error
      end

      context "by service" do
        let(:options) { base_options.merge(:interval => 'monthly', :interval_size => 4, :service_id => @service.id) }
        before(:each) do
          @service = FactoryGirl.create(:service)
          @service << @vm1
          @service.save

          @vm2 = FactoryGirl.create(:vm_vmware, :name => "test_vm 2", :evm_owner => admin, :created_on => month_beginning)

          add_metric_rollups_for([@vm1, @vm2], month_beginning...month_end, 12.hours, metric_rollup_params)
        end

        it "only includes VMs belonging to service in results" do
          result = described_class.build_results_for_report_ChargebackVm(options)
          expect(result).not_to be_nil
          expect(result.first.all? { |r| r.vm_name == "test_vm" })
        end
      end

      context "Daily" do
        let(:hours_in_day) { 24 }
        let(:options) { base_options.merge(:interval => 'daily') }

        let(:start_time)  { report_run_time - 17.hours }
        let(:finish_time) { report_run_time - 14.hours }

        let(:cloud_volume) { FactoryGirl.create(:cloud_volume_openstack) }

        it 'contains also columns with sub_metric(from cloud_volume)' do
          skip('this feature needs to be added to new chargeback rating') if Settings.new_chargeback

          cloud_volume_type_chargeback_colums = []
          %w(metric cost).each do |key|
            cloud_volume_type_chargeback_colums << "storage_allocated_#{cloud_volume.volume_type}_#{key}"
          end

          described_class.refresh_dynamic_metric_columns

          expect(cloud_volume_type_chargeback_colums & described_class.attribute_names).to match_array(cloud_volume_type_chargeback_colums)
        end

        before do
          add_metric_rollups_for(@vm1, start_time...finish_time, 1.hour, metric_rollup_params)
        end

        context 'with cloud volume types' do
          let!(:cloud_volume_sdd) { FactoryGirl.create(:cloud_volume_openstack, :volume_type => 'sdd') }
          let!(:cloud_volume_hdd) { FactoryGirl.create(:cloud_volume_openstack, :volume_type => 'hdd') }
          let(:state_data) do
            {
              :allocated_disk_types => {
                'sdd' => 3.gigabytes,
                'hdd' => 1.gigabytes,
              },
            }
          end

          before do
            # create vim performance state
            allocated_storage_rate_detail = chargeback_rate.chargeback_rate_details.detect { |x| x.chargeable_field.metric == 'derived_vm_allocated_disk_storage' }
            CloudVolume.all.each do |cv|
              new_rate_detail = allocated_storage_rate_detail.dup
              new_rate_detail.sub_metric = cv.volume_type
              new_rate_detail.chargeback_tiers = allocated_storage_rate_detail.chargeback_tiers.map(&:dup)
              new_rate_detail.save
              chargeback_rate.chargeback_rate_details << new_rate_detail
            end

            chargeback_rate.save
            add_vim_performance_state_for(@vm1, start_time...finish_time, 1.hour, state_data)
          end

          it 'charges sub metrics as cloud volume types' do
            skip('this feature needs to be added to new chargeback rating') if Settings.new_chargeback

            expect(subject.storage_allocated_sdd_metric).to eq(3.gigabytes)
            expect(subject.storage_allocated_sdd_cost).to eq(state_data[:allocated_disk_types]['sdd'] / 1.gigabytes * count_hourly_rate * hours_in_day)

            expect(subject.storage_allocated_hdd_metric).to eq(1.gigabytes)
            expect(subject.storage_allocated_hdd_cost).to eq(state_data[:allocated_disk_types]['hdd'] / 1.gigabytes * count_hourly_rate * hours_in_day)
          end
        end

        subject { ChargebackVm.build_results_for_report_ChargebackVm(options).first.first }

        context 'when first metric rollup has tag_names=nil' do
          before do
            options[:tag] = nil
            options[:entity_id] = @vm1.id
            @vm1.metric_rollups.first.update_attributes(:tag_names => nil)
          end

          it "cpu" do
            expect(subject.cpu_allocated_metric).to eq(cpu_count)
            used_metric = used_average_for(:cpu_usagemhz_rate_average, hours_in_day, @vm1)
            expect(subject.cpu_used_metric).to eq(used_metric)

            expect(subject.cpu_allocated_cost).to eq(cpu_count * count_hourly_rate * hours_in_day)
            expect(subject.cpu_used_cost).to eq(used_metric * hourly_rate * hours_in_day)
            expect(subject.cpu_cost).to eq(subject.cpu_allocated_cost + subject.cpu_used_cost)
          end
        end

        it "cpu" do
          expect(subject.cpu_allocated_metric).to eq(cpu_count)
          used_metric = used_average_for(:cpu_usagemhz_rate_average, hours_in_day, @vm1)
          expect(subject.cpu_used_metric).to eq(used_metric)

          expect(subject.cpu_allocated_cost).to eq(cpu_count * count_hourly_rate * hours_in_day)
          expect(subject.cpu_used_cost).to eq(used_metric * hourly_rate * hours_in_day)
          expect(subject.cpu_cost).to eq(subject.cpu_allocated_cost + subject.cpu_used_cost)
        end

        it "reports Vm Guid" do
          expect(subject.vm_guid).to eq(@vm1.guid)
        end

        it "cpu_vm_and_cpu_container_project" do
          expect(subject.cpu_allocated_metric).to eq(cpu_count)
          used_metric = used_average_for(:cpu_usagemhz_rate_average, hours_in_day, @vm1)
          expect(subject.cpu_used_metric).to eq(used_metric)

          expect(subject.cpu_allocated_cost).to eq(cpu_count * count_hourly_rate * hours_in_day)
          expect(subject.cpu_used_cost).to eq(used_metric * hourly_rate * hours_in_day)
          expect(subject.cpu_cost).to eq(subject.cpu_allocated_cost + subject.cpu_used_cost)
        end

        it "memory" do
          expect(subject.memory_allocated_metric).to eq(memory_available)
          used_metric = used_average_for(:derived_memory_used, hours_in_day, @vm1)
          expect(subject.memory_used_metric).to eq(used_metric)

          expect(subject.memory_allocated_cost).to eq(memory_available * hourly_rate * hours_in_day)
          expect(subject.memory_used_cost).to eq(used_metric * hourly_rate * hours_in_day)
          expect(subject.memory_cost).to eq(subject.memory_allocated_cost + subject.memory_used_cost)
        end

        it "disk io" do
          used_metric = used_average_for(:disk_usage_rate_average, hours_in_day, @vm1)
          expect(subject.disk_io_used_metric).to eq(used_metric)
          expect(subject.disk_io_used_cost).to be_within(0.01).of(used_metric * hourly_rate * hours_in_day)
        end

        it "net io" do
          used_metric = used_average_for(:net_usage_rate_average, hours_in_day, @vm1)
          expect(subject.net_io_used_metric).to eq(used_metric)
          expect(subject.net_io_used_cost).to eq(used_metric * hourly_rate * hours_in_day)
        end

        it "storage" do
          used_metric = used_average_for(:derived_vm_used_disk_storage, hours_in_day, @vm1)
          expect(subject.storage_used_metric).to eq(used_metric)
          expect(subject.storage_used_cost).to eq(used_metric / 1.gigabyte * count_hourly_rate * hours_in_day)

          expect(subject.storage_allocated_metric).to eq(vm_allocated_disk_storage.gigabytes)
          storage_allocated_cost = vm_allocated_disk_storage * count_hourly_rate * hours_in_day
          expect(subject.storage_allocated_cost).to eq(storage_allocated_cost)

          expect(subject.storage_cost).to eq(subject.storage_allocated_cost + subject.storage_used_cost)
        end

        it 'calculates metering used hours and cost' do
          expect(subject.metering_used_metric).to eq(hours_in_day)
          expect(subject.metering_used_cost).to eq(hours_in_day * count_hourly_rate)
        end

        context "fixed rates" do
          let(:hourly_fixed_rate) { 10.0 }

          before do
            set_tier_param_for(:derived_vm_used_disk_storage, :fixed_rate, hourly_fixed_rate)
            set_tier_param_for(:derived_vm_allocated_disk_storage, :fixed_rate, hourly_fixed_rate)
            set_tier_param_for(:derived_vm_used_disk_storage, :variable_rate, 0.0)
            set_tier_param_for(:derived_vm_allocated_disk_storage, :variable_rate, 0.0)
          end

          it "storage metrics" do
            expect(subject.storage_allocated_metric).to eq(vm_allocated_disk_storage.gigabytes)
            used_metric = used_average_for(:derived_vm_used_disk_storage, hours_in_day, @vm1)
            expect(subject.storage_used_metric).to eq(used_metric)

            expected_value = hourly_fixed_rate * hours_in_day
            expect(subject.storage_allocated_cost).to be_within(0.01).of(expected_value)

            expected_value = hourly_fixed_rate * hours_in_day
            expect(subject.storage_used_cost).to be_within(0.01).of(expected_value)
            expect(subject.storage_cost).to eq(subject.storage_allocated_cost + subject.storage_used_cost)
          end
        end
      end

      context "Report a chargeback of a tenant" do
        let(:options_tenant) { base_options.merge(:tenant_id => @tenant.id).tap { |t| t.delete(:tag) } }

        let(:start_time)  { report_run_time - 17.hours }
        let(:finish_time) { report_run_time - 14.hours }

        before do
          @tenant = FactoryGirl.create(:tenant)
          @tenant_child = FactoryGirl.create(:tenant, :parent => @tenant)
          @vm_tenant = FactoryGirl.create(:vm_vmware, :tenant_id => @tenant_child.id,
                                          :name => "test_vm_tenant", :created_on => month_beginning)

          add_metric_rollups_for(@vm_tenant, start_time...finish_time, 1.hour, metric_rollup_params)
        end

        subject { ChargebackVm.build_results_for_report_ChargebackVm(options_tenant).first.first }

        it "report a chargeback of a subtenant" do
          expect(subject.vm_name).to eq(@vm_tenant.name)
        end
      end

      context "Monthly" do
        context "calculation of allocated metrics by average" do
          let(:start_time)  { report_run_time - 17.hours }
          let(:finish_time) { report_run_time - 14.hours }
          let(:options) { base_options.merge(:interval => 'monthly', :method_for_allocated_metrics => :avg) }

          before do
            mid_point = month_beginning + 10.days
            add_metric_rollups_for(@vm1, month_beginning...mid_point, 1.hour, metric_rollup_params)
            add_metric_rollups_for(@vm1, mid_point...month_end, 1.hour, metric_rollup_params.merge!(:derived_vm_numvcpus => 2))
          end

          subject { ChargebackVm.build_results_for_report_ChargebackVm(options).first.first }

          it "calculates cpu allocated metric" do
            expect(subject.cpu_allocated_metric).to eq(1.6666666666666667)
            expect(subject.cpu_allocated_cost).to eq(1200) # ?
          end
        end
      end

      context "Monthly" do
        let(:options) { base_options.merge(:interval => 'monthly') }
        before do
          add_metric_rollups_for(@vm1, month_beginning...month_end, 12.hours, metric_rollup_params)
        end

        subject { ChargebackVm.build_results_for_report_ChargebackVm(options).first.first }

        it "cpu" do
          expect(subject.cpu_allocated_metric).to eq(cpu_count)
          used_metric = used_average_for(:cpu_usagemhz_rate_average, hours_in_month, @vm1)
          expect(subject.cpu_used_metric).to be_within(0.01).of(used_metric)
          expect(subject.cpu_used_cost).to be_within(0.01).of(used_metric * hourly_rate * hours_in_month)
          expect(subject.cpu_allocated_cost).to be_within(0.01).of(cpu_count * count_hourly_rate * hours_in_month)
        end

        let(:fixed_rate) { 10.0 }

        context "fixed and variable rate" do
          before do
            set_tier_param_for(:derived_vm_numvcpus, :fixed_rate, fixed_rate)
            set_tier_param_for(:cpu_usagemhz_rate_average, :fixed_rate, fixed_rate)
          end

          it "cpu" do
            expect(subject.cpu_allocated_metric).to eq(cpu_count)
            used_metric = used_average_for(:cpu_usagemhz_rate_average, hours_in_month, @vm1)
            expect(subject.cpu_used_metric).to be_within(0.01).of(used_metric)

            fixed = fixed_rate * hours_in_month
            variable = cpu_count * count_hourly_rate * hours_in_month
            expect(subject.cpu_allocated_cost).to be_within(0.01).of(fixed + variable)

            fixed = fixed_rate * hours_in_month
            variable = used_metric * hourly_rate * hours_in_month
            expect(subject.cpu_used_cost).to be_within(0.01).of(fixed + variable)
          end
        end

        it "memory" do
          expect(subject.memory_allocated_metric).to eq(memory_available)
          used_metric = used_average_for(:derived_memory_used, hours_in_month, @vm1)
          expect(subject.memory_used_metric).to be_within(0.01).of(used_metric)

          memory_allocated_cost = memory_available * hourly_rate * hours_in_month
          expect(subject.memory_allocated_cost).to be_within(0.01).of(memory_allocated_cost)
          expect(subject.memory_used_cost).to be_within(0.01).of(used_metric * hourly_rate * hours_in_month)
          expect(subject.memory_cost).to eq(subject.memory_allocated_cost + subject.memory_used_cost)
        end

        it "disk io" do
          used_metric = used_average_for(:disk_usage_rate_average, hours_in_month, @vm1)
          expect(subject.disk_io_used_metric).to be_within(0.01).of(used_metric)
          expect(subject.disk_io_used_cost).to be_within(0.01).of(used_metric * hourly_rate * hours_in_month)
        end

        it "net io" do
          used_metric = used_average_for(:net_usage_rate_average, hours_in_month, @vm1)
          expect(subject.net_io_used_metric).to be_within(0.01).of(used_metric)
          expect(subject.net_io_used_cost).to be_within(0.01).of(used_metric * hourly_rate * hours_in_month)
        end

        it 'calculates metering used hours and cost' do
          expect(subject.metering_used_metric).to eq(hours_in_month)
          expect(subject.metering_used_cost).to eq(count_hourly_rate * hours_in_month)
        end

        context "fixed rates" do
          let(:hourly_fixed_rate) { 10.0 }

          before do
            set_tier_param_for(:derived_vm_used_disk_storage, :fixed_rate, hourly_fixed_rate)
            set_tier_param_for(:derived_vm_allocated_disk_storage, :fixed_rate, hourly_fixed_rate)

            set_tier_param_for(:derived_vm_used_disk_storage, :variable_rate, 0.0)
            set_tier_param_for(:derived_vm_allocated_disk_storage, :variable_rate, 0.0)
          end

          it "storage with only fixed rates" do
            expect(subject.storage_allocated_metric).to eq(vm_allocated_disk_storage.gigabytes)
            used_metric = used_average_for(:derived_vm_used_disk_storage, hours_in_month, @vm1)
            expect(subject.storage_used_metric).to be_within(0.01).of(used_metric)

            expected_value = hourly_fixed_rate * hours_in_month
            expect(subject.storage_allocated_cost).to be_within(0.01).of(expected_value)

            expected_value = hourly_fixed_rate * hours_in_month
            expect(subject.storage_used_cost).to be_within(0.01).of(expected_value)
            expect(subject.storage_cost).to eq(subject.storage_allocated_cost + subject.storage_used_cost)
          end
        end

        it "storage" do
          expect(subject.storage_allocated_metric).to eq(vm_allocated_disk_storage.gigabytes)
          used_metric = used_average_for(:derived_vm_used_disk_storage, hours_in_month, @vm1)
          expect(subject.storage_used_metric).to be_within(0.01).of(used_metric)

          expected_value = vm_allocated_disk_storage * count_hourly_rate * hours_in_month
          expect(subject.storage_allocated_cost).to be_within(0.01).of(expected_value)
          expected_value = used_metric / 1.gigabytes * count_hourly_rate * hours_in_month
          expect(subject.storage_used_cost).to be_within(0.01).of(expected_value)
          expect(subject.storage_cost).to eq(subject.storage_allocated_cost + subject.storage_used_cost)
        end

        context "by owner" do
          let(:user) { FactoryGirl.create(:user, :name => 'Test VM Owner', :userid => 'test_user') }
          let(:options) { {:interval_size => 4, :owner => user.userid, :ext_options => {:tz => 'Eastern Time (US & Canada)'} } }
          before do
            @vm1.update_attribute(:evm_owner, user)
          end

          it "valid" do
            expect(subject.owner_name).to eq(user.name)
          end

          it "not exist" do
            user.delete
            expect { subject }.to raise_error(MiqException::Error, "Unable to find user '#{user.userid}'")
          end
        end
      end

      describe "#get_rates" do
        let(:chargeback_rate)         { FactoryGirl.create(:chargeback_rate, :rate_type => "Compute") }
        let(:chargeback_vm)           { ChargebackVm.new }
        let(:rate_assignment_options) { {:cb_rate => chargeback_rate, :object => Tenant.root_tenant} }
        let(:metric_rollup) do
          FactoryGirl.create(:metric_rollup_vm_hr, :timestamp => report_run_time - 1.day - 17.hours,
                             :tag_names => "environment/prod",
                             :parent_host_id => @host1.id, :parent_ems_cluster_id => @ems_cluster.id,
                             :parent_ems_id => ems.id, :parent_storage_id => @storage.id,
                             :resource => @vm1)
        end
        let(:consumption) { Chargeback::ConsumptionWithRollups.new([metric_rollup], nil, nil) }

        before do
          ChargebackRate.set_assignments(:compute, [rate_assignment_options])
          @rate = Chargeback::RatesCache.new.get(consumption).first
          @assigned_rate = ChargebackRate.get_assignments("Compute").first
        end

        it "return tenant chargeback detail rate" do
          expect(@rate).not_to be_nil
          expect(@rate.id).to eq(@assigned_rate[:cb_rate].id)
        end
      end

      describe '.report_row_key' do
        let(:report_options) { Chargeback::ReportOptions.new }
        let(:timestamp_key) { 'Fri, 13 May 2016 10:40:00 UTC +00:00' }
        let(:beginning_of_day) { timestamp_key.in_time_zone.beginning_of_day }
        let(:metric_rollup) { FactoryGirl.build(:metric_rollup_vm_hr, :timestamp => timestamp_key, :resource => @vm1) }
        let(:consumption) { Chargeback::ConsumptionWithRollups.new([metric_rollup], nil, nil) }
        subject { described_class.report_row_key(consumption) }
        before do
          described_class.instance_variable_set(:@options, report_options)
        end

        it { is_expected.to eq("#{metric_rollup.resource_id}_#{beginning_of_day}") }
      end

      describe '#initialize' do
        let(:report_options) { Chargeback::ReportOptions.new }
        let(:vm_owners)     { {@vm1.id => @vm1.evm_owner_name} }
        let(:consumption) { Chargeback::ConsumptionWithRollups.new([metric_rollup], nil, nil) }
        let(:shared_extra_fields) do
          {'vm_name' => @vm1.name, 'owner_name' => admin.name, 'vm_uid' => 'ems_ref', 'vm_guid' => @vm1.guid,
           'vm_id' => @vm1.id}
        end
        subject { ChargebackVm.new(report_options, consumption).attributes }

        before do
          ChargebackVm.instance_variable_set(:@vm_owners, vm_owners)
        end

        context 'with parent ems' do
          let(:metric_rollup) do
            FactoryGirl.build(:metric_rollup_vm_hr, :tag_names => 'environment/prod',
                              :parent_host_id => @host1.id, :parent_ems_cluster_id => @ems_cluster.id,
                              :parent_ems_id => ems.id, :parent_storage_id => @storage.id,
                              :resource => @vm1, :resource_name => @vm1.name)
          end

          it 'sets extra fields' do
            is_expected.to include(shared_extra_fields.merge('provider_name' => ems.name, 'provider_uid' => ems.guid))
          end
        end

        context 'when parent ems is missing' do
          let(:metric_rollup) do
            FactoryGirl.build(:metric_rollup_vm_hr, :tag_names => 'environment/prod',
                              :parent_host_id => @host1.id, :parent_ems_cluster_id => @ems_cluster.id,
                              :parent_storage_id => @storage.id,
                              :resource => @vm1, :resource_name => @vm1.name)
          end

          it 'sets extra fields when parent ems is missing' do
            is_expected.to include(shared_extra_fields.merge('provider_name' => nil, 'provider_uid' => nil))
          end
        end
      end

      context 'more rates have been selected' do
        let(:storage_chargeback_rate_1) { FactoryGirl.create(:chargeback_rate, :rate_type => "Storage") }
        let(:storage_chargeback_rate_2) { FactoryGirl.create(:chargeback_rate, :rate_type => "Storage") }
        let(:chargeback_vm)             { Chargeback::RatesCache.new }

        let(:parent_classification) { FactoryGirl.create(:classification) }
        let(:classification_1)      { FactoryGirl.create(:classification, :parent_id => parent_classification.id) }
        let(:classification_2)      { FactoryGirl.create(:classification, :parent_id => parent_classification.id) }

        let(:rate_assignment_options_1) { {:cb_rate => storage_chargeback_rate_1, :tag => [classification_1, "Storage"]} }
        let(:rate_assignment_options_2) { {:cb_rate => storage_chargeback_rate_2, :tag => [classification_2, "Storage"]} }

        let(:metric_rollup) do
          FactoryGirl.create(:metric_rollup_vm_hr, :timestamp => report_run_time - 1.day - 17.hours,
                             :parent_host_id => @host1.id, :parent_ems_cluster_id => @ems_cluster.id,
                             :parent_ems_id => ems.id, :parent_storage_id => @storage.id,
                             :resource => @vm1)
        end
        let(:consumption) { Chargeback::ConsumptionWithRollups.new([metric_rollup], nil, nil) }

        before do
          @storage.tag_with([classification_1.tag.name, classification_2.tag.name], :ns => '*')
          ChargebackRate.set_assignments(:storage, [rate_assignment_options_1, rate_assignment_options_2])
        end

        it "return only one chargeback rate according to tag name of Vm" do
          [rate_assignment_options_1, rate_assignment_options_2].each do |rate_assignment|
            metric_rollup.tag_names = rate_assignment[:tag].first.tag.send(:name_path)
            uniq_rates = chargeback_vm.get(consumption)
            consumption.instance_variable_set(:@tag_names, nil)
            consumption.instance_variable_set(:@hash_features_affecting_rate, nil)
            expect([rate_assignment[:cb_rate]]).to match_array(uniq_rates)
          end
        end
      end

      context "Group by tags" do
        let(:options) { base_options.merge(:interval => 'monthly', :groupby_tag => 'environment') }
        before do
          add_metric_rollups_for(@vm1, month_beginning...month_end, 12.hours, metric_rollup_params)
        end

        subject { ChargebackVm.build_results_for_report_ChargebackVm(options).first.first }

        it "cpu" do
          expect(subject.cpu_allocated_metric).to eq(cpu_count)
          used_metric = used_average_for(:cpu_usagemhz_rate_average, hours_in_month, @vm1)
          expect(subject.cpu_used_metric).to be_within(0.01).of(used_metric)
          expect(subject.tag_name).to eq('Production')
        end
      end
    end

    context 'without metric rollups' do
      let(:cores)               { 7 }
      let(:mem_mb)              { 1777 }
      let(:disk_gb)             { 7 }
      let(:disk_b)              { disk_gb * 1024**3 }
      let(:metering_used_hours) { 24 }

      let(:hardware) do
        FactoryGirl.build(:hardware,
                          :cpu_total_cores => cores,
                          :memory_mb       => mem_mb,
                          :disks           => [FactoryGirl.build(:disk, :size => disk_b)])
      end

      let(:fixed_cost) { hourly_rate * 24 }
      let(:mem_cost) { mem_mb * hourly_rate * 24 }
      let(:cpu_cost) { cores * count_hourly_rate * 24 }
      let(:disk_cost) { disk_gb * count_hourly_rate * 24 }
      let(:metering_used_cost) { metering_used_hours * count_hourly_rate }

      context 'for SCVMM (hyper-v)' do
        let!(:vm1) do
          vm = FactoryGirl.create(:vm_microsoft, :hardware => hardware, :created_on => report_run_time - 1.day)
          vm.tag_with(@tag.name, :ns => '*')
          vm
        end

        let(:options) { base_options.merge(:interval => 'daily') }

        subject { ChargebackVm.build_results_for_report_ChargebackVm(options).first.first }

        it 'fixed compute is calculated properly' do
          expect(subject.chargeback_rates).to eq(chargeback_rate.description)
          expect(subject.fixed_compute_metric).to eq(1) # One day of fixed compute metric
          expect(subject.fixed_compute_1_cost).to eq(fixed_cost)
        end

        it 'allocated metrics are calculated properly' do
          expect(subject.memory_allocated_metric).to  eq(mem_mb)
          expect(subject.memory_allocated_cost).to    eq(mem_cost)
          expect(subject.metering_used_metric).to  eq(metering_used_hours)
          expect(subject.metering_used_cost).to    eq(metering_used_cost)
          expect(subject.cpu_allocated_metric).to     eq(cores)
          expect(subject.cpu_allocated_cost).to       eq(cpu_cost)
          expect(subject.storage_allocated_metric).to eq(disk_b)
          expect(subject.storage_allocated_cost).to   eq(disk_cost)
          expect(subject.total_cost).to               eq(fixed_cost + cpu_cost + mem_cost + disk_cost + metering_used_cost)
        end
      end

      context 'for any virtual machine' do
        let!(:vm1) do
          vm = FactoryGirl.create(:vm_vmware, :hardware => hardware, :created_on => report_run_time - 1.day)
          vm.tag_with(@tag.name, :ns => '*')
          vm
        end

        subject { ChargebackVm.build_results_for_report_ChargebackVm(options).first.first }

        let(:options) { base_options.merge(:interval => 'daily', :include_metrics => false) }

        it 'fixed compute is calculated properly' do
          expect(subject.chargeback_rates).to eq(chargeback_rate.description)
          expect(subject.fixed_compute_metric).to eq(1) # One day of fixed compute metric
          expect(subject.fixed_compute_1_cost).to eq(fixed_cost)
        end

        it 'metrics are calculated properly' do
          expect(subject.memory_allocated_metric).to  eq(mem_mb)
          expect(subject.memory_allocated_cost).to    eq(mem_cost)
          expect(subject.metering_used_metric).to  eq(metering_used_hours)
          expect(subject.metering_used_cost).to    eq(metering_used_cost)
          expect(subject.cpu_allocated_metric).to     eq(cores)
          expect(subject.cpu_allocated_cost).to       eq(cpu_cost)
          expect(subject.storage_allocated_metric).to eq(disk_b)
          expect(subject.storage_allocated_cost).to   eq(disk_cost)

          expect(subject.total_cost).to               eq(fixed_cost + cpu_cost + mem_cost + disk_cost + metering_used_cost)
        end

        context 'metrics are included (but dont have any)' do
          it 'is not generating report with options[:include_metrics]=true' do
            options[:include_metrics] = true
            expect(subject).to be_nil
          end

          it 'is not generating report with options[:include_metrics]=nil(default value)' do
            options[:include_metrics] = nil
            expect(subject).to be_nil
          end
        end
      end
    end
  end

  context "Old Chargeback" do
    include_examples "ChargebackVm"
  end

  context "New Chargeback" do
    before do
      ManageIQ::Showback::InputMeasure.seed

      stub_settings(:new_chargeback => '1')
    end

    include_examples "ChargebackVm"
  end
end


text:
New to the command-line world? Or just a little rusty?
Or, like me, you can't always remember the arguments to `lsof` or `tar`?

Maybe it doesn't help that the first option explained in `man tar` is:

```
-b blocksize
   Specify the block size, in 512-byte records, for tape drive I/O.
   As a rule, this argument is only needed when reading from or writing to tape drives,
   and usually not even then as the default block size of 20 records (10240 bytes) is very common.
```

I'm sure people could benefit from simplified "show me the common usages" man pages. What about:

![tldr screenshot](http://raw.github.com/rprieto/tldr/master/screenshot.png)

This repository is just that: an ever-growing collection of examples for the most common UNIX / Linux / OSX / SunOS commands.

## Clients

You can access these pages on your computer using one of the following clients:

- [Node.js client](https://github.com/rprieto/tldr-node-client) : `npm install -g tldr`
- [Python client](https://github.com/felixonmars/tldr-python-client)

Let us know if you are building one and we can add it to this list!

## Contributing

- Your favourite command isn't covered?
- You can think of more examples?

Contribution are most welcome! Have a look [over here](https://github.com/rprieto/tldr/blob/master/CONTRIBUTING.md) for some rough guidelines.

redpajama_set_name: RedPajamaGithub


text:
#ifndef GCLIST_H
#define GCLIST_H

/* design taken from the linux double linked list implementation in include/linux/list.h  */

typedef struct GCList {
  struct GCList *next;
  struct GCList *prev;
} GCList;

#define GCLIST_HEAD(name) GCList name = { &(name), &(name) }

static inline void gclist_init(GCList *list) {
  list->next = list;
  list->prev = list;
}

static inline void __gclist_add(GCList *item, GCList *prev, GCList *next) {
  next->prev = item;
  item->next = next;
  item->prev = prev;
  prev->next = item;
}

static inline void gclist_add(GCList *head, GCList *item) {
  __gclist_add(item, head, head->next);
}

static inline void gclist_add_tail(GCList *head, GCList *item) {
  __gclist_add(item, head->prev, head);
}

static inline void __gclist_del(GCList *prev, GCList *next) {
  next->prev = prev;
  prev->next = next;
}

#define GCLIST_POISON1 ((void *)(0x00100100))
#define GCLIST_POISON2 ((void *)(0x00200200))
static inline void gclist_del(GCList *item) {
  __gclist_del(item->prev, item->next);
  item->next = GCLIST_POISON1;
  item->prev = GCLIST_POISON2;
}

static inline int gclist_is_last(GCList *head, GCList *list) {
  return list->next == head;
}

static inline int gclist_is_empty(GCList *head) {
  return head->next == head;
}

static inline void gclist_move(GCList *list, GCList *head) {
  __gclist_del(list->prev, list->next);
  gclist_add(head, list);
}

static inline void gclist_move_tail(GCList *list, GCList *head) {
  __gclist_del(list->prev, list->next);
  gclist_add(head, list);
}

static inline void __gclist_splice(GCList *item, GCList *prev, GCList *next) {
  abort();
}

static inline void gclist_splice(GCList *head, GCList *list) {
  if(!gclist_is_empty(list)) { __gclist_splice(list, head, head->next); }
}

#define gclist_item(ptr, type, member) \
  ((type*) (((void*)(ptr)) - ((void *) offsetof(type, member))))

#define gclist_first_item(head, type, member) \
  gclist_item((head)->next, type, member)

#define gclist_each_item(pos, head, type, member)         \
  for (pos = gclist_item((head)->next, type, member);     \
       &pos->member != (head);                            \
       pos = gclist_item(pos->member.next, type, member))

#define gclist_each_item_safe(pos, n, head, type, member)   \
  for (pos = gclist_item((head)->next, type, member),       \
       n   = gclist_item(pos->member.next, type, member);   \
       &pos->member != (head);                              \
       pos = n,                                             \
       n = gclist_item(n->member.next, type, member))

#endif

/* merge sort */
typedef int (*GCListCmp)(void *priv, GCList *a, GCList *b);
#define MAX_LIST_LENGTH_BITS 20

static GCList *merge(void *priv, GCListCmp cmp, GCList *a, GCList *b) {
  GCList head;
  GCList *tail = &head;

  while(a && b) {
    if ((*cmp)(priv, a, b) <= 0) {
      tail->next = a;
      a = a->next;
    }
    else {
      tail->next = b;
      b = b->next;
    }
    tail = tail->next;
  };

  tail->next = a?:b;
  return head.next;
}

static void merge_and_restore_back_links(void *priv, GCListCmp cmp, GCList *head, GCList *a, GCList *b) {
  GCList *tail = head;
  while(a && b) {
    if ((*cmp)(priv, a, b) <= 0) {
      tail->next = a;
      a->prev = tail;
      a = a->next;
    }
    else {
      tail->next = b;
      b->prev = tail;
      b = b->next;
    }
    tail = tail->next;
  }

  tail->next = a?:b;

  do {
    tail->next->prev = tail;
    tail = tail->next;
  } while(tail->next);

  tail->next = head;
  head->prev = tail;
}

static void gclist_sort(void *priv, GCList *head, GCListCmp cmp) {
  GCList *part[MAX_LIST_LENGTH_BITS+1];
  int level; /* index into part[] */
  int max_level = 0;
  GCList *list;

  if (gclist_is_empty(head)) return;

  memset(part, 0, sizeof(part));

  head->prev->next = NULL; /* set end of list NULL */
  list = head->next; /* set list to first item in list */

  while(list) {
    GCList *cur = list;
    list = list->next;
    cur->next = NULL;

    for (level = 0; part[level]; level++) {
      cur = merge(priv, cmp, part[level], cur);
      part[level] = NULL;
    }
    if (level > max_level) {
      if (level > MAX_LIST_LENGTH_BITS) {
        printf("GCList is too long to sort");
        abort();
      }
      max_level = level;
    }
    part[level] = cur;
  }

  for (level = 0; level < max_level; level ++) {
    if (part[level]) {
      list = merge(priv, cmp, part[level], list);
    }
  }

  merge_and_restore_back_links(priv, cmp, head, part[max_level], list);
}


text:
%Notes by Harsh Mistry
%Stat230
%based on Template from : https://www.cs.cmu.edu/~ggordon/10725-F12/template.tex

\documentclass{article}
\setlength{\oddsidemargin}{0.25 in}
\setlength{\evensidemargin}{-0.25 in}
\setlength{\topmargin}{-0.6 in}
\setlength{\textwidth}{6.5 in}
\setlength{\textheight}{8.5 in}
\setlength{\headsep}{0.75 in}
\setlength{\parindent}{0 in}
\setlength{\parskip}{0.1 in}
\usepackage{amsfonts,graphicx, amssymb}
\usepackage[fleqn]{amsmath}
\usepackage{fixltx2e}
\usepackage{color}
\usepackage{tcolorbox}
\usepackage{lipsum}
\usepackage{listings}
\usepackage{scrextend}
\tcbuselibrary{skins,breakable}
\usetikzlibrary{shadings,shadows}
\newcounter{lecnum}
\renewcommand{\thepage}{\thelecnum-\arabic{page}}
\renewcommand{\thesection}{\thelecnum.\arabic{section}}
\renewcommand{\theequation}{\thelecnum.\arabic{equation}}
\renewcommand{\thefigure}{\thelecnum.\arabic{figure}}
\renewcommand{\thetable}{\thelecnum.\arabic{table}}
\newcommand{\lecture}[4]{
   \pagestyle{myheadings}
   \thispagestyle{plain}
   \newpage
   \setcounter{lecnum}{#1}
   \setcounter{page}{1}

%Info Box
   \begin{center}
   \framebox{
      \vbox{\vspace{2mm}
    \hbox to 6.28in { {\bf Stat 230 - Probability
	\hfill Fall 2016} }
       \vspace{4mm}
       \hbox to 6.28in { {\Large \hfill Lecture #1: #2  \hfill} }
       \vspace{2mm}
       \hbox to 6.28in { {\it Lecturer: #3 \hfill Notes By: #4} }
      \vspace{2mm}}
   }
   \end{center}

   \markboth{Lecture #1: #2}{Lecture #1: #2}

}

\renewcommand{\cite}[1]{[#1]}
\def\beginrefs{\begin{list}%
        {[\arabic{equation}]}{\usecounter{equation}
         \setlength{\leftmargin}{2.0truecm}\setlength{\labelsep}{0.4truecm}%
         \setlength{\labelwidth}{1.6truecm}}}
\def\endrefs{\end{list}}
\def\bibentry#1{\item[\hbox{[#1]}]}

\newcommand{\fig}[3]{
			\vspace{#2}
			\begin{center}
			Figure \thelecnum.#1:~#3
			\end{center}
	}

\newcommand{\pipe}{\(\mid\)}
\newcommand{\ctr}{\(\wedge\)}

\newtheorem{theorem}{Theorem}[lecnum]
\newtheorem{lemma}[theorem]{Lemma}
\newtheorem{ex}[theorem]{Example}
\newtheorem{proposition}[theorem]{Proposition}
\newtheorem{claim}[theorem]{Claim}
\newtheorem{corollary}[theorem]{Corollary}
\newtheorem{definition}[theorem]{Definition}
\newenvironment{proof}{{\bf Proof:}}{\hfill\rule{2mm}{2mm}}
\newcommand\E{\mathbb{E}}

%color definitions :
\definecolor{darkred}{rgb}{0.55, 0.0, 0.0}
\definecolor{lightcoral}{rgb}{0.94, 0.5, 0.5}
\definecolor{tomato}{rgb}{1.0, 0.39, 0.28}
\definecolor{lightgray}{rgb}{.9,.9,.9}
\definecolor{darkgray}{rgb}{.4,.4,.4}
\definecolor{purple}{rgb}{0.65, 0.12, 0.82}
\definecolor{lightgreen}{rgb}{0.56, 0.93, 0.56}
\definecolor{darkgreen}{rgb}{0.0, 0.2, 0.13}
\definecolor{limegreen}{rgb}{0.2, 0.8, 0.2}
\definecolor{lightblue}{rgb}{0.68, 0.85, 0.9}
\definecolor{darkblue}{rgb}{0.0, 0.0, 0.55}

%Environments
\newenvironment{exblock}[1]{%
    \tcolorbox[beamer,%
    noparskip,breakable,
    colback=lightgreen,colframe=darkgreen,%
    colbacklower=limegreen!75!lightgreen,%
    title=#1]}%
    {\endtcolorbox}

\newenvironment{ablock}[1]{%
    \tcolorbox[beamer,%
    noparskip,breakable,
    colback=lightcoral,colframe=darkred,%
    colbacklower=tomato!75!lightcoral,%
    title=#1]}%
    {\endtcolorbox}

\newenvironment{cblock}[1]{%
    \tcolorbox[beamer,%
    noparskip,breakable,
    colback=lightblue,colframe=darkblue,%
    colbacklower=darkblue!75!lightblue,%
    title=#1]}%
    {\endtcolorbox}

%Languages
\lstdefinelanguage{JavaScript}{
  keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
  keywordstyle=\color{blue}\bfseries,
  ndkeywords={class, export, boolean, throw, implements, import, this},
  ndkeywordstyle=\color{darkgray}\bfseries,
  identifierstyle=\color{black},
  sensitive=false,
  comment=[l]{//},
  morecomment=[s]{/*}{*/},
  commentstyle=\color{purple}\ttfamily,
  stringstyle=\color{red}\ttfamily,
  morestring=[b]',
  morestring=[b]"
}

%Listings
\lstset{
   language=JavaScript,
   backgroundcolor=\color{lightgray},
   extendedchars=true,
   basicstyle=\footnotesize\ttfamily,
   showstringspaces=false,
   showspaces=false,
   numbers=left,
   numberstyle=\footnotesize,
   numbersep=9pt,
   tabsize=2,
   breaklines=true,
   showtabs=false,
   captionpos=b
}

%Start of Document
\begin{document}

\lecture{1}{September 9, 2016}{Nagham Mohammad}{Harsh Mistry}

\section{Administrative Information}
\begin{center}
Lecturer : \textbf{Nagham Mohammad} \\
Office : \textbf{M3 3112 } \\
Office Hours : \textbf{3:30pm  - 5:30pm Monday} \\
Email : \textbf{n37mohammad@uwaterloo.ca}
\end{center}

\section{Introduction to Probability}

\begin{exblock}{Thought Questions}
\begin{itemize}
\item If you flip a fair coin and do it once, what is the probability that it will land heads up?
\begin{itemize}
\item 50%
\end{itemize}
\item What is the probability that you will eventually own a home?
\begin{itemize}
\item Unknown
\end{itemize}
\end{itemize}
\end{exblock}

\subsection{What does probability mean?}

\begin{itemize}
\item The term \textbf{probability} refers to the study of randomness (variability of results).

\item Probability is used to quantify the likelihood, or chance, that an outcome of random experiment will occur.

 \item By variability, we mean successive observations of a system or phenomenon do \textbf{not} produce exactly the same result.
\end{itemize}

\begin{center}
\textbf{In addition to what is above, nothing else was discussed in class. Majority of the time was used to go over administrative information.}
\end{center}

\end{document}


text:
@implementation UserReceivedEventRepoModel
+ (UserReceivedEventRepoModel *)modelWithDict:(NSDictionary *)dict
{
    if (!dict) {
        return Nil;
    }

    UserReceivedEventRepoModel *model = [[UserReceivedEventRepoModel alloc]init];

    model.name = [dict objectForKey:@"name"] ;
    model.userReceivedEventRepoID = [dict objectForKey:@"id"] ;
    model.url = [dict objectForKey:@"url"] ;

    return model;
}
@end


text:
.. include:: ../../../Includes.txt

.. _reference-filters-digit:

=====
digit
=====

Removes all characters which are not in the range 0-9.


text:
using WebSignalR.Common.Entities;

namespace WebSignalR.DataAccess.Mappings
{
	public class UserSessionMap : MapBase<UserSession>
	{
		public UserSessionMap()
			: base()
		{
			this.ToTable("UserSessions");
			this.Property(x => x.SessionId).HasColumnName("SessionId").IsRequired();
			this.Property(x => x.UserAgent).HasColumnName("UserAgent").IsOptional();
			this.Property(x => x.LastActivity).HasColumnName("LastActivity").IsOptional();
			this.HasRequired(x => x.User).WithMany(x => x.ConnectedSessions).HasForeignKey(k => k.UserId);
			//this.HasRequired(x => x.User).WithMany(x => x.ConnectedSessions).HasForeignKey(k => k.UserId).WillCascadeOnDelete();

			this.HasOptional(us => us.SessionRoom).WithRequired(sr => sr.Session).WillCascadeOnDelete();
		}
	}
}


text:
require 'awesome_print'
require 'currentuser/data/models/project_id_repository'
require 'currentuser/data/models/base_resource'
require 'currentuser/data/models/project'
require 'currentuser/data/models/user'


text:
{% extends 'base.html' %}
{% load i18n %}
{% load static %}

{% block css %}
<link href="{% static 'css/main.css' %}" rel="stylesheet">
{% endblock %}

{% block app_body %}

{% blocktrans %}500 - Server Error{% endblocktrans %}

{% if request.sentry.id %}

{% blocktrans with ref=request.sentry.id %}If you need assistance, you may reference this error as {{ ref }}.{% endblocktrans %}

{% endif %}
{% endblock %}


text:
from .test_base import TestBase
from werkzeug.exceptions import NotFound, MethodNotAllowed
from app.exceptions import ValidationError

class TestEndpoints(TestBase):
    """ Test the API endpoints """

    def test_api_index(self):
        res, json = self.client.get('/api/v1')
        self.assertEqual(res.status_code, 200)

    def test_requests_with_no_token(self):
        """ Test that tokens are required for secured endpoints """
        res1, json1 = self.client.get('/api/v1/bucketlists/',
                                      headers={'Authorization': ''})
        self.assertEqual(res1.status_code, 401)
        self.assertEqual(
            json1['message'],
            "please send your authentication token"
        )

        res2, json2 = self.client.put('/api/v1/bucketlists/1',
                                      data={'name': 'Update BList'},
                                      headers={'Authorization': ''})
        self.assertEqual(res2.status_code, 401)
        self.assertEqual(
            json2['message'],
            "please send your authentication token"
        )

        res3, json3 = self.client.post("/api/v1/bucketlists/2/items/",
                                       data={"title": "Ndoo1",
                                             "description": "Jaza ndoo maji"},
                                       headers={'Authorization': ''})
        self.assertEqual(res3.status_code, 401)
        self.assertIn(
            "please send your authentication token",
            json3["message"]
        )

    def test_requests_with_invalid_tokens(self):
        res1, json1 = self.client.post('/api/v1/bucketlists/3/items/',
                                       data={'name': 'New BList item'},
                                       headers={
                                            'Authorization': 'inVal1D^TokEN'
                                            })
        self.assertEqual(res1.status_code, 401)
        self.assertEqual(
            json1['message'],
            "please send your authentication token"
        )

        res2, json2 = self.client.delete('/api/v1/bucketlists/2/items/3',
                                         headers={
                                            'Authorization': 'inVal1D^TokEN'
                                            })
        self.assertEqual(res2.status_code, 401)
        self.assertEqual(
            json2['message'],
            "please send your authentication token"
        )

    def test_invalid_urls(self):
        with self.assertRaises(NotFound):
            self.client.post('/api/v1/bucketlist/3/items/',
                             data={'name': 'New BList item'})
        with self.assertRaises(NotFound):
            self.client.put('/api/v1/bucketlists//2/items/3',
                            data={'name': 'New BList item'})
        with self.assertRaises(NotFound):
            self.client.get('/api/v1/bucketlists//')

    def test_invalid_query_parameters(self):
        with self.assertRaises(ValidationError):
            self.client.get('/api/v1/bucketlists/?limitt=20')
        with self.assertRaises(ValidationError):
            self.client.get('/api/v1/bucketlists/?s=blist')
        with self.assertRaises(ValidationError):
            self.client.get('/api/v1/bucketlists/?limit=abc')

    def test_allowed_url_methods(self):
        with self.assertRaises(MethodNotAllowed):
            self.client.put('/api/v1/bucketlists/3/items/',
                            data={'name': 'New BList item'})
        with self.assertRaises(MethodNotAllowed):
            self.client.post('/api/v1/bucketlists/2/items/3',
                             data={'name': 'New BList item'})
        with self.assertRaises(MethodNotAllowed):
            self.client.delete('/api/v1/bucketlists/2/items/')

    def test_malformed_post_and_put_requests(self):
        with self.assertRaises(ValidationError):
            self.client.post('/api/v1/bucketlists/',
                             data={'title': 'New BList item'})
        with self.assertRaises(ValidationError):
            self.client.put('/api/v1/bucketlists/1',
                            data={'title': 'Updated BList'})
        with self.assertRaises(TypeError):
            self.client.delete('/api/v1/bucketlists',
                               data={'description': 'dance time'})
        with self.assertRaises(TypeError):
            self.client.delete('/api/v1/bucketlists/2/items/',
                               data={"nam": "Ndoo3",
                                     "description": "Maji ya kufua"})


text:
'Need a list of all sources we need to make files for
DONE - Create non-duplicate list of all sources within Validation Form

'Loop to be completed for each source.
DONE - Prompt User for client naming abbreviation and save input to variable 'ex. MEDC or NBRO_FL

DONE - Create new folder in users documents section
    Title folder UserNamingInput + " " + PCST Files

For each source

Open a new excel file

  Assign current source FULL name to a variable

  Create short name of source FULL name
    (example - urn:cerner:coding:codingsystem:codeset:72 -> codeset:72)
    'Short name is determined by all text following the ":"
    'We can not use FULL name for everything because the FULL name will not fit on the tabs. The length is too long so we need a short name as reference.

  Create variable and store code system ID = urn:cerner:coding:codingsystem:nomenclature.source_vocab:PTCARE 'This is for easy lookup and maintenance
  Create variable and store code system id = urn:cerner:coding:codingsystem:codeset:72 'This is for easy lookup and maintenance

  Create new tabs with the following names
      a. Index Sheet
      b. Unmapped Codes
      c. Clinical Documentation
      d. Health Maintenance Summary
      e. codeset:72
      f. Nomenclature - Patient Care

   Copy the following tabs to the new file
      a. Unmapped Codes
      b. Clinical Documentation
      c. Health Maintenance Summary 'Will need to create way to standardize format of this sheet. Currently there are multiple "tables" or groups of information in the same sheet. Look into creating dynamic ranges.

  Filter The following tabs to display the current source
      a. Unmapped Codes
      b. Clinical Documentation

'Remove duplicates from main data sheets

       Navigate to Unmapped Codes Sheet
         Format All Cells As tables
         Filter to Remove Blank lines
         Remove Duplicates 'Use Raw Code AND Raw Display columns

       Navigate to Clinical Documentation Sheet
         Format All Cells As tables
         Filter to Remove Blank lines
         Remove Duplicates 'Use EventCode AND EventDisplay columns

       Navigate to Health Maintenance Summary Sheet
         Convert Automatic Satisfiers with missing mappings rows to Table
         Filter to Remove Blank lines
         Remove Duplicates 'Use EVENT_CD AND EVENT_CD_DISP columns

'Populate Nomenclature - Patient Care Sheet
   For Nomenclature - Patient Care

       Navigate to Clinical Documentation Sheet
         Filter to remove blank lines
         Copy all results to the Nomenclature - Patient Care sheet. 'Make sure to align columns. It is likely each column will need to be copied individually.

       Navigate to unmapped codes sheet.
         Filter to only display code system id which contains PTCARE
         Filter to remove blank lines 'Check by code system id column
         Copy results to end of Nomenclature - Patient Cwoare sheet 'Notice results will already be on the sheet. You will need to determine next free blank row and start there. NOTICE - The columns will not directly match up. Expect to paste each column individually to make sure it goes in the correct spot.

       Navigate back to unmapped Codes Sheet
         Remove Code System ID Filter

'Populate codeset:72 sheet
   For codeset:72

       Navigate to Unmapped Codes Sheet
         Filter sheet to codeset:72 'Use full name
         Filter to remove blanks 'Filter by code system id column
           'Use IF THEN statement to negate error incase there are no blanks.
         Copy results to the codeset:72 sheet. 'Again columns may not line up perflectly. Copy columns one at a time to ensure correct placement.

       Navigate to Unmapped Codes Sheet
         Remove code system id Filter

'Taylor what else needs to get filtered out of clinical documentation besides "Free Text"?
       Navigate to Clinical Documentation Sheet
         Filter ControlType column to remove "Free Text" 'will need IF THEN statement to negate error incase free text does not exist.
         Filter to EventCode column to hide blanks 'Will need IF THEN statement to negate error incase there are no blanks.
         Copy results to the codeset:72 sheet. 'Columns will NOT line up. Copy columns one at a time. There will be results on the page, determine first blank line and begin entry at that point.

       Navigate to Clinical Documentation Sheet
         Remove filter from EventCode column

       Navigate to Health Maintenance Summary Sheet
         Create named range for AUTOMATIC satisfiers with missing mappings 'needed to identify correct rows go to the correct place
         Filter sheet to remove any blank lines
         Copy results to codeSet:72 sheet. 'Columns will NOT line up. Copy columns one at a time. There will be results on the page, determine first blank line and begin entry at that point.

       Navigate to Health Maintenance Summary Sheet
         Remove any filters

'Populate the remaining sheets
   For each Code System ID that IS NOT codeset 72 OR PTCARE OR on the Blacklist Then 'Codeset 72 and PTCare have different rules and are grouped with data from multiple tabs.

     Create new sheet 'Use Short Name ex. (codeset:72)

       Navigate to Unmapped Codes Sheet
         Filter data by current Code system ID 'Full name
         Copy results to the correct sheet 'Should be the short name of the current code system id
           'Columns may not match up perfectly. Copy each column one at a time to ensure correct location.
       Navigate to Unmapped Code sheet
         Remove Code system ID Filter

     Next Code 'Next code system ID not already laid out and "Sheeted"

'Delete Extra sheets
  Delete Unmapped Codes Sheet
  Delete Clinical Documentation Sheet
  Delete Health Maintenane Summary Sheet

'Save File After all codes populated
  Save excel file within the folder created
  Name file UserNamingInput + " " + Source + " " + PCST

'This source is finished Repeat all of the above with the next Source
Next source


text:
import { Type, Mode, Constructor, IRawRepo, Hash, encode } from '@es-git/core';
import { IObjectRepo, Person, ModeHash } from '@es-git/object-mixin';

export type Folder = {
  readonly files? : {
    readonly [key : string] : File | TextFile | ExistingFile;
  }
  readonly folders? : {
    readonly [key : string] : Folder | Hash;
  }
}

export type File = {
  readonly isExecutable? : boolean
  readonly body : Uint8Array
}

export type TextFile = {
  readonly isExecutable? : boolean
  readonly text : string
}

export type ExistingFile = {
  readonly isExecutable? : boolean
  readonly hash : Hash
}

export { Hash };

export interface ICommitRepo {
  commit(ref : string, tree : Folder, message : string, author : Person, committer? : Person) : Promise<Hash>
  saveTree(folder : Folder | Hash) : Promise<Hash>
}

export default function commitMixin<T extends Constructor<IObjectRepo & IRawRepo>>(repo : T) : Constructor<ICommitRepo> & T {
  return class CommitRepo extends repo implements ICommitRepo {
    constructor(...args : any[]){
      super(...args);
    }

    async commit(ref : string, tree : Folder, message : string, author : Person, committer = author) : Promise<Hash> {
      const originalHash = await super.getRef(ref);
      if(!originalHash) throw new Error(`Unknown ref ${ref}`);

      const treeHash = await this.saveTree(tree);

      const hash = await super.saveObject({
        type: Type.commit,
        body:{
          author,
          committer,
          message,
          parents: [originalHash],
          tree: treeHash
        }
      });

      await super.setRef(ref, hash);
      return hash;
    }

    async saveTree(folder : Folder | Hash) : Promise<Hash> {
      if(typeof(folder) === 'string') return folder;

      const body : {[key : string] : ModeHash} = {};

      if(folder.folders){
        for(const name of Object.keys(folder.folders)){
          const hash = await this.saveTree(folder.folders[name]);
          body[name] = {hash, mode: Mode.tree};
        }
      }

      if(folder.files){
        for(const name of Object.keys(folder.files)){
          const hash = await this.saveFile(folder.files[name]);
          body[name] = {hash, mode: folder.files[name].isExecutable ? Mode.exec : Mode.file};
        }
      }

      return await super.saveObject({
        type: Type.tree,
        body: body
      });
    }

    async saveFile(file : File | TextFile | ExistingFile) : Promise<Hash>{
      if(isHash(file)) return file.hash;

      if(isText(file))
        return await super.saveObject({type: Type.blob, body: encode(file.text)});
      else
        return await super.saveObject({type: Type.blob, body: file.body});
    }
  }
}

function isHash(file : File | TextFile | ExistingFile) : file is ExistingFile {
  return 'hash' in file;
}

function isText(file : File | TextFile) : file is TextFile {
  return 'text' in file;
}


text:
package OptimizationTests.ShortMethodsInliningNonVirtualInvokes.InvokeDirectAObjectThrowNullSet_002;
// The test checks that stack after NullPointerException occurs is correct despite inlining
class Foo {
    public int i;
    public Foo(int k) {
        this.i = k;
    }
}


text:
import point from '../app/point';

describe('point', function() {
  it('takes x and y coordinates', function() {
    const p = point(2, 6);
    expect(p.x).toBe(2);
  });

  describe('#distanceTo', function() {
    let p1, p2, p3;

    beforeEach(function() {
      p1 = point(2, 6);
      p2 = point(4, 6);
      p3 = point(5, 2);
    });

    it('finds the distance between two level points', function() {
      expect(p1.distanceTo(p2)).toBe(2);
    });

    it('returns a positive value even when points are switched', function() {
      expect(p2.distanceTo(p1)).toBe(2);
    });

    it('finds the distance between two uneven points', function() {
      expect(p1.distanceTo(p3)).toBe(5);
    });
  });
});


text:
$timeout(function() {
    // ...
}, 1000)

// example - valid: false, errorMessage: "You should use the $timeout service instead of the default window.setTimeout method"
setTimeout(function() {
    // ...
}, 1000)

// example - valid: false, errorMessage: "You should use the $timeout service instead of the default window.setTimeout method"
window.setTimeout(function() {
    // ...
}, 1000)


text:
<?xml version="1.0" encoding="UTF-8"?><concept xmlns="http://www.w3.org/2004/02/skos/core#">
      <prefLabel>CONTINUING CARE RETIREMENT COMMUNITY (CCRC)</prefLabel>
      <definition>A housing community that provides different levels of care based on what each
         resident needs over time. This is sometimes called "life care" and can range from
         independent living in an apartment to assisted living to full-time care in a nursing home.
         Residents move from one setting to another based on their needs but continue to live as
         part of the community. Care in CCRCs is usually expensive. Generally, CCRCs require a large
         payment before you move in and charge monthly fees.</definition>
   </concept>


text:
/*
 Angular ui select validator (at least one value is required)
 Taken from here https://github.com/angular-ui/ui-select/issues/258
 */

try { angular.module('Web.Components') } catch(err) { angular.module('Web.Components', []) }

angular.module('Web.Components')
    .directive('comUiSelectRequired', function () {
        return {
            require: 'ngModel',
            link: function (scope, elm, attrs, ctrl)
            {
                var validator = function (modelValue, viewValue) {
                    var determineVal;
                    // Multiselect
                    if (angular.isArray(modelValue)) {
                        determineVal = modelValue;
                    } else if (angular.isArray(viewValue)) {
                        determineVal = viewValue;
                    } else {
                        // Regular single select
                        if (!angular.isArray(modelValue) && typeof(modelValue) !== 'undefined' && modelValue != null) {
                            return true;
                        } else {
                            return false;
                        }
                    }

                    return determineVal.length > 0;
                };

                ctrl.$validators.required = validator;
            }
        };
    });


text:
package com.buddysearch.android.presentation.mvp.view;

import com.buddysearch.android.library.presentation.mvp.view.View;
import com.buddysearch.android.presentation.mvp.model.UserModel;

import java.util.List;

public interface UsersView extends View {
    void renderCurrentUser(UserModel user);

    void renderUsers(List<UserModel> users);

    void navigateToSplash();

    void navigateToDialog(String peerId);

    void navigateToEditProfile();
}


text:
scripts loaded dynamically using [require.js](http://requirejs.org/)

Setup dependencies with following commands:

```
$ npm install
```

In order to build widget zip, run:

```
$ grunt
```

After command run output zip archive will be created in `dist` folder. This archive can be uploaded to [Sandbox](https://www.appsngen.com/product/my/applications/list).

## License

MIT


text:
package cs263w16;

import java.util.*;
import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;

import com.google.appengine.api.datastore.*;
import com.google.appengine.api.memcache.*;

import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;
import javax.ws.rs.Produces;
import javax.ws.rs.FormParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBElement;

@Path("/blobstoreworker")
public class BlobstoreWorker {
  @POST
  @Path("/profilephotoworker")
  public void newProfilePhoto(
      @Context HttpServletRequest request
    ) throws Exception {

    System.out.println("I am here!!!!!!!!!!!!!!!!!!!!!!!");

    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

        String userId = request.getParameter("userId");
        String profileName = request.getParameter("profileName");
        String blobKey = request.getParameter("blobKey");

        Key userKey = KeyFactory.createKey("User", userId);
        Key seekerInfoKey = KeyFactory.createKey(userKey, "SeekerInfo", profileName);

        Entity entity = datastore.get(seekerInfoKey);
        entity.setProperty("profilePhoto", blobKey);
        datastore.put(entity);

        System.out.println("userId" + userId + "\nprofileName" + profileName + "\nblobKey" + blobKey);

    }

}


text:
<?php // heal.php :: Handles stuff from the Quick Spells menu. (Healing spells only... other spells are handled in fight.php.)

function healspells($id) {

    global $userrow;
    $check = protectcsfr();
    $userspells = explode(",",$userrow["spells"]);
    $spellquery = doquery($link, "SELECT * FROM {{table}} WHERE id='$id' LIMIT 1", "spells");
    $spellrow = mysqli_fetch_array($spellquery);

    // All the various ways to error out.
    $spell = false;
    foreach ($userspells as $a => $b) {
        if ($b == $id) { $spell = true; }
    }
    if ($spell != true) { display("You have not yet learned this spell. Please go back and try again.", "Error"); die(); }
    if ($spellrow["type"] != 1) { display("This is not a healing spell. Please go back and try again.", "Error"); die(); }
    if ($userrow["currentmp"] < $spellrow["mp"]) { display("You do not have enough Magic Points to cast this spell. Please go back and try again.", "Error"); die(); }
    if ($userrow["currentaction"] == "Fighting") { display("You cannot use the Quick Spells list during a fight. Please go back and select the Healing Spell you wish to use from the Spells box on the main fighting screen to continue.", "Error"); die(); }
    if ($userrow["currenthp"] == $userrow["maxhp"]) { display("Your Hit Points are already full. You don't need to use a Healing spell now.", "Error"); die(); }

    $newhp = $userrow["currenthp"] + $spellrow["attribute"];
    if ($userrow["maxhp"] < $newhp) { $spellrow["attribute"] = $userrow["maxhp"] - $userrow["currenthp"]; $newhp = $userrow["currenthp"] + $spellrow["attribute"]; }
    $newmp = $userrow["currentmp"] - $spellrow["mp"];

    $updatequery = doquery($link, "UPDATE {{table}} SET currenthp='$newhp', currentmp='$newmp' WHERE id='".$userrow["id"]."' LIMIT 1", "users");

    display("You have cast the ".$spellrow["name"]." spell, and gained ".$spellrow["attribute"]." Hit Points. You can now continue exploring.", "Healing Spell");
    die();

}

?>


text:
layout: post
title: "PHP Stream : wrappers, filters... un allié méconnu"
lang: fr
permalink: /fr/php-stream-wrappers-filters/
excerpt: "Bien que très puissant et présent dans PHP depuis la version 4.3, ce composant est souvent méconnu, sous-exploité, voire mal utilisé."
authors:
    - amoutte
categories:
    - php
    - stream
    - flux
    - protocol
    - wrappers
    - transports
    - filters
tags:
    - php
    - stream
    - flux
    - protocol
    - wrappers
    - transports
    - filters
cover: /assets/2018-05-11-php-stream-wrappers-filters/cover.jpg
---

## Définition

La définition du [manuel](http://php.net/intro.stream) étant déjà très claire, je me contente simplement de vous la partager.
> La gestion des flux a été introduite en PHP 4.3.0. comme méthode de généralisation des fichiers, sockets, connexions réseau, données compressées et autres opérations du même type, qui partagent des opérations communes. Dans sa définition la plus simple, un flux est une ressource qui présente des capacités de flux. C'est-à-dire que ces objets peuvent être lus ou recevoir des écritures de manière linéaire, et disposent aussi de moyens d'accéder à des positions arbitraires dans le flux.

## Protocoles

Un protocole est une spécification de plusieurs règles pour un type de communication. Il peut également être utile pour vérifier que les informations soient correctement reçues.

> Dans une conversation téléphonique quand l'interlocuteur décroche, il commence par dire "Allô" afin de spécifier qu'il est prêt à recevoir des informations.

La référence d'un flux (style URL) s'écrit de la forme `scheme://target`.

> Donc oui `https://blog.eleven-labs.com/` peut être ouvert comme un flux qui pointe vers une ressource distante.

### Wrappers

Dans le contexte présent un `wrapper` est un gestionnaire de protocole (de style URL).

Voici la liste des `scheme` ([wrappers](http://php.net/wrappers)) supportés par PHP :

- [file://](http://php.net/manual/fr/wrappers.file.php)  — Accès au système de fichiers local
- [http://](http://php.net/manual/fr/wrappers.http.php)  — Accès aux URLs HTTP(s)
- [ftp://](http://php.net/manual/fr/wrappers.ftp.php)  — Accès aux URLs FTP(s)
- [php://](http://php.net/manual/fr/wrappers.php.php)  — Accès aux divers flux I/O
- [data://](http://php.net/manual/fr/wrappers.data.php)  — Données (RFC 2397)
- [glob://](http://php.net/manual/fr/wrappers.glob.php)  — Trouve des noms de fichiers correspondant à un masque donné
- [phar://](http://php.net/manual/fr/wrappers.phar.php)  — Archive PHP (PHP >= 5.3.0)
- [zlib://](http://php.net/manual/fr/wrappers.compression.php)  — Flux de compression **(requiert un extension pour zip://)**
- [ssh2://](http://php.net/manual/fr/wrappers.ssh2.php)  — Shell sécurisé 2 **(requiert l'extension SSH2)**
- [rar://](http://php.net/manual/fr/wrappers.rar.php)  — RAR **(requiert l'extension RAR)**
- [ogg://](http://php.net/manual/fr/wrappers.audio.php)  — Flux Audio **(requiert l'extension OGG/Vorbis)**
- [expect://](http://php.net/manual/fr/wrappers.expect.php)  — Flux d'interactions de processus **(requiert l'extension Expect)**

Utiliser `stream_get_wrappers()` pour avoir la liste des protocoles supportés par votre serveur.
```php
var_dump(stream_get_wrappers());
```

### Transports

Un [transport](http://php.net/transports) en PHP ce n'est ni plus ni moins qu'un moyen de transférer des données. Pour cela PHP utilise les `sockets`.

ℹ Il ne faut pas oublier que les `sockets` sont aussi des flux 😜.

**Sockets type WEB**
- tcp:// ([Transmission Control Protocol](https://fr.wikipedia.org/wiki/Transmission_Control_Protocol))
- udp:// ([User Datagram Protocol](https://fr.wikipedia.org/wiki/User_Datagram_Protocol))
- ssl:// (négociation automatique entre ssl v2/v3) (sslv2://, sslv3:// depuis PHP 5.0.2) **(requiert OpenSSL)**
- tls:// **(requiert OpenSSL)**

Utiliser `stream_get_transports()` pour avoir la liste des protocoles de transport supportés par votre serveur.
```php
var_dump(stream_get_transports());
```
> À noter que les paths des `sockets` web s'écrivent sous la forme
> `{PROTOCOL}://{DOMAIN / IP v4, v6}:{PORT}`

Voici plusieurs exemples :
-   _127.0.0.1_
-   _fe80::1_
-   _www.example.com_
-   _tcp://127.0.0.1_
-   _tcp://fe80::1_
-   _tcp://[fe80::1]:80_
-   _tcp://www.example.com_
-   _udp://www.example.com_
-   _ssl://www.example.com_
-   _sslv2://www.example.com_
-   _sslv3://www.example.com_
-   _tls://www.example.com_

**Sockets type UNIX**
- unix:// (fournit l'accès à un flux de type socket, sur un domaine Unix)
- udg:// (fournit un mode de transport alternatif, avec un protocole de datagrammes utilisateur)

----

Voila un petit tour d'horizon des différents protocoles que PHP met nativement, ou par extensions, à votre disposition.

**👨🚀 Il est également possible de créer sont propre `wrapper`, afin d'encapsuler la logique de transmission des données !**

Comme par exemple :

- [s3://](https://docs.aws.amazon.com/aws-sdk-php/v2/guide/feature-s3-stream-wrapper.html) donne accès à votre storage AWS
- [git://](https://github.com/teqneers/PHP-Stream-Wrapper-for-Git#using-the-streamwrapper) permet d'intéragir avec git
- [hoa://](https://github.com/hoaproject/Protocol) permet d'accéder aux différentes informations managées par HOA

### Contexte de flux

Les contextes de flux sont une autre notion importante de la gestion des flux. Le contexte est un ensemble d'options qui sera passé en argument aux diverses fonctions de traitements de flux (ex: `stream_*`, `fopen`, `copy`, `file_get_contents`...).

```php
$context = stream_context_create(
    [
        'http' => [
            'protocol_version' => '1.1',
            'timeout' => 10,
            'user_agent' => 'Wilson Browser',
            'method' => 'GET',
        ],
    ],
    []
);

$result = file_get_contents('http://../page', false, $context);
```
> La requête générée pour récupérer la page sera donc en `GET HTTP 1.1` avec un user agent `Wilson Browser` et un timeout à 10 secondes.

Vous pouvez également utiliser `stream_context_set_default` afin de configurer les options par défaut des gestionnaires de flux.

```php
stream_context_set_default([
    'http' => [
        'timeout' => 10,
        'user_agent' => 'Wilson Browser',
    ],
    'ftp' => [...]
]);
```
> ⚠ Attention à l'utilisation de cette dernière, car elle configure les options de toutes les requêtes HTTP faites par la couche de flux de PHP.

### Filtres

Une autre partie assez intéressante des flux étant la possibilité d'ajouter des fonctions de [filtre](http://php.net/filters) sur les données qui transiteront dans le flux.

- _string.rot13_
- _string.toupper_
- _string.tolower_
- _string.strip_tags_ **(depuis PHP 5)**
- _convert.base64-encode_ - _convert.base64-decode_
- _convert.quoted-printable-encode_ - _convert.quoted-printable-decode_
- _zlib.deflate_ (compression) (PHP >= _5_ si le support [zlib](http://php.net/manual/fr/ref.zlib.php) est activé)
- _zlib.inflate_ (decompression) (PHP >= _5_ si le support [zlib](http://php.net/manual/fr/ref.zlib.php) est activé)
- _bzip2.compress (PHP >= _5_ si le support [bz2](http://php.net/manual/fr/ref.bzip2.php) est activé)
- _bzip2.decompress_ (PHP >= _5_ si le support [bz2](http://php.net/manual/fr/ref.bzip2.php) est activé)
- _mcrypt.*_ (❌ **_OBSOLETE_ depuis PHP 7.1.0. Nous vous encourageons vivement à ne plus l'utiliser.**)
- _mdecrypt.*_ (❌ **_OBSOLETE_ depuis PHP 7.1.0. Nous vous encourageons vivement à ne plus l'utiliser.**)

Utiliser `stream_get_filters()` pour avoir la liste des filtres supportés par votre serveur.
```php
var_dump(stream_get_filters());
```

Il existe 2 syntaxes pour configurer un filtre sur un flux.

L'utilisation de `stream_filter_append`/`stream_filter_prepend`.
```php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'string.toupper', STREAM_FILTER_WRITE);
fwrite($fp, "Code de lancement: 151215");
fclose($fp);
```
[`Exécuter le php`](https://3v4l.org/VV6FG)

Grâce au flux `php://filter`
```php
file_put_contents('php://filter/string.toupper/resource=php://output', 'Code de lancement: 151215');
```
[`Exécuter le php`](https://3v4l.org/4nggb)
> Les 2 exemples ci-dessus vont afficher `CODE DE LANCEMENT: 151215`

**👨🚀 Là aussi il est possible de créer son propre `filter` grâce à [php_user_filter](http://php.net/php_user_filter) !**

Voici un petit filtre geek.

```php
class l33t_filter extends php_user_filter {
    function filter($in, $out, &$consumed, $closing)
    {
        $common = ["a", "e", "s", "S", "A", "o", "O", "t", "l", "ph", "y", "H", "W", "M", "D", "V", "x"];
        $leet = ["4", "3", "z", "Z", "4", "0", "0", "+", "1", "f", "j", "|-|", "\\/\\/", "|\\/|", "|)", "\\/", "><"];

        while ($bucket = stream_bucket_make_writeable($in)) {
            $bucket->data = str_replace($common, $leet, $bucket->data);
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}
stream_filter_register('l33t_filter', 'l33t_filter') or die('Failed to register filter Markdown');

file_put_contents('php://filter/l33t_filter/resource=php://output', 'Salut ça va?');
```
[`Exécuter le php`](https://3v4l.org/Zpgr8)

> L'exemple du dessus convertira `Salut ça va?` en `Z41u+ ç4 v4?`

On peut imaginer des filtres html>markdown,  un emoji converter, un dictionnaire de mots blacklistés, etc.

## Les flux I/O

PHP met également à notre disposition des flux d'`Input`/`Output`.

### php://stdin
C'est le flux d'entrée standard (ligne de commande)
> ℹ stdin: est en lecture seule

Exemple
```php
//index.php
copy(
    'php://stdin',
    'php://filter/string.toupper/resource=php://stdout'
);
```
La commande ci-dessous écrira `string` dans le flux `stdin` et ici on copie simplement ce que l'on reçoit dans la sortie standard après avoir appliqué un filtre `toupper`.
```bash
$ echo 'string' | php index.php #affichera STRING
$ cat file.txt | php index.php #affichera le contenu du fichier en majuscule
```

### php://stdout et php://stderr
Sont les flux de sortie standards (ligne de commande)
> ℹ stdin: est en lecture seule

Exemple
```php
//error.php
error_reporting(E_ALL);
ini_set("display_errors", 0);
echo 'Hello '.$_GET['user'];
```
Avec ce script nous allons reporter toutes les erreurs (E_ALL) mais ne pas les afficher aux visiteurs.

Dans un navigateur web ce script affichera :
```
Hello
```
Et les erreurs seront dirigées vers le flux `php://stderr` qui est bien souvent configuré par votre file handler (nginx/apache...) grâce au paramètre [error_log](http://php.net/error-log).

👨🚀 **En ligne de commande `php://output` `php://stderr` sont par défaut envoyés dans `php://stdout`**

Lançons ce script avec la commande suivante :
```bash
$ php error.php
```
Ce qui donnera :
```
PHP Notice:  Undefined index: user in /var/www/error.php on line 5
PHP Stack trace:
PHP   1. {main}() /var/www/error.php:0
Hello %
```
**Utilisons maintenant la redirection de flux GNU/Linux**
```bash
$ php error.php > out.txt
```
la console affichera :
```
PHP Notice:  Undefined index: user in /var/www/error.php on line 5
PHP Stack trace:
PHP   1. {main}() /var/www/error.php:0
```
tandis que le fichier `out.txt` contiendra :
```
Hello
```

**Mais on peut également rediriger la sortie d'erreur**
```bash
$ php error.php 2> errors.txt
```
la console affichera :

```
Hello
```
tandis que le fichier `errors.txt` contiendra :
```
PHP Notice:  Undefined index: user in /var/www/error.php on line 5
PHP Stack trace:
PHP   1. {main}() /var/www/error.php:0
```
ℹ On peut également combiner les 2 `php error.php > out.txt 2> errors.txt`
> `>` et `2>` écrase le fichier ou le crée.
> `>>` et `2>>` écrit à la fin du fichier ou le crée.
> `2>&1` et `2>>&1` redirige les 2 flux (avec le même comportement pour `>` et `>>`)

### php://input
Permet de lire les données brutes du corps de la requête.
> ⚠ N'est pas disponible avec _enctype="multipart/form-data"_.

### php://output
Permet d'écrire dans le buffer de sortie de la même façon que `print` `echo`.
```php
// les 2 écritures suivantes feront la même chose
file_put_contents('php://output', 'Some data');
echo 'Some data';
```
N'oubliez pas qu'avec `php://output` vous pouvez utiliser les filtres, le contexte et même pourquoi pas réécrire au début.

### php://temp et php://memory
Permet d'écrire dans un gestionnaire de fichiers. `php://memory` stockera toujours en mémoire tandis que `php://temp` stockera en mémoire, puis sur disque après avoir attendu la limite prédéfinie (défaut 2Mo)
> 👨🚀 `php://temp/maxmemory:200` stockera sur disque une fois que 200 octets seront écrit dans le flux.

### php://filter
Permet d'ajouter un filtre lors de l'ouverture d'un autre flux.
Exemple :
```php
// Applique un filtre lors de la lecture
file_get_contents('php://filter/read=string.rot13/resource=example.txt');

// Applique un filtre lors de l'écriture
file_put_contents('php://filter/write=string.rot13/resource=example.txt', 'new data');

// Applique le filtre lors de l'écriture mais aussi lors de la lecture
$res = fopen('php://filter/string.rot13/resource=example.txt', 'w+');
```

### php://fd

N'ayant pas trouvé d'informations utiles je vous laisse consulter la [documentation](http://php.net/wrappers-php#refsect2-wrappers.php-unknown-unknown-unknown-unknown-descriptior)

## Quelques cas d'utilisation

Prenons l'exemple d'une copie de fichier :
```php
$file = file_get_contents('http://.../textfile.txt');
file_put_contents(__DIR__.'/downloaded_textfile.txt', $file);
```
⚠ Avec ce code nous allons télécharger **entièrement** le fichier `textfile.txt` dans la mémoire avant de l'écrire dans le fichier de destination !

Maintenant si l'on change légèrement le code on obtient :

DO
```php
copy(
    'http://.../textfile.txt',
    __DIR__.'/downloaded_textfile.txt'
);
```
ℹ On peut faire le même traitement avec des ressources :

```php
stream_copy_to_stream(
    fopen('http://.../textfile.txt', 'r'),
    fopen(__DIR__.'/downloaded_textfile.txt', 'w+')
);
```
Voici la consommation mémoire pour un fichier de 5Mo.

| Code  | memory_get_usage(true) | memory_get_peak_usage(true) |
|--|--|--|
| file_get_content | 8Mo | 13Mo |
| copy | 2Mo | 2Mo |
|stream_copy_to_stream|2Mo|2Mo|

La différence de consommation mémoire est due au fait que `copy` et `stream_copy_to_stream` vont directement écrire la source dans la destination.

👨🚀 N'hésitez pas à utiliser les `wrappers`/`transports` cités au début de l'article.

---
```php
copy(
    'http://.../image.jpg',
    'ssh2.scp://user:pass@server:22/home/download/image.jpg'
);
```
> Copie le fichier depuis le web sur un serveur en utilisant `scp` via `ssh`.

---

Un autre exemple fréquemment rencontré lors de la création de fichier temporaire :

```php
$tmpFile = tempnam(sys_get_temp_dir(), 'php' . rand());
```
⚠ Ici le script va créer un **fichier** dans le **dossier temporaire** de php.
- Ce qui veut dire qu'il vous faudra supprimer vous-même ce fichier.
- La fonction `tempnam()` retourne le `path` et non la ressource.

👨🚀 Préférez donc l'utilisation de :
- `php://temp` ou `php://temp/maxmemory:100` qui stockera en mémoire puis sur disque une fois la limite atteinte.
- `php://memory` stocker en mémoire
- `tmpfile()` crée un fichier temporaire avec un nom unique, ouvert en écriture et lecture (_w+_), et retourne un pointeur de fichier.

```php
$tmp = fopen('php://temp', 'w+');
```

Ce fichier sera automatiquement effacé :
- lorsqu'il sera fermé.
- lorsqu'il n'y a plus de référence au gestionnaire de fichiers.
- lorsque le script sera terminé.

## Conclusion
Bien que très puissant et présent dans PHP depuis la version 4.3, ce composant est souvent méconnu, sous exploité, voire mal utilisé. C'est pourquoi j'en fais la promotion ici. J'espère avoir suscité votre intérêt !

📝 **Je n'ai volontairement pas abordé les flux de type `socket` car, ils mériteraient un article à eux seuls.**

## Liens utiles
- 👍 [Les flux : méconnus et sous-utilisés - Pascal Martin - Forum PHP 2015](https://www.youtube.com/watch?v=3tOGhPj8IcA)
- [http://php.net/stream](http://php.net/stream)
- [http://php.net/context](http://php.net/context)
- [http://php.net/wrappers](http://php.net/wrappers)
- [https://github.com/reactphp/react](https://github.com/reactphp/react)


text:
package org.apache.deltaspike.core.api.provider;

import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AfterDeploymentValidation;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeShutdown;
import javax.enterprise.inject.spi.Extension;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.deltaspike.core.api.config.ConfigResolver;
import org.apache.deltaspike.core.util.ClassUtils;

/**
 * This class provides access to the {@link BeanManager} by registering the current {@link BeanManager} in an extension
 * and making it available via a singleton factory for the current application.
 *
 *
This is really handy when you need to access CDI functionality from places where no injection is available.

 *
 *
If a simple but manual bean lookup is needed, it's easier to use the {@link BeanProvider}.

 *
 *
As soon as an application shuts down, the reference to the {@link BeanManager} is removed.

 *
 *

 * Usage:

 *

 * BeanManager bm = BeanManagerProvider.getInstance().getBeanManager();

 *

 *

 * Attention: This approach is intended for use in user code at runtime. If BeanManagerProvider is used during
 * Container boot (in an Extension), non-portable behaviour results. During bootstrapping, an Extension shall
 * @Inject BeanManager to get access to the underlying BeanManager (see e.g. {@link #cleanupFinalBeanManagers}).
 * This is the only way to guarantee that the right BeanManager is obtained in more complex Container scenarios.

 */
public class BeanManagerProvider implements Extension
{
    private static final Logger  LOG = Logger.getLogger(BeanManagerProvider.class.getName());

    //for CDI 1.1+ delegation
    private static final Method CDI_CURRENT_METHOD;
    private static final Method CDI_CURRENT_BEAN_MANAGER_METHOD;

    private static BeanManagerProvider bmpSingleton;

    static
    {
        Class cdiClass = ClassUtils.tryToLoadClassForName("javax.enterprise.inject.spi.CDI");

        Method resolvedCdiCurrentMethod = null;
        Method resolvedCdiBeanManagerMethod = null;
        //only init methods if a cdi 1.1+ container is available and the delegation-mode isn't deactivated.
        //deactivation is e.g. useful if owb is used in "parallel mode" in a weld-based server.
        if (cdiClass != null && !"false".equalsIgnoreCase(
            ConfigResolver.getPropertyValue("deltaspike.bean-manager.delegate_lookup", Boolean.TRUE.toString())))
        {

            try
            {
                resolvedCdiCurrentMethod = cdiClass.getDeclaredMethod("current");
                resolvedCdiBeanManagerMethod = cdiClass.getDeclaredMethod("getBeanManager");
            }
            catch (Exception e)
            {
                LOG.log(Level.SEVERE, "Couldn't get method from " + cdiClass.getName(), e);
            }
        }

        //null if no init happened e.g. due to CDI 1.0 or deactivated delegation-mode
        CDI_CURRENT_METHOD = resolvedCdiCurrentMethod;
        CDI_CURRENT_BEAN_MANAGER_METHOD = resolvedCdiBeanManagerMethod;
    }

    /**
     * This data container is used for storing the BeanManager for each web application. This is needed in EAR or other
     * multi-webapp scenarios when the DeltaSpike classes (jars) are provided in a shared ClassLoader.
     */
    private static class BeanManagerInfo
    {
        /**
         * The BeanManager picked up via Extension loading.
         */
        private BeanManager loadTimeBm;

        /**
         * The final BeanManager. After the container did finally boot, we first try to resolve them from JNDI, and only
         * if we don't find any BM there we take the ones picked up at startup.
         */
        private BeanManager finalBm;

        /**
         * Whether the CDI Application has finally booted. Please note that this is only a nearby value as there is no
         * reliable event for this status in EE6.
         */
        private boolean booted;
    }

    /**
     * The BeanManagerInfo for the current ClassLoader.
     *
     *
Attention: This instance must only be used through the {@link #bmpSingleton} singleton!

     */
    private volatile Map<ClassLoader, BeanManagerInfo> bmInfos = new ConcurrentHashMap<ClassLoader, BeanManagerInfo>();

    /**
     * Indicates whether the {@link BeanManagerProvider} has been initialized. Usually it's not necessary to call this
     * method in application code. It's useful e.g. for other frameworks to check if DeltaSpike and the CDI container in
     * general have been started.
     *
     * @return true if the BeanManagerProvider is ready to be used
     */
    public static boolean isActive()
    {
        return bmpSingleton != null;
    }

    /**
     * Returns the current provider instance which provides access to the current {@link BeanManager}.
     *
     * @throws IllegalStateException if the {@link BeanManagerProvider} isn't ready to be used. That's the case if the
     *                               environment isn't configured properly and therefore the {@link AfterBeanDiscovery}
     *                               hasn't been called before this method gets called.
     * @return the singleton BeanManagerProvider
     */
    public static BeanManagerProvider getInstance()
    {
        /*X TODO Java-EE5 support needs to be discussed
        if (bmpSingleton == null)
        {
            // workaround for some Java-EE5 environments in combination with a special
            // StartupBroadcaster for bootstrapping CDI

            // CodiStartupBroadcaster.broadcastStartup();
            // here bmp might not be null (depends on the broadcasters)
        }
        */

        if (bmpSingleton == null)
        {
            throw new IllegalStateException("No " + BeanManagerProvider.class.getName() + " in place! " +
                    "Please ensure that you configured the CDI implementation of your choice properly. " +
                    "If your setup is correct, please clear all caches and compiled artifacts.");
        }
        return bmpSingleton;
    }

    /**
     * It doesn't really matter which of the system events is used to obtain the BeanManager, but
     * {@link AfterBeanDiscovery} has been chosen since it allows all events which occur after the
     * {@link AfterBeanDiscovery} to use the {@link BeanManagerProvider}.
     *
     * @param afterBeanDiscovery event which we don't actually use ;)
     * @param beanManager        the BeanManager we store and make available.
     */
    public void setBeanManager(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager)
    {
        setBeanManagerProvider(this);

        BeanManagerInfo bmi = getBeanManagerInfo(ClassUtils.getClassLoader(null));
        bmi.loadTimeBm = beanManager;
    }

    /**
     * The active {@link BeanManager} for the current application (current {@link ClassLoader}). This method will throw
     * an {@link IllegalStateException} if the BeanManager cannot be found.
     *
     * @return the current BeanManager, never null
     *
     * @throws IllegalStateException if the BeanManager cannot be found
     */
    public BeanManager getBeanManager()
    {
        BeanManagerInfo bmi = getBeanManagerInfo(ClassUtils.getClassLoader(null));

        if (!bmi.booted)
        {
            // warn the user if he tries to use the BeanManager before container startup
            LOG.warning("When using the BeanManager to retrieve Beans before the Container is started," +
                    " non-portable behaviour results!");

            // This is a workaround for some containers with messed up EAR handling.
            // Those containers might boot up with the shared ear ClassLoader
            // and later run the WARs with their own child ClassLoaders.
            if (bmi.loadTimeBm == null)
            {
                BeanManagerInfo parentBmi = getParentBeanManagerInfo(ClassUtils.getClassLoader(null));
                if (parentBmi != null)
                {
                    bmi.loadTimeBm = parentBmi.loadTimeBm;
                }
            }
        }

        BeanManager result = bmi.finalBm;

        if (result == null)
        {
            synchronized (bmi)
            {
                result = bmi.finalBm;
                if (result == null)
                {
                    // first we look for a BeanManager from JNDI
                    result = resolveBeanManagerViaJndi();

                    // needs to be here to get a better performance and
                    // even in ee7 containers we can't rely on that lookup as a primary strategy
                    // (esp. in case of EAR based applications)
                    if (result == null)
                    {
                        result = resolveBeanManagerViaStaticHelper();
                    }

                    if (result == null)
                    {
                        // if none found, we take the one we got from the Extension loading
                        result = bmi.loadTimeBm;
                    }

                    if (result == null)
                    {
                        throw new IllegalStateException("Unable to find BeanManager. " +
                                "Please ensure that you configured the CDI implementation of your choice properly.");
                    }

                    // store the resolved BeanManager in the result cache until #cleanupFinalBeanManagers gets called
                    // -> afterwards the next call of #getBeanManager will trigger the final lookup
                    bmi.finalBm = result;
                }
            }
        }

        return result;
    }

    /**
     * By cleaning the final BeanManager map after the deployment gets validated, premature loading of information from
     * JNDI is prevented in cases where the container might not be fully setup yet.
     *
     * This might happen if the BeanManagerProvider is used in an extension during CDI bootstrap. This should be
     * generally avoided. Instead, an injected BeanManager should be used in Extensions and propagated using setters.
     *
     * In EARs with multiple webapps, each WAR might get a different Extension. This depends on the container used.
     */
    public void cleanupFinalBeanManagers(@Observes AfterDeploymentValidation adv)
    {
        for (BeanManagerInfo bmi : bmpSingleton.bmInfos.values())
        {
            bmi.finalBm = null;
            bmi.booted = true;

            /*possible issue with >weld< based servers:
            if #getBeanManager gets called in a custom AfterDeploymentValidation observer >after< this observer,
            the wrong bean-manager might get stored (not deterministic due to the unspecified order of observers).
            finally a bean-manager for a single bda will be stored and returned (which isn't the bm exposed via jndi).*/
        }
    }

    /**
     * Cleanup on container shutdown.
     *
     * @param beforeShutdown CDI shutdown event
     */
    public void cleanupStoredBeanManagerOnShutdown(@Observes BeforeShutdown beforeShutdown)
    {
        if (bmpSingleton == null)
        {
            // this happens if there has been a failure at startup
            return;
        }

        bmpSingleton.bmInfos.remove(ClassUtils.getClassLoader(null));
    }

    /**
     * Get the BeanManager from the JNDI registry.
     *
     * @return current {@link BeanManager} which is provided via JNDI
     */
    private BeanManager resolveBeanManagerViaJndi()
    {
        try
        {
            // this location is specified in JSR-299 and must be
            // supported in all certified EE environments
            return (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
        }
        catch (NamingException e)
        {
            //workaround didn't work -> return null
            return null;
        }
    }

    private BeanManager resolveBeanManagerViaStaticHelper()
    {
        if (CDI_CURRENT_METHOD != null && CDI_CURRENT_BEAN_MANAGER_METHOD != null)
        {
            try
            {
                Object cdiCurrentObject = CDI_CURRENT_METHOD.invoke(null);
                return (BeanManager) CDI_CURRENT_BEAN_MANAGER_METHOD.invoke(cdiCurrentObject);
            }
            catch (Throwable t)
            {
                LOG.log(Level.FINEST, "failed to delegate bean-manager lookup -> fallback to default.", t);
            }
        }
        return null;
    }

    /**
     * Get or create the BeanManagerInfo for the given ClassLoader.
     */
    private BeanManagerInfo getBeanManagerInfo(ClassLoader cl)
    {
        BeanManagerInfo bmi = bmpSingleton.bmInfos.get(cl);

        if (bmi == null)
        {
            synchronized (this)
            {
                bmi = bmpSingleton.bmInfos.get(cl);
                if (bmi == null)
                {
                    bmi = new BeanManagerInfo();
                    bmpSingleton.bmInfos.put(cl, bmi);
                    if (cl.getParent() != null && !bmpSingleton.bmInfos.containsKey(cl.getParent()))
                    {
                        bmpSingleton.bmInfos.put(cl.getParent(), bmi);
                    }
                }
            }
        }

        return bmi;
    }

    /**
     * This function exists to prevent findbugs from complaining about setting a static member from a non-static
     * function.
     *
     * @param beanManagerProvider the bean-manager-provider which should be used if there isn't an existing provider
     *
     * @return the first BeanManagerProvider
     */
    private static BeanManagerProvider setBeanManagerProvider(BeanManagerProvider beanManagerProvider)
    {
        if (bmpSingleton == null)
        {
            bmpSingleton = beanManagerProvider;
        }

        return bmpSingleton;
    }

    /**
     * This method recurses into the parent ClassLoaders and checks whether a BeanManagerInfo for it exists.
     *
     * @return the BeanManagerInfo of the parent ClassLoader hierarchy if any exists, or null if there is
     *         no {@link BeanManagerInfo} for the ClassLoaders in the hierarchy.
     */
    private BeanManagerInfo getParentBeanManagerInfo(ClassLoader classLoader)
    {
        ClassLoader parentClassLoader = classLoader.getParent();
        if (parentClassLoader == null)
        {
            return null;
        }

        BeanManagerInfo bmi = getBeanManagerInfo(parentClassLoader);
        if (bmi == null)
        {
            // recursive call up to the root ClassLoader
            bmi = getParentBeanManagerInfo(parentClassLoader);
        }

        return bmi;
    }

}