pax_global_header00006660000000000000000000000064135123223110014503gustar00rootroot0000000000000052 comment=433c0c646f7ad9d330343526f6b2c2bee5dcc295 active_model_serializers-0.10.10/000077500000000000000000000000001351232231100167115ustar00rootroot00000000000000active_model_serializers-0.10.10/.github/000077500000000000000000000000001351232231100202515ustar00rootroot00000000000000active_model_serializers-0.10.10/.github/ISSUE_TEMPLATE.md000066400000000000000000000010671351232231100227620ustar00rootroot00000000000000#### Expected behavior vs actual behavior #### Steps to reproduce *(e.g., detailed walkthrough, runnable script, example application)* #### Environment ActiveModelSerializers Version *(commit ref if not on tag)*: Output of `ruby -e "puts RUBY_DESCRIPTION"`: OS Type & Version: Integrated application and version *(e.g., Rails, Grape, etc)*: #### Backtrace *(e.g., provide any applicable backtraces from your application)* #### Additonal helpful information *(e.g., Gemfile.lock, configurations, PR containing a failing test, git bisect results)* active_model_serializers-0.10.10/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000001601351232231100240470ustar00rootroot00000000000000#### Purpose #### Changes #### Caveats #### Related GitHub issues #### Additional helpful information active_model_serializers-0.10.10/.gitignore000066400000000000000000000005151351232231100207020ustar00rootroot00000000000000*.gem *.rbc .bundle .config .yardoc Gemfile.lock Gemfile.local InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg Vagrantfile .vagrant rdoc spec/reports test/tmp test/version_tmp tmp *.swp .ruby-version .ruby-gemset vendor/bundle tags # silly macs .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes Icon? ehthumbs.db Thumbs.db active_model_serializers-0.10.10/.rubocop.yml000066400000000000000000000037741351232231100211760ustar00rootroot00000000000000AllCops: TargetRubyVersion: 2.1 Exclude: - !ruby/regexp /(vendor|bundle|bin|db|tmp)\/.*/ DisplayCopNames: true DisplayStyleGuide: true # https://github.com/bbatsov/rubocop/blob/master/manual/caching.md # https://github.com/bbatsov/rubocop/blob/e8680418b351491e111a18cf5b453fc07a3c5239/config/default.yml#L60-L77 UseCache: true CacheRootDirectory: tmp Rails: Enabled: true Lint/NestedMethodDefinition: Enabled: false Exclude: - test/action_controller/serialization_test.rb Style/Alias: EnforcedStyle: prefer_alias Style/StringLiterals: EnforcedStyle: single_quotes Metrics/AbcSize: Max: 35 # TODO: Lower to 15 Metrics/ClassLength: Max: 261 # TODO: Lower to 100 Exclude: - test/**/*.rb Metrics/CyclomaticComplexity: Max: 7 # TODO: Lower to 6 Metrics/LineLength: Max: 251 # TODO: Lower to 80 Metrics/MethodLength: Max: 106 # TODO: Lower to 10 Metrics/PerceivedComplexity: Max: 9 # TODO: Lower to 7 Style/AlignParameters: EnforcedStyle: with_fixed_indentation Style/ClassAndModuleChildren: EnforcedStyle: nested Style/Documentation: Enabled: false Style/MissingElse: Enabled: true EnforcedStyle: case Style/EmptyElse: EnforcedStyle: empty Style/FrozenStringLiteralComment: Enabled: true EnforcedStyle: always Style/MultilineOperationIndentation: EnforcedStyle: indented Style/BlockDelimiters: Enabled: true EnforcedStyle: line_count_based Style/SignalException: EnforcedStyle: semantic Style/TrailingCommaInLiteral: EnforcedStyleForMultiline: no_comma Style/ConditionalAssignment: Enabled: false Style/DotPosition: EnforcedStyle: leading ########## test_helper.rb sanity Style/EndBlock: Exclude: - test/test_helper.rb Style/SpecialGlobalVars: Exclude: - test/test_helper.rb Style/GlobalVars: Exclude: - test/test_helper.rb Style/AndOr: Exclude: - test/test_helper.rb - 'lib/active_model/serializer/lint.rb' Style/Not: Exclude: - test/test_helper.rb Style/ClassCheck: Exclude: - test/test_helper.rb active_model_serializers-0.10.10/.simplecov000066400000000000000000000057731351232231100207270ustar00rootroot00000000000000# https://github.com/colszowka/simplecov#using-simplecov-for-centralized-config # see https://github.com/colszowka/simplecov/blob/master/lib/simplecov/defaults.rb # vim: set ft=ruby ## DEFINE VARIABLES @minimum_coverage = ENV.fetch('COVERAGE_MINIMUM') { case (defined?(RUBY_ENGINE) && RUBY_ENGINE) || "ruby" when 'jruby', 'rbx' 96.0 else 98.1 end }.to_f.round(2) # rubocop:disable Style/DoubleNegation ENV['FULL_BUILD'] ||= ENV['CI'] @running_ci = !!(ENV['FULL_BUILD'] =~ /\Atrue\z/i) @generate_report = @running_ci || !!(ENV['COVERAGE'] =~ /\Atrue\z/i) @output = STDOUT # rubocop:enable Style/DoubleNegation ## CONFIGURE SIMPLECOV SimpleCov.profiles.define 'app' do coverage_dir 'coverage' load_profile 'test_frameworks' add_group 'Libraries', 'lib' add_group 'Long files' do |src_file| src_file.lines.count > 100 end class MaxLinesFilter < SimpleCov::Filter def matches?(source_file) source_file.lines.count < filter_argument end end add_group 'Short files', MaxLinesFilter.new(5) # Exclude these paths from analysis add_filter '/config/' add_filter '/db/' add_filter 'tasks' add_filter '/.bundle/' end ## START TRACKING COVERAGE (before activating SimpleCov) require 'coverage' Coverage.start ## ADD SOME CUSTOM REPORTING AT EXIT SimpleCov.at_exit do next if $! and not ($!.kind_of? SystemExit and $!.success?) header = "#{'*' * 20} SimpleCov Results #{'*' * 20}" results = SimpleCov.result.format!.join("\n") exit_message = <<-EOF #{header} {{RESULTS}} {{FAILURE_MESSAGE}} #{'*' * header.size} EOF percent = Float(SimpleCov.result.covered_percent) if percent < @minimum_coverage failure_message = <<-EOF Spec coverage was not high enough: #{percent.round(2)}% is < #{@minimum_coverage}% EOF exit_message.sub!('{{RESULTS}}', results).sub!('{{FAILURE_MESSAGE}}', failure_message) @output.puts exit_message abort(failure_message) if @generate_report elsif @running_ci exit_message.sub!('{{RESULTS}}', results).sub!('{{FAILURE_MESSAGE}}', <<-EOF) Nice job! Spec coverage (#{percent.round(2)}%) is still at or above #{@minimum_coverage}% EOF @output.puts exit_message end end ## CAPTURE CONFIG IN CLOSURE 'AppCoverage.start' ## to defer running until test/test_helper.rb is loaded. # rubocop:disable Style/MultilineBlockChain AppCoverage = Class.new do def initialize(&block) @block = block end def start @block.call end end.new do SimpleCov.start 'app' if @generate_report if @running_ci require 'codeclimate-test-reporter' @output.puts '[COVERAGE] Running with SimpleCov Simple Formatter and CodeClimate Test Reporter' formatters = [ SimpleCov::Formatter::SimpleFormatter, CodeClimate::TestReporter::Formatter ] else @output.puts '[COVERAGE] Running with SimpleCov HTML Formatter' formatters = [SimpleCov::Formatter::HTMLFormatter] end else formatters = [] end SimpleCov.formatters = formatters end # rubocop:enable Style/MultilineBlockChain active_model_serializers-0.10.10/.travis.yml000066400000000000000000000065301351232231100210260ustar00rootroot00000000000000language: ruby sudo: false ruby_supported_versions: - &ruby_2_1 2.1.10 - &ruby_2_2 2.2.10 - &ruby_2_3 2.3.8 - &ruby_2_4 2.4.6 - &ruby_2_5 2.5.5 - &ruby_2_6 2.6.3 - &ruby_head ruby-head rails_supported_versions: - &rails_4_1 RAILS_VERSION=4.1 - &rails_4_2 RAILS_VERSION=4.2 - &rails_5_0 RAILS_VERSION=5.0 - &rails_5_1 RAILS_VERSION=5.1 - &rails_5_2 RAILS_VERSION=5.2 - &rails_6_0_0_rc1 RAILS_VERSION=6.0.0.rc1 - &rails_master RAILS_VERSION=master cache: directories: - vendor/bundle before_install: - "travis_retry gem update --system 2.7.9" - "travis_retry gem install bundler -v '1.17.3'" install: BUNDLER_VERSION=1.17.3 bundle install --path=vendor/bundle --retry=3 --jobs=3 script: - bundle exec rake ci after_success: - codeclimate-test-reporter env: matrix: - *rails_4_1 - *rails_4_2 - *rails_5_0 - *rails_5_1 - *rails_5_2 - *rails_6_0_0_rc1 - *rails_master rvm: - *ruby_2_1 - *ruby_2_2 - *ruby_2_3 - *ruby_2_4 - *ruby_2_5 - *ruby_2_6 - *ruby_head branches: only: 0-10-stable matrix: include: - { rvm: jruby-9.1.13.0, jdk: oraclejdk8, env: "RAILS_VERSION=4.1 JRUBY_OPTS='--dev -J-Xmx1024M --debug'" } - { rvm: jruby-9.1.13.0, jdk: oraclejdk8, env: "RAILS_VERSION=4.2 JRUBY_OPTS='--dev -J-Xmx1024M --debug'" } - { rvm: jruby-9.1.13.0, jdk: oraclejdk8, env: "RAILS_VERSION=5.1 JRUBY_OPTS='--dev -J-Xmx1024M --debug'" } # See JRuby currently failing on Rails 5+ https://github.com/jruby/activerecord-jdbc-adapter/issues/708 # - { rvm: jruby-9.1.13.0, jdk: oraclejdk8, env: "RAILS_VERSION=5.0 JRUBY_OPTS='--dev -J-Xmx1024M --debug'" } # - { rvm: jruby-head, jdk: oraclejdk8, env: "RAILS_VERSION=5.1 JRUBY_OPTS='--dev -J-Xmx1024M --debug'" } exclude: - { rvm: *ruby_2_1, env: *rails_5_0 } - { rvm: *ruby_2_1, env: *rails_5_1 } - { rvm: *ruby_2_1, env: *rails_5_2 } - { rvm: *ruby_2_1, env: *rails_6_0_0_rc1 } - { rvm: *ruby_2_2, env: *rails_6_0_0_rc1 } - { rvm: *ruby_2_3, env: *rails_6_0_0_rc1 } - { rvm: *ruby_2_4, env: *rails_6_0_0_rc1 } - { rvm: *ruby_2_1, env: *rails_master } - { rvm: *ruby_2_2, env: *rails_master } - { rvm: *ruby_2_3, env: *rails_master } - { rvm: *ruby_2_4, env: *rails_master } allow_failures: - { rvm: *ruby_2_4, env: *rails_4_1 } - { rvm: *ruby_2_5, env: *rails_4_1 } - { rvm: *ruby_2_6, env: *rails_4_1 } # allow RAILS_VERSION=master to fail against ruby 2.5+ until this gem supports RAILS_VERSION # https://github.com/rails/rails/blob/master/RAILS_VERSION # https://github.com/rails-api/active_model_serializers/blob/0-10-stable/active_model_serializers.gemspec#L24 - { rvm: *ruby_2_5, env: *rails_master } - { rvm: *ruby_2_6, env: *rails_master } - rvm: *ruby_head # - { rvm: *ruby_head, env: *rails_4_1 } # - { rvm: *ruby_head, env: *rails_4_2 } # - { rvm: *ruby_head, env: *rails_5_0 } # - { rvm: *ruby_head, env: *rails_5_1 } # - { rvm: *ruby_head, env: *rails_5_2 } # - { rvm: *ruby_head, env: *rails_6_0_0_rc1 } # - { rvm: *ruby_head, env: *rails_master } - rvm: jruby-head # See JRuby currently failing on Rails 5+ https://github.com/jruby/activerecord-jdbc-adapter/issues/708 - { rvm: jruby-9.1.13.0, jdk: oraclejdk8, env: "RAILS_VERSION=5.1 JRUBY_OPTS='--dev -J-Xmx1024M --debug'" } fast_finish: true active_model_serializers-0.10.10/CHANGELOG.md000066400000000000000000001670221351232231100205320ustar00rootroot00000000000000## 0.10.x ### [master (unreleased)](https://github.com/rails-api/active_model_serializers/compare/v0.10.10...0-10-stable) Breaking changes: Features: Fixes: Misc: ### [v0.10.10 (2019-07-13)](https://github.com/rails-api/active_model_serializers/compare/v0.10.9...v0.10.10) Fixes: - [#2319](https://github.com/rails-api/active_model_serializers/pull/2319) Fixes #2316. (@kylekeesling) - Fix Rails 6.0 deprication warnings - update test fixture schema to use `timestamps` instead of `timestamp` - [#2223](https://github.com/rails-api/active_model_serializers/pull/2223) Support Fieldset in Attributes/JSON adapters documented in [docs/general/fields.md](https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/general/fields.md) that worked partially before (@bf4) - [#2337](https://github.com/rails-api/active_model_serializers/pull/2337) fix incorrect belongs_to serialization when foreign_key on object and belongs_to is blank (@InteNs) - Fixes incorrect json-api generation when `jsonapi_use_foreign_key_on_belongs_to_relationship` is `true` and the relationship is blank - [#2172](https://github.com/rails-api/active_model_serializers/pull/2172) Preserve the namespace when falling back to a superclass serializer Misc: - [#2327](https://github.com/rails-api/active_model_serializers/pull/2327) Add support for Ruby 2.6 on Travis CI (@wasifhossain) - [#2304](https://github.com/rails-api/active_model_serializers/pull/2304) Slim down bundled gem by excluding test files and docs (@greysteil) ### [v0.10.9 (2019-02-08)](https://github.com/rails-api/active_model_serializers/compare/v0.10.8...v0.10.9) Fixes: - [#2288](https://github.com/rails-api/active_model_serializers/pull/2288) Change the fetch method to deal with recyclable key cache strategy. Fixes #2287. (@cintamani, @wasifhossain) - [#2307](https://github.com/rails-api/active_model_serializers/pull/2307) Falsey attribute values should not be reevaluated. Misc: - [#2309](https://github.com/rails-api/active_model_serializers/pull/2309) Performance and memory usage fixes ### [v0.10.8 (2018-11-01)](https://github.com/rails-api/active_model_serializers/compare/v0.10.7...v0.10.8) Features: - [#2279](https://github.com/rails-api/active_model_serializers/pull/2279) Support condition options in serializer link statements Fixes: - [#2296](https://github.com/rails-api/active_model_serializers/pull/2296) Fixes #2295 (@Hirurg103) - Fix finding of namespaced serializer and non-namespaced model. - [#2289](https://github.com/rails-api/active_model_serializers/pull/2289) Fixes #2255 (@f-mer) - Fix autoloading race condition, especially in Rails 5. - [#2299](https://github.com/rails-api/active_model_serializers/pull/2299) Fixes #2270 (@chau-bao-long via #2276) - Fix reflection thread-safety bug ### [v0.10.7 (2017-11-14)](https://github.com/rails-api/active_model_serializers/compare/v0.10.6...v0.10.7) Regressions Fixed From v0.10.6: - [#2211](https://github.com/rails-api/active_model_serializers/pull/2211). Fixes #2125, #2160. (@bf4) - Fix polymorphic belongs_to tests; passes on v0.10.5, fails on v0.10.6 - Fix JSON:API polymorphic type regression from v0.10.5 - Fix JSON:API: for_type_and_id should always inflect_type ``` Should Serializer._type ever be inflected? Right now, it won't be, but association.serializer._type will be inflected. That's the current behavior. ``` - [#2216](https://github.com/rails-api/active_model_serializers/pull/2216). Fixes #2132, #2180. (@bf4) - Fix JSON:API: Serialize resource type for unpersisted records (blank id) - [#2218](https://github.com/rails-api/active_model_serializers/pull/2218). Fixes #2178. (@bf4) - Fix JSON:API: Make using foreign key on belongs_to opt-in. No effect on polymorphic relationships. ``` # set to true to opt-in ActiveModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = true ``` Features: - [#2136](https://github.com/rails-api/active_model_serializers/pull/2136) Enable inclusion of sideloaded relationship objects by `key`. (@caomania) - [#2021](https://github.com/rails-api/active_model_serializers/pull/2021) ActiveModelSerializers::Model#attributes. Originally in [#1982](https://github.com/rails-api/active_model_serializers/pull/1982). (@bf4) - [#2130](https://github.com/rails-api/active_model_serializers/pull/2130) Allow serialized ID to be overwritten for belongs-to relationships. (@greysteil) - [#2189](https://github.com/rails-api/active_model_serializers/pull/2189) Update version constraint for jsonapi-renderer to `['>= 0.1.1.beta1', '< 0.3']` (@tagliala) Fixes: - [#2022](https://github.com/rails-api/active_model_serializers/pull/2022) Mutation of ActiveModelSerializers::Model now changes the attributes. Originally in [#1984](https://github.com/rails-api/active_model_serializers/pull/1984). (@bf4) - [#2200](https://github.com/rails-api/active_model_serializers/pull/2200) Fix deserialization of polymorphic relationships. (@dennis95stumm) - [#2214](https://github.com/rails-api/active_model_serializers/pull/2214) Fail if unable to infer collection type with json adapter. (@jmeredith16) - [#2149](https://github.com/rails-api/active_model_serializers/pull/2149) Always include self, first, last pagination link. (@mecampbellsoup) - [#2179](https://github.com/rails-api/active_model_serializers/pull/2179) Fixes #2173, Pass block to Enumerator.new. (@drn) Misc: - [#2176](https://github.com/rails-api/active_model_serializers/pull/2176) Documentation for global adapter config. (@mrpinsky) - [#2215](https://github.com/rails-api/active_model_serializers/pull/2215) Update `serializers.md` documentation to denote alternate use cases for `scope`. (@stratigos) - [#2212](https://github.com/rails-api/active_model_serializers/pull/2212) Remove legacy has_many_embed_ids test. (@bf4) ### [v0.10.6 (2017-05-01)](https://github.com/rails-api/active_model_serializers/compare/v0.10.5...v0.10.6) Fixes: - [#1857](https://github.com/rails-api/active_model_serializers/pull/1857) JSON:API does not load belongs_to relation to get identifier id. (@bf4) - [#2119](https://github.com/rails-api/active_model_serializers/pull/2119) JSON:API returns null resource object identifier when 'id' is null. (@bf4) - [#2093](https://github.com/rails-api/active_model_serializers/pull/2093) undef problematic Serializer methods: display, select. (@bf4) Misc: - [#2104](https://github.com/rails-api/active_model_serializers/pull/2104) Documentation for serializers and rendering. (@cassidycodes) - [#2081](https://github.com/rails-api/active_model_serializers/pull/2081) Documentation for `include` option in adapters. (@charlie-wasp) - [#2120](https://github.com/rails-api/active_model_serializers/pull/2120) Documentation for association options: foreign_key, type, class_name, namespace. (@bf4) ### [v0.10.5 (2017-03-07)](https://github.com/rails-api/active_model_serializers/compare/v0.10.4...v0.10.5) Breaking changes: Features: - [#2021](https://github.com/rails-api/active_model_serializers/pull/2021) ActiveModelSerializers::Model#attributes. Originally in [#1982](https://github.com/rails-api/active_model_serializers/pull/1982). (@bf4) - [#2057](https://github.com/rails-api/active_model_serializers/pull/2057) Update version constraint for jsonapi-renderer to `['>= 0.1.1.beta1', '< 0.2']` (@jaredbeck) Fixes: - [#2022](https://github.com/rails-api/active_model_serializers/pull/2022) Mutation of ActiveModelSerializers::Model now changes the attributes. Originally in [#1984](https://github.com/rails-api/active_model_serializers/pull/1984). (@bf4) Misc: - [#2055](https://github.com/rails-api/active_model_serializers/pull/2055) Replace deprecated dependency jsonapi with jsonapi-renderer. (@jaredbeck) - [#2021](https://github.com/rails-api/active_model_serializers/pull/2021) Make test attributes explicit. Tests have Model#associations. (@bf4) - [#1981](https://github.com/rails-api/active_model_serializers/pull/1981) Fix relationship link documentation. (@groyoh) - [#2035](https://github.com/rails-api/active_model_serializers/pull/2035) Document how to disable the logger. (@MSathieu) - [#2039](https://github.com/rails-api/active_model_serializers/pull/2039) Documentation fixes. (@biow0lf) ### [v0.10.4 (2017-01-06)](https://github.com/rails-api/active_model_serializers/compare/v0.10.3...v0.10.4) Misc: - [#2005](https://github.com/rails-api/active_model_serializers/pull/2005) Update jsonapi runtime dependency to 0.1.1.beta6, support Ruby 2.4. (@kofronpi) - [#1993](https://github.com/rails-api/active_model_serializers/pull/1993) Swap out KeyTransform for CaseTransform gem for the possibility of native extension use. (@NullVoxPopuli) ### [v0.10.3 (2016-11-21)](https://github.com/rails-api/active_model_serializers/compare/v0.10.2...v0.10.3) Fixes: - [#1973](https://github.com/rails-api/active_model_serializers/pull/1973) Fix namespace lookup for collections and has_many relationships (@groyoh) - [#1887](https://github.com/rails-api/active_model_serializers/pull/1887) Make the comment reflect what the function does (@johnnymo87) - [#1890](https://github.com/rails-api/active_model_serializers/issues/1890) Ensure generator inherits from ApplicationSerializer when available (@richmolj) - [#1922](https://github.com/rails-api/active_model_serializers/pull/1922) Make railtie an optional dependency in runtime (@ggpasqualino) - [#1930](https://github.com/rails-api/active_model_serializers/pull/1930) Ensure valid jsonapi when relationship has no links or data (@richmolj) Features: - [#1757](https://github.com/rails-api/active_model_serializers/pull/1757) Make serializer lookup chain configurable. (@NullVoxPopuli) - [#1968](https://github.com/rails-api/active_model_serializers/pull/1968) (@NullVoxPopuli) - Add controller namespace to default controller lookup - Provide a `namespace` render option - document how set the namespace in the controller for implicit lookup. - [#1791](https://github.com/rails-api/active_model_serializers/pull/1791) (@bf4, @youroff, @NullVoxPopuli) - Added `jsonapi_namespace_separator` config option. - [#1889](https://github.com/rails-api/active_model_serializers/pull/1889) Support key transformation for Attributes adapter (@iancanderson, @danbee) - [#1917](https://github.com/rails-api/active_model_serializers/pull/1917) Add `jsonapi_pagination_links_enabled` configuration option (@richmolj) - [#1797](https://github.com/rails-api/active_model_serializers/pull/1797) Only include 'relationships' when sideloading (@richmolj) Fixes: - [#1833](https://github.com/rails-api/active_model_serializers/pull/1833) Remove relationship links if they are null (@groyoh) - [#1881](https://github.com/rails-api/active_model_serializers/pull/1881) ActiveModelSerializers::Model correctly works with string keys (@yevhene) Misc: - [#1767](https://github.com/rails-api/active_model_serializers/pull/1767) Replace raising/rescuing `CollectionSerializer::NoSerializerError`, throw/catch `:no_serializer`. (@bf4) - [#1839](https://github.com/rails-api/active_model_serializers/pull/1839) `fields` tests demonstrating usage for both attributes and relationships. (@NullVoxPopuli) - [#1812](https://github.com/rails-api/active_model_serializers/pull/1812) add a code of conduct (@corainchicago) - [#1878](https://github.com/rails-api/active_model_serializers/pull/1878) Cache key generation for serializers now uses `ActiveSupport::Cache.expand_cache_key` instead of `Array#join` by default and is also overridable. This change should be backward-compatible. (@markiz) - [#1799](https://github.com/rails-api/active_model_serializers/pull/1799) Add documentation for setting the adapter. (@cassidycodes) - [#1909](https://github.com/rails-api/active_model_serializers/pull/1909) Add documentation for relationship links. (@vasilakisfil, @NullVoxPopuli) - [#1959](https://github.com/rails-api/active_model_serializers/pull/1959) Add documentation for root. (@shunsuke227ono) - [#1967](https://github.com/rails-api/active_model_serializers/pull/1967) Improve type method documentation. (@yukideluxe) ### [v0.10.2 (2016-07-05)](https://github.com/rails-api/active_model_serializers/compare/v0.10.1...v0.10.2) Fixes: - [#1814](https://github.com/rails-api/active_model_serializers/pull/1814) Ensuring read_multi works with fragment cache - [#1848](https://github.com/rails-api/active_model_serializers/pull/1848) Redefine associations on inherited serializers. (@EhsanYousefi) Misc: - [#1808](https://github.com/rails-api/active_model_serializers/pull/1808) Adds documentation for `fields` option. (@luizkowalski) ### [v0.10.1 (2016-06-16)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0...v0.10.1) Features: - [#1668](https://github.com/rails-api/active_model_serializers/pull/1668) Exclude nil and empty links. (@sigmike) - [#1426](https://github.com/rails-api/active_model_serializers/pull/1426) Add ActiveModelSerializers.config.default_includes (@empact) Fixes: - [#1754](https://github.com/rails-api/active_model_serializers/pull/1754) Fixes #1759, Grape integration, improves serialization_context missing error message on pagination. Document overriding CollectionSerializer#paginated?. (@bf4) Moved serialization_context creation to Grape formatter, so resource serialization works without explicit calls to the `render` helper method. Added Grape collection tests. (@onomated) - [#1287](https://github.com/rails-api/active_model_serializers/pull/1287) Pass `fields` options from adapter to serializer. (@vasilakisfil) - [#1710](https://github.com/rails-api/active_model_serializers/pull/1710) Prevent association loading when `include_data` option is set to `false`. (@groyoh) - [#1747](https://github.com/rails-api/active_model_serializers/pull/1747) Improve jsonapi mime type registration for Rails 5 (@remear) Misc: - [#1734](https://github.com/rails-api/active_model_serializers/pull/1734) Adds documentation for conditional attribute (@lambda2) - [#1685](https://github.com/rails-api/active_model_serializers/pull/1685) Replace `IncludeTree` with `IncludeDirective` from the jsonapi gem. ### [v0.10.0 (2016-05-17)](https://github.com/rails-api/active_model_serializers/compare/4a2d9853ba7...v0.10.0) Breaking changes: - [#1662](https://github.com/rails-api/active_model_serializers/pull/1662) Drop support for Rails 4.0 and Ruby 2.0.0. (@remear) Features: - [#1677](https://github.com/rails-api/active_model_serializers/pull/1677) Add `assert_schema`, `assert_request_schema`, `assert_request_response_schema`. (@bf4) - [#1697](https://github.com/rails-api/active_model_serializers/pull/1697) Include actual exception message with custom exceptions; `Test::Schema` exceptions are now `Minitest::Assertion`s. (@bf4) - [#1699](https://github.com/rails-api/active_model_serializers/pull/1699) String/Lambda support for conditional attributes/associations (@mtsmfm) - [#1687](https://github.com/rails-api/active_model_serializers/pull/1687) Only calculate `_cache_digest` (in `cache_key`) when `skip_digest` is false. (@bf4) - [#1647](https://github.com/rails-api/active_model_serializers/pull/1647) Restrict usage of `serializable_hash` options to the ActiveModel::Serialization and ActiveModel::Serializers::JSON interface. (@bf4) Fixes: - [#1700](https://github.com/rails-api/active_model_serializers/pull/1700) Support pagination link for Kaminari when no data is returned. (@iamnader) - [#1726](https://github.com/rails-api/active_model_serializers/pull/1726) Adds polymorphic option to association definition which includes association type/nesting in serializer (@cgmckeever) Misc: - [#1673](https://github.com/rails-api/active_model_serializers/pull/1673) Adds "How to" guide on using AMS with POROs (@DrSayre) - [#1730](https://github.com/rails-api/active_model_serializers/pull/1730) Adds documentation for overriding default serializer based on conditions (@groyoh/@cgmckeever) ### [v0.10.0.rc5 (2016-04-04)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc4...v0.10.0.rc5) Breaking changes: - [#1645](https://github.com/rails-api/active_model_serializers/pull/1645) Changed :dashed key transform to :dash. (@remear) - [#1574](https://github.com/rails-api/active_model_serializers/pull/1574) Default key case for the JsonApi adapter changed to dashed. (@remear) Features: - [#1645](https://github.com/rails-api/active_model_serializers/pull/1645) Transform keys referenced in values. (@remear) - [#1650](https://github.com/rails-api/active_model_serializers/pull/1650) Fix serialization scope options `scope`, `scope_name` take precedence over `serialization_scope` in the controller. Fix tests that required tearing down dynamic methods. (@bf4) - [#1644](https://github.com/rails-api/active_model_serializers/pull/1644) Include adapter name in cache key so that the same serializer can be cached per adapter. (@bf4 via #1346 by @kevintyll) - [#1642](https://github.com/rails-api/active_model_serializers/pull/1642) Prefer object.cache_key over the generated cache key. (@bf4 via #1346 by @kevintyll) - [#1637](https://github.com/rails-api/active_model_serializers/pull/1637) Make references to 'ActionController::Base.cache_store' explicit in order to avoid issues when application controllers inherit from 'ActionController::API'. (@ncuesta) - [#1633](https://github.com/rails-api/active_model_serializers/pull/1633) Yield 'serializer' to serializer association blocks. (@bf4) - [#1616](https://github.com/rails-api/active_model_serializers/pull/1616) SerializableResource handles no serializer like controller. (@bf4) - [#1618](https://github.com/rails-api/active_model_serializers/issues/1618) Get collection root key for empty collection from explicit serializer option, when possible. (@bf4) - [#1574](https://github.com/rails-api/active_model_serializers/pull/1574) Provide key translation. (@remear) - [#1494](https://github.com/rails-api/active_model_serializers/pull/1494) Make serializers serializalbe (using the Attributes adapter by default). (@bf4) - [#1550](https://github.com/rails-api/active_model_serializers/pull/1550) Add Rails url_helpers to `SerializationContext` for use in links. (@remear, @bf4) - [#1004](https://github.com/rails-api/active_model_serializers/pull/1004) JSON API errors object implementation. - Only implements `detail` and `source` as derived from `ActiveModel::Error` - Provides checklist of remaining questions and remaining parts of the spec. - [#1515](https://github.com/rails-api/active_model_serializers/pull/1515) Adds support for symbols to the `ActiveModel::Serializer.type` method. (@groyoh) - [#1504](https://github.com/rails-api/active_model_serializers/pull/1504) Adds the changes missing from #1454 and add more tests for resource identifier and relationship objects. Fix association block with link returning `data: nil`.(@groyoh) - [#1372](https://github.com/rails-api/active_model_serializers/pull/1372) Support cache_store.read_multi. (@LcpMarvel) - [#1018](https://github.com/rails-api/active_model_serializers/pull/1018) Add more tests and docs for top-level links. (@leandrocp) - [#1454](https://github.com/rails-api/active_model_serializers/pull/1454) Add support for relationship-level links and meta attributes. (@beauby) - [#1340](https://github.com/rails-api/active_model_serializers/pull/1340) Add support for resource-level meta. (@beauby) Fixes: - [#1657](https://github.com/rails-api/active_model_serializers/pull/1657) Add missing missing require "active_support/json". (@andreaseger) - [#1661](https://github.com/rails-api/active_model_serializers/pull/1661) Fixes `read_attribute_for_serialization` not seeing methods defined in serialization superclass (#1653, #1658, #1660), introduced in #1650. (@bf4) - [#1651](https://github.com/rails-api/active_model_serializers/pull/1651) Fix deserialization of nil relationships. (@NullVoxPopuli) - [#1480](https://github.com/rails-api/active_model_serializers/pull/1480) Fix setting of cache_store from Rails configuration. (@bf4) Fix unintentional mutating of value in memory cache store. (@groyoh) - [#1622](https://github.com/rails-api/active_model_serializers/pull/1622) Fragment cache changed from per-record to per-serializer. Now, two serializers that use the same model may be separately cached. (@lserman) - [#1478](https://github.com/rails-api/active_model_serializers/pull/1478) Cache store will now be correctly set when serializers are loaded *before* Rails initializes. (@bf4) - [#1570](https://github.com/rails-api/active_model_serializers/pull/1570) Fixed pagination issue with last page size. (@bmorrall) - [#1516](https://github.com/rails-api/active_model_serializers/pull/1516) No longer return a nil href when only adding meta to a relationship link. (@groyoh) - [#1458](https://github.com/rails-api/active_model_serializers/pull/1458) Preserve the serializer type when fragment caching. (@bdmac) - [#1477](https://github.com/rails-api/active_model_serializers/pull/1477) Fix `fragment_cached?` method to check if caching. (@bdmac) - [#1501](https://github.com/rails-api/active_model_serializers/pull/1501) Adds tests for SerializableResource::use_adapter?,doc typos (@domitian) - [#1488](https://github.com/rails-api/active_model_serializers/pull/1488) Require ActiveSupport's string inflections (@nate00) Misc: - [#1608](https://github.com/rails-api/active_model_serializers/pull/1608) Move SerializableResource to ActiveModelSerializers (@groyoh) - [#1602](https://github.com/rails-api/active_model_serializers/pull/1602) Add output examples to Adapters docs (@remear) - [#1557](https://github.com/rails-api/active_model_serializers/pull/1557) Update docs regarding overriding the root key (@Jwan622) - [#1471](https://github.com/rails-api/active_model_serializers/pull/1471) [Cleanup] Serializer caching is its own concern. (@bf4) - [#1482](https://github.com/rails-api/active_model_serializers/pull/1482) Document JSON API implementation defs and progress in class. (@bf4) - [#1551](https://github.com/rails-api/active_model_serializers/pull/1551) Added codebeat badge (@korzonek) - [#1527](https://github.com/rails-api/active_model_serializers/pull/1527) Refactor fragment cache class. (@groyoh) - [#1560](https://github.com/rails-api/active_model_serializers/pull/1560) Update rubocop and address its warnings. (@bf4 @groyoh) - [#1545](https://github.com/rails-api/active_model_serializers/pull/1545) Document how to pass arbitrary options to the serializer (@CodedBeardedSignedTaylor) - [#1496](https://github.com/rails-api/active_model_serializers/pull/1496) Run all branches against JRuby on CI (@nadavshatz) - [#1559](https://github.com/rails-api/active_model_serializers/pull/1559) Add a deprecation DSL. (@bf4 @groyoh) - [#1543](https://github.com/rails-api/active_model_serializers/pull/1543) Add the changes missing from #1535. (@groyoh) - [#1535](https://github.com/rails-api/active_model_serializers/pull/1535) Move the adapter and adapter folder to active_model_serializers folder and changes the module namespace. (@domitian @bf4) - [#1497](https://github.com/rails-api/active_model_serializers/pull/1497) Add JRuby-9000 to appveyor.yml(@corainchicago) - [#1420](https://github.com/rails-api/active_model_serializers/pull/1420) Adds tests and documentation for polymorphism(@marcgarreau) ### [v0.10.0.rc4 (2016-01-27)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc3...v0.10.0.rc4) Breaking changes: - [#1360](https://github.com/rails-api/active_model_serializers/pull/1360) [#1369](https://github.com/rails-api/active_model_serializers/pull/1369) Drop support for Ruby 1.9.3 (@karaAJC, @maurogeorge) - [#1131](https://github.com/rails-api/active_model_serializers/pull/1131) Remove Serializer#root_name (@beauby) - [#1138](https://github.com/rails-api/active_model_serializers/pull/1138) Introduce Adapter::Base (@bf4) * Adapters now inherit Adapter::Base. 'Adapter' is now a module, no longer a class. * using a class as a namespace that you also inherit from is complicated and circular at times i.e. buggy (see https://github.com/rails-api/active_model_serializers/pull/1177) * The class methods on Adapter aren't necessarily related to the instance methods, they're more Adapter functions. * named `Base` because it's a Rails-ism. * It helps to isolate and highlight what the Adapter interface actually is. - [#1418](https://github.com/rails-api/active_model_serializers/pull/1418) serialized collections now use the root option as is; now, only the root derived from the serializer or object is always pluralized. Features: - [#1406](https://github.com/rails-api/active_model_serializers/pull/1406) Allow for custom dynamic values in JSON API links (@beauby) - [#1270](https://github.com/rails-api/active_model_serializers/pull/1270) Adds `assert_response_schema` test helper (@maurogeorge) - [#1099](https://github.com/rails-api/active_model_serializers/pull/1099) Adds `assert_serializer` test helper (@maurogeorge) - [#1403](https://github.com/rails-api/active_model_serializers/pull/1403) Add support for if/unless on attributes/associations (@beauby) - [#1248](https://github.com/rails-api/active_model_serializers/pull/1248) Experimental: Add support for JSON API deserialization (@beauby) - [#1378](https://github.com/rails-api/active_model_serializers/pull/1378) Change association blocks to be evaluated in *serializer* scope, rather than *association* scope. (@bf4) * Syntax changes from e.g. `has_many :titles do customers.pluck(:title) end` (in #1356) to `has_many :titles do object.customers.pluck(:title) end` - [#1356](https://github.com/rails-api/active_model_serializers/pull/1356) Add inline syntax for attributes and associations (@bf4 @beauby @noahsilas) * Allows defining attributes so that they don't conflict with existing methods. e.g. `attribute :title do 'Mr. Topum Hat' end` * Allows defining associations so that they don't conflict with existing methods. e.g. `has_many :titles do customers.pluck(:title) end` * Allows dynamic associations, as compared to compare to using [`virtual_value`](https://github.com/rails-api/active_model_serializers/pull/1356#discussion_r47146466). e.g. `has_many :reviews, virtual_value: [{ id: 1 }, { id: 2 }]` * Removes dynamically defined methods on the serializer - [#1336](https://github.com/rails-api/active_model_serializers/pull/1336) Added support for Grape >= 0.13, < 1.0 (@johnhamelink) - [#1322](https://github.com/rails-api/active_model_serializers/pull/1322) Instrumenting rendering of resources (@bf4, @maurogeorge) - [#1291](https://github.com/rails-api/active_model_serializers/pull/1291) Add logging (@maurogeorge) - [#1272](https://github.com/rails-api/active_model_serializers/pull/1272) Add PORO serializable base class: ActiveModelSerializers::Model (@bf4) - [#1255](https://github.com/rails-api/active_model_serializers/pull/1255) Make more class attributes inheritable (@bf4) - [#1249](https://github.com/rails-api/active_model_serializers/pull/1249) Inheritance of serializer inheriting the cache configuration(@Rodrigora) - [#1247](https://github.com/rails-api/active_model_serializers/pull/1247) Add support for toplevel JSON API links (@beauby) - [#1246](https://github.com/rails-api/active_model_serializers/pull/1246) Add support for resource-level JSON API links (@beauby) - [#1225](https://github.com/rails-api/active_model_serializers/pull/1225) Better serializer lookup, use nested serializer when it exists (@beauby) - [#1213](https://github.com/rails-api/active_model_serializers/pull/1213) `type` directive for serializer to control type field with json-api adapter (@youroff) - [#1172](https://github.com/rails-api/active_model_serializers/pull/1172) Better serializer registration, get more than just the first module (@bf4) - [#1158](https://github.com/rails-api/active_model_serializers/pull/1158) Add support for wildcards in `include` option (@beauby) - [#1127](https://github.com/rails-api/active_model_serializers/pull/1127) Add support for nested associations for JSON and Attributes adapters via the `include` option (@NullVoxPopuli, @beauby). - [#1050](https://github.com/rails-api/active_model_serializers/pull/1050) Add support for toplevel jsonapi member (@beauby, @bf4) - [#1251](https://github.com/rails-api/active_model_serializers/pull/1251) Rename ArraySerializer to CollectionSerializer for clarity, add ActiveModelSerializers.config.collection_serializer (@bf4) - [#1295](https://github.com/rails-api/active_model_serializers/pull/1295) Add config `serializer_lookup_enabled` that, when disabled, requires serializers to explicitly specified. (@trek) Fixes: - [#1352](https://github.com/rails-api/active_model_serializers/pull/1352) Fix generators; Isolate Rails-specifc code in Railties (@dgynn, @bf4) - [#1384](https://github.com/rails-api/active_model_serializers/pull/1384)Fix database state leaking across tests (@bf4) - [#1297](https://github.com/rails-api/active_model_serializers/pull/1297) Fix `fields` option to restrict relationships as well (@beauby) - [#1239](https://github.com/rails-api/active_model_serializers/pull/1239) Fix duplicates in JSON API compound documents (@beauby) - [#1214](https://github.com/rails-api/active_model_serializers/pull/1214) retrieve the key from the reflection options when building associations (@NullVoxPopuli, @hut8) - [#1358](https://github.com/rails-api/active_model_serializers/pull/1358) Handle serializer file paths with spaces (@rwstauner, @bf4) - [#1195](https://github.com/rails-api/active_model_serializers/pull/1195) Fix id override (@beauby) - [#1185](https://github.com/rails-api/active_model_serializers/pull/1185) Fix options passing in Json and Attributes adapters (@beauby) Misc: - [#1383](https://github.com/rails-api/active_model_serializers/pull/1383) Simplify reflections handling (@beauby) - [#1370](https://github.com/rails-api/active_model_serializers/pull/1370) Simplify attributes handling via a mixin (@beauby) - [#1301](https://github.com/rails-api/active_model_serializers/pull/1301) Mapping JSON API spec / schema to AMS (@bf4) - [#1271](https://github.com/rails-api/active_model_serializers/pull/1271) Handle no serializer source file to digest (@bf4) - [#1260](https://github.com/rails-api/active_model_serializers/pull/1260) Serialization and Cache Documentation (@bf4) - [#1259](https://github.com/rails-api/active_model_serializers/pull/1259) Add more info to CONTRIBUTING (@bf4) - [#1233](https://github.com/rails-api/active_model_serializers/pull/1233) Top-level meta and meta_key options no longer handled at serializer level (@beauby) - [#1232](https://github.com/rails-api/active_model_serializers/pull/1232) fields option no longer handled at serializer level (@beauby) - [#1220](https://github.com/rails-api/active_model_serializers/pull/1220) Remove empty rubocop.rake (@maurogeorge) - [#1178](https://github.com/rails-api/active_model_serializers/pull/1178) env CAPTURE_STDERR=false lets devs see hard failures (@bf4) - [#1177](https://github.com/rails-api/active_model_serializers/pull/1177) Remove Adapter autoloads in favor of require (@bf4) - [#1117](https://github.com/rails-api/active_model_serializers/pull/1117) FlattenJson adapter no longer inherits Json adapter, renamed to Attributes (@bf4) - [#1171](https://github.com/rails-api/active_model_serializers/pull/1171) add require statements to top of file (@shicholas) - [#1167](https://github.com/rails-api/active_model_serializers/pull/1167) Delegate Serializer.attributes to Serializer.attribute (@bf4) - [#1174](https://github.com/rails-api/active_model_serializers/pull/1174) Consistently refer to the 'JSON API' and the 'JsonApi' adapter (@bf4) - [#1173](https://github.com/rails-api/active_model_serializers/pull/1173) Comment private accessor warnings (@bf4) - [#1166](https://github.com/rails-api/active_model_serializers/pull/1166) Prefer methods over instance variables (@bf4) - [#1168](https://github.com/rails-api/active_model_serializers/pull/1168) Fix appveyor failure cache not being expired (@bf4) - [#1161](https://github.com/rails-api/active_model_serializers/pull/1161) Remove duplicate test helper (@bf4) - [#1360](https://github.com/rails-api/active_model_serializers/pull/1360) Update CI to test 2.2.2 -> 2.2.3 (@karaAJC) - [#1371](https://github.com/rails-api/active_model_serializers/pull/1371) Refactor, update, create documentation (@bf4) ### [v0.10.0.rc3 (2015-09-16)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc2...v0.10.0.rc3) - [#1129](https://github.com/rails-api/active_model_serializers/pull/1129) Remove SerializableResource.serialize in favor of `.new` (@bf4) - [#1155](https://github.com/rails-api/active_model_serializers/pull/1155) Outside controller use tutorial (@CodedBeardedSignedTaylor) - [#1154](https://github.com/rails-api/active_model_serializers/pull/1154) Rubocop fixes for issues introduced by #1089 (@NullVoxPopuli) - [#1089](https://github.com/rails-api/active_model_serializers/pull/1089) Add ActiveModelSerializers.logger with default null device (@bf4) - [#1109](https://github.com/rails-api/active_model_serializers/pull/1109) Make better use of Minitest's lifecycle (@bf4) - [#1144](https://github.com/rails-api/active_model_serializers/pull/1144) Fix Markdown to adapters documentation (@bacarini) - [#1121](https://github.com/rails-api/active_model_serializers/pull/1121) Refactor `add_links` in JSONAPI adapter. (@beauby) - [#1150](https://github.com/rails-api/active_model_serializers/pull/1150) Remove legacy method accidentally reintroduced in #1017 (@beauby) - [#1149](https://github.com/rails-api/active_model_serializers/pull/1149) Update README with nested included association example. (@mattmueller) - [#1110](https://github.com/rails-api/active_model_serializers/pull/1110) Add lint tests for AR models (@beauby) - [#1131](https://github.com/rails-api/active_model_serializers/pull/1131) Extended format for JSONAPI `include` option (@beauby) * adds extended format for `include` option to JsonApi adapter - [#1142](https://github.com/rails-api/active_model_serializers/pull/1142) Updating wording on cache expiry in README (@leighhalliday) - [#1140](https://github.com/rails-api/active_model_serializers/pull/1140) Fix typo in fieldset exception (@lautis) - [#1132](https://github.com/rails-api/active_model_serializers/pull/1132) Get rid of unnecessary instance variables, and implied dependencies. (@beauby) - [#1139](https://github.com/rails-api/active_model_serializers/pull/1139) Documentation for serializing resources without render (@PericlesTheo) - [#1017](https://github.com/rails-api/active_model_serializers/pull/1017) Make Adapters registerable so they are not namespace-constrained (@bf4) - [#1120](https://github.com/rails-api/active_model_serializers/pull/1120) Add windows platform to loading sqlite3 (@Eric-Guo) - [#1123](https://github.com/rails-api/active_model_serializers/pull/1123) Remove url options (@bacarini) - [#1093](https://github.com/rails-api/active_model_serializers/pull/1093) Factor `with_adapter` + force cache clear before each test. (@beauby) - [#1095](https://github.com/rails-api/active_model_serializers/pull/1095) Add documentation about configuration options. (@beauby) - [#1069](https://github.com/rails-api/active_model_serializers/pull/1069) Add test coverage; account for no artifacts on CI (@bf4) - [#1103](https://github.com/rails-api/active_model_serializers/pull/1103) Move `id` and `json_api_type` methods from `Serializer` to `JsonApi`. (@beauby) - [#1106](https://github.com/rails-api/active_model_serializers/pull/1106) Add Style enforcer (via Rubocop) (@bf4) - [#1079](https://github.com/rails-api/active_model_serializers/pull/1079) Add ArraySerializer#object like Serializer (@bf4) - [#1096](https://github.com/rails-api/active_model_serializers/pull/1096) Fix definition of serializer attributes with multiple calls to `attri… (@beauby) - [#1105](https://github.com/rails-api/active_model_serializers/pull/1105) Add ActiveRecord-backed fixtures. (@beauby) - [#1108](https://github.com/rails-api/active_model_serializers/pull/1108) Better lint (@bf4) - [#1102](https://github.com/rails-api/active_model_serializers/pull/1102) Remove remains of `embed` option. (@beauby) - [#1090](https://github.com/rails-api/active_model_serializers/pull/1090) Clarify AMS dependencies (@bf4) - [#1081](https://github.com/rails-api/active_model_serializers/pull/1081) Add configuration option to set resource type to singular/plural (@beauby) - [#1067](https://github.com/rails-api/active_model_serializers/pull/1067) Fix warnings (@bf4) - [#1066](https://github.com/rails-api/active_model_serializers/pull/1066) Adding appveyor to the project (@joaomdmoura, @Eric-Guo, @bf4) - [#1071](https://github.com/rails-api/active_model_serializers/pull/1071) Make testing suite running and pass in Windows (@Eric-Guo, @bf4) - [#1041](https://github.com/rails-api/active_model_serializers/pull/1041) Adding pagination links (@bacarini) * adds support for `pagination links` at top level of JsonApi adapter - [#1063](https://github.com/rails-api/active_model_serializers/pull/1063) Lead by example: lint PORO model (@bf4) - [#1](https://github.com/rails-api/active_model_serializers/pull/1) Test caller line parsing and digesting (@bf4) - [#1048](https://github.com/rails-api/active_model_serializers/pull/1048) Let FlattenJson adapter decide it doesn't include meta (@bf4) - [#1060](https://github.com/rails-api/active_model_serializers/pull/1060) Update fragment cache to support namespaced objects (@aaronlerch) - [#1052](https://github.com/rails-api/active_model_serializers/pull/1052) Use underscored json_root when serializing a collection (@whatthewhat) - [#1051](https://github.com/rails-api/active_model_serializers/pull/1051) Fix some invalid JSON in docs (@tjschuck) - [#1049](https://github.com/rails-api/active_model_serializers/pull/1049) Fix incorrect s/options = {}/options ||= {} (@bf4) - [#1037](https://github.com/rails-api/active_model_serializers/pull/1037) allow for type attribute (@lanej) - [#1034](https://github.com/rails-api/active_model_serializers/pull/1034) allow id attribute to be overriden (@lanej) - [#1035](https://github.com/rails-api/active_model_serializers/pull/1035) Fixed Comments highlight (@artLopez) - [#1031](https://github.com/rails-api/active_model_serializers/pull/1031) Disallow to define multiple associations at once (@bolshakov) - [#1032](https://github.com/rails-api/active_model_serializers/pull/1032) Wrap railtie requirement with rescue (@elliotlarson) - [#1026](https://github.com/rails-api/active_model_serializers/pull/1026) Bump Version Number to 0.10.0.rc2 (@jfelchner) - [#985](https://github.com/rails-api/active_model_serializers/pull/985) Associations implementation refactoring (@bolshakov) - [#954](https://github.com/rails-api/active_model_serializers/pull/954) Encapsulate serialization in ActiveModel::SerializableResource (@bf4) - [#972](https://github.com/rails-api/active_model_serializers/pull/972) Capture app warnings on test run (@bf4) - [#1019](https://github.com/rails-api/active_model_serializers/pull/1019) Improve README.md (@baojjeu) - [#998](https://github.com/rails-api/active_model_serializers/pull/998) Changing root to model class name (@joaomdmoura) - [#1006](https://github.com/rails-api/active_model_serializers/pull/1006) Fix adapter inflection bug for api -> API (@bf4) - [#1016](https://github.com/rails-api/active_model_serializers/pull/1016) require rails/railtie before subclassing Rails::Railtie (@bf4) - [#1013](https://github.com/rails-api/active_model_serializers/pull/1013) Root option with empty array support (@vyrak, @mareczek) - [#994](https://github.com/rails-api/active_model_serializers/pull/994) Starting Docs structure (@joaomdmoura) - [#1007](https://github.com/rails-api/active_model_serializers/pull/1007) Bug fix for ArraySerializer json_key (@jiajiawang) - [#1003](https://github.com/rails-api/active_model_serializers/pull/1003) Fix transient test failures (@Rodrigora) - [#996](https://github.com/rails-api/active_model_serializers/pull/996) Add linter for serializable resource (@bf4) - [#990](https://github.com/rails-api/active_model_serializers/pull/990) Adding json-api meta test (@joaomdmoura) - [#984](https://github.com/rails-api/active_model_serializers/pull/984) Add option "key" to serializer associations (@Rodrigora) - [#982](https://github.com/rails-api/active_model_serializers/pull/982) Fix typo (@bf4) - [#981](https://github.com/rails-api/active_model_serializers/pull/981) Remove unused PORO#to_param (@bf4) - [#978](https://github.com/rails-api/active_model_serializers/pull/978) fix generators template bug (@regonn) - [#975](https://github.com/rails-api/active_model_serializers/pull/975) Fixes virtual value not being used (@GriffinHeart) - [#970](https://github.com/rails-api/active_model_serializers/pull/970) Fix transient tests failures (@Rodrigora) - [#962](https://github.com/rails-api/active_model_serializers/pull/962) Rendering objects that doesn't have serializers (@bf4, @joaomdmoura, @JustinAiken) - [#939](https://github.com/rails-api/active_model_serializers/pull/939) Use a more precise generated cache key (@aaronlerch) - [#971](https://github.com/rails-api/active_model_serializers/pull/971) Restore has_one to generator (@bf4) - [#965](https://github.com/rails-api/active_model_serializers/pull/965) options fedault valueserializable_hash and as_json (@bf4) - [#959](https://github.com/rails-api/active_model_serializers/pull/959) TYPO on README.md (@kangkyu) ### [v0.10.0.rc2 (2015-06-16)](https://github.com/rails-api/active_model_serializers/compare/v0.10.0.rc1...v0.10.0.rc2) - [#958](https://github.com/rails-api/active_model_serializers/pull/958) Splitting json adapter into two (@joaomdmoura) * adds FlattenJSON as default adapter - [#953](https://github.com/rails-api/active_model_serializers/pull/953) use model name to determine the type (@lsylvester) * uses model name to determine the type - [#949](https://github.com/rails-api/active_model_serializers/pull/949) Don't pass serializer option to associated serializers (@bf4, @edwardloveall) - [#902](https://github.com/rails-api/active_model_serializers/pull/902) Added serializer file digest to the cache_key (@cristianbica) - [#948](https://github.com/rails-api/active_model_serializers/pull/948) AMS supports JSONAPI 1.0 instead of RC4 (@SeyZ) - [#936](https://github.com/rails-api/active_model_serializers/pull/936) Include meta when using json adapter with custom root (@chrisbranson) - [#942](https://github.com/rails-api/active_model_serializers/pull/942) Small code styling issue (@thiagofm) - [#930](https://github.com/rails-api/active_model_serializers/pull/930) Reverting PR #909 (@joaomdmoura) - [#924](https://github.com/rails-api/active_model_serializers/pull/924) Avoid unecessary calls to attribute methods when fragment caching (@navinpeiris) - [#925](https://github.com/rails-api/active_model_serializers/pull/925) Updates JSON API Adapter to generate RC4 schema (@benedikt) * adds JSON API support 1.0 - [#918](https://github.com/rails-api/active_model_serializers/pull/918) Adding rescue_with_handler to clear state (@ryansch) - [#909](https://github.com/rails-api/active_model_serializers/pull/909) Defining Json-API Adapter as Default (@joaomdmoura) * remove root key option and split JSON adapter - [#914](https://github.com/rails-api/active_model_serializers/pull/914) Prevent possible duplicated attributes in serializer (@groyoh) - [#880](https://github.com/rails-api/active_model_serializers/pull/880) Inabling subclasses serializers to inherit attributes (@groyoh) - [#913](https://github.com/rails-api/active_model_serializers/pull/913) Avoiding the serializer option when instantiating a new one for ArraySerializer Fixed #911 (@groyoh) - [#897](https://github.com/rails-api/active_model_serializers/pull/897) Allow to define custom serializer for given class (@imanel) - [#892](https://github.com/rails-api/active_model_serializers/pull/892) Fixed a bug that appeared when json adapter serialize a nil association (@groyoh) - [#895](https://github.com/rails-api/active_model_serializers/pull/895) Adding a test to cover 'meta' and 'meta_key' attr_readers (@adomokos) - [#894](https://github.com/rails-api/active_model_serializers/pull/894) Fixing typos in README.md (@adomokos) - [#888](https://github.com/rails-api/active_model_serializers/pull/888) Changed duplicated test name in action controller test (@groyoh) - [#890](https://github.com/rails-api/active_model_serializers/pull/890) Remove unused method `def_serializer` (@JustinAiken) - [#887](https://github.com/rails-api/active_model_serializers/pull/887) Fixing tests on JRuby (@joaomdmoura) - [#885](https://github.com/rails-api/active_model_serializers/pull/885) Updates rails versions for test and dev (@tonyta) ### [v0.10.0.rc1 (2015-04-22)](https://github.com/rails-api/active_model_serializers/compare/86fc7d7227f3ce538fcb28c1e8c7069ce311f0e1...v0.10.0.rc1) - [#810](https://github.com/rails-api/active_model_serializers/pull/810) Adding Fragment Cache to AMS (@joaomdmoura) * adds fragment cache support - [#868](https://github.com/rails-api/active_model_serializers/pull/868) Fixed a bug that appears when a nil association is included (@groyoh) - [#861](https://github.com/rails-api/active_model_serializers/pull/861) README: Add emphasis to single-word difference (@machty) - [#858](https://github.com/rails-api/active_model_serializers/pull/858) Included resource fixes (@mateomurphy) - [#853](https://github.com/rails-api/active_model_serializers/pull/853) RC3 Updates for JSON API (@mateomurphy) - [#852](https://github.com/rails-api/active_model_serializers/pull/852) Fix options merge order in `each_association` (@mateomurphy) - [#850](https://github.com/rails-api/active_model_serializers/pull/850) Use association value for determining serializer used (@mateomurphy) - [#843](https://github.com/rails-api/active_model_serializers/pull/843) Remove the mailing list from the README (@JoshSmith) - [#842](https://github.com/rails-api/active_model_serializers/pull/842) Add notes on how you can help to contributing documentation (@JoshSmith) - [#833](https://github.com/rails-api/active_model_serializers/pull/833) Cache serializers for class (@lsylvester) - [#837](https://github.com/rails-api/active_model_serializers/pull/837) Store options in array serializers (@kurko) - [#836](https://github.com/rails-api/active_model_serializers/pull/836) Makes passed in options accessible inside serializers (@kurko) - [#773](https://github.com/rails-api/active_model_serializers/pull/773) Make json api adapter 'include' option accept an array (@sweatypitts) - [#830](https://github.com/rails-api/active_model_serializers/pull/830) Add contributing readme (@JoshSmith) - [#811](https://github.com/rails-api/active_model_serializers/pull/811) Reimplement serialization scope and scope_name (@mateomurphy) - [#725](https://github.com/rails-api/active_model_serializers/pull/725) Support has_one to be compatible with 0.8.x (@ggordon) * adds `has_one` attribute for backwards compatibility - [#822](https://github.com/rails-api/active_model_serializers/pull/822) Replace has_one with attribute in template (@bf4) - [#821](https://github.com/rails-api/active_model_serializers/pull/821) Fix explicit serializer for associations (@wjordan) - [#798](https://github.com/rails-api/active_model_serializers/pull/798) Fix lost test `test_include_multiple_posts_and_linked` (@donbobka) - [#807](https://github.com/rails-api/active_model_serializers/pull/807) Add Overriding attribute methods section to README. (@alexstophel) - [#693](https://github.com/rails-api/active_model_serializers/pull/693) Cache Support at AMS 0.10.0 (@joaomdmoura) * adds cache support to attributes and associations. - [#792](https://github.com/rails-api/active_model_serializers/pull/792) Association overrides (@kurko) * adds method to override association - [#794](https://github.com/rails-api/active_model_serializers/pull/794) add to_param for correct URL generation (@carlesjove) ### v0.10.0-pre - [Introduce Adapter](https://github.com/rails-api/active_model_serializers/commit/f00fe5595ddf741dc26127ed8fe81adad833ead5) - Prefer `ActiveModel::Serializer` to `ActiveModelSerializers`: - [Namespace](https://github.com/rails-api/active_model_serializers/commit/729a823868e8c7ac86c653fcc7100ee511e08cb6#diff-fe7aa2941c19a41ccea6e52940d84016). - [README](https://github.com/rails-api/active_model_serializers/commit/4a2d9853ba7486acc1747752982aa5650e7fd6e9). ## 0.09.x ### v0.9.3 (2015/01/21 20:29 +00:00) Features: - [#774](https://github.com/rails-api/active_model_serializers/pull/774) Fix nested include attributes (@nhocki) - [#771](https://github.com/rails-api/active_model_serializers/pull/771) Make linked resource type names consistent with root names (@sweatypitts) - [#696](https://github.com/rails-api/active_model_serializers/pull/696) Explicitly set serializer for associations (@ggordon) - [#700](https://github.com/rails-api/active_model_serializers/pull/700) sparse fieldsets (@arenoir) - [#768](https://github.com/rails-api/active_model_serializers/pull/768) Adds support for `meta` and `meta_key` attribute (@kurko) ### v0.9.1 (2014/12/04 11:54 +00:00) - [#707](https://github.com/rails-api/active_model_serializers/pull/707) A Friendly Note on Which AMS Version to Use (@jherdman) - [#730](https://github.com/rails-api/active_model_serializers/pull/730) Fixes nested has_many links in JSONAPI (@kurko) - [#718](https://github.com/rails-api/active_model_serializers/pull/718) Allow overriding the adapter with render option (@ggordon) - [#720](https://github.com/rails-api/active_model_serializers/pull/720) Rename attribute with :key (0.8.x compatibility) (@ggordon) - [#728](https://github.com/rails-api/active_model_serializers/pull/728) Use type as key for linked resources (@kurko) - [#729](https://github.com/rails-api/active_model_serializers/pull/729) Use the new beta build env on Travis (@joshk) - [#703](https://github.com/rails-api/active_model_serializers/pull/703) Support serializer and each_serializer options in renderer (@ggordon, @mieko) - [#727](https://github.com/rails-api/active_model_serializers/pull/727) Includes links inside of linked resources (@kurko) - [#726](https://github.com/rails-api/active_model_serializers/pull/726) Bugfix: include nested has_many associations (@kurko) - [#722](https://github.com/rails-api/active_model_serializers/pull/722) Fix infinite recursion (@ggordon) - [#1](https://github.com/rails-api/active_model_serializers/pull/1) Allow for the implicit use of ArraySerializer when :each_serializer is specified (@mieko) - [#692](https://github.com/rails-api/active_model_serializers/pull/692) Include 'linked' member for json-api collections (@ggordon) - [#714](https://github.com/rails-api/active_model_serializers/pull/714) Define as_json instead of to_json (@guilleiguaran) - [#710](https://github.com/rails-api/active_model_serializers/pull/710) JSON-API: Don't include linked section if associations are empty (@guilleiguaran) - [#711](https://github.com/rails-api/active_model_serializers/pull/711) Fixes rbx gems bundling on TravisCI (@kurko) - [#709](https://github.com/rails-api/active_model_serializers/pull/709) Add type key when association name is different than object type (@guilleiguaran) - [#708](https://github.com/rails-api/active_model_serializers/pull/708) Handle correctly null associations (@guilleiguaran) - [#691](https://github.com/rails-api/active_model_serializers/pull/691) Fix embed option for associations (@jacob-s-son) - [#689](https://github.com/rails-api/active_model_serializers/pull/689) Fix support for custom root in JSON-API adapter (@guilleiguaran) - [#685](https://github.com/rails-api/active_model_serializers/pull/685) Serialize ids as strings in JSON-API adapter (@guilleiguaran) - [#684](https://github.com/rails-api/active_model_serializers/pull/684) Refactor adapters to implement support for array serialization (@guilleiguaran) - [#682](https://github.com/rails-api/active_model_serializers/pull/682) Include root by default in JSON-API serializers (@guilleiguaran) - [#625](https://github.com/rails-api/active_model_serializers/pull/625) Add DSL for urls (@JordanFaust) - [#677](https://github.com/rails-api/active_model_serializers/pull/677) Add support for embed: :ids option for in associations (@guilleiguaran) - [#681](https://github.com/rails-api/active_model_serializers/pull/681) Check superclasses for Serializers (@quainjn) - [#680](https://github.com/rails-api/active_model_serializers/pull/680) Add support for root keys (@NullVoxPopuli) - [#675](https://github.com/rails-api/active_model_serializers/pull/675) Support Rails 4.2.0 (@tricknotes) - [#667](https://github.com/rails-api/active_model_serializers/pull/667) Require only activemodel instead of full rails (@guilleiguaran) - [#653](https://github.com/rails-api/active_model_serializers/pull/653) Add "_test" suffix to JsonApi::HasManyTest filename. (@alexgenco) - [#631](https://github.com/rails-api/active_model_serializers/pull/631) Update build badge URL (@craiglittle) ### 0.9.0.alpha1 - January 7, 2014 ### 0.9.0.pre * The following methods were removed - Model#active\_model\_serializer - Serializer#include! - Serializer#include? - Serializer#attr\_disabled= - Serializer#cache - Serializer#perform\_caching - Serializer#schema (needs more discussion) - Serializer#attribute - Serializer#include\_#{name}? (filter method added) - Serializer#attributes (took a hash) * The following things were added - Serializer#filter method - CONFIG object * Remove support for ruby 1.8 versions. * Require rails >= 3.2. * Serializers for associations are being looked up in a parent serializer's namespace first. Same with controllers' namespaces. * Added a "prefix" option in case you want to use a different version of serializer. * Serializers default namespace can be set in `default_serializer_options` and inherited by associations. * [Beginning of rewrite: c65d387705ec534db171712671ba7fcda4f49f68](https://github.com/rails-api/active_model_serializers/commit/c65d387705ec534db171712671ba7fcda4f49f68) ## 0.08.x ### v0.8.3 (2014/12/10 14:45 +00:00) - [#753](https://github.com/rails-api/active_model_serializers/pull/753) Test against Ruby 2.2 on Travis CI (@tricknotes) - [#745](https://github.com/rails-api/active_model_serializers/pull/745) Missing a word (@jockee) ### v0.8.2 (2014/09/01 21:00 +00:00) - [#612](https://github.com/rails-api/active_model_serializers/pull/612) Feature/adapter (@bolshakov) * adds adapters pattern - [#615](https://github.com/rails-api/active_model_serializers/pull/615) Rails does not support const_defined? in development mode (@tpitale) - [#613](https://github.com/rails-api/active_model_serializers/pull/613) README: typo fix on attributes (@spk) - [#614](https://github.com/rails-api/active_model_serializers/pull/614) Fix rails 4.0.x build. (@arthurnn) - [#610](https://github.com/rails-api/active_model_serializers/pull/610) ArraySerializer (@bolshakov) - [#607](https://github.com/rails-api/active_model_serializers/pull/607) ruby syntax highlights (@zigomir) - [#602](https://github.com/rails-api/active_model_serializers/pull/602) Add DSL for associations (@JordanFaust) ### 0.8.1 (May 6, 2013) * Fix bug whereby a serializer using 'options' would blow up. ### 0.8.0 (May 5, 2013) * Attributes can now have optional types. * A new DefaultSerializer ensures that POROs behave the same way as ActiveModels. * If you wish to override ActiveRecord::Base#to_Json, you can now require 'active_record/serializer_override'. We don't recommend you do this, but many users do, so we've left it optional. * Fixed a bug where ActionController wouldn't always have MimeResponds. * An optinal caching feature allows you to cache JSON & hashes that AMS uses. Adding 'cached true' to your Serializers will turn on this cache. * URL helpers used inside of Engines now work properly. * Serializers now can filter attributes with `only` and `except`: ``` UserSerializer.new(user, only: [:first_name, :last_name]) UserSerializer.new(user, except: :first_name) ``` * Basic Mongoid support. We now include our mixins in the right place. * On Ruby 1.8, we now generate an `id` method that properly serializes `id` columns. See issue #127 for more. * Add an alias for `scope` method to be the name of the context. By default this is `current_user`. The name is automatically set when using `serialization_scope` in the controller. * Pass through serialization options (such as `:include`) when a model has no serializer defined. ## [0.7.0 (March 6, 2013)](https://github.com/rails-api/active_model_serializers/commit/fabdc621ff97fbeca317f6301973dd4564b9e695) * ```embed_key``` option to allow embedding by attributes other than IDs * Fix rendering nil with custom serializer * Fix global ```self.root = false``` * Add support for specifying the serializer for an association as a String * Able to specify keys on the attributes method * Serializer Reloading via ActiveSupport::DescendantsTracker * Reduce double map to once; Fixes datamapper eager loading. ## 0.6.0 (October 22, 2012) * Serialize sets properly * Add root option to ArraySerializer * Support polymorphic associations * Support :each_serializer in ArraySerializer * Add `scope` method to easily access the scope in the serializer * Fix regression with Rails 3.2.6; add Rails 4 support * Allow serialization_scope to be disabled with serialization_scope nil * Array serializer should support pure ruby objects besides serializers ## 0.05.x ### [0.5.2 (June 5, 2012)](https://github.com/rails-api/active_model_serializers/commit/615afd125c260432d456dc8be845867cf87ea118#diff-0c5c12f311d3b54734fff06069efd2ac) ### [0.5.1 (May 23, 2012)](https://github.com/rails-api/active_model_serializers/commit/00194ec0e41831802fcbf893a34c0bb0853ebe14#diff-0c5c12f311d3b54734fff06069efd2ac) ### [0.5.0 (May 16, 2012)](https://github.com/rails-api/active_model_serializers/commit/33d4842dcd35c7167b0b33fc0abcf00fb2c92286) * First tagged version * Changes generators to always generate an ApplicationSerializer ## [0.1.0 (December 21, 2011)](https://github.com/rails-api/active_model_serializers/commit/1e0c9ef93b96c640381575dcd30be07ac946818b) ## First Commit as [Rails Serializers 0.0.1](https://github.com/rails-api/active_model_serializers/commit/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e) (December 1, 2011). ## Prehistory - [Changing Serialization/Serializers namespace to `Serializable` (November 30, 2011)](https://github.com/rails/rails/commit/8896b4fdc8a543157cdf4dfc378607ebf6c10ab0) - [Merge branch 'serializers'. This implements the ActiveModel::Serializer object. Includes code, tests, generators and guides. From JosĆ© and Yehuda with love.](https://github.com/rails/rails/commit/fcacc6986ab60f1fb2e423a73bf47c7abd7b191d) - But [was reverted](https://github.com/rails/rails/commit/5b2eb64ceb08cd005dc06b721935de5853971473). '[Revert the serializers API as other alternatives are now also under discussion](https://github.com/rails/rails/commit/0a4035b12a6c59253cb60f9e3456513c6a6a9d33)'. - [Proposed Implementation to Rails 3.2 by @wycats and @josevalim (November 25, 2011)](https://github.com/rails/rails/pull/3753) - [Creation of `ActionController::Serialization`, initial serializer support (September, 26 2011)](https://github.com/rails/rails/commit/8ff7693a8dc61f43fc4eaf72ed24d3b8699191fe). - [Docs and CHANGELOG](https://github.com/rails/rails/commit/696d01f7f4a8ed787924a41cce6df836cd73c46f) - [Deprecation of ActiveModel::Serialization to ActiveModel::Serializable](https://github.com/rails/rails/blob/696d01f7f4a8ed787924a41cce6df836cd73c46f/activemodel/lib/active_model/serialization.rb) - [Creation of `ActiveModel::Serialization` from `ActiveModel::Serializer` in Rails (2009)](https://github.com/rails/rails/commit/c6bc8e662614be711f45a8d4b231d5f993b024a7#diff-d029b9768d8df0407a35804a468e3ae5) - [Integration of `ActiveModel::Serializer` into `ActiveRecord::Serialization`](https://github.com/rails/rails/commit/783db25e0c640c1588732967a87d65c10fddc08e) - [Creation of `ActiveModel::Serializer` in Rails (2009)](https://github.com/rails/rails/commit/d2b78b3594b9cc9870e6a6ebfeb2e56d00e6ddb8#diff-80d5beeced9bdc24ca2b04a201543bdd) - [Creation of `ActiveModel::Serializers::JSON` in Rails (2009)](https://github.com/rails/rails/commit/fbdf706fffbfb17731a1f459203d242414ef5086) active_model_serializers-0.10.10/CODE_OF_CONDUCT.md000066400000000000000000000063011351232231100215100ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting one of the owners listed at https://rubygems.org/gems/active_model_serializers. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/active_model_serializers-0.10.10/CONTRIBUTING.md000066400000000000000000000074441351232231100211530ustar00rootroot00000000000000## Have an issue? Before opening an issue, try the following: ##### Consult the documentation See if your issue can be resolved by information in the documentation. - [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master/docs) - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/v0.10.0) - [Guides](docs) - [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable) - [0.8 (0-8-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-8-stable) ##### Check for an existing issue Take a look at the issues to see if a similar one has already been created. If one exists, please add any additional information that might expedite resolution. #### Open an issue If the documentation wasn't able to help resolve the issue and no issue already exists, please open a new issue with the following in mind: - Please make sure only to include one issue per report. If you encounter multiple, unrelated issues, please report them as such. - Be detailed. Provide backtraces and example code when possible. Provide information about your environment. e.g., Ruby version, rails version, etc. - Own your issue. Actively participate in the discussion and help drive the issue to closure. - If you resolve your own issue, please share the details on the issue and close it out. Others might have the same issue and sharing solutions is helpful. ## Contributing Contributing can be done in many ways and is not exclusive to code. If you have thoughts on a particular issue or feature, we encourage you to open new issues for discussion or add your comments to existing ones. #### Pull requests We also gladly welcome pull requests. When preparing to work on pull request, please adhere to these standards: - Base work on the master branch unless fixing an issue with [0.9-stable](https://github.com/rails-api/active_model_serializers/tree/0-9-stable) or [0.8-stable](https://github.com/rails-api/active_model_serializers/tree/0-8-stable) - Squash your commits and regularly rebase off master. - Provide a description of the changes contained in the pull request. - Note any specific areas that should be reviewed. - Include tests. - The test suite must pass on [supported Ruby versions](.travis.yml) - Include updates to the [documentation](https://github.com/rails-api/active_model_serializers/tree/master/docs) where applicable. - Update the [CHANGELOG](https://github.com/rails-api/active_model_serializers/blob/master/CHANGELOG.md) to the appropriate sections with a brief description of the changes. - Do not change the VERSION file. #### Running tests Run all tests `$ rake test` Run a single test suite `$ rake test TEST=path/to/test.rb` Run a single test `$ rake test TEST=path/to/test.rb TESTOPTS="--name=test_something"` Run tests against different Rails versions by setting the RAILS_VERSION variable and bundling gems. (save this script somewhere executable and run from top of AMS repository) ```bash #!/usr/bin/env bash rcommand='puts YAML.load_file("./.travis.yml")["env"]["matrix"].join(" ").gsub("RAILS_VERSION=", "")' versions=$(ruby -ryaml -e "$rcommand") for version in ${versions[@]}; do export RAILS_VERSION="$version" rm -f Gemfile.lock bundle check || bundle --local || bundle bundle exec rake test if [ "$?" -eq 0 ]; then # green in ANSI echo -e "\033[32m **** Tests passed against Rails ${RAILS_VERSION} **** \033[0m" else # red in ANSI echo -e "\033[31m **** Tests failed against Rails ${RAILS_VERSION} **** \033[0m" read -p '[Enter] any key to continue, [q] to quit...' prompt if [ "$prompt" = 'q' ]; then unset RAILS_VERSION exit 1 fi fi unset RAILS_VERSION done ``` active_model_serializers-0.10.10/Gemfile000066400000000000000000000044511351232231100202100ustar00rootroot00000000000000# frozen_string_literal: true source 'https://rubygems.org' # # Add a Gemfile.local to locally bundle gems outside of version control local_gemfile = File.join(File.expand_path('..', __FILE__), 'Gemfile.local') eval_gemfile local_gemfile if File.readable?(local_gemfile) # Specify your gem's dependencies in active_model_serializers.gemspec gemspec version = ENV['RAILS_VERSION'] || '4.2' if version == 'master' gem 'rack', github: 'rack/rack' gem 'arel', github: 'rails/arel' gem 'rails', github: 'rails/rails' git 'https://github.com/rails/rails.git' do gem 'railties' gem 'activesupport' gem 'activemodel' gem 'actionpack' gem 'activerecord', group: :test # Rails 5 gem 'actionview' end else gem_version = "~> #{version}.0" gem 'rails', gem_version gem 'railties', gem_version gem 'activesupport', gem_version gem 'activemodel', gem_version gem 'actionpack', gem_version gem 'activerecord', gem_version, group: :test end # https://github.com/bundler/bundler/blob/89a8778c19269561926cea172acdcda241d26d23/lib/bundler/dependency.rb#L30-L54 @windows_platforms = [:mswin, :mingw, :x64_mingw] # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: (@windows_platforms + [:jruby]) if ENV['CI'] if RUBY_VERSION < '2.4' # Windows: An error occurred while installing nokogiri (1.8.0) gem 'nokogiri', '< 1.7', platforms: @windows_platforms end end group :bench do # https://github.com/rails-api/active_model_serializers/commit/cb4459580a6f4f37f629bf3185a5224c8624ca76 gem 'benchmark-ips', '>= 2.7.2', require: false, group: :development end group :test do platforms(*(@windows_platforms + [:ruby])) do if version == 'master' || version >= '6' gem 'sqlite3', '~> 1.4' else gem 'sqlite3', '~> 1.3.13' end end platforms :jruby do if version == 'master' || version >= '5' gem 'activerecord-jdbcsqlite3-adapter', '~> 50' else gem 'activerecord-jdbcsqlite3-adapter', '~> 1.3.0' end end gem 'codeclimate-test-reporter', require: false gem 'm', '~> 1.5' gem 'pry', '>= 0.10' gem 'byebug', '~> 8.2' if RUBY_VERSION < '2.2' gem 'pry-byebug', platforms: :ruby end group :development, :test do gem 'rubocop', '~> 0.40.0', require: false gem 'yard', require: false end active_model_serializers-0.10.10/MIT-LICENSE000066400000000000000000000020561351232231100203500ustar00rootroot00000000000000Copyright (c) 2014 Steve Klabnik MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. active_model_serializers-0.10.10/README.md000066400000000000000000000313251351232231100201740ustar00rootroot00000000000000# ActiveModelSerializers
Build Status Build Status Build status
Code Quality Code Quality codebeat Test Coverage
Issue Stats Pulse
## About ActiveModelSerializers brings convention over configuration to your JSON generation. ActiveModelSerializers works through two components: **serializers** and **adapters**. Serializers describe _which_ attributes and relationships should be serialized. Adapters describe _how_ attributes and relationships should be serialized. SerializableResource co-ordinates the resource, Adapter and Serializer to produce the resource serialization. The serialization has the `#as_json`, `#to_json` and `#serializable_hash` methods used by the Rails JSON Renderer. (SerializableResource actually delegates these methods to the adapter.) By default ActiveModelSerializers will use the **Attributes Adapter** (no JSON root). But we strongly advise you to use **JsonApi Adapter**, which follows 1.0 of the format specified in [jsonapi.org/format](http://jsonapi.org/format). Check how to change the adapter in the sections below. `0.10.x` is **not** backward compatible with `0.9.x` nor `0.8.x`. `0.10.x` is based on the `0.8.0` code, but with a more flexible architecture. We'd love your help. [Learn how you can help here.](CONTRIBUTING.md) ## Installation Add this line to your application's Gemfile: ``` gem 'active_model_serializers', '~> 0.10.0' ``` And then execute: ``` $ bundle ``` ## Getting Started See [Getting Started](docs/general/getting_started.md) for the nuts and bolts. More information is available in the [Guides](docs) and [High-level behavior](README.md#high-level-behavior). ## Getting Help If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new) and see our [contributing guide](CONTRIBUTING.md). If you have a question, please [post to Stack Overflow](http://stackoverflow.com/questions/tagged/active-model-serializers). If you'd like to chat, we have a [community slack](http://amserializers.herokuapp.com). Thanks! ## Documentation If you're reading this at https://github.com/rails-api/active_model_serializers you are reading documentation for our `master`, which may include features that have not been released yet. Please see below for the documentation relevant to you. - [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master) - [0.10.6 (latest release) Documentation](https://github.com/rails-api/active_model_serializers/tree/v0.10.6) - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/gems/active_model_serializers/0.10.6) - [Guides](docs) - [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable) - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-9-stable) - [0.8 (0-8-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-8-stable) - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-8-stable) ## High-level behavior Choose an adapter from [adapters](lib/active_model_serializers/adapter): ``` ruby ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes` ``` Given a [serializable model](lib/active_model/serializer/lint.rb): ```ruby # either class SomeResource < ActiveRecord::Base # columns: title, body end # or class SomeResource < ActiveModelSerializers::Model attributes :title, :body end ``` And initialized as: ```ruby resource = SomeResource.new(title: 'ActiveModelSerializers', body: 'Convention over configuration') ``` Given a serializer for the serializable model: ```ruby class SomeSerializer < ActiveModel::Serializer attribute :title, key: :name attributes :body end ``` The model can be serialized as: ```ruby options = {} serialization = ActiveModelSerializers::SerializableResource.new(resource, options) serialization.to_json serialization.as_json ``` SerializableResource delegates to the adapter, which it builds as: ```ruby adapter_options = {} adapter = ActiveModelSerializers::Adapter.create(serializer, adapter_options) adapter.to_json adapter.as_json adapter.serializable_hash ``` The adapter formats the serializer's attributes and associations (a.k.a. includes): ```ruby serializer_options = {} serializer = SomeSerializer.new(resource, serializer_options) serializer.attributes serializer.associations ``` ## Architecture This section focuses on architecture the 0.10.x version of ActiveModelSerializers. If you are interested in the architecture of the 0.8 or 0.9 versions, please refer to the [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md) or [0.9 README](https://github.com/rails-api/active_model_serializers/blob/0-9-stable/README.md). The original design is also available [here](https://github.com/rails-api/active_model_serializers/blob/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e/README.textile). ### ActiveModel::Serializer An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/serialization.rb) and exposes an `attributes` method, among a few others. It allows you to specify which attributes and associations should be represented in the serializatation of the resource. It requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself. It may be useful to think of it as a [presenter](http://blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters). #### ActiveModel::CollectionSerializer The **`ActiveModel::CollectionSerializer`** represents a collection of resources as serializers and, if there is no serializer, primitives. ### ActiveModelSerializers::Adapter::Base The **`ActiveModelSerializers::Adapter::Base`** describes the structure of the JSON document generated from a serializer. For example, the `Attributes` example represents each serializer as its unmodified attributes. The `JsonApi` adapter represents the serializer as a [JSON API](http://jsonapi.org/) document. ### ActiveModelSerializers::SerializableResource The **`ActiveModelSerializers::SerializableResource`** acts to coordinate the serializer(s) and adapter to an object that responds to `to_json`, and `as_json`. It is used in the controller to encapsulate the serialization resource when rendered. However, it can also be used on its own to serialize a resource outside of a controller, as well. ### Primitive handling Definitions: A primitive is usually a String or Array. There is no serializer defined for them; they will be serialized when the resource is converted to JSON (`as_json` or `to_json`). (The below also applies for any object with no serializer.) - ActiveModelSerializers doesn't handle primitives passed to `render json:` at all. Internally, if no serializer can be found in the controller, the resource is not decorated by ActiveModelSerializers. - However, when a primitive value is an attribute or in a collection, it is not modified. When serializing a collection and the collection serializer (CollectionSerializer) cannot identify a serializer for a resource in its collection, it throws [`:no_serializer`](https://github.com/rails-api/active_model_serializers/issues/1191#issuecomment-142327128). For example, when caught by `Reflection#build_association`, and the association value is set directly: ```ruby reflection_options[:virtual_value] = association_value.try(:as_json) || association_value ``` (which is called by the adapter as `serializer.associations(*)`.) ### How options are parsed High-level overview: - For a **collection** - `:serializer` specifies the collection serializer and - `:each_serializer` specifies the serializer for each resource in the collection. - For a **single resource**, the `:serializer` option is the resource serializer. - Options are partitioned in serializer options and adapter options. Keys for adapter options are specified by [`ADAPTER_OPTION_KEYS`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/serializable_resource.rb#L5). The remaining options are serializer options. Details: 1. **ActionController::Serialization** 1. `serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)` 1. `options` are partitioned into `adapter_opts` and everything else (`serializer_opts`). The `adapter_opts` keys are defined in [`ActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS`](lib/active_model_serializers/serializable_resource.rb#L5). 1. **ActiveModelSerializers::SerializableResource** 1. `if serializable_resource.serializer?` (there is a serializer for the resource, and an adapter is used.) - Where `serializer?` is `use_adapter? && !!(serializer)` - Where `use_adapter?`: 'True when no explicit adapter given, or explicit value is truthy (non-nil); False when explicit adapter is falsy (nil or false)' - Where `serializer`: 1. from explicit `:serializer` option, else 2. implicitly from resource `ActiveModel::Serializer.serializer_for(resource)` 1. A side-effect of checking `serializer` is: - The `:serializer` option is removed from the serializer_opts hash - If the `:each_serializer` option is present, it is removed from the serializer_opts hash and set as the `:serializer` option 1. The serializer and adapter are created as 1. `serializer_instance = serializer.new(resource, serializer_opts)` 2. `adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)` 1. **ActiveModel::Serializer::CollectionSerializer#new** 1. If the `serializer_instance` was a `CollectionSerializer` and the `:serializer` serializer_opts is present, then [that serializer is passed into each resource](https://github.com/rails-api/active_model_serializers/blob/a54d237e2828fe6bab1ea5dfe6360d4ecc8214cd/lib/active_model/serializer/array_serializer.rb#L14-L16). 1. **ActiveModel::Serializer#attributes** is used by the adapter to get the attributes for resource as defined by the serializer. (In Rails, the `options` are also passed to the `as_json(options)` or `to_json(options)` methods on the resource serialization by the Rails JSON renderer. They are, therefore, important to know about, but not part of ActiveModelSerializers.) ### What does a 'serializable resource' look like? - An `ActiveRecord::Base` object. - Any Ruby object that passes the [Lint](https://www.rubydoc.info/gems/active_model_serializers/ActiveModel/Serializer/Lint/Tests) [(code)](lib/active_model/serializer/lint.rb). ActiveModelSerializers provides a [`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb), which is a simple serializable PORO (Plain-Old Ruby Object). `ActiveModelSerializers::Model` may be used either as a reference implementation, or in production code. ```ruby class MyModel < ActiveModelSerializers::Model attributes :id, :name, :level end ``` The default serializer for `MyModel` would be `MyModelSerializer` whether MyModel is an ActiveRecord::Base object or not. Outside of the controller the rules are **exactly** the same as for records. For example: ```ruby render json: MyModel.new(level: 'awesome'), adapter: :json ``` would be serialized the same as ```ruby ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json ``` ## Semantic Versioning This project adheres to [semver](http://semver.org/) ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) active_model_serializers-0.10.10/Rakefile000066400000000000000000000037561351232231100203710ustar00rootroot00000000000000# frozen_string_literal: true begin require 'bundler/setup' rescue LoadError puts 'You must `gem install bundler` and `bundle install` to run rake tasks' end begin require 'simplecov' rescue LoadError # rubocop:disable Lint/HandleExceptions end import('lib/tasks/rubocop.rake') Bundler::GemHelper.install_tasks require 'yard' namespace :yard do YARD::Rake::YardocTask.new(:doc) do |t| t.stats_options = ['--list-undoc'] end desc 'start a gem server' task :server do sh 'bundle exec yard server --gems' end desc 'use Graphviz to generate dot graph' task :graph do output_file = 'doc/erd.dot' sh "bundle exec yard graph --protected --full --dependencies > #{output_file}" puts 'open doc/erd.dot if you have graphviz installed' end end require 'rake/testtask' Rake::TestTask.new(:test) do |t| t.libs << 'lib' t.libs << 'test' t.pattern = 'test/**/*_test.rb' t.ruby_opts = ['-r./test/test_helper.rb'] t.ruby_opts << ' -w' unless ENV['NO_WARN'] == 'true' t.verbose = true end desc 'Run isolated tests' task isolated: ['test:isolated'] namespace :test do task :isolated do desc 'Run isolated tests for Railtie' require 'shellwords' dir = File.dirname(__FILE__) dir = Shellwords.shellescape(dir) isolated_test_files = FileList['test/**/*_test_isolated.rb'] # https://github.com/rails/rails/blob/3d590add45/railties/lib/rails/generators/app_base.rb#L345-L363 _bundle_command = Gem.bin_path('bundler', 'bundle') require 'bundler' Bundler.with_clean_env do isolated_test_files.all? do |test_file| command = "-w -I#{dir}/lib -I#{dir}/test #{Shellwords.shellescape(test_file)}" full_command = %("#{Gem.ruby}" #{command}) system(full_command) end or fail 'Failures' # rubocop:disable Style/AndOr end end end if ENV['RAILS_VERSION'].to_s > '4.0' && RUBY_ENGINE == 'ruby' task default: [:isolated, :test, :rubocop] else task default: [:test, :rubocop] end desc 'CI test task' task ci: [:default] active_model_serializers-0.10.10/active_model_serializers.gemspec000066400000000000000000000043761351232231100253370ustar00rootroot00000000000000# frozen_string_literal: true # coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'active_model/serializer/version' Gem::Specification.new do |spec| spec.name = 'active_model_serializers' spec.version = ActiveModel::Serializer::VERSION spec.platform = Gem::Platform::RUBY spec.authors = ['Steve Klabnik'] spec.email = ['steve@steveklabnik.com'] spec.summary = 'Conventions-based JSON generation for Rails.' spec.description = 'ActiveModel::Serializers allows you to generate your JSON in an object-oriented and convention-driven manner.' spec.homepage = 'https://github.com/rails-api/active_model_serializers' spec.license = 'MIT' spec.files = Dir["CHANGELOG.md", "MIT-LICENSE", "README.md", "lib/**/*"] spec.require_paths = ['lib'] spec.executables = [] spec.required_ruby_version = '>= 2.1' rails_versions = ['>= 4.1', '< 6.1'] spec.add_runtime_dependency 'activemodel', rails_versions # 'activesupport', rails_versions # 'builder' spec.add_runtime_dependency 'actionpack', rails_versions # 'activesupport', rails_versions # 'rack' # 'rack-test', '~> 0.6.2' spec.add_development_dependency 'railties', rails_versions # 'activesupport', rails_versions # 'actionpack', rails_versions # 'rake', '>= 0.8.7' # 'activesupport', rails_versions # 'i18n, # 'tzinfo' spec.add_development_dependency 'minitest', ['~> 5.0', '< 5.11'] # 'thread_safe' spec.add_runtime_dependency 'jsonapi-renderer', ['>= 0.1.1.beta1', '< 0.3'] spec.add_runtime_dependency 'case_transform', '>= 0.2' spec.add_development_dependency 'activerecord', rails_versions # arel # activesupport # activemodel # Soft dependency for pagination spec.add_development_dependency 'kaminari', ' ~> 0.16.3' spec.add_development_dependency 'will_paginate', '~> 3.0', '>= 3.0.7' spec.add_development_dependency 'bundler', '~> 1.6' spec.add_development_dependency 'simplecov', '~> 0.11' spec.add_development_dependency 'timecop', '~> 0.7' spec.add_development_dependency 'grape', ['>= 0.13', '< 0.19.1'] spec.add_development_dependency 'json_schema' spec.add_development_dependency 'rake', ['>= 10.0', '< 12.0'] end active_model_serializers-0.10.10/appveyor.yml000066400000000000000000000011211351232231100212740ustar00rootroot00000000000000version: 1.0.{build}-{branch} skip_tags: true environment: JRUBY_OPTS: "--dev -J-Xmx1024M --debug" RAILS_VERSION: 5.2 matrix: - ruby_version: "Ruby23" - ruby_version: "Ruby23-x64" cache: - vendor/bundle install: - SET PATH=C:\%ruby_version%\bin;%PATH% - gem uninstall bundler -a -x -I - gem update --system 2.7.9 - # gem install bundler -v '1.17.3' - bundle env - bundle check || bundle install --path=vendor/bundle --retry=3 --jobs=3 - bundle clean --force before_test: - ruby -v - gem -v - bundle -v test_script: - bundle exec rake ci build: off active_model_serializers-0.10.10/bin/000077500000000000000000000000001351232231100174615ustar00rootroot00000000000000active_model_serializers-0.10.10/bin/bench000077500000000000000000000117741351232231100205000ustar00rootroot00000000000000#!/usr/bin/env ruby # ActiveModelSerializers Benchmark driver # Adapted from # https://github.com/ruby-bench/ruby-bench-suite/blob/8ad567f7e43a044ae48c36833218423bb1e2bd9d/rails/benchmarks/driver.rb require 'bundler' Bundler.setup require 'json' require 'pathname' require 'optparse' require 'digest' require 'pathname' require 'shellwords' require 'logger' require 'English' class BenchmarkDriver ROOT = Pathname File.expand_path(File.join('..', '..'), __FILE__) BASE = ENV.fetch('BASE') { ROOT.join('test', 'benchmark') } ESCAPED_BASE = Shellwords.shellescape(BASE) def self.benchmark(options) new(options).run end def self.parse_argv_and_run(argv = ARGV, options = {}) options = { repeat_count: 1, pattern: [], env: 'CACHE_ON=on' }.merge!(options) OptionParser.new do |opts| opts.banner = 'Usage: bin/bench [options]' opts.on('-r', '--repeat-count [NUM]', 'Run benchmarks [NUM] times taking the best result') do |value| options[:repeat_count] = value.to_i end opts.on('-p', '--pattern ', 'Benchmark name pattern') do |value| options[:pattern] = value.split(',') end opts.on('-e', '--env ', 'ENV variables to pass in') do |value| options[:env] = value.split(',') end end.parse!(argv) benchmark(options) end attr_reader :commit_hash, :base # Based on logfmt: # https://www.brandur.org/logfmt # For more complete implementation see: # see https://github.com/arachnid-cb/logfmtr/blob/master/lib/logfmtr/base.rb # For usage see: # https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write/ # https://engineering.heroku.com/blogs/2014-09-05-hutils-explore-your-structured-data-logs/ # For Ruby parser see: # https://github.com/cyberdelia/logfmt-ruby def self.summary_logger(device = 'output.txt') require 'time' logger = Logger.new(device) logger.level = Logger::INFO logger.formatter = proc { |severity, datetime, progname, msg| msg = "'#{msg}'" "level=#{severity} time=#{datetime.utc.iso8601(6)} pid=#{Process.pid} progname=#{progname} msg=#{msg}#{$INPUT_RECORD_SEPARATOR}" } logger end def self.stdout_logger logger = Logger.new(STDOUT) logger.level = Logger::INFO logger.formatter = proc { |_, _, _, msg| "#{msg}#{$INPUT_RECORD_SEPARATOR}" } logger end def initialize(options) @writer = ENV['SUMMARIZE'] ? self.class.summary_logger : self.class.stdout_logger @repeat_count = options[:repeat_count] @pattern = options[:pattern] @commit_hash = options.fetch(:commit_hash) { `git rev-parse --short HEAD`.chomp } @base = options.fetch(:base) { ESCAPED_BASE } @env = Array(options[:env]).join(' ') @rubyopt = options[:rubyopt] # TODO: rename end def run files.each do |path| next if !@pattern.empty? && /#{@pattern.join('|')}/ !~ File.basename(path) run_single(Shellwords.shellescape(path)) end end private def files Dir[File.join(base, 'bm_*')] end def run_single(path) script = "RAILS_ENV=production #{@env} ruby #{@rubyopt} #{path}" environment = `ruby -v`.chomp.strip[/\d+\.\d+\.\d+\w+/] runs_output = measure(script) if runs_output.empty? results = { error: :no_results } return end results = {} results['commit_hash'] = commit_hash results['version'] = runs_output.first['version'] results['rails_version'] = runs_output.first['rails_version'] results['benchmark_run[environment]'] = environment results['runs'] = [] runs_output.each do |output| results['runs'] << { 'benchmark_type[category]' => output['label'], 'benchmark_run[result][iterations_per_second]' => output['iterations_per_second'].round(3), 'benchmark_run[result][total_allocated_objects_per_iteration]' => output['total_allocated_objects_per_iteration'] } end ensure results && report(results) end def report(results) @writer.info { 'Benchmark results:' } @writer.info { JSON.pretty_generate(results) } end def summarize(result) puts "#{result['label']} #{result['iterations_per_second']}/ips; #{result['total_allocated_objects_per_iteration']} objects" end # FIXME: ` provides the full output but it'll return failed output as well. def measure(script) results = Hash.new { |h, k| h[k] = [] } @repeat_count.times do output = sh(script) output.each_line do |line| next if line.nil? begin result = JSON.parse(line) rescue JSON::ParserError result = { error: line } # rubocop:disable Lint/UselessAssignment else summarize(result) results[result['label']] << result end end end results.map do |_, bm_runs| bm_runs.sort_by do |run| run['iterations_per_second'] end.last end end def sh(cmd) `#{cmd}` end end BenchmarkDriver.parse_argv_and_run if $PROGRAM_NAME == __FILE__ active_model_serializers-0.10.10/bin/bench_regression000077500000000000000000000206671351232231100227410ustar00rootroot00000000000000#!/usr/bin/env ruby require 'fileutils' require 'pathname' require 'shellwords' require 'English' ############################ # USAGE # # bundle exec bin/bench_regression # defaults to the current branch # defaults to the master branch # bundle exec bin/bench_regression current # will run on the current branch # bundle exec bin/bench_regression revisions 792fb8a90 master # every revision inclusive # bundle exec bin/bench_regression 792fb8a90 master --repeat-count 2 --env CACHE_ON=off # bundle exec bin/bench_regression vendor ########################### class BenchRegression ROOT = Pathname File.expand_path(File.join(*['..', '..']), __FILE__) TMP_DIR_NAME = File.join('tmp', 'bench') TMP_DIR = File.join(ROOT, TMP_DIR_NAME) E_TMP_DIR = Shellwords.shellescape(TMP_DIR) load ROOT.join('bin', 'bench') attr_reader :source_stasher def initialize @source_stasher = SourceStasher.new end class SourceStasher attr_reader :gem_require_paths, :gem_paths attr_writer :vendor def initialize @gem_require_paths = [] @gem_paths = [] refresh_temp_dir @vendor = false end def temp_dir_empty? File.directory?(TMP_DIR) && Dir[File.join(TMP_DIR, '*')].none? end def empty_temp_dir return if @vendor return if temp_dir_empty? FileUtils.mkdir_p(TMP_DIR) Dir[File.join(TMP_DIR, '*')].each do |file| if File.directory?(file) FileUtils.rm_rf(file) else FileUtils.rm(file) end end end def fill_temp_dir vendor_files(Dir[File.join(ROOT, 'test', 'benchmark', '*.{rb,ru}')]) # vendor_file(File.join('bin', 'bench')) housekeeping { empty_temp_dir } vendor_gem('benchmark-ips') end def vendor_files(files) files.each do |file| vendor_file(file) end end def vendor_file(file) FileUtils.cp(file, File.join(TMP_DIR, File.basename(file))) end def vendor_gem(gem_name) directory_name = `bundle exec gem unpack benchmark-ips --target=#{E_TMP_DIR}`[/benchmark-ips.+\d/] gem_paths << File.join(TMP_DIR, directory_name) gem_require_paths << File.join(TMP_DIR_NAME, directory_name, 'lib') housekeeping { remove_vendored_gems } end def remove_vendored_gems return if @vendor FileUtils.rm_rf(*gem_paths) end def refresh_temp_dir empty_temp_dir fill_temp_dir end def housekeeping at_exit { yield } end end module RevisionMethods module_function def current_branch @current_branch ||= `cat .git/HEAD | cut -d/ -f3,4,5`.chomp end def current_revision `git rev-parse --short HEAD`.chomp end def revision_description(rev) `git log --oneline -1 #{rev}`.chomp end def revisions(start_ref, end_ref) cmd = "git rev-list --reverse #{start_ref}..#{end_ref}" `#{cmd}`.chomp.split("\n") end def checkout_ref(ref) `git checkout #{ref}`.chomp if $CHILD_STATUS STDERR.puts "Checkout failed: #{ref}, #{$CHILD_STATUS.exitstatus}" unless $CHILD_STATUS.success? $CHILD_STATUS.success? else true end end def clean_head system('git reset --hard --quiet') end end module ShellMethods def sh(cmd) puts cmd # system(cmd) run(cmd) # env = {} # # out = STDOUT # pid = spawn(env, cmd) # Process.wait(pid) # pid = fork do # exec cmd # end # Process.waitpid2(pid) # puts $CHILD_STATUS.exitstatus end require 'pty' # should consider trapping SIGINT in here def run(cmd) puts cmd child_process = '' result = '' # http://stackoverflow.com/a/1162850 # stream output of subprocess begin PTY.spawn(cmd) do |stdin, _stdout, pid| begin # Do stuff with the output here. Just printing to show it works stdin.each do |line| print line result << line end child_process = PTY.check(pid) rescue Errno::EIO puts 'Errno:EIO error, but this probably just means ' \ 'that the process has finished giving output' end end rescue PTY::ChildExited puts 'The child process exited!' end unless (child_process && child_process.success?) exitstatus = child_process.exitstatus puts "FAILED: #{child_process.pid} exited with status #{exitstatus.inspect} due to failed command #{cmd}" exit exitstatus || 1 end result end def bundle(ref) system("rm -f Gemfile.lock") # This is absolutely critical for bundling to work Bundler.with_clean_env do system("bundle check || bundle install --local || bundle install || bundle update") end # if $CHILD_STATUS # STDERR.puts "Bundle failed at: #{ref}, #{$CHILD_STATUS.exitstatus}" unless $CHILD_STATUS.success? # $CHILD_STATUS.success? # else # false # end end end include ShellMethods include RevisionMethods def benchmark_refs(ref1: nil, ref2: nil, cmd:) checking_out = false ref0 = current_branch ref1 ||= current_branch ref2 ||= 'master' p [ref0, ref1, ref2, current_revision] run_benchmark_at_ref(cmd, ref1) p [ref0, ref1, ref2, current_revision] run_benchmark_at_ref(cmd, ref2) p [ref0, ref1, ref2, current_revision] checking_out = true checkout_ref(ref0) rescue Exception # rubocop:disable Lint/RescueException STDERR.puts "[ERROR] #{$!.message}" checkout_ref(ref0) unless checking_out raise end def benchmark_revisions(ref1: nil, ref2: nil, cmd:) checking_out = false ref0 = current_branch ref1 ||= current_branch ref2 ||= 'master' revisions(ref1, ref2).each do |rev| STDERR.puts "Checking out: #{revision_description(rev)}" run_benchmark_at_ref(cmd, rev) clean_head end checking_out = true checkout_ref(ref0) rescue Exception # rubocop:disable Lint/RescueException STDERR.puts "[ERROR]: #{$!.message}" checkout_ref(ref0) unless checking_out raise end def run_benchmark_at_ref(cmd, ref) checkout_ref(ref) run_benchmark(cmd, ref) end def run_benchmark(cmd, ref = nil) ref ||= current_revision bundle(ref) && benchmark_tests(cmd, ref) end def benchmark_tests(cmd, ref) base = E_TMP_DIR # cmd.sub('bin/bench', 'tmp/revision_runner/bench') # bundle = Gem.bin('bunle' # Bundler.with_clean_env(&block) # cmd = Shellwords.shelljoin(cmd) # cmd = "COMMIT_HASH=#{ref} BASE=#{base} bundle exec ruby -rbenchmark/ips #{cmd}" # Add vendoring benchmark/ips to load path # CURRENT THINKING: IMPORTANT # Pass into require statement as RUBYOPTS i.e. via env rather than command line argument # otherwise, have a 'fast ams benchmarking' module that extends benchmarkings to add the 'ams' # method but doesn't depend on benchmark-ips options = { commit_hash: ref, base: base, rubyopt: Shellwords.shellescape("-Ilib:#{source_stasher.gem_require_paths.join(':')}") } BenchmarkDriver.parse_argv_and_run(ARGV.dup, options) end end if $PROGRAM_NAME == __FILE__ benchmarking = BenchRegression.new case ARGV[0] when 'current' # Run current branch only # super simple command line parsing args = ARGV.dup _ = args.shift # remove 'current' from args cmd = args benchmarking.run_benchmark(cmd) when 'revisions' # Runs on every revision # super simple command line parsing args = ARGV.dup _ = args.shift ref1 = args.shift # remove 'revisions' from args ref2 = args.shift cmd = args benchmarking.benchmark_revisions(ref1: ref1, ref2: ref2, cmd: cmd) when 'vendor' # Just prevents vendored files from being cleaned up # at exit. (They are vendored at initialize.) benchmarking.source_stasher.vendor = true else # Default: Compare current_branch to master # Optionally: pass in two refs as args to `bin/bench_regression` # TODO: Consider checking across more revisions, to automatically find problems. # super simple command line parsing args = ARGV.dup ref1 = args.shift ref2 = args.shift cmd = args benchmarking.benchmark_refs(ref1: ref1, ref2: ref2, cmd: cmd) end end active_model_serializers-0.10.10/bin/rubocop000077500000000000000000000014341351232231100210620ustar00rootroot00000000000000#!/usr/bin/env bash # # Usage: # bin/rubocop [-A|-t|-h] # bin/rubocop [file or path] [cli options] # # Options: # Autocorrect -A # AutoGenConfig -t # Usage -h,--help,help set -e case $1 in -A) echo "Rubocop autocorrect is ON" >&2 bundle exec rake -f lib/tasks/rubocop.rake rubocop:auto_correct ;; -t) echo "Rubocop is generating a new TODO" >&2 bundle exec rake -f lib/tasks/rubocop.rake rubocop:auto_gen_config ;; -h|--help|help) sed -ne '/^#/!q;s/.\{1,2\}//;1d;p' < "$0" ;; *) # with no args, run vanilla rubocop # else assume we're passing in arbitrary arguments if [ -z "$1" ]; then bundle exec rake -f lib/tasks/rubocop.rake rubocop else bundle exec rubocop "$@" fi ;; esac active_model_serializers-0.10.10/bin/serve_benchmark000077500000000000000000000013301351232231100225420ustar00rootroot00000000000000#!/usr/bin/env bash set -e case "$1" in start) config="${CONFIG_RU:-test/benchmark/config.ru}" bundle exec ruby -Ilib -S rackup "$config" --daemonize --pid tmp/benchmark_app.pid --warn --server webrick until [ -f 'tmp/benchmark_app.pid' ]; do sleep 0.1 # give it time to start.. I don't know a better way done cat tmp/benchmark_app.pid true ;; stop) if [ -f 'tmp/benchmark_app.pid' ]; then kill -TERM $(cat tmp/benchmark_app.pid) else echo 'No pidfile' false fi ;; status) if [ -f 'tmp/benchmark_app.pid' ]; then kill -0 $(cat tmp/benchmark_app.pid) [ "$?" -eq 0 ] else echo 'No pidfile' false fi ;; *) echo "Usage: $0 [start|stop|status]" ;; esac active_model_serializers-0.10.10/docs/000077500000000000000000000000001351232231100176415ustar00rootroot00000000000000active_model_serializers-0.10.10/docs/README.md000066400000000000000000000032601351232231100211210ustar00rootroot00000000000000# Docs - ActiveModel::Serializer 0.10.x This is the documentation of ActiveModelSerializers, it's focused on the **0.10.x version.** ----- ## General - [Getting Started](general/getting_started.md) - [Configuration Options](general/configuration_options.md) - [Serializers](general/serializers.md) - [Adapters](general/adapters.md) - [Rendering](general/rendering.md) - [Caching](general/caching.md) - [Logging](general/logging.md) - [Deserialization](general/deserialization.md) - [Instrumentation](general/instrumentation.md) - JSON API - [Schema](jsonapi/schema.md) - [Errors](jsonapi/errors.md) ## How to - [How to add root key](howto/add_root_key.md) - [How to add pagination links](howto/add_pagination_links.md) - [How to add relationship links](howto/add_relationship_links.md) - [Using ActiveModelSerializers Outside Of Controllers](howto/outside_controller_use.md) - [Testing ActiveModelSerializers](howto/test.md) - [Passing Arbitrary Options](howto/passing_arbitrary_options.md) - [How to serialize a Plain-Old Ruby Object (PORO)](howto/serialize_poro.md) - [How to upgrade from `0.8` to `0.10` safely](howto/upgrade_from_0_8_to_0_10.md) ## Integrations | Integration | Supported ActiveModelSerializers versions | Gem name and/or link |----|-----|---- | Ember.js | 0.9.x | [active-model-adapter](https://github.com/ember-data/active-model-adapter) | Ember.js | 0.10.x + | [docs/integrations/ember-and-json-api.md](integrations/ember-and-json-api.md) | Grape | 0.10.x + | [docs/integrations/grape.md](integrations/grape.md) | | Grape | 0.9.x | https://github.com/jrhe/grape-active_model_serializers/ | | Sinatra | 0.9.x | https://github.com/SauloSilva/sinatra-active-model-serializers/ active_model_serializers-0.10.10/docs/STYLE.md000066400000000000000000000062231351232231100210660ustar00rootroot00000000000000# STYLE ## Code and comments - We are actively working to identify tasks under the label [**Good for New Contributors**](https://github.com/rails-api/active_model_serializers/labels/Good%20for%20New%20Contributors). - [Changelog Missing](https://github.com/rails-api/active_model_serializers/issues?q=label%3A%22Changelog+Missing%22+is%3Aclosed) is an easy way to help out. - [Fix a bug](https://github.com/rails-api/active_model_serializers/labels/Ready%20for%20PR). - Ready for PR - A well defined bug, needs someone to PR a fix. - Bug - Anything that is broken. - Regression - A bug that did not exist in previous versions and isn't a new feature (applied in tandem with Bug). - Performance - A performance related issue. We could track this as a bug, but usually these would have slightly lower priority than standard bugs. - [Develop new features](https://github.com/rails-api/active_model_serializers/labels/Feature). - [Improve code quality](https://codeclimate.com/github/rails-api/active_model_serializers/code?sort=smell_count&sort_direction=desc). - [Improve amount of code exercised by tests](https://codeclimate.com/github/rails-api/active_model_serializers/coverage?sort=covered_percent&sort_direction=asc). - [Fix RuboCop (Style) TODOS](https://github.com/rails-api/active_model_serializers/blob/master/.rubocop_todo.yml). - Delete and offsense, run `rake rubocop` (or possibly `rake rubocop:auto_correct`), and [submit a PR](CONTRIBUTING.md#submitting-a-pull-request-pr). - We are also encouraging comments to substantial changes (larger than bugfixes and simple features) under an "RFC" (Request for Comments) process before we start active development. Look for the [**RFC**](https://github.com/rails-api/active_model_serializers/labels/RFC) label. ## Pull requests - If the tests pass and the pull request looks good, a maintainer will merge it. - If the pull request needs to be changed, - you can change it by updating the branch you generated the pull request from - either by adding more commits, or - by force pushing to it - A maintainer can make any changes themselves and manually merge the code in. ## Commit messages - [A Note About Git Commit Messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) - [http://stopwritingramblingcommitmessages.com/](http://stopwritingramblingcommitmessages.com/) - [ThoughtBot style guide](https://github.com/thoughtbot/guides/tree/master/style#git) #### About Pull Requests (PR's) - [Using Pull Requests](https://help.github.com/articles/using-pull-requests) - [Github pull requests made easy](http://www.element84.com/github-pull-requests-made-easy.html) - [Exercism Git Workflow](http://help.exercism.io/git-workflow.html). - [Level up your Git](http://rakeroutes.com/blog/deliberate-git/) - [All Your Open Source Code Are Belong To Us](http://www.benjaminfleischer.com/2013/07/30/all-your-open-source-code-are-belong-to-us/) ## Issue Labeling ActiveModelSerializers uses a subset of [StandardIssueLabels](https://github.com/wagenet/StandardIssueLabels) for Github Issues. You can [see our labels here](https://github.com/rails-api/active_model_serializers/labels). active_model_serializers-0.10.10/docs/general/000077500000000000000000000000001351232231100212565ustar00rootroot00000000000000active_model_serializers-0.10.10/docs/general/adapters.md000066400000000000000000000175651351232231100234210ustar00rootroot00000000000000[Back to Guides](../README.md) # Adapters ActiveModelSerializers offers the ability to configure which adapter to use both globally and/or when serializing (usually when rendering). The global adapter configuration is set on [`ActiveModelSerializers.config`](configuration_options.md). It should be set only once, preferably at initialization. For example: ```ruby ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonApi ``` or ```ruby ActiveModelSerializers.config.adapter = :json_api ``` or ```ruby ActiveModelSerializers.config.adapter = :json ``` The local adapter option is in the format `adapter: adapter`, where `adapter` is any of the same values as set globally. The configured adapter can be set as a symbol, class, or class name, as described in [Advanced adapter configuration](adapters.md#advanced-adapter-configuration). The `Attributes` adapter does not include a root key. It is just the serialized attributes. Use either the `JSON` or `JSON API` adapters if you want the response document to have a root key. ***IMPORTANT***: Adapter configuration has *no effect* on a serializer instance being used directly. That is, `UserSerializer.new(user).as_json` will *always* behave as if the adapter were the 'Attributes' adapter. See [Outside Controller Usage](../howto/outside_controller_use.md) for more details on recommended usage. ## Built in Adapters ### Attributes - Default It's the default adapter, it generates a json response without a root key. Doesn't follow any specific convention. ##### Example output ```json { "title": "Title 1", "body": "Body 1", "publish_at": "2020-03-16T03:55:25.291Z", "author": { "first_name": "Bob", "last_name": "Jones" }, "comments": [ { "body": "cool" }, { "body": "awesome" } ] } ``` ### JSON The json response is always rendered with a root key. The root key can be overridden by: * passing the `root` option in the render call. See details in the [Rendering Guides](rendering.md#overriding-the-root-key). * setting the `type` of the serializer. See details in the [Serializers Guide](serializers.md#type). Doesn't follow any specific convention. ##### Example output ```json { "post": { "title": "Title 1", "body": "Body 1", "publish_at": "2020-03-16T03:55:25.291Z", "author": { "first_name": "Bob", "last_name": "Jones" }, "comments": [{ "body": "cool" }, { "body": "awesome" }] } } ``` ### JSON API This adapter follows **version 1.0** of the [format specified](../jsonapi/schema.md) in [jsonapi.org/format](http://jsonapi.org/format). ##### Example output ```json { "data": { "id": "1337", "type": "posts", "attributes": { "title": "Title 1", "body": "Body 1", "publish-at": "2020-03-16T03:55:25.291Z" }, "relationships": { "author": { "data": { "id": "1", "type": "authors" } }, "comments": { "data": [{ "id": "7", "type": "comments" }, { "id": "12", "type": "comments" }] } }, "links": { "post-authors": "https://example.com/post_authors" }, "meta": { "rating": 5, "favorite-count": 10 } } } ``` ### Include option Which [serializer associations](https://github.com/rails-api/active_model_serializers/blob/master/docs/general/serializers.md#associations) are rendered can be specified using the `include` option. The option usage is consistent with [the include option in the JSON API spec](http://jsonapi.org/format/#fetching-includes), and is available in all adapters. Example of the usage: ```ruby render json: @posts, include: ['author', 'comments', 'comments.author'] # or render json: @posts, include: 'author,comments,comments.author' ``` The format of the `include` option can be either: - a String composed of a comma-separated list of [relationship paths](http://jsonapi.org/format/#fetching-includes). - an Array of Symbols and Hashes. - a mix of both. An empty string or an empty array will prevent rendering of any associations. In addition, two types of wildcards may be used: - `*` includes one level of associations. - `**` includes all recursively. These can be combined with other paths. ```ruby render json: @posts, include: '**' # or '*' for a single layer ``` The following would render posts and include: - the author - the author's comments, and - every resource referenced by the author's comments (recursively). It could be combined, like above, with other paths in any combination desired. ```ruby render json: @posts, include: 'author.comments.**' ``` **Note:** Wildcards are ActiveModelSerializers-specific, they are not part of the JSON API spec. The default include for the JSON API adapter is no associations. The default for the JSON and Attributes adapters is all associations. For the JSON API adapter associated resources will be gathered in the `"included"` member. For the JSON and Attributes adapters associated resources will be rendered among the other attributes. Only for the JSON API adapter you can specify, which attributes of associated resources will be rendered. This feature is called [sparse fieldset](http://jsonapi.org/format/#fetching-sparse-fieldsets): ```ruby render json: @posts, include: 'comments', fields: { comments: ['content', 'created_at'] } ``` ##### Security Considerations Since the included options may come from the query params (i.e. user-controller): ```ruby render json: @posts, include: params[:include] ``` The user could pass in `include=**`. We recommend filtering any user-supplied includes appropriately. ## Advanced adapter configuration ### Registering an adapter The default adapter can be configured, as above, to use any class given to it. An adapter may also be specified, e.g. when rendering, as a class or as a symbol. If a symbol, then the adapter must be, e.g. `:great_example`, `ActiveModelSerializers::Adapter::GreatExample`, or registered. There are two ways to register an adapter: 1) The simplest, is to subclass `ActiveModelSerializers::Adapter::Base`, e.g. the below will register the `Example::UsefulAdapter` as `"example/useful_adapter"`. ```ruby module Example class UsefulAdapter < ActiveModelSerializers::Adapter::Base end end ``` You'll notice that the name it registers is the underscored namespace and class. Under the covers, when the `ActiveModelSerializers::Adapter::Base` is subclassed, it registers the subclass as `register("example/useful_adapter", Example::UsefulAdapter)` 2) Any class can be registered as an adapter by calling `register` directly on the `ActiveModelSerializers::Adapter` class. e.g., the below registers `MyAdapter` as `:special_adapter`. ```ruby class MyAdapter; end ActiveModelSerializers::Adapter.register(:special_adapter, MyAdapter) ``` ### Looking up an adapter | Method | Return value | | :------------ |:---------------| | `ActiveModelSerializers::Adapter.adapter_map` | A Hash of all known adapters `{ adapter_name => adapter_class }` | | `ActiveModelSerializers::Adapter.adapters` | A (sorted) Array of all known `adapter_names` | | `ActiveModelSerializers::Adapter.lookup(name_or_klass)` | The `adapter_class`, else raises an `ActiveModelSerializers::Adapter::UnknownAdapter` error | | `ActiveModelSerializers::Adapter.adapter_class(adapter)` | Delegates to `ActiveModelSerializers::Adapter.lookup(adapter)` | | `ActiveModelSerializers::Adapter.configured_adapter` | A convenience method for `ActiveModelSerializers::Adapter.lookup(config.adapter)` | The registered adapter name is always a String, but may be looked up as a Symbol or String. Helpfully, the Symbol or String is underscored, so that `get(:my_adapter)` and `get("MyAdapter")` may both be used. For more information, see [the Adapter class on GitHub](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/adapter.rb) active_model_serializers-0.10.10/docs/general/caching.md000066400000000000000000000040561351232231100232010ustar00rootroot00000000000000[Back to Guides](../README.md) # Caching ## Warning There is currently a problem with caching in AMS [Caching doesn't improve performance](https://github.com/rails-api/active_model_serializers/issues/1586). Adding caching _may_ slow down your application, rather than speeding it up. We suggest you benchmark any caching you implement before using in a production enviroment ___ To cache a serializer, call ```cache``` and pass its options. The options are the same options of ```ActiveSupport::Cache::Store```, plus a ```key``` option that will be the prefix of the object cache on a pattern ```"#{key}/#{object.id}-#{object.updated_at}"```. The cache support is optimized to use the cached object in multiple request. An object cached on a ```show``` request will be reused at the ```index```. If there is a relationship with another cached serializer it will also be created and reused automatically. **[NOTE] Every object is individually cached.** **[NOTE] The cache is automatically expired after an object is updated, but it's not deleted.** ```ruby cache(options = nil) # options: ```{key, expires_in, compress, force, race_condition_ttl}``` ``` Take the example below: ```ruby class PostSerializer < ActiveModel::Serializer cache key: 'post', expires_in: 3.hours attributes :title, :body has_many :comments end ``` On this example every ```Post``` object will be cached with the key ```"post/#{post.id}-#{post.updated_at}"```. You can use this key to expire it as you want, but in this case it will be automatically expired after 3 hours. ## Fragment Caching If there is some API endpoint that shouldn't be fully cached, you can still optimise it, using Fragment Cache on the attributes and relationships that you want to cache. You can define the attribute by using ```only``` or ```except``` option on cache method. **[NOTE] Cache serializers will be used at their relationships** Example: ```ruby class PostSerializer < ActiveModel::Serializer cache key: 'post', expires_in: 3.hours, only: [:title] attributes :title, :body has_many :comments end ``` active_model_serializers-0.10.10/docs/general/configuration_options.md000066400000000000000000000121231351232231100262210ustar00rootroot00000000000000[Back to Guides](../README.md) # Configuration Options The following configuration options can be set on `ActiveModelSerializers.config`, preferably inside an initializer. ## General ##### adapter The [adapter](adapters.md) to use. Possible values: - `:attributes` (default) - `:json` - `:json_api` ##### serializer_lookup_enabled Enable automatic serializer lookup. Possible values: - `true` (default) - `false` When `false`, serializers must be explicitly specified. ##### key_transform The [key transform](key_transforms.md) to use. | Option | Result | |----|----| | `:camel` | ExampleKey | | `:camel_lower` | exampleKey | | `:dash` | example-key | | `:unaltered` | the original, unaltered key | | `:underscore` | example_key | | `nil` | use the adapter default | Each adapter has a default key transform configured: | Adapter | Default Key Transform | |----|----| | `Attributes` | `:unaltered` | | `Json` | `:unaltered` | | `JsonApi` | `:dash` | `config.key_transform` is a global override of the adapter default. Adapters still prefer the render option `:key_transform` over this setting. *NOTE: Key transforms can be expensive operations. If key transforms are unnecessary for the application, setting `config.key_transform` to `:unaltered` will provide a performance boost.* ##### default_includes What relationships to serialize by default. Default: `'*'`, which includes one level of related objects. See [includes](adapters.md#included) for more info. ##### serializer_lookup_chain Configures how serializers are searched for. By default, the lookup chain is ```ruby ActiveModelSerializers::LookupChain::DEFAULT ``` which is shorthand for ```ruby [ ActiveModelSerializers::LookupChain::BY_PARENT_SERIALIZER, ActiveModelSerializers::LookupChain::BY_NAMESPACE, ActiveModelSerializers::LookupChain::BY_RESOURCE_NAMESPACE, ActiveModelSerializers::LookupChain::BY_RESOURCE ] ``` Each of the array entries represent a proc. A serializer lookup proc will be yielded 3 arguments. `resource_class`, `serializer_class`, and `namespace`. Note that: - `resource_class` is the class of the resource being rendered - by default `serializer_class` is `ActiveModel::Serializer` - for association lookup it's the "parent" serializer - `namespace` correspond to either the controller namespace or the [optionally] specified [namespace render option](./rendering.md#namespace) An example config could be: ```ruby ActiveModelSerializers.config.serializer_lookup_chain = [ lambda do |resource_class, serializer_class, namespace| "API::#{namespace}::#{resource_class}" end ] ``` If you simply want to add to the existing lookup_chain. Use `unshift`. ```ruby ActiveModelSerializers.config.serializer_lookup_chain.unshift( lambda do |resource_class, serializer_class, namespace| # ... end ) ``` See [lookup_chain.rb](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/lookup_chain.rb) for further explanations and examples. ## JSON API ##### jsonapi_resource_type Sets whether the [type](http://jsonapi.org/format/#document-resource-identifier-objects) of the resource should be `singularized` or `pluralized` when it is not [explicitly specified by the serializer](https://github.com/rails-api/active_model_serializers/blob/master/docs/general/serializers.md#type) Possible values: - `:singular` - `:plural` (default) ##### jsonapi_namespace_separator Sets separator string for namespaced models to render `type` attribute. | Separator | Example: Admin::User | |----|----| | `'-'` (default) | 'admin-users' | `'--'` (recommended) | 'admin--users' See [Recommendation for dasherizing (kebab-case-ing) namespaced object, such as `Admin::User`](https://github.com/json-api/json-api/issues/850) for more discussion. ##### jsonapi_include_toplevel_object Include a [top level jsonapi member](http://jsonapi.org/format/#document-jsonapi-object) in the response document. Possible values: - `true` - `false` (default) ##### jsonapi_version The latest version of the spec to which the API conforms. Default: `'1.0'`. *Used when `jsonapi_include_toplevel_object` is `true`* ##### jsonapi_toplevel_meta Optional top-level metadata. Not included if empty. Default: `{}`. *Used when `jsonapi_include_toplevel_object` is `true`* ##### jsonapi_use_foreign_key_on_belongs_to_relationship When true, the relationship will determine its resource object identifier without calling the association or its serializer. This can be useful when calling the association object is triggering unnecessary queries. For example, if a `comment` belongs to a `post`, and the comment uses the foreign key `post_id`, we can determine the resource object identifier `id` as `comment.post_id` and the `type` from the association options. Or quite simply, it behaves as `belongs_to :post, type: :posts, foreign_key: :post_id`. Note: This option has *no effect* on polymorphic associations as we cannot reliably determine the associated object's type without instantiating it. Default: `false`. ## Hooks To run a hook when ActiveModelSerializers is loaded, use `ActiveSupport.on_load(:action_controller) do end` active_model_serializers-0.10.10/docs/general/deserialization.md000066400000000000000000000046161351232231100247750ustar00rootroot00000000000000[Back to Guides](../README.md) # Deserialization This is currently an *experimental* feature. The interface may change. ## JSON API The `ActiveModelSerializers::Deserialization` defines two methods (namely `jsonapi_parse` and `jsonapi_parse!`), which take a `Hash` or an instance of `ActionController::Parameters` representing a JSON API payload, and return a hash that can directly be used to create/update models. The bang version throws an `InvalidDocument` exception when parsing fails, whereas the "safe" version simply returns an empty hash. - Parameters - document: `Hash` or `ActionController::Parameters` instance - options: - only: `Array` of whitelisted fields - except: `Array` of blacklisted fields - keys: `Hash` of fields the name of which needs to be modified (e.g. `{ :author => :user, :date => :created_at }`) Examples: ```ruby class PostsController < ActionController::Base def create Post.create(create_params) end def create_params ActiveModelSerializers::Deserialization.jsonapi_parse(params, only: [:title, :content, :author]) end end ``` Given a JSON API document, ``` document = { 'data' => { 'id' => 1, 'type' => 'post', 'attributes' => { 'title' => 'Title 1', 'date' => '2015-12-20' }, 'relationships' => { 'author' => { 'data' => { 'type' => 'user', 'id' => '2' } }, 'second_author' => { 'data' => nil }, 'comments' => { 'data' => [{ 'type' => 'comment', 'id' => '3' },{ 'type' => 'comment', 'id' => '4' }] } } } } ``` The entire document can be parsed without specifying any options: ```ruby ActiveModelSerializers::Deserialization.jsonapi_parse(document) #=> # { # title: 'Title 1', # date: '2015-12-20', # author_id: 2, # second_author_id: nil # comment_ids: [3, 4] # } ``` and fields, relationships, and polymorphic relationships can be specified via the options: ```ruby ActiveModelSerializers::Deserialization .jsonapi_parse(document, only: [:title, :date, :author], keys: { date: :published_at }, polymorphic: [:author]) #=> # { # title: 'Title 1', # published_at: '2015-12-20', # author_id: '2', # author_type: 'user' # } ``` ## Attributes/Json There is currently no deserialization for those adapters. active_model_serializers-0.10.10/docs/general/fields.md000066400000000000000000000014341351232231100230500ustar00rootroot00000000000000[Back to Guides](../README.md) # Fields If for any reason, you need to restrict the fields returned, you should use `fields` option. For example, if you have a serializer like this ```ruby class UserSerializer < ActiveModel::Serializer attributes :access_token, :first_name, :last_name end ``` and in a specific controller, you want to return `access_token` only, `fields` will help you: ```ruby class AnonymousController < ApplicationController def create render json: User.create(activation_state: 'anonymous'), fields: [:access_token], status: 201 end end ``` Note that this is only valid for the `json` and `attributes` adapter. For the `json_api` adapter, you would use ```ruby render json: @user, fields: { users: [:access_token] } ``` Where `users` is the JSONAPI type. active_model_serializers-0.10.10/docs/general/getting_started.md000066400000000000000000000070571351232231100250000ustar00rootroot00000000000000[Back to Guides](../README.md) # Getting Started ## Creating a Serializer The easiest way to create a new serializer is to generate a new resource, which will generate a serializer at the same time: ``` $ rails g resource post title:string body:string ``` This will generate a serializer in `app/serializers/post_serializer.rb` for your new model. You can also generate a serializer for an existing model with the serializer generator: ``` $ rails g serializer post ``` The generated serializer will contain basic `attributes` and `has_many`/`has_one`/`belongs_to` declarations, based on the model. For example: ```ruby class PostSerializer < ActiveModel::Serializer attributes :title, :body has_many :comments has_one :author end ``` and ```ruby class CommentSerializer < ActiveModel::Serializer attributes :name, :body belongs_to :post end ``` The attribute names are a **whitelist** of attributes to be serialized. The `has_many`, `has_one`, and `belongs_to` declarations describe relationships between resources. By default, when you serialize a `Post`, you will get its `Comments` as well. For more information, see [Serializers](/docs/general/serializers.md). ### Namespaced Models When serializing a model inside a namespace, such as `Api::V1::Post`, ActiveModelSerializers will expect the corresponding serializer to be inside the same namespace (namely `Api::V1::PostSerializer`). ### Model Associations and Nested Serializers When declaring a serializer for a model with associations, such as: ```ruby class PostSerializer < ActiveModel::Serializer has_many :comments end ``` ActiveModelSerializers will look for `PostSerializer::CommentSerializer` in priority, and fall back to `::CommentSerializer` in case the former does not exist. This allows for more control over the way a model gets serialized as an association of an other model. For example, in the following situation: ```ruby class CommentSerializer < ActiveModel::Serializer attributes :body, :date, :nb_likes end class PostSerializer < ActiveModel::Serializer has_many :comments class CommentSerializer < ActiveModel::Serializer attributes :body_short end end ``` ActiveModelSerializers will use `PostSerializer::CommentSerializer` (thus including only the `:body_short` attribute) when serializing a `Comment` as part of a `Post`, but use `::CommentSerializer` when serializing a `Comment` directly (thus including `:body, :date, :nb_likes`). ### Extending a Base `ApplicationSerializer` By default, new serializers descend from `ActiveModel::Serializer`. However, if you wish to share behavior across your serializers, you can create an `ApplicationSerializer` at `app/serializers/application_serializer.rb`: ```ruby class ApplicationSerializer < ActiveModel::Serializer end ``` Then any newly-generated serializers will automatically descend from `ApplicationSerializer`. ``` $ rails g serializer post ``` Now generates: ```ruby class PostSerializer < ApplicationSerializer attributes :id end ```` ## Rails Integration ActiveModelSerializers will automatically integrate with your Rails app, so you won't need to update your controller. This is a example of how the controller will look: ```ruby class PostsController < ApplicationController def show @post = Post.find(params[:id]) render json: @post end end ``` If you wish to use Rails url helpers for link generation, e.g., `link(:resources) { resources_url }`, ensure your application sets `Rails.application.routes.default_url_options`. ```ruby Rails.application.routes.default_url_options = { host: 'example.com' } ``` active_model_serializers-0.10.10/docs/general/instrumentation.md000066400000000000000000000017511351232231100250470ustar00rootroot00000000000000[Back to Guides](../README.md) # Instrumentation ActiveModelSerializers uses the [ActiveSupport::Notification API](http://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event), which allows for subscribing to events, such as for logging. ## Events Name: `render.active_model_serializers` Payload (example): ```ruby { serializer: PostSerializer, adapter: ActiveModelSerializers::Adapter::Attributes } ``` Subscribing: ```ruby ActiveSupport::Notifications.subscribe 'render.active_model_serializers' do |name, started, finished, unique_id, data| # whatever end ActiveSupport::Notifications.subscribe 'render.active_model_serializers' do |*args| event = ActiveSupport::Notifications::Event.new(*args) # event.payload # whatever end ``` ## [LogSubscriber](http://api.rubyonrails.org/classes/ActiveSupport/LogSubscriber.html) ActiveModelSerializers includes an `ActiveModelSerializers::LogSubscriber` that attaches to `render.active_model_serializers`. active_model_serializers-0.10.10/docs/general/key_transforms.md000066400000000000000000000016671351232231100246600ustar00rootroot00000000000000[Back to Guides](../README.md) # Key Transforms Key Transforms modify the casing of keys and keys referenced in values in serialized responses. Provided key transforms: | Option | Result | |----|----| | `:camel` | ExampleKey | | `:camel_lower` | exampleKey | | `:dash` | example-key | | `:unaltered` | the original, unaltered key | | `:underscore` | example_key | | `nil` | use the adapter default | Key translation precedence is as follows: ##### Adapter option `key_transform` is provided as an option via render. ```render json: posts, each_serializer: PostSerializer, key_transform: :camel_lower``` ##### Configuration option `key_transform` is set in `ActiveModelSerializers.config.key_transform`. ```ActiveModelSerializers.config.key_transform = :camel_lower``` ##### Adapter default Each adapter has a default transform configured: | Adapter | Default Key Transform | |----|----| | `Json` | `:unaltered` | | `JsonApi` | `:dash` | active_model_serializers-0.10.10/docs/general/logging.md000066400000000000000000000011221351232231100232220ustar00rootroot00000000000000[Back to Guides](../README.md) # Logging The default logger in a Rails application will be `Rails.logger`. When there is no `Rails.logger`, the default logger is an instance of `ActiveSupport::TaggedLogging` logging to STDOUT. You may customize the logger in an initializer, for example: ```ruby ActiveModelSerializers.logger = Logger.new(STDOUT) ``` You can also disable the logger, just put this in `config/initializers/active_model_serializers.rb`: ```ruby require 'active_model_serializers' ActiveSupport::Notifications.unsubscribe(ActiveModelSerializers::Logging::RENDER_EVENT) ``` active_model_serializers-0.10.10/docs/general/rendering.md000066400000000000000000000160061351232231100235600ustar00rootroot00000000000000[Back to Guides](../README.md) # Rendering ### Implicit Serializer In your controllers, when you use `render :json`, Rails will now first search for a serializer for the object and use it if available. ```ruby class PostsController < ApplicationController def show @post = Post.find(params[:id]) render json: @post end end ``` In this case, Rails will look for a serializer named `PostSerializer`, and if it exists, use it to serialize the `Post`. ### Explicit Serializer If you wish to use a serializer other than the default, you can explicitly pass it to the renderer. #### 1. For a resource: ```ruby render json: @post, serializer: PostPreviewSerializer ``` #### 2. For a resource collection: Specify the serializer for each resource with `each_serializer` ```ruby render json: @posts, each_serializer: PostPreviewSerializer ``` The default serializer for collections is `CollectionSerializer`. Specify the collection serializer with the `serializer` option. ```ruby render json: @posts, serializer: CollectionSerializer, each_serializer: PostPreviewSerializer ``` ## Serializing non-ActiveRecord objects See [README](../../README.md#what-does-a-serializable-resource-look-like) ## SerializableResource options See [README](../../README.md#activemodelserializersserializableresource) ### adapter_opts #### fields If you are using `json` or `attributes` adapter ```ruby render json: @user, fields: [:access_token] ``` See [Fields](fields.md) for more information. #### adapter This option lets you explicitly set the adapter to be used by passing a registered adapter. Your options are `:attributes`, `:json`, and `:json_api`. ``` ActiveModel::Serializer.config.adapter = :json_api ``` #### key_transform ```render json: posts, each_serializer: PostSerializer, key_transform: :camel_lower``` See [Key Transforms](key_transforms.md) for more information. #### meta A `meta` member can be used to include non-standard meta-information. `meta` can be utilized in several levels in a response. ##### Top-level To set top-level `meta` in a response, specify it in the `render` call. ```ruby render json: @post, meta: { total: 10 } ``` The key can be customized using `meta_key` option. ```ruby render json: @post, meta: { total: 10 }, meta_key: "custom_meta" ``` `meta` will only be included in your response if you are using an Adapter that supports `root`, e.g., `JsonApi` and `Json` adapters. The default adapter, `Attributes` does not have `root`. ##### Resource-level To set resource-level `meta` in a response, define meta in a serializer with one of the following methods: As a single, static string. ```ruby meta stuff: 'value' ``` As a block containing a Hash. ```ruby meta do { rating: 4, comments_count: object.comments.count } end ``` #### links If you wish to use Rails url helpers for link generation, e.g., `link(:resources) { resources_url }`, ensure your application sets `Rails.application.routes.default_url_options`. ##### Top-level JsonApi supports a [links object](http://jsonapi.org/format/#document-links) to be specified at top-level, that you can specify in the `render`: ```ruby links_object = { href: "http://example.com/api/posts", meta: { count: 10 } } render json: @posts, links: links_object ``` That's the result: ```json { "data": [ { "type": "posts", "id": "1", "attributes": { "title": "JSON API is awesome!", "body": "You should be using JSON API", "created": "2015-05-22T14:56:29.000Z", "updated": "2015-05-22T14:56:28.000Z" } } ], "links": { "href": "http://example.com/api/posts", "meta": { "count": 10 } } } ``` This feature is specific to JsonApi, so you have to use the use the [JsonApi Adapter](adapters.md#jsonapi) ##### Resource-level In your serializer, define each link in one of the following methods: As a static string ```ruby link :link_name, 'https://example.com/resource' ``` As a block to be evaluated. When using Rails, URL helpers are available. Ensure your application sets `Rails.application.routes.default_url_options`. ```ruby link :link_name_ do "https://example.com/resource/#{object.id}" end link(:link_name) { "https://example.com/resource/#{object.id}" } link(:link_name) { resource_url(object) } link(:link_name) { url_for(controller: 'controller_name', action: 'index', only_path: false) } ``` ### serializer_opts #### include See [Adapters: Include Option](/docs/general/adapters.md#include-option). #### Overriding the root key Overriding the resource root only applies when using the JSON adapter. Normally, the resource root is derived from the class name of the resource being serialized. e.g. `UserPostSerializer.new(UserPost.new)` will be serialized with the root `user_post` or `user_posts` according the adapter collection pluralization rules. When using the JSON adapter in your initializer (ActiveModelSerializers.config.adapter = :json), or passing in the adapter in your render call, you can specify the root by passing it as an argument to `render`. For example: ```ruby render json: @user_post, root: "admin_post", adapter: :json ``` This will be rendered as: ```json { "admin_post": { "title": "how to do open source" } } ``` Note: the `Attributes` adapter (default) does not include a resource root. You also will not be able to create a single top-level root if you are using the :json_api adapter. #### namespace The namespace for serializer lookup is based on the controller. To configure the implicit namespace, in your controller, create a before filter ```ruby before_action do self.namespace_for_serializer = Api::V2 end ``` `namespace` can also be passed in as a render option: ```ruby @post = Post.first render json: @post, namespace: Api::V2 ``` This tells the serializer lookup to check for the existence of `Api::V2::PostSerializer`, and if any relations are rendered with `@post`, they will also utilize the `Api::V2` namespace. The `namespace` can be any object whose namespace can be represented by string interpolation (i.e. by calling to_s) - Module `Api::V2` - String `'Api::V2'` - Symbol `:'Api::V2'` Note that by using a string and symbol, Ruby will assume the namespace is defined at the top level. #### serializer Specify which serializer to use if you want to use a serializer other than the default. For a single resource: ```ruby @post = Post.first render json: @post, serializer: SpecialPostSerializer ``` To specify which serializer to use on individual items in a collection (i.e., an `index` action), use `each_serializer`: ```ruby @posts = Post.all render json: @posts, each_serializer: SpecialPostSerializer ``` #### scope See [Serializers: Scope](/docs/general/serializers.md#scope). #### scope_name See [Serializers: Scope](/docs/general/serializers.md#scope). ## Using a serializer without `render` See [Usage outside of a controller](../howto/outside_controller_use.md#serializing-before-controller-render). ## Pagination See [How to add pagination links](../howto/add_pagination_links.md). active_model_serializers-0.10.10/docs/general/serializers.md000066400000000000000000000335301351232231100241400ustar00rootroot00000000000000[Back to Guides](../README.md) # Serializers Given a serializer class: ```ruby class SomeSerializer < ActiveModel::Serializer end ``` The following methods may be defined in it: ### Attributes #### ::attributes Serialization of the resource `title` and `body` | In Serializer | #attributes | |---------------------------- |-------------| | `attributes :title, :body` | `{ title: 'Some Title', body: 'Some Body' }` | `attributes :title, :body`
`def body "Special #{object.body}" end` | `{ title: 'Some Title', body: 'Special Some Body' }` #### ::attribute Serialization of the resource `title` | In Serializer | #attributes | |---------------------------- |-------------| | `attribute :title` | `{ title: 'Some Title' } ` | `attribute :title, key: :name` | `{ name: 'Some Title' } ` | `attribute(:title) { 'A Different Title'}` | `{ title: 'A Different Title' } ` | `attribute :title`
`def title 'A Different Title' end` | `{ title: 'A Different Title' }` An `if` or `unless` option can make an attribute conditional. It takes a symbol of a method name on the serializer, or a lambda literal. e.g. ```ruby attribute :private_data, if: :is_current_user? attribute :another_private_data, if: -> { scope.admin? } def is_current_user? object.id == current_user.id end ``` ### Associations The interface for associations is, generically: > `association_type(association_name, options, &block)` Where: - `association_type` may be `has_one`, `has_many`, `belongs_to`. - `association_name` is a method name the serializer calls. - optional: `options` may be: - `key:` The name used for the serialized association. - `serializer:` - `if:` - `unless:` - `virtual_value:` - `polymorphic:` defines if polymorphic relation type should be nested in serialized association. - `type:` the resource type as used by JSON:API, especially on a `belongs_to` relationship. - `class_name:` the (String) model name used to determine `type`, when `type` is not given. e.g. `class_name: "Comment"` would imply the type `comments` - `foreign_key:` used by JSON:API on a `belongs_to` relationship to avoid unnecessarily loading the association object. - `namespace:` used when looking up the serializer and `serializer` is not given. Falls back to the parent serializer's `:namespace` instance options, which, when present, comes from the render options. See [Rendering#namespace](rendering.md#namespace] for more details. - optional: `&block` is a context that returns the association's attributes. - prevents `association_name` method from being called. - return value of block is used as the association value. - yields the `serializer` to the block. - `include_data false` prevents the `data` key from being rendered in the JSON API relationship. #### ::has_one e.g. ```ruby has_one :bio has_one :blog, key: :site has_one :blog, class_name: "Blog" has_one :maker, virtual_value: { id: 1 } has_one :blog do |serializer| serializer.cached_blog end def cached_blog cache_store.fetch("cached_blog:#{object.updated_at}") do Blog.find(object.blog_id) end end ``` ```ruby has_one :blog, if: :show_blog? # you can also use a string or lambda # has_one :blog, if: 'scope.admin?' # has_one :blog, if: -> (serializer) { serializer.scope.admin? } # has_one :blog, if: -> { scope.admin? } def show_blog? scope.admin? end ``` #### ::has_many e.g. ```ruby has_many :comments has_many :comments, key: :reviews has_many :comments, serializer: CommentPreviewSerializer has_many :comments, class_name: "Comment" has_many :reviews, virtual_value: [{ id: 1 }, { id: 2 }] has_many :comments, key: :last_comments do last(1) end ``` #### ::belongs_to e.g. ```ruby belongs_to :author, serializer: AuthorPreviewSerializer belongs_to :author, key: :writer belongs_to :author, class_name: "Author" belongs_to :post belongs_to :blog def blog Blog.new(id: 999, name: 'Custom blog') end ``` ### Polymorphic Relationships Polymorphic relationships are serialized by specifying the relationship, like any other association. For example: ```ruby class PictureSerializer < ActiveModel::Serializer has_one :imageable end ``` You can specify the serializers by [overriding serializer_for](serializers.md#overriding-association-serializer-lookup). For more context about polymorphic relationships, see the [tests](../../test/adapter/polymorphic_test.rb) for each adapter. ### Caching #### ::cache e.g. ```ruby cache key: 'post', expires_in: 0.1, skip_digest: true cache expires_in: 1.day, skip_digest: true cache key: 'writer', skip_digest: true cache only: [:name], skip_digest: true cache except: [:content], skip_digest: true cache key: 'blog' cache only: [:id] ``` #### #cache_key e.g. ```ruby # Uses a custom non-time-based cache key def cache_key "#{self.class.name.downcase}/#{self.id}" end ``` ### Other #### ::type When using the `:json_api` adapter, the `::type` method defines the JSONAPI [type](http://jsonapi.org/format/#document-resource-object-identification) that will be rendered for this serializer. When using the `:json` adapter, the `::type` method defines the name of the root element. It either takes a `String` or `Symbol` as parameter. Note: This method is useful only when using the `:json_api` or `:json` adapter. Examples: ```ruby class UserProfileSerializer < ActiveModel::Serializer type 'profile' attribute :name end class AuthorProfileSerializer < ActiveModel::Serializer type :profile attribute :name end ``` With the `:json_api` adapter, the previous serializers would be rendered as: ``` json { "data": { "id": "1", "type": "profile", "attributes": { "name": "Julia" } } } ``` With the `:json` adapter, the previous serializer would be rendered as: ``` json { "profile": { "name": "Julia" } } ``` #### ::link ```ruby link :self do href "https://example.com/link_author/#{object.id}" end link(:author) { link_author_url(object) } link(:link_authors) { link_authors_url } link :other, 'https://example.com/resource' link(:posts) { link_author_posts_url(object) } ``` Just like attributes, links also support conditions in options ```ruby link(:secret, if: :internal?) { object.secret_link } def internal? instance_options[:context] == :internal end ``` #### #object The object being serialized. #### #root Resource root which is included in `JSON` adapter. As you can see at [Adapters Document](adapters.md), `Attribute` adapter (default) and `JSON API` adapter does not include root at top level. By default, the resource root comes from the `model_name` of the serialized object's class. There are several ways to specify root: * [Overriding the root key](rendering.md#overriding-the-root-key) * [Setting `type`](serializers.md#type) * Specifying the `root` option, e.g. `root: 'specific_name'`, during the serializer's initialization: ```ruby ActiveModelSerializers::SerializableResource.new(foo, root: 'bar') ``` #### #scope Allows you to include in the serializer access to an external method. It's intended to provide an authorization context to the serializer, so that you may e.g. show an admin all comments on a post, else only published comments. - `scope` is a method on the serializer instance that comes from `options[:scope]`. It may be nil. - `scope_name` is an option passed to the new serializer (`options[:scope_name]`). The serializer defines a method with that name that calls the `scope`, e.g. `def current_user; scope; end`. Note: it does not define the method if the serializer instance responds to it. That's a lot of words, so here's some examples: First, let's assume the serializer is instantiated in the controller, since that's the usual scenario. We'll refer to the serialization context as `controller`. | options | `Serializer#scope` | method definition | |-------- | ------------------|--------------------| | `scope: current_user, scope_name: :current_user` | `current_user` | `Serializer#current_user` calls `controller.current_user` | `scope: view_context, scope_name: :view_context` | `view_context` | `Serializer#view_context` calls `controller.view_context` We can take advantage of the scope to customize the objects returned based on the current user (scope). For example, we can limit the posts the current user sees to those they created: ```ruby class PostSerializer < ActiveModel::Serializer attributes :id, :title, :body # scope comments to those created_by the current user has_many :comments do object.comments.where(created_by: current_user) end end ``` Whether you write the method as above or as `object.comments.where(created_by: scope)` is a matter of preference (assuming `scope_name` has been set). Keep in mind that the scope can be set to any available controller reference. This can be utilized to provide access to any other data scopes or presentation helpers. ##### Controller Authorization Context In the controller, the scope/scope_name options are equal to the [`serialization_scope`method](https://github.com/rails-api/active_model_serializers/blob/d02cd30fe55a3ea85e1d351b6e039620903c1871/lib/action_controller/serialization.rb#L13-L20), which is `:current_user`, by default. Specifically, the `scope_name` is defaulted to `:current_user`, and may be set as `serialization_scope :view_context`. The `scope` is set to `send(scope_name)` when `scope_name` is present and the controller responds to `scope_name`. Thus, in a serializer, the controller provides `current_user` as the current authorization scope when you call `render :json`. **IMPORTANT**: Since the scope is set at render, you may want to customize it so that `current_user` isn't called on every request. This was [also a problem](https://github.com/rails-api/active_model_serializers/pull/1252#issuecomment-159810477) in [`0.9`](https://github.com/rails-api/active_model_serializers/tree/0-9-stable#customizing-scope). We can change the scope from `current_user` to `view_context`, which is included in subclasses of `ActionController::Base`. ```diff class SomeController < ActionController::Base + serialization_scope :view_context def current_user User.new(id: 2, name: 'Bob', admin: true) end def edit user = User.new(id: 1, name: 'Pete') render json: user, serializer: AdminUserSerializer, adapter: :json_api end end ``` We could then use the controller method `view_context` in our serializer, like so: ```diff class AdminUserSerializer < ActiveModel::Serializer attributes :id, :name, :can_edit def can_edit? + view_context.current_user.admin? end end ``` So that when we render the `#edit` action, we'll get ```json {"data":{"id":"1","type":"users","attributes":{"name":"Pete","can_edit":true}}} ``` Where `can_edit` is `view_context.current_user.admin?` (true). You can also tell what to set as `serialization_scope` for specific actions. For example, use `admin_user` only for `Admin::PostSerializer` and `current_user` for rest. ```ruby class PostsController < ActionController::Base before_action only: :edit do self.class.serialization_scope :admin_user end def show render json: @post, serializer: PostSerializer end def edit @post.save render json: @post, serializer: Admin::PostSerializer end private def admin_user User.new(id: 2, name: 'Bob', admin: true) end def current_user User.new(id: 2, name: 'Bob', admin: false) end end ``` Note that any controller reference which provides the desired scope is acceptable, such as another controller method for loading a different resource or reference to helpers. For example, `ActionController::API` does not include `ActionView::ViewContext`, and would need a different reference for passing any helpers into a serializer via `serialization_scope`. #### #read_attribute_for_serialization(key) The serialized value for a given key. e.g. `read_attribute_for_serialization(:title) #=> 'Hello World'` #### #links Allows you to modify the `links` node. By default, this node will be populated with the attributes set using the [::link](#link) method. Using `links: nil` will remove the `links` node. ```ruby ActiveModelSerializers::SerializableResource.new( @post, adapter: :json_api, links: { self: { href: 'http://example.com/posts', meta: { stuff: 'value' } } } ) ``` #### #json_key Returns the key used by the adapter as the resource root. See [root](#root) for more information. ## Examples Given two models, a `Post(title: string, body: text)` and a `Comment(name: string, body: text, post_id: integer)`, you will have two serializers: ```ruby class PostSerializer < ActiveModel::Serializer cache key: 'posts', expires_in: 3.hours attributes :title, :body has_many :comments end ``` and ```ruby class CommentSerializer < ActiveModel::Serializer attributes :name, :body belongs_to :post end ``` Generally speaking, you, as a user of ActiveModelSerializers, will write (or generate) these serializer classes. ## More Info For more information, see [the Serializer class on GitHub](https://github.com/rails-api/active_model_serializers/blob/0-10-stable/lib/active_model/serializer.rb) ## Overriding association methods To override an association, call `has_many`, `has_one` or `belongs_to` with a block: ```ruby class PostSerializer < ActiveModel::Serializer has_many :comments do object.comments.active end end ``` ## Overriding attribute methods To override an attribute, call `attribute` with a block: ```ruby class PostSerializer < ActiveModel::Serializer attribute :body do object.body.downcase end end ``` ## Overriding association serializer lookup If you want to define a specific serializer lookup for your associations, you can override the `ActiveModel::Serializer.serializer_for` method to return a serializer class based on defined conditions. ```ruby class MySerializer < ActiveModel::Serializer def self.serializer_for(model, options) return SparseAdminSerializer if model.class.name == 'Admin' super end # the rest of the serializer end ``` active_model_serializers-0.10.10/docs/how-open-source-maintained.jpg000066400000000000000000010502201351232231100255040ustar00rootroot00000000000000’Ų’įExifII*’ģDuckyd’įqhttp://ns.adobe.com/xap/1.0/ ’īAdobedĄ’Ū„’Ą„Š’Ä    %! 1A"Q aq2#‘BRr3$”±bÖW—XĮ‚’¢²Cs³%•ÕGYS“4”Ō&'ct5µÅ6v–¶w×8˜ŃĀÓdeĘg·šŅƒ£ĆDT„¤“EFVf†7(ØI !1AQaq‘”±ĮŃ"2RBr’3šįb‚¢²#S4Ās5ŅCc$ńƒ% ⣳TdtĆ6“D’Ś ?ļń¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"§­vŚ„·5r¼Ł«ŌŹ…n=yk®×5]­ĄE5/{©9©É‡,āā£ŪŖ‹.Ŗi:ˆ†ˆ¹–oH4jżƒ¦ļ˜¾nƒĘOY®“–9H«6tÕŹ&:.øDå9C NQB¾C8óä׏ęä—×·Ÿ”{³ć‡Ķ/lk–ˆĖ=Q(§oŚČÄGĢĻ Ł7Q“Œž7p';gI˜Å hNm4DŃDM4DŃDM4EŃē_äh\°ČÕ¬ĆYø×ø>ć(Ēr|µ…V#ЧpÜ݃!×'.“0śŌ ėĄ1s$eP9MŹmˆ«®,ņWsX›“˜UōœŽ/ĢÕ&— Šóqį6‹%×pÉŌtŌa\;# ˆ™6Kµt‘T„]Nrģa"“<ķņŏ˜Ņ•—yku˜”ŠÆŁ:@MDÓm lģ<ż…’oXŒ’^61¤]ūµ×T6āTŹ¢‚R ×AdÜ ‹„»Å' &²~¢J¢§¦©äļEb&²Gķ0nS”¦(ōé¢-]c«ĘG“ž;ł[ćܟ"8äĘż[æJc K&ĀFA[ėHˆ ŻĆ(ĀNXį$£œĀ[.ŻćG«"ÆyÓÅRU2dWDVW\ŽĮ*Q#ÅäŅ–üUV —9§M„cțƒ»vś`е±yōĻóŁ_ĆO:ŖÖx÷Ē9gēøĒŹN?Ųėų¢u¶äU~ń#I—ø6ĖŽkY9Zū%,e›®G­ćÜAWUHō]9;}dg)~ «ŗ"fAVÉ8vįSФD[®E|Ó~ ¾kųüē­ĆŠXHøGäŽ+įyœ a£-)oØgܓ|+CÉI¬Ū#Y#œTŚ?fœ:ÖL !)ˆ9*¤S+Ä_“?#²<ü̾*¼“Õ1Ū.BÓpÓ|į‰2Ž>­5ÆFäŖŚr‘%˜n£øw¬ŖvFn"¬eV1ĢT,p x9FĻ„]¢Ń\_žH9?Ƌo>|iųķĄrł³œyūȾ`68Ÿœg)Š1:®¾qDžÉr3zFä"^B¢s*ģ«¬Ś”]»p©RE‹¢/FÜ—ąš¶(…ä ŖÆyĪQ8ņ£—nT˜U+•+NEgɽ¾~½®ĘŒŠ•œMe’H§ų@¢‹ØG¼{Zó)ųŠq§Œ<É?<§ø'Ēi<į—1¬ éź›ĖŽF»ÕéÖR}‹ę ~¶'&ŅMėBvqēAŖØ¬õe’"”¼äų]į׍õ¾pųņØŹq£‘¼Ģø“!Wņ¼MŅÕeŗ\YݲjŠfvW— Ɇ³rÖėdT›#¹ÓlĶ‹†eD¹]=^NXł—ēīt—ńGĮĪĮŅ8ē˜ü¤ńkŒÜęE‰č]ĘiäKküŃI„cW”¦Šf±ŌT©¤erä±vVč²d»”_“"½<¼˜eScÜu/]ĒĪ`ē0 H[d郧?ź1ć#É<#yŌ¾š}wėutÜ:bdNC•.öŚ"ó‡¼qS^&ł ĖIžWx*”yeùŸ‘™4AņÕ¤E“+Ų”q}ÖWė."fWg²F?’Œ*ĀéŅŅ•Ų~øåńżó#3U",ņxžņ}į×x¶äŽWāŽMĒCŠ/.—)ńß4ΰu—1ÜīQ“LՊ=0Ļ'd”ę*łō¢0U5P“t›‰WEAŅ©¼QBhŠPüœ~%īAął/(øw‹œXį$pŹŻ«Ü]³=M­|ĮT×.\ZÆŃ6‰ł8K‚E‡Ž|)I™Ä*²`ŠEׯ5Y©]‘^¼Åų‡³>D°x »ń6ŸU§ćŸ&ĪkgŠ6R„qk’ƒ{ž0ž¶Õk¶8Õ œkÓwgq“ HOœ!Y¬įŸhŖĖDYę/•LĮ€<ŻxńńĒDm‹g±W&±Äķ43š†›s‘«N¤UɈRåėV&Q0å;œvaΘ=õŪ‚ą`LUn²DXQyē“ĶŸ5¹ū›ŽžZł5>ą+iļ£eÅIVµõ>{ėqŒ0MdNķłōæņ£Ģ§“ĪmMxÅćFxĒÜ-ž¦Ųc9ņŹģ9_{kŽä„ż9‘ļ\P¼ĖŲĖ;*j— W‘š‘I­$¢ÕéNd}ŅKDYFā÷?s¼nݲG›Y˜]šxėyĖŲ¢ Ż=b ĀLóN/E+1A“aˆ'[;¹\ņ\[Uš5nVlŽI*ÅI·IŗŠ*B.¹˜?ńłaä‡)øŗĻŗāMŃ·&3…V"€øš­aɹ:‘U±H…ŗŻ—óT{O”ćÉčśĢy”īVDĻŁ6*ҲqlŃI7„]üņ%ņ±Šńżē'ݤ>“LĒė5ņŻ+čŖć镊„+Ū ü‡Ė S¬æÉÅG*§aLnŻ€GD]įüÜyŹäVäļ•|Fā~!ń±ĘŪ‹ō”q¾‰–ŽČ9’©)!V²G‘ś¶o©2Ż»—M¤ā"‹4åHÖ H;hŖ:"Ÿ>I¼īņ†‘ĮY7‡˜±TÉžX+©VńīDæŽIbˆćFžĘ įcŽ×FŽ„VD4Āó"1oŻ*ɛ4ź9źE3%ˆØV|€ņßįĖ¼Ć|īęESČł÷•#p’7gxĪ·2~Ķvw°iƒńÅ$²”¦ö›œZ…UiFKóv)°‡r &ģ‹{ł‘åÉÆ”īFų°į¦`…ń±Iįd¦ß”³ū&1¹› fŽ­)ž7EŒ3čź¹é±–s]:ł6.…Fmįg/Ü(Vńė‘V\ó ĶLGˆüæa®~×j܋埉 C¬›÷Æ·†¢Įgz*”¹¹ØņO© ]wę¾›ł&qœ±2bQÓ•Ńį÷š6—‘ÅŁsgŸ|”wמĢųģ­ä‰,5Čč ܝ‰”ņ.HmęR¶Õɝør’Łņ­‹ė•”€v7T‹¾ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"|·ÅĻSäŽM“y%37óF(N9Ū©-Ÿ"c‹%@¬=ˆJ³nčÓ‡UØ „Źa2DX…ü3Üwå—¼Vc¼7˼vž(¶Įå,»-Žńü„{ØĖÜ/“Y¾ń5šĶĆ牷¶Éßd§Ż·M"7ąWŽ*‰‚ą©„Š9åŸĆ¢93)y£“iœ *øŹe3ĢŌjq•™Jć|’NČńłµķŅī“U™2±@b€$‘~MĀN_“›~ƒ”Č¢$pč‹’ÕĒńDßųۈü@6Ā|zćOiųśŽ™Ō;Õ>Pöœ^o]$™\Ÿ%~!&éč“Ö±XéÉw ˜śke]ŠdY’å€,-‘ü-ÕüZā‰T™Ś0\K<‡„røG©-É8¢Ųeg.ÖäP<”ćaņÄ„ŗmŒŠ ŠDĒĖ€7E“pѰ?įqä!üeņ׏µYć’+¹g‘c›1õē*ŪBåcˆ…¢·“€ū“25ZRčr7ĢrsGŖųR\n—ŠķÓ"ī!ā’‚_Į¼C­L–Ō÷A>{ŗ¹Śé“-ņÆm7ėVŖī»HuģR«!‚¦:Ķ¢›¶ECœé˜ę"ÄÆ—Ģ;āėÉ,±Ē³÷ rłĒ„ńŒīfÅYnŖ é-ŅĒÅ āķunv–éUīIZµ2éE&åE‹µX„3¶īȰCĆÜJq峟>.8ž§mÜXɾ'o“9å\#rŅ!›•œ”F ©yŗĖŹT’›sšb«–LA‹6źžR5!naQHč7~(ģEĘ*Ÿ‡ģ…ų÷šM­Ć'Ϛež„3/†­ÖrjXŲ«'“^]hŅ5$ÕŅńōĀŚŠ›„Ī蝠Iˆ„æ=?å¢¹ą’ p“„/–ærc‹™®æÉŲĖBo!ńœęsŹļcgė™9ó )»ciO\EŁsŠŅĄf©×#™™įŒ±ˆ±_š|Q~ žGņ Žž[ł7*ł”XĻ“ŲR­Ć¼y|Ę0/ńvĆv×9«ŲŁ)L†–/­Ä»“Ć"vń裤åZ]g’+&ąTL„^Š"ź‘ā+Š>Vų[ĶNnqĒ qĆ!ćŪ>rƓ‹˜å†BŽS!Ī”’b›Ēćfŗ„^øĖ<õ$[Ć0,£)øFNł‡jüńŽÕˆŖŽ!sĒ<'ókŁŅ£Žóß'Üó*”#+ i…“°šc’q10V?ø4]ņ-kļā”’|Į«„Ū®ƒeżg-¹]Āz"ĘÖRš‹ä‹-ųOńĀÉĢUWw–ųļĖK³=āĖ6^ضƒ«ā[žLĖó‘Vū„<™„«z­bā“W(ÖW•|Ʉ‡¦Ł‹µQPؑd·˜¾*óW"æ÷¹Į`ĘQVīā ŁĶŖÖ¼¼R’w³q™­xь„uWˆL¤é<y¬ŹĘø" µXcœĢS%Śb+ŁČŸ\§¼ž#Īy¤Ö!–āŽįžBÄyŠŽK­~.j:ä“+ŲAA:¤8mf²¶›’͵õš®Õ³¶ˆ„s“9;s Ų"ĀĻ? 9q·$łįŸńW+ņoyČ+<2żN× “1~qĄ·ł¹‹£ˆLµ‹¤ņ„Jåʓ O&ŗRneĻ&DŠp”lĻ łKqš^K˜bSr^„lŸ¬9ŗa©dÕr„&9(Õ+9Ük8ØVU™ł£¹rƒ¦(“h˜‹Bø+V€æĖ$E„!~4¼`*ˆł”ń5ŠsfBČęrS‹Y^ÖŹ ć#3e WĒĒ{Z±Ū,•£x5éÕĒL' ]ÅĢVQr™%Ņ|ę?DVC+pĶ·œ»2¤yC‚ľ<81- ÆvL …®Y,ęki£y™8;„ö=$ćŽČ$ÉĆŁ¤†:ā©”ß9WDŠÕž&ī<Ż)<—š0ć‰7ˆž9Üc²ūž"āKė%$})£j34 ‹Čmż‹ čLĘ\°RėŗDÆŹńā…Eƒ2+Żä—ĝŽcš“ŁņӑV^jbģ°ßcfµH4bĒLƎ-õ{,Ŗ”ˆ+|/.Õēoć˜D7vđŽœüąœnE•Ļ?Ž+óÆ,ükqWˆž=h5¢X8×ČÜ1N®«rƒĒ)Ó1:Ä9G6{bŸ}‚F¦«m‡zŖ 8łåY±TķˆåŚh ©bĖÅgļ­*ßM ™ŠčŪjö ĻŽ ėē1v/ÆD»ŠśĢ›%›<Ž˜ŒłÆ]Ŗé(šØ®Bœ¦)€ŗlU”’όFņ|aĄX£y7Ąµé™7ųŸŁ¦} ˆZŌ¬‹éÖ¬$–å涓f³”Ut„’žA¹ÕʬŁ"¦‘xćįŚņ˚’”¬ßäzƒÄ,¹ä*†”-_ b²ÓÓ .ŃüĘ\ˆµŽ+ŽJŃUŒ¬ENačȦ©§--iq3&źI×ÕN²®ČŖY^4ž'üéĘ\MįžGńć‹<[£āŹē2Ÿ-źŹ”ŗ#RėģhģĪtÉ6+ꬭ˜ś=²l#+q²®U'£ŃĆ“"—žR|åĢwĮŸµŸŌV?;ų«ĶQY‹iÆU„rPY¦«÷ܝ}z¬õ‚±_ycŸĢōXY×ŃßV(GŚ,U"ٹȱӋ|HłÕaä׃>\y7X©ę¼Ė}äÄ|Æ#q=RńDŒqÅģD«Ō†ČŌ…²>šõ„F8±Īœ-mŌÉŲż%¹—tłģƒ‘H‹6üLńĮŹN=~#^|óe:Lcrk-RÆ]\ LY±8’ē!$ØmäҲ”iglĒvw€õhõ#Ė SßĢ;:"‹™ÆŽZübyOę28·Ķ‰ļÄé ¼nrf‰¼”ä' *9]„Ń7-)–ØHx:F7øQ]ømMæ8M„»|L„É” &t„āūĄ©(Ar܋:ÜšįŸ¼xpó›ońW&Łę)œ¾©Ł&å¹_—"ÕĆ+·†²„ŽŅI[3 åŽBjĀ£ĒO&R– #—ā܄`B.²üńåļ–ŲėqŽkĀǰ†n«Xٚóägd ˆe/ ¶œr»Ü%PĄŹEx’z½OUś)³ņ ·TNÖ-ƒ²¦fä^€¼JÄ׌ ÅĪ;į<›‘eĢ‹‰p®4ĒW¬”$āUćėż²ŸP‰Ÿ¶øy:ķüć³ĪJ1UĒŖńe](*aPL:"š"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ±ĶĻĻņiNmĖĢ.Žó7L쓫̆ĆEæWŲČNž +]JF*FJ¶ķaõų]1#×‘"-śĶs ń{Ā?5kMcˆa†<^ö哛ÕĀNjvåøż,\ż!”ŻĘÕ!)0hXœXZG t#ŪØ²Ŗ‘UUNr,€č‰¢&ˆš"h‰¢&ˆš"h‹f¼Œ{dß,åó6éF gRJ®é“lTLąĪC”­*1Äź J6ūś"·¶ نjT“2E«.cĪ;tšc›_lڬ5-ĀŚbÅ LŒ³h5^Œ/¤UÄżČØnClEq™¼i Ń«ö›¾błŗ=fŗNZ!€“]"źęSīŚVÄw buŻw²n£aäę"–tŁ’ī\øb’ÅnŖŖØ‘Īb)Ł¢&ˆš"h‰¢&ˆ¢_6ųQü‚ńīĖĘ.HDOMb«\ÕJ~YfĄīÆ7õ ]Š>ĶfslˆwLČw±ÅM~Ą(ŻC¢`0J8XŗÜ,Ev”#į`bćįb&uTM”\[D˜°h™×:«š BœĘ0zˆ]D™žńĀœÕ"r•©•¹7GĮÆxł]°'d’oZkC}9?8ŖėÕ[8ēÖ2 ¦E©-ß³G]‚A2H")•¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢.‘¾DC:y€óļ7ā2c9ä=p›‡xf»™ó‹,_qyYœŹ)Čcü[’ŸXÜ­ņNąÕ™o5™ė°l ,‹†0ĢŁ=‘H§t°"r(×攣¢Ó‹¼łq+„×Ė·Ģ!ĘÜ?pń·;‘.µ©œĻõčÖŁŠČį|]q7%rm@T­MŗHGµ]«äŃb²®Ń\ń3Šļ9ƒv>>B¹”ĻlĪρy“×0ī!Į7gY#&O'ˆCGČÅŚ˜ŁŻĢüĶfŅ֟ɤ¼h©óŃA)'7"£wė.$P‚Ū•¹Ūųy;—ītĢ/ʌŃxµd#6įżn*õ)Œ8ŒyZ‘Ed»}€‹¼ˆ”Ź5ˆ©xšT[惫:U⇖3¦q ‘zeš*·%ų‰±›=Įņ_!į mOƙ[.ĮŁŃ¶żįÉ“*ä\E¹9yØŹI©4Ķé@«}I_ŖØQ"®ĄPū‘yėł‰åō÷Ÿoxńć•¶B“å>įĮŗ]ŁĖC¦³‹;㢪¼ŖńO’{F®Xq6ŲĄƒ§±1Š$q35Õ+’(!’¼urÅĪ^\ųhÅyŽė'ÅæäŸ/ŠmhU:żU. c2Īó-Ų±B“ż•ÄœUO—$ŃÄĀˤAõرPPņqĢ9?ĆĮćKŒ|uŹs•ģėČžSß8%=™‹c–®]*uMĶ“¼{EÆŲźž¬ä:ź*©ęU ČA0]±P\īAÉH¤n<§Ö|2y¶ńļĮž*sß4݇(Ą‚>G seā{»w>œÕā …}Śڦ¢pń°•ʲĢ­s½ŸxgdYÄÜ·å!¼ĖłģćJ™ŹĆ/ŜQĀ‹1“„įVńĪWkJÄuH‰ś‚ Ś˜a;·vÕdÅŗ€s¹pOTQ4ĢB.²Ž+ųĶ5X3.]ķ<„Ķč81*g §•³ŲWŠW<¹¹[3=qÄŪ¹G,‹\­I<s!ņņ±,äk„"ß#“R—ČeéߐßĆ_Ć^yfl­šeyĘü’·¬ńcx•— å¹ä.."ćrQƒœzå)kć|k ŻŲĪ¢P”s)&¹Ŗ²ž‘@ü•ńиǘ~9 ’ŽēN8m€9TKœ˜Čøņ^ĄĀż˜˜;w“ąķ+ŽlÉ>{a¹ŹEP8÷2é6’‹¬ÅūĒ1­V2,X¦Tˆ®'ˆl«Wāož^ExĒā×0rĘXąµ N)`¬ņ!Õo(’ZZ»Ģ_R³°Ęö…! Ü')j™Zv…lGORxšˆ¹­•!^„å’%9Éį{ń^ņ'"ņöAäõ~OÖĬč8^£L®ēJ¶JgT¤ĖĒÆõF4ÉHųvɤUĪ،\˜­Õ]Č±Åų—ä—ž:9ų€'¹yšń’#xżjŒo€ŌŠ»NWYĶ` + IŹĻź¶RެpĖ“x‚L˜&W %$ī³*IŽ/$›ÄH²[åƒńdlóÅN'qWŒµ®Hb|ŃŹLiÆ<™ĖŲΌēź¦_±bɒ1—ž§/^Č„ķ3Æcä_¶,CDIhōśš<ŠF~īęž+g܋Ķ<Øl—ĮžfĢtꁅxėŹL¼ÖǜłK‘dԁ¶9ĪVśĆ輊M­īįė.ݶkĪQŅN&©®ÉHCŒĮ\ņē?ˆ^C¼Øä^\dn2pC bĪK-Ęīāy»ikłžR¬Sew•*éd•t›+Aa=$ż¼ƒ×Æé2hĮ6Ą±fæĆ1‘šd |(v‰[ ö” •1Ģ»6ŹØ°5wHĶŁ‰–:„ ƒ™Jś,Ÿ”¹H.ū@D}g“DM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDMi"².*ĶÕItŻŚŖ*TĶŚa!»NA1G“å½6Ń®ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‹AG ¦²»Ōsźz@ Øb¤P9ūŌ) š{zw {½øč‹_DM4DŃDM4DŃDM4DŃDM4DŃDM4E׿ÉĻ€Zo^I°äž8å~\į†@¹b•š''œįؕ\¹ä† mO°?msؖ!Ą”ՓÕ]”6ĀJ5‹FĪœ­S6ˆ¬×? Æų‹äs/d›ū|uŠ0Ņ“Öųq$ß/dŹÆqƒŒ9`Éә”„c‘‡ŗT„ddåą‘ŒI%l”] ć{bŹEæ>(iž'øĶ”øĆ —,9īŸ‘ó¾CĖi9¼V"a“‡Æ[ėÕ*\eĢCG²±Ó 6ŖŅŚżUīĶ›Ź>YuHÉ¢F*"Ä­Ÿš²¾-ĪķRĆ>Wł³Ēž\¬öKr)Ē2³qōʜ…¶Äīzb&³$Ė&ÅćŌė Ó~£VH;¤;|š`™œ¼vp9”"ϧütń›Ē†jhó3E”IŹ’ƒ·9[@}čę&u¹r¦k8—&ēnXdōķ\«NœŠŗŠŲÓŁ”ačīc % .żōäƒ'īÜŚšķȳÄß ø›‰¼žņČŖĘZ½Ļ±ēun ēŗŽdߥ±‚y:vöeŽK»¼NŹK>UÓG®Ś“H˜QU7ŠīLE[ų˜ńeSńcƛOąr;Œ¢ŚŪ•ņ¦I™Č«!O•”ūųÖ"³I”ę, M5D«E1]É\,ŌNDR •2‘@FŸ†·@ųĖŽ+ėYīąV¹ >ČņJ»f©PīēkłÕ£«=Oe8Ę:ĘĮ…&žŅų¶yw‰Eҧ¦R*k,žėWĀ|\o'äū–#ĪŽ5Kd‘ņ:½¢Ó˜lųė)¤ń»Ǥū/6¹Tź1Ńr‘Æ‡ŽŁa†NKqr‹å)­„~ž bžBpS9ć å–"Ōā½®"÷™™Ųڲ™±rRķNæ?Ź4{R¶Xך c9F6‡M㟃6oSw\Žh‚@Ž@Ž$ܑKŽ>ž|+ĒÆ<åńõŸr$Õk›[5¶G ¹©Ōb$qÓE† ”H(ŠÄa~*i­m©$Vļfœ”ŒØ·B0†M4H¦Ę=j·āJ;ÅTŽP˜x¹#ĒK&]­Ą3Ž”vņĖ õµ²ēW–6ՒR“R®œ&ĮgkŠh+ézżĄ +͇»äžp‡Ž˜ß“łƒ<ˆńūX“…ć÷4135+łƒūC%S¼«1k†Ÿ-rÓ,·Ļ|eš1ĖH  ųØØį5ČØ^$~Ģ;ˆóÖ;åg0¹“ĖO!<‰Å’Q–:E«4d+T%VŪé£ōģ(BĘćz(¼dMć¤-/¢œ!ŗO; čŠ+D~ŚżyÖDĆõæ'·©š#dv9 g…TEĀøLmČ«Łr1į‹*Y{rq<Ž‹aĀ>:* ²P©ˆ9œŖŗ¦8Š»‹#:"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‹M$“D€š)‘$˾ÄL Bā&ŠP ÄwĖ¢-M4DŃDM4DŃDM4DŃDM4DŃ`w³Łł4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃD_†( ‡`Üwč¢,\?Ÿ…*EŠ^­/ĶŹė¹X)'ŃFÆbÜåfŠ#ų×'hģ'`±›ųYFå]3jŗĶÕ(wę(€‰Ūń;ųApq'õŲb†Åw¹Ā|‡Lƒ°€vƒ›sžĻ°4E½’I«Āłq‰®Dš¤Ńšæ‰wĀŠS^ŖĄw ±±/ J`Ü7ŲÅO¹Lš÷h‹ėż%æå×V’ü€’įO¢'śK~’Ė®­ž)ł’ŸD\¢‰ĀR„"Ļj@å)ĄŖcĢ䙡 LųøCż@@D_é!xJ’/|ž/³’ ż?ŅBš•ž^ų’ü_f’žś"¤…į+ü½ń’ų¾Ķ’ü+ōEاā=ššŸow>1Ų÷Š§DĶJģS†ąō±‘ūķ)¶1}ą"Ž1üEžäDJߟ˜½1 ĆśuW-F@īči,xŠ£Š}ĆŌz{tEʤ1į{žŠ7’ˆdļ/D[„’†5KŽO xH ø†Ź’ģ÷oźÖؐū}ƒ¶Ć¢-üB¾Ąž˜łĆ=ŻŻ›ƒKč“}öÜ o¦%ßų[öķ×}“E˶óõįµŅb¢^B0 @$ŁĢ“ū3īPŻ×ŠPK× ķŚ>įé¢.Uæž.SõņƲ—øK³‹¢Ü6ßdG¢Ø—ÆAŪa÷h‹p_:žLb”<ˆqŒĘ€ż€ŽŻLfĄR‡^¢"żrēĆń ¾ßēā—ųցŪ÷żm“D’>ˆ_ūEx„ž5 ?ł¶ˆŸēĀń ’hÆæĘ¼’6Ńąžn|E(@9|‹ń(z€1U“?ŁŌŠ<)Ėū”¢/æóŪx‹’“cˆ’ć–„’—h‰ž{oöŒqürԃūo“D’=ƈæūF8ž9Ŗ_łvˆŸēµń’hĒ’Ē5K’.Ń#žzŪ’œo‡@aĻxōØoŌopĻ¢/ßóŠx–’“s†’ćóÓz"žƒÄ·ż£œ7’øóžœŃüō%æķįæųżĒŸō戟ē ń-’hē ’Ēī<’§4Eņ4^%S!Ō7‘¾‰HQ1»3Ö?Pū7ŌӚ1Īo°GŻ¢.?üö¾"’ķā?ųę©åŚ"ž×Ä_ż£G’Õ/üæD_æēµń?’ю#ŽzˆmžˆŸēµń?’ю#ŽzˆmžˆŸē“ń’hĒæĒ=G’/Ńüöž#?ķā7ųēØåś"Ż7óQāIÉNd¼pų‚7Æœč­‡q Ć“®e’1Ćņ€h‹|ßĢæ‰Ēj•<pŌŹ~Š? qĀEųJ&ŌZy4ĖŠ=ā芢må·Å»Āü‰p¬Å‰æ’Ų… ƒ`Ū±kbg÷ūvŲtET-äēĘóx³Ķ-ĻĪ’-0Üļ“˜\R‡æŲ[˜œG“wŲ}pϼ¬ųĒŽo*÷ČO ‘`ģ7lćśĢaåPéś)„oQ_¼ŗ"Ń7–äģļņĀ’ś„*„ß“Xp;ˆpÜ£·ß śžžˆ·-<ŖųČ~ $ÓČW ×PĒ*`Rņs ‰Ļæi~+‰z›mUä‹Ēˆµ”Ļ|ØÓ’¬Žōūū€½»żõö÷ˆøf¾Pük>Tč“ņĀåÕL½Ē!99†7){€»Ž÷0 »‡mTiłńö²dU.tščéØR3—“XTJb7)ƒ’=½‚¢/æóˆx’’.n’ńĶa_ļŪDOóˆx’’.n’ńĶa_ļŪDOóˆx’’.n’ńĶa_ļŪDAņ#ćųDyĻưė5…zā?ū›{ƒD\GłĖüs{Ææ æųē0·ö¾śļ¢'łĖüs—æ æųēp·÷ė¢'łĖ¼s—æ æųēp·÷ė¢.5ļ”o‘Ę1łįks0XĄ~NįĮ1ßćųnféŠtEL/åßŃeLŠŽExZU ·p’X™@ź`ŲéŚNAč>įŃļĢgŠ6%!Üłį‘J”„„łŒ\n%ÜŲ•ō~Ś"ŠmęWÄėµJ‚Fxje ņ ¢]ŠŽź->šeéö]j;ó!⁉ČG>FxfS(A9=>BcEĄJˆ‰›ŲU)Gpö€č‹‹qę£Ä“Q(+äk‡ĆŽ%ōsČ|"=ĀŚYP õöŪ苎?›ß8|‹ń4D¾Ń&]­(O·”ČčÄ7·Ü#¢*½ēßĵǑųӋŽ`RoYG.ICĄQŻR”ķ¶Œ{#k±Čż*¹Ps’¢ ]RćlsÄ© Šļ˜(¢dQDĪŖe1bōDŃDM4DŃDM4DŃDM4DŃDM4DŃ‘Īį,y|QšāÕŚtĖAڹŲ[ø$ć øåŲQTŽŠöh‹ĘĘńW/,9™¾\}r*4hУ蠩ͱˆUĖė$¤ŚOœČ9{.gM8¢‚e)H*Ā#°ŖhjŌ8»]½ŠįöB2éąsph ʲŗėĆ—LÅo9š™@Ą ’å[p%.ÅÅé"Qģ½}ŗ« ZPł­ ¬·?ŖļųמdŽ/’„<ē’=Ń’¼m{*’÷ XžU·c’ć_DšÅҜ†>@ĪJ¦(™1¤”P ˜“`1@ĮÓpźd ’øZĒņ­»’ēŃšÅÄDū½Y¬Ōć»nŽėq.Ķ·ÜÓ¤ü[ž]y*Ģ ks-žĖæć_JųcāČ%N_5"qŪeė]PCaꧤöŽį¦@ƒę·½–’eßń­$|0q3÷)=š×.ĆšćZL7č=ÉŅ@Ż4ȧ浺;²ļųŌ]…ńćĄč¼ÓȚžDŹY˜ÕL\–1§c¬Uޤź¶|÷’ņķŹåŗĒ U®%Xrņb· “UҌڲjįŚ¦võŪš+Pžś9£·°Œ9ī©%ÕŹŠ9H§§™J\ńG¾ĀĪM:ŽßǟÄ|’ČŲ"‰Ž i{³ 9ʤ —V’pŻÖ<6Óä[ļYŪÅÉ“sŁjĘ:w5L½eh˜øųæ›p÷!]a`ć(-_›Ŗ©˜E“|Dˆ ‘žÄ1Ķ!%ƒ9i’˜åŖ’URńģĢlvÖģŠāššȉ&”Ž%än«ˆ®ŚSĖ=š;ĒŹ„¶’-EĪTŖķ­ķjÜ0-i32±M„ŽU™ŗkœ•Y×cAnV|‰/ėŠRNNÜļ”m›AŠīŹį’:<±łDeĪhq` Ӎ$Šķ Äv#¦“sIń²ƒC²“Ųµ1w‹dąw ß$ņ ±’+ئKÖ'ø>”@äjkš.Ė–Nø„9S¾†Q@i*ĄīāŸ"%QéĒīģ.¬.]g}āŗa£˜įB= ī#“É5ž&āåŠ_Z@ÖWÕ}Xḃ›o1”WsüÉ|b’ Ļžx£xśĘČ7žįźßČ·żæų“üÉ|b’ Ļžx£xśd ’øz·ņ-’ož$’2_æĀFs’ž(ߎ>™ī­ü‹Ū’‰p”? Üy“gk=‘ʹ‘”n…æ1+5i„°ŒģŽ^®é»™U«kG«›Hā*šedE½S›u 4–Š©ė0½ŗÓĶÓāˆJ&,Ć5)”:»k\i¶ŠI’£ėÄš³ČłćxQ^žź½ž\_µéOō}xžy’×'ś<üm’yæži ’ŃzQ?ŗī’•k—ļś<üm’yæži ’ŃzQ?ŗī’•i_Ÿčóń·ü=ę’ł¦ƒ’Eé@½žė»žT}„?Ńēćoų{Ķ’óMž‹Ņ?ŗīæ•i\t׀*Ö¢œĪŁ9'–kL“2Æ&ģĘpķ!{ŽwR’ŒŚ1@…(n"uJė8¦ņGdd,sĪį˜žĄ”|æĪe]V0ļ ¹SŹ “S*×øį*y =¢ęÜYŒ‚¬t61„GpÜŹø™)J^»{7±=ŵ³sNö“sšwm[^‰cĘ|C(‹Kӝ!®&Ž é„ržµ AšU™īRŅR–ūĶ ŅÜH8V»:£L™“ŪĀĮ…Š6d©«DŃP™}¾æĖIŖ™UÜ ]ƒq‚øāKHĶ k¤<¾Čļǹw äÆŻÄŁ5‰­ģÉ­•õ߃Hhūuå ķēo x›āµīUÜĻ“'¬ll˜Ź V«ur°’-Ļ"Ōčņ†hŠåxƒÄ›ŲŽį¢f]Põ)&» vkĖĘ[>6†<œA5ęY|eņ«Mįžŗ×mļ&’āŚ0ģ®kC]ė†£mFÕ)Vü<œlIeS.}ĶęÕ9ßH ü@C @Ż#=ąŚ(ŹćŠī’•iZ_čóń·ü=ę’ł¦ƒ’Eé@ŸŻwŹµŽ”’GŸæįļ7’Ķ4ś/Jžė»žT}®ō§ś<üm’yæži ’ŃzP'÷]ßņ£ķw„?Ńēćoų{Ķ’óMž‹Ņ?ŗī’•k½+šß‡Ÿā|Ķ ?i”čFŽŅöt AÅw{ā“®-ļįāĮ*Ń܌ĖMGø‚ö”M~PõdFˆ˜Ć¶ĆæĀŠ(ŖWq¾vŸBā ųvńG^ŽOä öö÷cJɶū7ŚÖ^ķæsJ*æ»&žC~Ńō-ōv±włPßŅsūļŅ‹ßīÉ?ß“} ÷ż¬]žT7ßńa\žūõåū²Oä7ķB£µ‹æŹ†ūž,+Ÿß~”OīÉ?ß“} žŽÖ.’*÷ų°®}śQ?»$žC~Ńō'ś;X»üØoßā¹ż÷éDžģ“ł ūGПčķbļņ”æ‹ ē÷ß„ū²Oä7ķB£µ‹æŹ†żž,+Ÿß~”OīÉ?ß“} žŽÖ.’*÷ų°®}śö‰żŁ'ņö”?ŃŚÅßåC~’ĻļæJ'÷dŸČoŚ>„’Gk• ūüXW?¾ż(ŸŻ’!æhśż¬]žT7ļńa\žūō¢vIü†ż£čOōv±włP߿Ņsūļ҉żŁ'ņö”?ŃŚÅßåC~’ĻļæJ'÷dŸČoŚ>„’Gk• ūüXW?¾ż(ŸŻ’!æhśż¬]žT7ļńa\žūō¢vIü†ż£čOōv±włP߿Ņsūļ҉żŁ'ņö”?ŃŚÅßåC~’ĻļæJ'÷dŸČoŚ>„’Gk• ūüXW?¾ż(ŸŻ’!æhś7ųxpZD8Lņ7,?PD¾™£)ŌŲ’?„#§sFPGÜ bķłt¢„ÜWqōa`éq>`¶®æö:Š .Jäę釤GT*›Å]¾ QD¦˜•Aö›}ƒ„qdūįgŚ>…±?įŪÅ];9?@=żųĪ“mĒņml.ŚQT8²]š7ķBŁ-ųvqš˜’-Ź{‘ =€¾*ƒPĄnŽ‚qNī;žĄše^’v?łķ’ō؍Œų&ϊi|nńā&湞ĒČ.%^[Ż×­ØŃģ–rnšĢZE!/2.’5²v˘ĀeGpŲ^E±iWēR¶7™=rŚV»)¾ƒ•{kÅ$š"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"£²!{ńżäæĘ§Y‹ūšÆCż]yų/-³’éļ×īŽ80Ś›µ”D? Ć÷õq›×6łż=§łżŠ»jµĖDM4DŃDV–‡ƒ1f5ød…RŖ6i|ŹS§ļ'Ėø–±Ė8X«©„›õZ*ŗŹ_J=Æ¢Ų;@LSĄŸu©ŽŽA¬ļ&چ±ƒŠ TµÜ꧒‹_3˜īØźĶĪ$‘åkŲŗ8 "2—ł–•é5 °æ W>vm½‰·0ūRz-ƒµMb×NoüéŲÓõK†nĘŌ©>°:Z[R¬‡»ź³Ö>@:Ō†˜•¬CÉMĢIE×+ {#-.õ¬T4$;ž'/ß¼QlY5@ s˜„³_dM,Ń:iœŲķŲŚ—8€Ö“o$ą_S’©Ų±ł–k$ęÊĆXJ”Ž8ʵÉV3ü›~ĶJ¶s’E›¤Ż—٬ٽ¦›W1ĻLŠ »£œ@„L„ę0ģ¾¼sšŃ™Äē³aÓ5éįA!˜-Ϙ.o§¼|āļ+sƅ‹»i¶ų™žĒÜé*ļ"ēeØÉ48ģ>¢ “wµżC‹8kLŽ^ŪµĆčµĮīū,Ģ{–ĮeĮżįĆg)ü©Ž¹÷Ų“žy"bw!eü?ĀŗL|<”»Ø|INCe¶qńŃī»^w"ß[Vq¼)š ‰ŽØEC¼)ēq‡=Ō~oY E¾‡k$ļsƒC¤>I&‚œkĻ—”nśwĖ[h›ājr—+A³ŗžR£—øE‰oøÆę.L”jåaøR+÷‰iü’e°]ąbŚŲ#<Ł„kĖČ+E®2eõʐ±YR‰DEAßmv aGSb¹µö§4W[X–ĆjזŒ€4 *\1©éW>WÉq²Jv’ÅŹÆĢc¹ł3ŲrĻ ٌ\“Įn‹wł3PĶ£źł6mSłŹą(kH”ˆ7Ąš‹DjŗLzƒ3³ÕŗhĄņónC¹u?•Ÿ8/ų.čiڱtü=3Ęqōćq ńwšR ū@cˆ^zEā©‘źńw:LĖyźÜĮWłGȦ»etĶs“’‰•Žx“yYčgé³ęŅEŪ')%“!Ź!­he‚C Ķ-‘»Aü» ūÆMŌ¬u{õ-2VMc+jדŌę#ap*ÖåמČÜeÅDT×,Ź•öiøl`†€ÉnÕ\»ļņź]ŪW›Žį°™p NpÜ%ń˜ģ‡“ą;ŖøĻūƒÖæ ą'X0ŅkŁŚĻŌg¬īüŖjī;“zŽ·„šZh‰¢&ˆš"h‰¢&ˆš"h‰¢/£&r9LD‡Ų”Ʊ1üĒ6ÅßŃ6ģ_¢’€@PS?¦oŃS“}3{žķŚnŸ`č‹ćDM4DŃDM4DŃDM4DŃDM4DŃDX Ķ#ŪųŽ<]ķɼ9’ėķ8]RWAįōÓžk¼^¢:ńlI¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢*Jż°Q.¢mūB„cīŪØķōw›ģnŚ"ņ#šh 7nJķ¾ßuØūvūÅcŪ~£×®®3zē1„µ’1’ŗb‚¤©Š'*jķ1Ha({ŗˆĮŌ5Zå;6­=©¢&ˆš"ś) }ūJcv€˜Ż #ŚPź&½…ö°4EMBÜiöWÓQ•»m^Å%[]»k|†"iģ‡ePķPškńŅńk9"'rĒģ6ĮšŽÅz[k˜×ĻŲĒū%Ķ :›iQб9¢ń-KĖ\|vLe’ņd u²¤Ā1ŲŚøŒāØä4kuŹ8Xßæ‘‰‚«C.Õ6p‘~į&Č,B˜Gąm\¬iŚŗŻ[RlŠ(¤Č&G ­Ś@cŗ«łw{„iww:–„#c1Äڜ}bI ŚNaSŚŖ¤(|•/pĻfˆ|X—ØJÓp¬#„%q­ūsŒ¦¬®Ü·iūUČLĻńū¶éEF+’±ĢŹ |Ś—ø«5^)—$ßĮÓjŲZj9!Ć;ŗ}Qō@Śiā®>¼Ö«g§fƒMŲNĒæ¤e§bwŹłrŖęēDL&0€n"=DDu§®vp CŽ?(¶-NQlŽ ęIņī^ŃHåėĒq‡ųŻŽŽą>šŪ\Ū\vmVnbcBżRł/lm>Vč±;Ś6™ė=ÅGž~ĪÆ{Ć+Œ4˜č›6CČ8ÖjĶ:„Ą¹=Š+ž¤Ą^,j4nčķ§ęåa>ŸVi±W{%Žøv¶fąį“ XĖ=Š»l1oå4öGB|ģćĶ…ųwšK²Łu=EĢ‘ąKZ^?ŠFźźõƒdŌøż‡ŁÖŖOŖōg„¬@™’ƒj³Š-k¢¬SU’w--Q–ˆ<“š  e]cA³_KŽŃn-£†ßY¤µĒ‰#iå$/‚gÖu1<Œ™ķ‘ž#Ŗ×µÆfӀƒ˜®ßćsC~ZEę½ŗw±ą“ML‡Ķ”vyņ>šĀķ-#/T;„Zy ܦóFzIN±ŠU”)==h|G„A¤Ż²g<ĘųózÄ=b)PoR–“Zͤ¾i,ā¶[Ā> h£©IŁMœĖ"T|Ÿä}|ǐ°{ģMĜ§5é4¼ˆŽÅ’2 Õ>ė+`‚BB¹?’&ön¶»IDÖ2HµYD;TP«“\«MįvĆ&Ø&š§sƒK˜Š ÄR į¶“<Š{GįK~"Óé²>2ĆG5夃ŌGZ»‹ēZVL¢WĻyŁoCRC eL˜cW)IFh¶—8pź¤Q¢jÆMĆQöß18:ä ^±„g•“ļW§łu­Ē÷eÆ‚U!lēŻC©SC0qæ™ų•Õź]Zķ=µÆÆ$T²XPdyą`ĖN²Ł””—N=3/č"CØ)Ę)¶ŸÓµķV{£Ó.”žFАĒ@8TŽJą¢.ųCZ±g‰rÖ2>RźŅ(¶v%üT¦0$¦Au›ń¤aŌMØäN4gj¬qP@š’O(§!Īc@=]Ģan„Ŗkt[é "šžFF:Öaä߄I¢Ŗźiø)EGX›5·MDŌ/zj•Cc”LÉ(MŒS‰L°†©ń#٘W¤*¤_“е˜Ōž%¾?’ž•3Ø\ąEĄ›÷®4ĢnTč"²icĆ{D6³^x‘ūĆ“/?æŽÖ×ž%AOłYā\]=Ę@&tČ4–ņˆ5Ī•€ņ)©C.£Ō£R‹­®2«VIź²+‘!Ż€‚ĒÄ;j±ė‰'‘ƒ~łNš›)Ü^+Ų*UJė‘\ŗ¼nž1āµcG+š„gäöTbœ¢d0Ī?eøi |§y@wō]M³0ˆl"_nŗ.ņ»‹/ØéćŠÖ#¾WŒßa™mĖkĄw/”»”4r4yĻ”pŹćžTŽiœĒ³WRq°)ZćV3„bˆä€żŗVūŠ9Büą½vH颃ķŲ[ΟņnĮ”:äŅ?’&µƒµŁÜ{Ł-x/Gƒdw9ólīV“Ž(ĀŁn1e¼ ō“dgl<Ė?äė¦N+ n€WrĶrm”ä[Ś2Z2MšvĖ “H²™¬:ć¼Mm„Łė·z6Ćį~@\ģĹ¢5 Ąŗ“ę Nā;ÖX_&‘Eõ‹@øī­7*™VĪį‡UgxŽ‘AĄ™;͵øįŒŸ±õZ S\ć€ß(įÓˆųŌ­)d„Ķ&bœČČ0UB’X]=bžĘ×S²—O½n{Y˜Zį² ņÄmqPŚv»Øé×­½d{šjC‰ öևK¬Č52é&iWų–ō.Acꌍ“±§Ō|ŠĢŻØv±¹'É:/mÄ7Ń1㤠_YŠżńņEņ!¾PāνįKļ@_§ČO…-0p÷]øHŃ“oö†>‡Ń5ĖMvĢ][8g§¬Żķ*‘꣏8Hų¾ĮŠ“r:Ż\ćģ-¢ļ‡t¶LšO·ā)+˜†}éģ!Ņ'P eü½ŅNÆÅVķp­¼Ę%#”hėyhVø›Pf‰=Õ}|…­év —A³FMŪ²n›8öMŠdĮ¢%"ыDˆŻ›THT›6L¤(@)C_Wƙ*N'iZŗ"‹ŁY[Ś%ņ·l0l>t\_*6ĘOŻāȳDHŻ»ū‹Sb„E¢EATQTv¢%"OŚČ$šEJ>’L¶ŌIE$6ŽnqĢWLłóKˆ8c©é5|®^–ļk¹Å9ź0VĆŌs僶ɜń4^!†ĒX€q&/…ČŠY)+<ŻŽŅĀՓ/L%!YĘ+-*±­SzͣӐŖ Ó ĢRŚŅ“Į¦Ęö—gsŻZҘ ƒ:˜ł³ó2˜O³6‘: xjĀsÄÕĚ4c€‰ÅO J®6š"h‰¢&ˆš"h‰¢&ˆØŸ“čxf‹=’r]…µb[A$d×IĆ„Örķr3Œ‡‡Œdšņ3– ·ė&Ł‹©ŖéŪ• šd…z%¹”AĶ+¶9äyVF'sה-–Ž—†ą•M''±U㲯-¬± ˆ'ņUwPĘŲL_“0­-6ŌL©R8 N 2it§#Nķ§ó~X-’ßJ±¶Ęā³ĶČ0`󻧐žųĒ%kŒŗgŒ±ŹnSL2YƗ±™³2:qO™]rW•źœ]ućv Õ7؋Vņ #¹@Sg3K¶i«³;¤ś£nLmÉ#æ¢Ś*Źßć;†– Ͷ‰ĪämłŒMĮ!Y’½é&rŪØÜa°']]E p«e”€‘\ä)Ę7Ė{60ˆU}œ/ØøUΉ§“ęw®y§,ł(qJ#Åg“'k{ĢGxEA>Ÿ(­¤åuöiUpp­öł"ķwü*§K6sYŹe]ƈ"Š7P;“QLY ‚† ū dO` éUWö­ßó#żÆBŪŽ{ę1DHožFĮBˆŹ†(JcżįŲĄļŅ«Ļķ[ĻęEū^… ļüø‹ngŅŽ%|“L@pß ĘæX¦7B€4FĘEøżƒÓJ§ö­ęé"żÆBą—łäƱ|Ył8‚Ęņ_xö¾ŗUyż«}üČ»]’ ćs{,Fz’Tńä韬$wöć'¬7L¢uÜ-ä;tŅ«Ļķ[’~×Ā“ņM‡YnI¬CĖŖźä/{†³|[Ė œ5L?IGFÉ Bģ*ƒĆz ś,?¬>ėĖbG¶Ń’®”掹Tģų;709„?H ćē{‰}’›J…lšö¬6Fė7ĪU|ĻÉæ5ˆrƒ‘#ĄGŽe£~릯J”S *9ѵ@i࿻үF7埲ņāĻgĢMr‘CDCŽ`›"jw‰NxE^£,B¦n¢ˆm¶½XóXŽ[ŠĶŚ9H4ķŲ¤LS”§!€Å0Šbˆ¦)ƒp1D7č:,Eõ¢,g‡ńx¶7Ū“xw’×ņp5IŚŗ ’¦Ÿó]äjõ׋cM4DŃDM4DŃDM4DŃDM4DŃDMQŁTŪćūŅė‚HÓ¬źŖ%(˜A4įā¦ŅO~ˆ¼‰¼ėÉ3—© S ōb°˜»‡åÕlڹĒĢoémĶī­n}9°WyKp°Vī¹ §2ł(&껩ß-U²ü„m2ŸōĘé6ˆ“hÕ4Š;ēŲ sŖaÄG^:”Źc„­mnxnÜOo“Š~›¹AęV6½Ė^[ŌL‘ y&ˆ€•å½s!0P ">šßy¢I1ßaķrSmü-śėĢÅHOĀÜ?q\ö±‚w¶¬żŅš­łOä”3R6“cŒ3U0( «'6Ś õĄa3†H-e‰MŠDR*Eū곕ÆÜ|½Ó$vky¦Œr®U÷Ŗ«üķy{“vć–:Ū”ģ"ł€*?Ū3¬ż¹¶’ö¤ū ōŖjkŹĻ#ä2UÜC…*Ŗ›pÓwkiŃßŲr2h­uL_°ęޙʽĖĶ9®¬ÓĪęņ ­ļ”Pē'ņ¹øė§•s¦Z sœĘ£UZ 7@lā¶fĖL¢@÷æ]ɍü!RI+hÓųGÓ(m `}'zĪūN©T ^x½21ü‡‘Œ‚‰IbĖ¢¶ebŪ7ŒśtÅ+-Ż1b‚ Œķ;×&Ec‡©²ŠqÜuS6ؘ3¢1ϧˆ.——c«N„ŸŻÄ@v· śŽĶĆß¶®.6æ4EŖ€ģŗ#ŌvU1Ų:ˆģp€=ćÆW‡bX»’õ$0n*ØacAå<Ē;P3†ŌˆÉbø‰”?s(üÖ &j•`e– V«Ö •v®“NŒŖØ˜éhnŅnµ VbąYˆjā7nĖŹHź×é#¾hpÆĖŸ–\’MśˆÓ¢lP1ĮĪ2 ē”õ@ukZ» EĄ­ćŅU©ö¶oåVµŻ.ͦeņ%åódšÉŻ­’Pź°^Ef銉ĘĆǵģe™…¼Tb)7O~Óū¬0Em‚HŚ(?.UšńN­ÅÜE'ė2.å”;™­Q€P,wÓŌHiµ%}B&jUÓ Ŗ˜©¦š)Į²*©Ģ TÓ! ÜaŲ Ž»„“Ƭ”Ń0°žČY"öVŒOŠ’Ž*Gx׆œ‘ä?"²TūTSicĀX$ųŌ ÜÄ}ģ6ĢØ»0tu€EĶĶÅQ9³¦P(g-Šaväšę”ų–„$ķū‘ź³źżf§­OĪ!‡JŠÖ#W²yœ†@ZråĶ’¼ ¬Žäg³¬ŻĘœąˆ L[^qļ"®Qķ!±÷ „EyŪćn-«¹Ŗ°± n‰Śāw›\Ēę&•ųÆ \ŠĻnĢåõ=”ÖĀą¶—ŗĮėbŁĘ‘Nܽcgå̲1-]‰–Ÿ°Ź0‚€šžš–r“øXx–«>••“x¹Š“FĢ›ØŖŹ@™GŁÆ•#ŽI¤lP“¾Wø‰$šņNÅßCAs6žE\fśc‘99kpa!VJ&R³ÄŹ<ĆeY¾©ā¹±D'3$ģjąUXd\īFج™(/UM›O…W/ßVš? GĀśPd Vz:grуēuNšøń!Õļ> Ų’ŲÄiõł¼ønWīļR­äšuŸß"[ŁéW8Wõė=zHN£)X‰$ ŻÓu~ä–)MŽŠÄŁT)T ”å(†ź“h¤|6hŽYj:µ\+š¶Uźn5],’–[/ģ±XžÓ(¹ĘZå…, ¬ø>Ķ,`‚ó jgVśÅ՟Ą¬i‡\óˆ,›k}‚‘Ź3u×Öøõ®aĒŗdVŗ³u+f†Śß0Ė”ll”Łehę.£Ē3ÕIȼ±[>Ą˜6Q8ģŪ“bʹ¹ŗKźX+µtX«`™1)õ*1µ8Å )8"¢ŸŃ;§EŅ’Ÿ<æŅ°ćĪw7ÓĶŅ­p‡Å«NķCQš‹w †Y6¶ y)ėHFĘįµĮlé˜Ńš‹:ĒÕŪKā6µWÕ *͐~°¦6hĪW²¼“ذÕ¦m2oœ=|ä@§QāęT‚ķč“Pl]fK©ä¹ų²i=AaJ`ä`"Š˜Ń9Ü+o?ļr²M£b^NńŽū4¹ÜæČ˜’$Č¢āŸ;"Øīū'a²øE”‡pŠŅpĒg$&3ÆOčߖ|_ųµ˜Šµ×S·gšÉ8Éņ¾=‡ymā» ėŒÕ­2Hiy£‡/8ę?›r­s5–z·e‹Oż’¶»ˆĒx܆ŲwæßäP¬Vž ?†„ ‡ę”qī+F ˜zG[Æė#AįūHNŲņĒĻ#żVv› 5Ø]²ĘŹK·ąÖ0ž½ŹRcŚ$/”Óq½\†%z‰Z‡«D™N«¹mĶ&¦vaÜT*¹éÉĒs(įc˜w|{‰Äš““ņžUóüÓ>āgO&2=еXč­+M”ń$nJ$Ó ł¼w”hŽIbüæNłTīT)G‰•7Ø$G©-f§O‘2%3^’"ŃRķĄ ŖeTˆ¬–”§Yj¶°Ōcl¶’ Z{ˆ;C†ŠįB7)=+W½Ń®…Õ“ˆvń¹Ć«!PuŹ,§Čź¼§$±å>·_ćž-“°¦_1ōźĻčy«(eYXŲi\‡X’(Ļc÷Pāø«%”ŽŖšģ\L¹œ¬‚‰ŚĒ š]Ÿ Ļu-“Ž”\†ę3ڜ„kÖ5. Ø¢«lā®-f½¦Įc#óUķēś6Snõ25ŗ-4DŃDM4DŃDM4DŃDMFĢ_QeɎmĪKXZ–cšI„u8(—IśŠsܻȚć>6mŽm&ėb·m>HSX»*‰D%4Č’œ*ÖléüĮmZ\?cć’̞Į‡ķą²Ō"""""""""#øˆQź"#©õ”æ4E‡ļ#jČę<ÕÄ?1›‡Ę”NEāĢćČé)ȱN ¦Āå‘+°œ®Ż“HˆŖ^:°X%T\Ž›d‚0ē*å9q®`–hŪ( ¶dĶ$ļ!ĪrŃ­$ņ r¬›i£‰īa©ńø“s֓Ö\å5Wó”Īfy–ŗŁåxĮ”&xo榔6ž²ó–m*)HēNŚb$ ”ūN:FŁ Ż•KE¼lf§²¬ŸÆė@€²æ2Į¬Mަ÷8²Üќ»ĻG ļRVŗ{ÜóŠæ“péåYćįčń…ĒE笶Ģ,§/ņ½®dÓ¶lŃĶWQ|…ȲM2÷Ya©²9LqY²½XN>ŗźH‰$øęq%ÅI€(Ń@²å0ĪÄQĈÄų£ć”ŹR'(µzTqP)HŹ·Ų„(Ą]ƒ^/UŹŃDM4DŃDM4DŃ+_Lœ„ˆ™H梜¬k)Ć ‡B;Eb‡AŃ›”āÆē$-5Ęü 1* ly9L?d$Ōļ]×Vr`ŹmE ńįĻÅæ%Zƒ|»ĮN9K8*Ŗ,•\}Œ-FQP8«oÅĮM“8!¶(ģ鈀”v b«-~Śn;;[7Œ~Vå¾Ī“:…{‰²<”ē$xßb`),šLʙ{žMVA\Ž“öņoAlR6ŲąX7Zm•č¤ń“»”`īŃŠÅžY•ęO<»FĆžJ)É­-I}ŪĆ\ĶĮެ‚­73 ʳ dh»J)Ģc œ‹$…DŒģɲpb˜QīnšīØ«j|9ąFg±.sF%§NPFŚrméXÄĶhśßˆĒŁ=3)Ż’8ˆa)ĀaóĶ„żß ¶ qp^šš©N’M?ę»ČÕź Æƚ"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"¤2E$(7† m꽨YZ'ø‡Øęź$Üqī8{4EäMąŃ¶»rY¹ĆāBÆ@Dć°ķŽ•†Ę˜‡_`ˆ”z~M\fõĪ>b’Kkžkæt.gÉH$NH®šE)DÕ Ū„ö „ėÆŃ9¾Óz-.’aCTæŚS<IįŲėŗYķ~uķR¶åų"1„DDvź""=4EUԌ— śŅ53ŪRg&Éćŗs¤Ž$[Rvø×?,Px‹7Ș;Ō&Ā øch‹‡™)‰/(CÅ ‹ ģ ?4#>¹÷Żč™ąƒ?ęĄU8zˆŽˆøŻd§ÅaĄ3žPLM·vlr“pų»r ALm‡ØöīĻfś­›W?ł‰ž™o’óąrĻ®.Fš"h‹‹Žƒƒ†VMxxHhuęž}Jix˜˜ųŦd{=?ØKŖÅŗ IæōžYqQMŗwh«|²Č‘Īph ©&ƒW`ę —LeU?›1ŹU?"ö¢:**F#jÄ>)Ø~×&Ł`„—"Ø*>ˆĻ®Z®"¬ RvB¶Ž<]ĀBS“±dĒ0ĘD=Ŗ„Wn@NÜĘÜÆuę  gVåńÉM­k}R:]O³S¼-ņśA`ēźī“¤:ļ9ķń)īĒZó¾ƒqYjćŪ4Zņ 'A$PLų»ķŠnŻ2¢‹V¬¤³«t"‘6M4!@R€ Š5„;jĒӜ]¢ÄI$üDÕ®ņDx©1”(łSŻqěµ£[\«²©K¶čīQRšVF&ˆ’©ŌH70l%]±=š”ģdŒ1ȍĄ‚ĀuŒe­Ä–—1ŻE„‘¼8u(}–2Æ;™W0¾Hʶģe1 ęL¹–ęÄĄ"bóŽe¤¼",ń7:.”VoKĢĒŃ<ųĄ‘d,Ę/³ÓYɍĖųOåĢ:µ>©tęČĘ<‹fķŹÓōŻQķ€r V˜ŗµ"_Š8Փi1ŪX\NĄ\Fįæósō)łł€„”…)R” B€R…(l ƒ¦ŗ’ä)¢(Ét»S°/#Ŗy^õb‰¦cü†Æ˜īõdšx„|KÜ6.³ ćĒ 4Ōzz‹»“tW8&Š`c‰J:÷Ś>ęя…„Ҳ@(6ŃŲyh”8£KøÕō6ĒfĒI{Ė ZIlßĆpčšÉ;©8-Ļ˜JXįķ<…·F>‹¼ņFU…ģŃ2ə)Jf*dxģ%ŽVEO‰‘ėŌu żŲ>¹0üāĘßRŗ}£lml6‰åqŚ{{”Ģ6QiVpéYlŚ8”ć+łźģčµŖIk1T­>eÄ0¢ rVBN·9 ,Ī×@æW¾\–¬qzŠ"ć·WtE*² øU»ĘK”ģå#œ.ĶÉ ŠĘ dZ]ÜŲ]G{f÷Guƒšį“łPƒnŸq¦Ż6īŲŃķŚ7¼1¤W ®<—”Öón2 ä*¶ūŪ¼™]1×ÅY£$Z[Š'(%\Ź?ƒRŸ+>žBłŒö"AĮY åmߋxī㊓ĖK)"š¤‰ÅņŠśÆ}2““mćC°š@[—ń$¦ŽČ ĀW¼gnńLvrV˜ģ+#zŠ€š"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢'ŖŠ.ˆ³r™Ć“@+t *®ap"č”'“ŖŔwĢpŅ—”¦?x9zĖ܎²ŗP\;{”rMŌ,”·:…¤FijK~„E¹JŁtöZ7•Õ=§Š·©Ś#p¾ĢlkGPużg++e'' '90ä¬įį#_Ģ˼7蓊‰h“„“£oüĢ›œćł Æ Ē`^€I ŚW]³ĖH Ń?cŗ6#”Óń~2ŖĀŃńķ¹R„Ō+Œ’Žƒ®Vą™„Ķ"՛D @ö˜ŪwDĀ":rŚņLÆ)*<Ź»¼²©ń†»‚°>wŹ9Ó||­f9«"ōz3‹mü-šįs­Š˜WĖ^zu_°śNˆ%ĀÜEb,}ĢsƑFĒŽ5y?Ė^Ybėē¹jī"7++]āģŒ¢ZėŒן1Ó:5äŅ— –J£ÄĆEBGV)8ńźdf°…Ŗ¤YšņqĘ(ž3SyA ®F·Ąä\’’ Q1LE üz°ęų)ė=r߇—ĘÅżJõ^ŃēW²§>¼K*ģd„Œ‹–Ģ,ä¤U­›ōü›Įł^tÓ±.iKŽĘ™3'³ÄßG£<̶X¼\µ”8ŖƒķžĮŽ-Ņ6©®­uĢe•Ü<óg \3z¢RTH­~Xņ¹Ä±d =ĘžēÓ(INÄøS%Ā– Ż‚m‚Pj‘‚j¬éē¬u•"ør>]šN)‹ĘÕ܁^Ķ™v+†“Naņ%bŒofĒüdĆö Bö2d\ģ ­m,#iś›˜č*äm’yfĶN±|°cZŁ/(Ó ÓĖZ¤vKćx÷—ø7L¦s{ ĒMWr¶q¾c„ęń»*šÅ%}œc›ŻX@V,‘·QpŒ¤Ķæ|Ż$šH¤Óæ*Üze•giŹSsrųf¹™«¼l–ęDmŒ·šr.Éi‰”·Ä*Ž#,nķÅ{ēŲÖ_Ų>÷>>̹¢ÜK¤ń $Ej8ęĻ‹œĖÉ‚1ß  *¹ź“\S›.ōŗ“9hĢ-°Mo‘—®9’Ó^æY]µĢ˜×Y·“nvŸIZR"]“'ī•S¼‹[˜ü¶äŝŲ3Žx’‘{ŒÅįÄbņ(ßFą™jŻĮlLLIV£Ā‹›ŽPČušÅ&ՐÉõõc+Ī&ÉNA‚N£ŸqEń åZŒčų² ćĪEfū ŒŲ‹-sO3ąüR×!ćī2¹øbŲk™_ę$«Lll–††s0h L%ŽZ2ÉH;bĪ=Ė5–"ß'Ü@²cjĪP²d1DӖŅ<$ØĒ䤣YMŲ³č[¤ėTč8¶õY{k ²Œ#6֚쐹+gu VR«‹dĀ+ćÅ>OT¹ql9R‹[µWjš¹Ÿ:į–*ZъIk+Ü –­ŲrÅlƒ4<œ³Gy»=)é˜(eJ°¢]•M5Ä*'Č? (\’įÖxā~Ao rµR6±<ż™‘‘$2xööÄ%IŻNÜŃ£±ō»N²QÅNEęcÅ{åŪ'yĶńEYĖ­Ü2Ķų#)ān?g6o\ŠļSĖ 2ä*õ…Ū×D9Īåōāq ¤V0•2g†Ų ˜”GҰ4ūAdŁbn™œęō8N£QŌ½fµāĻM4DŃDM4DŃDM4DŃDM4DŃDMpVÖ¬FĮKﰈ0p ·]‡D^DŒ#yK¾Ą&„£@7Ųļ-§}·ė°wj¶.qóś[_óū”m|Č$÷”–Véøbźō– ń¶Ā@Äüž‹ōĒżv¼“¦x¹xv3Ė$‡öæ2‚ŗ„mŹCq&N“ųNÅV˜Œi“ėBXŪĖBHR]L¬äĶŖĮr`§ź^ÕQµ¬ÉI”żR ‚`ÕĖ:yÜÖ@ʗ9Ī!­kZ*ē9ʀ4I$ŠRŖc#Äq‚é@ ’N1$œ · _‹ü»Ä³¹§œ˜X2/•9Ūqš»‡g”hŠ×±ÕŗĶQ7*X™h¾/¦¶ŹŃęU5"Ü8I²v [·9Č]|I’ æķŻüg7 ĻsØE„Å+ān§šž%ŒÆŒzå¢Ir!oóŻaØ84ę]Z_“ä …kÆŅéńø»/-a²I“‡‹`›9üŚPł‡UI#*¹–¢™DTTā!Laµmŗ3$ŸKŽš]'ÄI@18¶5»Ē¼’ä,UŸ_Ē/ó?3iÕwIĒKfg«ų :•ß¹Ü]^󕛳ūÖż‚[ ©µjå0n‘Č©©ŖŪmx^źVē¹xŠ»©˜õЁޤtg¼ų[›¢õÆ8Ū’:E2q™—•Ė š ˜;Š’§£0tÕ3“~”Óa÷kʬöšœ#Ś™Äó49UœGŒÆ?w‡ŁH«ćKÄ,U ömŻ£:dĖBŸ³Ó+ČČ6°ė:DŪ‚¦”ˆ ŗŖū8ZŦÆ|®ėĢ®Ė?MęĄŅÆ25śĆ÷S)Aį7}^L…2,d,vō¦žLnõüDGŲ*VkxIh§…SŹ\ļJ“¹{šŅyĢÕĘ4¬“åGåzŒÖ½ #/œ!«FØ*Ŗ‡ZŅJŌońąåŠF]R.‚žŗ.Ń]D—"‰°W Ńģ-¤2[“±å„ø9ŪݤžmŹą»š„ęv8‡sä†V‡d0‚QÖn9Ük‘®Kī3‡•É'ļ¦Ż©&%ö{J•†xoL#ńśĒĻU“aį“ͬįĮ '5ųBlaŲĻØx[*ܟ&Q„ägrfg9Kń ĄŻ=šTÆĆZcvų‡„Ž€Ʉü;<Ģ’jÜł'ĶUtżP©£Žų‡ilǧčĒ8 #Å[l?Āōw÷iR²†‡„7d#µĒŹUhŪšŌH»)Mhņ×ä)ū“oėÆ1Œé­L?ĮY”R–+}½ūŻŚT«£IÓČ#쯕}©ų`±Ćõʼߔo*Ä¤*@›.AV ‘ōŹcŸaB/$”w8”N!°ģāø4Ż=» ‹ģ7йæ ĒCo˜ņ+åuĻŪæ+# øūǦ86Ā:*æ°žD?a¾…Ź”ų^ų¦|’9¼ØI’rå©ßŸŅĘÅŃ{š6_ɋģ7Š©¹?Ć'V„pńĘņ‰äŠˆEA1gvÉÕ ĮÄč”ādm†£'GÕ8™BDżbģS@ö„[~™§IķCS@ņQS£ųry%ęc’E\D§WįõŠō͆X€tĄ}„īąé¾•*Ļąŗ_ņ[ßéTÓĮ”<^Ģ&xļäėgŁQģłŖ-8äĪZp "]•ēKĪZœa@Zśg0u ;ŖU‰x{K”P0°ņµĒĻQܬ  üżRŌJ.Gˆ<5ĶLĘN*äćš$Š6Q7P¹V5¤÷Æ,%Żæ£ź&‡p¦č#„T{ųRÜŸįŹšŽp~E”ębŲ µĀ\īĀ9#‚Y©Ū¤™EÄf6å}Š®K,d’#œy!›?“Fc˜æ0eZ„¹DĘ0ŚØk޽µ«āž,CxŪÖßETÜMDÖLФr*’¤*‰Ø™ŠtŌLåä9DJr¢°†½P ļDM4DŃDM4Eo2ó·ģ1Z2Œ±fFyčamL›]Ÿhˆc|É ·A뢿l¹Œ;Ł27Źķą‹xŽšń”ˆxtxŃLÉDĢ* ēW¹W¼:ź½]S}ę÷ėl¶[Ę̃ȷKšüCėļŸ*•š¾¬,HłeŹÖŗ~#²PšrĒjW.:ņČŪ$6Ä2łE\©–)éŃŚŃøŗ”ĢsGĶqGķbÅ4©ē—L†±R&Gh½Jy¢k[ ujiч2‘Óā†G9Ņ{M„{ÕŌü*¾Ó/É?"9ß&×§ ²E"“Ęž=Ļ ĘJzäž÷`ˆ°åÜń9/;[xö ćK6HQ±©51Ri fM…Ūf¦.ÅōæųŹśłˆ'1$‘Qp¦Į@¤“ólmléš“õhFqŻvš•Ż‡X 1Fœ› Œł—€95£lIČÖļU|ŻÅœˆō"%(I¹Śœ­ķ-Ÿ’ÓENĀbœķWłuŽSŽÅ0”Š$ä^'ā¬{Äæ0|œĖćĘųŽŗqW øŹ’u j6Ģ‘ˆqt—cģNéńĻ0Ø0“IäuݲH§pŚ Ś­Ō¶‹ŽG<=b+Ūäł=”3‰³@ó¦:łŽĻü|½ēN?«\årø£°Ų*`ŹžZÆÄäiĪCŁK1fG¦ÅÄŠ"ķŃ+©-f÷‰ųg3aLfī­ž9—".R3ƒ&œ“. ¤`ĢqE€Fæ_‹Ē8³S”–V½EˆFĪS$¬Ä䂏88ŗ*&Iŗ$XˆÆųŽćĘ„ežcnB7…Ļ9Ė“4Æ \em’«cwKRøStĄ±¼6­8³0JdN9ągQńÓŬkŠ„°ś).‚€G"¢k~'o\ŸF÷äVĶ’ÆŁ8ńÉkĒŲf¾NŹü]ĪQņWK¼Ó1“.:Q”-ŸV©£*č•čH£ząłŅßR)cTš»Č ,‚0œęV<…„ÉEW†œŒ“Źa-źŪ5E>lĪ™ylƒ†žCåŚŒ= "É%ČIĘ.іk?įŃ~³”)HŠŚW¼5Ēę ApćŽ9ē‹wŲrW#‚|ä}RĆ˜r¾Ią–=ÅԘ†™lŠ»ĢWB2s±ī¬µc·žZQŗ $ŚYe¬”ćĘ~?n܌ĒS‰æē„k† åĆus}­~5 fĖcåG‹|żZØēØęĒev§V¢(—YܼČW—ĒØ–A &č¼"ßŲ|nÉ`Št${ęģLo‰ī)߬¼±y°•l¤µ_,»ä„m®}ĻPöcnÄX{(ÓÉ4­ÄæšłDmÓó$¢Ė],Mć׏¼ą ±œ¶XbĻxųĢQœ‘W.©WYkÜäƒ ³W™žyZ“|īķ¹–svšĪŃ4Ø·na!ŠŖf E,ł=Ā×üƒÉ9:)%D™yĄžcš¶1‹š@Z[0ŹÉģ `ŹK.6˜?šK:ĮĄCĀ “ßē™ü·k‚,bŪ<\Ł1äƞć’ ÅĆüoēc„eóĪs…a%łšāžAūeBR¶Å|xĀkr[REw6…s–(k^nفe\-.ņõĶ’hʙ5ŠųęYpŁ!)€Ś„ā^Óų³‡/ų[VĻų^„g5“Ł•žĢtoČįģøⳓĶB}'QƒTµĖń6ó6FŌTfa9 1^–tŗīnæä9ĶRvü׊h8ź'-ÜÆ0UĢk;!b¢nRk*Œ+Ēł†ŻV”ć6ä\1Č7 }Ö)ƒS„'¬õ>ń!{ŒWGDM4DŃDMQŠ™›c¶]ØŠ³Ģß[±Ā•ŌīŠ+„ŸĄ¶#땵ܑtR"ĢęcJs ŗ&URīõQT„"Š£äŗI pŃ-vøW›4øż1SØ5ėåPĶKb«ČÓLR”‰©z„ ˲…ßD[»żņ‹(—\‘,Q• ŽjV;åęŁ4æĖCÕéÕw– 5ŠYĻi¾^2=w+ŸaģI#Ż4E°Č9Cāœ{cĖįQʵ%lÖ[¬ĆŌŃÆÄWŃLŠ©.éł=DĮˆ&”D^ą06ßpŃ9œp&äę2±aģūŒé¹{ŪŁ¤å>ėÖj!Śj&r$õƬ_˜Œ•iźwµ|ŃD4WePU5 SJN@ńRõį‹”x£-{°äæ|±°ŹTx·h½;u3wć–bnFī"øēa±URnŸfbØ'X]Rę1>Ņ w‹/č;–«ÄDrDėėqI›‹€śCyéIŽ+̤īŖZ2h‰¢/ҐĒ0…1Ģ>Ā”ĘĢ#¢.FĆ^‡|Ö.bĆ_ˆ”|Ŗh1‹•œŠ’|²ĘģEQļ] ńŚĖį!S!Œač#¢©¬{†fµÅ£x…Ėč©MhøhÕūgQļ‰ź±jåƒä¶ßŌdõ»Oo{uL»¢ōŅ6ƒ^Åo|[Z\#Ę2qęʰ“"šāļhć}łćŚģš5¹'38†Ņšb=熸ā‰x· ÖģPQX„Ꙁ6M:Q%«[ō›ów-ęW¶l·,ö%hpéŽ:AŖČö³Õ•š²I9l铤QtÉóuš>b露|ŃĀgEĆG­"ŻµpІ"‰ØSä0€@D5ęź‹Ż†£j³æ‡Z‹„yCęŒ‘ T—‡Ļ8s0S©J®żd‰‡ļxŁTkok ČŖ²®«POP⤒‡J0¾ƒ_„ B†«zŅ˧“×nĒ męùl¶n¶i­1é®+µf±JźĖqdYøūƒŒÉLu† hž@łå„éė%ņƒ8ųŠŹ8ļÄc†čō¹Qˆ1½ģ®īWROāXĖøÆ³ø4MĮrœqŹąŠbf¼ąĒ˜æ‡«æ”˜Ŗ<3Ģ–zdžhB„;”¦ķœyaavŅźīN³śf@f3H‰ØŌLÖĄŲ[½lB‹‚b+,s$g,1wĒu.g[<‹qŁŅ~4ņf^Ī/q¶«q»•äׄ¬”š-K"aŖ&6§ŽŠæV&d_¼„U99jŖØ:]ć©śz"¹q~Q®˜£u«ŽXåkS+ټe|qĖ~>ĀÅJä, ĀžÓ3F㦢ŪńL"Ń1Rʜƒ¹0©HVåY؅ŠĀI„å ””[™H§Æš[jøc—ž-3T—-šWM-ĶŽ9f®OEĒį×R eóÓsÅb£ŒÕDȵFR–܉Ę(Ų¶k bė€?]ą¦:"Ŀƙ¼Ņ•L¼„³Ģ9Ģp^0|€Š0æ)ņ>3Ć’öŽ6)ä끸ŗžö¬J³T«`i‡·ć v ćä‚ Ł7fõ6Ä"Ė/ŠīzTņ/7łÊo#3!0]obL™Å§›ZHdGœ„V·|ĖxŪ–9–žcźķXĪ☜•„«ąE܋GĖ@H²÷>3O*³¹‰äރˆł™mØr ˜ųĻ ńĘĶOᬄNeĖęÜ„/÷&­UƲ¹­Vå!- ĶŻ¢>ÅS3WOÜs²;r‘SQ<£•Ä7) †bĻk‹¼YWž^ss}©Ž ±;ƒĖüä.:ējul;Ćģs7ĘBVĆf²ŗŹę[ÖbŅvƒF˜£ż ³°,²ąĢ»mņMą“=Åäi¹{v|³ńo•œoĪÉČSĀ•5_ä=ėR²Ōlā©RŖJŲØņņ ¹–‡bH'rĶUQø d“"Ą6AēŽiĪ^9ó=b„Ķ[ß<ź™7ĆķŪ2ó«¬I‡ź“žēĀÓ1LÅ…]ŹŲ^‡Dma±ĢXē ¢Ė9°O4$ŌŽķ—t„ļ#< ä®9ņ?dĢ‘Éģ½ –čŽK꘦ĶÅŚc‘qWb:E;Ē’‰œ^āŹ¤%b*aœ5™ģ«&ocć#š\ZW‘‘AŖ=éEB õ8Ńzš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"ųQ2Ŗš‰7"„2gJr‰Lw :"ņ5šóœnlęĆ$ Ž:^½’b$ot椑ÄJ(v&ßm€>/É«ŒŽ¹·Ģoéķ?ĢīµP^G‘nWŪŖ1¦ć„Ī~›(Y Ś~žĮķ ²0©“¦xׇ™žtžUuJÜ? N s–<¢ć[³Ŗ#Ė­kÕ-Y6HɦÜ×g3R™M³¼YéŃkß 56wMŹõLå±L˜ ÓčE龊$œ‹€Šd€Ø‹b–LŠ'órF) ś—#`U4ŚģcŸpÓD]T?g2—!ųšĒ*ĖchčÉ>+Śßkvśœ“›OŌqm…²0ŁbĄŌc#ŸĒ¼fÖ6}°E‡rT1;ˆ¼÷Ÿ³R=āģÕ:j ²=āŠéT×DT"j J@Cø„0{@@² āįŹirzāÜĒQęœōH"*|¦B¤Ŗ¶Ą#øö`›ķ¾«fÕ”üĀčńĀäw±ė?šøøśh‰¢&ˆš"ˆüĀ5šÓSļz¦Ė+\²ó ų‰ģ­Io½Y–b°.zhv] KĶĖ}€LałŽü „ʁm|aö¶Ó- p°ÉNR ģsź^ˆø' cĪ7įŒ]€ń4Ģmˆ(Õ¼}L‡D©¶‚¬F7Œh«µM w(ų¾£—JØ©ÄNsŚ]ÅuXó-›8ė’9…ĀL \›łĻ:ŒųūljĘöG•˜Q9·PųįĢxū “:n®4Ÿā”UfĀ.–Q³6Į"éA!“XŠ9²ņ—Č[ÜWņAŽųł§n¾=üwc®^“˜‚Š„aīpr&ßfåG·D–„µ²Óų×/„ém§˜µ’õ[!ą\ę¬el%UÄĻ–ÆÄÉ_)³imÉHeīų¤—6久l½x»å%³†ŃJ©d~/#ĻęV·œ1š­£cŖĀtćÓi'°8¶Vdß9MŃź:M‹ÕœÄ[Ļ >A2žiÄ9¢*r’ąjļ sÖ³ü1­Åc42§*īPöĪņć*ēé¹ĖŖ/2EsQć¤å¤jt²3;“éļI8t™‘»"y&ĖYŒ”œ#ĖŹŌÕŠ˜ŸŽĀ –• Į‘°qRõ?Žr…ŹĮ^lÅō„²8õ–.śhPt‹J㢔E5U"Ą¹|–ł>µŽar5ėŒYéĢ?©™»Éō.<Ä L_›xE™1Šó‰Ų1­5fk #“ń„Šõh²Ś ß¢āDčćĄZ9“‰r MJ üĢ·ęėņy ĻLo™syÄL-µī/Š3ę7äTÕzZ*BfĒ’ņ­®'@ŗĘü`©ĀĆ*½vœŚj\PlO3/%8š¤@‹[–¼ŽņÅüėÉ~ńځeĻ\†æåxŽ`šõk¼s¹j”æ !±Xd>Lāæ½Ļ% 3TĶøŹNƒŠė,ņ9,Z2E!}#¤Eńw8ļw©ō'¹ŸĪæćƒŠ[~E±]æ%dŗ¶5ĢÖ};–dp‡x•‰eņߑndcŽwaŒ8G˜XߔxĪń9÷O,M²ĶT{±®Vāz8ź‘¢M–ˆm; ŖDņņN€Ķb-äē!Ü<įźŁW<äŗ>R²gϹœxWJaØÄjügČŲnÓĘźōF }źĖ°rtMĆ»±IY„œŗk?\xį“a “V¤UsäŽ]°ó_•˜X,œĪ³sÓ ņ—ŽŌŽĀq껚¢øÜĆ\q~ĢW ĪX#>/Z(m’i“ćo^ų£»p,ć~D¤x‚"$S«ń%bŸŚˆīC]#4®ük™ĘĒ2FC×<=§ßąŽ<|–Ę"‰ éŅRØÄ1LXz‡·EKŲŁcw²ąAč8,LÓlmī4ś„¹”@mUø+b»€­ē"ŚÉ¢P7š€©ŗßßŖ×!‘†9ŚŅGa¢©tT+GJĀüŁå''r&+Į™g`¼;Œpž0øĻŻ/–Ē˜/“<™eČäk^ƒik§Ą7qŽģ¼…g‚~kųĻHHę4œĒ‰³‚bœenjŁÖJ;fŚ…­²ÕŌė6GLäĻÉüŌ)Ž¢=æ¬TīHšéŖtEFŕGPrQļ#^ÄD抔9×~ĮÓn£äNŗ…]Ež5U%;:Ź”c¦G®¼^­Ł™²;3F™£S03QffA#33#$( C5Š"ŌČīOLKŲ%é¶Ś"įšÓŖ, ›Õ™U«+,ň“®5ƒŒoŌb×lź0[Ä$Ō‘č s–H؇ja訑 M„„"Żr¾ Č:(`s,ś2RUĄE²ää”NÕHi½Qė蓱DĶ–PL¢‰‚^ĀģEó9Z®Y“b’æ aF.A9xħ"˜K'*“gL’“bGč8+IŁæ]"¬˜@Ic”“ę"Ü9„†zģĻŽDF;~hĒp†zåƒUݚśØ,ž ĪUHė c՛&u›÷zJ2‰Š"P؈Ā(‹‰ŒN>9† °`ՙD·‹x¶…n’em€”;'jeŲ6(h‹njøŒė«B0‰Y_4jĮķ‰8¦$xŏĢ&n„Ź€H8hĻęÕō“:‚DżSö€wä_ujū·»p;“œsgj?GŻµ‘éœå…øüæźgāĀ«²ģįATācq·"嚳hÅ#"É«vhĆLJIŖ 7Hī䮿ū£&‘HS8|łŹ‹,qåUPĒ0‰Œ"$Tó5*2ŗ­B6ŸVŽ©®‘Z®ĘæŅŗ²"ia“N5DŒšE(”R”Ų¢-ł«uÓ»”~xS¾œMŠ3OMÄĪåыīśbRŽEZA8īńō ؜Ü{6ŃŌŻzĢŃ(ū$=‚YI"Źn1”«DdcW#Øé›?AĀ)½`é2؊ PQ% (€†ś"ć,«Ńė¬>õÜŖAFV£Ÿ7ūÉeR"1„L['$Ö%ńQψĶ®_Q4•ˆķ.ÄPC:y5ń‘Ä,{÷’'r³Tśåj9ō|=6åQ·Ū\6rfļĄŅhķiėt§Ļø@ź·`Äéw¤™ÕķŹEÖט~L¹gå¾·`Ą¼nĒ×Īš芹YG9ä։Ārs‘tG;§/OĘ4@Hc Ī9_AěÅ],ł‚¦ķ?¦.#ÖöŠR×­lŚc€‰.y }cĶČ1å¦Õ‡8Šgž!’ŲŪĮ³‚ŖŃŚ`Hh(ņ Œƒ¶dÕHNćw*ŗ¤bŌŹØŖ†:«(&:†1ĢcŖxrI&±|²š½Ó8“ĻFÆO x¶ŃDM4DŃDM4DŃDM4DŃDM4DŃƒ•L‹g ‚©ŅAUH˜mŗ†M3¤ Äsˆm¢/# Æ×“ĢϐvOMŌ‹ś³÷)ģōÜ9ø_–X½„ųCµE„:t VĶ«›|ĘžžÓüĒžėU½ņ2@/0īĘėŗøėmĒm‹4žū{7ܚńžŅ˜ąO’·›žtžP”7_w·ŻłõJÜW¢ļįLćä=„¹7=Æ-m¹Æ/ÉTŅxé±õ:6d•f9Šjˆ © ½ņJÄéB€ź”Mø”6"ķŅI«×21…hw‰1z]€Že&™J;p W鎒hķ¢.żIÆäŖ-Ļ[%%W¾UlėzĤöĖīU©Ź=«2xržīˆ¼b2Ķfj‘u“PģYy\wyČøåÓ÷')Ż=Z‘p‘…X\˜N‹„ĪˆCl°hŠHxÜ|f\É®·„›ĆŁZ1@0ēŽ*D"cÓćĒūžĻŖ™ķ-3›@.÷faņ:ģ_««‹&ˆš"h‰¢+)$Ž=÷<|HGĻw•ņ+†^9 ČӑÉK8«¢ØŸąהųQ(ž‘śØ~ÅŠ>^ž'9ś^vv×̽5muÅĆJW+ӎ”ĶĮCL=¬Ź}nøīR-ŒƒŖü×ȼŒś¼#‡h,¬T§Ód\7ł„5}ŌO»“ę"Å"Ip'*9łĘ̉ŽÖĒxˆ˜Wf<ћ­8āz«ŒÕ˜ŹŠę·™8·‡–ø88iųŁXśLBP#—|‹‰T:āČż¤Zt_(ž9ń8–pHK'”«c3dř3Öž3Zėµ9ś…Öó”œc›EN¢įö9ž„ I9øōž7JE`äQ|U¦EPcždqƓ5–±$ż?^Ńå ><å:·%x÷-LäŒüÄßī¹Zæƒź­ēÜÖģU+…Ā£#oa(–sVjō„LŹ@D‹ōyEĒ‹ö4ätg°£ 8mŠ9%‘±~Zq‰iEćŽ^ÉU8iź¾SoŠ“c!÷§)(ÖĒ41Ö9Hų!‡šIŪ†ķä_ Õ&ˆ£FIņµćÕ.r+—˜ņ½ŠsŸ%øķĆņd‹->ƇlÓ¶5”­&Ā*ˆ„ź3;[Bäi$`ķ²QĻdb)i™cLÆ¢pŃcpÅģŁÓPoÖla„J¢ÅKOc¬‹^™ø ‹'Õ ¬¹ŗYŗ'õ” ·t”Š0z¤(˜JP¢½Īśę6µł-/"¬‘1φܐĆXŠ5ŖŒĢ­¶Rµšø­ÅŒ‘J¢0ØUŪNOd,iĢ9’FŒ[5ŹŖ»6i7:Ą”h«ł9ćÅ{=ń ń;‰™̹Q›3ż5=®ń†ć‹œć˜B”x^rRĀł:Œ"^Ģ×.F#0ŻÄ©ŻD³ Š’Č‘%H¤Ķk™o1@āücÉüi’ņg,jOٹsć²DĘ8Ÿö·$ż‚©«/\F×%?bĒ‚«˜8gŅ 7`‹åÓ;“xR·”y'ŒøoKe~ZJcˆCD‘‡”s9“!XŲ"ćģo§ [$ 3ˆÉ‰[Œœœ‘YĘ4jāMŪ—‹dĪ¢€Q"Ęß|œāæd9g›½ŗM<ę’.øÓÅśµ/Z’äAˆ„ä.L„b<{BÄ3ьņqņ4ެ²ydnįŒ2ģ1zāeI²x”„,nsąžRā¦cϰøŽē‰qü³L׍exēaO%W§0”'вg&±ēķ6.’Czš@Č@‹ÖŽ"»TE³„UP‹Üió;…¢(ų#ņn·^įŽ8äpĪŖÖVǶ”ä'²§sqČÜ[ NˆŸ²_čXā6)är¤Mėqf‘1)•LŠrÜ99Ēü/–rŽb‘øć<‚ĀWqUÅą\$ś÷Éłw|„Č7ź2ʤżAÜĢŽI¦f‰6ģÆ6nĮ«xņ@JlżĘŽ™7Ų¢(ēÅWń äJNĘå‘Īx±“ėČ(ąÅ!œ·Cš©XhQ7O]ż–i¢ “TP tEÖū‹Sµ~1qʳg eÆ`ŒE`UYe~·ėģ%}UœŽP §qŽsSś­rk×5÷“=žĮ•Ät+ó¢ÅR냸f—’Ÿņ*nł ęv5 žƱ<¬Ģ\SćT1ōż„–k 'Š9YMU Õš,ŌŽ@Ää,ćŪé Ō`Žep€ć@O¬żÕ‡ÕŚ(hŗF’ēŸ„3»€ "ŠŲn™o0•¼HĆ3c ¼™elpŠĀG±q“ąž·R«Ó0#Vēpø˜ė‡aքūɟa{óŌ֔ņE õ«A±mtܱĶå ų/xą¶=j‚dcūKĪ9aėdM6ķŃĘŲNB•ƤB4“Jg4&ķģ €l #¶ļņņ,ś…ÅĮĶ>ÓæśV³ÅŅųzAf÷½£¾¾e5ÖW+M4DŃn̼FćŽy—ijČ8ł¹2h&9R•-3Žņ¬P Q+•Č·ŠÖČÜb$éW(”:6Ü4ƒQYÖŗķ˜ĖȏŻ8·°×ŗŠŻ0ć éé®/ņIĢŹŌj}Dä mœZǐ“ˆ&ļ Ӕ”pŻ2ōYsŸ~¢ad¶ņéø ׏•I7_yūŲ!qåo•÷-Ål³“Ų…’¼åäĘq£Ē Ńc?‹+÷PcĪļ0oPÄ<ę”Pņ/|Ćł#s °ˆŗZ 0ĘCC ƒŪņ°±°‡`ĄüRĮ„9ā«ŹįtżoJ¶ŃŌģߓl7LX§šæ!™ BšœHdüYå3D'!MŠŹG2±8€Š<„!eŠŃB‰@ÄX¢Ųa (rqØČ›#”cXśå$:†œ˜ā“xŌćķŪGYŽÓČnFŠ„]¤v}äNČPɬ˜"¢ėNGĒ;ōDw)VMB€€ķ„—ˆµIPö·ź“yź®ī?ąļ1dū;U Ž8’ĶŗN¢§ÉOŒ˜‡vŽž“Či dŸŗ‡zMĒe›%6ķ‡^Ń`Ė©_ĪܒĶ!aŚ*hziµJ Į„ģøFž&oė©"xҧjćŒA/PMõÉŪ›‹ś¹ee~XĒ’{*ān½»j“µt.’L’Ģw™zvkۦˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆæ@DŪ@DGٰ]’&Ś"ņ6ń³5³ļ7ĻOM‚ó+±L¦ļ!öō-ˆ v—æµJ;ąźć7®oóś{C’Q’ŗ 䍩Ņ啁ؐA7xß‘5;v ٤įT(Ž)ś…ßģÜ5Kö©n’ū|Ÿ'”-¼»yĪŖbšĶ€)5:Ōö¬‘OyŌj°É¬›8ęl£¾iƒy $ü’ “6źøDžšk,aģD@xūˆłķ¦|ąøø‚āÓń"¾¹ö^(‡Ē—)||™^c·‚6—Źö±Ę„ŒhĢšG_ąŽŗć LŁĄ’ Ņ&ę–Zf Š*{ŽĮQ€qŻEč_įśŻKā½"?Ę^P¶°Ļ“9 ‘‘ńŗĻ›7 ±]¶ė1lqvĘ&rŗ©ČĢÕĶ*ÖÉUT…X„[e˜Ŗƒ£Č|ˆłßĆ=ų/ūŸEa“Õķ„š/ģ^šłlī@Ķ”øāC+‰o0kD¬­Z×µķmŽ+į{īÕŸvsĀįš) ‘œ“ʎښ„‚ Ķ {ę6;Ƈö²—”‡“\ U@•øØ‡Iö€īS¢¼Nćæ“ķKY_9ÜŚ‡L±[Ż‚„ rDŽ—Č*§yA­ŚnĻYuJ@ŲDĀGZWĢž(½ą—ś×éÖķ»½Ņ“»›¶Āē¶Co„Ź\ŠHi­'`ÅKh:tz¾·i„Lów7 Œø –ēpm@$Wo*óA˜ń÷’9-åß9`Ė-:ĻƒķY·,ßm91­E”Aā(։™{Ѭ5)× Ē…üō±X“O½bœƒśō@„/ĖŚļūĮįŻ;ż¶Śü޳›N›ŽļķbllNt§ćd“+£0ēlžq‡Jāē7~!t(>Uėāé4Yįŗ45yœ€Öŗ<µnY2–¹Ä2€’ŚŒŽ?AM`!Ųņmdś"r«•ņ†g.ĶF(ŗ”®Śkģ颠S6QūČę‹ ”ɬSD¦)‡ėĪ׬ø§‡4ī&Ó^É,u (nęͤ±µäæ#‰aŽŅøoéŅŪéwś|”Ā{wš‚(†ń“s^µŁ[ łõ4DŃDMGQc+†HʬŸbēčÄfœMx¦ęģ4±ŠH‘Y[˧?XPWp_Iø?ōÖgźɐŸ3ÜsŚńĀ”NšęŖ4}V;§×įĶZś{®ßĻB©Ģ»¢xŗņe‡¼˜qö?!T–BŸ›))2«ņKK*v—|-”Z¤v³qPļ}95Ŗ²r-VO°QzŌ; $t‹–čŁ]ņ9#š6ĖĄF ƒ°‚²_¢­b–>;r’"2/"ęėgSŖ9mƲ%9v0±ße¢3Ēs[¬½Q¾Ä5¼Ō”­Ų:č£F-e7]Œøī'Ż3hŠ.eæyӛNÜd’ ™« äløó8qiģućūMst.,q’7%•®8‚.Ė5%wø^óCI9VņӳΜ“DNĶ&ĢŃI¹Ģ±°Ė7®YžT®[2ĖP‘ō:’ ųćå±+Šć¤Õu;‚ųĶĢ%8ŒėI(öŃhž°U®Ć8lŽ6°dÄc”†"ÄO°³—Ü—ąŽ,ǵ,4ŲźE£™q<Į<²Ęž† ā†ĻX#=+h£āŽBEaj,Ėå9)7TN« ŹÓs”qѳU(ōm¢)ŻhšĒ8ēˆÜlć%)Ó(ŽńŠ®\ųķ¾Z"*Ś2ŗŚ¹KŽpt®ToŃtž ¹S 9š‘Žpąx„ć‚‚ °˜Ę"Č® 73¢ł;HĖ̱£Ž9Ō8­‚įKˆ{„’ņVüŖĖ+ĆĀ#3bœ±©P©ŠėՕG©6"‹Ź”ȝUŅ ˆ±5Ķoy–y”9ˆsu…ŽūĢ\KȬ5M”¼ņ©‰'ØTŽ0ą\d ē؜zÄĆX}Šœ:‚š„“ZNÓ3wiŒœ£}IģĄ>_c»omSŁKˆo9½[üxćõĖą¼q†²&5³Ōdā) ć!ĖY/y?ė’-œŒÄÆŅXö.ńPf ŖÆĶp~C¼jņ?”9jŕų’zāf'æMSńÕB‹Č)¼M™jü±ćźŌ™Éåģ”ĢӆóM@ŁM‰¤$N»½…ƒh(aIń$ĆG$RS˜sr”{¹Eßb!ü†Fa¹ŒÕOdĆ,T‚“Ҳ^?—y_±'"į'1ĻY8‹DÆY‘d߆œ<žćžŹń¹3$“*r#“™Ł›ł/–šB–Ɨnµ ÕfŌJ *SVń„M‰„€Œ:ĘX±ńÅUŹŖŃŃ\šaĪĮč_&ü?Ž Ž€·7 «BAße?jŪęH"¦óö„ói§“§ßEļ‡ŗŽĄ¹ß>!c„cIb@¢¦Į“.I(P–JŪzšM}öėŽßߢō1ƒ`Ø+kŹ?Ž,p§ł#“*œć³y,_€³B†hÖ¤»-+KĒ–;+ŌY„‹g ¼ Å8Ž°č½ŹÓ“ÅÖæ‹üyćĪæŌŖüw‰Ŗ²oÄś’łfzMœõ†ć|Č×ńrYq'Oß8™I­"IvŒÕQ4X·z`AÓS­AjœXö‹xcśEäõO:Ÿ:õi ¢&ˆ°K(Vėž&ßɽ0‚)]øŲdD¤8Eķ‘Ė"ˆl= 7š@w÷j“µt>’Kę;ʽ=õāŲDM4DŃDM4DŃDM4DŃDM4DŃD_ƒŌ6č=Ų?zAŃ‘Ƈõжt꼅l݉W™‚X¬ZwƒV…5āž`lŌSåŪś€Bwhżu[6ł÷Ÿę?÷ZÆ&u«Yü¢qĪ£}­¶¹Ņ&ķ¼|†³T^‚¦e`…³Z #숨š§j耔J%ļ!D£Šu̾v_ė:WÉž*Õ8véö:õÆßMopŚf†X­ä‘’4@sKp448ķ[·Éˆmnn4ė{ę läŌĆ^ƱĶsŚ<Ųā7ļ]ģńnĆŲV=Än!ĘTleūŠ;ŲŚ5Z¬ĶŚ“›ŖńVLŹķTS0”§SøĄ½G_Ļš–½ÅWüKÅZ®Æ«jŸź:śźK—F%Ļ ń -©Ś2ż †[(>Ę ·­iĄHŽC@ó؍䟇r<µĮD68}Eä~™K*qĆ%ĮŹ:ÆŲjY& ?ULģQēFF%­Ŗ_"ŗ‰œ ’¢ƒßŠ»ņ7ēēČ?™¶üyüGšÓi¬@Ś“%›ź\±»ē²yńY½Ńų‘ V©ĘÜ-hO°hRв[øīx“ŸvAźžz;вÅāŗóĖl‹Į\iē#HnF:ˆ—inU»ŠŚ®§āćg¤˜Õ,ӈTŸH@ĒY¦ėh6VI²&OŅ{źw"„R'ļöŸØXjöjŗTŃÜéwP²XeĮŃĖŽF8`Zöąy ųŚhf·•Ö÷ s'c‹\× øG(8*KĖ—,q7ųW‘ģy&V;ļAś^6ÄŌÕ¤Śrļ‘l²ń¬aŪĆ5ų,ڶ+żVErHՋCœĀ¢øŗĪ-G„um>qšō»øÜ9ZūyGX+?D”Į­ŁNÜĖøHź‘„Z l\{†1Vƕjńź¼|“dSpąh‘öUr—Õ8wGagŁÆę+…“ŁĘŪÉ'™ĶŠIšČĖæ†ĄęTZ NWß7÷;­Ą€žuÓ7ĻĪ~ĄS\ÜĀሠkļr÷e”g³ĪH‚lɱę¦alµIŲZ$¤›nŠ°ĖŠjŃ/ˆįUDēhi’÷$r'ūcžĆųg4–÷zī½<ƅµ;†I¦Ū=Ī-kā ī£iūøī^ę†å —Ā2Ї5Īł ēdś^„©æL“cEė {&x¤¹£#M®`©5©n`Ü @œźözŠ cŗbs cö¦""AżŅˆkīÅš˜Ćµ|h½MQY$P1EiĶĒ%Ükōj»E‘l¬ÕŽE(ö†vć»åŁ5÷8~żĒa½4"‹ "ŲH‹&ŅĪīžaoeå˜īh®§påP łCʧPAfC8dŠB€øś‰ņEM•"ž—ØÕÄ¢OŅX£ŠR:ET Ó·~šmܲ]£jķ—Ąu¬ž-iLŽņŅź×:ēßR\‹É–Ō€Oó4lo“n0ģ’KłĒ.åąŖćŠÕ=Ćs‘E:m‡}bĖc²O4Lw#žŠ{ nہ8¶ī3,6R–ĶéP¶åĖś½”üŸįPĻ”R F5ĒXŽ×‰qD™iŁ’-vž‡w‰³µ#3©×®0vˆ'Q€šN¾aBH”‘H„óā­d”E‘ŗRچ‡HM­q+~į/ˆ“]5ĆR„ÅhɝRņh֖‚2‘P1õNÜJķ“…?žgĮŒŚT|Øp“"Q\Ę(眣ā,SĢӁ§Į4ș¬SŌdŸ#ćF«Ø’‚d”ś’§6ĀD)ˆAŗEõ¦Æ§ŽįHĻŃvæoQ+%ų’Ļē†ģ”ݳ˜ ˜'ꚕŠ!%¦ńs„J Q2.QÉu_EŁū cńŽĆ°śńI©ILņićŸ!ś!Jē‡,jø/r £¹‰ńPvü ”¶&ģĆæ»³}JX|„Œ¬I"½#Q'Pq°·ZŻ_“I~ā‹čØŹAr+¹D;DzˆµlY/Tø“¶_é5xÖ©Ć© Ŗ ‹d c,įܓöČ"‘@DĘ0{tEĀ×ó~¶‘ŗ•\æ‹ģɼD®Z)_æŌęHé¹Ą Eۚ:YÉWDåĄåÜ¢żVÉŁ+Ŗ’5= §ü£ū}Ÿ¢øūtEņīĻ[`‘—}aƒd‰J'2Īå˜7H¤(˜ĘQg DwŪm[y.Eńņ5U˜ĪŲn)$Jc¬¤–O¤±MōĪ©ŻN$TŹOxˆ€ˆ­cŽš=˜ģļ›Hj!ßø8ä†@Wæ©Ń[™CnægæDTZŽQ»j½Üģö‹¶ĻL¢ØgÓŅ–äA£v^š(21ŒaÜŪ6”ǠھŚ9 …ņf. ĄA]”åZ’kPję!\ŹÖ¢›iĪys©Õ­&ˆš"hŠÄg>N`N5Ɩg6dŚõ «1s$Āaw/l—`Óprņ"”ŚNÉ Éiܑ·Ė&n‡Pŗš¹£ieŚX^_;-¬n}6Ć¤œ¢ņ§3š.«Ń¬9« NäŚōmžƒŠajRóšĒ*Ķ'­żœ·!e ’M5ŠEה3Ö&u€åź‰fŠ&pkņh²,ōBžwAjĢīkØH>­G>žŖØ3ĪŽPŁV;¬iĒ_Dƙ}˜›<äÉē·'ŒśöŗVے0 Ŗ˚Yډū ;ū åā]>7e`{Ē(äyēmņśw0:ź`םĄlņł•YVēn\€]Ŗ™·WV„ö~?[§ķ³ßed\cK•vvz5Üź’%ūŁ“ł¶«˜6­øŠĀwˆßš7…Ō§h&jÕ÷^C’ŹQ+‡Ń"„ō̲/MŗTr%bėD±Ä[jV…} `ƒvG‘Ļ›˜D‡ķ9{TAĖuŠd×AR¦įŗÄ2jŠÅ ąA…,RĮ!Šf–ŹÓB*ŸEmYīAaö™’eL*õŁc‰“(óõņGÜS‹“•`Ŗ0ņŖ©Ŗc§-čø1@¦buŃdZÜ[–\ r86ް¬Å3X¼Ŗą¾ Āć«ó®2y“ńuCQī'Āł įgĵ>kįė- ™jÄ™j©.?¶<}TDAģT‚+Ē®³ä(F8,‹zZkƒš6^՜® Ūq’ŖmŅ.+”ŽI8ÅČü%,Ś•ŹN!Y¹''–°MįUŲ?RrŖīzz>³eM df¢iČ7(¾Uźn ^©E›8M„q^.ŻtĖ>Jm“āĶ2c~µĄłåó—XEø_¬p°9~9ėšŌ{Ō’$£œĢŃT\E%°śÖ°ĶŻ··Jū,ąJö Ž ?Å{}¢Ęš–‚Zb( ŚøK…fć ł4‹›xus u¼R»'ÅJ’·ŽCź6g“øĘ$s[#›į‡gsA·÷¬CćŪ % ÷9ó_2[ņ(×T¼{CēĻ7ņÖA°1””–®ĆVó’ō÷¬ ‘ć•›¶:@"S›ŁØ­Wø{J1Dfų›ŁĄ1ĆnaƏ—«ŲćģueyłĶOŗŠT$#ŽŽģB¹ŻFĻažĘŖ²k ćÕLč˜Äģ.ˆŗŠųīć–1ć¶<Čø¾!üTmžĻLu2yIg3O^O“ÅÕɒ|ć )’m9syDJRš<Ēõ AhŃ µI]†ŅĒłŽņÆPmx¶ŃDM4DŃDM4DŃDM4DŃDM4DŃD^E^g®iģ^Š;ø„6ÜKµśį±wč°)żVÅĪ>b’Kjź»÷UiĖüÆ7ĒĻ"ŲÆ6×bįggńÕ;äˆX; k- (źÆk½53I"6Q')·pdž¢F*ˆŸµBüE ōaóµ~Õ&žßNÖ4ł­$–±²f–—30-$m£†W “ąVÅņ§T“H·ƒT­|Ö·„į®­ M 1ėŚ;—a ųŒ8ĶyųÜæMµa™u,šĆi«"±»Jqo?ŲŽJŌ " gMۈŪÆĒī/’`æ=ų.7;‚õ ?‰4˜‡Ø×7Ą¹,cķS 1ņs/¶ō›Ü«·Q3i÷goˆ3G]ō‘€Šs¹¬Y”ĒÜ®Ą™r¬•¢…’j¶HWūˆņ2a›¤ƒ¼›öœČ( @ß©wÜ ėćī.Ņųć†_?qWźzĮc˜Xꐌ‘J‚ą×Sõq]>Ŷ׬eīŸmcjøņ¬ŅjMh¹ŁŁ6õéik=’yš·}=Ęøv‰|›„KęW8„Œqē|GĆÄö:9€{Ė©›.4 ‰#iݱ}ņ§ååŸĆĒ?ńP4T†;©S†8ø+óŒiöt²›ØĆČM¦±YfĒ!Ł($®’ Ü…TęuŌĀsĀmŒ5Ę5-CR‡+ažFĄp yŪŃ^EŽģ,¬%Ģe‰†nv4åWżÕy¹4 ¢SL» H ‡³nōĶ·īj×¼ę{ÜOISm‚Š1 .įŽč÷ŹŚōūmy„yĆö3U]D huČź6eƒčw1²qұ‹”“–ė&²^ć€å錶„¤Ž7QÓ¦|w ֐ąAŽ}«PŅōżRŃÖń6KG–œFƅ G/VĀ©\)AwXꆮcl˜£bĀ­—2NUŖŹfr¤ĶQkŠQµJėI:ŻŹĒ``›‡×Ū[1n²~™€­UŪsæAüÆā"ā!w.±0–Ņ,aČ֜ę„Ā­ 6•V¤/š¾rš÷ pęŸnĶ&Ł‘_NMhIõA"¤k›a”Ō,ÆŚ°v½Į.؃\HļæęÉi TģuźÉ©ó–‰v wāQīßp~ŗŅłé—7}܏m9G«1)Ą^L&tŻńC¢SūF/T`ŌwĄ¬$\z©’­ҁd7TŌ[²yzÜO”«-+ā7Ǥ³ĻŸ7šĘ:ģײ6[®7.Ē:€r±ƒ¾°`U@ŹĒéwv€ū< Y-×µfŠ IķiņµjÄų”ńżż)3ąRNŗD;S ^FŹö¦`bѓ÷—ńŠö‰6ōL=¢!ģP#õķYā†R3Z;ĄŖä¦¼Sxü*ąēµ–]PXWÆY/Õw žŠµvŁv‰ˆ†ĀDĤˆ—m„CJKuĶU›&wXiņ…Mˆ.—ł¼9`Kž4ē’o#³JsūƒVžhū,’…Xłœ[ĄŁ„ ‰?Ą9žrń^n„rĀ•nLēœ}žńu¦)ģ²īņ2t†Z€ˆŹ0¶X·ˆ·€rWI¬Ń3²U¹Læ«įŠwIā=¦-Iō–ø:€4ŽCAANR1åY&Ēš•«ur§-b²7*j¾§ņG<®4œrÉüIėŹŹŁ>¤é2Øüņ…Smö׋geÄŠĘö8s8:’ģs’ąō$Œv.āŠŹŖ8Cœ&\€@Ž‰°Sµˆ";nīß®ūč«/`Śęö…e2w |ZHꔚwš’q—••2æ5#Č[Ö£q*Š•žMܰJT"Yē6…ŽŌ“DZ,UŒŃPYŅ„v#źŸŚ, [O“ŕ¤ņ7Ö=ƒĪ±Ė—8fĶyMæ'©ŲĢųĘ65ĘŻ„óbN>FiRHÜz/2ģɤŅEw -–ÖņŚö?ŁįĶߏ:FаČ’µüøÅT‹6ć—&ų3Ė[;Œ?ĒLם±Uj^݃²å¢MµšÆŒ3<½r!“›³¹ÄcŽ2®O¢¼‚N’¦l‹_E<Ė'RalN/ Į ®Å­ńu¤§L~” Ķō"ÜŪw†¹ķ‚=lÖ?TŠq”Öńvv.Øē†\s澑Ć`ˆĆ|_ ×Ń®±9Šį )‹īC‘rŹ{D©¤Õ`t¢d6é” ‰m²¼DčŪ#ŽŃa“„īvä¼Ė‘Łń ԶϾeżĶ“l®G]ݾC!ŲZŲaŒSi.nPNÓ±HN@ń NÕxӏPĒqŌz¬—,ųį<õDń•­“:²³ fŅéŽĻ ;ģ’N›V`ķ#‰ŗūć!qm āŽ"ŚFdh#}:BĖłG+µ˜Ööz¤ŽžÖvĢ×ēeu[\ÄNcEyræŽ%Ņš&w‘}RÉ9EŌ^Ė3±­³NĪyQ“gń4Œs¶°vl€¼ ,ŚA”š¢ŌŹ&”@J`0oÆ]„ŲEœ#«²¤Ē}¬87†,b’h,āńDn ‘R( ŖŠ˜īīĮг§?EĆx¶0\=UEŽ.fTX³„•1•UuA „DDzŽ“ѱ|™rģ÷2<ķ28÷•v:ˆ”„)Lcę)BĒ9Īa„(‰„@q颰±W|„VZŽÓš&±ēųȧŖĒ=ȍn°”J,£Ög¤Ņ„9}?-mbĮr’#fńŖ¬Sz+(˜z£ƒqØŚŪ;$Žõł=«pÓų7Q¾€O#„AĆEO^"źĢXüŗäČŪź”g $—ŒM©¤d¬¹Ę­aŖY"õQ•‹¢;E’"ąUŠŌĪQź«ŠŁųbLŲĄcŁ»­f·.¼BĒͳxn„Ė^æå³"µdl…Äęe®p 7ŲĖ37²XX²Üž«Ę«M*؄ņčLpl“ō•W·“ƒÜ K5{7»)%½#:önŗk ”™®xÜE;ėę+0Xļ!ŅņÅ"³‘ńÜūK=.ßœ“ Ó0P„pŲē:+7rŁr¦ź:N9Ś*7vŃrĆGI(’„)Č` 0A<ŪJč'il­4!VzõZM4EŖÜ„;„p!ÖH¦˜å~!÷uŃÅÕņ­aƒÉr<‡&c3“ß26GŹŌ¬qAųüćsVķ“Uę•H"ø¶ŖÕc·F ˜ģ*a¹=OS}Yžī-WÄĶ_ ‡0mtrķ®žåŽō+{1£Ē -¤NŒ… ØĒµHZ†8ŽÆMĪ]ålVŒ‘mĘnāĻ“ÆņHN\ģA¹°IĊ 6oʼn“fĶŃE² ɐ¢c ¢Æu ›łˆ€¼ä<¦žX#āqżiY,-8ćbJLFXņ0ęRvA–LSjÜN½?Ě }Äś­ģ’ üC¢6ÓÖ°FMy3a·™GjZē qv¦É.tŪO†"l…Į2øœåF; ĒdŠółMānĪJć–|g ːT±é ‰ę0µz”Aś+3gīé.#ęP@¢cVDGq‘±āh^ńi«ĘėKü0urś.#wWrµ«ž™‘›Ķ"FŻéõŚŚgĪnņ9±ę 5˜>TykDĀ™ē‰Ł‡ä?ŪŻČĶĻZāŚ¾šygF"E‹jĢ2j)µBĒj:_VFM?`-ŁFäTĒM]3P}Ź“|lµkɘ9„ĪsrÖ°Ōœä8×@1QŚtŗV7£S†yuĀÖŚ–=¬Ž9§3ĘĄü£õpó+£ÆTbh‰¢,άv’‰›Ē„H« ­ÜmDHr Ą¤q;ol¢ Ō Ż5B°¦(»T«”pĻśgžc¼ĖÓæ^-…4DŃDM4DŃDM4DŃDM4DŃDM4DŃ‘ωvdäĒ9ćH=Äa2Vd0” V™*ŽÜ£°ˆˆnR‡Aüś­›W9ł‹ż%Æł®żÕ“Mpēr‰© Z³•;õmŠńՌPp‚±Ńė®/ *ĮźKÅYkāšE_”r@1 sŠJ¤'8š²Šv­Dā-CCqŁ_nćW1Ū ŁPF-4Ž08T,0ēī ę~9Ō¬¹~ߊ.xę¶‘TśņsļØvé%—P‡kO˜““³ŹŖ>›vldT”ß·b”D-–ŠTŅ‹§čüec«NĖ6Å;/øęŽR\ ¤E`,]×|ŅÄŪ-ŁųęŽDUXŠœ¢±SF“·Ówb”3¢°AuÓ9u•(Ą¦ÜŸńK8k_‹ąu>ĻR…›>"&JŠG3Iå¤Õ4Y5}-’au=£Ż·Ć{˜Hē”õ‚¦Óī8į¼ž£f¹ %e|É(ŲŖ,Q·e« Œeū ^#ōž©»wNp/“¬k]R}>Ņ; 6+{kZČā¬c65­Šł­#»÷7–k© \÷¼¹Ī<¤œITŌļ1„M÷2Ķ‘(ΊCtžšŹ§ź%V“^=UļiR|¶ö®³aā[öā†Hތ§“z<šE³‡šĖš{Gzˆ6Ž(”ŽŪ¶yhF^ąjM½l©÷BJG/c'ÆÓ”vђn<-vÖĘVE¢Hīō ‰ŻB²Vŗóe½J‹izŽŁ“m—Fßi—Åķ“-móZģ® I©"»ŽåŲ×Ö°ķcŌ‰ kPŗz2ĒZ ³"“i/,É'låä8©!'*åŗ”ė¬ńUzÅPŻĄ p„0Ų¾`ŌęŌ&½āŽ{Æā×f;;Ų%W_^¬Ń耀‡“püį¢,^W♫™ļ•Ē ŚCflށš˜†U3$ę]åŚJQ Śy!ąmŠm|Łó67Zk÷R7$q¼¬ŠŅ{A_ü¹ü¦gĒ$e‡õ  ģ¢ėT³ffiÅ<~®>aedJtTŽXrī“w»¶»#½¢«ÆĀNjėf‹©¶UįcŪF™¶ī[E“õ7.æĀœ/ą‡ZÕ^Ēi¦G/— ¦ÖŠ5„ä —W‚č:Χ|÷M¤é snŚŲė9, ‹9®Õ/p`&“ÄbN«ĢƊ׌ÜäT;Dųi¬¾zkĻ\©†ź,Ļ Uéh“pčįÜbøYÉQīķōÖł›¤ŪœÖś}‹<~'Wń\žź(ēŁ¾A–āöž\7KįŽšDÖwÕZ‹‚9«5ʛ)#ȼŌöƒXq##Ž$ņ±—Z|’*ŗl“&Ä×`2lkWkŹ ³·Ą·h —éu¼zuؑšXX¶āFåĢ#Ā›’†jŹžP=Vś1ø–/žżÖ‘ŗ¹ •' ) ŹšŚījmQGĒŽ »c\ŁĪI|/5;)Žs„Ļqāö%cŻĖFćlS*žŽdUŹ=Ė>¼2:®Å"įŚƒøvŽŗWŲXŲiĶ62ĶIZ¹ *M6P ŠEóĢżBņć]:}ŌÆ”[5„ōĢr@*iRM*j²©µĶDM4DŃDMk™g @!Õ_ÓŪį!Ž~ͽŪĆŪ¶‰‚Ó*Ŗ{ˆ”ˆoćĀC~łv¢ŠrŠCę1Ē÷Ģ":"ųŃ=9Y÷““YŽcleķainFgģÅŹĖ>9qwÄI:¬ŽZ·K a°)?"uLŲ#'­ )Ŗ_PŠ((ųJŁøL–įß$tqµŌv“#’›7õ+š9§†„ųwć’ÅŽ$Ϲ3’øG\“„eīCy'Čxs3—ėNZ-°8Sž6Ļz}YŠ{0±ņoĪŻ³6 \¢Ŗ•ĖzxĶ.phšÓ wcłmVÉ®N[¦\A=ÓįsDLs÷fpĪCp­q®ĢćÉG•¾GåoŁ §%į2TˌvŪ"0±āźõŖ–åG“kDżÅŒG%|µ+znŻz£%ųZ¬@2ž°śZŚćŌ lž’‡œµ«[‡FWĻą#½·n›4ońrxn~ih|Bܕ4Øß\)Š®¬³\šäŚ5f<{ćŽyBŗßf>GÓźœuĘtŲ¬e—é¹ V~½HøØÓ7÷`kŽY —ŠŚ¢°ŗ„ķėSĶĮž d,ĢŅ €kE @öŠģŸ-~RqŽ™ÆXńŻ“6Œ¶“1¬t„¤N@1ÆŖ Ā›ÖTrK eÓ)W_©Y¬HęjM®.RfĆd†^yĝ}ŻJ*T¦&I( •ŒŠµ@„Sm•8÷ęÉšń9ĮŁZé$+ø Mz((¾“—įbt~V:fš’ą+†P15Ųp §ZÄv¬ŽĆĪ”ÜīWÅøš®6Lä1ž%R‡nč#yGę“8G®“*SEńeÜf+¹b;[+ĒcŠĆo.lŪ²ŒÄx½¼ü©·•ķ–>øJĶyč2:g1-2|©9zńĶlr,RPŽ‘U`ŲĄc,±p/o`·c˜ē喛…Og’¤-׆8ni^ĶBé•ejƜÖ>aēV‹•¤GĒ<—µ™'Ķ’h/šB7gTfš)‘4 Ų6?¬ąšĶ _Lžŗ¦PąüŚBérŹŁź 9ĶIē<ż «G`«Ž<؁̮œróüŃć ä\8YÓÅ«Åp¹Äź(ŗ€e”曦ć°@Ų:jÉ®õpSu%q~ŲĶę«Ļ¢[­^}#_qś„Ņ¬Ńž’‘ˆvŁœ@ä »ÉŌ~š©£óƼs)»āqWŒóӝ¤0qŁÉˆM^įŠ—•ĘŌÉ«K°mÜ Ķ ÷é9”„*®uz™C·m(½ÖM/5ÄÓ¢ø.7Ē1«· £ŒB¼ę„e_R+MM4E |€ßī׉yJĒF“{^›x„: ęŻ>›ŹEróu€©Z®-Ün “%~]Ą¦ėŚĶeæOOp³p÷Gä`«ŚŅ@å )} ڽZ.)ą—c]ō`²¬Ī¦h&,b[§v‰!B‡”ņMŽ(p›ÖQN§9Ī&2Ŗ˜ā"aęHłžd“#Iē]ķl`5¢Œ”‘…°dLß?'Dć]AÅŚZV¬mö÷Ė–æBĒŽŽ£ė¢ŅŪmvŻĆHé$ŪØ,!#.r铈(‹vDĮ5ć¼8]³{õ[“ōš7HYY_j’˜tŲ󹦎qĮŒśĪ8W˜īe?ń_ˆJõ¾“öךŻd¼ķ.“d,K“Zq¾*dtŃ1ÜĘÕ«õyH{-‘6ż@«HČČ:\C¼S@-£ŖHĢ,cdlH“=礸:ėx³ą½.&ƒ©½÷hs#Į­ ‘ĪēyÅbĶį^£aŸ¶aõ×#&³²¶`ßWÆhĆE,ąŽI6)%šŁ„$dŠ (éįÜ((˜żĄ&Ī×dž1ÜfP6śīžZ”v,IxɲŗKK‡D×Fäk²ŽJ’@ēÅcŪ>Īē®dcRÆõł[ƒœB'?cēźÓ Öć#X[šV–Zķ"īEķ"%ė‰U™¢(ńć_JKbdJiķM+5 =Ä08¶F8Ō€yī" .sÅŗyӃō]PµāhėĄ ~Ż€š‡6† JWbģĖdŗU*ŌéĢ‹;4Ę2_¬H]å¬GTĪ#T£bUŸ{6+6"ĒpĮ¼2&_½2œN˜nPĄ5ŗÆžYß … ™K²¾µ„;V7­\ł˜¼²Ē9ńk#å ?QĖ8Ÿ)LåÜļd¬šć7Zž4¼ĘßZĮĘęœäā»ä%'k Lčˆ0:ĘeŽ™ *bŅJÜtmźÖé·wY@h4h55"›°Ā¼„^,ęKŹ>¤¦š®ŹQmŠ™‘gćų{ƙk5ęÖķŠR AŪł½•ÉXĀX”3®{«%\] F{¢²f8-āŪÕ'ÅĖˆ]Qēæ;Ü×8ļt¦Ęäxæ¼Åzę|ĶMłS’½ć>ēœ“sa-rnNž§B™µŗČ?7į¹#‹ķ–PźÓm*ō š «y›ńy…Ė6{ “?+[įzÜÓU˜°”a¼O‹£”kŒ6J:ŃsØŪ/ĪäŅļ0–DļŃ\ŖØ‰! oÅ*ųˆoX—„,Į›™œ<­¾JÅ,ņĖ`»dGL e˜š˜p«é‰‹,ņÉŗ™³Hæz¹ŽwnÄĪi‡}Y{³ļW˜Ü¢ƒb®EGJGŗeI$A1äA3™3 Oŗ*‚FM@ ½J:¢¤*ö…ŹĆęlŁ€)÷ŹĘ#Ģ ™UĢ ą²…VtŃ1׆5²æ4/ՍV.Œ£6Ņ+¶TÄ]1pČž‚¾¢E)Ō–Ö×22Ićc¤Ž¹IĖ]“ü¶ā®Ēssoć‚G¶)Ó56VŸ‘)¹ć«É+ų?ÉŁU›āįĘŪ_^Ę?ŒĄ–ŪSŽy*ĪN,†ŠÅy¶ĶRA³ĮŸbŸsÉ0P^$į©Ī€Oiös_{MhO%vÉ]ŖęV@į]„özBōgńßå_‰ŽI©Īžį›RõŒæTh¾Yć†B"5ģĶ‹, ¤ą%««)µŠ®Ō6žŠ3ø§E9TMcY|o‰å’6ƒµz× Ķ5FO?™rōøOZ®RŻ‘½Ļ+gü]R"‘O¦›™Ōņ·čµŸ3Uя5¾§ŌĪDą“eݤ”÷( ™$Fé]±­'°UbZ&•r"&¹±µŲØŲāōčĀ’Œ€vŲ7ĶKŖ×!s‹Ü^ļiē׊ä4T¦ˆš"Ą­ŹVB+ń.ųätĮÆĪøż”qR0ō ™L[g#$VÜ6¬¤VTz“ÆMRWBįŸōĻüĒy—Ø>¼[ h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢/$OjĒ(yęąÄ)=[)Īb”wluS°zˆ_Ž VÅĪ>b’Kkžkæug[W*XŲēÖa0Œz²ę¹śĢ~/żu¶8>6¼lsAķ\ĆS‰jC¤m€9"‹<Ō†ćõö‹Ź¼YČēüpåäUV_QĀՔĖ!Čģ|ķéå%šļ7a4¾K ČIø8¬ ³3hóø2Ė Ī›JŻÄš[Dė‹—²8*ē8†“RI•5ĆSjF„³…ÓDēbćŹ)±­7mP±šv"IV.¾¹‘Õ}jj…|qā?•Ö'HzŹ’ßI’fķ’ē7pŸk†A% ‘Ģ·źP±:_h:Ō‹H½¶¹–?i±ČǹøŅ¤HŲiC¹oŗ·Mį4j¶Ņ1Žö\ꖞ_UÄmåé pbF_ų‹ĪÜmņ'‰SāMS#qo(`Ø®VcéŠæńW->±_ńÖEĒ6lWŸ)LĄ0g!*q '€“Qœ .'ØoOnŅn"‚äųĪŹĒ0ŠņūŗVĒź²ęb-fŽqØ-8`HĄlö°“YŽ„ĀX’.FDYIcŽwZu"[4æf[Ōå‚ŚÉ#8£Ļd儎?—rQ2Ė–1v؝p);ŌLŗŲEŒsŃĻ4mkV=Ł%\MziE֛§ĆtÜFJŌäys¹3<š‘¼å#U[EĮÓ 3j³Y- ›ö¬ŸŁ¬éL:жe“vɶn‹—ŅұÖ+cHČ5¦ wŁ¬äż„HLŠgVÖD˦Ē@ą 5£žī² Ø9Č®ģęĒ w­Š/ 8ēVŽ‘Ż$‡8]ä»×יł–o޼KĖĖŠUå¦Z»q®‚ü S³c˜Ķ–¬q8šd2”ózÓe_·(ģ² |%„4Ėņ-å˜īcÜI_1j:Ke曭=Ćų"éī#˜œĄtTŽ„‰®:šß&åJ‚S9„‹„¢G/RJĄķŪ\ml¦ä°JŲ GBĮ܋—³ÉCän²ĮźA9Œ^qwØBɁø©{Ķ\F4}7° ė·é;{©Ū¾KL­†&ѵĄ=Ą{ īå.Ų0®ÕĀĮāüż™3qųΤ$µ3#U¦µžZēāB‹jh"‚¤¼¾uą±µ­‰¬D«©3|RĖ81Œ¢Ø¤ŻĄéu­½·ĒHąėcģå8½Žī8¶ŸJ£ÅnŪHŌÆu3¤d1]3 …[}źŒ›čPśĒ ­Ę\ÅÜPä4F1˜¼”}Š}H‡É—&°IÖgM_sc{Uš€µVŁ:{óˆHGFĪ­é9j ŅMTÄOLrÅyińQ4³×,-'0Ø (qzjś\š&¢,Ÿ •ŽŒ=®¦SBKHp©`FåWŲ¤Øv²RČĶ£“Öc؃–®'h‘Āh“X r‰‹¾Ą “Ųv,"XķėžįĖBÅ<Ń£R©2Ÿ3 –\ÉĄę ńĄM 7T§Š¬vx›ŗm•.ÕLxD$å#œ5zfŖČ) 6=yēC’*•č5ņ£qµ•«“ćw'õ, 4ņԌb]lk‘¦ˆš"Ś?`ĀU‹Ų¹V ebäŚ8’‹“hŽB6IƒÄŽŻć ÓY«ÖNŠPÄU%dŌ!„¦ŪEč%¤9¤‡ „`BėĆŹ\*/'ĘŲ¦­m»P¬4E2d|Į}šĀß9a!si ŽZZ6"«cø­;pWŠMv`#TŌÖ”®iĘńu ˜Āó‹Kƒjy[Z ņ޵ٸ7RŌ5‹'G$RĖ$8µ„ÕŚź³a*fųzÆĮŪ9 ņR¾Ā5µKīQ¬Ł*~=ÉQ­Yš„ć ŗ“¼ó"b$äŁÅÖ§gź”A§zg0•A(k7b_°ĶRń( h 9€Ž-ĆeWYąŗ‹»‰£4·zÄ ‹†RwTüv€³Œ—˜ßhīU¹¤,:`Q‰±Hį\łHŽõ•n”½>Ö|XZ݊ø¢ČzÉu(¹%Ųusš]N˜Eՙµ"µ {ū«@©āƒ”²JprŠŽu|¬|żāō/åõc ŗĖFJÕ@©ÉaŚōĶĪĒ|ÉV+Cj\7¦Ō¼\“…āVŠģHÉr5171żXfv믃srOJœĘ€ V¤ć…\ŗŽ›Ÿų›dń-jČ .q4 Zįs_!-Yļø=–PįF`ćī7É|XäF5­­ČX¬tYœ±%nĘ׫|m¢“YŸ·„d eR%$Ói0°®ŖÆ²]ūn¼=hŪFJĻ’=Ťå­yµ\3ęĘ”>§mmtČd‚(r—Š8“É”šS õÅH›Ÿk¢Ģć9źüsź żQķVŖTĢŅ)z‹ų… \Ą¤“¶;6GP[2fI=½1(”65Ć[,Œ”NŅ|PģÕßZÖ½Ŗ›ńā/„÷žq–?%įŃä3qšRČx^DŲgs}^"NѐktÜE]mW澙­!\­S-fĢĶ ˜”©÷‚žŠbZMß“ ėĖöI-ˁkHh*NK·c›®ćĖDh‘xśzƒO…Š$“$±¦ ø»ĒQĢ„«üŹmÖʔé*üR¬gŽį3•M.Óåź¶½‹b¢óÓüVYő<‹ā—­ķš—,Ӊō×xIķZ~2~ŗ’Ów«ŖŁIqYńT$ap‘ŅSÓQ5Cå6ĘÓDpŠ/)UZpżŌÜ5zŹžĖO¹Õźy'$ŹFc+Äåvi–?Č*L$|U¾­Rŗ9dnbŻš(8u‚ēt’+€öi¶Ż8rx˜Ē[8ŅG8Ļ†ĪŸ*×õ&9ρźŒLŖxmĄ7büÆrù҄ōdh™s(X›„e©Ō"ŒJõHõyŚGōŽÅ?˜>j©Ó:}Ŗėl›EµŌą{©%0 b'8ę*!·²ŪI@=^NU/¹ å >r«5p óö—RĘØ`«¢ĘŪ“4 $¬Ļy)ó8Ä).m5ēhĘc¹wqÕõ™/ńé™)1*.Š3vČ·@ś£„]é’äømc;=“×øóV]ż×ÅiS|(.˜¶…æHEM7į]‹'ā =®’n£×7M4DŃ Ÿ?oų›üt¹r—Ŗš¶Ž<0){@Ū8•sqŠh®ĀŌŗxCļķŻĆaėŖNÕŠøgż3’1ĖÓ«^-…4DŃDM4DŃDM4DŃDM4DŃDM4DŃ’‰DJNDó“Ā#ź#f`Ü ø O²Žaßā‘(oæ¼u[7łż5§łżŃéY×ÕÅŹÖ¹ŹzI²TČX'~V‡ååčhv…łŁk^G"-“ČSѱIpXs.” eŽ$II艬@YÖ¾&śféÖ£Ō3Īʎ@O}6ģĮwƗzDN”u‹Ŗ|]ŠyD{@[iź• grīZ“ K²[ņ=Ü­Õ§b8ˆpœ¼L;U®tW„dģZ2iÜłć·(Ę“VX QŌ¾ŅG¹ģm“g֐š0sŌķ®ąO"ź1—ŹöE ū‰=–W”ɼœõr%°_‘(lš‰ī,Ī[{V±Ö\ŃŽ1ž£„3$¢. x̲N ؕµ*i Ŗ/š7"„Mb‡¢.”WĘÜšģ–ćCČvUŌäX¼ęĄƒ¼r…&ÄQ^‹gڽĪ,eŵ>ō˜0eÄ;óP©crńuĖœM‘o’¹ĪłŻ:¬„Ę>ĆøŽ­źæ%oH„]bƓ²ŠˆšDķ#āZ‚›vœH!¼D‡Õu6³eXE wcU#<saf< ó;"c¼”fr<¦Ei!ģŃ“*¶E­K”]„,.rĪõ«Y6ĒŖ™·aWUt›Ø'zėKK‹[c f›OŽŻ±Œ¾³˜E]–Jlq® cZ*“=Qւź s-õÜŗSœäkŚąš2}  )8Wf\–”éRg6×3śöĖ’ćA¼Ō1ü·‹,‹„#AØĒ»+3nNą%œõ½I*ą((|[‡Bd¤1˜ņ€$“˜m8ģÆ&å²Y2尗\ĢŁó8¹®kC@aÅ Pź„½[._ĻįŚ³œ³—#/ÜĘ\h¾Įęš˜śū÷M<Š›2,`±v@‡jśzõY¶I»Lķ«­ šó/"EīLT»fŁžó¶I$ihØ­9\ĄG.åcS}¬16īīwƻƯ•ŌͶŒpŚąNʏk”a“…VårvC6-ęü„ŹŁ÷ 0}·Ļ£–³]źÓ[+žÅVOęÓŖ¹`™€‡:` ‰£®„kmćģ_ńmēĒńŻŲŲłIÕʝURĆWÖŗš" ²nP aŖ<īHÉö˜Ś}.ø‚kJMICŖįB Ę69“dÖ15*čåA›&©*éŅĘ&CčÆA×2ˆ it®Ųå€ē][łÕä;"rĢ–:¬'-02kitVö¼€DŌ$ó+HG®r$Čż€tėMU;4Cē•v°zhć¾jąŻ‹£hü=ž÷}ēs~Æ?)ŪŠ±ń O‘@ ƒtĘBÜr˜™‘ŠbŻÄ ‚؁7/iKé€tüšĒ.§JŁCkŠ©iŗ b阩^x¹ąP‹D¤@ĒųŠ‘Œ¢@"§h€ˆ³·UøćR¼,hWxæ!äü!*“īȗĢG(ķ37v½uäTl‚j‰éÉ×ÜŹĢ‘@O¹V†9 ±ˆb˜uSdxޱ.lm.Ū–ā6øs…Ū_šós'4ēī>;ą•ĖŁ2ąž3°’–Ŗr]dń›ČKŪžŲ›a|©(śÓI„Ÿ*ÖēoŪ>dVU’ ‹¶j”…pē‹tūkKßÅcž8Æ&Ą±ķŻLķ\FĄ -Ū°‹2źų`UƒxĆ©v–²V*Ę£TKhÄ9‰¢M\hڽ*Ćj–³VĢT‡±ÄœÖ?Č ‚ķLŸ@XĻ܀vč:Ń£–Q3ü)a5—†ž€öł‚É SUų‡|›&µ„‹²*€ŗA‚Ķ^=žf‚®[³IYr?t«ą#"|n×YU)ŹN=GĶ«œ*ŚākZŖaŽąKØ}8b¬ÅŚMŪ/Ż!é5Ō„ļµč£;ł§’– F™2ōÖ:½U€ŽEģķ¦Äń« Ś2Auϰ˜JÜCé wµ–QÉ! `…¤’h ’wøżżĶī¹=½£łL§ĻM½kµœł“łÉž@gĢĒ ‹ä5•:&:Åy[5N›ĄÕčTŹįZYkńKBĪŁ]/1—~ńź]ĀqɈr‡|ŻŗįĶAö¶š¶®č,āis­ g$u ^ź81€7Łń9jŚ×ļņŪNÕ“=āŽÉƽqĘW»#*jŠiWм”;±“K]õFᆸC(ЉÅJćŪ\~e”Œ˜Œ)Sģc$»čÆIŹL™TMqA1*ɕDÅ3”¦/²›å…ū.쮸†Žņ7U³3ლy@ „TŌTA¢éSĒÅwī†x“Ł!p”c¼Ré"{Ž"‹œI²łįż¾…ÄŠķv…—ä†WģĢ)&y³ĒW!ÓMūyš"±F{Zŗ)å®å£ĀXłWŸ&ŌÅp’ŖŠżwĆ|W ńM·¢Ü¶g0 ķ#,ēsW” ¤ģ+…ė¶§9ņźp»įŖ2ä°ś^¶j0>šT’‡Ą˜Ė!?-yłį3(?RƐøR«Īnųč·<0¦åĢō¾“,žĒKQĀĀ 6‡*Ī“f© Ą6øn.-šžoFō„q ŒēÓ®r<żŠõµŲ¹WĖÜū?qm;‚žU.Į‡æ»Č'ävIsāó`<_‡į!«…–ØK*½Ž°Éݟ"NŲžŌE%AVåTźC&-Nņ2KŠNßQ½ø¤o8ŪTŅ!}Ć$ŒĢv4²1˜“·ƒŹkĶU7äO]ńé12öl…™ćŌåSīAr7NW™*ņČŪ/r“Ó°4رm]”½Ļ·jÜ­‹V(īSœ…¢ļüią’ž“ϐbv­FÕF§ÄĻÕu5“Č>Ó°­9N; cxpćvZČ\-ĀNY\§q“N¶e æ”ņD[ŽŲ„NvĢ U±9+¼mš.Ɛ£NÅW³#¹¢!c\¤×Óxõ‘ēš›­©L籯1µ‘µ¦ T6®q„+M€WqĄ/¦8r ’Ą-b…ī…“:IžńBźQ¬mAŗ•.”£E%vA…ÅƾģĻ#(µō”:©(qU©žˆ'1Ó"‘Qēˁęķl!øź #FĄ·#ĪҰmĻOvü׊_äAŹl$9! ’•¤äŠ1­.=Õ.vĢźÅY„Żč_5;9݉6“;o—ō"i&$@Y±¤ķÆć³”†G]=å„ģ&¤8 0ąGG&kZ¦€ķbŠ ‰āŃˆå(sI«Y#q’£aÄoŖ| g&֭ͱ¤ÄĖü‘š-×4Z†9+:ŻEŃlŹH¹d#`8±bĶDUZRIĮŌlÕ§9CįŲóī0NĻ6ˆ­XŅē>„ŲR½g MW/ŽŽķ·"ČÖKēI3sV”ꔌv ,šĖę¼oäF"›˜iŒą,U^ø¤¢eQ3[<®“™ęćā!k^sbNa\h@Ø+A·‹R¶¶BĪåņD2Ō4ø\PØW#ćFƂ"3=Ź ĻyĢDc9ĒüS=ČkœÖÆXķ|Ž‹¾ĆŌģt,Ūģg.굇šJ«g˜„‰zé›&ĒpGdūAų°uŁdg6žČHsƒbHØ$WR€‘RŖ<8cÓCē5©nƒ£i–d…­9M˜ø€HµUl\—&y-ÉceŽGĢįJŗ\4Ź\Ÿćķ?ąČ‹ųk%†] „ZÕ~˜Čw‹"²YX²$Ł"•AX@ćŚ&“o-ÅĢ9©+Aõ©P:—ł­ŻIxž¹dyą~.a4­wW°m§B—:•\¹e‹Će@Ė-Ėüŗr~·“ńīŠPŪ Ę'ćHŪKŠ!»w*!rĢ’©aŌ@Ą=K°Rv®‹Ćqxz[]½īs»č<‹"2×i‚®¤cāż•›gpڦ« §T’–bEDĮ[±ęm "d–l„txG(» ‘n!oų•ŻEXpmY|`h暍Õ]˜ĒPŚ TąŚIÅĄ4Rš”šWǽŚ+n¦å``œµŅT1”åŀ6Ž“1hĖ A. “ŃVĶBŗQćéR(†qdČā•«L—$ź.*)#WŌp¢ [*ÖXŽaŻ7tŠS1›8HS ·¢Žź ÆĄ$ųS:7TRŽe+Ո ļ Ū‹YķR*I×½.”å'’¶Šm^‹ŗįøćF#ē…>@ń—’Ō¤īų–O™†>‹.-&kOĻIĶ–Śćr…JįS·Mŗ{ ŲĄ²T0rFQ3ēĘHh#jĄ ÄnX©Čœnæxśå.Nįnjø%ghĀ#!ń’&J&„*Łó Ė/$Ā6ĪF†™›"T^0RŠÉ؛µėR»L¢ŻĮ®Ć:عÖ׹R•4Ģ9zFžÕ®jv¦7 #Ć;yæ7"ū²Ö«—JüµRŪ f¬Nµ; ˜9f齍l}„É.‰·ķQ1ŲÉØA*؜ä1L`ŚeŠˆŒS¾'`AÄ~]įE5ĻĮģ$¢ī!Åcīe·I©Łüü–£µŽÆÕ8·®…Djöā9›s¤S6“Ü 5q*qkD¦ˆš"ĮE‘üL¾8Ī‚>±Ķxćf ”O²+X¬Č9[`ŪŠns©æšD»ūµI] †Ó?óę^Ÿ:ńl)¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ¼üF•CņPN°Xā“TMŌŹ(µ÷!*uLnŸr=~Żõq›×6łż= ’Ø’Ż•Ņ›“Å6Ą=¦l>ĮŲwŲ źµĖ¹‘ĆJ¦;ā÷!lxš¶ęĻ”r6M†É×ģaM¤„’ŗ\„IÅ"ŌA™F"„QßÕ]‰ Ŗ4"‹8»> ohČpŖč;Ä7šå¤7’6+HāšŲŠHiv\­.åqŻ\ĄmĒ“‡ų‹Š{C‡9/7äYŗ¾ę§OŠ4ÕĮ©Ł¦ŗ aŌbS5®ŠAĀ ¬lC1"%I4Ōp+,"!Ąõ[ūZL““Eg‡OŖŽw{Ļ;ÉŁŗ‹ķ}LµŃb­©ń.ŽŃžWYŪčßuƒpwÕN’qŽ’U€Ēš³(ÜPł^<†2 mĪ”œˆˆÉšöConś…ų«µŌSŸ-)A^µUŲ0MV»˜Ōė²,R‹šfSØćÖLDÄ4™U0„ŖgWŖ‚ÖšLĶVūH^Ģ­<¾žUC.ęcóøŌrzHSq2و<YøĆkNN–w­6ĖÕ¶÷‹|Ū¬eMži5|>ožs;ņ“–(ŲE¦˜ ŗ,[7v”?L€$)ŠRļvŗŒ7>5-Uą:ÖCĖ3æŲ€0i4;I€®q©hr’r 7IiĖu•¹Ür7Ś2qp15!_ ćłś-w<'b™Bå’ė pØäØvFM”"Ųī&æUˆ’2HBÕՉ"ÕĄ¦ŸĢ§%ź ķ‘¢›gipĖhģÖļf`H”5Ēø×7l_.üŅ7ŃqC¬o[‘ŠFŃRMH;ėACČčŌ¢ē ¢&ˆ±£Ī>Ę ź|ƒ`£š¹Ėõ–łXy¹XzeŅ:V±q‰“Žź,U ėErYĆqk&vßPķTé‰Õ\ā½>MCA¹·¶f{ĒF@„IĢÓ@M)Zcˆ«²| āk«>$ƒO½ß„ēŃÄŃ„¢•Čʃ`ę)§Ö.äBÖń6ŅĶX p+•‹·p„“dŸ‘Ā.•IGÕÓHĀDąT¢Ā!Ó_+ź—wš{dŅčŽI"…¤Š * q5¦å÷›mk|ÖŻø5ķƒ…p$V„S ūÕņĒEͼsČ­²osküDģÕijT¾3¹ĀIę ņæ/2ŚĀØĄb¹»|4V?•g4Ų"¬Œ@ō@[ŌžŅ>b^[Zü·ĒD)‘łü9[AJ9įŽĪ)ļzߤp¤}ßµ·”OšrC˜Zd„‚kVĘ\ qõhŽal>eØO٦o™ƒ3[ĻÜĮä4ōN9„Żf šVŚ5³Ė·Vµź˜ŚŠÉćųZKD*«ē 6®›®×YqœcN×už8×ķt}9®“Š£p|‘±ÕA‘Ҿ//>«Zp©ā µ»=+ƒō;Ī!ÖdmÖ²ę82G47Ö †2&T†ŒIŠq¢ĪN8¢CāÜwBĘUż¾‡Žé•ŠDQĄ½ž³*¼38d\˜ ²ŽĮŸŖīĪ:śE|G4®žgL’iī$õšŖÓEm4E…Ļ/9ć¶ĒĮĒūIņ ģóH,Ē‹ØB"05©ņ'c­L^§'ÜĒ×ÜUåŅ<‹G- ©"&"Ŗ$MdÓ6­Č[J9m\3iń"öß'ƂZģĒhĄš]œu“Ži#(«vänWK§p“cØŖ>¹€ ąÅYb¤ ·*†ŒąQŪŪ×X†ƒfÅхNŻŖ@Ą5žnE>²å‘‰é¤“FLP!hTĆoēJRwn]ƒ·ØŪļ«F›•Į]댵­T§²øĻ3D†l,mÅ =zr™$ū#šČ¼fŁŪŠ!€Ż„0(b”vÜŪŌÖ¹ē(T¹Ķ`ĢUč²āüR£Ōr-»Ł!qĶö6&J±xQ“\żM¤ņ)ÆU­•+%f-i*PE'®[(¢ƒé}MČŃŻ[K+ ŠF™ŲH-Ä8SnzŖ¤'Óu kf^\BöŚHkšs;=f’y 1ĆjĖē€N[Ģq‹™±\sq&-0‡2%V\V],©!+<–aåö3¼Ę "dāœ_YA­X–ŠńU£NbŠˆG[ć 1—Śi¾žęŲV»Ģuõ‡=+˜rcʰź]pō"öļZ˜8ŹC+¹Ä…»9Ż]”f»ć˜*n!³(Ęm^§Õ0[$ŠQż"ČŌHB¦É³b›aDēÜĘß_,ŻÜĖu3„•ÅĪs‰$š’w’wž•Ś"‰‘01 ÜĮ\żśūz‡QėŌ7öūĆ}µŠ®Ø“Ģ,WĪ8ŠÕ(O§H¼n€ĒĻ10µ•„± ©MR·Ä=KµfVJ”ų é«’ Tķ!ÓK­›„ųŠó†uĖ}VĶÄ:'‚į¹ĢÆ®ĒrµĶ®Ä(½cL·Õ“łl®-{HÜļ¢zAķ ä8y–gs°ĘM¶vżó›©}.ōb„č‘[Õ6VJ‘rtDæŽŹžĒ]rą Ų:mÆŃädŃ6hńķTwšĘ©iš:ŒÖ›G@ŪN­ŠI€ĄölŲÕj=FžZWä¬x6y»*Łīńp֌ks¼PŅn/_1„"Ö.9 Éˆ}Iģ½RϦČzH +°śū=Ū%’ÖHą9ftn <„ŒjŸįk»+!“»Ō[šÉ“4¼”®žezü0ēč›?œĄāŖ-Æ$ŠØ\ÖŹ|xŖŪjmābįaĮ|k…+-Č'e{_XiUœė(·Ø2Ey®TA6ĆÜp':Õ-ĪyÜÖM$-‘ĄćėŠ5ĶĀø’ ¹××\?Ø2k ю’Ś+§ĀĒ …˜¹9©źµ® c³œ¢‡qŠ^ą.āÜmūKøķÜm€G`÷ģØu³+ŽīRwč9‰9ĢmwĘj°ø\jIW²#xe§¢k3.!™]£ŠÆĶϲ5BšÕ‹–MҌŌ(¬Šb rXÄn kĮh5¦#f b7«0Jé˜\čßp£©R p”8;hßEÕÆ›Ų·Œ(å>9岄“¬vęYĖ.T ~āA×ęÕ2łz·).ŻĶ•7ļ Ö,<…i ¬`¢ŖŽ]ÆŽ.}”——¶³é±9‡#\K‰ #eÅŲW”kŚ„¾‘¢^Ūk÷ ™×šAFšŗF“HĮ”vZTāUõĘqrه81äėś“…2‹]Ä2XĖ ³°ØÅ+½Ś+ Ų+÷¶BµBĘŗ‘oT…PõhÖ0Qjŗ]Ł“+§‹‚`ŗ †åÚaÓķ žźÉ)˜€„{k†åĄ¾jń·ßĒadŅ ¶Ø$Ņ„ŪĘa²€šSmj„޶ÉDM4Eł)-ĮĢ•jw‰9@ŠĶvś5Aķ²Ē‡ļ¹fӈØĻŠÜī,6K1¬NOćH¹˜ID»Ü¶Uc‹1.ŹkāīŅÓ/ÅKyŻ•¹Ü™Ü‚¤Tó-‹EŅø–ņ7Ļ”Å;ŲĘęvAQA$o–œÕ\?‡ī}į§Rul(µłĆidŻóćG—į<<–^ćÜ%Źv¹Ž-µÕefźbf«ф£SNY3Ɗź"$TĒÄTַ޾…µ²Ō‘ˆi;kČ Ä˜Ń}SĄüE££Ć„ß¼7Y…„® ga”ļ-©nÜ+ˆ]ؓP TÕIBœ†)TIDĢ!Č`‘DĢ%1Q‚Ø5ŗt¬\ēō8Uāī•xäÖ<ćī=äN|ī“ˆŖU¦+øČYÆ/\WĆcj"2O%VėŅók倐©2mš‹.A0$SKŚ·PÖ%e›ž÷B)^F¼ņžJŌÕk:œŚ' ZK«˜āŽjr¹Ūh+Z7{²ŠS©C.9ć{>.ÄŠ™aS²KŚņ~d±¶³¹ƒ*Ydļ™ó(æLocœU£=€5Hmtø£d1¶(ÅŠč/ŽuFM[S›Q”Õņ¼ž’Č«č™=ELa9Ź@³øĄ]’³ŖŌbĶߊūŽ*¤qgS§r -ņ:K4gź¾;˜øWćÆ×zķ·([gYOVj6°ĻÅERŌ"ζUL;Œ×T««éńx0żFŽŚT÷¬•ä9ŪujŸ33D£+’m­SjXJbv(Š‘&9zŁ”¾jĒ9ŽĀ!ƒ;— śkŖ¢`E•$o T •˜AG›!Ž%gjœ²Ä¶üc`Xł üµ"'rŻ $šœęq (x©±XĪB½AVŅ1’LY®śj¤+¢Ø-ų™px õ‘äUR»Küw‹xŁ+ʬ³‹(ˆ°Éµ>gk‰²6-v‰4Ćܙ‹·Ż¢°Ü«©V-%¾sYl31.#"ˆń,—$¢JyüF—s$Ör’Łźq啕>Üeµx‰i;} v…Ł8Ūjö±ÅynōÉ3E%± uNšWSź‘°×®w9ń;<%Ė,×ā›±a["W Ōl¤šÕl‚É t3µHvd”Ō•jš[ VĮڽŗŽøjüźzµŪ‰2dŹāw¹ž©5ßZV¼ėDām=ŗf½shĄ^&v¹ÆõĄę„iNeFS¦ž³Ś” ›ˆńE¢€1ÅDʁĢ'ų»•ģ6įö†¦\(T(5 {kT®Ź‰¶öĄ€oŠ7]TŅ ’Łč6”Ų½æ%J™ĒŸœyČ֙ś|UÖ+4rS'ŁģNP‡ˆŠg?®rkX„Ÿ¼2-™CĆćŲń;… TĮ³`PG·®³ĘŠ<šš®“>G9!åć–ńY˜1ŹćŽ#Æ8Ē~k|‚ļ/Tq<Öģ¹=š»ÄĢåwmY*4Ą£ąY5:ą‹§N’OsŠt1+<{¦RĆ='ø(kė܇$N”›Ļ +W[”a~;Õ®3•ZµTš“{n¹ŗŒ+’ ,ąX8rįū£¼téC‹&P ^Ņ†ć­Ź+k2É TĢźWpē'©BŗIīžÖ¼—?`ėSŲžR‘‰„ņ•Ö(ńy[’ÖSękĖ'I_×!dŲ Ēć÷!ŽO¹ńi*˜ĄżĆÆ·\žņé÷·R]?Ś{‰č‡P Pz½Ėf¹Ęk# ē#ŚwYīMŻc(¤ŃDX•0‡āmńåÜaŲ/œf)@DD y=š”=Ą'0ŽßhŽ©;WCįŸō±žc¼«Ó÷^-4DŃDM4DŃDM4DŃDM4DŃDM4DŃ‘׈EĪć;óU„QÄüē)7ĻyČF0D@„1śn#ÓU³jęß1¾āÓüĒžėVx5qrÅhy;;€³œ%\ž„’cdčČ$»@ž“£źTŪfh§Āʁ ę0h³tĒFĶJŻó}Šž2z3ŲS‰rsy'üȘ“*"¶:QŗĆŲźmēķ™U‹œ5[ÆŌ€¶ÓI6^`„\ćÖq+é ėēŌ*­N ö÷ė‚߆CwwÄ÷S”ƒ@ß\“‡Ņ96ÆŠ‹łķ-&·|7„3*_ź1ś%®ņģS"2F:n5¤Ō$‹ ØiHį„Ä;Ö²‘/ŪØ&Ü1’b«†OPH=§Lę)¶‡¦£œ×1ŏgÖ>¶d‡ 1-–H¤7›¾:¦L3¤Ū¦ŪŒŒ“ĘIPą,ŹX LōĻ^7ŒIšVP½”¾,<7«źÓ˧X#d2‚ā˜4:ŸH·Öē¦ÄŌxˆųjĪ-k\xc_lNĖWZ×äh͇5F;6pż^ē®SČY!¼d^@Ī™FS*ŁjŠ’f›‡¤&ā½[ØVé­gŒ‹Ƽ…«Tك÷ÄLˆ9:‰ALGøčśst25®/³.c…NŅi»h9ÄÅ,ć&ŸY…žĮ£˜W˦‚“W—RKK[9 ųˆ÷ŅŅĻŁEEE³q!')&ķ»čę 2īß?|ķDš³fŁ ŌQC„(ˆ€hŖc#„q‚ēøŠ*I;iQNC˜i5E ;Œ²Žw/š,TŚćJ¶8TōTm’2L…Jæ0ÜĆģV0$0u)ŒŒŗÖtĖ"[q3ĘąsĮR·7åßź =‚3ļœ~Č­:ČQs™ •W>8åHÖÜyÅŖ„BOļwlŒKķĮ„qŖ=/a†c[†¤'1\j€ŗ@ć&ķdUH@†P„Ž6.*Ņ®n[kysĶ-§6×…ŗi_-fŅgf„qr]$G6Vz£ µ5$ŽP)Q‚½ų׉ü˜ćĶ>·\ĘĆ@äę g¼;¦³ÄĘ9™(ēq­Ż1ZY{mLZ  $Ū¶e‚Š“‡”ĄĄ~?Ä|9„q ä·ģšK=QīÅ®nxI[üF’q&„Vø/­tI5½Ī;XYö˜ÖŒ„®šęŠŠV7° Ą‘LB¶Y•+ćs乞6r|¶Ģa g»VRĒQŽ‚»cUźÓk4Ü+JäŖ±Ī='étD­ÕßÓ6ŚŌ,uYdŒ:óOlø†»Äq$”­,i.• "ūę`_ö—­»cA,s0; p{€⣑^.>֝Ūķ5īMݧi6IĘ&ĖĮćĖ](xā°łvėŁ²µƒfHZr¤Č IŪ ›6Ø‹rözĒW¼p— Xš•‹­­ÜeŗÖIH”q9XŻĀ§I$•ņÆĢ~>Ō8ĒP=Ž·°ƒÄIŪŹvTµ¦4 ftŖR•TÄ š„*©˜p2j†÷Š`mĖ›Æ­4E‚/6øgē«xw‘W­I>Äw' “āś% ŹŲéTćģ‘Ļ™”GŲyb‡æV¦m[^E¹p…ŽIä²qĮć0éźv,ćan˜M.©ŃLé&ŲLŖ¦"~“PŌTēPāM1L" PķÜG¦°Ż¹tŠmY¬ąÆ°w'pĒķū~·Č̹ČÖŖĀĢh÷XŠENÆZnV±Ņ³ ±~ńeßĮ9Eä‹õ_ ›dÜ¢ ż!PumcX¾±½–ģ`F`\I=tĄąCįŽŅ5}(ź7²J\$p!® kCyM 5“Q@p¦ÕŲOŠž1øwKoyĒ5ÜG5FÉÕöćõŹ*Hä˜ūÕhL¢L/×'p™‚v²'>[¹³„T8ģmk÷WZœ²d¼–@ö•Ė”ō b·}?MŠ „ɦ[Ąa•“ĶLł›ŅģÕU*§8%‡ć’ćŒg\ŖTšĀ½€O;ˆŒsŽä`ß Ēw溃cŲĒ;3…;Ț@ā”„éˆˆ›Xo2¾_ˆĪsk^ZķRq2Ž;ƒF,ņåČhvŒ»(yVŽ,9“Ķė|9Ƌ[0’¹W‚LāŲ®R§Ö¶W˜b\ż`Æ,Ębu –­ >‰b…²/Ž‘…_OįŪ]®X³LźņŻĶšŹhšK˜ @Ź ¦;ü ā+,5™ģōš~®išå%”Åøā@&ƒ›•w’o8itȞ~,čéGMµlœĖ“½B L£ŸŹ÷'Љ½A»ˆk–d! µĒ”lļ`UQT ¬xāKŒ`ōXF'xś]yģrūÉ3e꾕’}7$ A%:ĘLH`6ć«Ó²ą?Į{ó¼ģ£ĆÅNTSnÅŅ7‹ņ‘ó5;®^lĮ8ųÜłČĪ@ēŚógI•škłK,ŁēčŹŖ’§õNi*Ŗl—M3Ŗ(”ī×4ł›qń|_=¼D¼ZA А Q'@kˁ;*ŗļ@m“] t®sł+˜œ½e ̲aXÖØõÅ„×^V}ō¬”rkŠņ…W ™%Ż(~ó6@ɉ~#ā ®\čL’».ƒ·rۃņ°W(1łć]“ņjķŠą ¤gœ/Gœ’k{oė%Ž$,ō˜(Ū=*±&( ;źC ä,»G#W2'0Į½ź©éÜ'Ž_;!#ų”’5Œ¼mfw6i ƒ\h mų‚ŚēWv› K€ĒÄśĢ#3v;(>±€įĪÆ²|€‡ĖÕv$«ČĮLʞyćI źĢ’r°oŻW„ö=‹Ä»s).ÓrU@Ū}j×Z=ę—+¢½ńNGŒÆķĢŅZqšj*•†ņ ¶‡Ąę¾<ÄfiØ9Ma”Ą­ŃźGń3Øq%7`ĢV–'1J¬U§6äYŲ…ČS‘3Ę>IBī¢p×čfŽĒǤZ2\$°ƒĢDmŖų‹ŠžÉ8ŠńĢŞ;ئV¤V¾æJcÅ9Jb S“¦(ī”4EĀųų»Rų§“ł=Ę9Ń%_[”·ó‹».[ Ä\kNQŠą£c:ĒVhvv&ńĶQUČĆĻģ‚f#aŅųžÅīš;ø…CØĆĢkźó ֕_Kü¤āX®4‰“Ė·R{r^9KiŽI…IÅqо_š„pä-žg•Xk5p„£cʈį<[Ÿ.¹ĒŻ¢į6¹UmÉV1:Ōw5K*­ŽĖ¦ž^e«–GōŅMæ«éG~ u%³6š8‡[ŠąkZ‚6Pŗ’vXA}+ęŗ†M5Ķ0Ö¼Ź×m2Š‚jjMFbƏ"?EžĶ“Ŗu^`Hjž=-ҾŚ^әÓūɐ˜j‹č˜j~IŻ&[ ’"§u0š€ ‡@ąJ[p¼MŒŗņBd”Įøד÷Æß|Ąó5šl!°‡ —āē ģ Įµė<į_ļ5ܔkDĶ\|kQÅDĶösŪ2;Äī­$¬Ö2Äx¶Ńuxé&ˆ»2dĪmԁPHbQB4ĪQįK_źyœ3DĢMsweÆZĻł”q’£Į§G!ŠęG—‡ ­eŪŗ„Ōē””WžD°$-n(ˆ×ņ·-)KÆēźż1ĘLŒ‘nUŒ¦6U“ōƒ;B±„2~«vęõLŠ©Ŗ™LA7fūŃKQ¦Ś<‹åżG@ŌtÖ f˜v–ŌӜŌl<Ŗ{˜¦(‰LSD S€€‡Aź«Š~h‹óDV’'dŖž(©ÆkŸo+(e1ƒÆV+͈śĻsµĖD źu¦JŖ‚ ĖJŖ‘ÄTYDš“l’ĪœØ“tP–ęš+x<ī ‰¢¤ß—zʳ“šśį¶Š ČīĄ7“Ģ?2Ęę­svbĻ·HQpĪIåŁbęĻįśĘBČõŲ@5­Whķ2–J+˜•įėOQ#zƌŗé„XJ<³^¹Ńu»¶\ŻZ¾o „­‘Ķe ­r3žwl !w^×µ~Ó™¤JŲŲ⠟‘„äJUĄŠm SJŪę/9‹—ŲžN2CÜģÆęĢFgŒ»1Jy&“5õ‹Ē P²>aˆm HŖĄé“ĀNµŲę1HE@˜ų‡Jįū”##|`6†„yk™Ł!{p®‘‚æyÄž„nčoüž\‘Ń“JĆP}W°4ŠÓģĆ­r¶U¼ŽųųćūÉŗ—7hÄøÅ¤AfŁ\šįņ&W„PAćé‹Yį¤gķģ(ńėź¬ÜMŖtŲ7Pä!„Ó8£…µŻA–s[Im<¦9€a;ŹE ¶WA“«Šq_YAįĒ?ˆŠ0Ē£3=¤©ǬEO»ĶWłspä•Ēœ™^~ŪJ~{½Ķ°ZØĀŹ|Šøf Tė8”‹§:NŚ¢˜Č·ųŪØdæX˜õ[kK{FxvģkĢ6ōņõ®7Ä|K®ks˜õW<Ÿd“Ņ9©¼€Żźjk!jź‚Ź–UéŲĒ!Ś™”O!J³HĤ_Ó^m(‡e‚j˜l"*;™: :ˆ˜ą+öŃīc€}7“v•;¹ŻĄŲ 2‡ócČj×Ņ`x™ŠųmĖī£Q-Ɖł†“/gį/©Ö$ŖPtžJø ƒ”D hA˱Q‹7d\  ‹=óŲź6>s- Td»Õ, ‡«"š™HŻmz.›Ė„ņ9®p83}wsr żŹņįтʂ6śś)“S–„)dŻ4‡“õ‹v€ø\Ćü%XLsõtę0FĄĘģjīqs‹ŠŠ9ĀļlĻücāČÅ>ŸƒČycٳšL›øpĀ+¶»F2€…³ønš‰ĘÅ_.ÅlŁO\HG¢ śüŃ@u-Ō<8[§F}y=g}Q°uœz:ˊ7²Ī{ÖąęFģ½4ŪÕå]Œa1„ư „Gb€”æø„)CÜŠZŅ戚"h‹.×E·ārńę¢åī!ī\s@”Ųżs£Yš¶6ĆŠ;*Aߌ]·÷j“µt>’Kę;ʽ<õāŲDM4DŃDM4DŃDM4DŃDM4DŃDAéŌ}¢/#ėLóĶ”D{ŃVRdϰ—ø{»ścŚ b+æ^”«ŒŽ¹æĢa’mjź?÷BĻV«\­~”ݦlSl =¦1M·šLQčb°CŽ"½ž.sģcYĻ5„Zss‹‹ŹłkƒŃ™jZµIʘ‡#Œ“ݧ/e…IģŌsÜ?&¤ü‘é(ż ģ¢/¤$lq/3āż+ĄæW…«œŃ!h“¶”u¢ƒ*(vƱ~Uq[5īü1ŧT……¹\H4Ä1ūXn&žÉXućo*&ürdŪ$G(|€e|1„N͆©|½q«¼„CRßĶĒČV[āI٬įˆ0Ķ£x(V­ ¬R/[+Æq Ų”­‘śN‘¬Ś6Q•Ģ‘Įī|a­sČę9I“QŖ°Ķ_XŃ.ŻĢ×ĘŅĘĒ!sšĄNF` Ąā_žl2NsĪ­ń×p&Iäw.²Ü=ƒѹ;Ķłˆ{­ę…™n֗8Ė„¢k}ć†zŗņR¾“ć„cP%Ÿŗn˜ŁŠŪiš«ä‰¬†Šøļ4åqĞA]»×:¦»vČęsęøq£[øW 9M6mSīådkwå-ŗQ°#nżĮŲ€ō™¦FMĆI¬‘D€ˆ±ņĀĢčJ"`”V0|"¬9Š^ø|-hb²}ōĖ5Ō®į¤’ĮŲkÖ“zø–öĻB‰ł”“6’½5J§9˜öœ†!ƒŚS”J!ū‚:Ł— ‹ēEźŠÆh„ž Ån[ ”fRĻUˆė ‰E½‚¹I­Ū2CŲi G±/eŖ¬ å¹¾Óų S F_¹u¦<ģ9^@y*@ÆN+ |“³eׇȊęŝ) ź©ēLÅp•“Z»’E)yĀ&™g)ŁGØR†ČF“ōHD?D`tų{C`מīII õcļ=%}4ČĆvķW^Įņ~&6n"A›ø÷ђ¬ŠzÅŪķ”fł¢Ķ×!ˆ(;h±Ó8ېĀĒŽGÄń$dµą‚45‚®2øTjųŪ™|pŠiĒéL™0“s… c¹cȵ|ƒˆė‡č”Y*1&Ł]ßӋYÉZÉ6hR·9”!bķćZŅ/©>¤Łā½śf ŅɽBFGć\T–ÆkM·Į@Ųg¶o±ā0{µÓFźā(Ƈq ·#}ŗe+Œu’7ę;+[VLµĄÅøƒ«³J")8~?¢Å=]Ģ‹ % 37fW* ‡+8på`0&±©3P•Œ¶aŽĘåcI©ÄÕĻqŁ™Ēm06(×Isq<——Ž»•Õq€(Ö“nkF“؁-O„Åüć¾Öč±-Ŗ“ܧĒjī_²V"‘…iĘOŒÉó4©+lTCr„/5_;rʹM%”SsØp-ķÅŽö\<½ŃJZŅMHnV+“ҦœŲn\—ę5¬>ćh8Š‘Š}•VH±E“ė5ąŒp§sų.Ę¢Τy÷ł%¾ŃōĄ¢÷öėv ™…tµźõ4EÆ"-l9GÖų‘X×^eQĶ»‰€“µ®é SnE»äĒĀÅ/Uu\,{¬I3ŠÆäR(P¼†ĀŃ×SŌĘŚ`6’M sąM ó^ףofC^ĄI'0;p8R§f8 ė­ļ)øæjā_w‹-VFVČEIŖ½Ż“PĮ!i©Ģ·‘‰wõ(>‘N*bmšĶŻ"G ¢;"± XXŻ3PS·Œ®£›ZЌvļbVā mōŁŹį#ĢĢxĢ FĢhA"¼‡zļµĀjÖ#qĘØl9W¢GĆøØ³›™”—ˆ„Æ0«:ĒpR“ބ۸I(ŃEŠr¹žŖ²`O—ļõ é€ėøHn^*ēJ$p¤œ@¦ż»(»‹±Ōc-Ż\į@BĄMp„)¶»”ųÆŹ@MĄAMUĆŹÕeįb„+•× ×䫯ؠźü ØÓ9Ģ+øÅRQ©Ūˆ d Q öķÆ×¾”ąć]µß^uTnńµńb FĀĀ)…)±i“³ÖIŁašXąÜKR‚4×8ÖņmHŌI13ŃG²0AUƄœ Ęž¹+µQ>āõбąA£¶sŠÓ¼Isš32™…qmEEy*1Ė|ņ’Ŋb¼ĒÉö™5Œ‹4ŗ;ā}CŽ…»Įß]ƜŖÓā^\āŽ?bY¾+åŒeÅč­fȘ ‘ÜušcnGĢ6b‹BCŻb¤D %f.D½¢jmļļrV8ž*€sńĢö:‚¼ķ4;hå5†rģ’š7'ŒsćéŽFrņ5ķØŁ˜ķUć»m{‘jb÷8k‡_ŸØćėÜvJĒxēƒ|9šć~ œČµŠxÖ±wä5łŶCŗT*˽Q׊+Hü³åˆŸŖ‹‘LšÄŌuY ”²ŃŽqsŚłhv†ĘĀhO+ŽŠcDįųYpĖ»q&”+]V5‘¾(3 Ž’iCjŠqŹĘ’vUI^ń¹ÅóŹZ—œĮ”‘Īƒń‡,>ä먂ƒ|O‰]gĒćŒMƒØP.RQŌ38śL­śĘx¹ä_¤avé4޾ƔąśKŻ ^žÜ#Æ“ü(ēøņ“É€„@Z÷ĢĀėy£eÕĄŸY~2åö#”‘0m£SSPHć8łĒX¼ŻĒ*=Ѥ å-q¤aęŲHļŠ®¬¼D,¼Ojˆ=ŠtX¦-ÕIź+ ą†03†į«ß\›=FS!ĮēgOå‚Ģ“„\Ł3Ę“°`qR’ų7y<n{Č.$Ćøm19<żg9ĻĀČH3‰m2YyņĘ5…põ?šAźĘœŽkń¢³„Ó2c;„묻{m§g;ćč=ĖGā. ŒĘėŻ:xÄ·qōtޱ½eF"Ć\±&¢ÕĖzʂDEEÆNFM¢šN U*¢‘®œ‚I¹LĄdÄŪw†ŚŲ—,sĆG‚8!rś*T3Ė/Éiä%r#šĪ;uerˆ=ŽsĒ•ųcŖ]ö±4*Œ§gšŠ”Ųˆt7];Œ.r[ÅhŻÆvcŠŻē¹o<%mH„»pÅÄ4t Oy‹žČū€=¢>ąŹ:ŠVą²Œ+ņUšTLT±‹ó„.Ή ŽVd|¹œ¦ĢN ŽˆćŪ DŁ®o«\Åw~ł”ū¼yh)^æ"¼ŃAE_Šb„9S&r r3”H¢g!€Jr†1DaéØī}ėՍ®FĒ6āVBmÉŗĢr1ø#MĮUye]‹lFŃT™©5ÆŠł?Ń2·cō×FmtˆPw«7źŖÄē?błmÅ2o_¼j`sMvŗ:Ÿ“ޱȵĪ!Ņżæ’»ŒaśC{}ż*Qōč b¢b3•DĪCŠ&”H¢g(€”Ą"Ü:k³.d¾!ŖəsxŒč ¶ŁG“j"a™KßóU:=ŒsEŻ ģłuéŲ¹ņJˆōķWaöģ#±Mšō>.ŖĀv09Ż‚ƒ¼®Ä^I97„8ŻĘŁĘy«Čg2g„ß`Z7! yœóg»Ö§ŽöŠ.„œ5‰®ÕQ«GH?žœz”BÄ5]Č÷ŖTPWyā¶…×œ±0TŸĖ ]FŅŅāžå–v/ø‘Ōh½;N.¶<~ńĶx—Ä„3+^lsC¹2«E°gŒƒ©¼ÉJ&?°ØŻÕė%K“O$ėäŹH8lÆŌZĻA¾‘°"‡ŖŖ¤rØšHāKłÆĆ5¦:a¤…NćÜŗ£øG¶ÓoœöŻ2<ϐ8Ņ\CiBюTõ]‰¼|žĒŹŽą^@sāņēģ•HBbŃ'! Yæ¶VŠ“”cŒņźä±gžŃŽä|nŽ*}zł­ōEäĪĻrŠBBķ3hśU܆āņÖŽK—™Ļ8 ™Ķ$°- ӊx—L€Xéz•ō s²2)剞±©9švÓæ”©ŖL?…įŚĪ]Œ"™LVe+6c%IŖ1k+M‘'|Ķz{¶1$V_&žīš8ījØs”@5]¶—¦YĖćŚ[ĮŁrędlk²Ö¹jŠ *¦ŹŠ«üGÄ:­ø³Õ/ļ.m™Ć%šIhxkÜF`ŅFjV„ŠŠÆ-Ÿ*2ø6š<ŠĘެy“aJ$|MjŻ[ĀÉÉN3å,Ļ11$ē0}Č{'paMĒŌšĢ Ä£ČÖ8²­eä“PŒ›!ڶ²’‡TLėXꕔ£‹H¦"Ž#Ɋ‹W¼Óć1Cq,Q8Z×ø]µh45߂±ø‡pž€œ Š_ƒÜ’ͶöģŁ8{HäW6qĶ/”$)S¢ł§š»l»l`“ÆŅhE9C°ē8ļ4ĶżĒµ½$yŖTa½€l$õ,¤W2ß"$č R0&0āGŒš%›øūœ×1,Å7?dŲb‡m~k‘—Ły¼£ _ģn©ā>–õÖćż$"Qœ²į)$9|€bzĻ”`O«5¾¬M«łÕ®ÅÜaĒx¦]”Õ~.³ČęŠ4n»&¬äUĮĻ+4ŖŽ¦%Õ;Ć ƒ™ĀŹG¬9„ŻuµŁé6Öoˆ4Š ³“ع®ä™„®&‡_{-Ž›[°\,ŽÉ\ŖBÉXgŸØ=©“Šˆh«×Љ‡ųbŠ"RŚsˆ7Ԍ²²4†‘±¤“Ģ3éßhšµĆb~75nj©YŖŠĶŲg¼ēA˜Ź™EwOŻ*VĪ*™9ŽFhŁŹć‹)0§ˆHL†QAp¢‚q8vń«»™/._u)õŽāzįŌ(]­Ł¼mķ›?„ŽŌ żcŠĖŅ„ģUB°‡9Cżi„?ŌՕ£‹ćDM4E«Ćųš’Kę;ʽ@µāŲDM4DŃDM4DŃDM4DŃDM4DŃDMyųx —:óH:ä)6ß®’~®{lŃćīżŻVĶė›üÅžš×üĒžčYńÕÅŹÓDQ3˜Ž(˜ś©`³Iä˜\‹‘ qāO93<ęLÜb8N®Ė,š+ŅÆŠ„P¢ŌZĘ[©Õ©£ŠhĢSčÜ(Arµp}αk«‰“w5kj÷8Ę°s8‚ ū$c\6·ł ēgĖųŗ”¼ŲųĄoŹ{ĒŅ-’nÜbĖ,+yjš‘ĘMÜ~hÄu›­**xŃŹgĖ:†‘U“Tp”Z7¬'ŌĻßŲgüēĮ…ī©ķh<­$:(wc‚ś.9ŠuyY¹rĮ•²4–s€8ģ5Ć#p¶p·+…Æx¾ą^2ń½„2½5¤°r’)NSmŁĶ2}€œ–j¬öĮ6žŅ¬b€¬sė}™V,L%XQܤ֍ŖjV·NnÆu&”ØÄśxµĶ`pÜē+“1µ;OŅōĶBźÕÆŅ-"ÓōéY_ˆ‘Ķsņķh5Æ!{Ø6ą±Ę?ÜzČł_’+A/sæąˆÕn+r¹NąŅ×sø!Rf¦b†©ŹĮĆ[£¢oŠՙ** £µVnUł~ąéŚ ÅżŽ“Ī„†ńą’Ą ĢrąI#Õ¦‹„qÜÖZ^¶ū-a=«@ÅĮÆ© ©ĶJ[5)…;TÜqćę’/ņß.՞·Ŗž>Ϯ˜ń,°$]‚9d¤aqŖ¢ć`:vŖÉ‡Rz€ĢT‹@œé·ć.£i'éĘ<7ŽzŒAĮnš¦I¹Ż"Ó²N‘—šĶćīF‚­M-?TvķŌE¢»lØÉ¼A¬Š•‹m~e'-“t˜8DJtĪ&1DuX5Z^¹„G„Ü3įŽd“š<ģ$QŌ©®¦iBÓxŠ–neńŖą ”EĒ9ļ,:HDt±(˜źs—¼£æ}šv0nŠ:i|}?… ų`ć$­•qņ.•ņ‚Ū5åŻŃŲÖµ£¼Ÿ(RÆ3d§ųÖ2ФL{y9K¶R§ŠŠnčŖ4cä‹)9dxB¢²ˆUėÆJŌLoL®ŌHŹˆ)¹…£n&rC#…ĻƔQ­œ+ĶUŻfĘ—ŚsĄóžąUįüį°ūĄGq÷€ˆt `«ĖóD[ YHč8É©‡­ćbbŗ““‘v”QhĀ=’'pķ敎 T›¶A3ę…(ź¦1ŅUKNÕłŻ(ŲUŗņ®ļ]I3Āb ŠØG>ē,äĆ<.Õ2”ĮńÅŪ~ķGIÅÓ»b`<ī'ÉE+čńš¹®wI>•& rĪĖXė•p·lēŹ9¬fĪÕWøć|‰tޱOĖb»Ū4P³›"Ž2.ß É¬‰‚}’…o舔ÅOXĻ՟¬Dė @²6¾…€×Q›ź•ܶ®ų~Ōc,h .i4>šŚ*Æ6`ĘXŹāt•+÷l™‡ķøaN­µĖ¾!—¬d ”›~Ö©D[WŠdff™«“–h¤«\‘dĪ™„åßÖāļ‡CĖŪ±K°µą·3yĘ; ŠŅ‹„^[éxŲŻk4‘Moķ‚Ļ_#÷PšmnĄ£ŚwįŹ½O޵œM)ŅV§+F ōÕjm$äŲIUeėč׆»0‚夹Ŗ6®Hrö¬S66¢÷ēń«IK³Tr“Z޵¶>(¼/†„mĆ(q«@ĖCĖQµ[ ×Ķ.pl•:‡Ē×gä“Õ”]6ˆXU«21V{,RĢPf}ŠĀż² ¶PéH ęĮe{}Y¢nz»Ö$ŒŚŗ¦ sØ«­WKŅ2ŚÜ;Ā£F†»=PÖPP‘Je£ ÅqÜõ¾Ö±9š˜/3&'øŁjtŚŃ^‹J§”Ūl”ƒŌ1\{…1Na;Šs¶8ų¦ĶŽĪXc PLÅŲ=°Ņ_Ć£š$[@jījPϵy¬K=ĶÓjĒŗˆ}Kr°rÖ¤#bĄ†ńüī†ń„!ß+yÆZÆćʼD„??+W­M[G3±•x4Ŗė# ĘiUŅlÜĒTD >żgĶÅŚˆ™Ā‚iVš–× œÜ‹M‹å‡ Ke¼ī¼s^}_n‚æG— UZCä Fo|‘rʱŒ"(dģY—Ō ؽ±Uć&ōÜĘģļqœex>öŻŠHō¬+”\7)¬EĶékOī–+ Č."rÕZ"YCČE×!É`ė,uŖU2.;ĘTd•³Ä“c9 UyĢiė§”hg,Hŗ½ķDĪūW ¦c\@ņNHÅ3ģ$.ćb”&#ŽD-\ŖĀIe‰ū“DŒcōÓm!-Z½M6IĖ”ÓnåQ* ‡ōMOFŌ4}.ŹĘķ€4^NēA9XąwƚCPj6-ėKÖōķgU¾æ“y.6p5£Z*ņö‘¼‡øTŠŠCµv=½Ü0÷qęWĶ9*ą½0•y’²M¶×9p“DWÜŹż¾ŖŃqĖ+bw_…p剔 Ł&Dt±ŌI¹TYSĢQÜ_K“-Ķ52“4©ĒeNÜN4Ž®Źūk¤¹™Ła®gI“ h6`+¹kµżˆņÆÓ, Ę³˜0Ö@oEŹt§RŃ/ƒœ$<“eŪ\GĢ4Ž’hņ*n1¤ƒ#ŖŠ+¢ŗ$0”=‚?apęUŃܲ­48ŠŠ8aQˆĄÆGĀßŪµōl–Ļ£…F†­8ŠąEBė'ēĻ4ŻpĻ28«3ĘzK;ö~©įü㑯,‘7¬kšłE”n#”ŲdXüäqœ>²ļ3QnĒJ“!…3&e»¶~Š-õM>ń—yŬψTĘsšrV”¤Œi²…j]ÄļŠÆģ¤‰Ń‹˜™)ipŁāŒ‚¼ Pø…vą°vnFńŸŌZó‡¹ŽĮb;§²×¾RÄXžjjĖkx¼ŽÕaæL?zÕŪÉÉ·'T@‚š(•B‘$ČBu³I£kIźNČįµŽxkZ0 P”ķx«…[c`t×’ē8Dē¹Ē9Ī$œMwa²Š|ĆĒņˆ³wEń¾fpoc™æd¹³+¤xŃÓr¹`³4!ÜČ »eŁŖ™Ņ?xnčuGEe\³źø${œzÖŁśˆhu®“HČŌE)\9–2'*hP1D»šI87ūf-ĒĪeņö!½1u’-č=J¤Åżj³ÉW°ˆ¬ĶG/…" hŻCœJ@ėk¤¬W„ŁgQiŹ=¬I ;‚ÅŌ5mFĪ ÷zhc^r4ŗHÜ3šå F= ōäœ/^āē 1”qn˜žŽ¼Ŗs”—mŚÖš†°]ä^ŧé"·×ū”HP/ʧŲBģBjØ®}©-|)Jr ģĮjRD-lĪJxmkŹvŸ:vœÉq«d†j‰éŸ40e÷XļjŽ„éÜÖZUęö„a»†O£ŻL\Ŗqę‰AUš%‘Łź"™FsJ»u…•Ō§‰FŒyź>„dŪė«føœqvčƒ®ń——XŹ©Œ±3nõ MHhŗ”Œ-FŻe„Ā¢Dį(Ł Į_ ø(öI˜Sh›ƒ2&ވ—`ŚK½:y ÅͳrMI©žW4>õ“ŪŽė–vāĪŅņFZ@ŚZ9ā*ŃÉM›–Ņ„„9 Į ¾åN­æ _hÓ“ģ«FŗYę¦Zę’®4–# uŠNĒ.ÉĖl„H±N-/.ƒ¶É®c8hį?AĄöMi\Hėy‹' ·p¦ŹņP ÜŪ–³«h,½ŒĒf·xøņåŽT¦c BŅU‡Ŗ.ŚB\T…lÅōŌŽ Źv3źĢVHź3Q`@å«Ųs^ŗl×}ši6ī{Øy1Ųy 9TݹŽ&¶Ō%kF[¹z•gČģO œ0VSœē“N>ÓO—L†ˆZ1ąKE¶<Ķ|Ę]UŖ3ńķ”Y¹÷EĮ)ØCݔŽfŅ”Ćo!Ą÷UUužŻńāÓŚ1źŃbÜmˆ«XĒ~xÅųź„mņ2|v­ĖĖPbVYŖŁ‚ŽzżŽ³nZ-ĒøĘ?›|”‡ü„ó*&õ©WLäSq4֏yx8Šk9d{į.“jSQNL0Ćr󎓽6o—ĢŌ"†6NČ¢pph3TøćS_*ŹņdDĖ·qĪRć°na†ćī Ē[Źł•a×ßęł'kŠ~œ„\Ջ2e÷gDę9 Z•ŠćN\Cį)RÆML&Ač*: żsĪ$‘Ϲ™īŲ "o@G‘×”.³„Z›=>\(ģ™K±ó÷)™­MH)‰ņŒ|¬DXä’m5 Łsī„Ä –(zß4 ˆ Uvѹ ?Xp'pˆŽ“}gI’ ü{f“nó°}²œ„ģŻĘŗøŖÓÓłįĒŪŽseƒź÷Š„ŪłšĢ¬ōäMŅ@_<€‘fĪbżm%‚n Rµ’AĖe”æ2/šåĪ:½qĀZ­¶–u)XąZFf@" ƒ°ģ”fāąš)qbÆ@Ū`'jvØhėbĻ%^²WåŪäTäĆ5£åbdš)¹2~És¦”GŚSt‡ZÜRÉ­žĢĒ5Ćh ŌĪ ­co1²ń‹*Äń"į+'gĘvX)yŽ!dI· =œqZŖ&’ÖŽ;]dÖ)-tő+¤ź ńĒט¬€ś€+°XMōŸqCx’Ą‰ØŻN ŲAŲöŽGoEÕ(¹÷hķ¶wāćųN>øä'étüųļWƐ ĆjąÉ{ås%ę,ƒPĘYŚŻp¾ ƒøŽ&ruŽ6­Œ©Ģ­­&ŕåfÓf1„$Ī’ ÓLź O±J}²āxmć2Īę²1““@ŖįKwg–䃗(h<øŌӗ`ZŽaĢŽoɧĶNOdé !‘0tEß qOŠxĀ^:C į(‚ׯ9—‡Ķ“Ī”†W4]2mf?évYÖ=£… Dˆä×Ģo5&iVQ6])Ņ5’=ÕĢźø Ē”āŅA©]MŗøŅīćŌ-.#5„oiępØ<ÅM"ēīHßųĶC°Y|Yņžz‹•)iÜiŹTfxæ“i™ dȦó•ō•ZG;Õ$#XY '«!+Ԉ ¦Ėöīb†Ķż«)c4;qiūĪbŗpł…£ĪńNŚfx#’µnŽp³—ā÷eü Į<ˆs„Õ+½ĻƊīŌĘó%ń¹ļ¶·ŲsL\"×ucšĘX•Ü, ·LÕY”—0"ŖÉU>óx¢CY<¦˜ž²¹ĪÓ=Š“¶āZ$6ø ó W8|ŠČÜ©åe߄ō+<”o0#ųŚŽ@¬ĀŹ=އĖyxš‘V{c¼¤y›Æ=GĘ­gųƒ\TZe7®¤ąäd ö­Jeßńå޵Ģ6“Ź¢o®Œ>£M0Ē— ,WVøżA®Ē’L½.õTvér7‚‹QźÄI5—F=‚ ‘12h»ˆ‰Œ žĄŽāÓmćnZsš`+Šī¤qÆēW&Rƒ9Ž‚dG Žätą¦xč£üb¬čʘ†ü„ŪY,·†ß†»ÜēdŽaT•»…„£ļ“Xóїaė8,ögfrꌳ`: Ź1ćČ`H„’Z’ƒ2 kMϹOښ–*ÓɱśįO ėF;? øé!vÉ!#¼yŖÆzWėµźüīĀ_®@ĆM Dƒ-Ö@Hb˜Å1ĘĀŌ5ź×^܏s֒; /¢”4DžĒå`~Qpˆŗū}l³ß‰_ǜŠbŲŁcmZ{Ś“µN“Dūūż_LOž»TŠø`×L’Ģw™z“h¶ŃDM4DŃDM4DŃDM4DŃDM4DŃD^F ĖŁŸł¶ŸńdāĆżśč]VÅĪ>b’Mkžc’t,õjāåI¢+w\”Ę–,™c &ą„ćSźV4Ŗ¾] ČGDßłvެMPH‡;iVńŃ čū|‹I'‡Ü„W}Pķ«zįūY™¦Ö:gŗ.qś‘Vƒ¬ę<ō K E»W(¤²*¦³W-—L‹ ŗ+č8nŗ*ɬ‚ÉÄ9 SD^,°iˆŚ±åžkNs·sYÉoiŸPRI¶%sšņXb(õt«Õ0£¶ŸlŁ‘r±Œ c Pų@ MĖ«ŚÜJg³Ē?Jƒ7m*§kfÜZ™Żąß›ŁÆRžuŗÕn•]‡ŖT`aźµZÜrp5Ųö±0±m ƒ(čęi¢Õ£dƒqŲ„ Ģ"aÜĀ"7ŌŽł_!.yŚNҾāl0SÉś°ņĢ$ Ü©­ÜĖČ P;s \³Ō(õ(i€}‚%įmXģ» jg?-ķ€Œf|ć-*äĢ@¤unĮ·IzL÷_Š;±©^"„ßĀō‘.ż ÓÖķ¢Įā¼mǵƘ~¬“ÓŚæp2ĮkęO ,¤U–6ÅųŪ ²SŚTę®ė)ÜAöw”¶ųt”ū ŽĆŌ5̾cÜնփq/=x:ź)ģ¾„ū§Zy źØņ+ɛ•BO-ńŗ“¦Ē$}žŁ¦"]ˆXøØśæxŻņ±Īģń-cŒ,Żq ų,°-»Šøųń¼®ØŻ¬"źo!5š®ŒB ¦ī¹ZU圮¤ Ā葩ė·n™N"%IŗeŃ eė·n~¢óZµ®ĖŠ…;j©²µdvĀąĄPĮe©M"¦ v|$ŲĄ€ī³Pr–¹õbρÆk2æh\=®ŻT”W¤ķ׋=~›U…Gח²Z&@ĮĘ¢aķ)žIɮٚ"©¾MÜ”¾€ˆķÆ#ŠIž#…®t‡`§°+…Į¢® ‰»g8x[1t›J­ŸńŚLаÕrõü\jī@W‹G<’fÉFŖø0‰Šį¹€;}’æƒź­h.żT'°«?śAq±|‘ŁCXųµߒ–*ŚsY|˜ĘN*Äz߯.ƒ ٬ŽA{lN f|+/Ńź”ĪŲĩ·Õ[s§FéĆL”ø’Ó7K”`Ś\(]«+ŁīÓ~ǘk8qž£É<e­:ćHµABdø©6Åb‹yŹT…Ŗb—.ž²į,'źSN`囜HŖ.t”d®mŪty”¹„A-č4©ĒhpØņ® ėÉ&k.ōŠĖ›T¤óę 69ŽŹįĖPB®©9Śvó’ŲŠńo“t†eŽ6×(djV6ŽĒ 2E/N-xL§i°£c’:ž‹b#> ”DT*aߤ–ķŽ# š'™ZM{Ś:ÕPŽ>k ­®)\ļkCAäØy5;Øü‰ó‹Éī@cŽcI½ŲOY6½˜ł}­ j¼ŽIĒn/‰xŻ &ŠßO°?¹&Œå§ŠQDY‹ķLoXЧ¬ŲćvŸdū™0ŗ™…‘“ķ vņ `ŽZØKŪ–jś”ztµ…¼‚IŽ1i{qd@ļ9½gÓe(Ŗ$ó}ŌUܲ'8‰Œ D®c Œ;ˆˆ¢uJ""?nµß įl~;9ÖłĒœU!”«EÉL";Ż­ŽŻ<7Ėß>UĢõWę®Z¾H[$d$xéŠ/óX÷b²®é…VŻ9ItXėNT¾0ETh|žĄE[Å6u»fLŠŪÓ( Ÿ„hZlVl”€nähs¼ˆhämå+ēŽ?ā»ĖŻMś}³Ėl¢$7h\yI Šī˜•tpVjā&vÄ<”¢UœX[b„å+—ģ{YlŁ“•×ÜŚ!‘*µ¶i‚ TŸ‚#6sŠ­¾+#DKėˆė#WÓŪŖX>ĶÄœZNē ‡ ā1Zß kīŠµ–ŻHI·Ŗń] ł÷Žpsж_§ņogŹüXµbLÉ_¾Õ×R‡+m^Iž0’‘t‘Hx—ĪBÉ,DĒFR)F„‘n±E(ī!Ēßo%ē|Ł#sėR™©ŹŅp<Ę“_J2ā;ė?ÅŃČ×7ÕĶ\½Ī)U`.¼˜²š_ŒŁ?yŹ8֞®­6tė}„£tā‘Næ‰ź¹"éfæ_ędŃ8"²)Ē5hŌŻė"›t\2ć³S¼e®Éq±ƒLqqŹ džsE‰%ģš]“Å@}Q-®4\ćŌŪ€]A_åk·$ņCåŽN{ņūȹH»hĒĀHµz…¢£ Œ±MjH¦2o"Øu±—X½ źMgK˜;”¹cehĖ8=† »É8—r} ę¾%Ö'ÖµY.§Ā„€9Ą:€vļZė ŻŪw _ ‹Č÷ØØÕóI‘vø ¤į«¦ź’]Ņ0”å0aÖZ“ęn $^:ķĶ Å%Jr}‰$1ś)bųō$„嬊ņU¢ ’€éb®dŁÕŃhŻ!6ā)"_hm®iÄPˆõyKŖą×u–Š÷Æ«8"ń×|1k$†Æ -Ū¹¤Ó»ó9$Ū5r¦JM%”q:-z„ ‰œ‹ĒIr $C¶{z•@å"»¬m\1ˆ!ĄDPy:ų›Č @Ćk§ į5É©’-§Õ¬źž€G^|fØ-Ųkا1•ĆÖū  ę$«/Čeže<æJćm}Ó6ŹZj’Ņ*ČłŚ-ć1Ž-”łˆ™ūzÅ\ĄŻõ®[ÕN>¾ĄāńńŽ¢ŸŃŪ.:ɶ-¶·uō›į”o{ö†ō ®;‡9  I5;ÖXGƒH«Ż¹ŒŲO9;7ž`U–ćµ^.ŻPĒóīc-7;Žf1ÖSü³ēbM¶ƒ°0~R÷"KĶhwʁ;wĢ‘„Ā˜ˆä]Č^Ć4uĪŠśtķźŗ”b|1µ¹6’PĖĖ+Ѱ¬Śµ”½C,ÄŃ;<ˆÕ”,˜²IÜya’jWj­²)C3±U•¬ˆK7} (k¶–#' ~¬Ą "s¦ ¦ź[I‘Ģø 8Āó”ƒ°× FżŹ?Pc] # *ńLv©‹…äNŠ”?z¼ØW “zšž³·ŒHr‘ŗ—Ö]Dų„@7ū58 ”‚Ėˆ—0WfxÉ/’q‡üe`pLMG:ŽS&ßņ>/Fߊńó ˜[»±dɝ²Fķ1ŃÆ»æV:Łō{'ž=wzįü6’ŠyKĄwpņØO˜Ō0pŽ”Ā>&į­ØŽ ö‘ܲSkg1#SµĒWUM½ŠB­ca^]U=$ž{ ł¬2Ź-×ŃM)5R0Ÿų ūµø/Ÿć-lsż€į^Šā°ćav靈BD÷Ōp– °BŹ0Y¬…nćU.F^åZt³„YgQ2ė•5öē((S L®uĬ’3oöĖäwī€Wi–āĆ$ˆ’ Ćh•YVėūšÕčŻŖÕI8`w«QÖŪs9C8#' ¤9š*T\wµh¢Év(b(żaC„w Fj—RŁY:āx€“cˆÄŃVMČä/ń^nĘuÉJÅź­ ŅåŒņµ}œi2U*żpua‹±ŗh£—‡Téś.Y¹½nqETÅ1é­é\Ie© ™ž_lń’HÉ9 ї`¦ŠF#•VX Ś¬Ÿ¹³Z˜C%`žEß(•.@ńŹAŒ5ŁeäÆB_iņŒc_Ō²]]„«“9j„£T’ŒV%ųŠBc¤dT<—pÕĆgPŅ"’M>čŠŃ˜±Ā¹ši»ZģꨳHrW«”ŹqŠy½€­X®””Ŗß}×L¶œ=uØ]£Š²Pņ¤w_u-"dRš`RØåVoĮLźĒ;]=Ź&C»Ōų[WP𣅧,€±Ą:3ķ ”®öó€“@\ĀÉ[źžQ‡z x#š¦!ĆaŲćŚn/°¬ƒ<ؤ‚Em%U·ć÷ąņ(ä „K%4ž"Ńšē“~åT Ū±ŹJzjĒ?ŠõBž’ą™$’Bh[L|A ™ZŻÅ¤z Vµ`؍kw®ŽÅ·/öUz]ŠĖ…übĆŁ†£˜y.Ųļ+nfN7v£I Åå\ I1ƒ·(ÖĖp"F‰‘Vl r‚ęē‚ųąĶ_[hųͱÄqņ9Ū‹łĘmö¶Pł+ƒv.ŚyĪ8q¢/q’·-GÄń°µŹž9Āø¬^„H­_…fڹY¬RŚH(g6#X²M5ŠŽ\¦™}Wa8õRęƒBq*Ķ-7Ī^"Ö­SŌ›ĒUė=eėˆŁØ¹ŁcÄ WģŻ,Ķć$H Ł‹ēMœ·:g#uUœŚź³ń×ZÜÉguØŪGs‹\×»-4"® 48ŗ]ŸÉϚZ„:¦Ÿ”jµń¾(üLĶp«MK€ ‚3PBóļĒ3™Ū›³U±›¶–¬É›²ŽD–JIH6Zó‘¬÷²r“ĀŅ ²o4ݱˆr”Äł`(€vģė…Ū “ˆ®`s_¬kØs]ėŚ"…q=r›]J[Ę>+ødsĒ‚×1ģ%®kšqiq\%lˆĪ6­žE®;IEю/q—ōRDW:ŠlQ"eōƒpÜzōūCyó,bA#Ä;”VGeĻOW•szø©M4Ekx݈™ÆĢ·¶S¬gkFĖŁ²Ü³€Dȃ‡ī)ACŖG:UPY*Ütā$naŪćn”}šĀ!cĶjł'?dŠu ī£pF™aZ3¾§¶‹&ł¤¾œL{Ąyērk™5#&ÉĘd¦×åÜŠśLźą%öCß­0ģQ¼>ģŗ“\ł‡ģ•Ėaõ¤Ęu’ä)% =¦Ź” %/ŌhsT—ŠØė×Å@ü‡÷ūučŲ°õ8„„Ń ‚Cߏ\ hŠ‚ČöÆÖŠJv?“F±Ž\‚WöģŻ°˜wž0—ķׅxMėłG\ĒüF>9ˆ%ƒl¹Ę&äÜåRŃ$¾ęßŲ=ĪD½=Ą„t>ŅĒ<Ž^«ŗ-…4DŃDM4DŃDM4DŃDM4DŃDM4DŃ‘ˆa?õ„ęßŖŖŒ‹(]„»( äŃź]„vŪU±s˜ßŅŚ’šļŻYéÕÅŹ—éJ&1JPÜĘ(Ś";~ččŠČTqģžPāO/Åvo6dŒŪ=Otr€ōĘkÄcŒtł€Dßā] ~½¢p0jŁÄ•ÓģĮ (ݲ(cĶśõ{æ~ŠiąŒ¤ß5b }”"—*ÄDÓŌ;A3¶|ÅOؐtIT–X@Åö·é¢µq ·šŸ¢ā; »z+I¢,jĆøGó¢(“+JŸ!‚ŪK‘fc˜ĶcņµIڌ«?‹ŅnęŪ˜>\”°*0ķzw”D[Ō—ßé伆„}GŌź²Ŗnv•…W‹Ü‹~įčœQšÓ _„Ü,›fŃŲƐpźcé‰ *‰RF6ęŒ ÅNq&T„Ā!¶ƒU„`uīu§°VWŝƒ•ńœĄr*Oć"1¶§©–rÄӈėrŹ5²Q6+\óöóö„ŽÖžŸUŠš™~Ņ2ŠĀ%ØD}$SÄD7IÄö·zÖ­3mw‚ZÜ\Ö“PS8€*kNUŪxz(4}ŚŃę™ahŲI­pŖ­‡'1Ļ"¹|ż˜ĢNmŽq„9>óÕ¬TiB?™±ŽlR ė÷økČh²ŹŅIC ;‹ŲaŽ~•s„éq¶é­¬’ČNW °V’+ƒ*„sÅÌdś­hǑdŚ*ĀķjwŽY8™=g !"ž%²%4ƒµā[8Yt#Q:„Me¤ŒŌ~XÅ)Ģ”CpŗŌŻńü8e.Ā“ŪŃ\TˆqɘƒP<‹Æ+’%ŖĶˆµČöж §Øõ߯ź%_–ÜĖ)"Ń»ÅŪ¶=®å/]xĘ}Ė”Ń*œ"LŪ£ŻŪŻŽQt# šžšćivbšį gsīc‡)"ø38-ęĶRz-¼:¶” n`eĆb²m,ŲÓŪ -wźœ¾å/y5™±Jx—8ÖŚń’Įe‚˜±ćü… Ī×@ĪPTé¦hMÖcؒŒl7ŹEžą£–fba”CŠ•5„u{@“įį{%֋;/™ ŚÉĒ2OϽ¤1Į¢µŚEk“+zޟÆé°DĶjÖkOˆk‘ŽŒŹÖš1Ć8©„*z;3Ó¼¹udž ®3䄺ķSi);‘3EÆ&ÅՒnB¦»“V—AVńŖ oz&õ’n¢8Ā%²Ćąį¹¤Z€j YX]×\qĒ ÕCŻĆ)·« œg<øæ a×±všń«$ŒĒq’"*˜ē³0‰®'ä¢);38mķ/°@ økHÖ[©LÓü×wšłÕĖsXZD)óØÅycv/Gs‹—9 ķž0—>>ńĢĖć¼fģæQ«d>F8Šm1ī–˜A2±!É5ŠbÕÉU@ēģ •NÕņ÷‡įųOÄ®÷œ`tCM„œv.}ĘŌ¶äY[øµÄUÄmf„ū@ė,Šq»e…N6ĶDÄēģOÓÆMR+“ *eķH†d0«Ē7ĖŠ @7`tüšź’Ƕk)É@|Ė™ž$ö:¬|•å#Ī hdz<&Īń< R„ҿƋ>5‰Į܄‘£W£ÓŽÅnkvyüC˜ŻĀUŲ•Ź”†īlr³n“@LĮM—P„Ų¼ßę Ü_Ł2ņĶƒÄ‡p üü¼ Wjģ(ųÖÓGÕ$±Ō$" Ÿ¤jH#wœ7ø’6*O"Üš^ s+ÉÜĪ4ø“lÉK''/)ˆlüµŽsōįȘ4waćRå³Aå[ƒŠ„ó°ĶZM*&żc0÷$±v„)nŸ5«®Üps\ĮČ$Ą“sŽEßõVh–ĮŚÅ­ūlåxĢēFę½’mt5!Īēm)Ž¹Æœó‹ĻN+ŗ€›ĻÜ~ć£ÜqĪż ž-¾b¼·bŖ8 ”Wø["_ŲÕĘļ­‘‚’Ņ,į˜•ÓD—łuÖ/ė[›Ŗiß.ć-‚’• y$φŃĻZt®¬üß»%Ś{Žh+G>&†— ō9¶jWfŌV“Ž_ā:®¤P©˜/«Å3Ķ4›Į­é¶|ŽR5cIk}Œ·+•ŚX [Ļ³ÉŠjū95#…EŽ6Y5@š>³„ŦźķÓõKÉLĄ MVŠą*7ļ]‡µ«[A~© |46‡cœį•µ9Qė:‡6…µƒ¾sX–®_JqYņļŲ4x’‡ÆeF†fwh¢ēÓU62„lģ!ğgpü[ōŲqß„éÄp(y[ē 1¼s«ŒväżW#—ŠŪ¹ÜŖĄ”-\UŠ);ŽŸĖÓ²l€w—āKŌó%'Ü^£×`‡m? ѶRąõ·Š©w+ßČW8żL™`öĖiµČII¶‚®6*ņ×;ęLČ@®VY€«ÉZ§ē5dŸĀ+L›mĀ`Ź0cG`r[ń-ž­ Čēāy0ÄłVMm~7y+‡ņ‡s5¾"œćīd›“Ņ®4¬EmČ­œS²Ėź$ŏQ2øÅ® †.UģC–mĻ²Ņś BØ+jĪņŽęS+Q²“Ē”m¼9¢éĆT—cÅk¶ŹŠŅ”¦Ņ·œ<į}©ŸĮy‡\fČŌNåŖ|„ēsM‚¶²ōù“bųK6ļU2y£ =‘°2;ŲÓ.²@'2ŠøC™ń®½„ė†ŃńA5ƒ”c˜ŁcØ®!Å®;hĒEŁųk…ōė«9_ ·^Ēu#K¢ƒ—Ąęø9§Õ8cŹUŪĶüM ĪČāģ•Č,«˜9=–Ųg ) M¹ņO"+r+Där}m̵.›3ƒĘ±l§ ™ŗ3ĘčC™EŪ ¢Šb ƒ„xŽž’ˆ!±†(`ÓÜ^șJцŽ{]¦š*@¢ĖÖųcM³°w2Mq|&ˆ5ó>»dmZÖ‹kQC€%Wõ/ń|šČlه³Lē죛¤ŌjqŌØ oēcéXīˆŅė%/‹’=mÜlōĪFy$šņ°ņMT*ŒĪS ”Ģ”ÓÆÆžėĀŹĢ¢»O?FåĘ8šŹŹćT•Pzøc”Wg?R†|Žį'28fĘ՝į1.AĆÓ6˜šYóV ²µVĮexHڊy]£ZŲ+Ѷiub“öO$Y¢łdRTÄõH#T°\œŒØ’›˜­6óGĘé q!¢“;i¾„zNć,†j»Ü2#(ļhŸä›.K³ęč«tĀT ]tpĖÅÅDµ`ģķ2Źh„‚'ę—KävgĘlplxMnÖŁ— ŌfpqČ#§“ęļ&¾Č®#})\WJį=vfhmҬ«\k.Ģ Ó0h#Ś­@5 ­i‚ČFPž¬pĖ üö<‡eS¤GĻOŗŖ;‘@®fŅlŠ’’n՞v‘•ž’PĘtżć“ŗzńp1Ō6Ć$Ō.i+‰‘ä ī”`€Ą's; ‚±±­c*h6ę¤œI8’q%c/ēžGĢĖäģ®’Šoä'sM•…–*ZŖ*ōöqTXČ6ŃŌ:¹£$"¤,(³Š@īØo— ¬³õ*aø„ļ4»gˆą±ÄŅ .%ÄśĒh8„„qLzk%.·2O+ė\ōo²vbzJüÅŁ#Ó¹–įÖV«Óč‰ēśüEšŸj}ig÷³·eżGÆßGĘo7+Q8‚ĄŠ@Š€Łߋ»UMiŅĆ sŸą’ "˜<×°:Ä:·ā:«ī\ĮŠ“‹E+S¼ŽÅŸ4ÕItÓ]•DL‹"” UT ¢G(‡A)Č`Ö°¦—޼E9ŽāmT;„‘Ģé—GÕĘēI~ÖŹ’]5cŃĪa:_R7hJ?nūk*ÖS­D8Ģ|ŹÄģÜ“§j·<¼+jĔ p?•dø¦č>,shyöĄތ“y€C`ŲŪļ°źž„ŽéĆvsŁZŽåfŃåÖąžˆķ„z¶Ž5aŸąüm–¬‘4ÜžJĶ9ˆūÅŌ$-Sd…Z¾ÅC Š”‹ą”z‹tÄ©YŸ·øĘߢ[ÓĮkš¬Šzp †ń]ܓjfܒY rŠž_XÓµdjóŸ! «IK’F:¬‰*ĢMZŻGĘDD„Üä$„›–ńź˜Ęóˆ ĪįŲ/ÕkŒkävHĮsł©īX‰ĀĀćDFzędƒ4Q¢›_2Ż Ė `:Ž«ŁÅ¶)®@Ł%c,,)]qŁhbå5Ī Ø ›˜¦ļ3‰¬.ļ'ŽKf9ķ ӖµŁµu 9įÓ"dģpZŒ@© <˜S ŪKkVš½ŅµŠ›fÆ[ėĻA¤õZn2Ć ąĄc8‡ORƒø€~āļŌu¤I°»$­s_ČA°ā„ƃ¶+“F±–§k‡žU3ŖŁ›ƒ‘āiķź“¤TlēÓčc‘5{€=ā]½śŌ-Må›ķĮ£Ü0é”T VB¢1œnÉģ[“^²é‹gH ™Ź”€½}éØAŒSlbAsY£’ŗ9Z[#v‚ÆmX]§q»ó ä ‡pƊL—ČīDäŽ\©ü“L©?'Xń½FF­kAœ-µ6—ӝƒØ·h•fēoÆ«4&Śč–R å¶`p<„ žņW;Õu{ŪuņŪ<€Ģ£.ā(#¤ķØlįß<±żČµzf1¤fČ“]ėٶ7&W±B °(˜Z»·Ō$Åiŗķ„ >`±frÉcŽÜäŁ4–¼ü#øŠõc“t­²×Žllr2Ī"‡ŗ‚‡»œŸ>2³’*d¦Rņ—“Y°ęĖSų’©Ć|Y,źä»$^=«ÜƒČ;ōK¤l¹y‚ņ«śmbvģĪ£?UŠØ«Mai\+¢h×»³…æņNc‰mv†ncy›ŠIYL»eģm¹‹ž*)·™v £A֚ēT ¤5RĒż:j¶m\ćę5>ÖæĶwī¬ń‰ L¶Ń0{Jö‡¼5qr®mźŪåģ„×c¾CttJĢ…āPYB$Y;ć§R‡!Žb”V™³¾hԁæQWņ„ŠUfé¶oŌ/ā²güĒ€y›µĒ© • ZĄ#‚š#5T‡{]«WiGXD=G²‰2LmR»{N¬„ˆ»psŻÜzŽ­­žGųóŗAƒI$s ĆØQC?Y Æ)Ł;ČT¤q6dŹŌfi®¤G1Q7yéó–FŪŃY²° Ų¢˜‰@ńŖv1DĄ³õx‹ed棒6»®€ĖeG^Ø•ĒŹ¾4dcł4püģš¬ąŒš&e\ŗ:e܈"™@LcŪo·°7bh±;ɦv‡t=ŅŪä¬Y2Ē9Ō}B [ńœ€ÜŠĆµR‘TPœ†jī-R›ō›½8AŪB„,œŃ;cwŻH C°ī4=JIņaJg)||ęI˜‡­ UÉuČ2 ģ«v.bkEČP ,”¬:m„6xv+v~%¦§O¶Łrž³”łV5øoŹü•źtj¦8pjĶ–—W®C§‹®j œŚŒkqŒ£Īh—‹f·Sˆ‰ŌiڹJ + ˆŽŚäšŽ‘ØZM$÷428œķŁRIĒŻ8ļƐ•Ü,īą•dx=  o*ž“RłDŚóT°¶Ä< £³Y .Ej˜,äĀuiWF’ūŒ' «°[Ō]—pØÜH ׬o§ŅK­ęi›IśĢ’OŠxåŲwófM .i#NK¦ģ>cŹrŽøĖœv Qt –ń6DB°“E£Ģ ź³7‡W4LApKö °¦D ßcł¦ ĪäxŌ@‡IĄ‚‰˜ć+qĆ­æˆ^ŲKÓÜ*%sƒrž„ƒhp8` w+v×3:aiį½×¤Ó#Aq<ķęéė[?yžčŅø©fņŌŹŒķ†³fĘ~č»F:±µ“M-5sZ”'å4Š\ -ÖU/L‡C\/ęļ^Zńŗ¤ĢĻmrD‘ŹZö–48ļ5ĄŌm”®Ā¾Ģł!Äv—-o¢1ę;ė6„‰Ē+ćx{²’ݹ^Ӄ¶fõv…<¦ŌTČ9zŖćI[Ģ~#Įµ¬ƒ5i†¼„P™Åńs,ƒŪ,ō{&½•ōÅ>˜ł“D‡]”õ‰±¶ß£|‹‚ā>½žŅFG4·ÆcC›™ÆsmŚ kÜ åŲ¹Oū‹–9ø×M‚éÆ}„½ƒ$“+²¹¬}ĆóyKhq¦Ś)ˆ<}qņł^FNĖtä%©ˆŗyęöp²6„;eŠC"·|"Snˆź=Ń »ÅI¾ż»†“[ēŚH`‚=>€®vŪ‚źŠ‚=g9£¹”t½ä_ŽF.g~„<.4Čė’Cˆ>«Zć+ÕāᣪŽ·ćw&QFŲ§4å¼q^]sėŗ«Ō2’&-UUL šź Ŗj *|^o×q×u¾øųßP ¹µ†SÓ$mqņƎƬ†›©]é­$²Śīh…qĀ9яRČU²ŃFŖZnó (š]j~Ż(¢‚L‘õؗ“Oęü»±"ŒĶ+bo“ēŚh±IŹ Žą“|EāŌŽc+ķ±°Ÿ fCY3e‘eōÜż3O=¼æ]B J§Ķ„t»F]ĘųĮ6½¾ń×Ö<=dŪ=.(Ą”Č?.Ź/ŸųšōŻź²Šśu;0ņŌõ¬šBNĀYbŪM×&#'į^™ŃĖC>o%ģĢ^¹zVĻZ(«u…¤ƒ5S“ĆŲ²F ģbˆjm®kŪ™„įkīc˜ģ8n8_"ä ŻŪw Ż·nń›Ęė4xĶŚ :hń£”Ģ‹–ŽŚø"Ż5p‰ÄŠ&”LCD !ŖˆPģ+ĄH5¢Ķ/‚Ü2Ē÷Łń R/UE­Uz4\DĖcø?ØäaÜ5! ]ł“’:åj Cn5…aĢ‘Ćd;Ąņru,łu]JhÄSO#¢‰ĆƗ®ŖLļĮÄĢ¢#P2-P)Œ›fÉØŗ€š)D@EÕ>Į¾ę1‡qÜwfz­€X>³Īņå†.Mő—3y‹ ¹N–ütP,ŒQnōŌ)Œū¦Ītū{VxŁ;$q;¾!*J£Ó`׳£f­csL34W¬¾ł+;dŃ5;3Rļ ƜŁOüKU9vićŚ|ܛöLž“U~ķüƒ¶ģY UėńĖ(Ŗī(ЧÜqźckV•¤Īę“Tē8uØöū#”Qöķ‡āėsÓ eå^”tåŪFM›fQņ.:r²å*eL„Ęn«e­Ćž‘ŪFā½[ÆŁļŠ0˜ā”öqČ jx×āÉ\œiY:•ś^ 3œeR—ŽźĪĘ«2 Mז³L͐'¤“˜ä ļģ¾Žw[¹‚\ćN­ž…ck$wS\Ģ(\āŠMkä]޹3äcĘ÷ 0ĘMĮ²Ł/+ŪŁ]źė¶˜ÅÜhämšR»j`t&Ø÷Š„²X½œq,“‹{2ŒV+’nZ” p(›QY_Å+ec(ąw‘鎻\ZC˜hąj! ÆLUŽćmȗ®@W1—7°O /õL“ÕŽ•‹“¦ŁmÕ OŻ ­zė‰r–Eަ٪r $ć.‹Y$ ńdŹŖ@ŽŹLźZ^›«Ćąj16Vn®ÖŸŃp” ā¶ė~"ŽŚå÷öŅ\A|ö²„„¤†ŃĶsr¹µÄ+‰ŲøˆīlZļ8g9f ģŽKĢŲžąīĖ |ĖĆąZƒUźöø?B«±L ½ō„…Ü„Ä{·Ņ*’‘‰›½‡KcJŃ4­…ŗl"2ļhŌ——šr Šr,{ž(¼½ž+½AņOsŖ-dmĄUjA.8įE—J7—_ (J¾aį[ēxN)(¹¼‘€sBVö\‘i˜õՔ“QƔ|X¬’¹# Ļ,¢Ļ?1ŗ|Ų@™¬ÜérHēOāņjj()Śv®=ņO){Ŗé^źžRIó•Dy)ņ-•8Ļb溄¼šĄ2¼ż!œĪgŹ ŖV|O†ėšw(Kc †µÆŁ-¬ž[c' $„T™X°nŖ¾«åN܆EHŪ'Ū™<{i£–FƒFG8Óe§8­w)KŽÕl½MJŽH pÅÄU W6 u‘MźŌó>/į®&“fż“„‘’°™ģaAa•øäK“«tŅt"%RjérŸpŠb‘{HN› Ż2#$†ēTŗĢM_Ly<€Ź„IÄūm6ŪĆh ‰»哨ō¼’>ŚŚe~H FŽ°JK•ÕB„šF#„.)&&io½¤S»T°PD!ÕīP$YąŁĒąŚ{GŚ~óĢ9ŽVæwy-Ūźģ#Ü?-„_ÜzˆźŹÄV2ż^k9–ńy"(ČÆQÉZ—Q4¼-¦³1ŽmŠĪŚ ĆęH ’9ĀNJ‡®Ģź¤}Ča ^,µyś9ŪQŹŅōs¬ķ<NZvåØę ƒłs,§a y&«)@½9™‚H„¾ Ø&ŒP@P2Jˆ¬FKPlŚRĮ¾µĖ†e~a앵ÄüĶ”ŚķÖ:¼­ĶćŅžu GHEUd¤ĖĪ‚cÖ>·䮜.¹‹üŃß®™@©Ģ"!ģßWcõA“Pt«oĆ~TX½ćJČtK5šzŠ)Ōܙ—Ŗ¶öÖX¤ŚKB0½ĢŹÅ“‘Ø(ROĜ™š :¶.[ÄM1ėƒæ)ż‘čY©d8U—ppŒqŻG²¬Ü(Q˜µ ¬ķću‡°5IĘ€Äųƒß¬įІ¶jģ£āŗ’ź—Ąn:/&Ō­&ņEVO9ϳµa•ĻVyĢĀ”€Īµŗ¤ŲwźR RūµBė¶ŃxńĀ>ƒ;¾¹…’ėYędĮįXœ°Ļ×ākY^&Öė÷ØŹŹ²®*³WŲ;+źmμy§MÄŹ0{&ĶpIr"Ŗ(:N‚fķäWłŠ‚Ł7Ž9‘Öb8 `,}{0Ź~{“™v‡”muÉ8÷Ø»i`ĘÕürFÖĖĖH‘fNl/”Ų7:`+"ķ13cŪs ϲåŪŲ½›Õ¦ó\©q«ĆfeĀuWŅ?'ngAĆl¤ēd !b“JeÆē"XgdBžJĒiy9(żR”€w *r•2sģ ]EŲ^='άĪü±9ÜĖŖ„\ū±Ü@~‰nƐ;Śg)Lm„J"ŽĆ·Ł®ĖD-åŹ<‹M¶zV3čŁW,;ŗäY:¢Ōę×ø§/[H9Z¾Jv¾ŁśīZ¦aw!^tdĄ@‡ō=3„m­RŚå÷†K—³'ˆšą6į” ö“©wĘ! ˆåÆ=Ió¬€ā™łIźĮM;£U,{iū$E0œDz™v»jŲanśŁlä|śćfå’ĮFNĄĒį½\Że«)¢.[‰¶–ŒyWŸØ§X>bӆpŻī<½Į²«Rl7Ŗ••nā³&ÖŲeō+‚ŽŽŃmō¦Ø¹Ń7“ł–&«6P˹²=æh<…N,Õyü1˜`Ó ¬¾(ČńČ=¾ØøwLšEø$&Ü Æ®röļü-µ¬ŠÕł.¢$?“åBJŁizqŁ[†7ć¦Rī'5·„\v¾ĄPÕ@Ćøī!łµą[GKø¤ÜčČģwēWs^­UG¼“|v‘¦j-LĶĀk)é8HMź ŃR"”ć„OęŃXEÜ@S €ŻŚšŹ’w+R˜ę)Q1Īb…¢c˜@„(~SvÕ+Ŏģ}óM?‡Š (ŠĢÆ9wLĢ”Nķ© joe1Ž—Äfļ™™Ņjč™įßEŃ8kż(˜ļ(^„-‰4DŃDM4DŃDM4DŃDM4DŃDM4Eņb”å1bœ¢SzŠ`Ų@CŽ¢/.3Š"¬™ß“šŽ¦n1uų»ć% ŖW+N6y—ß¹“³°šøś=ÜÄ{"µEŖ Ģ&ŌæØ]XCµ!®{ˆ&•Ą ÕĮMčzFŸŖĪćDšāM+˳’‹&ŠP£8•[ŒŹ˜Šnz·~q!Œ^e;h°W3ŗĪ•KÖʼnP—s<öZį6ÉS )8Öąö5Ųe[•P _Õnu&Ū8­ÉõX=źŠ!Ū»—Ę|Ćwz’\68.Zڱūėø ćӍ=ģž›ć5ߐT×Y •5¶•Ŗƒ$õ#‹*KżE&Z“›†d²DŖÕ½›)$“³}.=’:ŗS‰€V7ŖB$»Óķ­“_RÅŽ%įö¦„?V0k•œ¤āīŒ 1—ÜaZĪ; äėm«0Ž‘R“‡kÖCfĢ9 ²ŃĄ© .”T]‰df밁čĖĢ®²-+2®ŖÅYQH<ęRc&/¹hlVķžē1§~;;ÜhŖ­fīxī2ra³Ŗ{‰ūŽ_ČÕÄx„Œœt‹,Ųl‘mꈖńé½:R®RY꬇`•BØa9Š/u2ÖĻ•§0kÓÉPŠ0ßE•i©Da"d¦&¢ˆF“Yā©"ź(T‹æ¦Ÿp@Ē0€ż^Ø *h°ßĢĻ(×|mÅX§W_Üē”"§$­÷YGĪkTÖ9iHJņ¦„‹2ĻĀø‹"Š;&8śƒŪSXēš7”uUOiš3of•äF (6ē±aÓ&g<ł™‹89_2Yä£'JōŅõŠYQĘÕŪ»)ÅŚ֎IɆēLĀ+éČrō1mg 6W’M:Łķōė+jć†ó‰ļŁŌ³ĒXó7ńžARŖ‡/§v&2æņ® “™Lß©S!v¦ĮL ķGnZĮ’×3nńĒs€X6āÖ-¢e®@qź‹{„ZĖę"ƒ&äa]1pĖIHžŠ’…tĀB1ü\“\7QK鮑 ;m¬ė¶5Ń1®“ŠS©Nń5õ֛£M{fģ—,s(p?L·”`TæćŌ·0 Bšœ­_qę|ˆ«å,™[Trz’õkįbńż‰h(ēģr“Roɼ—AS²U˜™ [c*ÖķōHuZ½’@ģ ēeĢk¶=¢Ÿ£Ų·­*ęśęĀ;ÆUŁŚ iØ" kÆzĶ?Æó™K1öĒšQ™² ·¬utŒœŁņuœ•V“z‹ŠŹÓĶW‘•V³ć1xPjąÉ¬ĶrŖB‰Ä5wm Ē… ‹¬_•ąŒ34j˜ ŪF#hį?] ³Ū±‚ügk‹†"Aø‘ŽOdŠœėÆ#2~rµäZü?+s ¼NäMVVn €~4’³'üėżs7ćė:‡»āµ5RY’åj¢`©W)€ām՚Vybal »Ó]Gd{ƒĘaŽlk÷]”\öó]ā;=HMs;ģu†fh‘‘ųn-v¾$fƏa›AR«]Ģ˜ŚtŠČų‚Ķɔ2ÅiÄėX“.„.—:óˆy(xĄ ¶BV"B9¤<<ŗæ"Ź!ŅŃéC…Āo-­tŪ62ßLlVDź¶!€Ö¤śøT‘‰8•fīļU½|—:±šöāfå|ĘS#ÜŚƍ½ć÷&3uZPų֓“ųń-eŃQńõHRržĖŽĖG/#On²ŠSÓFD€šŖC¹QFƒ°7×>Np®©;ÆūčXē9ÄDčžĮ˜Ō€\Ņą7ŠÖ‹©š’Ļ>0Ņ-Ūa4éŽĘ5 ĢÉcåipcĆ\ź rҼ•RėÅņWz«ćEÉ/ažÜ£óĘURiåx’ ģ¬Ńė×.¢ł4&ÅŃ­jؑB2›“Ą"#³k6š[ŗŽ+PE»-£kk·+`Æ=Ŗć3Mqqyqqw”ŻIp÷¾›3<ę4ę©ĮJ¾h­·Uøń[PÉY¹e’«<AÉDŃt¹Ņ=±åŁĻ‡Ø};W%S ’ŻÜ§¬ĪÓN©®ĆŠaĢz¶zz”FµzŪ 6[““4Ó§wzĖ–R³W8åĘģ“p@†¬į,'qŸdBv¤‹6“jT‹øĘém°Ć’L؜Ą×m}G;Ūkhł668Éģ/Ÿ-ŲūŪęFq|²öŽ*Ķųö²ćYŽqŹ+Ż ļuģMWˆy3ōĻ};ŲŪ^ Ŗ”‹,±ķŸ8 &å?Ǿ±ō·Dė›ƒ€`»ūÖN²ÉŪ©LéšZ]! MŻŌSKR 14Eł•„±ö*u%ZlŅBŲEQ=J-łUŒ•ĘNB>­Ec Š $ŗ±ļ.„\¤9N)ö”ƒ+¢†¬”~įĪpä)2ĶqI*#ŽGŗ1wp*6r²<ęf+AŚ-ÜHŚø9ČŹÓ×ķHdŠx„{)a׊7E#‰Ī ̵•u œLRØ ">Żrߛ," )O“Łq+µü‹½FZ: bĄE‘•Śń{Übœ«#MÉ–Ø7É÷ĒI„ép°1Ó ‡õn£–ø[YŖ«sīEĮƦp €uūė{4œ„ŽÓł•T¦ #q•Jœ!ŌR©V…Q`(,xzä,QÕi ©˜1ne BōĄŁ©”UY Ł%TL>ÄĪbļ@4EųeÕ0|k(!żŅ†ß÷G^€I )±iėÄ[9Ń1īäž±³4L²ƒøŒ!ЉjŠœ@„xŽˆ”)rŌ”_cg #93ž0µjf] õ~iĆL•[¶§3= ±[BG;k]:.žŖr¤Ł!2Ęķ)DBĶĆC­äi$Ē R*ØĻ Y6o1ŽE Ų°€M!ĄŠĄļ;¶©E˜²ŽKē5ēÅŽ$Zؗ«“n&É֎bfumżĆŽ8Ś©ˆÆcø›¼”•löŁ’īH££'] TTvq¦²eŃ4­$[\|mŪȇ„×<œĀŽ l Ś*ź<[űŗĮŗe”‘Ky "w0fˆŅ X]Zāp"“¦Üh±qÅ:“¼Ū7ł+J’ż“aT‚¬W,oR(“ŖÓÜćŹU‰ŒeV/ÓM”·$°˜$]¤™]½TŹ( ";~n’‡€eˆŒÄ ä““ļęäĀå÷m¼““ óy”ĪŌr&ˆ­œé,ąęĀ.|§"A(†ål֌ \ ’…é™ytCģmøļ° NÆĀJwzŸ½ł–vżPč>E‘ģ!5Õ#“Y‘,$|õW(œ©% «5Œ‘›ŠG0®[ØD`źĶ_£EµĀ[–ŸI_®Ó“¦Üc+ŹŽŻ' hq“f%åŅPJcœ„rżŅmŒ’+¹QU;“ŽD½ē0‰QD…`[¬k¤pnŠ?.õCČ`.ް ;^¶Ø’¹ŠęmeŹvl›‘¦/ •O¢Źd;4Ģ:GL­]Xķ a€±GT¢’ ŪØčĄ •0>ŃšĄ?ÄødmcC}ē‚AęĒ—b³×nš”u‹œē{­. SœÓ ŪJ‹ų¾³V«ørŻWJ ŠNVŅžJaßĶHŗEū•–dęVE`L\*H Lź*}·9Īqż!Õė©,“?DA³žƒ„Mé¶ŃZŚÕžĖÜ]Rq”ŁSĢŚwŖÉKÖB¹"”u^MĖ*ų­°É"»£É¤„­×Ń;Ud›bū•VSqj6AB›Ø|b]ż :¹åĢ^ĆĶ9#°ą±®“­>ń“ž&“Ź0=£\p’+'ń’˜XY¤PŹsXŽj~į-4„ĪK³Æ; DzēŚź®ž¹¬Ėær²IŗŠ~V؜ ŁAÕSˆ “Õ“iŚų¢mČkCNF’× =®ĮĄnp®žu¤k:TZL`±®{’UõÄa‹6½®®<‹ ˜eČ\±ˆrŠ œ‡(nג7C¼HÜ»Y¦ä†(("=¾¼ć”·†:&ną_·D¶‰¤Ÿ ʟŠõ|ˊqSŌ›.ēÄa!]ŁzżŽęÕ }OQĀV̧1^Ä5³µ1Šą“rĮš*˜“āٱ­±žÄÓ0ūµ4¢4Č|}B·z'ø/@śµn&›Y®Ō ”V "· Ģ€P#H˜8öń‘­ˆ” ƒ6¤ l¼]aELבnµÜ¶”;YRYKŠQv+øÅY/Wć°]žN!yŚö;˜–`›Ō«’›µ&‰>\Ą of­¹Ä:ƒm:W»•¬ĢœĄ–ć͚æ_Ź9wQ’ŽįądAÜ1žqÅP2_<ÓŅ~į¶g:¹"‹VÉ"©ŠjżšŖ"—jNL\>:L†„Žń޽¼«ÆĒž^mT9=Šøq‚1mæŁKrĶ•rŅ8»$2É0•ŗö/„KÓ©Ń’“ķa WW nUnķ¢@€ÉD.©L”Sjo‡āuĪ ×2™Y·ķĄvÕa_ĮO²n'Jßo!e1=’Ąö±lČTW0M­Pä[³ønõÜ;i6h®ˆ* "‘Äč/Ģ^Ö„š"ŸĀ”ģńhŠī‡[ŌH<hsņ«‹ķ,ŁrČž%Ā2K€#UĮ®4 89Ōł–Ļ5Eu“†(“AÜ¢åŅ7”q­É4˜„"{*”{ęj*вIoŚ”D@ĶŠĄ ƒz'‘pYc’˜„il£h"„uc„DĀ&0‰ŒaĘ1Œc‰Œcˆ˜ĘÜDzˆź•i}&s¤rØAķ9 !ƒŚSw)ƒģPüŗ"ĘF=°'WüF¾9„UTˆć%qŽ TL¢s‰­3–*¹!°¦©åĄ§hDt]†?ŅĒłŽņÆTŻĚ"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"ÓTN *)‡r€™Ä…ūN(~é“EāGÅ[ŌäO"²-)”1oŽģ­FVŻ$¬uF»46¹‡.YÕ3É>'ė Fķ zčNAP :¦—ųŸ„×?,lq'”‚6 ĮHŁk#D†{œ†G ƒ“ņ®×Xoqó0ļ3ļ#!ņŹU„ĘכÆĶŪÅ8®B¤{Śõ2w€ĆŌdΟ`µjrøw°ė§jYÖv6¶ųŒ gy<¤ķ'„s}c[Õø‚ė=ӜąMĮ°r ½”ņ«WpäöGåUQŒ?ä1©*/å×iwĪš†„‹Ń®SŌoµŽä±dĻ¢©ŠfR„ó&7ØÖXõ¶([¹¬ōWź‚Cp[VÄŚķø¹Ų†· »NąØ-TćV%ι’lY3:!ˆņľ^».Y¼‡<ņ2­(ł¤l@¤‘TkmŻ C#‹vÄM2”ŽØ” ŖéA]ė[üZė]Ō­¬ī bÓĶƍø0į‹·øó»ØzųĄö¹JŖaCWž¶uNké±‘ņM\Ė7pN«ķR’#¤„Spš¢`\ēi»„zļŖŪwóČ^)!{Ŗ9ź§%žĒ^™§ZӊšŒ9!$@é6v’ŠČ›Æ¤& ]żą,&‚*Vß$‘ʏĻPö" Gøv‚±ķ!Ź*Ö2ģčŖ›`0€PJP½ĘźåÕų>‘ę±Įnځ“sw‰x *KœQŠ™P½E™e Ó~©0rbō߯RźROaŻM.ĢųŖH_3Ń®‹Ųéæ,ĶāŲ‹3Ąæ*r|e)ƒ³ÓĄP{ŗ–€÷jąŸ?}uåćžF­aĢɈ²„¢Y¬Uv‹‘eVrķŹh&ńDš•éXؤNc{5"Õ­’ Ģŗė„Ō×Ż°īƙl¼Ei-ž‘-œ ŗI’ī6§ØTže–ž2bPĆx’¼¬¬”¼„‘āłČ“·1ŚŚo,"ålQģĪŁ$Hʒ€  c•ĘcQįzÅūµ÷Ī@z¢›ĆI”<ēzé6VĶ“¶l,5hČ8Ty)ˆķ·KgÆxŃ8¬—(ÖĮsƹ¢µ9)Vgpo]RļO¦¼°ĘČJEĀ“,ƒ³7'@!Ģ:»儐2ßRŽBb5ń”’rø;&‡hŲ„ģ5]SH/ü9ј¤5,Ø`AØ­ ¼Ķu®Erœjæ09+Ųd˱’±Ppżµ&|ŃOÉ6NS"äiyĖ{:hSœŒĮ?ńƒ¦ŁVŚ„¤µŃķ„uį{‹ˆÆ#(zÕ:¦Æ«jšų:œ±6ҵ-cCA¦ĻYÕ=…A\Ž’Œµ*“?O ó«½?…īWŹ~1ŠM»¶æU—ŗX%&ÉR·ӓU$Š`ŁćµÜœ;„ŠAķ:ÅW· “P`ų IńZÜǐ4PŽ5&”ĄµŻKK²am¼„Oś=ō8žX«åGąćFU°6l½ņG’n,ÕśĖ™JŁ*ó• cwIżāBµoFŠĒµFĒ¢Ž(¹pTĮ# ½åÖĪm#¶s”ČÖ×h¦hü_©2åќÆ,>£‰ui“a]½{TØāœ;|+”yYšµXŪUóEVpŅ·;›M¦J:冱óĒ&–žzB;’r”‘rR‰Ćb¤ |% Š¢ ¹‹(¦"9œ}+rįĖéµ<]ÜĪāspHēÜČŽ&fĒ)óžEņĄź±ĘœQ ½m0|’ŁG”8ńŌфGÓUä>3¤4jÜ?H…›[m„į¾óņ«N`źo¹vVõ~Ek_0/^ĖVX³c½cŠ)ē=ŹįymÉõŠöĘJŌIwĪHēJ 7'§ŹVAäf¤É'’s4ō¢p©+"ĘØ”=i«g”ŚF‘ļ—Tę)Ha£­ĢĘ[²ŻõÉ,„}€s8įŗ‚„ī AįŲū©.™LšÄāŚÓ2° ļ©$ äšœµ~„ž&&6 ā5Œ$Sh6LĆĀʤDžŠHb0LB…)ŁLÅ8o°²­Ü؈k@Ņ‚›%9–%Ó_0/q&JŌ×i;ėΦ¶³ŌZh‹>Xļ7f5TÉV‘ņpł›^=łĀ#1ę°ĆdkkĀzi:LļBYō̊„Ģu€Š J;ėUā;öŚ6ćC%Ģl鯔b·NÓ~éĖ[¤Æuy)—ČpR{'YĻ0ŹJ pÆ»²ē>>ÉJ&cü“küÓD‡³Šģ@}0m3hą`‰ĶÓę$PźpÖ½óJĶ÷'`؊POGåU¶ü•Ō#“āwŚŹićĄZ9Ī>z,qT‡‘ø‘č§oVć¶0‡õ÷ō¤nS³2ī”{zńōö§7_`—§]jv §ŠžYrŸw“zO•NĶH*UE ƒÄ——pł}£ųčƒ*5Y«ż†`ēN™KDÉ­aøĻ3|»`1E"ƅA3œ97Ķ?š¶_.­#³³ŠĪ.»†Y`‚Y…½¼6šn5 Bä‡ ]:Ö­ńfŹé%‘Ģ··d“<4lƒ.³#¤‘ĪOĶkžÖ—½Ļ± ,ń&“­Ø ½ä4¤ Š9čaS-Žļ…8čź÷(•z—[µÖ˜ę¼`”vdQE„¼õ†Z6˜„˜¬é®„x×ę]Ę„?ÅÅü]c¤Ąno%Ónåįķ&Ö&fq|ö‘I~ųƒZņÉ/ļ ÄĶkŸšĢ”k{Uæiŗp2żśn›5˲DŁćmåĎ4{ä-…®©lQäi g;O’ńÄå^MzÅĘ.¦[µł›U÷”ŽUkwŲŹŸŹ«tŖZ(ĪäeŠŖß«ń/ $Łf.ŽŹIŠKģDŌDÅ×[’n_ī*ē\Ōm”Ŗ^p•Ī”…åž«;//t«›ÜķÓÆ-5&Ē ļtĖ©ŲlęŠę?ÖwĀą÷2@N½Ę¼Ū8¤/ŠŽ-A¾h„·iŠ+†EC4r@\įń°ųtnÉ#… ŁBŌ.¾Čū²[½ņ„09~PŲ ĄRgø€żŖoüP×čŚāŹ r1Ö±<’PĀ4NL ĪߔņÄßņT\üÅ+!ĆAŃęŖģ&Wa,ųf¬B“Y×lvhAt%@§0X¹-œŅxcŽØė*¶1ņ;$`¹Ü€T¬„š[ÉOkPžA§r“ …łó‘ø~Až4ā”% Ķ ĄŅxļĒ‹•jœĘæwV6.”9‘2$“łETʈ¢Õ"¬¹!…’L"0;ŅŽÅÕ©©pÆPU>)!vIZZīC‚Ē?DĮW¶"bv„ЦŃRCfų[¤Ŗg ģ)5 %(†Ś§Už Ńó•ƒeż8éwļ$u²“DQó‘!1WŖ·ĶUM»a¤&e”˜?ZR*ŪeE”ż"I“U›¾°lŌZ¬ŻB.ƒä1w ĆY6­lĻųW‚Y%A‚:Ų¦u»Ä¬„G.Źo_qŁ{‘1̚9¶qä£Å“wźć¼ƒLŸō–t‚+|Ŗ±2Ž”gć6õD‹"rŖt•(“sl#© 8Ršco8ĒøŸ"»iĪĮę„~[ĒqO¹©“#YĀbkĶk݆ĆSŠ=geŒZÆ,¶ģįø÷‹2ĘH;»DźŌņ×*DS]9Y¤ \“h÷:ز„A˟”…3‰Š~ }¬^„Ęł˜Ür±„ʕ­p€ÅNŪq ‡éOn³# øx¦g¹­ zÄb18Wƍ²§¬J׹2–ŹX¢¬é[Ę-Ŗ.įą¬ó¤“‹¬}a¹ŸBåLŹ•Ŗ®g«…%>£ĆzŌm¦Š¦™œĒ9H$R§“5¤ńw qĆl“ĖØec[\”ķ$‘@@'·  éŗųā­—²=ń„3SdÆY ĆÉŗnÕäl$fæėU,7KD± Z…UÉ}åQĖŊdŪ"©Šp.«wseclnu VĄŠR\våkF.;šŁ“·8…ä×"ŪO‰Ó^Zkvf{Ž mpÉĄ„]æˆ^D(±+ZŃńĘAcŪē$+X§"'7rrĘV¬ŌśŠgH¦^ļ—fļęßdJsü# oÄÜ3s €K$N8H̬ėps²Žr)Ź„&Ņø¦Ł†wĮ¬‘™ŸNf¹­Ģy©Ü¬uO6U¬lU:ß8ŅF=ŹŃŅģ>EŃEŹ“8¢ņ2V)ÉR–„”h±DŖµv’j¦`źĶÉm#ŲZEAÜAŽĀā%¶”opĢĶ8Œ¦ ļA…ZKõÖÅlBk¹³Ł%%NFŒ±Ā —’–¶»H†YF”Čxęļ%Ö|‹DŽ©Ņ2  (¦sõC"£te³åk%äŠ7ėAN°k˱`^ŻI¾=³œ÷{Kž šÓ(E4ŚÆ¶Ģ‘×kgå ^ĒĆ>°e,léō”ĆŌc` ­. lɾ–’ˆ U"”xȧ`ʉN!×X÷6Ɖ“ĘZē‘€kAsœiQFŠ’kC‚ĄÖļa¼Š'ÄK^7Œ2FÉZY iÜqXGĪ8ćņŅ·7\NWįė^į—o?Gr„_(žV…&»ux̢ڰā%2;rŽĻ&’bæ©ņtdGrż]ņgX»žĀ[=Zé¦b[ąĀēc’•/myjcp©«ć_÷ĆpHčo“Ków;[ˆ5¦GPcJTbk@NU*Ą@@ SÅ1@Å1 !Ź`ä9DJr£ø€€īī ä„Ń3ńņ?§Žꛋ–_“~<,’A·IVö *ŠKą`¶›Iŗ‚š&‹¢pĒśX’1ŽP½Rt[h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‹įCv&”öß°†8Ś%(ˆkD^1*““w{ēDMެŅĶEɕ¹Č«,Š-Ņqo„]$ÆŅn±…ę“ féž«u.E›,˹'iˆ•¹Ź0śÖ“}}nĶOO~Itł[3…HŒœ²M¾©&œ•ÖĮ _Z[É>Ÿ}}µä*Š×팊ž•äŪ¹L×Mrvh$ļ%$b䏌{ˆ¬=\Ė eZ ‘FrŹŻgOČʊĻU^=‘Ģ%lķźŽ»Ŗń·ĆbK ÷¾“ŗ=Ńßѱfš’Łicā.š$»;Ž!¼Ü‡ aÓµMž L•„Ļ0RĢ%!ĒP²,r%!wQ Z,é“ 0­Å÷mģõ ©¾˜É§ŗ"jY!ģ4>Z®Gó¶ĄEŖŚj-l°––Žā§šD"Ŗ&’‰‘TÕ9RQ%HUU5±D•Lą$Q5a)Š `‡¦¶eÄI# 8¹ökĄn-~ŹĘįH}’2SÓ`KЉGŌĄ–’am×ؚĖP„’ŒjŸźÄŠF®©„TL6 Eŗł±ĪčŽ=@vžeõT5®“ŌXśj2[±Ę“ØåŽ~·hRņ+žųćÓ:W²1 \©('2+O“¼mŒćį“S&š‘𳓋DŽņ†9ĄŽ…%:ĀP ˜ «ö³Ē+ž#5£|’™MŪpž„”Ć]A”¢gU¢øą1ØŚ6Ŗ?NļōyīŠėōy€£Ü?ūė`žśŗš°īƒäWÕŁį+\Q[0Üc™¶TA”|ÄU^ÜŃŖB©³ršÄ ¦RiJ;{w֙©7-ō£vo0XØž,O÷”iė“ł„QV­¹)Ź”ÓPŽ\3Ą…ĪpՋ#A—Ɣ逐ƒWģŗwūõĻø±§14Ē“6ģ^@:zŽĒ1öŪmt_–īBsFŃ1ó­{ŽZ‰ŪŒDwŖŽCˆ<°äf`ƒŹ¶®l'P!/Q˜’‘Š"&± Z¹i#D¦ ķŠwé™k.MRŌÖ=¢2«Ŗ»$„[&Ū‘±½ŗœLūŒ³4 6­ķK«…vs-mŗŽŸen`ŽŪ4-Ī\āHŲAh“ʛy׏qŽBøĆ]æń£Č|qZf³67NŽÜ8°ĒĆ41ĪĶĮ™UVV„ ƒsv$Ņ1ņ­Ś¦B"ˆD Ą5K6†IeˆoˆćOØģ{„Ś=ūĖįøt3;čĢÜ śķƬāv©IĒļ)\Q¾;mŒ®yGå–A€ć¼ńY›Äwóœå—ZāÖ=»é4¾žĢœ¼õ{zpk6ŪY³”ųR8²a¹ą“÷ļXš üClj‚Ksō˜CŪӆžMĶŁĒIż"I¤5m”Jņ³6ł“(¶­”y$š]½ķć"ٳLN«³ˆS P õ™-Ąå4e*O•`ĮjK†`Kė@ю;ŗO2ėæŹG䌽XÉWĢCÅį–8ų·r>>©f(Ččųö³v;…Ŗ§'7‘딉Ow±c6p“¤Čœš,Jw)AæĘ<‡Š5;RŌ­m øc]mpAśGn’øžEŽ8/†õ'E¾Ō.,å|W–†6¹“õ_h®¹E*’¼’ęŸ*a1Ś\o[JqĮ„« gZ¢ß“ķ.¬]årż ڶö/Eć#0]e³®Ņ=D@ʤ]ßW·½Öl_eŒYJĻhš“QøR‚‡•sm ļOįżJ=FąJudöC@‡y­M@ŻĖŠTNį q¾åe+1¢ģP-ęó-Ŗ¹ ]¶KCŲlJÖ0kCaNybˆ‹°#Wš’jƒŅ7Lī‘)TW¹Sâà –ńˆf”™µĢ@ .®&•4ÆJč’Ėo4®šŠ9¶Ļ9šj@8К ö Ņ0ö”Ęa@Dp7Õö7;ƒŅiŚ­AU:ų÷Mjē$Ķ|āh8KÖ*5ŗū„É'‘rÕi ŽF·ˆį0­HDA“\~$X¤ą„æāū£ł}ďŌćtŒWŖŽĖ%v#D»~›„؊lˆÜÅyØNĮ„—2D÷ce>¦ł”Åg?‚ąÓų}¼M÷Q‰§—ėxnŽĢii_jLÉB½å(F/ly4˜`öīŚüė鷘źąŅZ\] DÅÓ&łĆ³›iĘ=P@Uؚ—Ė­QŸ,ųæIą ^źX“}V&øę?# f£fųaČār=Ś}¼Ģņš“\I,LGWHė–ē^Ón5«hŚė‹wh©y‚PēTm3ŚZČÖøķĀ›ęNJdĘ"šdŹ-¦¢©÷+Ć䈺g^Ķž—#Œj±\¦źņRNŁ ą€=ķąœØ±7ÖßžŠųRŌxŅāŽÕÅÖRk­~RćŅļ¢Õ®ē˜6(¬ķ¢5öeæ·ejõó#V·LŅa(¶–BŚā®.s\ź½ĪuIo.ŃQµqX‡(ęœ/y’Ć™(µęŁb2rÓ^°Ņ*×8gŅ‘QćÅtõ&F‰½ī;ˆõ×bįĪ:×x^[énŒBņ ƒšR+CˆØŚv9—⯖¼=Ę"ļVńĆk”µÅ“•„Z  •%²8łŽ'ėYß aŒėFŗ1J°ö¼„—X»ŸrŚ*%²LŌw…Ra)GØ*ŲR®\…:'½æĪ+JŚM7Z³·žŚV8:™™…'Śm Ś;æł3«āūÖņ9JĢ)Žy|nB98K$›% ‹Ó&½¦ÅśQ1Å‘%\‚Ī…õŌųė]Ōæøµ‚ėz·OÕŒr2øŗœÆ8ņ҃rūDÓŻĆś@ų²©HsHy_L_uƒM¤mYØ©Źqn­”GŒ2{doZ^écØ [›­ŽŪ™ L‡4Ņ&B€y„ŻŃl„\FÆ$Nć2Ab”ę ńiF+O‰Ūf¦gSÖ<ÕÅŌߔPo¢Å“UŽ|/‹’t\­ÆŖ?J˜6»³Nź«=ČŽ=¹?6ā՘8ĮE’ŗ;!wy©¹°ć›“Ņ'Ś „œż b¼ī_°„ĪzāTžŲ½5ŸgŖj:|~ ”ĻŽĶĄ“t5ĮĄu°/t½;P—ƽ…›{±kKšZOY*_|rĆpŽÖ…|$ĘÖL]WlźĶPȳs\½Hv`°+QØÜźŽ™\k¬IŹ‘Ģ-hLI”Ŗ‚M•T‰ Æ·ŽŸ]·š/Æę‰k€y3eŹH°ĶµSf Šn|{>o”=Ž?+ķe®`9CƎ8‹­ŒĻr%W©É! _ɱ°™ŗŽŁ4NŚ:~zIļŽ–H5ōČ-"Ē!֗z¢PōS•1;@6.·.Õ„ŸM‚ķ䛫w˜œwœ”-$ļ9ßEÆqN‘ŗ»ÓX¶ø`•ƒpĻ\Ąr#I¦ģĖ—ń³„–Źy–“ }*Āö…E–šŖ½k"ŽZ»`“GČ$r'õC2g:ˆ¤ER©Ņ0C]Fņę²Éį¦-Ē„|ŁÄv6Q2+†ęJęפćEš>4gŚ.„ĻaE¼žfiĢ©—/ūKÜS3)…_Ѳåż’¹)o®@Ģ@ĒͰ S ü¶nøøYF⁵„§“}FĘ[¢kecK\@5˜Wh;E<ŖvŠ©øµ›Õęų’j}‹%Ü6i ¼·²¤ž.tt¤LŽĖ×qœümbQ³ÖĒ"‰żR1ŅKŪ‰L_iM5ĢxĢĀ yAØīRö•Ė.uć(¾AV3<:")Ąņ3Aŗ˜=K#aW¢A5»w˜ 0Æ]ĢiT¤ł<¼Ķcś†ŃxÆlśjfÉ79€r-+#Ē~%伛°ƒÉJįK34ŠŁ‘Œ ¤‹ó?„m“EŃöCµf9«Fh½B]³eĘį™mhŖ=Ź3šŹvYKĀŃB=>6ói1 { ›b”6×@Óįš,£Œąģµ=':‚Łęs†Źł0Yąń įŽń廜é?Žā:ÉƜvåmĢī-J%q\mc*™Ģvnd³®m~R¢a)Ö#ēķķ2b:¶«/Åj>OŖŅ:kyRV­š­ó¦§ŃܰŚ2. ;YĆG0†bžŻJÖ1Ŗ,“o“@”­Ń­ hcvźĮC’I©Ś¹ī4[ڹä6_qźF*.čŪÓMÕWó–&4䛯]fGn­Źŗ L7xäȔ› ė叜7skœ[i”Ś:¢8č}PēøÕē”­$Ķ_E|Ø·‹Iį«Ķrå¾Ü›@«‹XÜŃæ3Ż@×QJx2+9Pż©āęulĄõ³:ģ&;£ZC&O¾ŒwsR'_Œ$ŌsÉW·žMcņ„QgĘD Øs»ż ģ¼n‰¤Ģn J÷¹¾MXM\CrV§1ĶF¶¦‹”Yj’N§«@ ½?ĀdlwŠ÷…£ŒŁ­ō[—ÖuUę ǟ cyŠÆ0³8łżb¹Vj»+rĘGĢ­Og[ĘGĻLU bĒXā҃d 9E6*  *p8o¬p~”ˆī¼Hg|åõhk†\“©ūZāh@KSšwźŗ”²XͶž XAĪ×fĪ]@ē3cš\µ 4ƒ\U¹órĮ6–āĢLē–Ū4J"ĮlAgØāŹD“vÅJRv’™G怭2d©bµ/ĪL½XJ“v&z…Āįļ–×z‰–·žĻHqĢÄskƒXĆG8•Į€b]¹fė<»æ Šoup\A> 1t’ZŠ91y8ćUkp­»‘NR)‹ņ|¾±"µN vólĒ ŽHĖ\Į†f^žŗ„q&ö¢²ĶŌh,T ÜĖ&R¹D§šćŽŠ4ķ —/¹7ĪÉĆ=– dhŒ Ōn5iģ’¢8O]ā ų…öZˆ“0†4É$fLóźĘć!5% æ 2×h ,:ā ±(WĪ«”}Ī»yń“ęņ§zŃÓVŹ#.ļäB."ŃTLHŖS“S-ŚŖ'Ü3ˆįÖėĄztś§Cmœ&/cZA>­]Rīl­…„ńę§‘óŽ\5¦Ż‘½Ī›+piåĢH ĒPjcB”ŅhĀäļ LØVŖ‡v”Ī”œ©_†gŖżĒ8‘Uˆ—qˆ»_vÆĖYåńę|ō;ĖØ7TÕUšńZXūįCüA¾9­Ź(”K,¹ÅŽ•H c$3Y)ōuD¢!¹u$A7æ“nŗ.‡Ā’éł®ó/W½ƚ"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‹Ę׀° ,9ƚ•×f)™,ĢĖģŅ’¼YšÉ ¶Ą-ų&ķŪ[W ĄĖ—\ŪɌrC”ŽPćB­JāŅ×7Ś£©dCÕ\^ŖŠö)—_*™ćåš!°¾Ų…:ÄÅŚ•)fŖ—}Œaö¾uÕķ6śkāE#›1 ī]›Op¾“Žź¾«Ų]1ļUĶ:!ę/äĘ~ŻĮ×²<^IÅOT1D« õjś"ŗGE zfķuœ‡ €}cnļŁø.šI<ÖēXŲiē ”üéŅü^Žōb`˜§`|«#ÄTØ«Ÿ”Xćö/ÖŲ—]|±JįŹ±ć†ńżcöSž{’6:Ė9·źŹVDaLfŻf!kÜ/‡qŲNPŅŗÓī¤s®M™Ļ•~ˆšķŒ6ś› }ak5śRw ĄpŒ_¼ ƒZ‰M«„YĆÅ[āå=x’fōšµ‚f“¤»•_“žš ˜Ūˆūi.hÖ8õrņćHµŒÉ-ÄQąiė·ŃR{zŗp«š\š†©<Ā<<Ļ֖%ŒŪm5Dq QÄ]–H¹a%–e)nŸ¢/JŻ^ņ¶ō̉ bĀ?§C5»ÜłŖę‘Mė˜ń6±§jG «‹¤cÉ­"”ÄīܤŽ3š%äęĪvŁDMŽs­Ā%SP:Ķ!1°Šė Ēų¤ł5;W s½§Ö²ćr}xkVø[.y£&•Y9„Ķ5½ZŖŠŹ5]w;*ÆyN)fōC!|$˜ĆFq'wPP:Ų#@%sŽ#xmåÄØé  £"±TŒŒ{åJ©Gc$dš¬rŖQŲv2b^ąü”­ų1ǘł b²4~ņŖk1ᘬE‡ø™”( \ŌɐqÅr•Ž’łYė¤Ż 2ļ]·É¤»· ~EŌl»gģ2’0‘MæivŌtŁäŽpĢĒ+Å6ļŚ<źĪŸ©K}Ø_Ł\»9†g:"@Ø`ycš9…Z@ŻB¹Zä“iXV.𨺄HŻArnē»r•%s‡CØ' ūæ„ææ[kŠB¼ö–ø‚¤æęNĻ$ēJiĆde!±ŽKcģŲVY“żs`Ü©"#ķīżķOZamévē47™XŌ›šĪ wµĻaīpņ•_²QDy“™Ś*'L’<|ĮlˆmĄŽ“øåČ׎QߢŸ*įį8‡č‰ĄŚēÜRÜŃByÜ;[Ÿæ.žīiO.^vŸ˜ /“Cf„ńM²Ń]­V.°Ö:H¢\XSŽL;­œ®³ŽRnIWpB­żb˜É‰ĀGć÷ź7L³š€øyõk…O%A>E©ßšm¼œI ‰­$ģvšƒŹ²;_ņqšh+NųłäĶsäÖłG—ŒJ§#jŖ b%ś‚ČT¤!gQh —q7ɁŹL™G¦ŗu{ˆ°¼¶”s²ŌZ3ō;Y½kČ ~‹ėļŖ„s'“yš¼½_•8Ļ$<«¢ ‘ĮyüŠ»B®`M7HL¹5#[9L`ķpŁVź”D¦ßTO«čó·%ć—ōć8uāGR¹m”ėÖĪĻa#’éŹ1ꨭaē#e/µ^FĀT±Ļ"³¬—€Å‘¶+¾-keĪ·¼bĻ-.Ó1A¢$˜8¶¶Æ2صV]üƒ,ň‰¢cvT‡@āÉēmæĆpĖŽXš3ņݾĪzsm­+]˩𵋯~3£c]%”`.y ‡\ IĀ•„Ž>!“T՞Ä$›y4Š)nqrŁ›‚!éžÆÓķ×–ŅāÖB˶¹³S\kĻ\k^ZƦ­ÆģÆąlŗtŒ}Øp˜·Ś Ä‹¼ÕJį_19?JŖĘ?CZ8żĻö-©²AcTlmģ—z†|„Ēp^«V¬c.s5xłŌ#’Z!$żŲ"ƒ®ÕņūW–m.XnIwR7’©4§eWĢ’6ų~ŽÓ^†{6µ‚č īhq$W˜T×NE>(§"® ©Xe!]yåÆŁd`N'2Ń ²ĪA“dH†N ēr„5±Nm‹øūƒŁ¬y_āJéĪ'“ÕS ~Lˆš–“ĮE#Ą%`€€ža ‡^5Å®AŖ¬ŠŠ)CB¾JĀĪŹŻ«°Ė]į²bŸ˜(’Qł Ū@‹ū·‘©–Wńq—x =M$JǤä[¬ŲŠ&UD£ł3óļä¼ŚMĮе©›i§j·÷š­,7i“Łźs›»'QšÖ9¤ÓīģÆ$¶—F`š9s›Ap—¶v|Mø¾{h¢»·k˜ŁŪ$ šŁqd-lŃÉ ‘ŁšE@åē.܂«ŌĻ)b†©ŻėS’y"÷+;GŌ•}Ž)iRćh9I }ĘyŌc“0°*Ї|Ō†9ƎpwÉ.2ćg[šė.“ķKL‰‘žµ+Ÿ9](ˆź·0ÅcenÉds‡Ä]“ø½–—/kZv}OŠtĶ/=ļ‡<øÕßįyœŠŅįo,Æ-h£; ¬•‡ĪDņv’„™ä,…\jõéå}[SÖöKd™Z§œĮėQŠ:U”tlbegÉ5šÄ±LYu\ø_õsäæÉŻ7åVŠ–Ęä„FDEϊŚęSo ’,Ī’Rg¼¼”6[Ū‚$s"Š;x!łū‰øŽ}~ė5]šĮÅĆ0Ļu2ēpoŖŠc¤¶&` œē¹Ń™Vq½ALźeźŚMEt‘YŅĶ,I6D9CuR©{D}ĒnšķU7­f‡m Źć_-1–/Ė9*O*Ģ)C”fL“s“āl­eˆš† dŖĻ£BĮKDXŸ2#4z±[(“—h),PmówĪ.Ö8›SüG@h¼t29"Ķ2Fęę>Į šśŠTŌFÅõOȾ:Š8WJ:GÉš^<Ļ’9dk„Rµį£Ū€XęPփ)ŖSdL×ÅnVäĢ+Ēų¹ČŒķUŒŹPŁO0ÆMu'+@«ŃŖ»‚l‰e¹Wܳl«ŁŪ\›ĶŁ1tŖŖØ+v¦Sé¼ĆUĀN»ā]F'Ųhų!Q²¾Y«c5 5­q.p©]£oł­Ę\Ęvœ+¤Ļ¢ļŒdóŖčYlQŅ 4¹Īs@k ߚ˜(yuÅn1Ņ8ƒ˜Šųł„Ŗ«–ØŚBTI7~ū!SŪĒ9,łbM4evą‚¢‚ąUT¦1LaīöŻ/]ÖfÕ%ÅÕĆčÉ\A{ˆĀŹÓw& —]čŗK-0[@Ąéah£­4cm+yW €kŌ³×_Č5ØT[Ī6ž|ƒ©fõˆ$%•EBĆ"­"“=P‰$ąä ā.ą5óī³}ØŗFĒ%ÅĆ ,4ČņŚģ8S¹}‡¢iś[#t±[[6ąHApŠ0īQėƒ†ģUīā„zfɼϙ7ÉKåģW å” ‘Õ?Żž>c£¦źI1īļQ×ׄļęŅ ƒ][Kq!£ĒRiĆŗ3]K€čĖŽ¾vćĒŚĖčńm›Ó–Ö,O9Ķܤ ć`¼›ōļŚ6Å7“DŹ%7{V?ŖĶ*Ź]4äYģZŖüĄĀÜ"SéģHŪjš•~āxó ÆpØäĄ­bćNÓī©ń0C&SQ™4=Šó+‹œlˆĮYMZ׫?,ųū ĆS†–iš‚­ T%Uˆv՝fR24Ļ‹"DAAøƒƒģ™€IÜŁ4.*āõvMqšM™™Ćž\ܙ€p9ŖiJ×6­Yį K ߌb~BÖså9iJcZQA^"óGŽpö6*ķšńµ9fµČĢFY§YU$ m0°Ģāl0N+R†k.ÕŹ.1ŠhPŲĄ%åü[ņ’‹_«Jė=>īf™äŹę4½Æcž\Ēg©Ą‚I"•Ę‹č® ł—Į,Ń ŗœŃgd¹’1ÆiŒŃŲ@i…U¬É|jšņ‘oüŖć]žQč2ŸJƱóńA]¦f å8^:¼¢£¹ĘĶå#P“Ų'\²ÆČ, ›ŌyŽ”A•tśæŻIĄ:%æ kńµ²Ė,“O•ŁŻ ¤ FZ‚ZʇH$ E ł‡<.ĪH˜}rŚ“ņ8zĢ?hŪ[ĒSĪ˜Ķ ŸPt„4¦VĄQ6Øåœ š×īņm’H ķø‘P]:͵ÄÕHJnšnbˆ³\¢;?Ć5›‹ jY …§”Ųv“ŽÕ¹I{ų¦‹oØEA$чgā:œźR!,ÅĮÆč K“y‚°¼ūŠ$ķ¬ł+'V"²VN¾Ź&šÖ̟~$”€Ü,¶«tŠ»u' ‘‡e24M2O›}OV”ÜC ²3`ŹÓ•­ŻĄ…•kĀćKŅ#óMoŚs8fs·¹ŪÉ'O‘]ų.`q"ĻD’Ź5īSqŅkĀ¢³‰›ĢviĒN«Q·!ŌXŅrHŲL›#‘4Ģ š€UM¶Å(Mc»OŌ(…šL%;GTō`²[ØéļˆĪÉį1 §;h;Ö.“^A¹śõŻ—ƒ– sĆ® ”œU/”cŸ'g\üĪ%ńć]_q!žՊ†7zńŖå‹w: ¼~™ ą†˜Č'¶Śh}›ŌĆ„¼"¦6š5œĪpĞZyV³6Æ©źN'K-‚¤ ÜĻ}7µ§ŽJćæ™aĆŹŽŽ[bJ1łkę>QÕ±ŗµŗµjż‡+ŲŖŠĢł‚żgm'1sU«f­”čؚŃD†č—ææ®Ć¢éśnwYiš+óKs@®bi]˜(]j÷T³ku+ėŸ‰dLĖ•Ķ vRA4˶††§bÅDZ½ž;“ø)_Ļ\‹ls”ųŠõpŗ’*×*}„uĢż‰Phõ I°0Mæ UnŻn߁˦ŚxŅ<\*źļķ\›‰øŗå¬°Åhlu˜ Æ@ŁÉi²žpÅųUš/2īM¬ŃA¢ ⦧ūгT»Õō¢)ĢŌsj|Üa)Pd`1‡ņėą*p žŪŲŽ^[D÷ó‡iĮY<ӔØQ\>“ÜńuÖÓ=ać-³-DŗĒXģļģPƗ24cʉ,³läa䔞4¤ĄĄį×iV3£Óyī­­€uč`;*i^…æVv –Š•†Æ ‘‰ćMī®Įo"yć‘`Hį*ÆĢĘīAA†efżˆĪT\…Łd¤2Ķźß5ÄN łpõ @ˆģĀv·„4ć4ōäYÖz°1…ĢߛohŹ£Ösē×>ķųJSe:F ’O*”õŪkL^ŹSy|1SE—É“0ØHH×¾Q…=‹†’®ˆ—ˤƒŃģɗTŁkśt÷?3-šęę{ØÖāąq>Ó· „lsč·ńÅźū‚ e\źRģĘļ;v\éćq­"WīÕ96QoŽD¬»!Uipj¤E,T 4(üé&fĒD«7ī l:źŅßŪ6Ł×½„”¤ŒFŻĀ›vīZs`Č#p5®+*÷Ī>pĆ;ųšEĘģĒ &W€ĒxžĻj„¹U+¶‘’±Āńw9¶×WiHĆX_:æK3‘hŖŅ$QFk½"›Q(“A9ó»Zž|kާ()”l¢Ę–0ɔü»ĘÉN“EŅ+‘’ŠõSŖŪӁAx¹¦%9–j颛” ),ļLĘ €ė”ZŻCw’Q¼oĒņÅ@KāvWĪ²—āŪO3ż]£”›7 cD­9 ]Ōy[%’rå–y8YmŪØ NĄŠH®;TPdŃqø }|¹Ŗ=÷\U¬ź$c%Ё†”¤q4g é9A#n;Šśo†mŁéV’Bc;…kYfqČM=Öę „“ķ ?–Ī a,m—2…›"7»bŹŹČ8—“™Xn„$K‡Ķ\ŖÆÉ$”,B"tģn”Š*Ŗ&eCæV$h-­`Š6h "¤ ŗ•4­i\ĒXɋĄ£žźøŌœh„I ®QPŚ@N+8»„œ€äĶæ Čgzåm¶<=öd i6łÖŒoõ$%ž’²®AšM¬#˜øźß"ŻÕ~=WN_.Ųāér¶?Ź+†4Ėu/Å-Üł® EnXŽPZ×3“k€.£…ūū«wŁj‚8ķDŽĀ'»4ĶĢrē ­Ė”9 ’źzÄ6­9¾`üĀÆV¤UžR [l²ć^‚o Pv›„RvĪ>2¢° $ …0ĶßÜ&™ųy’G9Ņ®$“ŚVu°Š8ÄPµ¬‰»Š˜ …¹¬5rĀo䮣jž’.e‹åž)ĆZJ•{‡ŅĆ!h#U‹dž•ėęh4#ōÓdŃ«ö­½"‰’ßYāūfŻ‘©[Äįp±¬€†Rhkh0uI#\K«µy”2m4›K«†>Yē{ŁHņ¹ÄÕĪĢģĪ©`z­ k[M‹(“{ =VY„‘„ŠfhµvõTE»…$š$š/žiøī(8&õ Œ :łāöŪ\¾'·.5h”ŁCÉEŪģēeų%c³aBvµµP?Ź$7/„Å*ŃtėĢć½ŽœĶr!„’P'ūį’œˆl _į`÷ė}ł],‘qe³£ö¼x‡SœZ{‰ZĶ"øįø¦ū³o/sAą((<¦Ļ™±pӍ•VÆĘ“rųlŻ£T”]ŗk:LZDcŁeŹÜ«@ ßźzūn?_;ˆōƒڼŠÓŁ_3å® E]3O64Ż\vņ®?śÄņµÉJVųG28˜¢eŻę+Ō’D u9E» `ŁA9ƒ ”zˆjŃām4lõ}%d7åÉ>“āó¬i¹Č7|›ęƒrł‡, yĆśś°pvänqĪŲ2Ī+6™o.“(åP,‰^›fė šč”Ž&ŌĶ­Ō7‰ą$ĘyAU—i£æCŒŲČģĪ.®ś ^ČzČYI¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ¼bųNœ¼W+łia&d"į®2Uė<(·•†ŗå7P(>żb G5ÉĻ“tAŌKÕ ģß[' 9ńŽ:fš0‡P÷רU©qYmÄĖ' uĢTS¦,ķÉŻāŪBŒEł’S‹(‰wŪŅ-ˆņ téܑ¾Ķro™śq²āy&‘ĪŠńŅF=ė¦šmŠ›K0ėDņ:Ž#ĪŖ\· ZÜE7 >g•ńŃŁĆ}ӄūĪÖ©jSpö:©ńĶīķ(ļ­o….< n }™™Ś*;ĄV¾ai߉š}ķ°x‹0én*q[Ü ]bÜó¼hŹÕ•竿ĀC0‡|ąߣ±õŲĐ7<Ń·•ķ¤(ÕĒÜRĖ8Ųøu§› źvJ™Ē²„@Ŗį¹gńuÉ䫯iŚĢ×jķ(ė[jzQO=3ĒjłD÷8ė[°²ź. .=ų/¼x¦źKŠœZł[|łrUĆ®”<Ė“ę.ĮxCF’ a¬SˆāÓ)Yćl}T¦Ąmė+ÅĖ£tż%NsūG[-ēWTē:†ØsęźcœĀcż¢cˆč‹ēDMXĢœ¤©‰‹€C,ł5_:Uā?2V¬S?¢J‡zeõ¬źaŠAŲ7Āć‰ŲØ{©€Śŗzrė#øĖ|įåõåw‚ńr=KEģ`2 c±~1©UžE²ŲƲilNTÅ.ūśŖ(#ńėbŃ£ĖŻŹžą)éZž²üÓ1›šĪņkčQžņ²ˆR­ź!æĢ³8ݰ~įvī5ĆF€^ŠŒårl;ūGROšķd#‘GZ€nc®ĢąõSäY å®/4ļ.Õvž“®7¦×­µV鐢p”Ć䎜nŁī}åžÅÜ®=ūkJcŒnkĘ֐{‡”ŽųZüwRi×tKQÜH=KŲg$AY—ŸØ0ƒŗŁ˜;"ēDˆ£ ÖqŸŌ“;SÄĪ…iŽ ˆż¢:Ü-nc”ŗ&ķm:ėŠčwvŅDÖŹķŽÆU †EĪYk ejD>}DÆZ3…y,hÖ琔j~¤į•Ī>L²n֐Mh¦č˜²`™Õr’äH†3Z÷LŪF ·µīc"q!£3Ž\Hyä GEŅ!ÖĖl¦ R®ŹŚøeør¬Œć¼_V²_źÖī@rć‘87'T+gvśW‰ƒmQ²ÆŹ=£d˜š:ųņZ:Jm”1o8x铌 Kq?w ŌøÆU¾cµ„å›MhÓ ™Ÿ^*‡‚ŅĢĶē]ŸJąNÓ#÷]YĢśT»#¢yŁVK‹qÜ•ŌܧćĻņri‚‹sG’ˆŚi, ĄBąvŹ ré,ĄĶ±+ƒ¢“„Ą ŽENīŲv֞8ÖF’>ҼęRGķ­“p ‰—W4¦ļ«č­~01}’ÕüČ.ZdxU]EæZ2W(Ö+M–} ł¼¤k£)IǵéØŅM¢NøMdˆrģb”CĘńާ ¢k;{8eņGFgžÕxpŒZY<—24ķĶö1]Æźœ„œ°rJ×éõ)¬Ü³äcƈlqŁŽGŒ&źC|;ÆĖó7ęöÆHčk|ąŖbłoĮū6L=$že?#\rir­"±ˆZDP* ¼½/?9›Æ™O ćŁ¹…iX„Ų[_§®Uļ^Y¼‚ņ,Īš„ŌD}#īˆŪēėŚŸĒqÆy$’Ń™Z Xźb\ęåǦt­?‹ōM7A|éVq²:æ3Ø\+€ uIi"»OBj†cĒ›XeCŽø]›±V.÷LāķFė v†D„m+~Lﯨæy€Ąķ8ŽĄ ŹcōŚēįŪ;¦n仒#µ®žJb…z굨uŪ»g‰mc¶ŽQ±Āź: TRū‚—LõĄ¾BXyUŒķÕ~HÕ²zŽYęģ1VFĄäGnõµ†–īü­&’X’9fUŪ¦Õb®å9ÉĒW/tk{«Y0–ųTČāKˆ¦%Äø‚05<œŠžÄW:V¤ėņŁ-|F 4:øŌ(8Œ)“oUĢ”dēīaȼ”‘e—8ó‡āpĒ Ś›Ą^MWVYėÉilÆ÷Qģ„{Ó^oÖ„ X4Ż,Ō±é#ź™E \Åś]ƂY\Iqp§E·Äڼzž”ń!”·cCXBE 5;ŖI;6`¦:–¶4ˆ„µłĒŸu”b+ķ B˜é1‡mĒę\©é!ź«bö€ū}ø¤T”ä²ÜۭҊbŅ8¦Ņ(§sē]zŻå"aߣŲnŗ"Ǿ\ι:żb—«c[SXjŪÕ¢,Y&A“{CŬm»>£Fp” Ń1?¤ņEÉT :īEÄR9õf[ŸÕ$ŒFźs¬Ėk'\ŒäŅ0vķǛҭ8ćŽENǹ;@ę5z‚„,c+mŅ2†/EtX°v ½‚ŠäaƒXķ¼4FցÜ0 l‹5Śl8’óŸ”Ó’łGĀÖ*2¦E%ŃzD•¦qķŲ5­Oå?^e:kģ®-Zp˜NƳ!Ś\s¶›HŹiˆÅu}#ēgZ5ĒSmõµĖ†0wŗLć`kš2:»ĢŚąH #ød;-£5ZģxĘOÖ3酼±a"%½W!ėøŽ™CfęžXēa£eģŹÕ…Āq‰®ŖģP„Tā~ņ’rm> 'J±Ń£—ŁĀę>FWĆsŸ+å":€\Öē˘˜ŠEĪo5y8ƒ_æ×Ķ»ķ­ļ&c£ŽJx‘Ć@ÉBZ×?&l œ Šš¬‘j9ĀJ*2e©ŲĖG³’fq³ęé¹DL]ūNP¦Ø]ś60{‡^‚AØŚ„µXپ'qŠĖ.­‚ĒĒüG?:¹ˆe榨°2’«™=€‡^Aė5ݬrtD5#±«AĢģ‡Żlް,t½6iSęQ÷Ū.Ü~äĢL~>µUgŽAć,/ -yõ՞Ń!a™Ė+%‘~AœČyhÖČ0jĢÉ0Œ­Ņ& –żĆÆu‹kmKJwűćNµšW¹°åk²¶?R¹Ŗ ­q5%ehW:nŖ> ńž#uQµÓfsjłf„aLįĢ>+0nF¹r#œŁ†:6{édūĮ?ˆ+4®9bZ„dy\*WBQ®rŁ;ܹ…I i½"]‚L”«Xńž”l#ŅųvŻĪŽ¾Ø•ĻžGŠZ9š2«qæą­:éŅj¼Gr%115DŠ:C‹Īć˜ģäT.5åĻ é^9ńö2Ÿ}]ζŲ^?Ų GRh§Év†Ź,•¼c›zLā$"q慒pƒ§ŖČ»j«3œę!Nrq±¬gŒZŁ”#Ś;‡.8/”ēŅu;ni¬Ū µlÕĪj@vōPrQ[ŗvÄV:%ņŲavŠ S ą‘¼Uš¾ Qsśv« I©ÜØŖˆ™ā…7Ŗ©ą$.Ńį“d =„sT*$¼øŽWä“6;w£č]‹¼c˜l}ĄZ*›FVćȎALČBW\<{ U›,Łō‹ĒļÜ·Uµ@Q]“.Å×ā¹3ė/h­cZ(6 *{j¾œł{NŠRK+Žź¤TŲfµ®-Ł|”„!JB¤!z„)HB€õŲ¤(Jż¢/•REĀJ7pŠN[.AMvĪ"Ķ×HĮ±ŅYÉŖ™Ąv˜4EÖĆŹ6ČxŅó!ž1ģ…^É^ĆųĖ!O[1ÅĶʑ$é3KFަ„±ķ”“YČ»“:u!bVļZüŒšMU±Č6<śe¶³ąó=ń>Išę=£0ĶBŠ×¶¢¬õ«Pjӎ+öęļMx×­›±Įnöɉi,«^]č@Ŗ(įAPU]ÅYōåØŅłc±+w­&;ģ‹“ĻÅ·}éH¦&1pĒŠ1Ų{J%ߦśł³Š 0ߌs`[†ĀXā*9ŽŠ»W N&°8eÄ; = Šó…cūȅ¾{>GŃ0&:™k\>BÉl+čY”f2ĘN§UAKåöŠ“AĪ’¢Ś·Ŗ¶jTdČą‘5¶ĮÓžThģ²ŌäÕ.Ś\`·/#`w©kļĪ5ÜF0ł±¬:ėIf™jąŃ<į€ļ-oÆ#©Éź“Sx4Ž­*S·¼IBYł9“ŠĘ1•}/Ņń•'G­Yߢ;ˆ‰Un=5Ų>.Ѧ±ŚĒśĪ{¼ą.%š×.½Ć’U­o˜•ņĮÆzŪ„ó^A9Ž'T.Yŗž»eŒ&>æ'^ĒøC°©›†Śø5{–}Ė źĘß=U³¦@ļ½tÆéyóQcB͊±īó!Įzž3«³ØÖÕϜ4™ąŃ72Õņ1yŪņAĒer”ŒŖŽŌŅ®U£–vįqÜv"ghÅUæ×k%£+U‡zĪ]1pUŃöI‚±äi18Éä+ušõ&cŽēm–zuēpīmÄŖ½ķ»t lzQĶ“üJ×µaKĒO%Ž’oóźW"}sṍŒ E±GapIœ‘T] āˆw6\żw €5VØģ¶2s€;HQķvFÉ%i–Ł–pž$Ż÷Ī7x@]›āŗlń!¬ŃŁTEŹBQ „Ŗ ”Š =6j ”4–QĶĮĀ„t‹¬ FOeK¦3vųŒ¬ų¦p˜ågJ¦ŖHØŅ.Z}ķ6\ʉm`ŖČ2XD½ĮśŃīŌž›'ˆkCOQ4ķv—Ü2īĪ+Ę Å3sްÜĆ©Ą…0رģ\ēž7>¶ĀĆI‘åŠłH”Ž~Ķ“¼B†ŗc+)’IF² \5pвŌ RØ]»°oķÕZė ·ŽCķS“~e„\įerȉ£\7UćĢTƒÉŲŹĻ‰ņ6+­qźĮN„epÉvĢ=Ž–»aIiÕq+k"«§x£Ü|Y¦I=E¤6!¾IA!łwhŚMÄ?‹˜6‹1šsŻYY­{>W‰<;X*ꑌn1’1śM~ķźīxļ›3ž`ˆ•Ń^.ɌjŹa›“"āBæ­ß°ü£Ś%¢ńR9Š“¶Ļ”APŲD’Ä8”ĄaĢ×ōóoÄ÷·āLö×oGJå1ŹpÆ1 äÅepĶŲ›†ķ-ĖrĻlĆ €ķkā%ŽiģÆZĮ÷(āźųk™ł‰¾«„_ Ędüa+›EG*4Ģhß%×*š—›rd¾•Oms·J" SOÕYc¢R&C{É5ī…lė÷ę¹|OŌśĻČēP ī-hÄņR«šjŅ[éšäģ°heø¹ŒĖAźF×µ™‰ĀŒyĆfś`Q†"ŹģZ]”n .ö(2Ķ«£jßĶĢև Ś)BÖl.¬Ģܘļ+ >Cų’…jŲė…¼³ņĪ÷É¬å—łeSĖ6¾bŁŠS.£ €Õ“ĒS•y,uåõ3ĒĄdžˆÅ‚īNÕ7‘Ü,{— \j×§±¹l-†6‚0r‚ę“ķI¦Ü0 IāČ,F…ü™óÜ=Õt‡Ö!®`Ą ģ“å;”fC{ xĮyg„Æ+G~ķe=U[µn0h–I¢¾ŖCܤs§l’Éõ*‚AÜ7é—A“ŁHĀrч»2åÖ¹ą¾Ž@3Ućæ՚*|‹|Ǝšś±dپg>Žj€Ÿ±žDœ§”$ÜLP06Š‘rfɎūć® §[‘­2ō&5żZŸ2ī7³ƒ„:a±ŃŚ óŖ¶"mKlĘJ¾Ø`12eĖWmŅÅā^Ŗ ī Õ ˜‰wĮß` y®Ź%Õ%#chŽĄ+ßTŅ#1éńƒµÕwiùXīFē‹N‰®żĮĆvÜćy“}Xz…QEQłHś»X÷’óS+5•|Hęć(Ż0* ĢuP ÜQļbĀŹ;²ć4­ŠҤņŗ“7zņķöĮ¢(Ż$®­ęŚNŽU„‰³-;£Ėņ;1Lą«5&Uż‚æ…īŲī~™!!&I“C(X˜hc7@Ø,ŽÄHȑ]Īpõ5²Ńö¦Q§Ū‰ćQÄČ×5ŲSŲ~5åčĮA ü7^Na’3V€Ē5Ķƾß(ŻČ¦4>ĀY>±SEŻ¶Łė5Nå ~ōf›ŽD€n¢‡Qe hTl†­¶1–Xē2dhˆw@ÅÄ5ūėėI^[vņænXšĆŪJž’JšmµµÜLkå’x™°:G8ŖŠv}¬BĆĄ'T®BD×ā:‰ƒF&=œDqBjYŒ8åY ‹püļh˜J";õßT鮒ėV·2¹Ļy™ø“S¶»Õ:ˆe¶•qį€Öˆ_°S蕚‹E ĮµY»&lDÄ$2ȶI1tŌū3T¦!āCmŚÆ]ĪC}Ē]Ļzą”õh¦÷†Š"~;šÓAĶ!väŒTš„°‡Ž(žKåÖó]ēQAvQ6Įܰkˆqž¼£÷BśĒ‹µmv»÷ŠÉö ÖŲžŽ¢/£é˜ÄP†LåŒC”JbŲb˜@tE†æ3^‹Ī1åØĒFō[ŽeS"źŖåūD‘j*L”’pŗK³ØÕ0mńÆŁŗŒ'‘Õī*?YŚŲ;ā#¼yv,EK¾É3R5Ü%‹­ķč³yžÄś%å•Ģr3 źõŠ”½¢Ńfo¢’–މ` #™®©óäNą‰™¹_ č¶¶ ė½I†KkXó ‹œÖWč‡WėQ¦„U¶kZž”glĶ;MEsw&LÄT1¬i{ߗéŅręp­@¢ą­

ĄżµC`Ų:īcžīŗ ’¦®ļ*Ó5ßėĻŌjõšŌņ†M4DŃDM4DŃDM4DŃDM4DŃDM~J`)»L SmŻŚ" ŪÓ}‡Ż¢/oŒĢē‘ܹЙSēāVAč/‘$Č“ŪöüĮVS ū;µ¶p›Cīfc½“ A +3lOźćhl™E·ń³*¢tķuŹŚU‹>ņ·wc§¦`iJĖæ®Õé7\Ź“øŃ³MŌA`(”ū<-ŽīÕśEļß±™\=ęģlå^G ·‹H‘¼ŖÖQ'łIĒØpŁ6Łj‰&ź>’GLökkĶ„æO›¶Ń™)㢳qYhó;r’„0¬‰Ą~`üWˆ¾Yß4¾īĮ”Ģ©«Zvx Õä;9@[Ī‘ÅĘ& {ś¹£cöŸÖśF<Å]^:r6”˜pw/hІś”»! sB½p½?)Ʀᔋhid›K$‹+[¹@ŗ[6TDĒ!‹½ū¾ĒHŽ+€į,q†v×`–åÉxƒD—Wł›is§mī®ņźŃįzļ' cj+“ą³›įA™ļ­ł-ČG&dé'³x÷uŁõH»'hbč™[ÅŻvФc ­£'·l¹S1ŠU愛īM‚ž™ Šß®uWKć=@_źž”‘FÖį²ø“Žhzuõ"µ$ŃDQć7d”bX8¦Ć8LH%éL.‘æö.=R‡{NąčŸ&m„=©¤"#°˜5r6Ōę;vV ¹Ż:ņ'Š9^%c7žÉĢė˜>¼¢F2k¶k¶ĀcAQ(÷­"¬®œ|>Ā #īÕršFyšķXņJŪxŸpļf69ēõA>e×BP‚Ęoņv%‚PēmŠņĶž”ÜTWqōHé÷N*ęYcزƒ^xÜc|B ~ŗŲō‡4Ł­$ßBÕ¦÷‘Ć|ńC4 YhÆ}UȆp dÜÜĄQ, 1c#½‚TåœI‡Oįl¤iGóõÕ:Ė©eNW·Ī±§ś}Ū†Ńi'x§f³Z²å«Cā>åņ‚·?ZÆHN:ŹŲ²:RÜĪX¢ł”•yÅ^"}">tÉ $b¤ŃhtH\E‘ ]ś†¤4łÜÖ“¹¤Tҕ¸һv.™ĀrŃJąŃÄ4šŠ‡€āÜ+JšģÅm Ÿ?RĒĒ)—‰9i(\ķ‡U ³GhšZaHwI9f¹SYŗēa(”VHąLĘ0tŪRŗ¹/ӃŽÜĶ<ŠI­\F#Ą“œ`+·«‘LžQ“q¢tKøEē ‰1żb¢ ’įŠrm|¢a½P;ÅQL?LŪ{öց¬·6™/0°‚Ŗą÷åÕßz#ÜB¤8ź™RµrŅ)Cdƒ’2Ī–f”}T½‡cI#TU(‘DüĮūŹ $6āC\WŒ?«“‘øƒn;ź$xī_Oš)ŅfaÄ ‡aŅÖ.bWq±-å ˆg ŚĢüĄĪc™V×|ił‘¹FoL,“@xqUh1'0r„Xz \zܒž&ĻĶöe1Ü2žģ­ÅŌÜ$é vM‘‡~’®ö£#<®ŚÄpmyX[ŠW JĪƒ‹*µŽ;ņbŚ™Mk7™)Ü£Ä c(®ÅkWæ‚häĪ3ŽĖZŌų©iķ06+Ęóŗ?»¹yo®w–ØęĻw¤Ņ~:ģyt–ŽśÆūĖryź Ą¬›cžlć;Ń0YQ‹¼ n˜łdā~śÉĒIbėrĪ€>XŌÕŁH›#ķ÷nÖ@ŠņŖƒfcķj}āŽ—Npŗ…»CA²›sĀ}qMå¹ŪśK`‡Y‚­Žż¦Śgl. ÄźģÉ(õ wewč©– !Ś" Rœƒī9P1Qö‡(€€‡A”Ø%0©ėV½liņv¦²I¤¢„;l"wµx‘Ės÷&xŖkÜĆV•įhp”Q†ķÅhé³·N1xYxĀŖ&ś]Ž1¬ŗlĄ +2QF.ŅTv …"oį³!½’\Ē9Ž;ŚH'°…-¬rŠ=­sy§h+˜ƒć#6dE);J„j‘@ Ā5(ō @I%QR&˜l؇O³VŻs˜Ö•qŽqU¶¢•£y łCĆTŗ~-ĀȁzYhœä1D4„‰Üć{.*Čķ\‘4;GŌ@Ģ“Sø R”~Żoæ.nuyā>Ė­Oģ½…iüm >ŽO¤Ū,oō,JG½ üąS ī#Ü1@CnŌžpSI†÷ōkŽP÷›]xŠ®| +ŠÆmܛ …¬¢Š(„AŒ³ŠP;QæyG·~ŠG“Żß¤;m«Om*īu“•£7Ń]=PƦˆš"\®•NX^ŖF«”"g‘³åAŽœHũѪźéS]Ćā“ō‹ś^Šö…ūaYGof+ Pv[g6¼āžu–Ś-¶u }ŽńõēŸ6K•–ÆBØ×e+|L£Ōdgžŗ‰`ĮO©Łj•LÉuvšJ€G¤q W&(­é„ß°|ē}Ø^¾īk« (-_+Ȓõīkh\MCd|ŠķĖGÓeM`³²“e“6×ŗ““\2&ƒ£ēT4`]&y;«VWmØŪa« Ngė•I"S*ć’Ü©Č5ŲF){ £ŗc;ō»ļEBęIZā !ŠS Ä5ž)Ō`>½önįŗŅĶ’¶ckzħ„H3‡,f­ōąļŗŗ{ż@÷;ØĘŌØa›%H4Š?p±€@Jś›‹¤/V”€~!YäĘźørėŽŖ+›qöÅ^q8ŗĀīćT»Ž™3ģ±²šsf Jׇ~h4ėcŹŲ3žÓĢbæŖUŠqgē[ś7lĒ–l¦0,ŒDó,q`ŪÓE l$Ņ€t4’§öœG®”®Ö;5µ­³9Ž×NīŁœö×õ—Ysiqspžf쉭u?X®:#ŲRųĖ£+2³b cOÜu° Ąw–vųīÉ,‘€ˆ±@5äÜA­Ļ‚ė™[¹3ģÄŽä‹DŅ!ŠŪxŻ7¼šdwڐ¹Żźļ¶) V¬ˆ›&¤)³L[ HݹSD īŌC‰{³>„ܧŚT«@cr³ņ ż×ˆ ÅÓr åk¾kāZŌKdFY~ÖĮ™ųį“åä*µėŻ”kx£d¼czk *ė7Įŗ2ˆ+Ņ'Ó"ŃxœLφ°\ÄÜĘ_V^ʒr–āN©hZµ–£.«Ć†'¶ą‡Mo!-kž°Uqa‘XOR … €qĪk#ĄēÄŖÆ=2Ń)å;“eŒ¹ ]ÆqM›AsV’`É5 &GčŚŃŹ3/zč.Ačc—s€EįŃp”ꦏoŪŽÅĪ$c›(6åŹ†Zķ®ķ˜mYVĄ•gx#ąźÓ‰RĪ<Ā“ ‰’’*BŠ +‹ńC¹D¦Õucg‘‹lÜ7ī)OaŲ5Å4§ĒuÆĻvĮü&ē#¢” õŒWcŌķōXmŸ÷§(=4$Ž£‚¹TøsW©µL“5^æąćø™GlbZ7x©„DĀ'YŅg9„DDDĀ"#­^y Ó>SµĻ'“’¶˜#‰±hRö°U<—†Ö@¤ŻUrd{µTõé©H˜‰“;äāv÷Wa§ĀĢč¼Ōņ­É_ˆˆÓY} + u©%LeRŠJØ‘Š=¢™ūˆ !ķŗÅlĮdvØŻrā7/2&å1L5‘Ņ‚²ÖŗŅxęĪuĢ c8RbŽśƕĢ`ÜL°+Ü>ŻõŸ§}r £äu;U‡&Ÿg#³–敵išq&©jŎhło6Ę;Ķ‹é—ˆś’W’cl˜–„{bj»•“}±1É«Ś#ÜNāŒļŻ:óX†7Å5'3XEkP ų‚i£O+d”Œ“”}AĢiLVe ŒŹöla>āoŠķÄĆ×įūu֗\ŒO“ņ¦6åÓ¬UČkfÄæēƒ²c:Õj9%ąĖŠR-\™ˆMƗ­7©ņ^Ė$å£ź0bB™Ļ ÆĢ®łOĖk°„’ Ą×yuCOźšž\i‚ś#åŻµż× ƒńdŅŚ Š0‚ń»‚FĢ*A©Æ`„:œĘze8‰ČN»¦ˆõ‡°ņkK]Ml޵Iū'¬2äBA“¶ ²ź5tD^¶UŖ§jé*Ķ\‘5DST‚Lą(€€kŠhkȼpiiŲE‘ć.eĘLŽšDvAșQŽ:ŒŒm}ŹóxƓÉ?œ”›(ĶJ"‰Ē}Oå&PķAš &=»źżÕĮŗÓ–µ…Ū›€Só¬K 1afĖ6½ņ3<ÕʤœO5h9ˆ~Qųėóļķ˜ĀĒ_­YM¼„„„œHĘWŖŻĀŅLbĢI1ĻŠU“’¤=åMČō@Ū[šÅp%œ9Šåp!“õš[Q\*+UV”o=儖–®c.XAp%¾«Śź1” ¦ |uĄ„¤d'W\å0„É£*ÜÓ Ōn,ĒöŚōŒtĢóö#"żōͶ~eSF% ĢČH#,u@ʈśECĮ¦hŗp.ŅÅŠ‘ąx’5ĶŹ1öZÖćZšÓmY;]žq&®ūGĘŠKDQ¹®8™īvØ „p®ÅĪņ½ūY)<@MT*¶EW+½jŻĖ:®8¬ŲUTŹ`:-Üķ‘l“9ƒ±SR—q!ūf“šYóŸgĆČ9Üņ :šŅV½Ę3Ą·³Æń]7‰Mį¬k±č.p­[R“äŃó3“GłŗąĖ¾ŠT[ęž-čˆös4!3;„įńˆ{ 6ź=†’Ó×w•izļõēź7Ƚzµ<”ÓDM4DŃDM4DŃDM4DŃDM4DŃD_'CŚ0‡ēæ³¢/. eŖ8äæ æh¶†U„. ŲXĆ=’#k#2Śü³å£HvČ.%~ń#[¦!ŗē/¦MĪ%(ģ|5{meu#®žĒGJ˜|[‘®p¢¦«7ļxwÉžR±¶b.g©•v¤h9šrN±Ę9ˆĒ.“’ø ęIZÕ®ŲĪLHOU“Øa‚‘,qĖīµĒ.dž½÷,>«ćØ,<ĒŽPE œ±į>!½g‹ »„tśtezˆ*>Õ³MŹ•Ž/‘ŌłŠt“k·šRy"»,µM ƒˆiŲ<‰É»ŁĢ?y‚–b³i6o’Ÿ­x¶q\ŽįHŠD¬џ­AV;–£-v4†WBš[+AiĄ‚0!dƒĘ—8ÆĶ,ĮdēžHÖIÅ8Kļ6‰Ø=سV‡‘-Œ+?Z½ddū—†Nļ^§Vn‰2bÜZ»"Š(²Ø¢B•|Ä×¢’ł¶ŗa!`ĢöŠę‡}vV‚”µØč¼  Ctן×ŲŪĘāÖ°ģ'/¬]ĢټķŁC™Oøö³xK‡ŌØŌbhpYIĶ’?GÓą˜§\‰Ī™Ė’*q¬#6tYŲ¦H$E±@2ŻŽŽ1䗆Š““šbJē·’G5Ü²ÄŠŲ#‹@ŲMęO^Xˊ™œˆÆ1RJnA“k4ĄDUr T0½ n«…ģ¦0»^€NÄ$ Ŗ/]9ķźkGŅŚ«‰ĄÉšqńKõw(™‹@ļM˜˜=‡PN {ŠQÕÖĒļ+FNEUUUŌQeÕQe–9•YeŽeUCˆ˜ź(”ÄNsœĆøˆˆˆŽ®«js-łērw±J@ !!“ƶyął' P²‚2©÷#_²DQŹaŠUøuīŪVf;?‘Aq=ĒĆh.ÓŚŲĒė‘_ŁuŃ£L>Ÿä.%½Ėó;äeŲ;1N ä„šĀ˜Ģ•ܑŖąS›”¢;o¾¦“<ŁPręß—Č±ä±ivQįœ[FäõAó©=BE«¬ķĒĪĢ@(ę|°‚Žāq¶G’hķ:n›åū PvÆkgžŁƒ–Aä*2ų¹ŗEén߇§Q{īY–Ö²¹ŠÅ'’ˆ6µqāΚ©%2õ„K\‚`\A‹J­•;wće™]ŗ|ȇæYśq?A½§Ģ·®‘ĘŽī÷`ĘžæY½ćȬŸØ“ NQŹĄ$ižp‘ ²‡"~¢¦ŗF3 ‰Ō0uT[پā#öźkYśyęs|«b„flĄo‚OŻYäɍ³<ąJÜ*yÆX ŖÅKõiQ®@¼ƒƒ&aa:Øn_ŒzkDæg‰e39cw‘cšĖņk1rįŚ”SXm˜·ŹØXŻÖU£†ŪmņŲG¦#øō6āonøg:²YŽKc’åzś§€ŪM:sĖqž©­Ao įDÓY%Y4×nį%pŻdȲT¢EPpŠ…2K ©@Ä0 L°€†½‚ŅCĄ£”x@ “āÓ“n=*Į=ĮM먿żŽK³¢1’¾³‹ęāKlĮV‚®"g?ĒĪVHÕ#»õąb˜ˆ÷,ÕĪŻ£4Ķ`Ģ[ų£LĻo³+NK†tH=ŗrHĢą¢]„Aü9Ā&»lNąwLgŲÆ+ēi\Ž=Ė7ģ vńĶ$ŸąÖ„Y4¦\Ž:ÉüW™9ĶÜDź–V‡–ÅBųĒż[tÜV€‚!żŽĮŻ“īŪäcĢ$‚ ƒźc›iÆ XĘĘŁ •ņ…ÕUʱu)`l‘œŖ›$Ņe4õœqĀę".VŽMØ}½CĄ°k§Icņo ZD ¢ńM©ī8w+š“Õć’¤ šģGr—ęø¶ź&)ˆOTMūŒPŻž!f­ŅŖżHĘ“R‚”fNĶuÅ/—xĶB6xˆŸŌÜā˜ŽnŅbąŠC”€CVÜ„fĘüā»ĀŖõJøš"³Y. ßćE"ĶŹ^½kä–Šž‰’@Žcä”Ö»CÓ­•)ŅpÕų!訙Ź$Q3˜¦Ō~³<Öŗ żÕ»‹gŽŹg5ĆhpŌ#œAÜUVŠÅsŖŁ[NŠčd¼„8Aā ó…vHŒŒ‚bœL dd Cp7ˆƒg܅čTŪĘĒ"Ł’(t)5ńŒ²Ėp’įļ’cµĪ%Ī=$’WÓ±Ē š kYŲրŠ: [½PŖM4DŃDM4EĘĶBĆŁaf+vÖ³5ūL” ō;äŹ³)XY†KGJĘ»HĄ vϘ¹Q#‡ńL:» Ó[LĖ›w\FšęømkšjŅ9ĮVę†+ˆ_o;C ‘„®iŲZįBH+Ł,y;„-žył”Åō¬Šņ>ĆĮŽeŪH‹Ź¹e)oŠ%#%É»vѼ]ĘœÅŹuÉÓ:pŃ)X·rUŅ]r™o¤8WV:‹‡č‘ēaylÜŽ³ćā”Œķ„r» `ąŽÄV,µ„šĘ¹&FҶ—.öd§Õd‡t‘ƒÖ™›¼ŅqłžęR¬5–h»dü³… p† ˜ęÖ½Ę8tŗ±3•öųBn+7Ń@‘‚ˆ8k/StÜē(‹óA0źVœK£]ŠDRļlž”øw®gyĆՔ؄ĖēGė‚:±īR/xSń‘Œ.°rO9ł’Čܛ_•c/ˆ8o惒øÆ#%bŠrƒų±onŹųöÅń³ŌÓPŽż$ˆāŗ`Į$ķGOd~+ē„GĖ“ņØ¶éŚ‹äš™ĘNLŽÆ‘dgȟ4²ŸxįVaÉėFLØbv‡V¾2'sKX0ĒnĢyH]kQ»µ¹žŁx0µÕqĒ ›{ Ż’^.1;p±#²¤Łżq {e‰ŌWPźŠe‹Åќ·tS©øHamÓ}@žØė1­ļ`’—:µ…p}zćę_‘Ī&flKR«e&ĶŹSr6;®,»cŚźŒŁV™F §÷hxE„åĀNM°&éDŻĒ0nPM£­ģę|ŽŒ’X(×µĒŚ®9I ĄÆyĶŌl„:€8’Z@ŁMūT®ŌZ’MSnS¹?±`!²Łmy•ŌÄÉUq~Iž.ۊ‚Wß`€~Mm|}h;ŻĒČ«ĘRdŠŽßyķõRĆ'YR«Ņ¦Ÿz ›Ēm”ŠŒ.’¤tH$hś‰Õ÷5×&PŽ5y‘Œ8§GņDæN'ót‹ķų֘‘E嚒aä%­EwLƒIRæ°ĶLeLx‘E46+d„żeΊ Ø©ywY†ź ŗq ·1’÷Ę·Ö.qÜ$óģ®ūņĆWk4 m3NÉG†Į‹¤/õZ֍¤—9«SAŠ­x±å”ö+Ät:vnāē<²Fi™^^ٟr†ų–ÉYK'ZŸ¬žu x«üæÕ<[Ø$Ņ 69LābŃL¢˜­ė(¦“uŖpäļ3AØY6Ž”cjšrxd®nZāJč:y×m`lzv”%Ł$Čź0·;Ž9zdG=TŸžó‰ÄĢ{ņÚń*°„U™š¦6Ū^#‚“VĢįźß.Ń¢Æń~@½*ÅӇTČW¤SÅle«J-ō»«yī' .kØ6š=­­ɾÕ„DgÕmnķķĮ;š×6§`¬ouJ’ųóɧ³u·qć¦Bä¢ī›ŌØų+ Vå­<‚±L1Tķև‘ÅŠ Īk ƒ‚‡ĢĢYž_fÜß2³ā·ŁCJ3Dԟqšž\}9sl=ŌXRń^…—Ęųķs4hūĀFģ›GI ßZ*»r[9qžĆ3“1§ģ«(ÅĻęÜC—±"Vˆ»›|mńMŅՏģÕ£]"a¬ĄĶXT׳Ę]Ķ–wLĀ@MJĢŲ]>ԜŁ@ĒeAY>µ§cóY•ļ$Ö“!ÄR»š”ٽ`Ɣ-rū(įLi-yŖQ¢ ®y‚åb”Ųå)ҲVZūČ –8¬)eˆrŃėv-ŽŲ¤e×jōŻŠT§L Q¢ÅšŪZKršĒL\Ö48 ¹Ę‡˜Ö£8³P¹döÖ±½ńCGHē0–’[F±µŠIšŖˆĶ¬õ,Yv»@WØ\žĶ9fMć¼Ł™²%‚YVķÅXX׳VõōJSx(¶ĻŒ-3‘F5Ø*s€sœG=Ķ–źI,–ŃZźŒĶh¤1µuNņEV–ūų[3žć<×N¦gĪqä@ÜTK¾ęe„«…ixć„̑œŒē˜LžaCõōžb«ˆąlķY ŗ•IĆŲ:±›Gˆ’y$?ōŁAŚņ+Ų¬:žķßu[õŻęo„ZÕøßä7$FØkĒ,q~UaP”_Ā8ÉՈɤaŲ µŗĢīmæžN©æ•«’‰čvīž““sČźw Žą±&§0õål|Ķ}½ėTĪ>^1™Žb{vQ’ĢV©.Vń iL6„‚2²„±dŗ+ōRFNViŃUL‡Ķ(OL…ķķ„»Ęyõ‹g†1*FQJ a@6ō-~ī7Ē9lŽ/~§iķŖö`ԚĘM4DŃDM4DŃDM4DŃDM4DŃDM4EåųzqEÆČ-²ŚŲцc¹qĒd«Ö/Ęźš¾œŌ­īS*ĆW[ŹŌ\ي0ń¶Ųø5Ÿ#és ’O^"Q]øœ«^ā9K,ŁlŒ’@ģh ƘКW™n¼nŁ5)nLbY €¹­Ā„Õ¢ø4'y«¶¶Vä īXĎóĒņ:oń¤"ˆ¹–°7¬Ģ»æqƒ4c‰9®4ä,0Ż,8ĮŁ ā½’kj[5a©U“`›¤Ź*5Ōa·l2ü=Ći)ē{]³#¶oc«C°ÓHŗ½’ęŪć,$¬ 8šŃ=¤×Ä`õ‹-•”ĢŃė ķdęzµĢūN¢yćÖK¬`ØkN[ĘųKžxźF·”&h™ņöŸNĖXÖœÕoöŠöʃčÓ5xĮɘܟkĒ϶"‚‹ąū…õķ[AčDÖśń—6 ‰čēQȹߌXźÖ_ܶ‚fœ“°8{@Š~–ģG“Ś8oV++š'q›œŒSóŸ’õ üį/_ˆ»ŠėiŅ©XÆ&Y2U¶¹ŒŌˆ%>%ØÖ¤l‰Ł¾H^Ē<+…0&欟p µķ•½äßrĘ:ąƒ” w°WwZŅ,õBՏ¶µ•ńŪČ(ą@"˜×«Œm&(ÓqEZ³M (˜(]rŸŁw(6ājŃ £nŲŌ&Ę8€P!D6šø; mV6ēČCUXŅ$t˹ ;&‰ĶŽogsć °=Ē[ŪüMµpGļ+fNE/—öq­[²eŅ.19×°ŻlŠķH˜Å+ł—LcZ&Pž PŪŻ«ž«Fą¼\yJ‹Ģ\;:čcq3l“Či^įLš&4“ß …P0—µ|гH\TĶ>ąź¢³¤ {Ē^gnź“ĢŖšÜ=ŖĻłUYܝĢɚ RAx}€øŲf,H­•ņ#¼Ż—Z°l‘ÖYņų7¤z£27D¢sśÖA!>-ƒq Ļ0ļ={‘£Ov•‹…į³vwĖ—īGƟ2|D»Ģo]„ńĖ ”QĒ£«2ĢŻĻYŻ'„YĖß"PĒ–)µ£Ż4NaēŽ'>ˆ8\Č,DŗµÅµ¾dv.±„‰AęŹÓøŒqÄrÕn–\ k­io‹ZˆߕŃc\EHqv R Ņ¼Ŗ ×*6¼M#1E˜ģÉi®°dudŁ ”‹¼Vä ģćvb« nM6ż'Žóµė˾{oČ&4LePY¦/c„}xžŹĮ&īĻsztźbŻC¬Ł“pw‘=ĶøćšĻG«jĪ·‰…±¶2A;]»`Ł»y]5Ü%eĆZkßjēŗYŠņć_f“ÜŚvi/T ­ŚÕļå^B,­…Š™YļbWU›ģƒ[­Kśo˜*“¶†y4įT†RB T)L·gf‰4¬ū虙½ (ż Ąź¬aū¹ £jÉö]ńķÅė5ŗBc¼Z©äÄ dck«K~Qs=}‰AGÕėæž¾Ģ6’(Nµ@‹üź.¢F0˜7ŲCƒ[q.¤.ė™3[— ā‚™N݃“béļÓmrd¦rīTO.2d^\ȍ™>qbĖc1&ŁĆ)(™ź¦3Ē»,ƒ'I¤«WŠ³ģµX» DLbˆ”@GRć8Ģ:Œ0T²ÜPŒA{Ü<ą‚ŗ/ŠhļqĮĪøwsZ„Ö ·DŃD_ ¦šč¬Żt’pŻŹGAĖuÓ"ķÜ ”{AĀ ”é.‚„ŒC”ĮŠC^‚ZCšHp8x@ µĄ”Z2bÅŖ«Ž&°ŻATĻ(2 ՞Å2 æóé'X;„_R”_qŻX',Ūī;ŖŃĒ茧ā-¹u8ügnz³Ö„$čČࣾÖē6œ’ »ć>“Gõv³õ­*Ė`ŲĒ/•—\šŪ†DkhÉń8·S®“ å ¦9;ezÖŻ= C±²Į­= ā\*„ÆĪ=c 9”Źā&ž9ĆqXž+’ilfžVI†W5±k² ō@Ņē 142t}~KßĆ?īnPĘęHĮ.qqĄ8f9*Du15ŹŠ©Éi•Ģ+ £3 Æ­‘r¼Čøż¦ģQ,IšžŸo“Ė æ“6×$† n;ś!æ’]ŽEÓ+ÄS~ ęĢļ’¢ß*„y+Ę^ČYZŪÉŽPå‹õžt!“ņÆä+×ÕjUųCµ3v±Ģ£ä$ ėŠŃ(”Īia8TXźCœĘŻt™zī“eƒĀ֖š31kWO+Žó“—×=Äūœ€ µ=K垏؎ɭqŌóI–®Å°ÄÖ°lZŠæŹkR”U p„ˊnvćģ. 9įro­ü„°.ŚńVl¤½ŽĘ³XÓŲŹ÷#JĀ4µV^¾„Y®9Eār™LŚģ37Ž#v’n¤‘÷®Œ|Lq€#kŖā÷€ šą× ZrŌĪō×p,‘ź­ÄĖFH~I t…ÆmĆWœ®isM £†jÆŽŽ$c<įEČG”\qžwasqF„E®gŹŌć(ųŹöKyjęFņ²6€:+En23æĒ֘A2»ĆŁ«iĶŃIdci§EĄÅKļt3g±±³±ĻҌ²I8ōSE™™nį#ØĄ”’ģśČ»‡L›#Ƙ"q5hn#}wQCG¢ŁĆrˈógkŪ‚Ļ‚!—Ž S¦LX¤kÆ -ҁ&LĄSć¦Ÿ©DRX䜞ąR6ŻÉ(%ģģāä^Äēž@vņŗæŗŽõ²āj˜{,o›ŅUŚ#…Ó “YTĆģ"† ~ńDß­6J« —ä–Fė„Ś˜bÉĢdv†Pż†:@• i.ą9’[ŲsF”¢Rˆ€ŽĀ!Óp’°†~@ÖŪ§\€Ņ郔;É_2üÕj]4EH§.}ćQÖ·åCœŻv!ŪąŪŻ> XŽß³[—’Ŗæ—Ā>P“ī7’Gę…\rVó]a$ڹ/6Ō°5„­ÆnĪä#b(Pmß¼U‘I,ķC»—łxÅ©¦’-[‰EE»•*CŌóPŠū bw.M‚ ^MF'§ņĬ4UŪń{7ēœˬäžD“Ą2’ĆĒŪ *EŠĖLØćZĶRĄåĒ$~õŅĢIśģ]Ū(Yi2™B5,Kė Uļ)“}föKŪ™ģmćkĆmƒ†`sl-8FŹļŚŗg X[éńŪ]_Čö±×4vBZZŚ1M*FķŠIó'ˆŁ?…ä‚ĒĖŁo™¼uę Ķ;†.ĶĖ®ATĄ·;sBŗÅhäźFI†€˜•ZTĖ9d2’Ųe›ŗAŠ”® Xm#Q¶Ė=µÆÅ²”ĆĀ`;vÓ.Ķǐථ'WŃÆsZÜŻ~- nń^FĢ[›6$bG( ㊨y”+^*å*oygį#€w ‘nRV-½{(sŽóĢl‹L˜ŽŠ„›U—A·e|ŠŽ «Ø›#EډŲ0MŹgŻ5Du²Coolܖń²6ņ5”¾@›5ÅÅóÜHłŹēyIVÓż&ß!Õz Ūnpū‡<+ÅdQœ[éžÓēų}™¦pån;¶µ«CŽ0¤œÕ¾:ł zÜXEß$Z6‘Fm¬d$vQĄÕ’Ā£‚×$sˆ ”Ä«Q5 —fŸz–·,ĖÉ뜬żęoāN=Vß¼pš'łœZYäćcwlGUģe•Y35jR—°öĈń{ʱœ P’Ø!„ķ$óO”Bœ‰ĖœaÖAҙ¹/*™š¦lī›ĮŒ"ĘAv%£ż •\³XĘ(ļK@}å1" øżQēĒŹ¼%Œ4vVŸŅpņTy1®r£߬“hÕĖ "éĘ&Ēv$®Ņ ħ’w×Ńoįė” ˆģ"ą¾x雪_8N€cŽ·–6w²[·TC!ƞ‚•ė>EŖ|ͼ‚GEhĀ ĢóT֝BŖńqCŒ4\ߑónTŹßUµĄÕę*øŽ­FNb^— Ę.±x²%vˆ‰xĶ;Ź(Ų-IśM_F%9Mź s{,ńž·yk© ;Gåž$ˆ€;±©'oBˆį6Öm=—ĘąņEEq٘rP,ĀĀEDÖ`cŖµØØŗå^½‘5Ŗüs8ZüaD C}>1±¬Äå(…4Ź&Ū®śå²I$®Ļ+œēä’{J܃ZŃF€2€>Kā>sR&ĖÜ UóßyHCv#=m©)Žc|D!֝H7/^ķ½ŪėoąI|>!c7>7!PœDĢśSĻ!c†Ē:£ülYö‚O‹•¤J°‘õėÕbISŖ°zi™—Ŗ;ģšīĄh÷ „k\ŪKž« põüµ]ŒŻķóNvź0¶Ćö‡Øm‡÷µņčŲ»*‚8iėižK;nq1’­>Vf䝄/¦¼c ŒXt.ćśŌYā#ŌĘ0æQ¼RĒ2ęÖ»ģ¢=ļ[×ø;M ¢źO#UķÖ®¶äŃDMpv=f¢Ō_[lµŚ«0 ©óvYČøā@ö˜«K;h™ŠGW”·ø¹v[hß#æE„Ž@U©§‚Üfø{#oé87ŹB²ę?!l՗o³4ŹĖ4u…ā4Hė%ķuĘ Wvƒ$§ÄJĒgȐT]4„{Ģ©”Į|UØYO „œņĀęś0zŌę Ņ•ŻČ¢šåŌ2ĻtĀČåkˆfgŸV§  “޹ ט|5’ĆJĹ:ÕŪÜ HŪäiŲĘ Ā±Nu^KY§Š˜ō±Å6ŽącX|ˆāyØķJęŅŁ›Ą.‘ŻĮż„“{óƒBŠ­±·¹Ü§,mļ.w쨇˜9;ĪO Ų‹"aŽ?qŠ^z½uˆ+'„ĀŲļ.f» Ä#Ÿ³›J$nńQ±u(’ČŗMĪ,D}#˜”ķ×Nį_“:oźkęāļP‚®h ŒŅڐޒ*ü ē|MóR÷YÓåŅßµµ”ĄzÅĻ iRZHõv)_ œ¼_¤æyGĒ©“h$æź6“\ióĘÖ<ч`8²§v8cŠ“ķö+-bÖw½¢fķ§²M ”ę$×vÕŁ#•x~ĒĘŁ”1¬vlXĮÆ(ę®āÄteēAĮĢUĖž]—nzÅ2ŗĀ]šé)"é``r”Ŗ÷‡)‡we·ŽŒ#Ć īPq±r¹ufēC#OŽŅAfĢ®„ĢFź®Ė\`ü0ܟ̱Ń6ß!œ³u„bŽ™7®xėòJĀĮØø*Łžģ Ź¤wĀÕ"¦éØēĢÄŹŅv%ŪYŃŚCźž|2ˆŸS»› Ł[ČÜ;ö¬Fžļ¤•NB×Oä6HfšBRĄ^9~qó&) /ūėĖW&~l@’林÷l€Ö€‚ē½ŽŃ'¤©Dü7±’Ė7‚Ōy÷ N*Åīó—ÆžØŽŻ²¶dX·$ æDč~Mz©Yž51@­1Ū¹čīDįh¢“pE8ŠQ:rģ DŁ¹¦–ų@½ PÖ»Ŗp— ėNĻ©ŁA,§k²åyé{2øõ•;§q6椷&Ÿw4qĶ™ƒ”ŽĢŃŲ±óČĻ>r8sOš°bņkXjÕu@ÉŃTĀ܀Ɣ‡PĄń i i‡¤ä[¬ #åFYŃŃ•Ƥj&øNņvĖjn-cÖk˜Ķ⇸Ö¦;įcó[‰­btw" —‘ƒžĢ¤ˆĖG5Ķ«< ē’xĻ’Ł~’Ź*ĶźźK|„-¦kÆÅbćÕ37mųĀZ© ^·qńg®f¦qĆtUYō³gDTUEŃŽÆ§оRüOŪ[pS˾ ² Wzīń÷f÷‰f˜z“QQq\÷ŗœŗŽ±ė¾lµ,m2äZ2ģӜšākU›nq«Tųߎ3® ™€Ģ–l%i “‡ęqÕ õ÷—Eabźļ„$'Z&Žni±²ŒM6Ez‹wÖĢĪŗ%@W!ųß jśÆkr隫m Ć|9[#k’µ ›)Įķi'5 ap“S—pZj°Gwń|'‡ ®Ź\ ™]­q ģpŖ‹ń¼¦‘§c†Y&ŃÆ6ÜlÕ¬‰g¼m 7ę²Į%Žś[)ŃW˜Šŗb‹ŒZ¬‰ ³}ĪA³ŸMŃ QA-š]+‹Ē[YKWyņųŅvŸBÉXźƒØqiÄōuēĮdŪ«Č„–ȳ0øˆ4°jFT:'ŠP“8:˜ 5壉–ēn ńXåÆnUfõŲ Ę?hĀĆ7-/µ¾Qd«ÕFG˜˜j›—ņ§lŻŖ+»(Bž@p¼Ö™n|­Ś {‹ódh—e®Ą‰"˜(×|ĆįŅęĆne’źGĘܹs¼š‡A‰'`ĒŠī`ųYLEä;ņ;ŚĖ%z˜ā_1ēr+H€–/ĘX§1Ŗˆüy™āgĪ’mī.=~ŠŌf^>OŽęĪP䏃ńžYꅂČī1iŗŪ‹!bķTÜp¤A¶AŚ%‚A#¬eN Ł‚ꢑÖ‡_ć­qŗ^•¼Ń>Xļ/"·õMń‰mIĒŁ4Ž@mWm-ßs#²Ón’§ä¬¬”ąz*Ł=jJ¶Ā)]—ĒÜtĒ–ø‘(Eä¾NJKfĖüP8~—Ŗ©¢c˜B¤0¦MR˜ūwm­‰˜Č-mtŲÉÉkÓ@ ļ*»9ŅIpF.>\} OŽƈ†Ö ¢ż»¢ĄükOš)R~!äXņäV Ÿ½LVÜS2DŽ2æāk”ÕNuÅj:×$”ōi«ŅŃw*Œ‚ģN"R52 NÕŪGMń¤¦ć¶ÆŁ^2Ģɱ‰ įRÓźš‚Ņ6ņ‚ ؇—6X–V֘Tb1(Ÿ5’¹%‡DĮšøņņżWm°-•8ĄéĶŻ‰¢ösŲ Ė Į‰ģu~YY4S Ą§6Į¼¬qi×ŅN#—Ü›Õ=O©ī+!ŗŒŃįs[ļ3֜{ÕoŒ9„3"†gŽņEvjmąyRxŗµū¼z‰˜HŖ/é–bģ­ŌI@˜~XĆ[ø°¼µ§ĮžšÅæhT,Ų/-n~åą»“aģ8®"Õ$ž˜¼*XdWę,.<¼Ś±ŗHmYŒKŲ':Y!c"Ž=Ø! hsŸ`zkce’Dѕ‘ÜG¹Ķ£ē«HļY_†š%÷ń<–ÆŽĒŚsQķwsˆ\…§š¾9ŽÜ©s’ܠ啜§‰$›Z°žĒ¢_ńMčĖ×mq‡Å8_į^,×ķ0ętr°žŸŽ¢Šc*Ōé˜;µbźć]¹ˆ²čÅmlp%Ä2£“\zYŗ}ŸŲ\¶MbĒwüǬ…u{B}½Īƒ7 „ņ:Ā‹1!›ŲČ?é š*G؃b"¬`ōŗń§jęėķÜćūščÜ7ž”ϬļŽ*ūśƒŠ<‹ÕKSĖ4DŃDM4DŃDM4DŃDM4DŃDM4DŃ‹/ q=s(rS‘Ķę’N^°ŽŻ-R•ˆ²XŖ²“kä4£Nż¬­fJ.@Šƒ…RŲĒ:[("bZļźWze“SŚ<±ęZ@pŹM1[鶚„Ķ÷ŒŒEP vęą²eSbņ&ų–ĶQ¹>Ē')Ž-ŖõvnƳˆ4“^v“c~“gŹŁN’=#镈­Ü¤ć½DACĶš· n'CrnX+Zm}’kaĆ_€ČŪ«"öڼҕ>©źŻłnWµŽĒŠŹÕF“ū=*‚±$qe†Ż‹Ž‚ƒ×øUĒóuŅ*õŁBœ?&¶£kś l<‹Nn”xĢŽ#ž‡ĖUGåL(9*{ĢŽ¹Édlåüƒœhõ\ć&Śjµ#,ݳI%ėrO%$ż«B$ŖÉø;“$żŸ­>ŹEK‡_„dĒ«]“æÕō.M Š›:,Ŗ5ęECÓPö\ÕkŸŹó€~ņ$E¾s"JĪ7buŲ”ņé Q>ĄPߦ®6ŚŻŸDĻ•cÉ}y6אӹøå5±¶2T~U)¼ueR0$™¦ƒĘ5xf-Œ%ńŽPŒŻ™“ÜŻ© ;lÕż‚ƒbĦcSиهˆų+7ćł¼usØ74|£e>™4ŁW*ĶUfČC}2Ļ_Yė…ŅBZ)Ȃ…Rp@2*”É(r¹clĢ,~ĆŻŠ²-ę}“¢X°pļ…uü®Ār+‚ĻŌő2˜ęĒ[ŖäDYäs\aŌÜV]ŪdŒP“VDņOqż‚ķ¢ŗĶžøÜ蘂VĖzZ¼:“ÖZ™Ņ.šÖ:±»ÆŸh#~Š·KŪT҆¹hēæ+i#E30Ō8ÉJLČŪŻŁŲŌr6ŗ×,ĢJ‚CdƕµĆK$S¤ŻgˆÜcE»VΊ*c¶zŽ9į“1ŽFę)L:Śē•ķ-wh=ÓB“·ĘĄÜń¼=•čpéiņ‚B†žSź9j§#Œy ‰®Ī(±ź°6ĖR±1ŒL·„“ĪwM y4\5nFvćÆW%)3VP‚Cģ”»bum"ĖU|"ł”Ń1ū÷€Ž*Ā {AÖÆ4¦NŪ7ŹöW¤ ½ A¬`bÅR^,Åƕ£ŖęjĖ0²Ļf„ēÜŖUĢĶJ.uœ8u&ćqTźĄ]Š R†¶Ka³c`ˆĘ9?/2†æškĒŗyIt„×ņü±Yŗą¬QYq¶·< ¢Éü‹“ÅĮR®å•¦ß$޼¢«ˆz«&8xšKqķ*{vō×ĻÜ_wńœCs öZ죫󒺖‹ĆépĘväµKĶkJQC~|±nė‹wwīČS5¬Yqm­ŁŒ#²,a²uOźnGn‚ ✮aßį€ˆū5±šœ¢!¶yŲ^Gh*7Xa“M™£nE‚¼“Œf<ȰmOåփ›p©ÄL=ʋ`ébzg īTœ±ā"b”5ō Ą.¶’¢ęŸ"åÖŌ1J} įå]©į˜N?¬Õ¬KĆɭޱ\°¶r —Q²ķ§!˜Ź ŗK¤E2j¤ģ ö¾`š7G+ć#Łq„…ŲąęƒĢ9xĪuM+Źó*&ļ73³Ŗfh”øÆ·){v »Hīj7‹żÕ«žń-ļƒ? •ß’'‘ŖOėR[‚Ēē!łČ¾ČSö­Žbķ*Ō[Ä/s²[®§„Ā0q5Öq“lI[@O9|«x§ķÕ]ŹĀݹ°&@8„: š ŗžųœ×āsTŠ“RÄ£ėÜfŻųŲC‹#@.%ŁF"  NT®3å?#<žX‰q“Š[;)Pš¤aĢ™‘bHS€v(¦C±«IĘh“ßź*pLØėv²łO¦S5Ä·3żPŽŚ‰j7æ2ļXH¶ŠŽr^īŹŻSšƒįĶ†v"+dģ‰Rć¼Š(®…§*Öa%Š@įń—ī·+É0PßĶ8œ@žį0{u¹Ų|°Ń-Ķ[ep¬®/īõ›^°µߘśœÕoÅJG$m “iRĀųQźÆŽ!9ł3c?aß«@ĆQīäŽn¦Q4n¹‚é|“Sćö(hā÷ģÖēmĆŪʇ=Ö06Ÿ¼;–„qÄÓNņņĀēņ½ÅĒņėY Å߇Ƅ喸Āē,ęń/yrfd˜†„XJ­ā&Ź4Q7½5=Rģ;ś“Cµiž!{ŪM„‘ū“Ź>MrżžÉk:¦«!ų’Ę÷¼Xń¬ …\e‚•dFäk0¶ §Ųf‘łRhØY›LläŸĢ”qXUNoˆĘuÖdzmŒb‚6W–‚½ Ua>śņL+Čé*h³I(ęHFĒ"„tkR”c£ŃIŒ{TÉŠ‰¶bԈµnB°B€k01ÄP)Ō±I$ŌķT•Ćв= ļ‰ļ5hY¼i“ ¬•\…R^9©aģŠW6N£m)I²E$ŃtęU£åEḚŖ §wxµfkx„‰Ģ- ÷óķ”ŽŖdcč'0"[YŸœŹQ½§2xå„óVœŸįõęׇ1÷ßi&«ČgüZt³:RĢ^»ōR³ÜkuhU‘/s©5Y¼)T9˜Ÿ8qFŠū ĒĶ ķĖŽj}]æUŪG?Rś§8š=SOŽŹåććѐ“‹ŪMœīfĀ6‘CøŖ2⼕ĻtHxĢįŻžĆ‹ŖpāļÜŹäG¬=¼PńÅŪ%Å@8¤WÆÖz4rӉD(ōĻ›B”ÆŻ&ŻŖØŖ”“OɆČ>Cq pvWØä)D”¹(א  £$$ŁSļ˜ž'?€£!{›ÖW»ĒżnQYXxåäG € Š>`Š*y–š¹[ē„2•ćAėųsȆ%Žł¼o”‚Y’&9˜w7E Ų ¶@fŒ$Ż&ŖńG9Ha;#*ˆ×‰āHĶ jQ.3å¼±Žļ6¼Õ‚`æcŽ£[d1§.ų„vp»ĀŻL|¼\ķC"CĒī¤]…ŗ­U5~ŲÕ©4öĢ"W,OÅ<#¢qö•ą_“G|Ą|9€ćw?¼Ó¼ēŚ~„=Œ¾4ķ4ģpēēävŃтżņ]Ÿq¦Iāžm>"EÅ LŃi„Iå<^„cŖ™ f×y¹lµ¢VŸ2‹;¦ä;:쁛X ÜZ6ĆØG,Y…ϐp’ ėIxڽB«ĀÆ(Ų« :†­5ŖČń³‘X·W©Ķ`¹ś¬!E§dµ)Dˁ’² 8_ājŠ*¼éšżĢ–ŗ%ĶĢ ńŲ\@ę ‚ćĢŠK( HŃōČ5=~Ę ™ MmÓ i¼†ó8ƒø•^łVŹŽ;’µ$„!-ÕĪFŃo/~TĪ_æĘ³ģ±4•žÆØ,€±RŹā³%מĘK®^Į1ĄCž|„Ó-õ]Fk{”X |~œEł+Öźō…Öžf¹ĢeœŒ4sŒĢ?UĀ2G](ŖˆäĮčō@6ō˜³O` €;$Q æ8kč×øžuĒŌ¤āJ”Ł,ÄLw{B.F»˜Ø™?ÆŹ8")ZOÕŽ:™Æ¤œ‹93śük²ØT·PØ÷6ķSóš‡pCļ-Imń܂6ŒŽĖ_Õ.©Mšó£üHE.,’7²œµ§]· ±µ³9”\Wɒök®±‘"²~%¼ÜgR°LäŽ(ĖÖ ±–•,‰¼Ļ1£Lp„bYnõŗjœ Ó2šZė8›N¶Ö˜AĘ#• Sƙø½“äv`ęr“ķĄŖ¤²:uĖ„zµĢӬƀ="”<ė Z”xø™jŌ,ņ"Iˆfr €mź8jQU?ą€‘Ń@«¤!īŲįŖHk¶Ńz —ŸĮ,1œÖ%UW©ŠŹG½Ó`7“‰’KD>ĪąWo·V>ʬHwØsœ8yFɌŽ–cĆ‹Ā-Ķź6³5%'ć.ĀG‘W32·AøL@Ŗ$å(‡]^·»æ±5¶‘ķ€Õ½m5”xųą›ŪžĆŪµc¾s‹‰aīFp®ó š³¶±ČgÕõĮś”¼ŌĢ/Š&JYRę ¹©†-Łæh¢ā8œŻ1ÄČ÷0`i^CŌ²_—jułJĢŻ‘ōoĢĢĀA;,s¢,ŗb · Ŗ’G*nŻEDåļŽžķuEĢČX°ćE„Ļs·8¹9Ƽž[NF•˔ĹUŹ{ƒ»=’‰>įżvÉ V®.ž įŗoŽf-¬Ń“zJ ¬!«y żÓ€ Ćo§x:ŪMŅtø¬¢xoŌ—j€ˁ;y—3ѵ?Pž[§ĶÕn^K˜M Z*#ŒV• o&ŅI[lļ]–—ēÖUuĮĆć”Åī=“U4÷¼āąNc—“?T·¼v×Dį›y§Ö®ĢM."Śœī“й'Ķł§n@žž’­ÓU-Sl§„ćdėy¾=°Y£/+BÉõe ńbo5ē1–h&?·YUY?œHåÜ5/¬pö•­0Ūź¶ķ‘Ū*E?[oQØę\ĘĖR»±"KI [ɵ§«gf*·Ā>Cy†§ģŒóÖ7ĢłĻˆ•Ųh9Čī[KQa«™&½"š ¾žžÆC)†d C.”•<ĖČł¢Ę_¬Żt„¢o’ų£Eį–j²[pÅÖveōĪÖh#eP×`øÓ4¾ /c#Ԍmŗq„kNJņ6Šš*J†ŁŃō]·ń$šf^·-1 `Īž>g!&£œ•äd¬DŒ¶=–’då.ĪF9TÕDįŠÄP£ģešó\Ķ1¬x!ĮļrćUzAœ‘²ƒČ½Wõ8±DM4DŃDM4DŃDM4DŃDM4DŃDMxįųŽo·*łZØĄŻ½•ŠŪoÖå2~^ž†“Ī54°„Ö’ –ćĮƒžśS’GüMYäÅ&!4‚#9 ÄŹĢCŲź’‹ĀŚkR„õZ3–…•l ³g>ƒ— £¹ESĮ±· GF¾¹Ó¦76ʎŁĢG'ē[v±am©Aš·B¬ŪĪćŽEbĒ b‰y‹@y–9$Ēł ĆGu_ż§NĀ·šö­•g5#4”ä¤ģÆ;» "bŠ£±‹­ėR×ļŁBÉÆXšüŲøŠī„)Öyv-MįŻ5ņJoLyb•ĢĖź¶“ŲI­iĢ96«Į?ĆF†|k…¶‹;,墭O1kÉņ—«S$Ž˜‹“”±ĻKF9j AFż‡Lå(ļÓQÄ÷lŸĒ48z¹G^)‘gĀöĢt_öą¾µO”Ó™Zf¹Ū“(-YĘ­[ņÓŅžGMÄŲ®ųöl®–w'4CÓK­„žR²²%]«¤Å؊݊u1 RnńŠ LĒõöąīƒØ•ÄoZ$ü/³½śP–HHöŲ F;@Ø„yrķYcćæ/ó†@€ˆ—#(e›™ūh«k ½łä¬ę:˜\…2ģ.“<Y€šŒQ’ƒśā·tčĒLDr|ZŚ-®~&1+2¹‡{]_ĖØö­*öČYŹč^^Ł\ŚVœ„W¼z,ØŃäsõŚ©j‹®³ˆ1“2ź®m0¢LL 4mįĆn󀇪(€ė-`Šõ(ŃĻ ŚéE[-EBŌŻæ)~Æ¢Ų\+}ĀĮe­°Ęl™L£ézaVVf0 ”ˆW­IÕŲmÆq”u+<ša{ ĻßQ:ü«iį]diWŽ Ę:|ž¤ƒv8Ō£G3¬A¬®7±Ź-c5¬M#ó2ęߌeŪØćd&Ŗ‰»H£Š‘ÆTŁg,T0ˆ ¤_Šu!©éķ•ŲNßUćxpĄ¬n&ŅN‘Ŗ:6cm'¬ĆøƒåÖ²K–h”īKį,‰‹†e¢ŠłØž )v‡"Ė×ęU)W'EŁvƫ֍ž§)NCmKČĢģ,;Ā„‚S­”cCŚ7ޱ‚ź©<½Š½Zŗ2³²ĖÕ+}ZŻmćļ/žˆ–l Ž‚ŅĶAfĀ>£u“0n ]lł­Œ®ĮķiÆ1œaå±³Üį—§g”v…ĆԃŃńF2¢0j²„¤ćzMqDŠEEL™”+q‘ī”9HNā"sDØõŪ_4ŻJn.„˜ā_#i+­ĘŠČŚĮ°4ås┆IßtóY'L@ƒŗQŽ›³r wcG(.A }˱G}ŗźĄ„}mŠ£]ŪU]Ź,OH›įo&ZFB•Ā“Üsʲ,]M“ęäp… ^f4JcAŖ­]³LÅōĄ¢.ūŽ„ō¼±_Į#v‰[å 毅ģvĢ„urO¼5 €Ÿ_Ŗ¬@((2š¦ī >ļ·_F^.–łBå#Ō›źæČWs^ ][d.qNąÉŲ=ĀX{Č"µ<”™¬.”ķO3…ƒ&bŒбž ȤѮĮqŽBÉG…œ°.Tä,Z»˜ŠI–zE"åm~/łćaĢ æžų…Œ³+Ģ©‰2Zå¶ùŸfėU~­T¦Vr•sĘeŪfĢčäv••ŚÕŃ7ĶY±p‡ktĮƒ–ÄVW$xnęlÜ^®Ösēx×Hʼwę5ϳ~Ē9·Õ9#Ÿ±nGÆ×]äŒhŹWœˆąŖWĖśöIIŲDdlŖ¦ŁŚ 8\†"±M0‘hL¹Y1ü§“ ~sÉbćŽ?Ā:ą©|ŸÄ._fž?c”M,H\©ü}ČÆ°Ż†ƒz)ŲY”ßŪŖĢ$"ā£]¼;¶I‚I‘d‚CʄE;ł€°äģWGĢ~I¹»MĪTūԉǪĪb“ėß1^ ī]ś²¶š“XŽE§āAb®żWØĀŠDPŪx‘ä' łEr‘Å|ˆāmb‘¹Āā‡łQŹYHržõ'™ ‹y·ßq6 «å\„ƒZå6Ųī™$¢76Š­77^„?×ŲJü+¢) Ć_œéĀ–æ‘Ł;'ń½‰¼o¼ĶØĘX&ƕėŹŗ¶`ͼ}'’²Ņ–Ÿ”@ćܜöuÜlģĢ{RŁŃ˜ž;É%%ČM”Č» č‹„æš/\”Øy2ƒå_8Ÿ’¹Ė,½>T1§ŠąkŒ3^#Ž©d „ŖuxźŻaÜž=—#$t ™ŚŒœ e”Ė—isšĻ. –‘³ÉēU5ŁJ€–ŽYņ­Pe3ĒøKŜ}UL÷%sO’¾óR¢5v‘‘y2į•J>pķݰāC‚D[° qķßl™u3#rųm§>>`Ŗ2WpQ÷ę8»Cąo‰¾$fõ¹Iƒ¹Šļ›<’ę4d µ )›kŌyyHĢĒĒÆ $KZ.ŲF*"¢N#”Z ķ×SÕ\Œō®-ŗu—]Īē5јšŌ©ä­ƒ„lž;ˆķ#. ”?Jų~¾QŹNZS„\Žc<5ė•ŸĒV ±Ł¬gL}Dādū” H’:(d«^Ęļ @ĘERż§.ń%lݵö¦ńź9ĶcO.QSŽīā·_™—m~”kfÓS.yę/p¹•č*³|ķČ÷Ņ ‚Ÿ)ņApHž¢æ+ŃWK$ĄCŌWŠ@@„Ü;°k²“@\wĢO*ÄŌ‘KĶŠqž@Ą|}øGŽń~E„ål}i·\j įĄ"\8FA”¤ZS~å…Ņ"ž1dTyź‚)”G_Õbƒ[ÓeÓ®!.¶ŽkŖG²ąA§=†‡r¢;§A+f‹ ąAē–*Fr#Ź·+y+“0nÄ\ĒüY·įčl‡[{3»¼Ź/Æu<”޾†—=SaZ®&…M„ż]¼£Öx›ä“X‹&Q0+Ģ8Gå€įčn`žåóŪÜ9‡)hfRĀģ®Ąø—ź€#vŹKjšłŌŁńƒkZŅ£`Ā¢£j“¶>`ł»*ŗ–cŁ*ķÜwoˆ1¦9ĒģŪĄpģk*¤DŻ„@‚!±Īć¼@~»o0š–‘™˜óŌłO™D:śåßJ ĆZņ—&+ •¼Āró•N/Q’5ń‹ššĶvY6żŠ2)Ųŗ«ÓŖ>lvIv*¦RˆŪ¾LŚ”Ų D1Ó¢9z±s>jēuzJģåćŗ÷oÜoY>ó9’­µ\³–芯ģäŒ%Šv¹jPõå&Ę!œ{¤CȤ܏ÅB¤Sśę|Ci ŽŖū{q– @é nŅGIsĶ]R§0‡°vüŚ„Y* óB§Ym‚n„‰jŽn•ų,Ø>lR·1†É'5Oreˆ@Õ9šŲŽŪÄ@D@DeįÆFFŅ×®Ōé2Ą*o(O¹Fž}é•Uœµ–‰(P:gs*Ģ¢r”ˆ’jāķķŪ]”rāh"8x“; מW¶!ź§jåp‹uJ Ŗ7ÅGžŗŁ@ Cķ±Ō„wœD@pŪmi¬`\§'|Åø¾ZeĘŹÖÅĘČ/—ؼ˜°ć \½¦„ŽåGéØv’ö÷Lgfźį£Ø§ūZ.½¾Õlį¶‹’F¶µfc€ķqsXqŚ]GøSvńęqĄéW¶åą\ŽL+¶¹[ŽswtČ¦ÆWqBęFP«ĢķM›0Zr4Ų}kn²ĖÄX™ƒŌė/QÉqī“Ųv:M”0oŚ#®1óćHš ūmIĶ9\Ó¹œ?6ZrÕu’‘ŗ¤DŻé•õœŃ#yčqżćŲ«æ VF¶JEW‹ģ•:’Łöa£«ŗmĢpRćéČ{&A“t”:6 \«XŚŪNļ甓XJŸ·AłgòküK`~^CĶÉ׳¤…½|Ōāōnu£HųĖ£•£~PjOmo"Ž÷wWČUUk]m‰ˆƒdSI±Ś’v _¦=­É6*j×&aN™bå©ŅYŖ‰H!¶ŚūėO¶¾“6×1ZM˜7“)ś$n"„/’`¹šŚq4/-œÖøžž^u”Į;Ę[ɶJŲó\Ū{EĒŻ)|lai$wÓäl•|32ņ&ĮbQB>“Ļ’÷ó’. •g wdš³ņŽŽā7jN¹9„ŽįR„¶ ā ē!ą•-ĒzĶʵ5•ÅÉAj;+æ”ąjw©7™*l5Z»%ݽøL6§8Ešg;§qrīŁ·‘H  ŹzQj¹›‰J#¶ŚÉł”}ųO ^jŒ9gm»Śś¾Œoav [ŃÉńč}–śŻb§Ģ„,†:³W¢ĀRN“Ēčh&›wŽŠĮūĒŖ@j $bJuhµœQ#³Ó7i€_Ÿ­‘„ѤęåüüŖl<õ—VaÆ?\EĒ=$ņ½[ēĀ€”“ēož‹ŽeļōÉŗU^5W«.ŖõHI4㚢Q"›`)@]WJøų«ē4ĪG­ĪąhOY[õĮ¹“d®5vZ‘^ڐYi¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢*^ńnŠĒō«}öu)5į){ŗeX×s3+EVā]ĢČ„Į5_KIØĶ‘Į»dJeWTJB˜Ą"ńTā^wŒ©ē<Ūfc}t†Eq3%Ö”^{=a”·¹™nŃüdJŖ£ąõ&2$T€n :×øŽÅ×¶ĢĘ_SĮ£FÓ½l\9zŪ;™ lŽ.eF—;ž/góž`~d‘ŠœĀ“Ē­²Ęõƒ¬»6ĢÄģ;Ō3U$Ņ…t„H/](«”{‘ 5Ŗ6=7OnbęÜ܍ų@ņøą_Š0;ÖÖé5-@åk]mlv¹Äx¤r4 †ōœFåĀĘńĆG^ÖCögZ~OøŃ ¤œŪU&Ź£¦s’ĶJ:śš®Hö]śnł— ŌTÅ(ˆļ¾õÉŖźO“ĶāøŒ0ŚŠ@Ć`Š+lŅtÖ]–xM?Āv8‚qŚN;Õķ‹ Š ŹRĆQ©‘% ¦b}:­ĢJdŠHądĄtŹ·Ü>ŻF¾źęO¼’Gt¹ĒΤŁkkŻÅzŃęUwiż2ˆą‰;S  mš¦AČ@ µcŹÆł½°7¦YęZ\YŽŚ«ķE•¾ J›Œ‘»Š¤µqĮ“l0©“ē ÖSs¦ķ—īœ5³pÖ¶ż.šE)?óB74ć“Ÿ·rÕx«@Y±sć_0U§y¦ćĖĶŲ²×Ē ·3ymVN°ł„1 Žn:\LUuŅE;gˆ;1S%U*BÕR˜éŸtž €vF¹Æhsqi ƒ9ƍę7Š9¦„r)¢ØĮģœ„9 ŠJr§!ƒ €€€‡AŃzŗņr ”—j›‰GĖ,™Ē ©€›ĄÕƟau1øøˆ:c“ÕŲŠS`4S¬2Ę܌y ĮJŸӖ s,6k „ŗÅ9Ź %9i›Y')wµSHĢI¬Żč(żó±1Õ&Bwļ±@:k“üĀk¬ÄČĄ m£`Ÿ@ŗ§ךy Ūń/żÖ,‡ėC[Ŗ|»¤šńĒŪņM[|ĢĶEš9…/rĆ!J9„ž·Gßß%]+ę{鍵³šv¤t®#¶ø&‘9ž¾«šĒ Šõ-ŠlЧˆ ČĘųéf=⣭v;ü=†K7xå§ŅH•õ“Œ×[F|XUp4ńQ ö,z~į|©©äX"oѦ”ś"ö^‰0}±ˆÓ3FÜi“u@_&ėPųW„棐uģ=įgSJ!4DŃDM7ƒŚ=4Eē åīźķĢß(Yī“Y¶aÉžŅb\’»ƒ1œ6‡†b`”•Ææō‘(€u”ųŒcā¹ßsÄĶŽC칃šµłåfŠĖx#Ż”“kē]ž<#F2‡ń㔣ˆŠ q/ɜ„™ģŻY¤Ō’½>7ÕPßiŒ:›SĖ)Z"źÆä+É"ų󛹹‘[ņ`p¬W/ø+Qā7Üipä­Īé‰+×4ņ# ¬UTóI°.5ĒׇŽ<ØŲźķ"#i\HėVŹ‘Oß*quīKąN_čܦ–Ę˜6ĒĪäŌ2.wW‹ČU٬‘VµāKtEęó^µCF±…°’2z)tŠY£ē@®]!ċµŸ)Yć0óBżoČ€wć߈d§aéÉkH0Ÿb“6¤[ŗD‹(yė=e>e~L›Či+ŪźVRČ~7n–Œ­5YŽD¶9ŹŽ;˜†Ķu%”&Šļé ļjŖ·:N"÷Õ@C“+{™łefĒs¼ŠŖ»ņ!ĘŗŽ ]† ±Éx )d~}å\‡*Yf>ķ)r8ÕI\”‰äU°5¢Ą@cfŠóÓéL؜šNš³"RSɖHĻŽüep撶ØZęäew>ųąŹ9 „®¾ņėCćÖQŹ×Ü}Œļk[« ķU©+MsZ2ó¤¾L’ķŽķvˆī†9ōE|…ä.d|łƒéŸ“¼mĶ(Ž%łIĘnńO%8ļŽP”f§×_žA§/¼‹nŅē‘ź7L—‰Ž&Ʉ¤bIÓDÉf`ŻĮI¢²bEt<}łLȒœ£į%μ׃ĪSœć杛–”꧉#qWņę\Fzāo0^DŵØGļ.±ü­•˜[<՞rA¤3DŠÄ+‘v§Ń__Ä?ĶNqŽük®ń_"²Ć×nPņ’Æ€gņ²uˆĖe®‘Q™§Ū¬2ˆéÚ¬ū‡0h„ók¢±›¢cŠ>šĀE“¹ ~,­Ž“©¢ō š.„~-Beéę÷Rå¬ūĖŪ’ō“<ŠĖ÷ Ä{gjĮO¦Õ‚Em“”āTŪ uRL C}Oǧ[3 ? +ā6…|ŚŠjÕČŚ” ”#Ø÷õ֔ŌR©ZR)ŁDĢV‚M—Š„bސEfąCŪ€Šc”×nl­/-_eu_höŃĢ#9(ÆĆ4¶Ņ¶{wLĆV¹øFĀ cbę r.½jšž¶ß^ŽŲßW»\d7o“U- ֋Ž$¬ŗ¤LĪ>źK֝E‰‘$j‹`"dmŖ,¬mtØæ²ccµe @O ‚;Ī%{=Ä÷3¾āéī’įę„Ī5'¤÷s)é‘`2*šÅ2'!€§"„ĪCōœ¦Č:Ģ;„חČCä·.ķˆŃ"”ЈļJ‚EžįĄÉŌDD:¶«ń«ĖÄŃ5q'}b`;{ż4š9ģ ·02’dōJ^īĀ ś~]Z˜V#ło^«±ļ‹·b|;šbŒ¦1žČ(ˆ†ė%?GĘ2,ʓ0ūūuŹxŹu’yciņ©Ż<Ö‡’­jĖ9B> üųW<!Œ¤G+ų“&čÅö¢Ą3µB9ŅĆŌ> (žCkbįWė×~o"ŒÖtŁGč•osk1wA’1vžƒ'ōĄ?÷4äAOŽ+õŚĘÕŹģ¬Jųįˆ^?Ī:}ź‘|‡ä/9N,›²”WUž+Į–62®Š™lI[ņ^™L`1OøžŽ¹Ē¼·J—ō¤hżŖł—JŃŚ ‘Ó`ü*¢ņ?Zo—Y6ÅÆvź`ŒéÉŁ¶ģܹn?xŖq±Ō|P.EŖ‰™dѰOÉ»Oŗjƒ#€Åī j¼2ćo/Ċg’ā(‡A9ŸÜėWø‚ģ@ŲbƬ÷ÓĶéY ÅM ŽžĶȀ²+,ķSmŌŻ¢VÉõ÷€Ÿœuś/£Ęd6ø“ęó/ŸÆYČÜ'‘ćŚEf.(d$Ņ"1¹å¶?xųÓ!2ę<¾QĮ›“śØČ«cwŪŽ&9K±L=”®GóūMe×:ų7ųÖó°×ōMAļtĻ“Wεć‹xI¤s5Ģė"žubiKÆČ ½“39Uh‹„õ 3Ó|IÅį_##^­¬Ē}Ź™.öO«XoēžØ€Žą‘v‹ł;‡Oįč¤{iut|GįŸDy{¹ļšIųĒLöŗ¶°1»ÕĄž³YSy«lŁ#ŁŗI±A"¢›nĀ™?L”ü2˜1‡©„wQ׊,Ž6F"`0)MĖ’—9ĪĢO¬¶Źż}i·0‘pĖŁ¦Ö²X–Œdƒ5',.Y²q7(dHQy&³Öč™cīqMū5D6Ööåīa•åļ ¦gqå4W*Ÿ,’‰\Ü¢»†&ƒš¤®~“ó.3ö`ÜC³ÕÉOĄHE? BŃŽ$ÜéŸÓ˜–dr‰N× ’p÷Ÿ Ąģ·†āķé õČčõB“ŅšŒó¹£“»ŃU·-›½n»7i•f®ŃQ³„Ž%Qˆ)ØQūJaüŚųˆ FÕ%³ŗŽŁOā@įt_QĪO𵁌 cü½Ś¤™?Ę%ŪŌzėŖšŃ®’ĆŹēyVū¢c§“ņ—yW¬6§”²h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢/nÕR±ņć–1ēx£³{uP Š$PNRåa@R‰Ó) ńūżžĶj<`’Ę'_ā’…Ėmį罕µ§šæÄ՗/ŁGaƒźŅ~ ”@§ŚööÓ0OøJķą~¹ļÅæQt‡o)QrIƒČ¼“ņ9Š& Ź„£aP@īS¶žøH#±…3")7Ś—kšķ<8o›ü -ĶsuĮųŌŠĒ”–ĻQN~] YPßNd ¢Ø&a)ø(’8@P¢ ~ŲDw ƒQwŸ ›w•% @śīٹ^7ń¬äcÜƹn‘š¹Hə.Ā”…æV”€^Ƥ}ŒQ „5„Ząįµd–‡ §b‰QĻ!ŸøzC$éŖ£ķ9wŻ5Ņ7Nä•.Ę(‡»ņźY®nfģ*=Ą“Šķ[\ %ø’–šWŲ¹“,•.žF˜EDB>³’‚²:*„܍{$ ’Ņ ųJŒ²N&Ę] ×Sąķkā"ü6ä’ƒÕ'{}#gbäw |<Ÿ‹Ś7ųO4x/AōņJ„š:ߏv4Įé:'k†ā`2¬^&Y/·PQAž [Źē#9£‚Üę¬BéÕf1¬–KĘĒ‘øPŁ9:GŒY…ÓH*‡é9&¬e˜r$ų­ŻŌn­§³R²u»°“kNöøbėSØż'PeČĘ#ź¼nsN„’ÉØ`7rkÄK„Ųaī'nŪ†Śł³]øtšäó“ā%Ć©n§Ō-hś-hģwJü.\Ģgšø(ó‡·Ifæ·YWIJ1K$dē0œ³§ÓXNō٧x(¤Yį^RgŅS:¢ąŪ“͹“³c›_Hź8)†8=”Ćx]šµyT¬½ÆŽ¼ŗ[ē²5æb vDµc§˜nĶ}°cz\­ęK(ūźøŹbß#µ‚G;ś»µ…UÉćÕ3…DQ(mȱ¼š–‘ČŽQpćČ.Bį–"„bŒĒå8åM̳Töv£Źa|Y“Xݬu«Ū÷õČ×4,ŲyfŃ2(¶ŽjĮĘg/k„…Ā„TPņc‚ōłĶ®ErĻ ńā &ńߙøŪˆŲ36Ó8¦NäŽHctāĒs‘ö‡QČ¹Æ ^†ÕēŁ&ȵkɱ̂ejéŁČ§‡·M’³Ēńw¬fo7¼Ē#=YĘU8¼¤ĒűDHٲ£vÕŪCl×Xµ ī6ayŸ•³Fø Ü{Ą ß<·pó=š‚"Ӄ²ē‘åoƒļłI1Åü}dÆ<>.ŹEĀ.2&A­4¤³~Źkå©·i·¬%ŅDÅ~’gX‡QC żgäw‚lź˜ėęŪ]2”7—8ßDĶ™Z†ļ[¤š¶? ēųCæYĪvŗ2ŖŲpÖ&Ŗä)iI$ ŒÓ$„‡Öī:żās‘pžBģ]āYų7›rN įÄn?£r~‰„ó&p cųĮāĪ)”ÅzĮlĀ÷ 6ńį¬,rU2³Ō­ č‹W‹&š°¢Ŗ$\_ē/¹Z^c¼Scć/3ų£ĖīSd/ņ ›„k™ŽžŻ sŸ"ā`ŸD­*5ų‹# ¾4¬;tŽm“½”]™TŪ‹4r™Õć$¼dņmżqÕ¾%•.µ].|~Æ-‚„±}wė᳜u|ʜV Ī=§Wļp•rˆG¶š!¤ ”¹Ņ~‹² ÄRś”Ę>6cå£ÜPxł„(ī"r ‡-E­NÅ4J±ŁRŻ)_µä¦G„bf×ė4 ŪĘRŚAėWK$²Ē"‡(‘_t—ü@¼K>ł<⟠ąßG=ØpėY9Y•Sfąt\ƑˆZV8­K;ÓböÆYtĪiE[NwÅģß;N=Č'cA>Žņ«ŒUŹkcY ¢(”,dź<¬¢:Od›ä*厠šŻ E-Źó™ŗ€ÄM{鿂ąąŽ©q1?ė4öfD;AźWŗē˜1.1pÕ,“ńż Āč‹ęģķ¶ų()LÓ8i łīŅĮŠi›¼ĮŚ]ĶÓT>XćöÜIT—5øXćÓōŽ×½BŖ ƒĘ ¼l —ÖhREP)€§ŲČ(˜õß®¢ą5ˆt,'mRW—‰¢.Ņ"ɑ‚ O±Ķæi~2q»@Ęķ»Ž­Ķ÷NčAµvńvsżßäņ7Āžt­.š]ą"™\į,pS³ø{e[›®ĮÜ$nŚåül?õFśCŹTę÷NśŽ`²Kq·ÕńķJÉ|¼NĒÕ©”ųgö+E–]C! ‰œ?’|±”ŹŻ²%Ҙꀠ&Ō£ņ¼E.‘Ę€ ågø†‚ē`а«š< ń‹’Ö<9Ē,®Dŗ<æņ3śŁ ķ ^„Œ‹J½±Č/Ņ³š2^NQņÕ$Pj‰#ŹEżQ0“½w^Šļ-µH®®rµ­' ÕÕ"›°ļPz½änÓ„lu')ʘ)•3¬{m!w’,΃onķ čßķQuUĶNō.KQÜX/ųÜęil9–¹Ÿ'ac‘»ŖūģÉzĮp4²F\鶑‡Š—2 rī©QP„č!ĢxŻĮ–¾śSł>uŅ8~ƍ¢`Ŗ™xŸŚ<ߐyƤ.aŽ£8ć0L…_Ėßķ ¢päĢęĻ•½3ķ·ÄŌ¾šÖ³b<#§C½ó ėČ?e½ź3ˆåĻØ±‡dt=uŖ•aéd1}=Łwķ;h“Ņ2–b@7Q0°n#ŌGØõ×讎ģślDl£‡cœ<ĖŒß ·O”w€Twņ%`™įŽ[%Qċ$b”9ČyX‡n˜JĀ9‰ČµEԜxÅDž·qĢU[¹€B›mkæ1- ļŻĄ›īĶ6ą%eM9…j¤ųbsm­Ć3\Zįš„`k”҇„H<3Ccޱõz°Į‘X7Š‹ŽŠjŪÓŽ„d+"b1vµ›Bœ@@>%G؎§ōKéśtvķnZ4 s4ŃŲ+Ö£o® Ķˤ&µ$õ“R®¦„–h‹Ąą2ü’˜é܅¬”Ž%'’oq‰"QŁ]Ź”¢ńŚŪnN„0ģohkå?÷/ØWš½)§e”ަ“yŌöšĢ¶O“ß”¦“Ÿ+”ś×Ź‹-u8ͬ•Gń2qŅ9Ī·(øDé2¦1ŽGj3žā” &†ökŖpĒś<Yß¼Vł”’¦³„ŽR½Xµ°)tŃDM4DŃDM4DŃDM4DŃDM4DŃD^:|Oš|ĮOų®ļå’a˜ ÓøÓż>/ó‡ī¹müž”/ł'÷š³=®jŗ:ˆ¶4Ńy‡L”é&ÖŃT¾Įæ(ōQYóÕ¦ŁŸļ³8ÅÄĮø{ĒŪ©»gÓęŒķdŒpč9š|”D^7&” ƒcć{OHŹįä*KR*µ8ŲV%L”’Ŗ¦oöÅŌLĀ’»„JDkzS«JµI[*L­ %m$ܦł'Ą]Ä»õuQ±ĒŚŅP÷€ŻŠWDG‘[’1 ż%nµÓń“U£,ŲN§¢X*}/é7Qs‘.CŖ2p²’tŁ@ŪµT‹æMĆSv—RZĪĖø $izJ‰¼“ŠīŚ\ ĘšAōõ)?ÅNEŁg'a+U/4÷L`2q3|³IuĢÜV‚½Įa3x»ŒY~m©Ą©-ė“8n€ķÜō½FRĶ—QoŽC¼/õ­*}żö’RµiÜFå” żī©i†w,ĘMŗm²ręaŹ«˜¦ˆ6QgŖČ$c€¤Łä9Œ±DRģ(ˆ¦¤§bŒš ««FŹ\FćO+«9‡³N­˜Zóf†¶Źžī:ŽÜīe ,·F¦¬ĖW2)3œdb8PĶ΃¶å0¤å"§ćAop ¬DPŒ|ż«f6×w–F+¦Ņvš“šcŁÖ;ÕĒĢüÄĹĖåš+Œ¼“•’æUÆ­SŸR &µqÆXYĖ&ō¬¾ŽŹPŒ?Ģ–Lķ]5'é&=į«·7–ņĄųĮ!Ū¹łÖ5Ž™ymwÄŹŠģĄįĻՂ±L+g‚‘¦äz•R›UŹō{-Vó³A’s5Łv’«×ĶEG0rņYšJ±X∐nš @G[™ž'ŪČI‰ķ-=b•[ˆö€$”•<ž\U`D£8ŚtÅżsėo"ŃZŖŻAėÜŽ>£Œ‚ĘõšN«sˆm×Ū­Mœ#?Äøqo3({ŹĶ7ó‘€zVax“ń%š%,IU¢)™ÅlP6Ź[ĖC׳·Ó¬’•YöqĻ^.Ķwń^Å †J2œķ— s‡Zö”a…ćķIĢB("£Ź²”šIcŁ·“)^ćc÷)  »ƒįY“ē©(]Ć”ˆc8U1Ü>ŠaųLäUēw*”ģŲæU`];NČé&b‘DdQõÕ;…ŠŠEųš•Č8nqčķÜuC£cEMUAī&‚‹ ųtR6NęZČå6ĒåØZ¤ķ`pŗ-₵MEŠšEPH€7”»@ŪQ\VA}>Ÿæ"Žų4ŲÜ’ļÜb惰ūżšŌÖą*ƼU¬{ꌂū‰œäāÆ/”“Q4ź¶Ŗ¦tPī(æOĻ6gu8oPö1šÜä|Ē-J„HNMęŖ}¹™nČq2ŁeÄĻ£Ž8AÓ2,‰x·į×"8MˆņžĻY†™œ#%³ŽKŹX÷#Į×ßW²M½Ė.¾MɶģĢķĘĀ)yžĖVɑl„KDŪGÄ Ō€įĮÄ}",YāŁ&ĀsĖWĪ1Q°æ«<±gÄføYŽH—˹JŻ™č£Š„3īD·UāZcŖK 6Z\ÓPu¶óJKŗ*J9xr¢D E°ŹžłÉ3‹¹ń.Pć-GrŸqĘߛ.„ļ¶pĖ|cāÖ6Ā“Ž1Æ• *‡õQ±äKņ‹ƒø§ÅĶa;//ń~Qā|ś”ņWZīy{¹Wä[ÓGm£›_ķÕ¬{mNYH*Œ«”įœ”}B‘A~!¹Y–rÄ_"ł˜ń3häsœ‹Čš¶ JŻZĆøCš ”śł³ÉjL:8ŚĄIsŽÖā÷*֟Tl5¢ķ‘plś~ŒŪ[GĄĖ×4™¤16Ió@Ųćs°dm&„˜ķŖƒł/Ä͟³«2œ…°®ģ„‹ŖfČ74ŹK'Ųe¢`2Ża J¼CłgNCéń³S"šKØ$6ŪV‰ó2Iþ6ŃŽ 1&'–·”ĘhņŅss¼µ}cåø“,7m2?ŁZXīA «?E®ŹNāT6})qÅŅ[ęz„­"ė^Y³yčI“[%#W qŽ•AÜz®!¬U¹R$cµ‘b¢×)M±»Č” ŌōĶVĒW“mīŸ#d¶vĀ;ĮAĮ…ĶļōūĶ2éÖWńŗ+–miī ģ ī UÄöõ =@CØPč !©†ø YLj½“@§$ŖÄ1æD§nÉg ˜ŻCį¤žM[—õJ «7¾8ē$`ßóZ)4ŻqźĀF®ķióĪaņl[©¼€;FE· žK®mĒtmÄ/hõ¼7yB›ŅńeGTüŹsroł õ)ūU£ ß”aąc€å7N«J)„YL •]ŅBb$˜N¢¦ ·8†“2f³R‚YHÄ­$īłĆ+nö°Tå+®*¬­ŌŚÓ˜ é °J“ž Į_t£Ź­²Øfć_°>Å^2~¹4Ä¢(ØRœ‡ åķ0ļÖC™†ŚØ"ĘMGbĀ(TēCČ·1Ó¦ŗ„ĶTųło—q“GķBU;“·×nfŠJLćčeF!y½„Nr4xŁ‘ÕŠ)G“%F­ eüæ– Ü=}Zēł+éWÄĪ0ø×›ņÆ/^¦¢ķ3w)ĀVҚE@g,£š;y ,싸&ēQˆłw©¤cįhnŠ) A76ć+Ļh­Ļ¶y?[ä+kŅ-„ pn hčRóŠŃųc‘×5—6l½Źū70&_žob±T"Ń0ER1U£¢˜ ¶Ż€@6ćüA£gM‘6ß»+ĻyZNø’R“¤ßłÕĄć,Š/1,[DLc}"fĪÄŻē1ĪPZĮ#$™T£Ž}Ōr‡°¢!°č§J$Ór”,ƒµåĆŹ¹ŽØŅŪ²}ę“÷ęU†iŖĪ]ńu²©[+sĶĢŒÓv講(“\ ¤Šk:1NTȤ[˰‡Ę"ž²5ĖK‹ķ*[KZߒ•4HןŖ‘Z°š8.Ł4ævŚ× ķi¼…u\]u æj‹(rļŠv1Ģ`ßņģ:–q«‰ēXCE„Æ©¢+ÓĘjs˜¹lĮwyŲ#pœ¦ĀżéĆRėsį°z^¤õ”šöūĄ >ż|Cžā._/²ŲŸR+(鏾\OFĮÕE°Ł¼(Ų6‚āzIō„vø2Č]TrłĶž“ļÄĘ1€œšą©H"=„ńŲö—Ń/q„vxŽŗŸ ’£GõŸūÅozśk:]ūÅz¤ėaS ¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ¼{<{Ŗe¹]ĶU) ¢Ö+§)@{Jce9ó¤ÜDĄ@1žŻi\mż$꟯+sąæźę’(~šY…×9] D¾B?\·|JÜ"F­l2)/O½eš„\“ōż@ūŒĮ#ūÅūGS:kA¶øå,ģ½ž’¢µK‹~Lä}¦;Š®¾9“D6,d”‹VN¼\Ø•Ņ *‹“zä2f>Å*†1GØmÓķÖ ÄO2fh$³`‘”™\@”Wič6č™ĀÓ„D”øŸē[Ÿ§»“©Øc˜GÜ#¬qÉ ½ öv ¤+/‘ł±ōQŽŹŲ#ażnäY¼œpF .°€B5Ģ2³NÄAčēpū:×Lŗŗ~X˜ēl{NĮŅJĆŗŌ-mYžgµ£”įŲ6ž ¬•>ŸČÜśõ4±†8—ĒTéCGŁÓ8C<€f“E”LźÉŃń›Å[®r+¦s™©Ž$ŹhĆaŪ4T· "ļ-e˜—ōJ3U ¬›ŁtšYn'n˜ˆ‘hnsģšī!y«iÖ-&iZéös8öl뢽sĢiN^S€üė>˜#E`ŒY\ĘQ’ļlHR^NbĒ$ٳ'V-’YäõŠ`ŃģĆäć»”~§ Ł>ā·nRLsNnmØ^?P»}܀4ŗ”p€sį“ņ©˜bĘ#“ßĪv¬—āÅ]2Ē‘Īę—2h$GļYÉĢo—†"‡:1‡s$L†1CÜA›¼źv*_‹šŚ­›6KHć(6Ķ¢Ž*¢Uß¶ /w AE/»DĮbõż5vŗ“éI4nÅXŒ NՄĪ(dźĘF»sĢJĻ£„]ņŠßb=FŹĮjķŅ6¾ī”F‡Œ”“©æ„bŲæsćŅļL0 ż‡ųŒ­&·}“Ž+Įµ¹†,.y 8aPó`·N ž)-."i(ø.ŹpvRրi¶˜1µ„­į5ā(eĻ*Qlü}—²&Ü?ÅsQy…wœšM½h+¢{E©Ķŗppöµ(³[Æj_‡q,!ęĪ Nżl[ū@µŖq„Ęh2=£ų č;öI=Jń'ńć¾0p{ ąÜ‹†2~`ä.#­ŗĘŒž'e§Tqõ–‡Tv» ]<źąõż’āīAWČĒæA­qŹ„pÄē‰T ėź«}hŚ[64ŗFą "„ ”ß²›Ž5ĆbłŖēC}ÅŪ„cšŲ\k°“S·žu¾Y~$Ÿ&™*1ņŲfBÅ:ūw­E±¾=µM“déālEŋ!ęÅ„®Ż¢N½C)[‹ØBœLRµw¬źŒ¹¾«)øV ņęĄõ4,Ū} Ę2•{¹Ķ`ō¬ń_9š›Š2…†$sµ.Į‘ę'ß^o§n,¬·¼Ÿ•nŒĶ';}¾ŪŻ,“ĖõŗUČ=nĶćō]ØÜ‰¤Ń/M4Ė®zĶcS•śµĢRŗ,ĒlŲŚ ę›jI_Mé’šŽ‡\?gq'Ź0Ģ*ē1s‡Ŗ Ü P§·?<„ń—āH`ø<øŲ¬·ić±”S©õ{Ä“Ō3bIČ)¢H ©°`”EŹēpÕ¢$Tž³‚÷Šb[ZKØ6 ”sœrµ£`©ēÜ+3UÖlxz!uØøµ®$5­ÅĪ#AP(7’@Äo+„Ÿ,Æåȇłń&GSŅ[ĄÉ¦Å\pŹżo±+a²KŽ®uلٿ’ZD­ŒA]w~Š2‹(a—~įūė=&ęäJņJHŲ)Ģ āmwNÖµв‰°E–‡ ŻRKߔR¦“߀ŚT\w}ž®żįŽ­\r.ŗ\ŖÆ1„¬bC%Iš¶Õ.±ˆHT_7xÜœ‚‘z’F[ä×ļ)ŪØ ˜ĄmŽźM7P€\Ł ^³v°ø…mysjmds# _”Ó3N%¦›6,ŽŗM›D A¢iµ@… ŠD[Ø$R€t*d r·8½Åēi5ķZżkж/;f3C~īäØt9)““ŚRL_%…8‡³ÕJ²üŽp?n·>‚Œ–ąļ!£«’“¤DņŸ"å#ŖRt¬€Ē1įKõ÷ł®!ŚRyW٤é–PxŠÄpW,[†ģ§Ł¹U0(¹Lß2žä9„¦©ń5Ųģ*ė%s0Śb0÷āó;…gn³qw—Z•Ŗr¶7”Å—µ}$š+WÉĮ×W]BEU“Tę0︈ˆźÉŪˆWÄķŽY%Į’‹NR&GéüŅń’‘)°g"~–@āķÖ'5µMOŠTeh6$i3q@ū(œ‹³LéŽĘ0PbxÜ«±Ū Č=[ńSųkšRµę¼©‰ž®„~KćĪ^bą½  j½bŁ"œŻ«›mżį«t#j¹Pv+Ų×ń$ųIvW'<č©ūž­Ö9Īm.ßĘAĘ.MRžčh‹‡²~&õøå$ēvkÓc[ÅājE]ƒ}“l×ūGŻÜbļ¢+,·āµšžØ)ä ųåūŒćfR* &PõH."P†ū˜„h½”^T..?ńdx€–t“8‹—!e.^ōĘńŅųõuÓÜCŌEée ø{@ŗ ½quU 4ŖqüR~,Ąv1łXQ·ŠŁD?woß¦­üMæóö‡„UįÉīžÅó'āZµd2’½ć«‚9w&?tHłŸ–MĶĒÜ1 P(œĻĄ‘ģŽ@¾$^ā‘oōµ@Oš÷ö˜ļ[Ó,ĒńeiŗßX÷l뢽„ħÕi§)Ą,Oå^lłzåĪlÆńƓ\Ąˆćf3ɘŽå‘©pb¤ļ8·%O¶V!-”"ę ØIå+6€“$ŗź³wč¬ÜĀŽŻūźZ’Éc£»QÓ!sflgÄŲÜĶqkØÓŽ"”Ģ„ސŁīŽĆč ½]ō"¢§f»5Ę~"āœ*SqŌ$vŒQ7XÅŻ©f¹åÜŲł»…f¬«?&«©{ŒXw 5Ø×«MŖęH©&dX 8ʱÅŗ– |Ļ~­s™Æw²Ų”ÆÜÄÅŪ+€©`k 5rŪm4ŲmēXkKŸļ8žŃµ4Ų³……ųĖeVf× …pĖ»z§yeœCć—RVKˆu¬n”®ū犟Ŗæ6&PĒźmĒ®“'jšÄa°ŗźč1‚,”hżš€t)±mjIpŽ:»iŹÜzpVFŃć+Ē…Ńė©ųÖņAꢳ·Œ1duƅD@EEXBć0‡Q „Žżõ%qe»C"ŌnĆFć!wļUcæIÓ$5u¼Uś y(¬^AńāÖÆ ż.)ńīµehŁU¢½i)w&‘LĶ=fIģ£I*±JSw·(›ø7Öl\kĘRĆtčĪßZ˜}`ŖĖ“%øų‡tyŽ dźŽ ć4“fŹvĔ…i+U^NXźŲŽ\‘ˆŖČ\kŠ—9KDB6A&4łGź*TOŽ£rœ› %½óUÖuX“뛫‰&péą\#q`”$bšŚTPąm¦žę^ˆ£kc‘„Š(3·1ٹµ*UšV.·`„œˆ—ŖŁA˜‹dt“7•©8°q2Ž]”ÓuĻź2A³`2K‘AHÅīķ€ur&‰óG+.#aia0sˆf͵mqź×Y–xed2Ęö›iŹZŠ_·a¦»•¼²)•JępéU(¹æN¢¢ź±BŒź6-tĄŽ˜vX¤äńĪN` é®źø¾ß ęß&§Ń€É5¦¢ĆƒĶ]<¾ØDzƒ”cNu ¾‘Ćw¦¼bĮFȼzÄĒ(é1ž•‰qBćœĶH¼Ö«rÆy Äźq3Ræl±Wņ|æ_½U“Œl ģ̾µ»Ē ĢŅ Ż7Uń}$X(+G.•W¢š•å扨Ćy(hŅo¤šd,s]ō%nRCD•”-˜PŠ9ßZŁė3ZÄ\ukx±‡µĶ”Ą}ø˜ć*.«rš’\N6ńŻ9؂53‚øY’(ŗå0Æ"Ö ›’qļ w÷—“}śļ-5 Œ*¦Č€ŗ­Ł”{Lę½:ܦ¢S/ķ"˜Ž%﯋9‘g¼^&原cČüĖYƹŖQżNnW6|žĘĢZeī*æ0Š•xK}ććÄĢ·PTE÷ Šč {8%±šĪ «ąųī3ä.`Ø"0Śfi"„“QÉZ(ÖšLüIs1ӞĘĶGćLٜp”Ć/6åqøÆ/‹S³qęć>ŽĮwĘ”£;˜D‹$’9¬rō‡V&¤vc¼2N„Ŗ~ŗ¾©Ž¢bł2˜z—_qü±ÖcÕø~!ņ[E m(bwMÆÖ Œń¶“.­Ļg% †wĘHŲNs¼S ©w®”“ōŃD\ŽÄµ£ēŠUœ¬šī«ĒD'·°@NoaKłvĘŗŗŽŅ#$‡Ćy?–Õv(+²·fóȬ>ņ1ųĪźOrٽЉhȖŁĢQGÉÜčHė3ÄęĀ—hkŚBĻG“UTr£t“;7 ]1HЦ¢`S_üŁį=~~&Ÿˆü[{§ øģ hEMŃ4å t·°p3šśĄĶŽa˜np=µĮLGžMü“hWHņ»ͬÆ`7†Ŗ/a·ŁŽ* ~­³:­fVÄįŁĒįŠŪ¼=¢=5ɇ«CĒ9 ¤€½f‘©Čń ¼ī¢ėm‘rĖÜŻų„ųÆ{Ćq7:¤›žQpž.¤V‡7DšQĢdö7bYד»JÓķ`$‘)—kó)7YĖ3B”åŪ£hļµŅć†BŅš\}W \M*0Ć}7­ŚĀĘėMµm„ė w¤“‚©ØŪĢj½sµ2³DM4DŃDM4DŃDM4DŃDM4DŃDMxōųū[Īi¤˜w“ė¶£|ŻŃ<³*Rˆ wŻ­+©šPŸś§÷JÜų.æ0’„ž ³Ųāżˆ’ņ5Ī*D”XŹåÖB{Q·Ućaāå.9$Ł6¹1Œ«]ŹŪģU–m$ŁÉŒLkgƒjh§f•:^™Õīų¶Ū“+Oˆ†G:Œµ0¹¦CƒC‰bh#Ŗk×Ģ“|gŚølĶp`ŚZ PļWJ捹y’ „+ŽŗH‡ "-‘Õ mźmŌ(µ#Ł®N½0•+NīÓ~–ĢZ]¹’¹¹ŗ—~ÓØß*Ɠ[ā–°Ļ!§ģ¶§¼) Vńó&ŪŖć2ņā~4Ź50…[ųšæUjD»ƒsd+Ä„‚Ä›nļ„TJ95@½@wÕ'SŅ¢öÖ¹ŸĖ+‹»ZŚ„ū½Vl0cyŠ;ĶJ”X‰ø ŗśÕ6€ÉżĢå(:ɧNrHtb€npøŚ#%0n)Ēüš’¦°nu[ė¦ųo~X=Ę ŒģÆ]U†ĮwˆAt¾óŽcŚTŽ1Œs ŽcĘĢc˜Ā?hˆī":ŽWWĒpģ&Ī:"Ęß2ų¹³.Q¬äE]˜w1‡ķ0ļް•µké(¤öєķ)˜ŹVŲĀ“ŁA0˜¢Ö…ĪČ#æ±Ų\Č`éŽS“uŅ“ŒZdy… Ŗ{MGrĖFÖóWµ\LÆDM|2(^ÕE üS” _Ž0h‹`hx“›øńqĒ0ūLfMŒoß„t ^Ō­Dcc››½¼{$ütZ ‘æŁ2Ž” Ź•Lߣ&luāQ«EYK>PŸŖājŅmĆw™É–8ŚjDaļlŚ]U÷é±Rö±īęöŅLķa=‚Ŗõ»ŃÜCaµąN+æ£c±ü̬īq¹`I­iķ™¤žOQG{?™ū”üń©8“|ˆ·Ķ¾†}?ļ,Ü£pĶ~J¶ĪA„'Å®lS?OĪ'¢d“r>”~īé¼iæ#õ‹‰ŪŁa…ŹØÖ:]yTl¼Q9`‰ī?¤CGœ«=Ėo —£:#in;`X÷[ö+X«Xrå͹N;‰#s~Ī®w%ż0j`īė·»]Nł”ĄCÆå–b7Pv07÷Š—_ŌdĀ1c˜T÷įܬlę4ČŅ-fóīPɼ…•fŗnŚ3ÉV5ā@=«ÄcśqT¦}¦•FĪo°5Ó4n įĶ  iöѵܹEOIöė¢¦žāä湑ļ<ēĶŹ­Ŗ›)įeē2[ĢdŌU:ņ¢ĄÕK¶³M…ĆGøāMsšwŻAGuÕ£ń …#›}ń5žŠ5źÉ4B;½ĻoŖą~°Ē¶£™Iézö­£XLįųŻėFzXp-”ēTõÆÉBia²Uģü0ćĖ…QÄZgź¹C"ŅBPņ‘ČJ5—yQƒŽŸIŠĶLAžń5H€w1 ĪOĖk›i]ā;ńC³µĄį̧fćŠćüM:ŌMļ1Ņ0ōՅ§½F\ĖÉ.^gi DĢKŌń3,}&3T¢2w3˜,’+¶Ž“U†AȬĻrk\±BÉ””!ž/*Ū`tÜūušÓ8&ĘĘ9.Y(£½PĄįdyYPEZ칚v µ>-ŌÆäŃ“b5g¬dsN Ńņf~WG36W  ŖKP^Rā7ze@YE¶ˆjEż2øQ$D§QŹé""“p9ʚAśéģÖķ207Qj„ŌÕ^?H«~¤į¹żQĆķ*Ÿƒ÷@ڹ·Y"ńYc™ß1XH*\ų”uƒDž”Ō}D²b)1D§6ȃ³Ūp—Ż­Œ£.Ņą“ݐw‚žœi1­]‡õĶTŹh‹AĖFÆSōžµlń/rnŪ¤å0ߌ Eˆr€é@v¢Ū2ˆ‰ŽPėGÅĒ0Tåģ:¬Ł6j”‰¾ż†: ĀQvūkĄŲ½©P³$Ē—6j(€–^"Üā$MŲ]ÕR“›é/¢wWnįIyšū€śŹ×J-?FäÖĆ_ [’5yĄöMƒFÓŹV3sĜž7ä>OĖUę/„¤±UĘ:^rŗÉ%ÕĒ_±~:”Ču¦ Žąņ^9äq§bŠPŲŅ‘…@?š…_Nü£Ō.-ø>ŪS€9ņX\Lǰm|!Ņ0s·ļśM¦õǾkŁĘxŅźÖJ5—QÄöŸv@ĄćĢi•ÜĘ»”ļ…šˆ²CDXėņ-f ,qórģ”YŹCĖ4EülƒUōxĶĮ/æcu믧!š+˜Yqnąų$hs\6øTŅ{ĢR HŅA„`BäõuP¾N&)b—¼Å!ŒRDęˆ”€ač^į ·”نŌQöÅ"v²6ėš`ƒÅGéõųT TT?pėvĮč5L §hź˜7?A޳u„޽æś¬g'O0ŪĻ“©HžĄDų·kłr«SqŅłw–|%Ē-NŃ»‡¹ęzėõØ ¤ć[ć<•­Gt Č%TĀw‰·Kø¢¾§]÷óŽa¾”“4Ćé]SåT&^1ūZƹǨW̳䇯LŽÓ2P¹żh„“C‹Wj‰‡¼\Œ*N€W8wqż½Möėä’É”5%}r$…§3Z¹€먮n,œ7āPā|säžĘĢĮr›‚ń’¹"‰9lģ“ųÜźŻNC"ä¢S”D¦)·t®ŗ<`ūĻżāøWø;‰§#Ż’ĘÕė ©õ§¦ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"ņx±ž/52Āål6Į·Ŗģs+ßI˜\äšö§dŗlõ¾Ff­%M±NYąpe/£wK¢Ģ=`zK“³Vå†Ū’f5ķ­hąÆZ¹ÓBģŠ½Ģu6“{•nēšė~#¾Ē4…Āe!Ćd¹›Ž€ #ڐ&¤}Ļģą/_³XćNÓĘČ!ū ō+ēPæ;g›ķ»Ņ­õošäž!Œqk‘»Qšµ‚å.ÅV¶źĻ*°£+“Š¢£–/¦C,7‘vŠŠ“LŹē1DHQė°jģ–¶ÓF!–6:!±¤VÅ`K(y9ŁĪÓSSÖ«) ’Š7øĆAĻÆE?S`Žåfx&ōDįf0÷ļš’X߄éž¼?`zqﻓŖxŽ'’ '~É9>qŲ¢&/"1Äź†ĒżÆ7ė¶©üJ’õįū#Пsﻓ­ņ/’b'Ż,AÉ~ą9Gõ¹« *^āNŽ2iČbż”ś&÷ļÆ?ŅO’ėÅöB÷āī˜īÕĪ£ć;ńNgāī;ž»-ńÓßW!ˆ€töjŸĄō’^.ÅļĘ]{ī\é|rž+lŠXĆ;¢`'Ée~4Øs‰¶¦[į•UAĪ=5ļąŗO’Æbóāī}÷vŖy_ŠhÅUu1 ĶÓøĄ?FPv÷“I| ŗū ޼:vŪÅŲ½ųĖ”ōŻŚø'œüQŠd[ ņ”D»NaJ:Ljēˆ`7p¼]’@ÜŲJ"möŲw ĆCŅØ·Š½o.ŽGv­Ģg?$c•‰Å\Ņl"&Z5£1!„ŪoÜR8Køv¾ąŪUK8˜#ģTüUĘĢī[¤øķų¬S)ŠLgͱģ#걄*!·ńL²§1}ąūōüK?ņ#ģ^üUǾW&†üW(ŖLWĢ#‰ŌŁĶk;(‰@zz›¬UwżŃū5ēąšWņßéO‹ø÷Źä …’‚‰ņ“ #ķ.>Ām öōP‘(œ÷uēązOņßéO‹ø÷rü?ž,%ź³–æ’ĖøYģŠdŪOĮ4Æä3æŅŸqļ•ÅŹbŸÅoŗnń0–ōĄ@F/błŃ(öōV½ *ę0ˆ‡]yų“ü†÷śSāī=ņ­†…ųŸ„Agš;%=#©ņżÜbg!č‰DOMV˜įaD§ö€§÷oÆ é03æŅ½—-Ųņ©sb?Ķžēž­¼ī ”‚~ōų¶±LRģmÄ –7Ūū:źķķł ķw„Uń÷~łīō+!LāŸ,n½ŖV—Änx׉mšµY3ćuĶXgs²Žy'.ŚøęŽę-ćÕÖ*,›!Ž;†Ą^č6”—ćmć‘ĢhšŌ°f!%dŁėZ®žŅŪ9ßj@„+ĖBÆ@[ßüńeCWM|ā:lĮU|öR¾‹Ī0ݶ‘`¤[· Ņ>‚Å»— G¬tŠeŠ ¦C›³·øwæ„in‰3®4ØY Ī$T’91'kSÕ5 b§+„…¦ .*1Ļ|Õ6ōĮ~ sM?[»Óųµz8ˆnī©QĻŁśAķŪ}Ož#yļ÷Bƒü:ĻÜļ>•NĪšĢd«1S¼ę£ĘN›ÖE^+ä1¢pźtWBˆ 6T;z3Bˆt[}ķԌ,‘õaę…[,­cp|l£‡9ō«w#Įo)ńqŖWę8Uͳ°Sµ27”ć6b“Y”‰Ųą‡zźéģj„€ŠU1é¾Ż5cÄ~\¤œ«*iGqgŹ$I8Ģå€śh×ņģ”@Ą_„©-)Dpæq7žę«ló3c” sKćO”Ś#Ēļ*Mę|qež-!!N1e§°®Ž9TĖ.äaМE¢²§8īd’!¶č·M`\Ś[]’PĄćĖæ“b©|l“*®Ó|øāÅnSŠq&^!^·v G‰€@Can±?Ó—ŽļJ·šŠņyV„„–”½iN0rb%!(˜«æāeį‘ Ū¾źG8ų„Ż·ė¹Cof¼üMž_ķ;ҟ 'yVā¶ļČ=1fŲC3ŸĶŹ?H'8’mråg2Ņ.„dUJՑpø«"łEshŸ`ų@Rģy‚6`Ą(2¬ÅÜŖr'T„…[äDŒ”Dɂœ}ø¦*a1ЁŽūĄ5_'*óĮ|›"łL s༆BŻ„9ųūo‰„@„1 ölĶt†$E©°$&īģ°ÓŪ¶Ūh5Ū±ōcģ>”ńĢµMä5Ø!ńłDD:„m±]Ćo`ĻūōüvļŻ°śWž3ł•’²ņ«!Łļņ—¹J„p®åWŁ­› ¢Ę¢u5“fb‚ÆŌx¢ŽSr`[¼ęh ×]Bi¦386¤Li‡Z¶óœŌŖ°ÜŪÉI “Ёo„…3kaøl•…ö»Tül¼ļōŖ2…š<įȅćRé„ ō2v†’gpĖuš|l¼üŗÓ([¤¹×t/hž‹N1˰Šźy0®ā~~‚`ö€ž]z/¤äoze įqļÉŽ_ćÅė^ą)xžĀū%‘"Źm ԚŲ"r+'¬^F>…ļā2{­ļ_üJ¼Ų{8׃Š;Āĭp÷g øīiż©iüŁ?gмüFOu½źČŁ|śņ2Ė•ė9I|7„ڳŽķxńāx<{ÖöŪ-NĢņaĆåm‘#ęŽ)ĶRE2ØśGSø¦0”ÅŗxbĢŪoJ‡WÕ®Šl„1*KHā[ĶģŻĄČÜēFXC³R„ƒ\5ØVŠĮę?2Ś2mŸ#Kb|X@³ET#€‹ZŌͳ1Ø·”j‹ō¤ĢH;pņI "ŽØ…#t€….Ęīßx?XŸƒl¤°²kf·’LēÄ­A„0ĖAB9AZ÷8ńv¢ŻJö‘NŲĆ)Ź •ĶS^µæÄ>eņĪ%«Ļć)š“tå‰õa‚Ņv¦ Ö 'f^Ķ¶Ø³p„‚É-³õdc"M°>„!@7m'ꎫ¤Ś ķ­ßl×ø°ą±®qv@C½–’CpĄa¹jwœ'gy/Žédl¤ā2śÄ3lŚi>*šŸ“"’“ž=ßß’žÖŸķ|ž„æ÷“R’ō­žÓż+ū&׳ņv5~’Ÿ—!ģqĻļ·Q½ @GŽ "äėÆ÷“R’ōmžŪÓū"׳ņv5P¶æ5łÖįŖ®pMŖ 1j¾ČtĮE ŗ«˜N×s( ”łæŸQ÷Ÿ5u ĒūHCZ0ĻķYš…“ †Ķ!'™«ćy“Ź88Ÿ>@ą¼e(’ĒdØō+3“¶„˜L†IŖ’ØńÉäY8bņ5ÄS01Ń@8Ø`8m¶Ś7krń…Œv,l1FüÕa$“Ļ›rŪ8UæŚŗ‰Ō­æ‹)am€”Ū³¬-ų¶9"oę8‡Ēä½»ś–Üœ·ęŪ¶]µĪµ-›'c} £ī%žėx{_éT׏8ŽNyåóm…¹oŠ(X’ Žv~>åŒõa­­bZ— VĀVHĒpMŌ{8īNB[#d0„$ds$ĢB™6ę\ąFķ,¶L°·ĢsœŠI©¦ž…Øk¤šÅ龕dŽh6“ĄRø×rõDÖj‹M4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDVÕĪÅÆräN{wB¬8ĢŠ4 ŒW ’Ö‹n{|n:°OEZ&iĢę QpŒ‚«µ‚²@!¶ę"¹Z"h‰¢&ˆš"ü€ū@ofą·ļč‹`¼LS’˜Žc#ÜĮ±Š»6Ź”ĮהТfŗ"ą†ƒD1Ģ ŅŖBs˜Ēä8œĘiŒagøˆżŗ"Ō 5$ $ uX % üOh€ū@Kņ›ˆæ E¤'üŻ:ŖOäW¢ ż¦a¢/ĆQ(ē8ØzmPĒ·9«±8ģęb#°ˆ“ÖĒŌ@qHØ. ˆŠ`µjP Aw²7hˆ»DZ c\r©=5h•€ŠU`ŽNāļ±»LĄKÜōpļšž•(–Sć"ˆī%BŖ¼(ˆƒˆ„pŽŃ8^1ń°Ÿ”ǼOƉčEžŌhˆ§øÖ±Šuxõƒ•9 ŲC©‰ØG1IøiLh—qŲ:h‹Tœjć’_Ķ` &Ÿņ1UŸī`CDZźqĻk'é+‚p҉nōό)Oø½J=‡ƒ1wöŻ4EņN8qį?ŠĄŲ`›QĖžę4EÅ­Ån0¹'¦ćŽ8tūĮN۾ӜćRī1‡ķlƒ¼(‰‹Ć.(”Ā""`ć¶!¢"!OÜDtEųnplęŸ†ķó¼m›čP7ś±e“d„äkY–ĄwuūX×ņ¬Y«2d^:Mŗ.œ&ŻūU“*§÷ RŐ2',8cł/™yc€|HäLof‡Ķ×ī'!*¼¤–nZjŅU+y!°*ņ°ĮTN Hę/K"įVźK.Ś=ŹD4Et¼ RųsĖw×’¾`³/&øõ?ƒķ5¬‰Ā^\Ż']ē(œ„k Ä~O¬Óe`qŒ:Ѱm”Ž‚’Œ+k™ŗCēÕMɀ„SĻČ÷āÄ\+äømĒŽ4ęæ ȋlĪbė„šCYŁćų#ĘĒ؟…–Ė Qȓī- Ŗr$’čŹüŠH H:LEPa/Ä#ŬóĀž`ņv­2ES5p‡Ū/™Ė†ł@Ņ2ģÅy‹ņGEśČH zœŻ‘™cԚ+#øŒīī9'"ŗĪ|ŌbļŒ|ĒŹį\ŖĖÆ (ėڊŁ<†aĘšM¤Ė#Yy6·Į$ŻÉÖAUį„Šŗ.L-HEdyQų”xÄ¢Īē-:€Ć™¬l„¢ \€8v² Tnér(Ņ?Š Ÿ\ą¶Uę6_ąO#°tŪż…ÅÜO’’…iĖ„rT<Ն³=޲DŽ;ŽJæ_]ż•Ćé¶Ń-œ0~¼ƒTNEzNHé9A“z)8j›‡ĶÕtBG5@rOŃr†3Œ³[č–_­±v©^qüŹÕŪ”ęČ;|ÅņQSm•@ ؀3¤©r’ķŅ"›z"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢.†ž8-ĘüėęĻn4å÷ źįµ•VkŽ9øę|{h­ŃšLg~@8B1&ķ\4ł–Ÿmˆ›eŚ™Ņ]„'­ģōL$Y$Ė)ņ7|ńp–~=8ļCĀüX“`ܧ„łD×QqĘ7Ē4g­«9ć;}²]Nŗ“·¶Õ,ŃTœĪe+-ÓY]ĪR˜‹¾:ü‘:ü7T>^q;š\"äMėČ^Zä,Åī„p†F“8ć>UĀ—T…ĒUĶŅӏmֈ#_]NČ¢śĀnū„P‰Č|Ćb‘eSÅ’ˆHņO„Yó/8˜#ƒ¹ ę§ėv˜lrņņ,š„YœŽ/ų¦Łdجč¶W$Éęžī;tiP0ģ’rŖOœ:E.¼]ńÆä7ĖŽUȼåD„ķ!|FŹ ī,p¬³ĖDėœ J¶Ī%š/ņ’°¼®äl‰Ī¼œZūō±§ā …|ČZLŒiŃOīŖÕĘ ‚²Lä 2“°„+V¤I»9ųņoė\š¹ųr¹ƒ_Ē6 ™]ĀMqÆņ]F­O–JĻf“¬y; 7\A ‡ Jd>?ŗ…cé:hy ¢CĖ“¤YĆüOųÖżBāČːų÷擗°ł3ųŅ‹ƒõĖ]Żpu¶2ʤ:ĻYĀ! ß ½‚*ĪS­•±" ,ŻŖcRŅu2•\éŒxßā;ŠŁn×K»T®ó“ÜŅ¾ŃŚcė_)™^.&Fˇ®‡°ĢTąļx芟Ķ&ŹV†LćŚø+ŌČŗ¤^8æ‡ųMĪJaæ/(yJä]ņǜŹÓjR-‹šo>é??5LČ5ßr„FDµ<š“–hķŹq3/¢óH|ā/Ī•"ōšŸŸł›šuŹ6õćm׍n3ć%éµNc)…ܳ³YqŃŚŲ–°<Ÿ«K³ÆXįĒ9B=Čŗs É%eU:N؛…ōE×K™ŁŠĖį’ń óœ¹?Œ¹G‘ó¼¾ā¼EsĒäv9­ŃcĢŽŖøK:®XeÖ!fŖópŸ²IH÷‰A').vT¢›Eż2Šx†mĪȎxł4ą3ųŸr˜ŹžYxĮtʼ‹½ŲZSU®øW;Ū”ģķ¢›&›iõŚūœšU Åņ. gż=élœ ‘§ń‹ČŸ4ŁĒěß\1ą¼CĮ9ÕęvĆm9}*«Øź­eWŅigj¾UüĀ‹3Wā¬ßŗU'Č\Äe ŗI®R,łŚį%›Ę·„‘Ł6vü‰ć÷“8Õż†ĪåóéØ#_o°y'^eāĘ_é3Nč gÖķ£€ į¼JĶRōÓ"FˆÆķCy†ócĪĪäžtpęæĮ p.µĢĻāŻŹ¾Fē•ņÅYŌ\ŠiO%‹3/i®JÜiĄŠķJ¼éŅ䔕pfF,uła½pł4ē]Ļ“øóŹ?üŖd”˜a¾Ašęā£zĒ1&¢"’Zc!ÉSnõjLz ®»Ķ+ vJAŒw$õ\ŖEŸĀ’ČO$y÷™ŃĻ:K’lŲ²„{ŖĘq30fŚ!čY%ÕŻ7¶ü,mŗęŸƒU„¬ä”ZIcHIH¶4›ā5OŃ"ģõ¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"§”ź5Jō•Žf±^ƒ˜øH7–¶ĖCĀĘĘIZeZ2B1¬ó&Č:›mŁ6é¬äźØD*` R€C¢/L‚rŖ$ ØB2(%9H ¦eSķÜR(dŠ&č"PßŲ"ūŃ‚m›$³‡ 7A%݊ftŗi&Eœ™Į$ŒįB”°¤J&ķ(l4EÆ¢&ˆš"²<™Āńܐć øļ.ųŃq9ėå|/'$@TʎŽŹT9ź;×Å* $°™£iÓ(C”Ū— €õŃ_ńQĮłæ< Į<5°eó žo{õļ-ėē¬1z{ŽKøä•££"‘—xŒd#«z2īTUDŅl™D©&E!9qÉ:ųĻ›yG~„±Ł)x/Īd[4 A(Ŭņ±‚īŁĮ#5%¬’©źŹįÓt„HåöčŠeo0UÄxÆĒ¾oœe’$1g’ę1 ak,t:+Ud2õe .O&EĖX£\ĮÄ ¢ÕłŚ õ*q8éĒ,„4dĶ‚FE‹FĢ‘:ī&ˆ$Ż#¹vŗŽ]ø2h„2e8‡qĪa0ˆˆˆč‹s¢-5‘IĀJ ŗI®‚靑X…Q%’P¢EU3ˆ¢jĀ(€€€ģ:"ÓnŃ«O_å[7mó.vēåŃM˜t·o¬åL„õ\+ŚĒ6ę6Įøč‹TÉā™ŽBÉTHĘ(S8é ÓSPÅÜ6Ó{tE÷¢(Īo$RųApāTnDŠÅ“לmsƒæLÖ·±€sK““’0Į5”„téy ⯚„é»@AEøޘœŠ«āo=0¾fä×)ųONų¶Tą &­äK]²:1“&BūéM+°œ©øŽ•“r C¹fVŅ ł6j‹Å„P"؇Ŗ$YŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDP7ÉÆ8ė¾8ø?ž¹ƒb®­p>+­²-V¢šŽ”hČ6éøŹf>‚”jƒ•¢`¤.3ĢÉ š©œĶYzŖ§9JCtō}åóĻ/ øĆĘæ-œĪŹYņ«Œ+`¬ēŠ-Syć9Ö¾wĒFćå[!×±—¾õ„4„£;rj¤öŁN“vŃbŁي“k-äd ‹Ȳ’Ź ¾ZłēåK4xēń#‘±’±· £#¹+“1•Bšķź·ĆŖ…ś›’ć†VmŲG²aŚIśńožØš“°„T5 Čözņoą?ĢKBÅb„2DZ¦sĀ–|¹ˆ¦Ö{s“š=Ż™ķž½ X†maL4`‚;\«¢ƒ„‹ÜƲK-ųz’Œ«•“Ģ/2ųšŚ1ėdȚ­ VĶńpź§ø=d[Ć71‡Ųu Ü!¢,Äćaę~:ž æ,Ų/’yŅĖ[ātßa¹{ŠZÜņܽDbjF&±Łq­bĮ&Ž˜gMe.§’+ zęXĖ)®B+Õų^üf?!ܶ¼äŽA{’óŽĶøāĶp—kŅzÉH™‚·ćŁŁ”ąc"¬č…”‘‰ΈøržŖŹ(©Īq"å搎DxĢÄ|!äv“ø‹©—š“ŚĘw„5‹®?W,āĮ¤Ü­søģī¬PRņJz.Ŗķ"¼h³GmœS8‰7!S«o:8N“åßxŒž¬<ā³r·`?XEHcź1üd\%JŃ ÷q„‰ŁŅnV ś[ć9v5ŖR‰Hzh:MEH²Cų{<Øó/åīqq£ŹVXĖ—č|5ĀłJ[4Śģ'-Pi‘xļę9ʬbö9)+ƒÕģ˜_0ĒM…h{ևqį"ŻO˜ODTō–æ=™×Œ\‹óEEĢ\ić_Ü#wł,oŜŽŖ“'ĪPŃ·ˆšJõÖwsP%2S¹g2–$"”‘oh„FFČŻF¬›µHŠ˜¤YLņ ęj’ ¼2ņŅK‡|~É6®gg<3pŽÅĪĘO[­b¦,ŗ×Œ/d37™"–ŌP‰ŸśqŌI¤‰‹ņęGDPĖG›ļ)üfņ›ŹO\`ZŗžW5=įż„OQńŌ„–+Ź\zęógdāJT·)†BœŒI BoĘ>i2&“r¦čŠYšž^cļ7ȗˆžMä\% äæų9žOāG ¬XÕ¹č™1ˆÄŲniTc#)sõ» Uڳō‰Šm¤# ķā²1rNŲ;dR,@ųA.Gń½ĄītłØĖü Źöj&3Ė9KĆńĻG/„łš%ŸŃqŌnPČ.žżI»“g2mķŠMDFĆK°„Ŗ=tÅO§ ö“ņÕēė‡üXĄ>`9s–øĒxāß%²4l]€.é\ss•ǹĆi„Z©—jŽ/}z„dµn¶¤¤Yä­65“ˆvÕĖō](±›¢EŸbsü˜£Ķ'Š~.§Ē\s}ČüO’_¼Ė÷Љßņ 3µID£±Uä’$ū³8ćؔÓ%#Œ‚’}«„č¦"ėÉióGęӓÜōä‰ äš o¼žå½2™žečXł“M#8BVß dLYlŒk-ė—6G‘§œķS(±„H³#āóĻyÖį’.fü½Ż±žä×s}ƒ eč±=v„7—kP¶ŲjÕźõ=„²°ö<Ćd˜Å7HÖŃUĪö²Ę‚õ™&9€¤]~bæW™QēLO`āĘ`Ä/mŁ«<5Æā_øŸÖ²tØā6’ORo>ęĖ BJjŽGM¤GŽ#ģ}.åœbMAb.ĻŽa|¼ņ3ŠŁwüń߂`ł%䃐±ĢÆ,čÓńϦń¾>Åi©bfövyŪ{†9ĖLIÖ^‹EŸČ0ŒŒ‹`åü’‰&-RrE‹’Ćŗė’Õ’3Žbi¼×Z”nZĶPp„».½nž‚“ŪUŲ®įxUŪŗYĄ±wxĪō($Õs¬‰J‰S*@EŻDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDMcƒĖoy#ńłČ^"ÅYĖN¶d(Yœ`tŗČC6ȘöĻ ~¤2“ _­÷JjÅ\AŒ™ÓnŗķŁ9QteŅKD]”IąÜ­\,³r*×Ķ߂G'9 †łéĮÜÅ|ČJ“„§!0$[H3Å<’Š“‚N„kµ7‰}„Õ¾Š&9L¤ć~q©®a`FĪŖ~psĪó¬ķĒėĒ09ĘN!qņ_TøĆLLe*ŃŹvøJlāU˜č–±Ōšm,›„õ–Š’N*‰Ē ą„rYMó?Ā«wœœā¾9F1lŸxÆÕ'±‘eåŪĄ0VõŽÆõ\‡āić'ķ#ž5dńŖ(© Ÿ¤šĄ* ė¦E 2†•9ąć^?ł3H®r‹ qgÓ±®B“±µĆŗT€a£ #ŅJ«\’ Ūh;‘ä\Ä,č÷ p"ĆĻ'Dłaā5-WģÜ<ͼ]æTp7Õ¬Š0õüū˜r•fõ9‰Ź‚Ro&ń„²…OÆ<Œ˜’zTŲgˆ‘™ž¶Qs6"”¼ˆšįĻĢ™āĆŖųņ²³ˆ9ēßł_äśK2U Ö›³żYūK f–¤ŚŃŚEW䎐tŖ‰rĒ剪Ev¼¢S|łAēEóŠ|ŁĖWnrŸ…ŚĀҹĘƓ1«¹G[ā×¾JćZ»&ĶY!/±ŠŁ”•ze”;‹m"¹Ū8Q"É$‘~ü6šy¦yaóJą ņs\ «ŅųÕ^¦HGĢLZā,VšŪ)8xŪ[t”)<ŻÕ…+Ź­^Šś.ÅfÅQ Œ™[?åäŸĒ1°ļ«Q<ĖŽ3…8Ōj2¶ČźiģƒY¬ßéSQ0ņÓŖ²¬©8¼Ct-ѓ{ČāQ0ø*„H‡"|ģš={ä· <:ńŠ‹1ŠÓˆą§FēzŃ7ķįrn?c\ Vslœ#˜ˆ× ä¬R.+Ɵƒ¤Y”1õÄ]7P Eˆ“²‡ƒ¼£’¼Ür˜3ĪėkšĒšČ|iČÉׯN«Ł‡'kć#bSŅ® ŹN6F³X+ČóŪ–ķ—TČŖŻ¢ ‘aį‡įļói™øĮZń‡–r×0÷ø”+Ÿ®ør~”,éĶ‹“łRŃ-.آÒć"×°Ė3­“»[…¹Źł8„ įŻ*b°•|ŁŖÅ"Ŗ±_‚_2y'ų}€ä•Ś™ ^ófÓpžÄ7¼ģų_‹lSć`Šā‘ž£¼•‹¹'L<”ldzĪ$c‚ŚÕ0]Žücȳ-`ńO™­æ‰: É Ś…V²ńr½Ęd£ÜĒķżg;ÕźńxŅn— ³[SEš‘—|Ń«ČäˆÜ‡;¤]”Øš‘ń×ČÕæ{É3W”ō8ĢŪ…°Ä‘›X„×77“߈¬}§ššŲ9/Ź?6¶¾KWqtv3攨vŽ]"&ÜZēńž\……¹™®TŽ]„t­Vfæ/.DdŚ”Š&żœ«ö×;q"„ÜLńYų‰ųæSmĒž1R¼_pm’ń4l‹Ķ,c]°ē,­W¬œY}nĒd±@ä«,Ōċe—x‰I^®ŸęÖĢ0ؙ Ķ~,|Ļq™š½Įż$3ó*JD<4÷įū¤ōd”kŘŗd3Ö&µ(éQ+†ęķV<ﻪ‰LQ+.§ćšų@,&•Ų7Ł<˜ōö«qL7Ė¢*|’ŒwÄiF—*# TĆūęVOŪ¢-2~2ēP¤5{—©Ā + ÓE2l;˜Ėj, "|$£öh‹Kń‰ų†P'iŹÖāQ«… „T ū‰čd…ŠψJ:"śĘ%āOŚ-łVPėśĆaHA'O`l\Œcī?ÉŃČž1?Ą`iŹÓˆœøR “ #°˜ŻŁ ¦ķ/“v°4EPĘž/ÆÆ·ł«#!~-ƒźx-ņ½Į¾ŻĮō{ ·Ć·_·ovż4ER9ü[¾h.RŹŁāą"Nm€Æ„yŠD7ļxÕ« ‡m’Ÿßaūw"ųCńoųbY–>SĶMŽ nvĖ`+їDwŪµS6AĆa0_C†ŽżrGüZ^Éģ͹]Oäqū)üd4E ‹_Ā؜ ūcĖĄ#śĮćžHĮ¾ā*l>Ī…|©ų¶|.ōs`WždP’ŽKD[aü\ĆŁ”sYæ6¼õ’dkŚsÆ <…h›ńrxf(C$ē‰CpąKˆ ĒoŃ/xGņˆ”£½ySČ{½+Åćį¼=—.@›ņ†œ’VP^*–‘’Ƈ†įlä2Ÿ˜*X’¾M4E؇āņšŽ±ˆU.<‚j)„Ē_͘©ˆo±N eœœLmŗvƒÆQ }«ų»ü6¦tK¶\ŖŖTŌU,`6 ļŗė‚ņ(Ŗd‹·PLŖ)×”GD\Ņ’‹oĀśC²yo2:ҘL†ČE1Š)Ģ±nnņģ=6ßŲ"tE¾CńhxVU1:™Ć*µ0¢ær©”Ź"ŚĀ[GaÜŪīg]m_~- 2­ó`”P=˜` Žšę „w)¤£cŪnuP:Ł¢-›_ÅĆįĀŖŁC60PDĮņ®šÜīŲa3Ž6ŲŽķ”ßķŃŃ’†2žŽQĶj#^Cūā$ŃŠ’‹£ĆAG!ge?‘maæżńtōE¼Ońoųb9ęŹy©#z`pILzĀŠ ) ¢^”}毣żÖˆ¶gü] ģČYŁOä`[XĘ.Mh‡āķšŲ;’ēÖ| ‡ßģ}z{CgĆÓóķ¢-TæO†• c gtD¦Ų®“‰ŽoÜQEʤ‡÷Bł4EšoÅ×ᨾĖīz?ņp5˜7’fģš"ڟńyųp'čŪ¹Æņ0LĄo’}˜OD[3ž/ĻÄżžH+üŒä?ć,©č‹jĘ įšæ¢’“J#¤ń—$ōE“?ć ń_ŃR©üŒ%ń—āh‹jĘ+⿢˕ź# ×Ćž3%'¢-”’ļˆŅžŒ.”žF؇üfWODZÉž1ŸGH4_-8ˆī‚˜Z²*—o`ˆ„”Gc{¶8ž]“E¾7ćńø)Ó}‹„¢Ą}žĻŠžPÜ4E±_ńŒxŠK³ÓŒå›®õ C Z“_H£¾źŸęr{}Č_xøß`ˆ·M?ˆW¬‡*˜DTw…!N@0øAŽEx¦ĄŻŠ¢;Ū°hŠ Cń|xsWŌõ,܊mŁŪŪėą¹z»ļø§ņÓī6ķŪÆwo·¦żtEµ{ųĄ<<µ!L„Ļ%$„M°¦ĖؙŹoŽa‘µ0'hN‚#æ»mqüb>!KģC•gžN…÷yš"¤ĖųĖüR „ńc@ę!aJŲŹĪ8£‹EūA°Far”ÄųAHÜ "Š—üfž*7t¤f7ęLӄJ—n–+ʬJģÅß° ³ÜĪB¢U6öœ@ŁīŃŸ/ćeą(ŠüQę @ ś®Öø`ārõźp¤OLŽĪŻłōEµuųŚx$B²āO.)Ü%t8u”;6ĢK"½0»·oĖ¢-Ź_›€ęK¹~(rż5·ō’m†VOmƒaõ“Š£īģŃ?%ųÖ f®Óõ÷†ˆµ ųÓ•SaH9«uś€p]rˆž4/gLēWó= %HqĪ'8œ{wŲLŲ%.Ęéøč‹‚7ćRńØ „ćļ4Œ@…O¹˜d;ǧNĮĶ»—DT½—ń°š “dSāĻ.ģ  ‚ØM4Ćõ†©$śevŪ%ŁUQAąś%ī“EšÓń²pķ“;ī)ó³ĮÖ·jÓ ¼nAŲ7ōŻ©”Y(©w÷Š$É¢/Ćž6^‡ó|Rę’”Ļ “ūYPś"ڟń“šŚ"Ų9ün<;*g|1ä²ė~¬Žm8±ŖgĆ”ÕJeé“ ½ąChŠŻIž8L,“‚–ĒŽP~ŌTŲĖIēŖ”S€KaŲål×̦eŪ|"ØßĀѶą’āąĀ\ŌåĒ8„ĆĢ“žē«ŗt¼ĶeJÄō}^rIƒÕ+āXXś£'3d„›¤ÕS ģĢ€-źUķģ.ޚ"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‹Œ›"ŖCK‘v,xĒäDŪķŚ©šŖ ›}‡mŽ!¢/oų;fœ›š#²½Yz5Č“F7s%.Å&ņķąöéšžēyō§āwŽ’pō.y—ŽŽ2;˜˜.Ēżńķ†ęˆmö eõ‡żŽ©6VCčćŅ}*”Øjé÷B©Ūx×āoAy‰kĄHŚZźqߎ¢Ör‡ū]Pm,÷3¼śU_}¾Nįč\Ņ~88^M»š¤b»Ēq(ēģ$æŸT›KcōiōŖ¾:󳇰zš:ļß=Ž…Éøv·õuĒ?ŗŅXG÷Ę[}{šÖžąTüeßóŸŌ[‡äėŽńIoś[O†·÷ŸuüĒ'õįßł:ćŸüR[ž–Óį­żĘ§Ę]1ŹßøUĒØLƑ>‰ė-ā\ĘPŻĀĀŖWėĆĒ.xgéĶ„Ży%ŒŌĻŻ šŹB÷˜ ¾¬Xsŗ–dw“ųM«‰8ćÖ­{¾-qķŲģ¶'­¤b›©[)2Ä@ف¤¢ĮÜżMQššū£½^ų™żćܶßÕG޽?ōSÓ’¢_ģ’Ėtųh}ß*÷āg÷¼‹čx§ĒQ(—öQŌ6ÜXūƒóė{€éšŠū¾Tų™żļ"Ó'ńŠ?źŖ÷dģ’ōīŸ »åOŠŸŽī ÷ś§ńÓüĮ’ĪVośwO†‡Żņ§ÄĻļwBU:ū±TüćdķĪŸ»åO‰ŸŽņ/‘āwžŖįrNĢŚÓįį÷|©ń3ūŽD/øčQÜ1\/śé;)ƒ÷8!§ĆĆīłWŸ?¼{½ pW楷öS]ü®'Ż?ŗś·wötųx}ŃŽ½ų™żćܵ ž=w OYå`įžŽPŚ|<>čļ^|Lžńīō/³ńg)±-Xpģ TĒŹdäŹ"ziššū£½>&x÷-8ÆĒ…J6(®€ī›™äMū¦Fa3?8隊ū¾UļÄĻļyŹ|Tćŗ@”KŠkā “°Ā£ĖĀævé™i•’tNÓmļÓįį÷|©ń3ūŽE£żSøé°ģŖ§æźv}’wž]Óį”÷|©ń3ūŽEōPć ÕLīÉY‡ūs£§ĆCīłSāg÷»‚Ō*ńŲ?źž¼?ķˆ·7§ĆCīłSāg÷¼‹P8³Ēp’Ŗjßī¹žķĢŽŸ »åO‰ŸŽņ/ āßCى«?ŗ¤Š’nTtųx}ß*|Lž÷‘h©Å^;Ŗq9±M|¢ ²nēŅ'@Ū¢iĢ”€?˜:隊ū¾Tų™żļ"мx'³×Ēłn§Ožī\Ś|<>čļ^|Lžńī[’ńdöbj°’(%žźHtųx}П?¼{—ŃøÅĒӁ@q-P;GpķNH‚=6ŲĀYä>tw§ÄĻļåō^2qų¾ĢIQå üßīŸŽŸŗ;Óā'÷ŠÖ5ą’Ŗ*gī²t?Ūy§ĆĆī„ų‰żćÜæGxGŁ/÷¹żąxÆ~t'ÄOļž­˜üR’ń?łfŸŗā'÷Šłž­\ߣ7’w·ļ|īŚxū”yćĶļųÆ8ž©†Ät †é4v‰ś€‡C¤ō‡ėöéššū”{ńūÅlG‹xn'®]ś8/ö 0PŪņ{5ēĆĆīłWæ?½äZGā—N;Ž(›lGöBļp”æåÓį”÷|©ń3ūŽEóżSųéž `’ē+7ż;§ĆCīłSāg÷»‡”~’U:ū±TüįcķĶŽŸ»åO‰ŸŽņ/²q[öbšš’-Ģé’ŻĖ›O‡‡ŻėωŸŽ=Ėr^/ńš¾ĢMV唿ŻIŸŗ;Óā'÷rÖ3qų¾ĢIPĪŁéæ¶ųuļĆĆī„ų‰żā¾‡<ż‘Óŗ}€~č݇O‡‡Ż ńūǹ}‡0Õ/÷X¹ķ¼>t'ÄOļåūż[pų!„Ķī?ņ½>t'ÄMļņčOox­`Ą84=˜ƒ~ķR(ßī›Ž¼š!÷Bxó{Å}žĮ0wų Ę’ūČĆäŗ÷Į‹Ż ćĶļ9~~Į0p’Õ8’ŽF?“ŪO/tv'7¼{P0&f Ē»R‡ģ‹a<½ŃŲž<ŽńķVćĒ„F ¹ēĖƒÕź¬Kō;NU`Yńq-ČՃaō”%žƒf©‰Rn›…Js¤( Ä@=ڌøhl¤7„-ÜēBģOē^Āŗ²Æ&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ¶ÆƒvOķją?}#č‹ĘĆÅĀ{’ l;£Įė°+r“0ī_`ōo»÷u-¤}ćžÆBktϬ|‹4śZźż PƱJPĆö@DtEXĆӞ=ķZC½ƒ^‚‡Ķ¬?E3tD‚ó’a}ś¶éىUµ„ķŲ®C˜ųĀö²jšC°w+·zēŪŽe¹ÄGņäÕ¢āv«” l\Ž©^¦ˆš"h‰¢&ˆš"h‰¢&ˆ”–MEbfKbŽ ™Ŗōģ{ś”1Dz’×Ö0>"÷“=½Ś“~šŌu—܁æ1ó+=bl>JtŸØ;{=RbŸŗnƒūŗ”†Ŗū E9Ŗi¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"hŠĻxķIEæ× ‘Cŋ K¶ą’4B®±śˆÉ¢™Œ>żƒQ7?~īÆ"—µū†õłW®®¬,„ŃDM4DŃDM4DŃDM4DŃDM4DŃƒŻ³€ūPT?}3h‹ĒÅā% ŹUōŅuܽ?€„¢Ž”’Ū ]KéŪś•AėwÖ>@³6Įƒ©'I“hŸz§ź"=‘ō•TŻ{'ö}×Sd€*V¾&^hx6PčˆŖ8Żgg!}e!ńvˆ€ŠI° ļnć¬w8»”^kCzW3ŖUI¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"ˆŁDÉ#ρH@X‘UtÕP?Héü“µQ)ŗ½cķü­[>Łč *?»'Ģ­-•øŖÄ‹ul°ķķģT; æävź—ŒÖ*V•äŃDM4DŃDM4DŃDM4Eųc„QE DŅH‡UUT1SI$“(™EUPāR&’e Ģc”ŌGDꎭlÅ`t»§eni#ĘoĖ­IŚčŚ•å 8“僚;ÕNT³ūŌĮVXZ¹Sģ%JϘaŪ»)vėė·®V,ŅSq\ķŌsøŽČ48õ)¶pf¬įWdiēpóUm9ϱbSI`FÓ(l¢”|µT”pńŲZ#éź)°{½@ß׬ā+{UoH>…D¼«Ę*Š×té 6[­Å¼i{‹¶b™7Ź•»$r]}zģ[ē&·¶¢“1źĘŲ„$y½ÅԜ…„ĻŻ<”.ō­BČ’ÜDöŽZ+ 0§)NC@Å9b†œ‡(³YŠ=~艢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢+[ć|vüD<.ü¹Ć‡ū,fą?ÕŌMĻß»ņÜ„ķ§oå½z⇰?0kVBżŃDM4DŃDM4DŃDM4DŃDM4DŃ”ܒö¦pżņˆh‹Ē7Åņ ƗyPÅ ÖVZ 4ˆó}ė¹&ż€ŪŲŽ„ō’¤'yT¶*ČĄŪ˜łĻÄ<;hv„A”Ė .\ķńøP=¢#ķŠ#±Kģü»źMĪ.8Øf“4.[TÆSDM4DŃDM4DŃDM4DŃ?Ź’„ !ŗü‰R(mļž„š{‡ūÆwęÕ£÷‡ ,¶}ĖzJ”Ü W-Önobə=žĮųMž“Ū½8Š*”ŖµŠ&tŽt”Ó¦są>ćD£żÕ•¾uā&ˆš"h‰¢&ˆš"h‰¢&ˆš"ąlčĄ6jb²}1-,łŠķv(‰©/bšrEB6<‹$¢‰Örįc‘»6©(ŗĒ*dÕ©ēŠŚ#4ƌ "ÖÖ{ÉŪonŅé\h«PĀšöˆ²“-µikpä휭VlžU:DP ”WN< »½‚Eƙ$ŌIȆÄnŠC鎋}®]]<ˆIŽ Ąm<ēņĮu=/…tū(AŗhščŠ’kAĢŃÉŹNÕ{E­›³h‚ “DŚ3jŠMš4n‘@‰ ÕŖMč$@)R””ŠP¤’jv­ ŠŠFĄ6.2~1y˜w±me_A®ģ‰9XƏšznXĘncl2¤LSž)ĒUĘšĒ‡ć±[š3,F6ø°ćhü¶._T+«g#3ņ"b=Œ¼L‚'nž*Q£y×Č(Q)ŃvÅŚk6r‘Š; NQ z×9§3IĀ„ģl,0ķT„¬5 I5.¾ o3t£×Ś«'~Ć©8ś£zŠfP‡c §ĪĪī>h­g­øAD“KåĪdĖ­§MÖ&‰­m鞍NÓĻŃĻ€=ėŸk|;o4~– ‘€—“b0<棉±] åŠŪf®H#)6Ķ'ńÆŠī.Ż]Ą@éœD ©Lš©é*CĄ(†¶ęø9”ĶÅ„s÷4±Å®ö‚굟ń4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM[ˆ&æā%įyß“3-%pķŃõāi uū=T‹ø{Ʀ¢®¾żß–å/k÷ ėņÆ[° €ģé¬uš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"noMyx–h™óÆ.Ö8™“øŅ¤ü«tø&'žŅ¦»©=4ŃĻ (}_Ł–§Č³±©Uš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"‡Y5Ņ åK3tŗĶajÄtĀ”˜Ā%Üzü³²OćjѧˆzĖ`> kŹU'ÆWŖƒ±µō^•r†ÄtNįwŖžÄ?£ūŗ¶ńUęŠr*T*ÓDM4DŃDM4DŃDM|ā–iXķ÷ĀÄõ[UŽ9ĘuCH’ĢŹĶž@–mī*ļ¦Uo' œQʲ‡ŅųŽģ¾vŚ“ś=%t¾ ÓŪ³õā<åo3FŽß2:֖ņš"h‰¢+‘2Ćōä””cv†š¹ŗP¬T~ŻDŚsō¢"S•DÖ~Į‰eTjČLQPL}“ū{F–ų÷',#ržcŚ{Ō5ī¤ń'ĮŲ ÷g ŠQ¼½cy87~8+­I€5V±b5Iėd=i5Y(ŗÄu.åC9|wnvvńƇ* ޲›ęźPŲ”‹<ž,„ā¹NŹņnŻ B҇·lXgWI©ÄŌļ*08„OęÉŖ£Į­3.EI䊻cHkÜ+¦2DCĆdŪµœk Ź`ˆ—b•ar%n<=xf€Ū¼Õģņnü¹—5āż5¶·‚ź!H„č;ūńėW#[ӓDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃ»ń¤=戟…Ćöę`²ÄRAž®¢n~żß–å/k÷ ėņ•ėq« !4DŃDM4DŃDM4DŃDM4DŃDM4Dѐo‰ŠģĻ\ĖKų² ż…öą_õu'¦ūOčU«ū é>E’ŽVņ%ĻØPwF•6צ­hVB9ŌŗŠ©7¢¤äÅč¹EŒÕ};³Óķ.żū÷tŲs®'šJŌŃFZŪ‹‡–J ņ¬{›Źż·qģĀ5Pą÷ŻfŒ?»Ū]’±¬/Ä]¹ƒµgžß|ö.)Ē•¬’cŹā ~‰wč'¬īM·å2bŌs^~"’twÆ {ŻŲ揦^9vm‹ń“cG6ĒAžĒź­’·Æ?“Żoz÷šČw¹ŻŹæÅÖē|˜ž ćKL·3ź‚GgŽqĪKø|¢†€ÆÜÄL¹aūLįTŠńÖ±Å?1xO¬ž?Œõ]3I“„C®ī"‚£ō[#ĆŸŠĄā²­“ o’Ń“JļŃnnŚ :Ō÷‹ćŸž9ęˆH7į=^¼ŻŃ=D›ŁŸQ €n ¬Īo07z‚ßB(B˜C®øēūā’lös:Ŗr½¦„Ćm{3zC™nZG8$)ČxZ™¹›m%9ŽĮå*ßäģ=ēcÅø™į£•āš&u\ČcŖu{(‚)¦™TPęgL¾Ś¤Œ €õķnoˆ=Ś”į’÷“žÜų–å¶zéQ]<Ń­¹YŌ“Aė]E1ēw!Vēąm^ß-f#ōK_ÜŅOrĘl÷®`ÖåŸ×,Į\«X£d §±a`§£VŠoā& Żū5CŲŖE} g­G©Z2’Mž ‹ EY,Nd‘¼r¶F1Ć •ķ2ŽY#^׍ ŌŌqTŖžDy\s ‚ļ^Hų ŠŖ”ł½XõMż_ųŪŽQŲŸ‡ŚņŅø×^@9bėŁ“4’ÖTŹjī Õökόø÷»‚«ąm}ŽóéYrą~Sæę'!oÉ%lÓäæŲaQ|«øįF2>6VĶJ„K&-ŒYā†ī ÄM°ŽĄIZHł"Ķ!©ŖŠ½Š8¦É£rƒåSKYK 4DŃ%Č%OöĮw:.S\ĀŚŗ!»ŒÕr×aJ›U6ż*»`ūu`}ćŗ¼g7ī×å+ˆÕj•ĆN“l% Õn>ŗ~ń(*Pü¦OqüįŖ\*L4<ŹŽjҾš"h‰¢&ˆš"h‰¢&ˆš"üS@ į]½$ eÕÜ@D¢Ŗ‚""(ūōMø._ŽTCć§nAķŠk¤ŒG÷™I ƒ£Ÿū±<Žß˜5̵ —ŅøūŌģĮvż“cł`öćēW§XjU4DŃh̹eōk‡Tjk‚…C1t钡¤É¤„DĄÖ ±Ž“ć'ŚgNĪ ViØR”cl•£\ó}Ž'”Ļ7 ߊ 5]IńøŁŚŸć` ÕNĘ·Ÿ”żĪ®)Ę,1Ō)=QłėD“t=,”Œ”€ę0ø4[Ē£šøPŽĮī]]Õ8‰„6ĒŗŗuĆšĀ!°yĻ?“bĶÓ“öXŎ7±ļ ę“āUÕÖ*’Qߑķ’c^¢ßślq“ŖR+ÆģWnĒYŃöŠ'ci"ĘfķŹ>Š Lhs˜oŚ>‹°ü»Ö³Å–ĀćHséėFązŽŹčÅÅ7A)„£łĄv솺ä+ó^"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ­ĒqŪńp°ś±ŃĆ÷ń<€«Ø›ŸæwW‘KŚżĆzü«ÖļVBh‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢/!O$ģä?5 ģģ–A>ßL‡oėūæŁŌ–›ķ? (}_ŲgIņ) åIĒfĘm}īrrŖžćZ¤¾’ž±«ś‡Ż7ėy–.™÷®ś¾p°aؕ4«¬eŒrgæU±f&¦X2EŗÉ„V§Ö©!10ż^¦’/jMš5HW.W:Mš S*²„L¦0Bqh!”Üń7ŽAaĆöq&žgGG)Ś\ćƒŠ^÷Ö5Ī +ÖöóŻLŪ{f9ó¼Š4bOęå;ĬćĄpū…m Źö óóœh¶Aņ¼4ƶo¦ą"ńTʳd¹–ڇd¬›3˜æ3€ś}‚ŃdĢGų³Pł·ó‡ē„³Æ¾WČ8ä©qh×ļįĻ©ź-„é6'Ųc…rLļ[ŠĒģß.~PqGq#8Gƒt«®!ć7ē[[PAlƇ‹yrāŲ ŒƕķfĊŻo19Ū‘ąŅسĶPžå†§qšm‹kģ¼…&q\±œĢ]²dµ:(ŖśBv뛲Šām·UćĒ®l¢ŸiC©Ē·`öŽŗu§źVĮ¶z5Ž‘k£Y¾›jŃĢÖ“DIčĒ”}’ķ'Bӟy®Yj³Ū[±ŅKs{¬ŻFÖ±ø¹ņ¾9-įc6ø†€6•ląó-'Φēņ«‘x¢}ŗĘQ Œe–ņj±m×õ a[®źB"I 9„D½ŠŪŽā;޶›¾ā.,±,āĪįķVĮ†;Ė0ņ)ø€ŁzŚBųē>]’ń•gØ»EŠų§ˆ4V¤ 2āžžÕŽ­13ŪÜÅ+Z”ē>žõ7ĶyŒŁQg_ēĪ)ǾI°P$f)fZ¤,63ęĘ'h+-V¼V‘‹BŲę(¢ ©šUĻfĖJ"ĒYņŠĖƒµwŽüŌõ—c1V4‰åDzhņwƒ7RÅ3ÉmŲ}?"į;;…@Ɯ‡§¢Š+Ōl,VéH (3v”ˆ"U[¼|¦łósÄüG/ŹÆšzoößĪ«H³ŗĢ»=¦£ųĶ*ą’'…Ąŗ,Ī‘‚“sĆ^Yš&©¢ X£§æĒҜiš”|n­ %n®Hķ¢øČ×Ńźv ńžĻåųʛ÷ś†H»9Ūų¾‰a˜m’č{žī¦l~ćõЁŌOżĻź:ȳ h‰¢,K²xĻ2ē»L„+ū”4‹Å1”`¾=£ š‡ķģ0®Ń@ķ‡`÷†±Ū÷éó_ąĘ!ņ•Čjā„=½7x°Cģ[IVBÅź©$aõPp¤qßķLw(žmZp”WŚj*øķRŖM4DŃDM4DŃDTŻĶəÓ.OQ9ŚTmJ@öœĶąŸŖR·©„›kĒū Ŗ™‹ĄēUpńt™āš2•F9ܚ¬ńŹ1le¾U:ŒO¤Ķ±SMQõW>Åķ.ū@rł›žķģ$d8ƒÜķ^cÓ"4ø¶š §Õr %MCGŹ;‡’ƹz‡Ŗ¬4¹R,‹w˜¾“Dź'Ü`/puķ0n;€Z‘”,8 ćaYPČéblŽk˜ā=““t®cT+Ŗ×eȜƒ5V#q"HŁsČ'õüźqĪD™»„ÖnÕź¤1PPV9 mŒC C”€}¹V·d¹®EYL0®*;SŽö[|–.Ė.lq§«C€**Ą`ĢŪK°#dƒmS{,Ó×®ŽI4|RŖą‚šŽJ„‚IlčHsT7ÄQ0ˆżu)%õŒńųr†ĄSȵŲ4ZÖa\"V`¹Ng§9Uł€]ŖrD €äwmÓ}aܲդ|3‹†ś’ą„ŗ„ßXE)C·–¢„YlīKł0Eā.üö°õäż£Wk«V[=n&śÖH«4½Eßi}Rn'fāSwė:Ą@żF/‡×£uV°ėČōk1’r†åØśCmU~°€¬Ø‡°UPCó „uŠ×!ZzńDM4DŃDM4DŃDM4DŃDM4DŃDM4DŃDM4Em|noā"įXż¹ž€ģ±kĀ’«Ø«Ÿ¾=^E/k÷ ėņÆ[ķc¬„ŃDM4DŃDM4DŃDM4DŃDM4DŃD^C~-‹éņ[›ÉtåŪł*Ü_õu%¦ūoč Wö#é>E||«’ż®Äæüeæ÷Ös«ś‡Ż·ėy–.™÷®ś¾u†ŗ ē”ļiŽkrw ķöĆT§Õ”‘äēgę‘«ö¤čRwØ~å8•$)ŌPÅL†0jšī¹£šĪ‹wÄ|Aq¦‡cnłēšCFEc3œćĢ0UĪq h. ųa–āfŪĄŅłžąŅNĄ»(Ņ+ʕV{†\J±ĆKs’×ѧ;9­’%Ą1’č¦čÜjć܊ qglA#vÉH'Ų²*‡®ÆkI8’ϧ\Ožįõ(>p|Õ·š/’Ö³9Ü5ĆŅĆŖ>3—ń}QƒŚ€œbˆÕ®oØŚĒ™ÓżžŚæŪW|óā÷p‡ æąō‹LŽÖurÜ̲‰õ"ŚŲ ofˆ¢Ø ”šRÖ7įP„ר±F‰®³:$pįWŅ’.×QōŌü«ƒ™W“6 ww²ņĻW9”UeLavŲ:krÖ5½G^»ų½EłœÖ†±±ÄĮƒcŠ1źĘʊ֍ƒÆč“å7É’—æ$8F. łqa–”Ś:YÆsw5(당ČĻ<ĪŪWz¬$LchŖuŗjĘß,2tŒõ¬qĵ¾‘d‘’nŲęIŲRŖą\”ƒ²éD”äA"å©¶‚Iü¦į‹k 'ū’łųĖ€K ¶²!€ĖČd”sŽÜ¹FĀkų?’Éū€×xĒę?žÅšĢæŪ:3˜ĖØ¢q ŗŌœä2†żć,ƙ,uZ. ĆĄ262Ų£a§ŁėŪĒŲ”¤bä ƒ¤H¶]³•rܑ5ŠvaūJ`„«éŚĘ™Ŗ@ė:hå·c‹K˜ąę‚Ż¢¢¢¢£“~pń·Ė˜/õˆ4N8ŅÆ“ķjźęŽ+˜¤ŠWĒ(&7啭qk²ŗ‡qkŚź9®hæœs±Ś1īY„ÆH§!ĪĢį¼4¬[Ԝ71Įé{c³‚¦”fķdޑĠ`HĒ)µ üĘÓ“¾!į)õ s“[4ÉŚA¦_mµ9”ĮĀ“Ģ8…ö_ū㟘_$’ܖ“Į:ć/lt½~x¬o-'d‘ Å-e1HęÉ Ļ†H¤->V“˜ä+,»,Xų—}²åzmQ¦FÄŁZŸ.øĖ&’n)<„ÄÆU¬ņßFT Į¦K­Ę¹YĢ\HUp2g!Īłc_įĶ?ꖇmĀzÕÓōž(ÓęčZĆ UóHtCij¬ęxk&ˆšG4[÷Wūä’gÖZޟó×å5‹[Åvń:mgM…€GŖ[4-Ü14Pj34’e÷q‡ Ķž.><…q6§Ęl£Sµį÷W®$rRœ†hāĘApc®įõY`$„ ÄąŪ˜—Œ]0ØÅÉ$ ‚ż€‚Ŗ(”Ź_”>BüŅÕ>cšÕ֗Ęp6ĒęÆŻ?ZµŻ0z—1’Zö1ćBįź×;ZKZ ü$Ötč¬fdÖnϦ\3ąŪjÓōOé7aßJBh²Oひų­\9ż.—ēiĮŒŃZ’Ę46¾Ž±žœt•¤j·G )٬µ„š"hŠŁŻ œÅ—Ź%ƒ)ŹŪ"īw(UÄĮłĪmXxžŸ0Y ŹWę«^&ˆø‰˜š|ŌDż!2ˆńŗ|iä8Oī€5ć…G:©¦‡™[­YWÓDZk,“t•]uSA:Ė,±ŹšH¤™Dź*Ŗ‡‘4Ó!DLaÜtEgQĪōI‹Ōś6ģµl@é$zī!¤Ś2D€®įC¢Ł±«Ę?`£§ ¦dŹ™V÷†Ā«.¹…¦„Ōó+ķ¶™Ā Ps©”Ią‡— µ…‡ųøä œ‹œÉfĒø YTŽ˜(ES¬äi¦3¦MB˜¢Sś^˜€Åøl6 ė~‹Iīō«āÅßIĄwśDćĘ·šˆć$ŒŸ‹,‚+¾rHųÓBē¬:Ō_+ÜTĶ.įŒ+ø½ā dS.āa›łń£{{’2÷ąNēw~u$*^<į^Ōl‹ĢCļ “ÆLŹČd¼ņźä1‰Å ³,Wb;§D ˆޟp{D: &õū€U‹&oqRžš¹y#•GÕ·sӋTē[~R•…/W6}ęžpŸ7f«9ģL?D}-ĶļÕ³w77b¬ZB9{UjŪšžņńoÖHłaƶPįŽtc8q[Qŗ*w;¬˜šĒA=ö(Ÿā½uOÄĻļwB«į`÷{Ļ„T‰žœŽł“†“ŽY§—+ĘĖ5tŒØĶŠQH ~Uļؒ‚]Ća ÄĒk»‚ō[@1 ļ>•XWæ ­ž¹ ^ŽņĖ•ŚFĄÅ0†ŽhیėŠiÕ&lš¢gyŪ£»dJ@2Šā¹„GQ®°“{‹œĄ\MNßJšYŌā`Ž9\гrę‹ųSr/ūē–¬Č`÷‚|gĘIī߃ķŖ²žXļōŖæÕœīĘśąæ….ą$8©å‡;™pŌ>?ć$‘!żĀ³½'õĖł;‹łõļįöĖž”üoUžs»”[)ļĀŁĢ触5 Źķzr7°åMžEā mųŠdĪgÕ솺Ŗ(^ŃŲ»G“GmØ:e‘śė>•uœA«7žmG;[čŖ·Æ g•iØxŽppźmdĢ Ż ÜI’”ŗ ~‰Ż/‹69ĆŚTČppŽ­"Ļō»UįÄŚ˜ŚXWó«]9ų|üąC®D`²?»ƒs‰Æ;Č(3¦ = ()KL æäßT~ kŹžŃčWź#čĀz’„Žų(óÆŻWŖ×¼_; $=K,å:좂NīäRun©”*,¢uS)DqśRķßč¹ąõ0WÅw śńÄG6až"­EėĮœl™\ސĄ¼WƳ„ŗÖn‡jæ"x«Ē5W+?Ž`šķ"WD­ "d–8§Ü}Ń ·ßYZ{,ē‡8 …aźšäŚ©“sĘ A$įUņóĮoœ† ±‹Ēn1NzļDņB1”ß·Ńhi– Ȓæ•a!?.§~5ŽčZÆĄ·Ž*Ų[|Ił½”Ä;°KųFʔ|Ėrc ŁģŖµ(īŗ‘Ō&%0é$€LVȐVXv)Cøuč½åoę^|#»æ:€ūf@Ā~ r7Œü©ć€$uy“äĢZ¢€nąķ%‰~ÕŹ(lnć€ģQ¶Ū{Ķ»ˆķØü¹•—YĢ6Pž\듨_©Wö'‘„Ś`ģĶōĮĮ¢$»U™•(™4Ÿµ!žj=c”7.DĻ·]µ}ÆcÅZAXīcŲhšB«µR„4DŃDM4DŃDM4DŃDM4DŃDM4DŃDMZŁų…øF "ē¼DoÓ¤¦AĢ ?½Ø«Ÿ¾=^E/k÷ ėņ•ė¢ĄüĮ¬uætDŃDM4DŃDM4DŃDM4DŃDM4DŃ‘Œbś|ØēB_IJ?/ū „l/śŗ“Ó}§ō«ū é>Exü¬Øq;üGČŖ~D«(’ePÕŻGīŪõ¼ĖKÄyżåUO”VąæåłņX¦o9gŹ9Ū'<{ÄĖ6"ßrc’AV9«“Fhą†(¶¬µX0\J%ļHIø¦šŚų+ē…ģ:~iÅņ5ņ½Ÿ*ųfu~){ >!ä‡iś8pśS;,²¶µ£«ķDtłYĄüCĘA§pß Ććń–½xŪ+åayž-ĖĶ0ŠĆä‘ßF68ģ+‡„ŌšŅą“‰EćÉyŽĢŲģr‹(īj×i—pwÓöy·«˜īŹLČŖuTQCŻ@7é«ޱ6·ØÉČ`k0°ĒŒ±CF dl õżD|‘ł9Āæ!ž[Ų|øį6ę·¶i}ĶĮ–öņ@ ÅäĒi|Æ«MDQ68›ź°*°L "=؈žMD©ŲŗÓZēø5 —G<„˜ŚWĶ%:Ņ6Y” 3°Lœśi;ŸpŅFśl’Åc,€=UR>Ūk£š·ĖØĶ ®ą|–ø1JęŌ¶ ęHƘŒ3F|)šąråpŖųgżĆŗM3ō½V߇5‹k$µŃ™ØišK•“j2Csaq‰)·¼ćō¹”tm”K „¹\1łŽ'˜;ĶÕū©Ņ 2Vތ“«·†—ICČ|é—]Ct*ȜcB”»@}ĖšEʝ„1Ęafc­ŚFL“”°P ¤šm_Šæ"øĖFŌ’Ż®‹Ē1n!J“‰ć¼¼šcü69×>9’Gl¹p‘ī>«ZŅēQ­$K<ēbØĢä¬{0Łp³FÓ#¤„,O+…Jm»'zÖB¾Įć–« Nē«¶TA8Ši«ŽnŅŽśä¼ §jö|5ØŁŹß…¹½‘Œ…³V"ęås%sZį›Õm 1"‚§śOžļxćåŸ|śąŽ'°˜kś ŲŻ]źS遗ńA(ž6Žiby†³ÉĮģ2Rų²4ęT„ß#Ro9ūŁ£¤Bčœ™då”H»9gÆĢEŹ$2©¤ÕEÓDź›õE1€E2‰µ+”šę·”|æÕ4ˈïęń2FĒ5Ս­ØĘ„øąŃė38łµóĻå7Ķ’÷Ÿņ’ōKŁ!ąĶ/ąEŻõĢĀ$“ÜČ KŚČ$P>W’ ¤Čšć n‘dqߎæøCżP×Ļ{Eķ…HuwöAÅh“„4p÷—|6p‡ĪOńś9ļī €€(ī. 1Ś0“ÄŠ½Ą*–!ģ,,“L{ ą‚§oMõ(5³Ā_5øSęė’Ė]‘¼+Ænkå{Lŗō›¼FČÓjēœKZāæ›_÷Ćņ:×åĪ=WEŃ!šøKXˆėz[Zd"G¹š…›9Ą{£`Į±K„ÅńęC‰Ųōā ņ×§ øé)p–ģ8¼ Ś:ż³[€vŌłWēeł­Óŗ)­¬„†š"hŠXĪ‚™w0ؒÅUO¼°,B€GU­ ¤Š0‡éb”ćæ³æmYĪźr Y  +Č|„5Rń4DѾžcņD˲»•&ŽĀ©æėSüŸ÷äÉ«N*ó G:µ—¼‰UĒQ͟ŁŖU䯧ŁY;š]‰¢Ö¹ŠśZIŅė&B‘2ģS½ę(ś“ł«Ź½oå`YÜń™ų{34]×9 ä®ك8Ō“ÄåØ|/IÓŚÖTĖ-[™ćgł,ÉT%)7Ę1kĶĢ„²Å( ¦gŚU]FĶpép3“Ņ„!·lB§ņśh 7,|?švĘæ±®_į÷ļĖ„=qĒz;ŗm.ś­ęy’×aŌĒp-µŁ,3äšf ׳gK=;‚(s›Xė!eCDMq…š‡<ĀÕŅKFĄŚ1“ӈ"æji„!ŽŗvŜ²Ń€Ø½J1ŪÖ ¢’ę $¢ØØB˜LCrz"h‰¢/M1Å2ż¢Rļūūh‹ļDM4DŃDMY+ß$š1 hkžaǵT³[ŠĄø¹Äµž-—œŃ8“³X|a_xEŌjöė$ś ŪdXĮs»@Č}mˆ$W·DVē|MŸ#ļ’˜ŽęĀęĻeĢ—‚/ĒdÖIšµŒ³‡ģĪź™"ŚU“ ž½:ĢɊ¤!Ū9DÉ®ŻEPU5 Eu4jż²ģŸ6nń›¤Žƒ¦Ž‘MĆg(*Q"ˆ®‚Å:K$”D@Å0{tEןÉ?įųįW0Ü-rćāt®óał ?#āˆhˆ éXüŖ/åģA xŲk½]ӕEä«v‰Ź6:Ä2‹9'ōEjkœĆ™¦…Rę5ķŹįPŗPŽ©yĖyĪćľ\ŃŠĘ|‰” ]Ķ*Ó:ŸIÉx¦qĮIś¼²)ĀPż{e¢j‘%‘pƒy8'Œ®ĮžUqnb9›Œ~EŗÖBĘM4DŃDM4DŃDM4DŃDM4DŃDM4DѰńĪŃ'æˆ{„Ø®'5cw…Ģ7­Ž>n"!—nP0{ĖønŻE]}łźņ){_øo_•zį‡NšĒY ¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ¼Š|h –\šǹ1“Ė °‡q?jÖĪŃŲvÜŗ“Ó}·ō*‡Õż†tŸ2ŽA±½›4Z8‘†)Ék>WĢ®(0!{„’Ö³Õą:1}鱜‘–8ū4Ģ>ĶCqĻéüĀ×ü[«4½.Źāź_©NŽ—eŹ9Č^hVļ»»±}ģŽcGK;•ŪęuƊ÷–WJe]ū¾>ųōǾį“ÕY$¢Ų 1¢¹ššr‡Ą  ŖīĖ` Ģ:üöłQ„k‘|­²Õ5䗏xūP›ˆ5 ^ļ‰{†Ÿmõb·¤fĘē­_“_üup h7¼WžįxŅęÖˇ8bĢi–³Ü9¬e³K.„xća†ŌHĶāKIq¢ƒNWcš³–‹mŠkÆńk¬ä!ę=sæV5čR'' ‚d1Ūŗ+~ńP ÜnĶ¢ü§ā=V?ī–>3hšs2HčÜ*É =…ōĖZlÆŌ_5’ł!łņņ÷ą8oÄāYŲtٳZČŃĶ•ļˆė—ZĻG†ŻŁ1¬sķ®ŪoœŹÖ5ą‡®«æYcT‡dVŃÅT’Œ—hU›9zŁItŽC¾I£u¶Ž•gˆ"”ˆ¢…1Ķź b€ė­čß*xL¹“f•Ą±ĮŽ£ƒ\#-‘„Äzń¹ē0Źj _šŸ4?łłŃĒśųcLXÅ+.ą’āų„ž'^6{)Ū o„µÜĢHęI#\÷˜ö4«sP£]²„¶$—”X“RlŪÆc‘8¾d•o94Żļ¤ŖåTķ§klŸi;ŠoaŒģz¾¹¢p®‘9¶lM0Dā!`˘°Åm@¦až6ćSB6€iþYü„ł±žā>eé,×ęŌeVæ†95;—ƒnćæ¾Ž|ސ<Ā’„¼ž‘åffÉLƑ¹¤*\IČåˆČŗ„DI7u ėźP’|¬²j|ŖLcÅ1‰+x“CŅY`TC›Ö³\ķß6ųlŻųeģÖĪk“###1sėāUē·.e.nÕöķæ’æ=Ć_isĀzn½ÅĢæń7W¼k¼&ŪŪ›1£=I§#Żć¶)@,“ ŪŁāŪŻkėD$bI0vķ=—ˆž–hņ5ߏ.×x„A&oD;ØP1ŹÖżi¢qn™,š%Ą™Ą±ĶiĀHŸšöęž «›“Ōb@4_q'üŲ’m|§Ųü×Ń%Ó-ÜĖ«y¦m$²Ō-o!šÖoHkhąČ'pÉl€µÆ•ųŖßø\›eVVĀĢ›ZA"`HY„“ŠD!ŲÄ -½7Ŗ‘Gjõ8£ŠG¹P0B|ÉćšĘš-4ē’ė79²c›Ća'4‡5vHŪ³64£hŗĻū’lw?øņN#ć{P~Vč"rC<yrÖµÖöqų^9ŲŃ%äŲ¼ĮF—x—ć+_`{ ~@ |¢æ£œ¹üc›cRē 'eŸwķłVs—„Ōžaż ‘4+&9›Žx(ŌīŽ¶S“zw¦Ūźķ'Õ>Mq……µ~:×KU¶#kn“›Ønć{y•Æä$/Ģ_žOøRŽó€xKŽ2s§q ģ^źbmõ;Y*ÓĶćZĘG!5\÷ cBq®‡ Į_˜a)ć9Lc²Mµ‹7Aæ_é [’»ÆŅĶā[½&Śīq–y dŽŽ{Cœ:‰!;ś‹Cod`ÄS³*u$°“DMcāL€ž`Ķ’ĀnĄ ‡ńE*…%ø~~ąOČ#« ūĒōł‚Ļ’Ī9\¶«T&ˆš"će›¢åƒ€Xé¤T“;‚¬”ŠB"dJ'”„ Dū@@Ā"ß^P½i”ŖĪ7įŒń÷Pͳ÷Ļ+ł²ŖĪĢ«Ktę!į m‚;ę#*•ZK„cļYƣ¬Ń[-žŠ£ˆØĒåZ<Œ_uS §“ĐŸ£¹l0GįFŅ8•Ż“VU冯$00žhxsĻĢ+PD¹ÆĪG„mjBF;“wFĪzb–«¼Ī“Ōl¬J³tg1qo’qč;rŗ„ZüJļ‰Ź¾sy»-ŅBkcž>pž~§Tć½2—Œ2ī Éī9N|UG»HÜUĢS’Ptõ«ó’Ī×ęćĘbR\ŽH£tEȦoy­’Rź·2ńfXÆa(zE„Āģ†7ä_‰#ÄYŠ…Č·ŖĀ•W…ŽDČĀH˜Ļ2MVU+{āóž~H¹ėŸ0¾fak“šāÅ•ĀėÉ:ėŽ<×ńư-rb•*ū`œœķQ1YӔŒ†µĪBżģ²“Pøł& \ 4ĢuQH¦ß›’$¹;Ŷ&ćo!hõ”ļõĖVyµaėŽ540æqg,üjϬ5Ya,Ķ5§+k¾ĢōhB.é’k©ōć:EaķLH°-ɾ{łšĮ–,‘Ćy|įŒäł …ų“3å›3ņ Q”*˜÷ćÜU‘­7ī 0©“„sdƒK0W£*16£×eąī,Ų §i]Łs{—¾M3F#Ąœ̵Ž#ćč>ńƙüäʘ²4\[ĖņvĶ£ ` UAŹ XÖQc- łüÜÄ»1uņDō#g ˜"…ׯ5¾Pk“Ć\a…øy`ęžgńķżf=½×p"™‡WšGŽČ6,|ÖŚę^¶„n+Ź3§²W›O?vŽzŗé&f9R+7$Z‹ó#ə\üĖĒ~ä3»'p“5™Æ"o<0ąÅ®į–3&zV’keLĄ|{Äü„>WĆųf‘J§×Vł’oRҰGAś/b+sŖ£B.ÉX1aŹpˆ×3ĪāŚæ7ßįT·ĪiÅ»U³kźśŖ3^r­W³Th‰‚–]æÕ[ÅJE·~¢K‹%["¢=„]_¼iZ/¼Rāo9Zāל’\įó{ĮÕL‘ė8ę·xĖaäw6ZäNW`¢©EyS'ĢLQd'Lwņä:(N|ƒ"·»œdKŽß&üóYē=˦-ĮĢ9mo ą~?¢Uř‹C=ŲpFc¶gL’ź™%—f|€Ź”`”‚F1Ā1pN˜ĒJ©\ø2DPŸ¼–䓏$~O|xš¾ƬRņæ/¹1ɜ(ŗ½Ž°ŻĪ³„+²XŪbŧįm·Ģ±t¹c‰ØWk‰ŪĀUSTnTČ«Žcy¢Ėy§˜ŁS‡^:/¹4“üw…k %ƒ8ŹŪ2ęīEņBUŀO‹£¤ó}DŲGŽÜ~ÄčV“5Ęłe)Ļ"œ’¤‚™Ü芷 ń×ȶOóEĘŪ§'9‡_dzxæÅŻFĖyŖqJ…[†Ž”²¹Ķöˈ'¤2ÄnTe?Lɹb¤éģœŌcX'ßD‰ŒbÉ';‡bE’Ž“¹Ā¹¼łŒ#…§-8OfĶVĮÕ%dģuXĘ)HåŒO"˜‰M+oØÅ*»VĆÜa•f ²nJšāĒ7hT¹”ķ-vĀŗ8Qķ±·Ź…nćmćģpģ„P'p˜Č ¤JeŚ(a)7Y“ŽōŠ>2¦˜ąö‡ „(G“±Å§h*Ŗ×Ŗ”ŃDM4DŃDM4DŃDM4DŃDM4DŃDVĖĘŁĢˆ“…½Ā"šh….żvÅĻ>ĄÜډ¹ū÷uy½ÆÜ7ņ޽puad&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"ņ/ń°Ėrėžķżž…Āmæą²ÕøŸü®¤ōļmżu«Ēē>@³#ŠU/>MYÕnbb˜>Vg†-V(T‘ÅŲ*Vj)~ĮīŲĶž$eÓŗ¾ą;€kåļ÷æ.¤ļöė«iUEĪ«w§iäƒB#½æ·…ųņrg;ņł±ž>Ł„dL{éŹXĒ;ó®ØÓ¹BÕd“d[™e²w–%,²ö’&ØØ/ѷسɳXźĶ鹐qÜ —“Ęźģaļpę“gca„ˆc0il‰ `ĆBMķ` ­@ź Bć¾.į›mVӇÆī,ķu»Ł^¶7a=œ²²y-ŻPiäcKņås…ZNW8 `gML­H6*ö’£VV’~³č^5ümrĢŻ±Ömk");lÅŃÄ}o$ĢĘAnā()•bœ¢`ƒÖ[-Ē…mnÖM–V>hIĻ€’×9»źĒQĶ”mKKjkC×¾UͧčĆR×µ‰nōĻĀāŪLÕ#‰ŅEm«µš(%5Č"ŗ„ŗŽbęJblģ˜5…¢F乫lW‰™;°¾°Öą„‡‚¬^'"ęĄJūF$E›.ׯ=MŸrØw¢'8æ\źśłšixƋffµĶĆē™ņę{K$>3žKźµ­.£]GPxLkI!~÷éŚwūu’mŗe×k惣[i:U®œ"··RŁjCM†Ż¬‚ K4³¶ŅŪēĻyŌ.坭ŽJ*Ų"į_0ZĻfŸqv³Cžžb•Yū¶I„8u•ÆžådU“é$¬Cw% ¬Õ¢ * ¦‘ROńΟu{ŒŅķmŪ”ŚĢöÄć;ābKšŠ‡5†ūå|ŽsKœņņW’h¼mÜ'¬YŻüĮā fo›A¦ZOØŪ³HøšŻ­Ō÷é§\ÕÅÓuIńŗk Ö¶pE+a†Ż–ĶKy˜›$sˆių–SQnƒµÄ|‹TݶSoa»)»'šNQ”z€†¹ •żŽ›rŪŪ ŸÓ6=Ž-pėG(5x_¦ÜSĮ¼7Ēzü-ĘZm®«Ć×’Ū\ÄŁ¢w!Źąr¼}°µķ8µĄ®>­OÆQ!R¬Å% ‚Ī•¹N±€WrQuÖrķEXęčĒ8ģB””Š ÉÕ5}K^½7ś”®žńĶ“€4üŅIÄØ/—ß-8äē GĮ¼ a‘ĀšĶ$¢,Ļ5’gf{ß,ļts°hsŽāÖ1¾«Zé劽2]»¾vj1®Ūū7Ż&!æ»ķÕiZ­Ļōö·2}X¤;z²µ_˜Ÿ/40N·Ä+_P“ˆŠmĮóQ¼R”F¬óž jPÕil{s†y‘k7ś„ά¤3–³A%RdŽŖŖ·n œ½„9€U7@/vŻ3€x RŌÆī­ø†Źhų~āĀ{y„€Ēā2vä,hp5©kEł”’Čæū‚ł3Äæ"›Ą¼ÄzV±ĘWŻÄQXĶ؊;W=ņĖ4°¹ŃĀ2»+žG:mˆČ7 sń?ā”Ždmf1ÕPT2†­ˆL§põ9„Gopﯯ¬č-š ĪæÆæŖuy¼KŻd¬DŃDXóv&W0gs˜Ū•¾BlŲ€Üõ(tuĢ^ŻĒ“L?åīß߬vżćś|Įg’Ɍ~‰ņ•Ķźā”4DŃšäSõc0.czƒ….–5¹ ÉŖ’ĖĄ½n‘’P‚M_QP˜¦Õ©Ķ!qżÆ@+;é*ōˆńcIĒ~58PІoŚ/ˆrX¬¦ś1Y:ü¹( ’X[ø"„Ś"ÅĘ-ńLĒy[^,œÄēoĘüb¼HäŽ9ńó7eڵŪbū›ŹĶŖ›.­“öw˜ņÓ*·‰xŲ"ŲģņƒōČĖ”¢(“™§ŽS·åKĪ9—9CQņd†IµRČY²± Š85=›ļē½åK§āģ•Ä(ŲŹĻz»É+0Œ³”ś-ؘ żR)GČ_Õ^HŗŹ20Üóņ ˆ©œjĢ3­gšš˜ó+Āŗ«ATä>I…ĻŻ•”'h©A¢Ķ߯g11ļ,©l¢jö×Į^/0ēså3qė(g,QQ§ńĒńškVJ”¶ɔ¼¢ń ĪC5ŗ‘cɏķ5y:Ląę>ÅzK8*‚.ŃIĢÆXļ—–źF^Ģ\‡āß#1ÅNƏ*‰āęK_dd±Ę^rŁ¬­_GŲ)·zTĬg‰“–‹vhéHń™Ūø/x‘X*/„.P°Æšür¹^ZKóOłw™,·Vö ͜yG‰^J.Āóž.RŽ¾ūµ°±›tÉė$d’mNPj-Ō/Øb)›ø/ł˜ ˜²³‰ņüWϜ:„ «4(Vœ7É ZžEm=H”<’„AI£'-dš¢‹WīĮTVT[*؊ Š|(Õćģų„öq緐ĪMc| +X“ĆŲ"ēXŠv†R…3 5ŽŻ^ °Õ'Ne‰Š:õ¦%dārEĀ&+ź ”V1@Šur·‰üzēTf6Ē™qē×ƾJąžMÅCÖę`ĘZ%ā—ŹŁjö¶nYK9cm®Ģ;fõæcGO"dōL h‹“ø?nj½oäźėV“yfåOüĘõµŠYŗ6cä’œyœ+ &—ķB`>yŖI® .@PO脌EŪ‚Üj,¬`ŽQsw‰×)ÅņŁ&z%͊fõn»f\£rĖyķum…‚ćo»^e¤ŽÆ%$„J¢m¤z^žé7LLq1 Āžp.;u…&«<‰ä-ėąž^]9”ǜ-dÄņ˜ēM䦹„Å£ÓĮbčūģeb°g)EĮä£ēę]“@EŚEł‘rEJZü c{3Ü»@gĢ>gŃųmœ²M›+änc܁I©įł %źŲīõ+wFXųŁ®™‹Æ¶ł7„kŠÖ6.gŖ¢ESk頙[`šSćöÕsäMœ£Ūk™×&BåxĢė]·¼¤ņYąč5Šd0vx¦!”k0®¬żMÓi 9@“‘|w¦r‹@„\UūÅ=_6Z¬9£łęĘC>Pńķg¶Ž0å\FÅĒ$¦1u-†/Æf7·OŁe…­;.:§Ā§%1SN)«ƒ5DÉ5n¢&‘\ģUāK `,›Ē¬›ó‡)±‹Ü\¹ŌmQ!•Śdd9#]½ä¢åūv}›Ģ5Œ‡j›ˆ«§ź/ ž ȟȤ¢)‚@‘S]5lł«–OEÓ7ˆ,ÕŪW •T¶p™’]Ņ8Š¢²G˜¢(ˆˆ¼šRĘią ×˾25HØÄq»—9ļÖS÷Ō"ÆÅ;@'e%ģ ƦūõŲ$ķX©Č:мm&Æ(üŹ©ÖRÅM4DŃDM4DŃDM4DŃDM4DŃDM4Ek¼pü?ˆ‡…#öęĢ{žŪ8/śŗ‰ŗū÷~[”½ÆÜ7ÆŹ½quad&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"ņ>ńŚs;ČH{6æŁƒo³lÅrŌ¦Ÿ÷čóØ]Wī£ėņĶy0»[1žKĮ6JE‚V­biJÉLŪĶĀ»Q›ōćģ…jz;Ō'ELĄĖ9hå#ˆ«uĪCµÄŚF—®Ł3Y·ŠėO{˜óĢŅų¤d±:›¬dŒ#¹ Š­Y!/’Č0Øä ƒŚ ˜¬8„!JB¤(„`”°~@פ’jv• ¹jĘ ŗj;dźA.ŲēlJĄE5“;–ēY&īTOęR)““a'wpn!¶­LŁĀŲ_‘är‡P@4$ ”;V~›=…µŪ&Ō­ujŁ#qˆJakdk¤a{Zē$aŃęmwˆŅKCMIqś3ŁšQ­ŁŹĒĢÅHĀæYܼC–Rź¬bĖ;8'1Ģšh,C‚„2Sø&Ž6ćIeä1Ēw,®ž)#{$m#‘®Œ jŃBj^Ņ Hqm(7ęEĻ j··Ü5¦éšizå„͌ā[Ė9mļ’TĒpņęImŠ;{†=³±ŠG0ˆé µ«Ł rŖ¤ĀšŒė麚ōĮgņPl礘‘ P$D„Ų?wŁźÓ7ŖLƒß¹ µŽ…aŖ¶^™ŒPš†2WĘĒJųŒŒµÆ­1h\)BVGüßć—’źw\,Ķ)š†Ŗdø¹Óķo® ksdW7±ÜMhęf²Dń.hāyŗ6‘„{䋗R5†sņoŲø,ĆÉH˜ĒrŅ ]4"ļI*¼Šmž;hįŖ-ÕP”åL†7h³„±³š/‡–&|…™( ah£KO!Z–ŸÅÜS„ź?XjqėŠŗń ®[s ĶĆnšfĪĀIµįū‰#wq”7,¹c‘©ā6¹³-Ł# &mÓŌ`³ĪŗŠ¬×(žjĆ*Œ&éEĆʵLEG ziw˜¤ÜNrѰpĒ Z’O§Ł2œĒ»œ“•½ź’īēĒųĻqUĘjŌ;T¼¾«&khy)NeŹÕx«Ė ”‚ķü›ĒWéüH²ĀSģ96FL×BĻe„®1†‰<Ü«gv7ILY&ųcŅtœwͤgfDŖM-½¬l1ÄĪL­k|€.s}­q°6§}Ø]LΚāyŗ*d{ŗ«Ō¦ÓO>D Ź,kÄ«V,ŖŌ2–DĒҹuć™,H›Ŗć¼O_±/TŸæd{5JfĆ[‹Œžlf…@Ź(ń˳&‚)U Q¼d£k›Õź¶!ņ†™Ō§«Rk³uMwr«+ä£ĘĘTń——ŖX«&^ń¶IBżHNūN¶ćiGk°‘„,čyįd!eڰ…pÖj)Ā$‘ō•12*J”Sń®ØWf·’Õę[•ćrÉßų“qž““’ļļdŌõŸōķėņ­fūś§ō RóY+4DŃ>I–y€•rå(õ•!€@Ž“œ[Žh§PŲSU-Ä¢€†¬7ŪÖ’Yßņ£ś§÷œ¹mV©M4EayFŠ«ńß2"ę-}a†ā 7heÖ?ņSE3 jĶĒÜ;”_¶ūö}`½8ų"Ā_ƒœ4•‹r‹ČŁ*qåėMĪUpŃĪ$ØŖ‚©‚%1™€Cm@­…`µ§!9yĘÆ/^X1_øBי¶¹ÄNPĢĘÉņrĘZĪ?®1ćuo Šķ• ņ¶{½ö揯*B$ɛr Ė’v§ŽEt³WāĄń"ē¹£›ļs€ė9įŽ\ńįkž}S‚cpHÕ|‡…1ŻĮ“Y4°*hƒ…=VīŌQ5HE&xä·šš÷HZĒ“,=…ń³ŁĮŪߐ!€ŚŽ“«R1yi29OŽŁbæ±Læy™ńõk!Å?3ŲįF5éT]!" £Ųb.[Œ<’å½Źgƒ\…äü–Ę|ņY:ęÆÅī.ćœ[h¹åšœMĖĻfü/{˼Ž{–U–•°ćŖŠī%#c©É7d¤£VąaY7 &EČłė“ó"'ńn£ÄĆcŚšäžwpīƒsČw+U…›ŚäŌ÷"1«ęW'óÜ>[Åx³/ņŻG Ø`Ģ čSyŠ<ƒ (dzsļ$¬M! D~žs dP‹!|åt_=8ʎWw@”ÅÓjŪrīŹ8åå‚2Ō,ˆļV\U‘"`®ŠD$=¶3µõ֋–lDŠķ’©É"ÆØ‰ŗŚų½ņ}Ź~#xĮĆ2öL‹ĀĪĮs’.k’¦8»@3Å׹ʶBČÕ/ˆāńjĘĪET›,”ęJš1šļ Ą©!Śb)wĪ_?5Śfm›ĄÜÉüH½Éćzd-Š÷w»8¼rO*d«”$”Ēü_ā^ć¶ ęüĶ œGuŽ@²M+Ō “$¢Årø6)}<šr†-ļø×‡ø.¶iņ'’š;Ϝ”ĄĖf(ģo…ųuTµG‹Gy_=;«\"]J8¾1 Ń䜚L¹(śi!ó„[ĆĶ“É~øgjŸJQ HĆeźżÉĘm㦠uØr,U-ŹČF¶*iĒ*ņĮ įr )¢tŅT€dÓ6ä)h“DŃ•vy²Eݼ‚yAøĮ-ó²œüĻqŒ—”®Ī%ńR€‡¤¢éwźQŌ•—Ż“ĻęQ—æx賏§5–°ÓDM4DŃDM4DŃDM4DŃDM4DŃDMZĻ#Ūų‡øJ?nnĘ”žĖŖ_õus÷Īźņ){_øo_•zćźĀČM4DŃDM4DŃDM4DŃDM4DŃDM4EäćĆ’‹KČ^ŽĻڧ’ÆĖmJi’y'GCjæuO™qŽW’÷1Ā?ūØÜ’ö»¦£ķ3 Ŗ4æaż#ČVßĆ'q®lDq×6Ų/µŚC¬e’/sŽ$ ⬯%)Q(I²ŒNBĆcŽjĶŃ  Ŗo•9ǰ¢]Äu÷davŚ7kÄÜ2rēxmy*i^„Ś/x ńx^GyÅ÷š~ZČ5n/Z°µr€µ0ĖE½9īœ\Ķ6Æø¢2ؕŁÓ›UqLD¤*iIŚ QīRœåWæˆ$#dmĢz*åpT¶ńµĄ6ńK–ßqGIäŽCņ€Õœx°ÉŪmNƑŁ_ײd8É9™×5ÖLd²Q£†čµMŗˆüŠ5ąs‹œ7B­šDČ!‘¦Æ}sJ:ƒõ¬Õ\øćź —šj—~ įœqĒ:–#ÆZ/9®{įh*}¶ĖL¼Ģ[ŸŅå”"ŁŽqõÆ«^Žt¤¢?M@ŹČ¦FęUBœS²Ö¼e{\ć]µä§wR‘š[wxöÓE +“P‡BkW+\Õ Š‚(kޯ熣°éīę)vI–<ēܮW…ÉVZb1ĄåķC°ČŅ-Ó3Jķi|e‹gŽ·Uc•%^ܙ$^ć:&ŽI#¼j7ŲcEz\GŚŪ+N˜(­ĶÄ®ó6(Ü\z#˜.W ÅYŲ0ÜiŹ~Q8ÆDv¬Ģxj“n\²×•˜§LĆI×ņĶ0öE;Śžefžā}xŠTDéĢ‘ĻĀ(@öą Ę]‹ÅzźķåÜ­čŽpeäpœ·n“~C¾s_(q0¶NJŠ·éQqøz7.Ix ¬Tg `ūtćōŖŃ@Hx¦r>Č jBŁ7]4r…Øg”²›hč×mPH<+pl"ŃŪaŠŠ]ŠāK0ę”Ƕ£±ytœ:ü•kcŗiv?;`Æ,y^iÉ!#aY`Č7h+·2łŒjW)ZVuµpĪ€8ŚŁ]©I\ļ4ڲY‡œŠ‰ßėTČd—³["h¢Vµ/"Ś9 HĶS°¢rZ/‡Ć“œ ¦6)ķ\oä³l‘Gzė8„eļlm«£ˆøx!&2ą œŃ‰­č5ęeĢ-Ź„'7ņ‚ńʬ‚Ļc#Ź\2ƒyø¼“J*_]bęß]³ŹĢŚkŠF¹f¬”rŠ–EÄ4«GnRIW&L¹qH%ŒHšā(zĮŲµķFĶŚ}ćģß$39”«¢x’3PŖöś®„hHØØ4'jŸ¼ ’āMÄśŅӒ潓[Ÿōķėņ­Fūś§ō RóY+4DŃžPT͹č6*Vj@]Œ' EHTē1·1U(lvū÷éaæxž‘ū”fŗ žń[­V¼M4EĘMCĘX”å«óM‡œ},ĮnļEģl›UY>hÆaˆMĖUĢCl ;A x@p-; ōŅ6‚»$žn~?%rĻā‡:N¹ްn.\S“JøIWŒļ¤Ü(z«o\ą£ė6’uņ‡M11†DŹ™=(åU4 ńd,;7t-† „ч»śVM¹ać7–9G›–~\ń ŸIp„L­ĘükĒĢŠœ_Ŗy¢ėaĘ™2Õrc3M“øŚ!źµYÉ:ķ¹X²Ź;‹–}Våß »#i^V.ļų}šķšäxecʘļ>pöc%?o˜yCŖ¼¬6}.lH®r¤ĪsŽš‘§ÉĪ_[Nr a›Ö¦®®G¤Ł“ÅS)؜ń_Ėl”ƒ2•k‘žM2&uĻy"ń‡ Õ®opµą|Æ9AQäd[\I€)²­ĪŒģćJŲĄŗ›•°ČU&ĶŚ.TĢ%L‚ u‚”ā>Kē/µžxŲē® åv"ę>æåø\ŁIåyéüZĮuŲ äi­0ĶYņ~劢pæ§`EÄŃdSŠ“øc$KpD‹ ϼfÕétž±ņC”"üJÖ埞S‡1XŖ„˜ķŚFĆ'kqǹ¾ZW%įÆO±“/Ž«̼„]Äs‡* ķ+½–|c좟2”Ē+6„£ņ»”œ&äJń”|Åj‰TįÕWV’Ćė±qglŅÉ^¾¶¤¼n ‚Lщx‚ŁŲ78ø"˜|Ńā\īOÉ؃—1Wü”ÄN?sr„øēgE”u?Ų9-(šL)w“••t9„1›6Š+ņ/QU %A@L¤h‹­tā]›,ųĖØšėǐ~7ósĖ×¬Æ •©ü±qTć IX?¬%n?:fė%÷ CqŽzø£¦ģ"é²)ĒŪ’IŠi·EeŠd]£9’Å+00D.=ƹb? e,œųżČ|[‘ęč„ɵČ<ƒĒ¬»SˤģŌ3XŖ*Y äÖ>YdI&ŠÄ2„PEBČ*E™ā—œ)Oe9®,yMŸćA¹_1P»óéEŗņæ’¹Ž’ŻFē’µŲ}{ØūLč*Ž—ģ?¤y ›’…ē]ģžD'²L-j]ż;ń÷+…®ÄՃÅcb¤/lQźńF|‚FD³S2F3vżĄŖˆ6]R‡b* anĶ-Ÿ¶„“ß·šäbMrŲŠÖŹ×HzʵĆ`]š±W'øŌėž>bč¶¼ē‹éVœØĄŅ&­^²„;=øS©üc‡Ą™1½JĮv“Š€3V›,‘nu€©9A®)„8ypĒ–µŃйÆ•„F ÷^si×޻ÊāŁńēŹ\āZę’4ĢĄŅZ ŌJ(ŸÉ#ž48ĶYńÄŠ/"¢on3r³ äK}š«$9 øćńĆZ)•õnÖ|eī±dæŪī+³\ģ+ąčŠuŒb¦R'ź×ysÅ ŽĪA@?:Ž’ÕŽ†gųŒ‰”.”hs‹œć@Eh3‚@&• Ų­%æײž4Ģtø¹Ļ¹“]°tfEõ+ĀWĘĀŹÓŠÖnžR’x«>­ĪŅ2}~&ĒłTŸk·m„€(d20GėbÖŃß„‡qØ¢£zŹŗÕ,īz<įÜLd‹š#œšOY„Žs\*ąĒ}«#ų‘ÖÆU°gxeĮł—*Ä]Ržź¹¶X]eK”©øęµŽ±cLвgö0µä$nł­" cš§Ū2Fø:¾±Æ% ”ŲpVķ5Ė»)”’ŹC`ihpsKœ÷ZŠ»1ĶŹą0m[õä—Ģ›Ü7É|Š—\—ĘśĪjŹ‘ r P²“š¶āź– ²ĻĒĒ[c7ZĖmoÅFs„~Iźė3E7Š“rÖ§Ek‰aØĀ»*FśnäXš_ŻŚ[O 6;††¹ŁFl ‡µĒ‡3R…ĄPœµ߇üŽe8Ī>pRõmÄüt„ćž(ä K<–GŠtķjī#Ėhxž_+]1’ŁRVvwYÓ]…Z ‘`„C‡Š6H¬čޤĢ)™Äyi€\*§ž)4qé²ēš”cžÖC‡ˆēÓė6‡1Č¢“.¼9?•\–ŹY|¹³ r'-ß²ģC`‚†Ė²—Ūiī`Ų­ ™ĮŲzŚv "ąé5HČ0r vNpŹ2¤šļVRVNrIōĢŌ‹ł‰yGk擕”xāBJEó„ ³—Ÿ;QgNŻøTāePĘ9Ģ""":õx»!š7’‰7’ėKOžžöMNY’NŽæ*ׯæŖHņ/5’±DM@9• \ɞY*;ŖŚØü†˜Ģ„q=Ķß“IFj¦ß“LSl:°ßmćŸĢhūØĻ蟮+wŖ×‰¢&ˆš"·ÖŚ3©‹ '"S.— O™1dĒŽąŁN£¢-耀€€ą =@@C €†ˆætE“tżƒ(½zј©EӔ[æ“źœ›čŠ–[$cĘźØ‚÷ŚZ $a"Ø­iƒMTĪQŲJ¢g|SĄ>ŠŃõ µ1ŃFּƔĒŲ¢ńK1“vbŽˆ©¹\ч`•]¼±!–lĪR•½Õ£ÕnQötŻŹ¢t€vö˜D\9#Ēu:§žš¹ĆūŒ„F7ū™ŃŃ r/£ģĪølwū2u$öł¢-@ä.fpĆćł²],öõ¢-ņ;)œS>yĆ$P=¤>Q¤įłŹ3€`Ń)Ė^*AØDf¹5ĒČu•äŅ”Ģųā=E ö‘7vDLpüĮ¢*Fwžœ¬1VJĒĢĪ*A°D†QGr¼…ÄŒ)JQ0ØęܙL=”ģ ÄtEä<Šų–‹PÉ;ņ3ĆĻP†­³½ųƒ}÷ł —!·Oo³DPƒ‘æ‰ĒÅv+˜l[’®<ÅȀ‘•‹t™K² ,²‚“ŸæĢ–¹ŽŲ³ŠŠ%&åTČoHŪmÆZ×8Ń ’¼sšŃWź?Ļ}rĒŹīGadäZü`£Ų‰aĆ;¬ĶžJ,’,”T±7¬ė8oo(78Š(ziDZLš-Šīr³ µ 痳Ұ'»d‹·Š£ž³–h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ­’Ņ߈„ ™ s~×h§Ā §ŠäΩįbi”La÷us÷ē«Č„ķ~į½~Uėi« !4DŃDM4DŃDM4DŃDM4DŃDM4Eųaķ)ķķ¾Żƒ}xżųŸwóåįĂń]ś`;ō(aū@Ÿ1¶¤ōģ$xęóØ}[˜yüʵņæ’¹Ž’ŻFē’µŲ}{ØūLč*Ž—ģ?¤y ±üņKŹæ™c"q¦ģŒa,š’¶ś”SøāÜ Ę½g )aŖ‘óĶԟ<±®Čt×EdÅ#ķVp‚ŃŖT6(Sj“XÆ{Ņß2žÅl·ĪĖŁģö UĢźNrĮ<żÄ¬Ģ¼‹“ünIHŗQeN=L”Ä}śõx»”xIńOćߒ\¤ņ#>ńžÉš3 œŽ}xvFČWhčYF8¦ÕK‚‚®ÄRk—Üs++-÷ĶÆ%¤¢‰›ŌP `Ų’RŁb€–“S³KŁiģžŹKéÜČ„…¬ĮĒ;eukC@f¾©Ū̧¾8įO0W-y·ĪņqapŗƒQ¦W°„µ4Ė^ńĻ[ē¬Ū“™Q”ķ—:½æ!;§XėlkŹø’U²²Ż87¬"G…M½»Ä×SƒąBŠCAÅĪs€kkøcWV€BAĘS™Ä¼ÓĘ~6y×R¢Z©y^CāK.±dJĆĻN½¾Ī”ė^+±Õćć[Üj*4 EeŚÄQdš"*Ą‘ĪĒ>$bXsQ•Ņ¢µĀ½XoS-wo±Ōü”sīÄR1µČšĆĪāiPüÆåĄJ›FÆīńy.BĀäY–¦„Ó)µŒu_­­‹ź¹ż­+ó.'.ΐ¾Į£­®ę¤—r1åä[—*J š*Āt J²„‚)‡(®ŽL9Bµxoį³ó>Il_+”h..kĒzĄŸUÅ£ŌĆÖō>«‚๯[{yć5*ų6м© ™Ŗ$³Ę³]ӀžĢœ—‘ĘõE=Pɧ‘!£/PīR”ģ‘Tļ:§j£{P żąs;;©^b0ż+?Wc%uĄ·?÷hē =h…“%ῤ×QĪŚ Ć)Ɩv¤–Žš"ģ„Ąßž$ÜG’­-?ūūŁ59gż;zü«^¾ž©ż#ȼÖJÄM4E޹G˼ä_%SPĄtcę1${q ĄS*XšŁŅözÆĢmż½vū5ŽÓY¤„y})oŽCæyTzøØM4DŃDTM§’ i’­M’m[~Õv=jƒ“€‚›(f&\€I8ÖoŹˆŅ*€źŁķ čq ŽJą|/4øŗ’Å”Eܛ·½rV¢š¬§iJBśŖ4lÖ Ü#°ŽĶPaˆķhģU‰„{WūŒ˜ @H+⺚~˜äY«üp] (?”ŪŽ¼0B~ˆ^‹‰‡Ņ+ˆ[‰˜ ė™ĖZ:±*œ…MO”Śn©(R „½ķć'Ū71€L?gpūĒ j“m ś=åT.§Kø*ć†xķG^-Ä-žjŪg~Ž2™C§\2e’ło™tŗmšGU«–5¤dß.åR¦Nҁ=C½ŻĘ( ¹"µˆUć¼śUŲ应чøz|æĆ3Ę_!¼aĮ™Ž˜Ū¶-Į—[E*åÅ\?–r]r®5hę>ĄŪ%“™ˆ:²č°6'D‰xŅ!ė¤Ż¶rL«T•UEœGø“ø–Š5H“84š¹`Ėńņ:Ģļ%Š8+^&ęq‡ pąC&LPīsqėē܉iFff¦œ­bE«i”źĢ+Ŗs4‘+†ŖŖ$p-ėh„®9½‹™ŒM}¢VqJ§šBķ8”µ5eņnCx¢dß~ÄĢk){Kæ]¾Żfü,<å`üTü½ĮpĘįÆNa:˜łeT0‰Ž¢—;ńŽsQ1Ķ÷£©Œ>Ń׿ »Ž})ńSūŻĆеIĆŽ=¤ØŅ_ Oq¼_ČPüĄ>w¼śSā§÷»‡”oŪń/-ŠR†5Žpb˜ĘŸĖXä\(cL"³‡Ó.[ØōīŠ5č·„}*šÜĪ~—‘n‡Šü|n.Æ~įäĆūRÆ~t/>"oxÆĻź«Ēæš]’}•’¤uēĆĆī„ų‰½āæ?Ŗ§’Įtżž[ž’Óįį÷B|DŽńVK4ąUoSlM$©ŻGµR'ęJbéĢs”IŚ mŌµbx¢e(ŪęYņŹś‚kł¼ĒX YÆ·Ł±żqÅJ%ć˜čVb’Ī™Ź<3&Ž2®7g«€źģģŪŲ¢‰ļ$“wåŌ¼–YYENsłyū•śoĒŲą¢xžc¹Ä#WdżŌŻdĒ÷µ‘ąEī…ćĢ~‘U+LA‰˜SeŒ1ćB „Ę+z]q1„ Q1½8Ņ÷@ ^š÷Ì}öI–CµĪķ*³‹‡ˆ„l įb㢀÷X¶-˜6} ƒD’H÷5X`0 ’I̕Éh¼M4DŃDM4DŃDM4DŃDM4DŃDM4DŃDTŒß‡ńš ~ܛZöXfĄ]E\żńźņ){_ø~UėI¬uš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"@öž@öęя’‹é’|»ę<ƒÄ”Éżå‘e ‚Š*ŒCG9ee›Ę"Ŗ¢*ؓW*E1¾!n=u#§}ėŗ<ź#Vū–}o1U‡•’żĢpžź7?ż®Ćź½GŚgAV“æaż#ČV&µ„D]ĀxgĻ,ÄĻ-™Vł)ŒiĒiWåüEBU6܍ v¹g x•riqŻzĆ"jÕ憎$ ˆŖ&²c×rć¾łŪ#€-k\1å%¤yĶ®¦ūM"{(^öM4Ń:­$U¬dĶp$·Äl8ؙć#ń äN.^³¤Ÿ2XŽ9mŃł2nńJ««ämł•3I§#Zš^³S“Ō-Xž 1‹†æ5«5"[,‰ĪY%+’<Ō-4p8,KKĮn¬ń ‘ 8V‡ ćBä ‚E97|™óūŸ¹Źž>e.3`?—ĘŻ„ÅÕßĆ‡æŻłÕ£ŖĢļüŹē@ųµĀLn“żē$ŁH@˳Mxζ܆;(ĒOQLM׹X ·Nļ~® >“ø«NŌ¦>Čhķ+ T*WTah“˜æ£Uėč,„TpŗvųČĖ„ß92Žß®įŚźøxéE cœ~#ŽŪĄŒcchc Žé^óWWź„Bh‰¢,l *©Ÿ9X²ž§hdśCDDć¹}6xWj}b\{=Ūž]c0CśC÷B'ųŃ?¼Um«ŖŚżŪpÜCpÜŲ!æ“D_„!Ļø”†0“JQæ>Ą;h‹R^E¦"\¦™„$¹Lm»JdTpUĆølŽśTr…ķ"ävҟaģ8nCmš˜>Ņ›Ų`üŚ/!iO£%æ*ȏļ@žŠź‡«‘ļ ‘Õµu4DŃ |³N@²„‰¦Vd/9.’i€Ē8¦ƒ‰œJݲ=Įń"ŖÕÖ-Ó:j(gÕ8Š>™D{„ŗ·,‚&f;w+°Äe~Q³BģqŒ*\.ü6¦³™9Iי^fy^ (lsMR:VÅNc S¢­GČ=jś?aŖ¹ČvRv³6łéÕ[®VˆŖÕjŚ$—ŹśķqRą2&ScĘ&|³yfęŃ%ā²"Zq/Ļ•ÓešŸ nĄxG{¢h«&r‘YżłćµXrƒ”»Q!~e²Ģķõ†ūŠ0ŒWœØ!H¢Ö1Ü ʜic£“UWKœŹ*åō”ƒ‘;”•peČÉ<8ز¦1„ „(f±cr“P,½Ņ;3ĶJ«µR„4DŃDM4ENŁķ•Ź\_Ö­2ČCʋ¤¦įd8;‡®AC7fÕ£īž»t±9ŠšIœŻ¤0ķ°….{X*ć@Ŗcó•‚„c—’×”²m‘$źr™Zģ>”B3čĖSD$nÆ­*ņ %ŌL#˜±)·*i€öŖSĄ86Gz¤å 䎤mćtmõ€Ģ]Ėø+½ĒœĆ@¦bŲ›“ł˜Kz¢ŖÓńŹPÆD+5vEG U€xŻt҈Ž@ĒPŖ˜SØ==v cc(ćGtjād’­fģB¾ķyƒ]Ø—'ÖZ(&ķ–<„L=¢c)4Ā=>Ām±æiG ˆM_ųˆ}ą±Ķ¼Ćč•qŖöźµŚ9Yz}†"Ļ‹„Y,žźOŪ"ķßUŗŖ"#éØpß`1D ]Ź :ø×µā¬ …mĢs (UCÆU)¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢+gć…ĀÄüCü)9T9 \Տ[‹šˆ"¾3x’©n‘DÕ1Lņˆ†¢®¾żß–å/k÷ ėņÆ\ c¬„ŃDM4DŃDM4DŃDM4DŃDM4DŃD^@Ž4۠ǚüŅbÕ_]³Uļ Ū­Śdżdf_E{"rz‰€Ā;†żu#§}㾯Dź’rĻ­ę*śyĆYO.^p³lmEŸ·üµrŚÅóØ¶Åślc—.$¬«•ŽŒ*ˆĒ(®©Å(ķ¾ŪjżģRHödąV.Ÿ,Q1ęGˆņ(Ļ_ń‡ČYFé/53ź‡S“NŃäō”Ć“J=GŌ8wl„åū ¹ŗūõŽ,&;KBČv„nŖz©åW^7Å ˆč¦slg¶éER¤ß"QŲ{ƒ×{9sl;l>˜o«ƒNv÷ŽÅhźÜĆNŸĢ®•OÅn4`r-vÉ·K?h€”tEQ”öö”ĪīŃā™1ÕĘéńmÄ÷zU·jra tćčRŅ‘ĆŽ4P=!±5vIņ$­ø®-ņ"pŪõÓź¼h™· ’V‰ŲÉe“ ŲŃ^||«÷wŚćNl<ŠH±fĪ-±ųgĶ2gÕ¼{Dʰ™¦Š%(M€ŗ¾0‚IÄķ[‰¢&ˆš"h‰¢&ˆš"ʄ*/̼­#ÓwLš›¦ćßźGŗÅĶxō÷ž&ŌÅ'𦱣ūÉ>æ˜)SŠžēųŠąņmŽi#ĘŅźR_G°O ¼„„4vę©SjØ6u&Į»’*ŠÖ —Ēq¾¹’fÜŠoLšOƳ@Óüp®ŽrĘŽS¼žaæ³zŸįśõ’‚ā[jĮ™īęÜ9ŻŚ­»tīuX²ÕqāädŅViä“ķśóižæY£ČéÅ\"!g‘]g"Ų‘œ=${nŖ uE!Ńl>aŗ 1Ģ»cåÕ³8ƒ@A8 @hę$­ęūåógŌŚūW²-++A—Š N8ćČEó@ĶĖ\„īwōńÖėŒćˆą0ļø£]‡u WjN½ ›”:m­fļŒųŠģŸćųm;˜ļ5=ėe“ąī“xŽåy'ÉAܶeĮŲmC|8š€±ŌÜ{¾čĬ”Ēķ”Ō0ō÷¢N¹¬“Su=~¹RƒEŅ)Ak> ZdʃQ:²˜zYī;°7īYD_É<Ē‹—sż2ĶGvåĢb ވzfu›7­»½DĪ"^ĆlGjö4^<Ļi_Z f”+ŠvņŖVą­&śFn©ŹO!Ҽ£°«Õ s5ę¢ŅMä3šÅ…ƒšµU_,›‡uŁĀ5jWIģ”ŒcÄT#†/ż[¦Ŗį±»Š^×eØZj–¼³x|.Ž7“ąŽBøÅå…Ö—vė;ʖLѰļˆę+÷Y Āh‹į@TSP žšž€)ŻéŠż†ōANŃzb¦ŻŪuŪŁ¢tģVcŽ\—»a^Np£š¹OĀžŹ8ó›iłFóVŖKJŚ/$¦;)į¬vD"ÄĒĘķ^)ź’-[•c,US'z„(åÖ$×-§»ųVČŅEwf«äĆn i‹Bŗ·“ųńGFęƒMłNśyqي—¾Zkł#s6õä=«øĪ]p«Č=Ń+ŽęxņŖué©”Ēy÷“oQ… Hć_’+"¤M+³dc™@pÕÓ620Ü6 ¹Ā­ŽFŽ„5»®(֚;pŻ^J±5KTMŹ)ČavVęrõƒ¦’ ŒŹN2R5ĮŚIEɳ1”łw̜¦%0ĒLĄ rä1L2QKń‰b5Ā Ø¹”’ L2ŠHÓBø¾ŚrZw8źMµ>9'%m2VŪbŅÅd%›dć#ą¢ŻĘƒ—JtT²ąŸh`Ek°Ņ£k‹s-£iV‘ĢŹ*.UkģöœŁ‰`žłĶŽ`ņ„;)ˆT&Ŗ’³ö•©—mfęŚD z“¬<ŗ„Iź*HåÓv’„8—“ĘėøĆŲq;ī®DGFšćQøČ75Ā̵¶3Ē&g aB69Nņ®”pÆČĢC×ēŖy/`‘ˆTīÕ%XGHŹ' £„ģc¹xr¾pƒc™2(Ŗ}ż¢ø†ŚŲ­µK·d‚F¹ükwZUõ›sÜFę³”‚ÆF³Ōz³Ö¼ig`’¬Pqų\ßĮ7ŽV~RZŌĘ£_Žw*ŲÆ™C év2e%F5DÜ,DŃ*H&²}Ēī8Q:޳i¦¼G99Ü+ASäRśv‹y©°Ė„M•S)åL­ö9{–"„Ь9—†ˆ‘—Æä†Ö®J4‡k$¬B•čįxĮ»×Éśąš„98nÖ5ÆXŻÜ6Ž:ēvŹ‚<Ė*ļ†ļ­-Żq%24cˆ>u"¢ >ŠüįÓ[ו˜Ķ`!LP?~g±ŗ€€š©p(‰M·CņtcĻģ­ę+&×Ū?Wε|X’GĆ)#&źUvĻM`hń‚-Ę»#õ“ŽF%ņOŻ­*ƒ˜Ņ‘U”QĀ’ĘŹS”¾ ćā¬ŁK«¾žņ=B x“ĘIµ’9]$“0ÄÓ+§ŒÄĘĄęL\ČŚÉ&Œ ę9ŁĶĮ’Mu ÉĖ„›g¦³„ŖĒL‚Æz øtDĄ‹ Š]”7q@ ;ö‡½+.Ł×/®»c#ø5«XāöŒM(āÖVПTP’11Ś©śŅ‰T‚Q(‘P %7éJp0 Mļ`诨ü¼–» Ä™:ŌŅÓīæåڼ‹ŗä¢FM­Œ˜«²H;nš‰Ž›„•HĀ""Aßq”­ŚF»‘ĪÜhE7«³Ģq®C×ŚŽ±®JX‘°ĆÜ/jÅ"ŻŖ†”:¬YяlŌ[*efj,‰Ī»Arފb ¢ė(ķvĮR±Żi˜’Ē7i ó)ČÉėI&Lä£Ü$ń„‹FĻŲ»DDČŗdõÜ“rˆŠk·T¦.ą°õÖ` Š‹‚ Š·Z"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ­·øuę’ Ł·sņŠ#™ØRęW»³½ ž,}<é¶’üųŚ4čķü/So~¢n~żŻ^E/k÷ ėņÆ[ŻXY ¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ¼ƒüf—Óę/6Sų‹!i.ćŌĮŁ—¤ `Üw£¶’n¤tļ¼wÕó؝_ī™õ¼Ė9:—P)¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢,o5nä¹ß”¢²†*2ĶDĶRQL!°¶4\‘QŲˤäD»č—Ó‡ØķŽĻ½“ė Yęž _TžńV*£,„ØÖ;«ĀŠRV£%cÕłŗģ]UóøJĆä¶*_1lŲīܤp)Šķś£·iŠ#Įųęž{ĶyšŹŲ ¬ „Ɨ1ßĢ»·XĮi”2X‹],Ē3ˆĒ§ nēWZ„P±^gWjńn„e,’)¢Ł#( ś§)UŒPŲ„(›“ŪtiRHțęnLņ;++,˜³LėØ0“³ÉD+8R•U~v-+¶T@µ(÷†4˜ū7+ƒń÷Ō4·ŅHhÜĶéRńYĘŹbīB“ĮĒŌQbtĀÄĄZ‰E1]Ī;¦€#¾įżŚm[±ƒ~›pbx„šoé+' »ŗ8ä7 %ēj’kÖŌŽ“½hÕŚ©žć8-‘¾y³’“tUŽ‚čļ²č­ź·S“ĮŚ޳¬ļm3_ˆh;ÅEw:ˆÅaŻŚ²ę"Ģ ˆ; > ×­aēTŲRÜ«‹±ūЌ”JĻģÓņó6ų6¦xŹyeeß<3ijҊÆå$A$D5ŠP»Kō§ź–Z–“KhŁÄd ĮF’v<Gmę5 ē>1Ó/tķ[5@Lnq©ma<­ļ*¾ÖŲµTŃ¢_Χ’O÷AÆW‡b³u%‘wY‹2e'¢|Ģ „›čć&%0{Kņ¢(ūp×Õ[“RæõO~>uŁt§gÓ 'łMīR€Ł"凹a‰8uJŠŽĶÜbņ–Ŗų‹“<ŗ³Vnj,4kԙݳµJ8Ŗ\QnÅ0Œ”›,ū“ło§SØVģÄŲ“;ė«–˜fѰ`’šžSMūpĒj×5Ū ”!²<āĻńA]Ū1ĆbмwiU†o›*øśuk>3§r;.U±}…Ź„t¼ę8†‘cN–Uł™ü2)©źą`č°ks² †ƒ`qō­&ö¦j„£ŠŖ™•Ó6\#`Ö„ŠŃ\åū^źķ4žøĶĻūŚÕxĒīįśĒȶΠūÉžØņŖ#6¤uč "™{”RįHBż¦6E«}¢ķÖ§§]śżĒ-»R“#ōŁūķ\żģ©/9ŠPT S1W\¦@(Žēal}Žaöō;·³R|,+ŖØ|”FqY¦”ļó«ķ®š¹j³š%/Õ/ė÷w*ć!LśĘź;|œM~5ŗ©³d˜m®eÅ'Vp;NõŌxY i #i{«Ü¾.‰‘JęN”‡WßśhĻÅ,˜ōö(˜£ō|5H?Ģ CYŅ®?Ė*Ķ9VńR·³®T>ģ3A86Ó²¬3j¼RJNeƒHöMŁMB&Ķä†9ÕTĒTę2…)J#Õ„’Fæ#)²ø®M Ltyß]»•§›Ī~¾xwšŃWŽŗK5“ŚŠD‰rØįµUz¢/ĆÕ9”jj£¾ś“ēŹęāӊŗĘĀÉjAĆ šVŲ-¹-0)ū±ŪĮ§n1öØąSņéee1ü¢ÜĮ«trw¬€čĪ"“źZēȗtJQ5 ęĒœX^ŌLĘ ·JFØŲ;=Ą8ņkŹÉČ;Wµg)ģZŹä;‰½#4”ÄŚ $śņ]1߯V•—(l ?Ē”„_øŌõ9{•=jvŁi™³U\>{'%ņPó­Wp˜,żŅ©1Q9$#Ŗį LLEĢŲr‡/ĖR;׎ ĶJ«žĪg³āfPik ‡qśqgto"ŅÄČŠŠh¢Ń*KFF¾E6±…he ‹b”§īŽš,ε¦5åŻł•‡ ÓI„+…6P~X«©Ē[üSœg‰ź2Ķl•ė(Ń¢›F”jÆJBµ³'QQĶZ]Ś?KŸD#Ó*å*+ ’.§§Ų!vŒaØu7½ ĶÄnńńBŚīŻŅ¤ŽÆ¬tŃDM4DŃDM4DŃDM4DŃDM4DŃDMQž0‘ßā0į‚'„¹2ĻAżc,`x|"Õž’MDÜżóŗ¼Š^×ī_•zŅjĀČM4DŃDM4DŃDM4DŃDM4DŃDM4EäćEŠ<ęw5œ‚^€;yptwwś ®ar %ߌ^’OŌŪ}ƒ}½š‘Ó¾ńßWĪ¢uo¹gÖóœŻKØŃDM4DŃ1`a P*Ęܘ7#DD¢ÆońÕŌIöõpŖkKŗ.poJ¤æ9ü5¹?*ĖŖ ķł“*A¾®xC•QāĮޜ|Į "Ż•8öFÅPŖ#æ±NÓØ (RūĆ ķģū41Šaµ†øģW(‡"„"‰˜§MB”ä9Grœ†1LQhVUÕõ¢&ˆ€"ø@ź"#ī [ėīXʱ¹\dlR¦z½4šfŚ]ń’‚”d VrUĮĒ”SlŁU =uD’ĒK¤pkG)¢¹RŹi\ćĢ2ŻeĖdĪJĶvŹ®ĖŹõŹŻ%H™U…jŒÖF&ƒR©•gLļsõłĘ<œ2ė&eY÷(ŲÄ0”¢"«OÅü?k+ė,'7Ńõ·ōkȶ“=BH˜(CMkĪIV› ×äVb|™p±NLßņ linĒxœSȧšŠ«ā-” čó»€pŁF‚ķ_YŅÅG·Õģ rn.Ö®u;÷[ŹŲü(]ź8YĄŠ“—rA r֋²p–‘k§é칁Ļ.™€øźƒ\h9A©5ä¦+°5õ8õ_ʰpXŚēkĢ·š¢–xś.¬½¾äiJŗęJš,”œd.„ ģJ˜>“~Į¹×8¶l*¢@Ńm4GX2LĀĘ[Fģ„ņ;+¶† s©š-źkūM=̵c%–ķķĢ#¹Ž[°¼ā[\*āp ź5ŸåĻ";`pę:ćl;³wL›]ČŁ¤ČŒ™SÄÕ¹¹œyP2€’FĶ4šS}(ż ©ĖNÓmŸP“āåŻY›ėČ@qčcGÖV&µ~Ś@Įc÷Ė•ņŸ©Icyœ÷Ø«čϐö&n$3DL’ ,/ +HŲ9•&²š§ z¦$E>)vxžŖŽ_Õµ‰Šd@č^”­‚;ۘKCmŲؘÖöø‚ēu’ØohīĘūż˜ķtĻs» XŃĢօŽ9®Ż8µĖ漜Ø]jU:dĶRŽō€O¹®yÖm™MŚY<“EõG4ͼf/“nĪiĢA ›²&€Šˆ¦q½yvŻ_Gv‘u;įĪČöųu CM(čÅiˆg!*&N›LÖ³„ŪÅ5£@†3į=µ.Õ²šW“0 .ņĘ׈o’ø ’ŲAQr„ŹÓ‰²pn•xqU+äkʊŠ ėW[-54ÕHå)”¦9 ²J&©²¾^E{£k·:6 ŚJčCšv‡5®ŚĒ}&œĄóR†‡©|Ą’ÓWŠķõkXŁ1kĘĀ×9»ŻĪHēŚ*1V›]”qÅF_loėP 8†A‹«Ģķv«\m$e¾EY«4ĖH¦ė;M±Ņtį¬ke–x²i‡:-Žbõ0Y¹˜[ĄéŽą²-mĶÕĆ`n×-»ŹžDlŃg’™b³ћUÖ|ś?“nŻ©PόźÉq—lŻ$S ‰ÅR `]Ä~Żk‡^øq£Ś­¬pͳEd•ŌčĪ©ī<ńÓ?gŹŻŹńŽrÕCšĻŹIøĢüä•n:—†(Ļ^J¹”˜Ŗćæ–"ļó•łćłŽ‚„ŒpéG* €˜‰*šć„ż* ŁŻŪ‘ʤžN¢²«-Œ"ĪÉįŃ0PчG/IŖ±ZßV…¤dLCćm ŻFƕ+Rœė䟑Ń'oŹ~LUdŹD,’ ј<:|m杈 b8fĶɧģž“y)DŖ±ÖSī-¬Ś!ˆ ĶŻłv¬h¬®ļÜg•ÄWé:¦½ŻÜŠ›Ę8ŽKS"©p6hGl£>aS:wL]³—Ī¬e–rģY[HS«°•"ŽĆڊD'šuģZä‘40FÜ£œ«’šÜr»;¦vcĢ)ՊŠsÆjߝ^BĻOXŌT „vq¢MŪ°˜}4WéČĄõU8s r†H)vź&ŽÕ.cÕZÖŹĀÓQB•HiZ|šCœčž×‡ŠĄ"įļ8ė)Z"B‚Ųå”I;\˜Mó™;PØ\°ĘXLY§^9ĻO ėD÷|[mØĖ{;x%ń Ž}W eHļnŖ“¹šīx¼0ŲǬÓ\Īś$;f]ōZ³Ōܳ'+N“cŽ…^ŅK„^Ślīņ!1 «ź7¦ T:G9U1T™2€Ą#¬½)i—?÷œ“¦P9?H¬MZ;­R×į²ĘĮšµĢNŹžˆUą;Čä'ĒBÆŖ €ü¶FHĮøģb š[u6ž1J?“[@×­wµėT<3{¹ĢķVŽ+Å»“)!Ž£VNvć;el¤VC„p ŁĖ QA¢Ä’‰9Ż  ǼK±pŲuŖź°Å©^¦I’  ÜįmzI¹ÓlÅ£āĻBMC€ŪĢV½žIž=Ž0x«•ČßŃ2÷Z[f¦QM(±dœ,DĒŃŲĀ Cܬk+!kwĖ„ik uM:–UõÄ·Vr[6=„T¹“ėVĖ&U35·"6š‰Å‹4lÖµ ‚Ō»ÓU@$MĪ?PŖĻPQŖ°½‡ļ) aŲ[mĪ£awā–¦XįŽ!Ą‚0ʄ* V±„Ykz-õ¾„§øGØŚÜGʓh¶nddAó½łV,;§®{–`*•³dŽq†āéÆ[­Y¹Į£5O2?‡ļŲŅ÷å§éU…ė‘j7jŅŹ­ ad‚&›¶ę"Ŗ7tÉźI*ŗh½dķ¤©Js”QŲĀŹ1āF‡·aPÆaåŽŚYŖ•)¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"hŠ›ńR‰]ž#®„Ž$ōņ*&ķßs3ć½±Ļ`įŃAG·vžĶDÜżńü·){_øo_”ÆYXY ¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ¼|(łƒ$:ĖE-õIB+R6†ŽmäįČÕ¼é<5A=š«Č®]Ć„dG޾2±Ģd³DcŖÅpīÉó/2ɂ’öé’ö‰”xEäÖs6åsz®œD:÷›Ū®?ŖńF”xKo'–Sīęõ{rÜmģ ˆ hčW*ü÷Ž”Ŗ­ŃśÅ‰&ē35„Ņs]†~ģŸĪ! ĀEü™Cō·uīģm`D/&Äåh;ØIė5Wį·eJĮ\­—2Ļł>6ĶėˆdV—˜Ŗ{×č' •bśƒ ;ꈷEs³ "Ņ A*]„!Šøķw°\Ķao}GŠds€4Ķ,‚­ Ū滇#ģŽęēŽZ†“BZś;¾‡6ŗśŖ”ģ§ĪģŽØ5ƒŅ'(XGźŃ÷łŽ!'Č ux8±{‹*EnH“б=„į',n˜_EN"Õ&ŃEW+žę±hrĪéöo„žLÆĒŪvgÄ 3+R1Å^lkZV=\­^ź’­£lŌ[Œ"±•NZ¼¹®Æcē«Ķ08¦~ŲĘ6S®ŠéœŠ b˜v8ˆ`ź16[b\£7© .gĮv×GLĒ É€n˜RUø[łƒ¹A˜½$ŖšĀ“Ėś±qf ōSē’)`Īł”G6‹ģ[Ķ›¬Œ|Sf dĢtŌr¢fģ(ž ݹŚ[ÓZŸJŲ„:•Ūü'µŻ ėŻŚ¤Ng½gbĪRfłK!K‰Ę˜œ©!Ē®a8_¹ŻÄDS;')˜‚#øĮ­öŲRž[×0ŗ5ŻŠDjś°š"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ©ÆŠ÷ž#ž!tŪÓČnŅöļ¾Üo·u÷mæv¢n~żß–å/k÷ ü·ÆYXY ¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ¼üZµß•¼ÅtP(•æÖ›w·é9Qā…€nCĄĻ}÷÷ŽŃŌŽš?ˆćś>u«żÓéy–vu. “DM4Eų"1„ Pź&0JöˆŽĄ¢+wu‘ŽrÕ³T"įŅn}QōLUA$ż%p2„܀'0—įߦ®Ę5;©# ŖÜźņ¶š"hŠ”°­+#–ī C(˜œS9N@Ó8€“vÉŖ\Šī•S]—”\˜‹,t¹żÄķm¹[øģ(¢rˆ•A”ŠŪ{µeĢ-čWCĮéPó“2*ß2M ī+TkLņīDŻ ±ęDbz¤’¢ā-9ˆ‰ § ϹV= »€óž?ÖdÓ“ĘŚ[œ·Š” zĒĢ:j¶¾±mÅĆ®¤d{:VqøgĒx8ŗ„}ŖĀÕ9E҆fé2™Ói‰ '7"÷ÉZ:žŽÕ3w¾˜@D ƙõ·ųQį†'›y×L†1LÅI>KZ×®c…˜3Té=µæJŠ&"S€&gR¦ „¾«dAŸĮTu…a’zėÜ®ŹhŽ•×o*YYļ`Ŗ¦,|KÅacPßõh7h° įb“ōAWNc˜}¢”ģÖ×rGĻJ¬žeyøßĒģY•x›h²…R¼ļ*åf9A¼ķõXt¾õGŪ«ÖŪr„‡ōäӏĒnaŚ$͐ØTž ¹C · ™k,vų›(Y sži°—ęuNśŃo¼9„ŁÜšł•ŒoĒN×ÖJzĮįĪ Ēh PsUW>ryr÷),¶µVßʬZĻ ¶é0i5t¼©p¶åg(³vD”L’/ØšQ$v™EŗĘdnӘ§ėStßĀl›>īźįŅ Ac@lXō9ΦŃ\Uz5čÕ/§ŗ“ļķ-ŁSkÜK¦Ć„­evs¬»į Ÿ$œÕĆųŪŃŁ“ˆœĀy’·rǐ„£Ģņ“>:©ŲŽu"Ygov,Y‡ėf„MƒhÉqDĆŚs‰ĄÄ‡gĮv~}Ģy"¾čģ÷®[Ŗ|ÕŌÄĪ‚ĀFiR3Žs‡cBŻAs¹Ä<ķg⒐lńŹĄōŲ҉š1x˜‚€ćp]ŖV{v@„Ś(–zųZģ'Wz<'`ī%Ó2½g(B‹"؉Œhm‡> ģ:k_# @õ©Čyq[_qŠÕᕺėā†XČŹāCC†ńÉQ‡"°|iĶŽYc|{•#<±›KęŲ«.h®b O1.U=7Ėdųl~»›·c!iŒo^nȋæp’Š ә#ÕLūģvü)„::F»Ä,ÅŪzł–‹}ó#ˆ¢ŗ•–ļo€$piŹĶ•ĄbŚŅ›+ŠėĻE¬IŃ!å±ŌՉĀ[ß²Ž0‘·G¶U“kcœ}’mu3YŽĮuV=Ż€‘@ķĆcĀŻuŽ˜Ć­ŗßZŃōpģĮh÷/™Ņ®£ŗÜ*{Źį%ü›ē€v“æŌH?õ%>2ķł ø‡īk× ėMEUo­“¹U,•7Ź»[,Œ:Ž“2¬Īõ¹Ónż×Ēŗ.Ÿ¼˜m«oh{ ĀĘ8±įćh+‰Å9{dsŖŻœŽŹ4’7Šæ@÷vØwi¤R7¶ÄūķZԐ¶®HB ¢‰ö96ņÕö³8z•Ą®•§^Ē{nÓü@1Ėz»ÄYé¢&ˆš"h‰¢&ˆæ@ĀPöā?˜Øč‹fńūŅ÷ȾeOos÷mŁ—ogAr¢@:©­s°h'”Rē±‚Æ sEG»É“V†žš õPö#ŅJ`ć¶ĆŠńŒŻ ¤Dą— ķĖÉ=˜ŻŁO*ēQ±Ś•F¾JŖ>oEœĶJØ4Ń’Ė$Ż%]B$ŠEØ¢ƒŚB=¢ažĮד® /unŖŽ$Ŗ(±ĄH/¦’`=čß¬Pą¢"½uq±®VÜżĮ[µ:s’„9pæü2Ź(¼sjķVŖNÕ”ÆQ4DŃDM}ēHäU3 LÅQ3”v1Aī)€CØhŠ>ĘHÕČlĻ%°üĒßu'%%$żė™9©‰9Ŗ,ķüœĢĆÅŻ8YC ”Ycä×Oc6Ķžó#‹Ż“­+3NōQxPų’7¢ÆåMAL/j?×kĒźØĪ4Tf­«Ŗ‚¶ć •ęB*ZU8»,8 ·“»ŚŻ¶!Œnöķfc”MEŁ ” g%]°˜DE>£½©māœe”T+š\ĶlģŠø‚­žiqøć8ĖM/Ž&_ĖŹŲĮ!n4ŌF6>.uü3ĒÅ5IŅæXIšQßqĄJ²¢P€Ž•vcŠwFČŪ”r×Ņŗ €š{fĶ$Æ.<”ó‚ÆZ•‰¾ÄHŪ"[Ūścŗ‡U•ņ®ų§;ŗqāüŽŻcxŒŽĘ÷:Ģ1IŗGö4’…Y »vK†ĒYžźĪųĮāøīÓe†Æ„-KłGm*ėDµp„Ҭ w2ž»]Ōnfä*« dˆįt¬Œ²™Ś ÄįU†én[³ +‘Ä “cJmēĒr¤œfl“ø‘h4LS DǬ °hˆĆõ€G÷µNH}ÓŚŖń.=ńöG„qŹeü®ęķpč÷4ø£‡ļ*šćæīėŠČ=ĆöŠóÄŗ÷ĒŁ õ Į–õ-0ŽŒ _™¤Ē•2ļ¶Ā$e$ÅCķ·ńĆŪ¦H=Óöæ2x—_ĢdzW$žpɤH ¢ō•Öģ+§2˜ßĘ2hŻÓ!Cņ^xPr;“z^5׼ϲāTµĻ*dėszõQ׋$q„ŁB²µĀÉXc"H¬Ā•†Ņ1Ö·qČŁž±EŠŖ51HmÄJ|Ū x_p=Rh+Bj;€čQśÕĖ-H.kC*ŃC˼•Ü+~ŲŌSm!™ņ&#‚v«v§^„‰šäķ•ĢB¢Ż?šs’2öB”u6ķw)Ü }¹Lp{5Ń”ŅZŃėŲ9ß9>eɧ׎óźxÆ<Æ~ަ:—YʧxßĒĢĻXoL#ńö!ČV÷’±/éu‡„{\©ĶŹ3’AÄ-«£Ź™ł+r,²Ķ½`L#qM•%“0@÷ŠŠ4œ(6……õÕĶĆ#Ą—< s¤sÓ¹t€–°ĶŹ4Rvį,ęNe(D3*ø¤W „`•€RM4Ź)‚FlPé­h¹ÄUꦘ•»5iĖ£k€Vށo½R鵏ĖKtĀķā!X2@$R‡|£b¦ -Ņ;Ø„U*-ĢaM2Ē PökL—Ć|ŽvQ‰ēó½@%޳;…90īU1ņŽK1€Kt|@Ų@JµDG üU± Q–/pvŸJ¹šę;±¾…£ūNŹŚū `čo RMŲ?hõƒ®”‹Ü§Ņ¼Ķ?ó]ŲßBūż¬e†É(doI‰ŠA?«%P©¾Å2Āp#Hč€oń¢#¾Ū†øŠ³°ŸĪ…÷-‰;Zß̶ųb_06SHäŗÄ=.…‹×&Ę̧ģ’RҐé^§Ņ4rr¬Š-cQ˜8‚½U\āR>†½;m³P™iķP`rł–=«ļ nõŚŲ˜*NZš‘˜įÖ¤» īY]4—[3Cøfįl«€Õ.&Ū5Ś[µkóZūԌņ© >ˆ¦īMÅ|Čw—`+O0ItŲßCOI=ėTeŌVN•’¼øn ‡ W½^Jżj”ŽæY†€‡deŲĘån܊Ŗnå×SmŌpķŸ«*c¬©ŗœĀ=uŗ5­`ŹŠZœēœĪ$•ĶkÕJh‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"hŠ…ń*;~#ī(šYv’®¶PžŽ¢n~żŻ>e1m÷ čó•ė]« śh‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢/līnæČģóe¬YlUK~ēdÉJü»Øšqušõ•L’N°ĘØ- ŽĶņKµP{ˆ#°„6Æ©]é¢)m‹p­@1R6ŗ€’+¦ęhh§1;Ē:ģχÆß“ükTŗŖŠ ¤%X(„ŪFĄb·k`Št¼TŚ-ˆsähygQ˜Ā`ABn"=u»XݲśĪ;¶`Ł 9ńŌj‹}jė+¹-‰c©^Qøõ…rõ–±;lū{¹‡ó¤Dx5[= ©²U–ÖB°š"h‰¢&ˆš"h‰¢(żˆN/ł'•+źvK0c'i”ތö1 €x|jˆ9ŠSŪŠ”¶Žżp_š ɬ2N[!üė§p™®˜Čóå]¾™6#6L™¦R4fŃ© Ą#fé¢PĢ×&¤žR·•Kd s[õBf¬čą‰¤±t!æÉJ60/ėķķMĮ@·QLĘ~®C)†Q ŻäŽ©ss6‹­ļ!±Ģ•õ$³¦æ,Yļ’|×r‰˜ĪĒ›¶](u0zä8|"°7Ü-ål‘Œøį‡8Ü£ŽŅŅ¢uŖĻŽ™EH1»ŁźĢa$ػޔk'7‰ŽF¾n«WĶ“hWłÉ—j©ŹD‡PD~ŪR¶–ŗ“¦l–qJfk4ąAØ5„<ø,id€0‰\ŠĀ(q wK¹ĶIų÷ÅŹ‘Šč¼£šć.ne•2 ĖÕ¹+c)ćZbYCĪ“µRŪ-*¼•-µ­ W.R17pBȁčśc±i/·Ńųź{{Ų£ø‹+1jšĒ€Ź8‡ŽZ5lz›.uŽ·¹€™%…įϦŚ73IĆ{j <ž²œų”Y5bX<Šürī÷J–fŃÕ~{ˆžP9E3Œ glڼ$lŒ5H×ćÓ5hń3©¢­\³”Ŗ Ÿ³]MC•ęI,ķŒ‡išŪčZ 5Ķf6㻸 ¼GzV3²:ß笩|āM‡ń÷Ēż†.½q†Ÿ¶ó_*ó‡˜w 兔qD”XJüėģ©Xa’fČüÉĘ(4V#‚”©øEĄ#Ż‘o¦éV>½½½¼Dm!o}<źÄś†§yź\O<µŲ Ü{Ŗ|Š–ćēņ8Y5MŹQIÕņw7s^'I,ŗ@Ņ߁1 ™K¬µŠrŅHLC:±ØEĄ­\‹3IFęX‰.Ʀ]VŽźŪˆųŚÖ;3āYéģ‘ī{qa‘Ō°Ė¼Ō Łnm®8{ƒ.e»;»÷±aö„m©$¢¼›…+‰Rœę˜ĒiŒ&Īaķė®.J¶®Ūƒ¦«·÷äĢ@°žŅ÷:QEč45V°@J"SĘ(‰La€vżĮ YY ó^"¶xm›&RY“Œ»³\慣¢B¤ƒUרҗTé$M“!ßŖ©×T@½S˜ĆøˆŽ“ŻgśŅ"ßø{ż>§Ž>@Æf¢T茱1Qˆ}m¾½~¬ÓŌŻå[ÄŹŠī,_v†I¾M»7†¬¤õ(G  V ² N@ß`ŚUҽ­k[Aź iŽĪUŲ#{Ž÷T’ʕ4ØqÜŖ¦x˜DGķxż¢:ĒYkėÓP}‰œvöü&鿍|h‰¢)Ļā÷£œü†q‚ż™B1Éuk4ŪuõS<]-Ȩ̇6UIŚ`†+#ošö»Ųz¶>€IrvŌ=cäZ—ŻlÜŃ“F{]ź*ō–9̔ΔǸźĒ9‡Ś&0‰Œ#łÄuŅW!Xu󣘚 ŪSjč™Ī÷šV'hµUąŠõ[åČH "‘ )¦j§»g€śZÕ$Ék“{ÜVÓäS>%ųyöciw^ĮŽWD›ć€%|ńą~Õgž5† ‡ć˘΄D¾’ż‡fą7÷ ƒZ•ģ¾³¾”k±‹Ęŗc7V§ bØ1ÄD}¢"==zėR[¢üŃD\TźŠ% .t‡µ@zR?€*7:bh6īżĶ\‹^UjrD.#nRø{zöŹżc"­K»ĶTÓ°@=J~-«h·ń³"Ī%H¦āBH³]Ü$‹ˆĀ•™Ü±U˜pW"s\ö5ķ ąwżjÅĆr>'–‚ÜFŠp§VT,”°n,Ų0f!°“`É”€v6j’ Ć ģ$÷j1Ę®'œ©¶ 0@<ŠÅå£ÉŻēŲa&e9^e{øXܝeŸ·Æ@Ż"RB¹™ ¦%¤™”§tį@IŖ˜©Øq }"ÓĒ—Å&™ååPõń·‡įĆj^6õžexa9Īq „ę1„Ü&ŗž}mėE_:"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"hІń&’‹~#ž(" ĢaźĀ &‡¬Ė¬~¢²i&c¼vé×Q7?~ī•1m÷ ^µŗ°Æ¦ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"ń‡āŒk¼†åŪŽ ł’Š.R"¤&C³“ø `!Ć~‚}ŗÕx¦¢H÷Ļ‘Oh8Ė żåY£āĄ¶†az„“)‘a1j‹nuŽÆ¢…™’Ģ$‘HTÜåE)Jį”…ʶ. ŗń“§@}Ø„#©Ž°ļŖÖxĀßĀŌ›8ödŒv“ÓÉE+µø-MSö€’ŲvŁ$Ä(„wĪ:­žŠTæŁ*Źk!XM4DŃDM4DŃžÅĢŁägšąK:…ų‰$ŻÕņØčĄ=†ެµ÷Péļ߉üׅ¢kyž“£xź]ƒ¤­«ć;õŚęķs„”ןYgV“lFķŅ‹©'Ŗ€ü³D0€Ɓhü$ Š£Æb‰ó<1›Ow:čph©XĢĢʳBFČLIؚŃįƒµ›ĘĆś’Q«dŽŖēRQÉLļµŗ,ࢂ)Ć¶Śž·Ó#sƒŅł ¤€‹ó*M°ōį+(ē½äXŁYJÜŲ‹œo‰T3õŪżs †–‹ė$ŒUģÖ+"BW…bģɃs¦ ¤w=ęKxh‹Egƒnꋦį$ø`w²2}–·apõœkB@t}OU’ęCd¶ÜŽsčSߎ~>l¹ęžAÕź˜ź)‚¤#‡1špQ &¢ e©Õ"¤äĄ^ąH€Ŗ…Üą@ß]Õ8”°QƒI^y\ā;Ļ”@¾ą7f%XŒK€ņ—ł/žˆ•Xł–Ÿ•em/¹Vó!N£äü™huW4£LR.ÄŹ»g‹·EH·ZDWhżĀ.Z::f+a ¢GXqƒk%ĘV“ÄɘÜĪd‘¹ĮŃŗ¤Ņ Tƒ†õŲ¾_:ytĻKx}är8MŽZDZĄd{pvW“‚ „ĄÓPŁ•ĄųńåŽÕɟ,éķå+h—ČX —PäaāÉ,©¦ }-—5W“jB“,ღ®8›ę;ĄåÕ ²ābŅu‡>˜e‘ļ„ŽL¹‹³ ŪA‹e»‹Mµ/øÖ4`Čņę2FÖLŃOk>PܔŪR)LjŖ:ÅśQ‰ĮÅ ģ@ ꨰ—¢½ĄŖ.1·§.QŚBŹ ūzĖlŖ¦ĄĄńŲój1L«,”ŗĪs+€J…Ćo‹ł«ŽŌųĮ¾ą^Ÿw‰Š@GÆ“=ūF€ßį=ÜėLāw6žœ«‰­jé¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ©?£·āFā§’Éążž1[ĆQ7?~ī•1m÷ čóÆZ XWÓDM4DŃDM4DŃDM4DŃDM4DŃDMxśųń¤B^yeĶč9šOŠ™³;`隮’­%–f‚¢Q$¹;\ģ$1D>ĮŲu­q3­£Æ¾|…Nhf“Iõ<įd;;,vh®(©Üm0æ`&=Ī~ķŻąY@S“{l3¦Ų¢!ø­ļŪ¦Oø‡]3wØx(Ž6mYnżõpī jė¢-q3Äõ!eI’ĪK˜:oŌ…ļö­ÕMö‚ńŽÉV+Y+4DŃDM4DŃDVRøüģy›ŠÅ8!)Šh²‰©ŁšüÄG¹Ē€§Ś±¦;~]õČ>kĒX-„ä.¢¾eæpc½I[ś@łmł…“QfxĒf\]CWiM›&®Č¾–± E0hŖ²FA>ī¢R‰¶÷ėiŠQ¤ż"ā:‚ßęvĪ@ņģ¤žEs 8čī–Č×8*̚`a) UD]Ł­1­ ĮKÕ«®Ś˜”°v8Äzī:3DWøī"sĒÖĮ¬=Np=JV˜Åd÷ė;ß̲gĒMQ‚°ĶGG8Ž9š*'Sü^I«dbæ ¹”Ÿ9a„#å˜įéJ®-ĵkˆ‡‰HE6Ź“8ŗ*Ļ‘aڽlŠŹĒ‹ˆö/”HŅK$c¢mŠó§–ÜŧŁŪŚÜ½¤Ÿ#€"‡ ~ $W+…*wPć~#Ōķ •ÕÌp Ćåžŗ¬Ŗ1fĪ-©F4mÉ=Å6ŒI£būDØ7"i‡Ž;n>żh%Ę®$žu©,Ay–DZSĀ$™Dē0žb†¼Ea,ö7#Š/ļœ*Ø/•nULcÜĒ7ak±Ŗ«¼Š ¶„^­¶d±ƒß"P§ `jx6R8{NõG”ł”–•ońŒLś-«Vάq„L"aź&Ź#øėK]h9rVmœ¼8nFmœ;9ŒF؝só˜ŪU4fphŚJ„Ī iqŲWyÆĆ©†Ļų{ĖöĀ”Öhʶiµ×PŖ¹‡ 3aŽŁ =Nģšó‹“Ü"ąĀŻu=yOpņÕqN"œĶØe÷[ŽļXłBĻRI™eSD¹ÕP‰=›˜ę”7üć©u¼éłķœæ¬‡4ł5—¹łČ l·cØŃÖ Čj,:8ŗØ²ø‡ĖŹ2§‹ņmŠ~tD=ŗŌ.%ń®$—qy `<‹”XĮšÖQD} ĄOK½cå¢Å Qī–Ģy*Ģ²Ø™£ČšœtaŖ&”E'H‚ŽĄ»'óJż((ę". ¾ŻĀÕõG꒜žLŻ£²s¹©ŪŠ«IFŔ§”’ŽŒ!’@ŅOŚ0)÷öv µ‘žęśŒ s½J˜sŲĻhŅWā²qŚ}Ay8Ä#ū@ążii1°Ąšė•°”}Āė cÉ ½ āFbį—„~0”‹–HĖDŹFJ¢Qķ:±’ ä ˆģs³YbDC¦āčę¹¾Š#„ö?Ų ō­I4ĒéŅdP¦.ń’;€€”vKż”ļ׬öŪŅ<«É~ķßTłÅįū›ÖĢs[~3ī6Ā%żmņŖ@7„ĘŪW#ūĆŠļ!V¦?Įm}ęyBÉŚæĪ)ü³ū”:‹SGj²ŃÅf|žØ€ö§KčŠ"ā®Crp(ūĒuGóėlŠGż³é-‰OżćGčqµ8µÄŃDM4DŃDM4DŃDM4DŃDM4DŃDTˆ‘Ūń"qL~ܟ4æĘ{ivŌMĻß»§Ģ¦-¾į½r½i5a_M4DŃDM4DŃDM4DŃDM4DŃDM4EäMćÅž»āķß³źWį°ö†gw¶įöģmk¼Gż4ęyŠšŃ>ż’SĪᣓR“Dl”ʙֆĖPjåŠc8ŽR§`h@sŚ'@¤F!E»O׎󁟖śā.XĮģwēXÜfŹŁC Ż%;GęSG]-s„¤ŗ@į7±tUD2¤1?±ŻÆA”ŖE{:gDēE@#™#€ū@é˜H`żĮ e,eó¢&ˆš"h‰¢&ˆš"·Ī«üœĘsmͰ¼Ć×d[¦)±WÕ<…Aœ"f\:t%Œ"݃ņė—|ы>™œ’Ó“9n¼śI37Š˱K »—āoUˆˆˆµdĢąWf³ōŸ$bƒF¬ cXNb®D\+¹;>!CŁģ%Ž$ҟ–+ 9Õ 9bjŹ™OģkˆTćńÅlI;)H„žBIQ&#¹ āąz‡A™OķŌęqa¢ŗéßy;ččĒ’Ü>ĀÕx‚ą ° Ųž½‹,’Į6źļy{ÆĆ¶Œ§×0VK™nC>Ee¢¢«RoL‘²Ā˜¹Io—8˜ęPŖ-ø˜„ķ­cFæŽ^&“ŠW;®c0©p ü¶-V7Vv×n`§WŒłyä%ĪraØRµg'ŽŁŪŽ3uj%e÷žĆ+2“®ŚI¶ØĘzī”bŲÉw+ŗ¾šp\Q¦ŪéwęŚ9$ķ4yh!¹·\M6PT× *mLĮņƒS½d{Zʲš"ŌE1YdQ/é*Ŗi†żsœ }ŻG^ATX[kŸ%¼róę]ƒ”ęžf Ė\å¢®CĪæˆĒżXˆD©cø“‘ ż‰mÓ·]Ītn-ŅĮõE¬v¬=4kžå•ģNĮøO•`ŗP“°Ói sQ’éˆu$›ļ‹žÕ}}·±b‘”–ņkņc±cśį~$Ńy3x‘'Ų…u‰!¢ĮżÜķ™5‰żÓAvØv/kzOgē*”„nvünŽą¶śŗ¬&ˆš"h‰¢+U“l(Ķ8&ĒŁgÄߣÕ6E7Ąˆż‚åBī?Üņź‡ʦ…m2Ā!¾8£lb+O”…Ŗu3īĖ1)aUAhÜy^ €@_(×õŁiįŪ Ć1é;;ض®‚¢[üåo}U­Ö¼¶µĘŹ*‘j”NšŖw®‘ ÜuXF¢“Ō²e/MÅh˜ÅÓ św7öė.əī[Č1=K P“Ć“w+½QÖ½<øY‰‡q‘d ŽB•…h-'Óvļd“kÅE °Ŗ­®eéæQ0ŽżuÖmcš­™šŃŪ“÷®{/w,ۜóNŠŠw./yąxĒĆnKēfė38ūŚÜŌ70ėä RŌqÓTwż%œŽlå({G}yw7lłw†štœzöʉ¼Ž ĪxÆ@Ä÷¼ßŻśÕŠ”Ņls½{ ‹jDŹČK£ŲØ õ1Ր”P‚qö‰”jŌŽƒh’ų®‰ķ?˜•^xØ …!ąĘx‰ē‘|Tżc‰źPmźĶ^1֏ĢWιCoˆMļßZŻŁ½Čqh -žÄ9šy‘ø9ĮĒŠ³ 8ßǚåb ŻЬG•‚‰zāć; q™³‹Čöī 4źvĄIwN>Ŗ*ŠĀ ØTƒæb” Ć|Ņ’A$vl¢Ē m+JžUCd>āLsn©ņ>™…)rŅŲĘPeot5G·ģpńŗń÷?”©8b“/ßŖÄC„%aÜ7A5ÕrĻŠ7xØNß äš'[—\05ÄŲņ…y•Œp†ŃĢ­'4_ń/+ßé-°›Œ_J†Ø3ūŁ–ó†4^¹B%‘„¾%źø”“ä8Ę1•~»<¤©Õ*«Å¢‘{\B“Ä^ü|īq4kSJmw7 åWe19ćĀ! m# »”fhĪoć@0 ԁ›Ō•łł)NĮ½eŒ|ģ”s¹UܽV9õ…7ŠØCØQ0ę¼ŗ¬ń~ōmķùdڶ°ÉJųf“ģǽrÄmõ„“DLP{sÅ%LĘ(ŠC^Ŗ‹Ģ]„w*dÜC^3 Ģ×y æ Ļ 9ÜĻ(Y68÷ęūLażńŌXSJĶÅ,E2ęVL†Ü[@b„VßąTģ-ī@£ÓnįAro°Į­·Bž•ßYhÜKżhś\-M­u4DŃDM4DŃDM4DŃDM4DŃDM4DŃā0vüH\Q’ą£.ģøŁlŪū؛ŸæwJ˜¶ū†ōy×­6¬+é¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ¼‹|e÷'Ķ.u$šõ“¦°SĢ@PżŃֳğwÖw‘Nčy'ÕU?ótkœ]”ń¾iõ@“ĒųČ ĖƒÓ$ ;Ō<…ŅoD ō¤&„:QC|)%ŪlQÖ' ĪŪmhg4l¬-ėĄŽŚ+œMnė!Į˜¹Žź|ŖV˜¢S¦ ŒQ˜Ś°€ža×Y\©~hŠĢŪY‹I·Ł7€Wd¶ ŌųVüĖY 5j°ńG*kUŖSDM4DŃDMFœŚ$c“ųė*a!>ro(ÓĢc(”D’\ĒēŸA)J§{ŖqŪpļÓZĢx¼Ns÷²FŸŚζ®~]AĶ÷™éUA '1H_Ņ9€”īźaģŽ¾]!I/UB]ló–ƒgVÜžEŠęŪæīóŁųŚ5]b˜@5>¤ŻÉ=Ū8·YT’„¶ŠŌģ‚6ƒõ²—æöÜGRēŚ¼Ž5ĖŽ=œŌe[ĢfBNŸÅjæėRc'É»¤&H å¾+‰f{PzDĆØ3F‰婏ģõŻ$_iµ«š=·¬?T”V;8̵’Ø}XĒNwŠ µmeϹ¢½{»ÕÓńĻt= *ojLEĢC V›ePŠ`¼,›v”Š TŪ Š„(óe0‡BŽ£ø’ ·LqõOÖÄW½Q8õÉY,Ö“¬¦ˆ¬o&,öŠ~Ė“Thł ;øŅ&”é­ćQQU›ŁģmĘaʄجā뮤‚E惘¤A«U &ŽGH† õ8c¹!¶Ž sÉ÷[ė9K€ŹņB®0 Ęof«¬TŗrÖ“›} ĻŽs-]4ģī^vj%µ½$EĒjMŃtņU“ņ香{…A&ŚŻxõ“Ükź”®šŸ†Ąr7øQd]‚eå¬,āō”cOB¼ŗ„YÅ"ĮuĒN!Šr˜ųīé`„„ńD¢"Ņ1Ļī×ÖUĻÅé–÷;ß ŚŠU™Å%<‡ŃUovY ÷.Qļoh\m÷(©ēģ&'ņ¦¬æ.}æ„Ėażf3¹Ū…œłU¹=XŚŽZ»·äļ\Ī®«)¢&ˆš"āę%šĀG8’xoÕ PķLė¬~‰ ˜“ź›§ä ĒŲšš/F*Óbč&ł7-×ŁÜƒx2‹Oܞ˜ŻØĘRjģÜXķNDćŃ&ģkqK€ģ°{õKsĘogičžä–0–ū[IĄwؓdĻšI‹>Q“}nLƒkŸ·Ļj–dbY“™“[čQ1ņiÅ+¬t q&1ķż5„¾ƒb~Ķi׎}ŻŪä„Ä쨨°ŚŗąŲŲG ƒ€ Ū”Šę“UlœtĖņq/›HĒ;(³ĘŠˆ*0ąŠÅ:jJrä0Cm`¹®c²øP©6=²7; ZUėćv:ĆŹ.7āpLU&EĶ8®ˆķ"ˆw)vÉU(‰²vé&4ö³8l;1ŗ¦4XŒ—Ü\Ööœ{”ĻąŚ×{X÷v ;שƒ‘EÖ:e&uT2d(lR&'! Ą) °{ƒ]IqU×WńfO»|nĄœ}`ļӐĪY“·+ DϲŠŠ08Z•ČÜfŽre†²~v”µ‰)p®}OCqņŃl<;k™.ČŁAŅģ<€®öIdāÅ¢Ŗ—¹ę³v§ę*,ŖŃ§v™ĪÓsŹ8mŁłJ#īŌĶ}ŸmĶ{;Uœ«7Y„n[™ßŅ™øz*©ß=HƤB›}Ģw®TūDzėO•Åņ¹ü®+v1œ +ńØR“šœü €wÕk(} Ģ– 0ž}µ­ń'ÜEõĻ‘Nh{'ÕU•IµžĮł=ótž2 e°@ ć\9H %£«s]ģz U§1ģĮąƒĪ(¹MT©M4DŃDQĒ‘čøĄ²@£t…–|Æ3\vģ OŃņ©&&ö,ēē2{żCZ‡3? ÜįZ4Ć_2ŲxaŁuVŽV•Le©Éö9“<„īśūö¬Ŗõ³E;%ÖU…:ĻĀ VrÓ©,#ö&:ąZE».u™/Ü“—»źĘ Čė §Zé’ų6Ļ“xo—#2śŠŠu‡ŲŖb‘=•z”N…»ĘY¦ø;‚jŠŖüm-É-n-^2@Q;6ĻūN* ) ”&K¬aÖęx½l®ÅĻs˜Zܕ8¹Åć.P ­Hę5Įh%‚RC«ĖłUkĻß|¤s£,вž]įr'ÄøāłJ©PńmĪ”ĘÖI{}’¼īĻuZ¹k·¤2NaŒa eŁNc&'õ ģPpve%…Ž”GĻ+ēČזѭ!¬ĢÖ`*K¶vģ^o KüIVK0#0NǐQü³Ć<ßāåž¶ Fžßkć®\qA‹‹˜Ń“‘w¬Y^¾ÖTķŽćęQ0{„†éŖ_蚅õӟ£\i÷–ļĒ+gŒ<ņ‚É E;ā{œLNcēļYÅŽIx…ÆÖyĆĒ«=‘T`T§ņ ~‡a˜~@ōŒcĄŪYp›õĪ_‰$ž]kw|'ÄÖŁ¤—O¹lcU…ą–ęƝYu¼ķıŌčRĪO)bųXy› ®KǬ`ėµéKlōŖ·ZљÄU”ōʼnś‰IØ(BƳ سL…ŪqÜ@–7Ņ=±2Œxh‹Ž£ §pVĆM(kбGČĻ)Y¬Żr~1Qƒ˜łg:f:*”ɬ‰ūl± ’Ä"„1Ŗ1ģ•‚HČ,;\~Hä‰ę9Z[#N ąBé[ē—0'y—¢6r+AńŸ ćģdŻ"œLŠWL˜*ę;āæ¢W?F—¬7SŽ*>Ķkzœž%énčŲYõ™nZ>œ$;dy=CÕu€Ģ“%ėÖņJŠvł]ėXŁ“Xäéū·sļā~¦Ź=© S9|ėļJh•2ŽęQ.’d5ĢŽey c[‰8ARO0ߊ¶xĖåŽ6‚\ēlN4S¹U±īPl"”ń6Bł]»½T#a™ ÜxÖVą%ö±„65Ī›Äœ:ēeÖłŗ\ihė /Vh©“Ÿ/COpu{”źįC—*bė{7Œäc]Åeۃ%£ećCŹ2¼)ĒH"ƒÖęX] ‰ś„(#”ĮšˆjdÉŃĒ,kāsiiÄģ#µÉšö\HŁę¼?ąA6ƒˆS]ŲTÄD}€pÜuJ”aźÖéx®9Ī –;’«½‰n©Œ`ÆsšR!˜ęŌĀt Qą”=Ŗ‡­{Šźöcę^¹ ŚE;UØ"I·!¤T!H T‘(&™@ B€téÆI©Ŗš‚œ‹ź‚ÜŽ22U(„’aģķS8‡rģĶc—P©Gø})ŲW""±{‹æ·WÜ¹’ōČļČV3Øl_õC‡AžšVH5¦Õ‹¬åĢYŌ æÅū&P›Ž’ŅŻ§ŲäšŪō?é:üåh|Gżź WSS*4DŃDM[l•+8Å“Y¦Ņ–‘•ŸUėzä#:‘™q‰=).vŃQķŚ ©NŖĪœ"Bwļf­É#c„kS°+‘ÄékJPm*4Ėr/+ȉĖT”T›˜Ćé/9#7q“!=Āv±‰ÖāJ°Ū”T€#ķn¬„;@ķY"‡“\{•9 ›3ŚÖxų„l˜ęq7±Ó. 1K}ŁF:‹h Łæ6gŽO((Ŗ‡ķģ7AÜTxӇPpäW “X‡5ĄņśUŪoņ<~ŞÄóe›śeüÄŻĘˆ—x«„l„ŹaąøT@ß«‚įćŚmz„[6­ś.ķ z&¢—tę ĖŃ*ö”{CAZ1Œ R8†ž7žREŪU ‘ōšą{UŃۜÓܹ|„ÄFسέԕw1LĪ‹gŠL‚S MÜł« ą( GجŠu蹋}GHT›Y†ĄAU¤tĆGm£į2!ó÷‚Rµd3­YŗpsmŚ’(Č Cع·č˜nqv«Dć@įUA‚fŠ–š+««ŠŅh‰¢&ˆš"h‰¢*'Ä·Ćųųœ?nUwžŪŽv`’WQ|īŸ2˜¶ū†ōy×­V¬«é¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ¼vųBĢ®_O­źČFJ[­ˆŹzc»…Ł»ÉóėƒŌ€ż½īX  ŪwįŠGZĻ;,Ž!żŅ§t!YdśƒŹ²éœr-vĶMcZ™o$œćĮ4Ŗ(ś©®‹č˜SAā ¦Eõ(Qķ0n>ŸŁ­6G‚ŃB¶Fƒ\VŒÉ(¬–ōån‘VœŖ K8‰óę”5ņ€˜­·ØQŒ€%ßaōƒ¦¤34ŠŚšG6Īg¶¾V÷¬<”·¤ŠzńķēiōåYЇ0 Ls£ø La‚"¬”øĆÄDC,¤DJЉ½C*¤cŖc€mŽe €œĒ ·ßm\ŹÜēŅ}*“gkZz‚œoćŻ.Üé«yŖä–Īcr|źBD¬?"c& āŪģÕI˜2ÅõķmÜ>ąÕ –g?¼ČćéUćĖ‹Zz‚¾¹ŒÜ)MB½B7[•£ ĖČÓ®¬\”„t.ź •+¦~¢T‡ŲÅŌ„½ķ£ƒ¢™Ųn'7—ĀśmĖKe‰“<‚Ÿ—Z”ńõ¶VŠ\]l¹!&õ£É o’žCzfŠĀ4‚¬„Nķ’iĒżČ>h®š[K°¬ ¦² zw ń5†%Āń‚½#”.oÄZŅÜ. ©µq§Õ?—åÉ~µµ-]4DŃDQŸ•…ųņ: (Õ³®›1Ó"JŃZż^rR‘mÓ3yó{}žŠėŌ 8¢!6ƒtóĀwī•1 ?&©9ō*3=¢č˜žå$Ä7ØüȾ˜*sެqpHɛōČŖuóC§C{C_;h/kuXZļbZĘz$ig”…Óļćń-$o5{1RĀøhh‹Õ+GCEHŁ"˜‘)—Ø‹„’€~ Č“Œsƒņ@¹NŖū[µ>ó©¦ņoöư{œ!qÄ =a…O-9R“'bŅƱdoņV5œĖŲY±§ŚÓ3I…Rł'‰(*ģžeQ#) šž™ś@@5+Ķ&PĀĒ7ĄļGVŠV+¢p”dŗĢźū”H•¹ŠŃ.@)äźļ‘ŒsømÜ,Lč[žĀpÖ±6 ?ĀǙ‡¶ŠĮaÜ­/%3/§)SnrĶÜJ»UŚ}O6P±‘ £×tŸ¦äę27ō]ĮOH‚cØ%čBCKµā8ēkl$ŗaŅ'ÉSŠ«`”P»Ŗ¾e‚>ńēÄžb¼”lVŽ÷»>TĖP—ųyŽ>K@Į9oėŌjõ†=ӆÆPŠR^Y±ØŻśļ C"-ŅHŗķÓ]ń ¼PLéĆcŒ5ŅNrq..#ur·€6Ö«½p~…ĆRéQ§ĮsŖČĖ\ᙄ †ŌLsTģ"vÖūՉčsĻ5ŠŠ€ČĻ­Iøøø‰* ļuFźČČQH¢åb“潊g ń“G —ēKõT®  qcŸLYBAŪCæÕ„O8;ö먛+"­µ p¦\Ķś8J)ŽR6Š.„>@ēÄeöœ®ĖyÅ~9ę،!›šĪG52&(ųŪ,r÷ƒ™F6¹Xk!G˜i"¤£L£ˆ/°QŒ.ŚE“kō”Œcl"Ī+«abŲöĪ‘¤TęE+I$8R™\5Ø$b0׬4ķFWŽk2x7M¶••h|š84 mõŲꐭ7ąØ/›Fœ¼āCž(}ǾAd¬Ę•"±e“‘fÕÕ^6ś"啛CܛJ.“ÜL¬±QvŁé&ü°˜ :…į’–réÜ[o©¹ńæO‰īxۘJ¶”V¤ƒJ…ČßošåÓTeh4å©Ą(ģc ŒcۘĀaŪ n#øģą×|QKóDMSsvČXÄ]ŗ*Īvųµ1tqžč nŌ żŃÄóėĀ@^€J°¶;l„A*ēłf8™ōL>‘ŠuĶŠ\¬ļ7@ŃÕ²IčU€„Ą c¤1Œ`)JÓĀPŹ":ńz¦ż’®ņąØHõ¤ķģjņ9ŖŪÕwr£iĖė}ŁĒ-Õnˆ@rމT’\ $M@6ą6µ.8¼Ž×O‚ŅGµĪ2<“@ĄW¶«qą+).õļ#i{šŃ$“SO"¾<ų™«bP8ģŁō“7Ī_š‘źXĄÆĪV)Öy…†Qr·gÜ¢ąłŖé źˆ&QT»€ŽĄĮߌ–I\†„æ˜冘ƒE§ćēÉ^nāöNZ©XtŚ‘u³™Õ‹ßmµØzVq#ÅŃ#i”'cV}^ĮrÜāšĄ_ Z•"m䊌ž’[h÷zµ“c7„£ÄĖ„ovņŠ}‡Āy0 ęĶgO°»•’ œZ‚|2ģd»ƒ©ķ°v·“Ø”œ2«3ē~Cg܏ µBɘs6@ÉÓ7ÆżåӔck4É餂¤)“ˆ \ؑJ‰—nqOąŲF¹ēČłdsœIœƒØv› Į«XĄ+ĖŹzÉŖ‡Ģģ_uŖøŽŠžJ~RĶ—+Õ^!$—ŸšAäÓė²ČF2X郄””Šl•0ō 9@Į­YĶ¢Ļc øø‰ĶģĄķ¦4墚Ó.#³Õ"»” 246Ѥw¬˜ć¼·ņ«GniSé¾w`Nnæ ŻÄ=Ŗ¾±ŪčĪ×$ˆ„¤y»Ć“č(m|Ć«hZ¦‡/ƒØÄX ĮĆ;”Ć óeŽ4ķZĆUÄ²:›FĒ–œzÅG:„{™šż²ėh£V"3 ū>ŅÓ9H4²U<…Y™B“åjœ¼‡Æ_±D9ށ@’$äø"„Lāó…ų»JƒO‹JŌ«įikd¦f8]ėSš“#}Fŧń _Ļy&£eID†„•£ +se½_ŹČ™Xh/ņ9[Z©(‹vÕXZåu»Ć¤`LņWsŹÅ7ŽMCN±JmÉŌ~šŻ_¬čQEć¾śŪĀø<9ŻM±<ŌZ«4]ņˆ[k>rw“Öć€õQ¶æĒ¬±‘kõšvkR&‰I«±‰±ŌAfmwYų4Ņ,CērēŠ4]n9Ņ%wč'óNrB˜ P0jŗÆiöķ.Ń–ķĒŚ‘…¬h;pØs‰Łøo®å±iÜw+Ēā„GnѱŽĪ;±”³åU<ēlHw©SæĒJQ+“™ø0{Š3õU@ ?Ę“k Óę,fŌ-Hż(ž’ʤēį)Ū„Ąw4’?įVP0¶hŖeĢ[~u‹œOĘŃŻŚB]Ķ.×W™rę>v¾ę1Žc2öÆ ą[¼8*b(’CŚ"ī1¶ÖĻoƜ5q£7':˜HĒ rŠįŽ ęįķj+˜ę0µ„Ō±ķ5n“÷)N®W¬ĒlˆĖÅßĀ5ʇiŠbŸMǾq“tt Qö›ē;Ū¾Żu›oyawż%ż§‘²6ædīåT¢X?©Šhž³hw«Wn•+ndĪĖUģ°¶ēC*Šń-_"į6µW,ÕD9\ ŃŃŹ’§'qT݆7Mo+\ĖrĒ‚]‡¤­ _{$¼ń#!Ģ ÄlŲ’ŌĀ‚M4DŃDQƒiļCWoęģ“ßbīµmčģ!Ü‚Cģ÷ūµb}­é>E“mōŗ•C˜G/\-$ńņؘ;xÅ¢Øōć,6“PčˆzfPY‘Ż *v|[ė„’kłbV[›˜dšēÄ ūG7"æ6ł„Īrž›ČQQŠwY³jOCDĀEĄ1Žvå*‹„HĪ&69—ÓŪ¢ š;L GøG^’ „ Ņ>Ziü-¤š›¦pEĢ÷œ)–ÓĶ<×2ČĘÉ s¤žćJļ8.~8P`ęõRŽD_@s€ @ę“Ā?œ7ŪD\\¤t\‹mec£Ÿ²;w»ym]61#Š‚tÜ$rn">ŠöļÆʋŠMpR«‘DčB,¢‹*JULŖŖ©Ģ¢Š(ąsC‰ŽsŻDDDGŪ¬Ųžķ½Č£$§ˆźlĢ|Ŗ®ÕJ„ŃDM4DŃāQ[ń q4…8C(>[qĮæķ+˜›tźr¤%÷ź"ćļÓęSßpŽ:õ„Օ}4DŃDM4DŃDM4DŃDM4DŃDM4DŃŠg.uŖĘäJ¶ėDl²Æ§~]yłvĶßzžĘŻŽ$Žˆm”°Óēģ)*Žeå\G¶ŹUNÄєžN)‚+āČ(čŠw&™€;D@Į ÅMŌķf²dE”šÉ‰<ŒqŁSČ„,ōFY\2ä<—5Ć )‰_¹¦Ģ\±”k0Fćż³ę%|r Ķv/“Q›“US&B¤ŗ+‰āųŗŚįŠ:F½Æ†¾+H"˜F õ½<w²pQɊhUZóȘ®Xe,dŒ|_Óėmr± «BV§fń|ŗ2/!ٕٹVõą m¾­©ÜN×ɧĆ5M\E»³8o9±žZR»T4šfžēfó¹»z6•Lć›Ä żøāO”¶m>ā1&6źÅ~»+xp¼ģPƒĪźöfŲzUŚł…ŃŁ(“”·X€sRŸR׌ų{—…ÓÄ@Õ®{ŚĢ#3LƒÖFP‚ĀöW傼n-kˆ=Š¢±ŅS€ĒvŒ®’só"cź¼;Łé›GĢ+c:ųĀ t\ÉG9·WlņHp(v#uL !Ś;:µ“×±éŃÜél½‘ŁZŲ¢ń]]»CK[ؤO ė­µ}ä¶ę;hŪR\pč$ŠŽŌ›ī9Asεāęĉ$«w¶lõ)Č2oUrŠŽČ »ØGL®š› Īįćur€o·v·aڌķ„åüĶgč“0~Ļkļ”{,qyäm=>Exä(³¹JĮMÉ0Ö)¬Tė\%Ź (ńʦś; “–®Ŗgpj°»žŠįƒC2|Xģ–?zDŪ·mJiÜ1 -72ĶšAkˆsqŪ…6õ«LŌ䓐I‘“4‚ŅM ąFś¬–ĢyäC6‹"¹ÖiģŸN`ŚĻ€łóŲŅgłEEČ‘JMMĮIå\łĘÆ[B“3jCt‘ŹFé˜U?śćój’īEčo*·’—+·qH؊#ø|»/興øŽ‚ŖņŒ:¤’UT “p²-2ī×EŖ!ø™wK&Ż ÷‰ŒŖĘ!=DDuį ŚŖœ%S Žé‰()}ē†p°ß/š²®Dۘ;A¼X<\M¹6ᄄ2Ę>ņłĮǦœųyVÉÖFśZŃ. ŖW+$‹ÉČhČčW]G1’°IH¶k£¹ņĒ„‘‘U4÷ōŌ/i‡pßV.oa“×3T@ʗ9ÄPI5ŻEvŽŹ[©›oi™ę€TIĆ oYž_ˆ÷ Xę|ł”n 9 xfÅ{ĢĪ,Źq«ŽaPHÓ`éÅw݁“½&Ā8\ł…ą³Ł!a«¶·Õ8€IĒF ¶ÅüpįεūYm5Oń£yæ]¦2%­©€ S:ZĀņ@±JøŪ&”»‡qjڟqÆoš—rgüøŲ#aé 7A$-‚ĖDŃōł¾&fŗ?Mī/pč.­:F*M›)PČ*Yā ]öż[£ø³~Ä9æ±­wĄ—Ż*cÄg(Qw[|IćĄ_³ĢƒfÄQ÷k=²(ę.vįS¹ ²L(óĪć8D˜( ›„ŌŒ ™z—Ė+·į;Ł™Ø5Ā ‚Ļ^€†–ę5=`vŠ )ˆ¦+A掛‰,£ųG,AŽ­hMhFZąH#a"¼«ä­“.eYbl³€¦ēÖz½ź­• żĶ|āE#«&HC1‹}Š|s “”hŗFī*Iį×Ń1ėš&Ø<]>X܉£šį0$…Å›¤kwšÆŲį—a-sNēłbT“ȐS ä*Hfˆy*[ąQŖ„†Ŗ\,Y,ÕēE^ŗ­RtÄ冔"t 쨈÷Ss’MD’“EŹp4ä©W™Üź²2_ͳ¦VŌf‡I9ÅO0Ū„Ī̬JjTZG˜ Žōå£8ˆņ€Q*"R mŒø×ō <Ļs Hå{kŁZö¬^±A+äi§m)ŚT¦Å‚’/’jłZūTeŒžē3šF6!)čéū”IøåbĮ•ü-Z5Œ¹™g+*ģ" ƒßĖøćŽ4SNv•¦Ļ#4…¤5 }Z€K° 4 Ä­ū…8WPÓļF”~|6°0—)ėP;+S† lk®æM¹S2§ÜئcØqķI2‡Q1ĪmˆB‡¼DCA‰ ö—»NÅnå2Ī4ˆ\ĢŻ]ą?(‰F2Ѭ²Żįü„VŅ–‘ļīƒRÖś³t3Ćm/‡ļ8doŚ~QŽ£¦Õ“ČY'?#NcŲŚžåM¹Ģ„[į®cūŌŲŽżŽeĘRcMöOhi6īcŽ;{µ+ Üķ»ø·ˆrewcoķ¬ 8‚–šĶ!å F?h꿕EX2~Eh€¹’ÅĀ<Ą?ŅęžÉې?øVAÅ ŪŲŽ æøu-kĀŚY4­ŻĖł{„Žļ >}vü ’ŪĄŽWóߑ½ÅY[ żœ“l›©¬‡2Lc(÷ÆåXćXŚŻXcš eŽg“hZĖ7MŲ¢&*³ ÷Ņė­žĖ‡[šml­a}pt¾»ė’˜]CŠĄ īuwLŅ..n%e [,m?P6£„ÅRk¦Väl·qÆc§Xź8"±ķz½ żF«8,t„yKł2œ„«bż}ĪDį]ˆ%Lź:w ¶&źA,õ ølŪ³`ٳbēzćā7°0ĒŹzÓ·j™š›P‰¢&ˆš"hŠ;ņ5=ėõ5Ē}’“9D>Ī÷5©£”D6öZ›Æ»V'ś'ŸĢ²-öøsy†±»7{&ŌH”L»§ļŅå2'”tcx|;ž@= SśĘnÓłoYŽÅ ž[K[9‰w†L»VÆŁČ~›ŽÉø®,P÷lb½w“:üĖÖā ¼śõDMqÓ&!!¦N”ʚdˆ”9Ō8ģR¬Ē0ūŠR†ā?f¼;BōmR檐!UŖ PŲ¬WPūfIķŌDƒ¬Ö`Ą9‚‹¶zJēµR„4DŃDM4EJx‘ųD}ł*Oż·m”ž®¢.>łß–å1m÷ čóÆYżYWÓDM4DŃDM4DŃDM4DŃDM4DŃDMx”q–±^²rc1DŲ£adš|€Y¶-_·L>ż¹!ș"©H»(&Q „DĄ;źģB¤Že‰vāÖ“{µe¾ććGŽłVÆ5_Œk-’,ƒĘvĢse!|™ÅSµ±Rąe˜ƒ,D¾Qq l †Ømœ„¦BAõź+Ļ.ĪÅ ęń‡S Ņķµ(ŗėiI%ĮZ*K>>?¶I™¹³~°»«Ų^4leA‹”’ZTĢ8p“bĘź7¹ÅęV³m@ĪŃÓL[ŽŃ³x[Ž—{o+Ö“Fēl”9IķĄó¢µJ&(ü"`īD@±­yM*hŲö]Ó×SōėEĶ®ršU4#<­ŲLOag暶`Gą_„›7„‡Iś½f2ń„‚ Ąd„l5õŪõ\w~‰Ć”c>܇­ˆd§oŗī‘ĖśC•ŗ©ÜģU¬Ó"rMj^›ōėŒTcגķä+'²ŁŕŠM·QÆĪ3Œ\©üŹ-U1”)L=ŚÉ6š:wZČ$vf(CƒZMIb¤0V|iEäBį™GkV—)CŌvŠ©ō €€‡A @©EGä†D{Žļģœw'tKjF7a5«’=Ŗūh€īė"ĶÅ·q8m7÷‚³pŠėyv;ČU¬ālfh:Ū×t:Ä£óW`Œ£éčŅY^ē‰@NaubG&Ļø÷{ ·°i[›ūé$sdšBˆö6­R8bh­hƐ+ŖÉ«XÄA¼cV±­Ź¼sd Pź;-SE0 Ģ>ļxżŗĀ$»ŚĒ„][¢"#łG}x‹óDX«¶āX¹ģŻėwŒ‘«Ku½…¢¤Ņ ęęµf¤=І{¤tJ©+cqUšQŖ‰”«cµLD„ųM©©oīm­ –Ö(ßnÖ¾­Ķ•Õ”©ŚŠąk]†«"ĀĪŅķŅG;ÜŁó “4Øęm Æ ©U.aʏ³aŠQAW(¼Vo#Ļ™!_ÕM[&=³JJ8`£b@ē‚#ÄTw|²[‰uŖź'U¼%ś%ąµm>ąe‰æ©+Æ$™NģÅm1i–MɨډĻóqž“n$Š~…Fś0š¬¶"‰[ļNÆbW„@}'凨V ³SŽŪ¶•fHƳP/ˆ!·j„l¹6ö†¹ī¬učŻšŚ¼—mvą÷¾‡¦„®ā”nzpŅ$o¦²ÜłZŚŽ‘LĶ=4S·!ÅDŹIŠs7&'B G„*e ½…Iė7˜~ck\6XÕ®ķS|)ECdŽfįzD;Zåī‘h±:ŗ¶“ŒŽĘ•šĢ}ęFŪTŠ”Ā#c“bhB&č¤r«ĻI®źDĀaŪSÜ?Āzī»t |D!q`aŚ(E]› ŒøļĮCėĶŖs *QCˆTūéj„91RFFµSLŻĘšuźn;˜JC”–0ˆī;„Du•×ē,,šcĢ’J±$¶–ƒ4ŽŠ!ĪZßB„—˵ECžK5ŠŠ Śx8 E™D>ىdā ĢQy\ˆm©HxcU~2²8GżG“²3;öVšķƒpĻ”žƒIhѽėƒq‘­.€ĮM`Ąøü͚ (R’Ńu¦Rio°_§łõ' [·ś«’ćÉ?ÄņßÜ+śģīūˆÆųXļ+U=“ä=c“™Ģ1pĖ›pL} i7½2•Č_,bv馉æ6§­øjĮƒ4v{Ó=Ł{¼6öÕEO­]8å}Ė}ŲŚ+ߝŽECŖŽ:yPTqöEČ+‰‹%“e^7‹0€ī •öd ’Cķ—‡Ūģ.„≶‚‘Émn9"hĶŪ|ÆėQĻľ9ę<²8Ó±ēČÕX°gxMø5b… Ē œK)+ÉŽDĀ™™Ą>ÄV(~]Pł,‰Ķ!žgņ’?Ęļ"øĘ]Fbo ĒüŹ·F©9xõ«…ŗL ø»'ķŖ¬L }:«8©ĮūķAüŗ£āŚĻ¹Š&ó^l‘ÜŖųg;ļe‘ÜĄå³CŽ©Õ+uZÅć–&»ŁŌõ¶e‹÷ź4NBIÓvŌ|ši:•“’jÆŁ¢Øn¶ąt€}Ś—Ņ.nn$—Å{‹[`³FPŅu:/ ŅNŅ}WI©R=M—LČø]d•EmĪ’ˆØQMDŽA…50”CŽ©]˜ļUm;6Ą¦kJ¾f¬NīNbAXkĢxŌäfpvīėčõńŒ«1”P»‰„mÓ3P7Ŗ ĄŖ”¢@8—tҤ·Š9ŲóæÆzēšĢF;¢ŚÕ­Ćœ ŻTĆ©JJ(tŃDM4E`9™†›_XŗĒ€īm¶0VŻĄp‡«ģéÓV'ö[õ¼Åd[ūNś¾p¢A½Bü'—Ų=§ŻŠ{M·»Ł«+%r5c&7d:āS–·`Qż…Xʒ¬UūwȦļŲ@Ś ūC ł•möOJ»ś©zš"hŠ–¼.-©VõOHŬĊvūTZ1Ź)|ŠŻŹØūŗ„ŽÉč^·Ś s“G噳mŚłfmö°žƒtŅģvÅģŪR2Š8’yÖćEāˆÜŽå¤”Z8UĀlė Ś"³µ!fcbģNeĮEEĀrF”U¢ƒšeOŠ#cœ‡ÜŽØo°kqpOš½žm«*ÜŪ“/µĻ±X$ <ƒJ3Rr^,[™0"gž ±xø— ¬š(Ŗ¹7ŲOźc~Qé«9oHߌüÖ#“°•F]-<ēÅ1ŠYķS¶B¶2DUļĖѦ£U$‡ÓŁKzEXą)•Ja0¹€£KwĢā@źU1–r¬7XSƍ¹Fo/bČŪm›VÓ$’õv(‹fRfd“7 Č ŠTTTŸL¦1TĢ%Ų¢.e¼†Xó;mh°ī#lRenŹU_^V%ā_įüHJ·&=žĻķEŌMĻß;ņܦ-¾įæ–õėC« śh‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢/^2Ģ%Ź į0Fåvƒ[ „įŖ¾ŖA_·ō·ōÖ)ī(Žż§Õčvž…‡{ģ7§Ģ²s=šrdʕʖœ§Ī[l ŚÉYœ³‹zĪ 2eThŒ‰$cĪ>:‰$Õs¤qH;ÕķĪ—^āÖ×`ü»ÖM؊ņSčåTw&ø¢Ż¬N*7ܜݟ,ņņ–³X/„Ė6Wp¶†°`½aŚĒ³Ģ8«Tkk͹UÉÜ&Ч`šDLęWÓ6'†di}­†8įĻҳ›?†r涜Ųót.š÷VØÅÓĶó5 =9»vĒFy.īń(Õ&āić[ÉD!ŗˆŽŹČ”®=! vę0”ÉĀ7†ąŽąLĄāĄjh[Ķ]äNŅ„ßÄ/’ÄP»ń+!W]Ó+³s!f« aŌ£ĀŠņš \,Ź&bÉ&Įi¼–zĮ± ćŃ);Öā jZ•“Qj²<³ŠVœÕ­9–ϧM,¶1I'¶[Ż»®”Ŗå³G1†S­É²gx¹ĆGŖ„¬ńĆøgg śķŲÉ“vu”Mø Čŗ*&³cy øk'>‰ %² „~]£a޲^ĘJŅÉ@,+øīß9ŒīŒ0†AYū“HČLCRäåeęć& “QĢ*Ź·Q|Ō†R:@:ŖR Kn~ÅTæ<,ŗ÷“2Xéā4{48go&>Ówm`¬G#­åm“¤ŗ7×#ŽŚ¢ī^goŲUī½ĢĆxü} -?‘žrŗß°wŠŁ[—Cć{QaąÅLß_8lzŖ,#¬æ’ŗŠŽ<ēč·¤žąNåī”8ŠĮ÷’œōßE(ŅEé$ݲ`‹fé&ݲ!°č© ‘@:RH€PüÆI$Ōķ*f ļDM4ENŚźšW8 żŠ2}ƒĘŽŅM”«D¤›•›Øš+¶:Ä2Œ]¦”€Hŗ&"©˜Å0źäR¾ćqkŻłcмpPā±ŅÖ÷BĘ<}•ØŻdå¦oļiõ».Ot­¦ ’ÕIyéwńņˆ&ĪŁ£PŖ$“rŗp€”Ą_L¢]Än-®õKŪ[ØšŲ s]Źź5ĄG²ź×ió©KYÆ#Ž ˌ„ ?* Ø>ŠŁ²«žœrĢ’)Ėä:åZi™;å,pöJL­S.ūkYū¼SB﹑‘`«Ó1Ć}RȄ±-¤ŽhŲeÕ}XO;\Č„&I·¾)FÉc$Óõ›GĢąB¹õ<“xI‚rV Īp«@Ÿ0ā nmŹļA;‘źnČļOD¬#śk÷n:×/ųgM•ÄÉcsÉG>#ś§ųUρLŚkwѓ̻·åØl£Ōqé <ź#Żó;™śĻf½ĘŚi]įXŠĒĪ®ńJ“\sg’P“ZF%7+Ś—|ŗė•Źˆŗ)’šŹ]un““Ņ4VڇFė¢āéĆP灭ØhM‹›qœ×š–¦n2Č-ƒ@k\(@n©ģ%e/žB1ŽŒÆŁlµiœs&+Iŗˆ±Ą0}»“ȓåć&čb„XȤRƒ†ŠH ‡qDLčŠ^ĒŹā }!sė‹ &vfµĀQĢTĮ6Źõ–ć›E·k€ÜÜĻiĘæMhķzµ+iu$śćAŖüčē«ń/ ‹ōŽŪ¹“E_ŖŻ#v”„/Ī_5 ŅŪ«Ēwfęü\­>#ą=—Cµ¼ąE޾\ĶØ)Ö׍ww“ķ~^c^U%M5ŪŖŃĀI8hą½®8L‹µpQ „«¶T§Ebˆ{ŒQ r¶¹Ģp{ 4# ŒWB 9„®“ķz•—žćī1šaž–ķSCŗ¢Hš¼‘Ō7QQXOEåacwu30“u·iœwÅP ŠäĖś2Œć؟X}„­ßš‡ź5t¶ķd‡éGźĮ‡r“5N %^ŽVī`¹ČEžBQųmr› 0©å¤ȹÓėĘĪ;päź¹åS"ž/i@ ,ß65“K[x#v÷8¹ųļĆÕßĖ^•Ė'Äń.%•ćph ŁĖ»”]¾3į–E$*ī­īSŪg7« õ³øCn£$üŠ…ö{ Ō„üšÖo8jvö4īŒw^õ?kĀ;iBĖf¹Ć{Éw—åxį``kH­n ¼Ų”Ś ąbcįŃŪŁ°§ݱG÷CZÅÅÕÕŪ³ŻK$®ż79ŽRTü6š[Œ¶ģcś-Č,"&ÄDD}ā;ļޱölW“^ÆDM~L "†ę(}¢>Ą yQ³z÷Ÿr¤¤ļtȅLŻżš ŽĖķ`ÕŠIÉo¾ŻæLвۇ³ŅŌ¾“©Ż ŠA)g.\­ūN ļXsj0K,aܕ©ģ=Ź™q”šŸp…«Y„Gų+=A„e‰ƒÜa<ć”e»ķ+#ŽŽķKCĀ÷nĘęXb•2;±€Ś >MvÜarÉĻ@ĮŚāģŖ^[![ŅHʹsI„30nWNņuŃos©7h²Ÿó °~}KŪpʝZ8Ü\?‘ 0w»¼(łõ»ŚTao)%ēæ#{жĻmešĶæ"ŻĄć±ŁUS‡6’Įź +pęHõwŹŽŃn§!Ńķ­qeµ“'–J9ßżĀóŲŠ¢¤ŌføĮÓO(äeZßŲ ®[ŲłŠŠÕ¼g]®Ø~£-e–,©·ėܲuę–)h‚ÆČahė-ī‹.Y®öū¬iĖūEƒ±«­}k iåqīĢ{ÕCō«kŃ‘™Mśmė YˆŻJ6lĪM·øÅMüŚ“%“gŻĀ\y^ā{›”w•sùŽKAś-½Łp_£×\ģ2éHŲĪQß{,̬Ņūö5ÓÆ£?![ģ׿pÜ"Ėż†÷Ó7z|$'3<ž‘'ŗ“īU# "‘ŃLŶŪåć¶C`÷z-E?ģkļ|§4Ž.w9'ŹÆ±ŒŒR09€EŗÕ ¤ŃDVśŠnŪžßŲk儛’tlevķŻŌöƒķĻžPżöØķCۃüĆūŽWūSJ…Øń¬m²Üi&EJĪ’lõ”¬”kŲ*u!¬L¬k²w¬œ` EPß~ `1rtŅX ƒA\óZy›ÜĄÆŁœ’H)ÖÓ ŅŽ’+/å4žV6ļŪō§«Å2exŠb#Ų*"`62JšE®5Č’oĖĪ<įE¹‚™Łģy9˜ŖēU«i¢&ˆš"°œŒHī(q­’‘UķŃ馩=ؘŠvBzŽŠÄŪ†Ż{¶Õ‹ŒX?˜¬‹lO7œ(YV*$tKżiYS‘5×?¬„zC&ķ`!Vtåa;s ‡cŪk™[v ,ĒÕϦś«•QM­-õ’±6vĘ1GÓu%UļųLb‰ˆ„Cģß^ķp<Ž…ė}“ÓéWoU"hŠž–±µ‡~ɋ–īłÖ.ßT;MŲ %«Ńˆ¤?Ž¢¶$Īč¦` R]”ŃzBć,n 7HQf€$NĘÖ¾ƒTŻSTčŁ$āZ¦ŻB÷˜©¹Y Ąįķ8ūzkĀjÜ7¦ĆЧņĄŖ ^…`‡oģjID­M$ŌUUESQe–XäIQH‚¢«,Ŗ‚TŅI$Ź&1Œ R”D@^"Œ6>NF±h“„bšņn¶ (Ł…ŚĻg€ĒuĒ īūø“𸜰5īż›±Õ.ę ˜»cdÕ-Ł'‚ʾA“ |€Ó®‹`ƒ†u9mō”Ł»cäsXõs]ś Ŗ%.cŖĒ4>;kb“‘]ÉÄc¼‘r›œ HRĮF>é&@7jEÜ>-ŗėĮŖG‰{Š6“€ķ+ĒpõÉ-m¼‘O#ĶbvwWź·•ĀēŒįjs‰o±3xjŃTafBu劫0‚ Č.‰„Öč·§wąA/ĢŹ @ę']Yµ­Öh!5y–å™wĀzŽoµ]¾`*KvšŠ`Iē]n&MUĆÕj“#ā„ķv8ī,°ŽY„³%夞eIq"G±C96źßP†˜·1łĀ=V ļV©ė¬b/z©LƒŅÄĻ!ė“ &§qv.Ū-Õelŗ„Æi«sPuaę]7NŃŁFĒ[.=xŖ–żœėUę›VŸµœŸQ:*EGœą%ł·/vīLˆõ*I˜ŻĘŪø@7Ō[åzø•œNՊŖž:“ņ‹‘r³˜’ÕHōpŖ&u}·Ł‰%3źėuhö=v-¼ ķœIĖ Ņ-ĀĻW#„["EA9ŒRÉǵ°wʇƒpFVŠei©&»Ø 8¼½éæ•Ę:Ö»*E)‡&žÅ$(øœ“9yÉŪäĆ!J¤Xy›«VŃ©GE±reÓ¬UėģŒvUŹņ€V:@uÜŗXG ¬b“¶Ģ“5ĢĀŠĖpjēļ8œIģ``:I%y’R]'p€n‘WCV‰¢&ˆš"Õ@v]ūLxįÆÅźĒäź@|IƒU1Ó\`³ģ<zFPäDY\Æõ²'ÜØ†2i€&#ļüڶĮ—_¾c­^{Y–u¾1ŪsLß) ģ‰D@CŲ ;~a `­©Pó8ź£4üÓ#¤-ŒC`“՞9¬Ł?'Æ)£cÉ?īo åö 5™żĢLš³Įī¼7°ģź”X²YŪČļ™e÷šr»“mėŖā$”. G/&…{.Ö× ‘Ä=””d%‰D¶Ū“Ī¢“É倾ĻY¤PˆūUß®ÆG5”x’2ūiłZK›Ł\ķŻ Óā¹ ,xeÄ<Ž®ķöIé éV‚„Oĕ‹Jä¦CĆc‹d›„Ü©²Ż *jUā Eė^9Öõ@¢%ļ®Ī „Č›` eߛėŪ@˧É-³kI-ä,pƽ—9¤gZÅ“mµÉu»YĮ§šę`sM=ÜŽV?©KvYŠN‰µ¾ćł¦m‹éƒ÷:Ūt‰ü7 Ł1ŗB¢_h•8÷©&Õv õĻīxQņ“&›pŁ\~ŒæĆž±&7Öi<‹oƒ_l`2öFŃō£õŁö@hżR9ÕŚ¬[Ŗ·V&’ØŲ”ģŒ“7bėCæAįŚ(°£ Ų†łø×‚“„ŅP£ŠJÖ/,otł<+褊NG+ŠvĪ vŚīŚńž%¬Œ‘Ÿ¢A§HŚ:čŖ-b«é¢&ˆæ@Ʊ@L#ģÜ yZm^® rĶZ¬§źŁ,PPö‡Ö„ććoų4ž8EUGņ¢:ʶ²½¼9m!–Sś-s»Ą¢ĒžźŚŲVāH椹<„[ēY®ž !ŹŻr?°†¬Õd†<ę÷vĻO–¼bńŠčĮ©Øø[V߈ õ$h?e¹Ü£Æéćī|IOč0Óķ;+{צLČ ?H ĀĄ§×gėQ¤»’8hj‹mD@:öŒ”>¤¢įk6UtēžH£§ķHGī,7ė·OĄ­²>æ²ĄyRÆoóęSŠ—ÉŃL—7CER࢐xSšHGź]§Œ#īśfüĆ©ˆ8sN`ͬ’zWŗŸ³į·¼Øéu›ĒIpĘHŚ+ߝŽEù\Ņ»św;Yŗö­l‘vŚ8ćīif‘nR~Ō£„6ö¤b¶‚×͓ѓv±¤ö½a>i'öÄņŸÓq±ÄĘ­TZŪĄž‹Sź Ķ3fž}Éņ1«F’œ‹óź§>К¼Ķ+¹Čh’ņ mÅ(ĮmęĒü#ʵ‹Yp㬽ŖĻ'æé ŁėzŪ!ž#ZkšIłr§åÕ?ÖżŌQ7œ‚óūDŽåļùß{$Žę(żzŽ2«×#”õŁĮĘ&ē}žuV¤y #ķī4‹ß™~soļDuKīīd_#²ņAŲ(;•L¶†­csrœOi©\ų˜ĘŪøĀmŗā#°~MõŽÆ/4DŃDM4DŃ»øk¶ T@ÅÉļQžäHē_Ąą!öģm©Żļ'’'ümQŗµł§÷¤¦Õ*;c Ņæņ5š}Ęj–[`ń3 ŠbƒŁ›8Č5*é;lrسW±ļۈ™¬œL‹S¢ś*Q”‡tœ ¢jw„@eŠįB”Śē4Õ»U{5–ø±¶ÄOZ«„ūõn(Ó2 ;4»Õį‘4Ŗ/›—`śŒsUŚ:/Ę¢MÜS[Ģę`ś–ņ8ó«™&, ;ł˜Ŗ¾µpŖÜRQZµ†*tP6b膐dog§!Æ„) l%]Ķæ»Uµķ²AT9g“U €€ģ !ķč:­Ršń‹ä„%J†Ū½[CoDvQ‰•L%x‚` ?ufmƒ§ĢUūhż_8P5ŗ`!ņEHĖ”‘VÄŹ‡é „ł™ÄˆˆĘÜ;6(ĆÓXcīAYĒļR¹¦äNÄ`łJ»†Ä‚™äā øń‹ņ…ötõWŅ6•v5R&ˆ­ĶšÉƒź°@¦łÄ•0FéŽĪa9‡ųö»m÷Õ§ķ—"­»åʶxēī ž‘ČķĶÆG™Æ¦SU‰ąš,Ō”°‚bs60ƒ°½Õ#Ź©wŅč>E>ŖvvWJĢ%²9Ū3Ÿ`œ‚M]39fcœéødäȘÉĆ'I#‰DJ& ķÓR-p{C†õö–8“ķ Ļgwć(übŚ>īņV^īŗg2g.8„7m%`b*Å2iXd]²`Ø’ ŗ«Ž:ŒÕīmhr}ć°'¶nŅcÖ5øįøž’0d“ź0fwhėXjĖY&_(ޜ=2Š“‹LEŒ[aģB¼Ü½ĢćX·OdѳL§Xźøßr•2–>ŚŻ¶ŃǵµĒy;ÉYŗŽ­6³~ė¹0„z±°ldcŁkFĮ†ŽR²„†1|V2§Å±F=²vWŃķ\Y¤Ź‘ õwŠ¢UM.„¾·Ó¢J ‚i€S;Īa^śé×3œO„ 7tō•Ż8[BƒDÓ#ų÷°:GÓÖ©Ė]”­SfœUŸåŌūCQėU–ŽŚøscµµ\ÄAĀ v³„MuTāC(R'󫛏Nņˆt³“hœ&|®¶æų-cęMģnÓ­ģ"s\łf.4 ąĄFćļ;¹_ū EČGĆH֌HĖķ5éóƉćš$Š•‰NĮ}X±·Ė:jq2e‚¤Ŗ™ ¶WņŚ]x­?Ćs±'jŲ8‹…ķuZ9€_C:˜‚ÖŠ4ņµŌ„7G<‰¬X[kp6ˆŠc`‰c,‚b=Ēn"Š“Tīģœ ŃPa0P×GcƒŚ6¾_{\Ē–8QĄŃq^(GoÄyÄQ’ź¢`żž<كż]E\żūŗ|ŹVלvōy×­6¬,„ŃDM4DŃDM4DŃDM4DŃDM4DŃĀSł’r:"šķĮV‚ĻŁDŽ›,1³¶8·2¤ ü¤3§/ł)iō#vRūZ‚†ųH²¤ßbˆˆ{¬cš× šę•ĮX¹…ņF\ĄHf'˜lYDäj-Mxź5UÆ.āģõA±·jƒ§ķĄ”)·ŽknÆÜŃVsOŸ×€žˆp9Hbąjœ–±I$lё<¤^lUż;NŽåķŽGŁ»¢øvmZÕžYēœ{]cŁö/Z /Õ­Qó©éœ)Ü’ĖMŗ˜D š„›uŌd\Rd9D/2Ķ łŖ¤ŸĆ,`©•”ƒyØóŃFČåoY‚©rĄøŽÉA¬c‰ųKžf¶U±U9ÜbRsĢg¢ yéėķĘ~b©&õTČTrÕM©P)J‰ )kØIrǶh_ m&¾Jo®Ģ Ķ‹-žĒÅ#eqŽp ZīR2<ĻähZō­'廩˜ØuŠśY(Ś]UoŚ”ż 5ŽQŸĪ1MĀ‚D×hŻtN@D5”˦¶\Ė›ˆ˜Žņ÷m÷Z I reé’6ŗ¤u@Üݜ§oP*ū’R,į›Z· Ķ~‚ÄՑ0 1¦2Yżš^D‹ ÉÜo®˜ü$ш¦žĘ¾®G%„ž6­2Oļ¼ ŖĢEyÜOB”ńÜ\įpąČ}ÖOÖw˜µ%ø]*xvżbØ9Mäör6:¬åʰč;„P ).FM ”¶ é 7HŖcź°3SˆīaÕ³Jü°Ģāé[RāqõBGPtÕCLc38ˆ`)Č7õš«”–).¾q[\R]ŗéfPD¢e[®‘A?ŸĖŌĶÖL”źu!ƒøzˆb5ۊ·±X”ŖÕUŖżŃDM}'śdė·Ę^ægPėūš"… ŒI±J£U ł>ģė3*]†n«,łf;GŁ0(¦”€LPxķa’ĆāŲū#Nø|Ė:Ó֊nœ~łW;XkjM4E°”Š‹œ`“\Ül|Äc½hłFmß²PCōLfĪ“U.ņR›nāPW²B’'¼o…RųŁ+rHŠęÄUR$¦ČĀ•j‘†A?ę`gŹ­ŗ“B—ōPlœƒÄ,qHū€­dˆ‰Ų—Mµ”n£˜’ŻĘ×y¾£ŗč2ž¶×c‹gÅż3ĖGŗļY½ē0źwR¦äČP|I;Ż Ģl«pŁ^J=pŲ„ėź®ž‘9!mø‘Fļ”?MQ õ}­GąŪJŁ"?ņ¦‡ :±ž¢ ²āZ’ā72A’2"pėm:Į ¾„½ä6lŅ‘ŖX«™†ø 9÷¬į¬‰É3¼W™+ķŚažó'E„z(äqÖæyĆŗLĪ-‘’Y\ņ“G׎`9Ųśr5KZė„MģŗƒōˆkśžŃB~³kĪ· y j²Æ3RĀó)æÆK«_›qy¹Ö+ŃL&QfÉś­Š·ŹĖ·’ŖE‘lD•"€%6ū€`’eÅWŽ_Gį½¹›įĘ÷8¶¤WÖČˆŲN (q4“feµ«ó“Šē{ZĄÓÕĢNŻĆ°™½ed6œ…‹±“#uķˆ‚d(‡č M·ŽQPa‚'m’ƒīÖ}æh Ņ(n®_śNŹ>Ģm'öÖ4śĪ©JÉ-¼ ęncŚņģŖ/ļ<,éDŖŽrÖS1‡c£½Œ°jPŲ~äĒT)¾¹eŒ_“u1•®1ŚŚ[s¼4»’ø^žĄ£~n0|÷3ó4ø7ö[ŚW%Įė3z•œ]_­œżBNĆ#ŹDŪļ¹ÖN²ĪĻ"ąĆ¾ā »LĀ>ŃÕé†[‹—½£č°~Ńk{”x5‚ŻŒ<Æ"æ²{×:h«“ń#C~“j­}£s€oģś„™{"Ęæ„DMcų¶qżŌ%Ē•ī>Fåņ•{ùwŽJ9Ńåvo _”GÆŖ=Ņ„”²)¾żÖi©9”wūB5Ò¦J“OœaXĒč44öÓ7z|$Gļ3<ž“‰īŁÜŖvG¢ ćŚ5n Ų¶AšŠ6E±OŁł5Œ÷¾CY\yÉ>U}¬cG0§‘kź•Rh‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ­>N«-v™ĆµVök 9iœ¦'fŖøE„‚$Éc¼‚čŽpįŅ!•ō=5Kń¤s@BkE›įß<Į”ŁaŲv]Ŗ[ĶąG”–»ÅŚ6UŹķFqDZ™¼÷ ¹b:Šw*tīѵ䎗żĄ„ЁØ}ę#6žĮ H;X”ŸR(@ś¤łŌiéC,«NæÄ,{Xi/ yĶģ#愜νjĻ'¼de%^‘">|«öQå:xŌQuÕ8‰wßqļ"Ō˜Ü‘–5¼ĶüėÖVļvysłJŻ+Å*~å;,ČxåŠ;ŠČęi÷BfŻČŹ6~ŲvžF½IŖ ®iź>•I°µ?Dw/ÕxŹ+$T ŸyDHq:~²Øƒ½„;ä) ½XįØ;ūõwū£RżĆéTžkīŖNĆĀL}o}+kÉ9¾zZ5Œ–qr‚i&Ģ1  ¦ž> ÕįĢaL½NsmÓmPī%Ōjįz„VŪ(Š@ZÉń F-–m É.GĒ@~PŽn“(“ ™L«ĄķĀ$.ĮŪė¦#¶żĄ#«ā‹öŠQ“éw„Ptūs;‚ćń‡5!ź"æ/.*²ŲÄGleKZ@©€žÆÕzķŹĘT śGė¬ī˚a?[’„[ü.nĻά}ĆøµLp“fė¦Ds%/5UlĘÜŚ&.›§p+É ā-¤a“hŚIĄE!ļr›“£øli'Y›Rŗts4ÖŌc]ōęV®-#·Š±ķ%Y*]9ÉŽ/ Ź]ĮĮueęR=…ā ¶Š@„y0é£UŠf’Ål'1Å2©ź›øĄ`.śĖÖuk}O:…Ė&–!,l ‰¹Ž],“UÕq®ųĖå§ĖĶ[ęÆ3„4k:ŹńÖW—Nžöo‡¶dV6²ŻĢ_&WēG ›CItŽh4pę©Ėlė£×¼Õ…œl>ā Ć$ĆĖÜ–"’Syց³Ä6åuµź©~€ŽĄ#ön?ŲŃ+iœØB&mŽāŅQĄ‹v1īŅłéWĒ\č’EŒ†n“™WźøU4ž8˜Å/Ų”īcE^@ ¦‡‹gk†Ź©SY#Ŗqøš1™Bj§aĖ‹·„°NŁ™ŗxv“bŁœÕŽZĢśQ"™§Ķ7HsŒ‘ŗķżbŌČ!·¬®%¢”¼äū •-_”™0m6üŹćńš«ĖƘݬ\ö<„F6g!&¼[œ—cžŖŚ^£0ły·*9…‡ŸP­Įōо™×#3ģ= CµmüMgkü,d<­ÄvŌÅC“ĒLs×*¶Ł}ÖR»ę%2mVB™Ē±c 6ZRr0ķÆ“/V$‰ßJ2‹tW§e©>] )[”Cø>!±qØÅŖ|<Œ0ĶJŃÄłBŪøVÖK=V醄–’0‡P+˜¾,³YV³”@Å{n†lrt2KXćŅ\Ÿ˜¢ŲĒ ‡~5Ÿ;²B÷ņ4žå„Ā.5+ks±óĘŽ¢š ĢdŪTķÖ hWéF9ŖS”«T|컸 ŌōĶņā•:‘ o–ŽŽA½*=džJŹ¢ˆ”ļHŁā`MCޓۗŚ[Ę!sŪu;¤ sö¶(Ģ’Śp2:Œ'I8Ńu®9Õ%3"Hłq ;;²±Ž#‚…Īš ’½qĀŖņĶ“*³Õ|= ½®GŽu˜‹–1Æ/‘±],mē¤\¬”ØBÜ棣RDš„AŚn „(Ø=ń–ń±xWQ8 jcvmŽiååiÄTAŖé!«]Įz4KŁL°½…йĒ×nL\üŠbŽJ9ĪćĘ„|ĘÕa”žæ‰¢ņåȐ5öW‰čš”\h9Žœ#TėQ®[G¼lżäŗŹ.G ¹ R—¦§nõ­BÜG »ņ³ĀiŁSSP|‹œėŗu“Zåäe‚‚įōÄęU_xĻ2eüJ\KEͤ×(7lšm#r˜”Ē«½E@XĀØ€›Ś ķNirI-ŒrĢē>GV¤š“‰ Vµ’°Ńø/X}H+i¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢-5€L’„‚)œ(”CCˆ¢/ G%aĖ9Ž! õ”Z: S9µĒ0zż“«2TnŁi$ÖE‚¢u{ż`!Īõ=nf[AŽfy -ĀLOB•Ņā|ņČĄģ¬,9°NĢvt©öxē÷~.Ų l69÷XO%ĮO¹—jģ#¦ŸEEJ™“•Nź=•nWɳ: ±ŠØ÷ ń×,Ž< »Œ4ŗx]PEF`7ų€1XŠÅš÷/µq!‘H(A”Źyķå^fqĆfrĘ]ÜŪR©'¬•¶Ł¬¶¤9ĄŠtYĢHŗbSoń‚}Gmi'T½ŹZĒĮŪ‘­g{@=ėm6µĢꗑļīāHT„]Ѷ'»ß’pt˜LM⼉KĘķMD×°Žē§©ńÕ:ŌqŹ‘ĄS8¼+Å ś ѲŖ{:įćl²Čź5™\zwDkm‘Ę(ć.$²_(–L1(UŠ;9ųšVė5ųFoäå•sŹ1iQó ēv³AWāßnī›m­~Yc|Ž“YÄöš©¶FXƳŻhPłK:Œ¼D„kĖSÉĒ<`{2ə«” ķ²­Ą"[=DT(ØÆScš ńj׌…6Ģ•o”WÓ;f÷a· ~Ėč͈€Ž‚±õøčēhv—ćAćEnńu‘w_Š’»søöš­Iø4EvæÕÜģ‚šÖ:©Z»&&ÆĶgq¦4‚›˜E± xåUÜN«ƒŅ“R1>ŽŃVF—ŠÉKć[ŒAĢJ<šÜJī$~m3Ü&D®Ņū ŸO“uXsJö„Q ¢³e(¬Ł`č)8IDUü©ŖRœ?{^ō/jž‹Ōѽ+bĒV[Ž?µ¼§dćO¾Ō2kfŽéń*Z¬U)Kś ²nad²Å¼3sv ¢b÷w›%öMŗ‘ŗœr°I«Ųöę9ZąŅŽQB+ÉMźżœÅ“2Ü“ŃÓ4ƒ»h&Ŗźj n)¢&ˆš"h‹ō@@@Dpč !ģ·DTóź“ƒ³É²—P;O7įÄ,ŃĆÜ ÉEŖÕĆŅøŽd’¹ÖDwSĘß¹¢÷\›ŲkNŖ+/·…īĻL²{Ķ9OhŪ×UńÖ1«Fj‘²ŽrSńż©½‡+W Ć7qń5ŗļÓJ%Vś7νŗ QĄœė½~ķ@E”lc&äYō¬«åŒA«d•p±ĒbGUĒ’»$`—~X“°Źp^*v*-YĢaܵz¤U28ĆšNäµ\;—U1’}i@­;MĀ;‡R„Œ³ƒųhīæįŚĒ÷Æ/w#6}£ę„SWŸdS§ŠøIĢWhøB总äÄ,±2}æ9 ćc… D °ØĒNGŲ\¤`"Š9QRīS€ź¶]C Ćį‹+†üļÆh ī^9ĀŽ5Fsš‰Ó£ę\ƒä ‹;L}vŪZw1„›HGŒZ˜Llé3&Pč#©ˆøšéŽõ˜ŅŽbAļŖĘ}„/>En‘ć~u„I9™w[­ßć[ŒON¦ÉwŽ;JMY¹vׅ`ŠÆĒ4ŌŅ1éņĀŖū€ Ż©ˆxŽĀCYs0†ļū0®+ÖR4QøŠŖī79öxéƒõń¶6b}½UœHJäk D·ōc£ZÖ«Ä\7鎳rūŒµ7Ś“óĻ€ō÷*›bóķ®Œg é+!¹d,±uTāBØ×ļJTˆ#œDݱ4f«•0ž‰Ż(`įõŌ4ÜGØIģd`č©ļùd¶Ī¶„pŲo×l(=øćˆČŒ9RZjĒ^‡Z“-˜ēQ«ĪHVä¤ķ>ŽžR“«łÅLœ|rdtŻ ŖėŌ0–Æ.äŒųWĶ59‰Š€@ mØӇ2Ŗ8ĮgŖŽm½§b‘uSŽéÓ*ŹĆD–Bęr®­vi™ •õB?ZSXl„&Ł į$Cū€ GMus32¼Ņt ­ģ×Uu¬cMGµŚUŹÖ2­bė“qOÜäĪIĒ¢«ÉÜ f!‘ŲĘ:Ķ!й²vŠ%ŪcŌMP÷޶==ĄAlć±³ø“J-§‡˜gҵ›Vc3¬Śą7ÜÕX“IJhĀåŹ¬›£”mmƒr”Ēį!›Ć©Ößsv©ļųC[EĆKą{ŅĆäZv‘;mõ[[‡ū øŒž€š³ m5k.Ų±ā!ä¤y[¦V*‹Ś¢Ń—„“SZ䕬-Ź8EI„ĮI¢«Xā ~}ډ¤e)„į͵Ū!=„:©|‹OtxŒ–ČCƒr†¢K‡¬ć춦‡bčœhĒĮƚ€[uIŵas]^\(i¾”q—Č įl!•pģ=V‚¹R”Xu=S©3ÆĄµöæb°IÅX®eEäh–µ GõÖ)QzĶŃJ™ČTõĆšxÖµ‹]vin©#Ć$¹õĶhŹś4˜]ŸAˆsMA%i·³ki-Öm PzĄœF>°§x*ń¦Ćgæfš|ŠĮg²8®×målņĶ=%>ķœA"ŹĮ¼ke_®r Į'²›“Ó)TPÅ1G`č·¶ö¶v26Ś8ćp'+Cjj14§ ŖG[-ĻĄē;ĆkĪ$œ28vT¬×ń©EPģ’F7{y<§•``lfq2-Ŗąrˆō9꾨€—§ī€ėVŌFY˜Ķā&v‘_:±®NŪnņfū&įą~©ĖžOųÓÄ­Å-‡ż-3÷ģ#ǹĄ°zėnŃ’Óbč?¼V³?Ž»ņܽcu&¬¦ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆƒ× ūD^˜™Ė6oŠyd'ęķ»?ŌÖ·ÄæÓGžgųJ›Šžż’SξnęÓk«jźĘ…Č•SÖ-­å‘z±Ųw¤hāĻÄ‘Ø wϱšśN4ĄäE@8ö™3ĆŁj¬·“ųiZē9ÆĢĀĄ ×uqĮgŻi¦{”qƒjÜ®£qźVć)NdUȹSnåŪVg\DŽ«4ŽS$)ßŅHT‡Ś%½š×ßķUL·b­œ1bķfn2féÄsƒ;Ž]ĖTZ=ѐU©1UTĪ£G&l±Ó¦%0ā]ö xę‚HPć“m”åB֒ ‘³›”nµJõ}§æØM½½åŪó÷ڋѵN(Цćę:ABvÜ~LĶ€"ĒB¶„jaŚRr,A2›ū £ķŌ•ēõ—+kӑµZ‹½·SfgyJŚÅ^-²ĶQ\ĄcśĄ` €RtéöØ,™D(€čˆ›R%ģQŃ’į^:WoĶź,m“EšéƒäŸ³lż1 …7Č&ģ›~Bø*€¹¢+s5ˆź²buX•ăn!ņ&ZołY8”æø‡ j°ņ9׊ŅĻāk49N»Jy”L&dS&ų¤»™‚‚&>ĮķōŒ žMTܵ*,ē]'@pÆ rƒ¾.#Ņ*%ED>k$Ö¦ESa?¬°ĄQ÷ģu‘9’ɓ÷ ænīb’1¾P­v”–ęš"h‰¢&ˆš"hŠÕcEʬĪe'`•D2ü²jˆŽżĄ5 P !öŪoĻ©=DR+SĖl?yĖČÖKŽQ9żÖ««ØÅžš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"£%L_ڟÓßā>T•9H"&*8«" Šb¤'fś–Ó>źēü‘’äj…Öžź/óĀTŚÕ 4DŃDMQ÷j4ś)¬lᢓ\£)śģäKŸ°U¬‘†9£,UŁ/M_‘”d*LEU#%SQ#˜ƒzä·~fR„Pƒˆp;A‡’ćš(šTˆGgx‚&ƒF0» ‰D źŁ]±S§œ”„?¦/]ԟL@ŖčĀ%ļQÖ©ŽĀ ‘wŲ/f°~.l¬?¢C‡ķP÷ž•M% OrÓ2<„‘nDž ŌŖķ¬uīś”SĄƒG®č€ā^»œē ślŻÓŚvL’²ßų“ų§Ż§Š¶-jœ„HŖœŁAvcœLSŽ2>‰DL šeC% PÜżĘŲ=»ļ½^.Ÿü‡’žOž•ęY}įŁł×Ō¼nH®Å»›³gųh¦H”\?SW3Eu R¦Oé–IœØŗŸ-Ņ]cˆĒ1ÖҼ2+wŽ!ōwģB\ģ:mŽd­Rõ&É·˜]ܰ"rÄ9®­#”£¤«³/ėoH;­ÉLB‘ °QcØŁE¦ģ6é’ÅĖbdۚ²ƒ}hiˆ €p®õS ‹jķŖøÕ…R“²X3KHĢH­"Ń[#’©ŲY@Ü.µ˜iłS€•i)h ݊*ō‹Ą׬d=G* Ē®²Ū}rʆ‡( kI §‘Pca5åē*Ä9ĒĢIźÖĮõ«u…‹§O/•ōžUkU ō”Uī`P,'!•QVjH= ŲÆ¤"& ęÜ_GI/&s#8å4sœ>¦Ą+Ø9*­eŒś±¶§—“ÆŠ¤^>‡w)ź­Ļ$­x°D°I[­Oc˜IĖ(*(©Ō2J8õ“2¤‰–QeÅ$ĄUTēÜu;…ÄĪ–ņ0œ ~[邼ѕ“q© "r²ķŒkŅō|·(ĻåŖ§="éY޵@IĶĶćŪ““Mć(†O×|łĶRĄŻ' ˜@ķqŌ:jSM†āHßję<5޳\A9¼üć•-”k ŃuXļ^s[Y+F5ŪpŽZhźo„&snwG›NåW(KćyÕJö¹bŠT®Ų&ĀK»Ņ†pń# m²*¦IšŠ Sr‰S7¬Cv{;±;|90¹niŪ†žµo‰8}ś\ßeüMcš)‹@v!„…»vŽz0b]Ar{F2ZI(¼“M^:DĖŻ·wa$Y‚“†’ƒ^³&—ėn¢j Øģ*Čx‰Zż6åČֲ7R‡v8Tr[ü“ć¾e«žŲõ»p;C€¦j K$f§²ī[ķeo·c7Ųy<…)õ©nõ$„WøY­©!0rī6ųˆ¤ `§^Ś_õÉ:’”3ƒQR÷µkM–ŗ«u‰5 ©gn”g«ü³‰hfāÖ“ ą¢Ē듳į$ŽÖ( ÅŁĖ’X4āī’9ʉģ!ź\tĢ0°•Õ¤&,6¼[ō(Fņ §¼ÕžbäѤŸõ1õŲ‡†j]ūvER0˜Ę0īmľMFо@ĘĖSĢŠ1§)V¤nŸĄšßšł%:{€®×LēśµÜÖѵß@6’Vj±½ż’SĪQń ±*2’kč7 r«Ü¹Ī2Ī掀Ū÷$Żp0t(“5¤É^„“5^p)@ P” )CŲR€lö­*“DM~Ā ŹwŽ–M»Vd;·N0ķ›WpŗĒ0H’(¦cĆŠ :ō㕸øą  M®1“ćNa±ŁČ<§ ā•LÉ(“k4Ō½’8N‘Ą‘–Œ–ENÓ }„CmIź¦ö@Ż€Ųņ…§“ŌW”“Śj¤Ž°ÕI¢&ˆš"h‰¢(…ĶX8·8eI•!õXü‹‡Eä ĻbŁN¦ÕD•P» É kCļŚ m¬«Rsįž`GŚ’¬N'¢“ę^†cS‹•w¬uRh‹ItRr‚ķœ& ·r‚Ķœ$a*Ø8LȬ‘„¢STHāQŲ@vz £hEZqģ‹ن"±Ü} †DÉL`=e Üu~ŒŅ܋PĒ0Dwé©«_KšO.F×¶Š×!§I\ćn.ńɲ 7.ĒNYś2ĪĶø÷V*w—1Œ""'PĆøŽ­»Ræq©™żFžEļ…ś!Vq˜ƒB¶q8ÆG59}”ZŗDP„"€m¢ÄĒõ@w÷m×}ZuŻÓĪgK!?XśUAŒģVåÊ<©&CŚ,ø‰.’š­WŠ°c¹#<(•ʧ ŁŗŠŽUp‹äNÕ1 @0ė6Zf‘ń l„» ĮĆõ†'­eAy}i ķķf{-ä9˜9ŽÆ+VמŠĀU|fVkÆPŁ×%#.ŻĒIV#ć+N™¢r =1\ļ¦\*‡ÄnäČt€Ą>ŠÖlœB÷Œ¾ y Ƙ,#–ŚA,=’Ži-#¬«°jĪ (ö9ź¤ī%$ĮKMāaqk*)€#® -ˆBH=ćXäL oˆHaß|_±w¬čȇ’”m±ńŸÅ„MØ(čĮvÉŲO9Ū½WšœFÄ Ńź™%‹ŒĻe“hF/l÷ņ"e™³Lź,F5X˜paObEÕ1Ć䥉‡ćXŽĶY~©sP-é c`oœŸkƱkח:ŒĘēP‘ÓNE*ķƐƒG0\›n5ĆĀ7Q#-gŚZ‰øCĀdēm¦*hĘ!u‹“ÆTŹm€PT†ŪÆMxu'梁īå,”ė”XžÉp*Ūx£­­NüFFÆ/g³\TĶ Ź6;“ōe,²uƒēź’6f“µ‘pD†‰ś¤Ź ";Ž› –Ę9ZĄAĮ¢€bv(Ė!%ząė9[M4DŃDM4DŃDM4DŃDM4DŃDMpöĮ¬ć”0CÉ®&ö‚,—PM¹@D6ļ¾Ć¢/®+9™&»8‰Žī9W"&0‰–²‘S˜Ū „DžŃź:×8—śH’Ķ’ SZ'õśžp§”t‹Č—­ä,fī›š…źaˆrūIBō1G €ėL  Łv)]Ź1­ę Xg›cF–ÓeqØäSY¦uÄĀKģB" ‚®Š•h#ø€vÆ@õ}ößŲ­QŚ>¦Ķ±ŠZ|ź}ŗ•‹¶H:Įe¾&eÅ ¢\ƒZD s÷½:B%żQUD†\Ūč oÉŖ? Ō’žļJ«ń /ę7æŠØ‹?$q¬*i# *…–IĖ‚¶L­ˆł¬K.ć•1{-&f ØF÷wå‘r±ŠQķ'³Y–Ś ģ®ž8šćž€”…>­k…뿬²G‘IJFį]y(«~BĻģ‡z~‹†QfŖdFõėŅ%ė''[««VR9ŁŲ«š$žQĆ©Sõ‰&Ü: Ųj1ƒ¤&8ˆ”vf—øsŗµäh–Ŗ&[ĻÖg֛Õē5*MGóƋńĪ›J³Ģ± ¶ŁĮ==1łŖ6DGÕözCś©ōöō×§CŌĒüæŚo„Ó})ćÅĖÜ£g(|ń#āYz]7$JŹNÆjÅņč“K”{™ĄdŖ¬ģĄ•ŪŲ„{Ve²ĀS½ąNŅ Ž QɶŃuŽ\ö oŠŚXą7ņŖŽęŹĒr‡“œ7 QlyĒQĆ!ĀaŲ~ķZĆßļ…ėØļķķ[łcķ7ҶŸĘōßę²ļBÕO—\wPŻ”‘[“®Ū«h!}›ļÜ0¢j“Ćś·ņæi¾•ļćZoó?eŽ…½/+8ņmæōŸŽßU˜»oöļ°kĻĄ5oå~Ó}+ßĘtßęw;Š“×åPŪ|™®ū’ąšöu¶ŪoŅŚ6ß~š~«ł_“ßJ~3¦’3ö]čZˆņƏK& N‰HxĖJĀ!ń&0¢`ßnŸhhtXx_“ßJ~3¦Ņ¾'s½ čÜŖćŃėB“f7ö ōüVžWķ7ҟŒéæĢīw”l–åæ!ĻūGjÆa ~Ä ­ (~҉»S( sm°įøėŃĆś±4š‡Śo„xu­4ĢÆź»ŠØ–Ü’Įѹ&ZĄē ʄU†ƒV#%Ū³—}č½Fbyˆ)1rfM›¹Lʤ~„ļÜzķ”ķ#Q–ɰ¶#ā2WV¤ Š EN īXķŌģYté] ČųŪJq©ĄŠ`UÅTqčĄūQ… żĆdżŠNšÄüVžWķ7ҲÓ™ÜļBų?*ųņOnPˆ7ņ"ģĒžŌŸ€jߏż¦śSń7łĪō-"ņƏ7ķ646¶4=  =7Üč[ˆkߥ5oå~Ó}+ĻĘ“ßę~˽NXqąā` ›^ŠßsĆŚ×mŠ#ŌtüVžWķ7ҟŒéæĢż—zŠr»#Ż’¤čæ‡ķ‰³†’É’śéų­üÆŚo„{ųĪ›üĻŁw”}§Ź¾=)øPˆ.Ū9eO~ķż‚h0Ūnæf¼:¬?å~Ó})ųĪ›üĻŁw”j’Zn>…=‡Ų?#`ž×Ń·Ż×ŸjßÉ=­’‰{ųʝüŃŲļBZn>·(Į‡’˜Ų ż’Ć4¬äžÖśPėp’š;čZ'ågHŽPˆ7ņ"¬ĒŽ={ų­üÆŚo„yųĪ›üĻŁw”mMĖ~;—ž²ł0Vż¢½žßÕæ”>Ó}+ĻĘ“ßę~˽ Dܽć¹ė 1žMvŅoķCkßķķ[łCķ7ҟiæĢ?eŽ…ųæć؆’“BŻ6Õ·Ü”õ׿ۺ·ņŪöŪé^~7¦ūēģ»Šæ?®wŪö†_żęm»~’ŠōžŻÕæ–ß“ßJóńĶ7ßwŁw”|0øčõ…æę¬[‡’lz÷ūwVž[~Ó})ųę›ļ»ģŸBųbńŠGoæźM÷µ·onŻ6…ßOķĶ[ÜoŪo„?Ó}÷}“č_'ę? ’ļņĒü‰Õm†žÜ)tžÜÕ½Ęż¶śSńĶ7ßwŁ>…¬ß˜vr%Č Žūˆ•ĶnԈ†Ūūw…ė·M„uįįķXĖ”ĶōÆF·¦Ÿ¦Gź»ŠæG—üv’“"ˆ&‰K[µ Ķü‚ż;ōžŽÕ«O }¦śSń½6Ÿx~˽ ܱę¾W2cG­,g®TŻ[sn}»V°Īē«kB“”g Äd&%A$•Y¦tSą%é-k ß[ŚĢ ™āČ֌ ņŠ“@*”u-VŚķѶ ģ¬$ŌóŠ`1R÷śżqCnļŚr»o¶ßsnßŸ“`C¦±æŌ½Įö›éX/rüsĻ%Ŗ’Ķœä¢ó})ńļ=Ź?°É¼"Ŗ5«ęL#ŽŖ®¾‰R¹ē*…YØsB1‡l’ģcˆcØ"TŪ‘$Ėģ(j@Į­Ņ²EŻŹįŪ½Z·Ü\ZŽÆ’xz‰Œ¤/1óÕ}s&TŹ£—”åˆS‚€\Ķģ•)“0€v‰D=1ąļÆ«J:Ö9ŚĮä!3C“=Ƭ­_뇣Щ"¹ł’‡®Ķž½Œė–ĻD›¢αSNĻÜ?¦*}Ū{õēĄŻ;Ū²‹©å¾G/|Fn‘ŻŸ™s®~āŲ{$O!ä諌hv²P9V½mUŸØ¾Qʈ;¬XŖ>Œˆ”,Õ±Hą˜,S 4޽¹“IŻ–Ž2É+BĀąh9C°Ću+ÖÜ4®5ŪO2ŗĮϾ(’śM\6ūi·@ß³žAf±Ō½Įö›éUüD\½Å~_Ī'ļ·ķ5ĻžńW]æ|`COĮ5/叓ßJ|D<½Å|'÷?iO~ĮH»v—Ūķ0Į~öś÷š=J•Čß“=+ω‹—¹}?xžõšė÷)7ažŌśóšMKłcķ7ҽųˆ¹{ŠŁŸČGHp'ķ\żDä£[» °÷|Q ;mŠwfŖüQ÷[ö‚óābå=‹Q/ œPTD"I#°€­G¹qŪbŠpŹõhﶇCŌ‡ŠoŚ ń1ržÅōæ>("!‘¤Ō)#6ĒŌ†H»ē׃CŌŠhżaé^üD\½Ė`>D8¬Ņįe7å 5ƒÆū$ :«š-GŻoŚ Ļ‰‹”ö/Ćyā±}–ū)’‘F°ņč“^~Øū­ūA>&.SŲ¾Ī'Å’‹-?ūĆM’öŗ÷š-GŻoŚ ń1ržÅ²yäs‹Ķ»} »¬‡qL#ņ”§‰ö{ oŸxĖsݶįöˆj” źnAśß™>&.u±'’Ž3@lˆžćśG§7ķŹ>œņ†éłµļą’ōžŃō/>&.u½#¼^ßė7qā…)ĪžßŹüÆēן€j”öæ2÷ābē[<”q˜€ •\ˆ° $*töĮźˆģRгÉö÷ū„Ūmļ×£@æ;Lc¬śŸ:§UņĒņÅ:ĪT\Åż*ŚeSÆńlܝ>ŠÕĮĆכßė>…ēÅGČW/āžc0ęļ7Ü;ävĀł^”!ŹZ{é»2²P°u&‘õ¬ŠžvĶĮŻ~9Ä Ū©'+ˆ5 L"6ĶcnėKF[ø‚ę£f$Ÿ:ƑĮļ.½|5–ØM4DŃDM4DŃDM4DŃDM4DŃDMXžJń÷r»eĪ6årĻęŖ,ī=¹…bizõ€ ¬ Ńįāf‘S3xB˜ ^ōÖ@ūv,’©阋¬Zƒ ÅqNa>VꚄ”ŸµP^Ņ‚bąŻĒ`÷ōßD\—śŽ(}#23=Q.ÅXŁoˆw‹°0”HnģtEÄŗü^,ÖS¹¾Yęƒ2z@OL™7,¦Ężp™|"sn"!šžMS«ž ’J ¾†~ę‹p1 ‡ß\8Ƥp÷sį?¼@z@ßD_ˆž ļdž{Ņ_’ėL4Ÿūœ$:"߇ą¶ńˆMó4„ æx}’Äźo¶ŻC|;mīūtEĖ7ü^,Ū׏üŅsö’é?#æū ;h‹pĮā¤ŹÄÉüŅM0)@RżŖā³w{Ž87ø;€C§»mr(ž ©†ŹŽł–ąvÜł{®ŽŻ‘Ā©ūD_ˆ~ Éķė^łą}żŁs§æżļ —mü'1ĢKē3P„O/cs0ķŚ¶TĘ»ˆˆč‹€sų/¼\+ßņŁš-{“ķ/ž’q:żŠn?¬ü"Ż:Nš"ŠGš]x¾&޶gę’’ož‘q[’±Ā¢.M?Į‰ā°¦S)óIR€l%ż©b²n?nåĮŪ†ˆ¹~ ßÅdh©·“.ć"æ?nßŃš?·‰`ē‡1Ä f,ŗ‚;l&į’ööžM“EæKšqxOnłĪ]/·ż×3ŌCĻčb„tEņoĮĮā8AP ž^Ōīģęzvčw{=.ģL`ĻwåßD\ĻĮ™ārœäNf²1‡r‰rĪ4\ąnŠ*ŲLĀ`‡oQėæ·D\0~ ļ©Ī9ƒšB™Œ"TC$ā`ÜP8ąóŪ}¢;č‹|ŸąÅńVSn¦QꒅŲ·ö§ŠÉŌØī8GØč‹~ŸąĪń:Q>@ꊛ{@rö2(ū æ¢-š~ Ū’ÜĘŻū€sćü¦Ū †Ū~M“E¹Į½ā@RĀÅĖāœvŁ`ĢŌĻP»ģ‰ˆ:äžĶiŸšmų’0 gę}¢"a&e£ˆ©øĄSØŪ§hzč‹@߃[ÄØ™Cƙ‡Ā˜f<~$KįܝŲ`T½~#_É¢-ŗ’ƒKÄź†JõĢ–åŒA)rī;Su}–ŻL0m„»ū?G¦ˆ¶üž* Ūߓy¤¦Ū÷é[“æŁ°ōĮƶŪ{¾ŻjŸšex£ÄČüŃ! Oö³‹Ģ›pąą1vüš"äü¾%ŅŪÕ¹ó!Ą‡·ŌĢ8ō›’ŽpĀ~Żr‰~O‰ķß1Ėu’įsET7üž†,GD\’_ƒĖÄ{w‡)WŪžė›cĻčPQŃ ž_ œå J č†ne逇“@MFw7æsmöm¢/„?æ‡ó˜‚V¼ŸD ]ŒTó{Q*ƒ¾żĒõi ˜ Óį†ŽķōE Æąńń”@„@ĄQ,Ł'ąŲ¢>µbnŸšvü»č‹ˆĮæāőMī8tĄē±DT/äѽsšyx……b“Y_ėEnt@/©%9š£Ł9TĄÜ>…^‹^bB˜GŲ nnˆŖōƆ¤öļ g‡šŁęŠ]’? Õ jįšŠ*Ё޳ @ {¶ś@% „@ĀÆ¹ĒØücף¶ˆ¶gü!ž·m/ ģq0ögyįÜ£æźĒԌSą żŪ§·D[U?‡5;{+ŠCeā)gIļ(ū7ưał67åŃÄ?燰ˆĘr`Ąq0”£œCd€Ćøµ0 w ‡onś"Ö'įšī]·ƒä’›{{ó’įŻłū+žĘŚ"Žį šāQū§Ča†ĀQβŪwßøŪB€}ŻD[ō·†”¶ļĒŁŻĘß÷lõj.’ŸåŪ”¢.QĀMįy?ÓÄY‰Ēü6ȁ’ †ˆ¹~/ e!Š8'(Ø&(€üƒĖ@r†Ąb‚vBL_hnhˆæQü&^ÓŪæå{ÖäZ.’ŸŠ² tE¾’DūĀg¦$ž®¹ øvżoõ‡Ķ¾”vĒ`ūėé|AŠw(ōŃŗ_…ĀB{wq²ö¶ß÷^Dg^朾$"ä?ŃQš‰ŚR’U»_Ā`7wõ†ĻŻĘŲ;L?“m»G°4E˰ü,ž˜ØšƒÄIGŽ™ŠnĒł÷«¦~ŃŁB($(ķ°‡¼DY·Į˜ qŸU°®ĘÕåßÉČ»Pė»xåE»ps*²Š(cH®Žˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"hŠóćšx’Ē÷ļܚČu»uåYÅn¹TĒTÅ„®¹'"ެõ* “Õ$WHŁl²Č!ė¦* Ō(b•#‘@\.ÓĻ„[ņ!Üx€Źs#^K1“ų iĒ3Q‰ą¬)g°G’"™Éź…½ÄĒ:ćŗ—{¹€–‡Ræ(łU™6:.“·$Y;äæ-8ÕĆ|z9S”YÆąśäˆg=|oņīD(Šōa}y›$·„Ü©›0når CŖb‚Iœå"į)<ŁāFGĀ“NFR9Ь87&[ė8ś“YŪ#‰Vž¾Ü-MhÕŹJO\؁Ł[¤n/Œśk’"ń'¦ō•L‡"¦ä+ƒéål̓OŹ\0L¹ĒšM‹#f¼~k¤`X±Õ*”œ­¾~ĀÓŌŁ&Õ(刓©2‹GB ‚'Ž]Čæpļ>g®8Ō9q‹óŻFcŽ·»ŒV>¬dٶÖ$kĖÄīCiŠaj!ļŠµ‹4ģ¶B~Ž1»gģ›(²®9@RTŠ‹x§>øVŽgŹw[“ųa Ū…)Sy,ceļčŁhTŖĢ;K š~ĘŁUȓ&Õøé= œVbŠžŖä!LVa÷)%łłĄ{®rń1U“ Ū›¹ŠĮ™G(T.‘ųķĀ«qŒil‰¼Ō-5XĖ“7|;7Ķ\ƙTp‹¢ ŗ}€”c?›.=Õ< ¶ń”y¶ŌåķĀxÕĆkž+ƒÉEäē)­6éØĖ^s^Ęu›ėlyU¦3e6éõ…ė2G‘č‘ā©/PäQ[оIü¼łĶ~^šÓ ųā‰Ą%ŠÅütĶ9O5½åuųųŚåfŖĢø¶[ŖĘcŒ=-7ō¶KŠ•ƒr *ą§tõ«5Ū»1 ē™ś‡Žž=pS*ņn•‰ņ'2·ŖŁĆĻŪ gŗqŪ^#Ėr%«CBvĻy†ĮiÅøfš±QÆ>­&vĶSLćõdY0ƒę?¦šż'=¹Ķō:V'ČaJ«-Ź ™óŪ#v.ąa_@eō(öh $³Y6Ŗ”"ɤ‰Ór‰ • ˜ŠĢbœÆ™øŁĒŽBęß$™»żĻ¦eœĖØ™#EŹĮÕė\H$›Eń k™WMe'rQPĪŠ„T5j™ž¹LīŻkńgÉW¹yžĆŲÖŐ*µMi’„0nwƙOećc¬£¢ņd=-Ōźr–J“É6ɧ+ڐī"¦Lź¦"˜#%cĢ;E³äü±x©ćlsJ‹ZnŻz¼ĻĘUźuؔ B)!5=2åœlsPUR Ŗ…(r»˜ĄExiäo…ŽAŽä8‰œ"rŚXŚB1…½©k7šDĖ&Ņvx; ZöF¬T,ōÉåć^·a8ŃŖńG»E»•l±HEĒćĻ'~>rĻ!¤ø„9{ƒoƒŠu/ćVī¬$å\Ė@Ēžƒˆ’Cŗæ?`„Õ‘ģ^9zŲĶ×( °аʼūįnĒł)å~Lā MexC$X„­Ģ …G21†ebu‹%33© Č @I ÷é ¢£ć5XŖ•1 €č‹‚GČļ^gü Åč¾Ić¹ģįÉģ^9§Ō+Ne,ģr^. ]ƁMŗH6$‘ÕSÕL ’DTļ%üSČŽ cnD1ČyO–8ä0ŽTĢł=ćčŚ?õ{«Ń֜}#+FĒ3Ķ’›”‘cõč0LŌÄ@d©æ,ܜāW3ų©ĮĀĖ&1Éž2¤ü bŹ?1o¬bg"ń|Š÷$՘ž’-hˆˆ˜Æ"b JĖ¢š°Ń2+"šķĢEEk|ŗs›†²ߓõßVH¬ķäs"ųä²ćźżćˆO¬^·CāŒ]Ś£h›Š·Y0[ū%J•aC\ģ3ō7n•ĆC&b¤ö=)6`üŠ.rņńā~Éć§ų¦ńq‹9gĢ ZÅJńXœwh†óˆņc)Zņ³|±Ģł› øaŪŻ@Hņz¬”“iD”ļł¶Å„[Ī2šÕß$?:1·!Wm•øłĮłlĖ&øžÖь„VŻŹ®FńƒŒµ Ž@“WLŸŃ¦”„¬SÉÄŹ ©żŖfHĄu‘bw šÄxµćš½Ļė\eöe”y¬ć5 JÜŁÓ²až9ńK/\šŌ²ŅO`¤c”glv)ö²ń­Ö#'É&ĮU’„5)Ūz]艢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆ“—AH,ÕŅ)9lå%pŻtȲ  ±5‘YÉŖ’©˜Jb˜ °ōчÅ|QāŽ °XmøGx Z­Æ^ÉZģŲÆc¼{`³HÉ*eä_Ųfj5ȉ§Æ×8e\Ø©Õ8ˆ˜DtEVeģ…yP[ē¼?‹³}ĆĘr+Ńņö?©äŖ‚ņĖÄ{õ«W8™ØU1p@QL€#€¢芔.= –‘ū3-ž\n0F«žĻ‹Z… GŻ“·f®żŌ_Aśš¤->_åÅ1ķģķé¢*œŒ™¤Ż³DŚ6M«2·+FÄA"7jV Rµ+dJ@M¶&ģ ¶ŪDTĢ–>”L…¼²ōŠ„©rKhńd«PĻ‚ńɛŲöp¶štÉP²Ä“a"į›=õŃMŌ!J9€HŖdY3lÕ³ķ·bĶ6ȳfŠ $ÕŖ,½?“I³rØ ›OHž‘J ö‡nŪˆØłüa-ac-§Q¬„ø¶Æ²·~„2–u'źŹÕZXĀF=ČN6¬É¬w1é¹õHÉs™D@‡qóŲgڲG.ŁńN5±å|%BÉóŌZ¼ĘC¤ÄĢŖŠóu¬„[‹%n:Yv靬 œ¢’ēL¢r˜JZ,“ĮŽf\„Ž\Ėü<āĪUŹĶ†([äģ“ĒĢKyČH ¾¼£t³Ōe,‰ 2’N×!ņēźŸhčŠéZpV¼ć¶Ų†ė‡1]Ć34QšbūN=©X1ŪSA }, does not work in 0.10 ``` - No default serializer when serializer doesn't exist - `@options` changed to `instance_options` - Nested relationships are no longer walked by default. Use the `:include` option at **controller `render`** level to specify what relationships to walk. E.g. `render json: @post, include: {comments: :author}` if you want the `author` relationship walked, otherwise the json would only include the post with comments. See: https://github.com/rails-api/active_model_serializers/pull/1127 - To emulate `0.8`'s walking of arbitrarily deep relationships use: `include: '**'`. E.g. `render json: @post, include: '**'` ## Steps to migrate ### 1. Upgrade the `active_model_serializer` gem in you `Gemfile` Change to `gem 'active_model_serializers', '~> 0.10'` and run `bundle install` ### 2. Add `ActiveModel::V08::Serializer` ```ruby module ActiveModel module V08 class Serializer < ActiveModel::Serializer include Rails.application.routes.url_helpers # AMS 0.8 would delegate method calls from within the serializer to the # object. def method_missing(*args) method = args.first read_attribute_for_serialization(method) end alias_method :options, :instance_options # Since attributes could be read from the `object` via `method_missing`, # the `try` method did not behave as before. This patches `try` with the # original implementation plus the addition of # ` || object.respond_to?(a.first, true)` to check if the object responded to # the given method. def try(*a, &b) if a.empty? || respond_to?(a.first, true) || object.respond_to?(a.first, true) try!(*a, &b) end end # AMS 0.8 would return nil if the serializer was initialized with a nil # resource. def serializable_hash(adapter_options = nil, options = {}, adapter_instance = self.class.serialization_adapter_instance) object.nil? ? nil : super end end end end ``` Add this class to your app however you see fit. This is the class that your existing serializers that inherit from `ActiveModel::Serializer` should inherit from. ### 3. Add `ActiveModel::V08::CollectionSerializer` ```ruby module ActiveModel module V08 class CollectionSerializer < ActiveModel::Serializer::CollectionSerializer # In AMS 0.8, passing an ArraySerializer instance with a `root` option # properly nested the serialized resources within the given root. # Ex. # # class MyController < ActionController::Base # def index # render json: ActiveModel::Serializer::ArraySerializer # .new(resources, root: "resources") # end # end # # Produced # # { # "resources": [ # , # ... # ] # } def as_json(options = {}) if root { root => super } else super end end # AMS 0.8 used `DefaultSerializer` if it couldn't find a serializer for # the given resource. When not using an adapter, this is not true in # `0.10` def serializer_from_resource(resource, serializer_context_class, options) serializer_class = options.fetch(:serializer) { serializer_context_class.serializer_for(resource) } if serializer_class.nil? # rubocop:disable Style/GuardClause DefaultSerializer.new(resource, options) else serializer_class.new(resource, options.except(:serializer)) end end class DefaultSerializer attr_reader :object, :options def initialize(object, options={}) @object, @options = object, options end def serializable_hash @object.as_json(@options) end end end end end ``` Add this class to your app however you see fit. This is the class that existing uses of `ActiveModel::ArraySerializer` should be changed to use. ### 4. Add `ActiveModelSerializers::Adapter::V08Adapter` ```ruby module ActiveModelSerializers module Adapter class V08Adapter < ActiveModelSerializers::Adapter::Base def serializable_hash(options = nil) options ||= {} if serializer.respond_to?(:each) if serializer.root delegate_to_json_adapter(options) else serializable_hash_for_collection(options) end else serializable_hash_for_single_resource(options) end end def serializable_hash_for_collection(options) serializer.map do |s| V08Adapter.new(s, instance_options) .serializable_hash(options) end end def serializable_hash_for_single_resource(options) if serializer.object.is_a?(ActiveModel::Serializer) # It is recommended that you add some logging here to indicate # places that should get converted to eventually allow for this # adapter to get removed. @serializer = serializer.object end if serializer.root delegate_to_json_adapter(options) else options = serialization_options(options) serializer.serializable_hash(instance_options, options, self) end end def delegate_to_json_adapter(options) ActiveModelSerializers::Adapter::Json .new(serializer, instance_options) .serializable_hash(options) end end end end ``` Add this class to your app however you see fit. Add ```ruby ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::V08Adapter ``` to `config/active_model_serializer.rb` to configure AMS to use this class as the default adapter. ### 5. Change inheritors of `ActiveModel::Serializer` to inherit from `ActiveModel::V08::Serializer` Simple find/replace ### 6. Remove `private` keyword from serializers Simple find/replace. This is required to allow the `ActiveModel::V08::Serializer` to have proper access to the methods defined in the serializer. You may be able to change the `private` to `protected`, but this is hasn't been tested yet. ### 7. Remove references to `ActiveRecord::Base#active_model_serializer` This method is no longer supported in `0.10`. `0.10` does a good job of discovering serializers for `ActiveRecord` objects. ### 8. Rename `ActiveModel::ArraySerializer` to `ActiveModel::V08::CollectionSerializer` Find/replace uses of `ActiveModel::ArraySerializer` with `ActiveModel::V08::CollectionSerializer`. Also, be sure to change the `each_serializer` keyword to `serializer` when calling making the replacement. ### 9. Replace uses of `@options` to `instance_options` in serializers Simple find/replace ## Conclusion After you've done the steps above, you should test your app to ensure that everything is still working properly. If you run into issues, please contribute back to this document so others can benefit from your knowledge. active_model_serializers-0.10.10/docs/integrations/000077500000000000000000000000001351232231100223475ustar00rootroot00000000000000active_model_serializers-0.10.10/docs/integrations/ember-and-json-api.md000066400000000000000000000112671351232231100262500ustar00rootroot00000000000000[Back to Guides](../README.md) # Integrating with Ember and JSON API - [Preparation](./ember-and-json-api.md#preparation) - [Server-Side Changes](./ember-and-json-api.md#server-side-changes) - [Adapter Changes](./ember-and-json-api.md#adapter-changes) - [Serializer Changes](./ember-and-json-api.md#serializer-changes) - [Including Nested Resources](./ember-and-json-api.md#including-nested-resources) ## Preparation Note: This guide assumes that `ember-cli` is used for your ember app. The JSON API specification calls for hyphens for multi-word separators. ActiveModelSerializers uses underscores. To solve this, in Ember, both the adapter and the serializer will need some modifications: ### Server-Side Changes First, set the adapter type in an initializer file: ```ruby # config/initializers/active_model_serializers.rb ActiveModelSerializers.config.adapter = :json_api ``` or: ```ruby # config/initializers/active_model_serializers.rb ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonApi ``` You will also want to set the `key_transform` to `:unaltered` since you will adjust the attributes in your Ember serializer to use underscores instead of dashes later. You could also use `:underscore`, but `:unaltered` is better for performance. ```ruby # config/initializers/active_model_serializers.rb ActiveModelSerializers.config.key_transform = :unaltered ``` In order to properly handle JSON API responses, we need to register a JSON API renderer, like so: ```ruby # config/initializers/active_model_serializers.rb ActiveSupport.on_load(:action_controller) do require 'active_model_serializers/register_jsonapi_renderer' end ``` Rails also requires your controller to tell it that you accept and generate JSONAPI data. To do that, you use `respond_to` in your controller handlers to tell rails you are consuming and returning jsonapi format data. Without this, Rails will refuse to parse the request body into params. You can add `ActionController::MimeResponds` to your application controller to enable this: ```ruby class ApplicationController < ActionController::API include ActionController::MimeResponds end ``` Then, in your controller you can tell rails you're accepting and rendering the jsonapi format: ```ruby # POST /post def create @post = Post.new(post_params) respond_to do |format| if @post.save format.jsonapi { render jsonapi: @post, status: :created, location: @post } else format.jsonapi { render jsonapi: @post.errors, status: :unprocessable_entity } end end end # Only allow a trusted parameter "white list" through. def post_params ActiveModelSerializers::Deserialization.jsonapi_parse!(params, only: [:title, :body] ) end end ``` #### Note: In Rails 5, the "unsafe" method ( `jsonapi_parse!` vs the safe `jsonapi_parse`) throws an `InvalidDocument` exception when the payload does not meet basic criteria for JSON API deserialization. ### Adapter Changes ```javascript // app/adapters/application.js import Ember from 'ember'; import DS from 'ember-data'; import ENV from "../config/environment"; const { underscore, pluralize } = Ember.String; export default DS.JSONAPIAdapter.extend({ namespace: 'api', // if your rails app is on a different port from your ember app // this can be helpful for development. // in production, the host for both rails and ember should be the same. host: ENV.host, // allows the multiword paths in urls to be underscored pathForType: function(type) { let underscored = underscore(type); return pluralize(underscored); }, }); ``` ### Serializer Changes ```javascript // app/serializers/application.js import Ember from 'ember'; import DS from 'ember-data'; var underscore = Ember.String.underscore; export default DS.JSONAPISerializer.extend({ keyForAttribute: function(attr) { return underscore(attr); }, keyForRelationship: function(rawKey) { return underscore(rawKey); } }); ``` ## Including Nested Resources Ember Data can request related records by using `include`. Below are some examples of how to make Ember Data request the inclusion of related records. For more on `include` usage, see: [The JSON API include examples](./../general/adapters.md#JSON-API) ```javascript store.findRecord('post', postId, { include: 'comments' } ); ``` which will generate the path /posts/{postId}?include='comments' So then in your controller, you'll want to be sure to have something like: ```ruby render jsonapi: @post, include: params[:include] ``` If you want to use `include` on a collection, you'd write something like this: ```javascript store.query('post', { include: 'comments' }); ``` which will generate the path `/posts?include='comments'` active_model_serializers-0.10.10/docs/integrations/grape.md000066400000000000000000000012611351232231100237670ustar00rootroot00000000000000# Integration with Grape [Grape](https://github.com/ruby-grape/grape) is an opinionated micro-framework for creating REST-like APIs in ruby. ActiveModelSerializers currently supports Grape >= 0.13, < 1.0 To add [Grape](https://github.com/ruby-grape/grape) support, enable the formatter and helper functions by including `Grape::ActiveModelSerializers` in your base endpoint. For example: ```ruby module Example class Dummy < Grape::API require 'grape/active_model_serializers' include Grape::ActiveModelSerializers mount Example::V1::Base end end ``` Aside from this, [configuration](../general/configuration_options.md) of ActiveModelSerializers is exactly the same. active_model_serializers-0.10.10/docs/jsonapi/000077500000000000000000000000001351232231100213045ustar00rootroot00000000000000active_model_serializers-0.10.10/docs/jsonapi/errors.md000066400000000000000000000034201351232231100231410ustar00rootroot00000000000000[Back to Guides](../README.md) # [JSON API Errors](http://jsonapi.org/format/#errors) Rendering error documents requires specifying the error serializer(s): - Serializer: - For a single resource: `serializer: ActiveModel::Serializer::ErrorSerializer`. - For a collection: `serializer: ActiveModel::Serializer::ErrorsSerializer`, `each_serializer: ActiveModel::Serializer::ErrorSerializer`. The resource **MUST** have a non-empty associated `#errors` object. The `errors` object must have a `#messages` method that returns a hash of error name to array of descriptions. ## Use in controllers ```ruby resource = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') resource.errors.add(:name, 'cannot be nil') resource.errors.add(:name, 'must be longer') resource.errors.add(:id, 'must be a uuid') render json: resource, status: 422, adapter: :json_api, serializer: ActiveModel::Serializer::ErrorSerializer # #=> # { :errors => # [ # { :source => { :pointer => '/data/attributes/name' }, :detail => 'cannot be nil' }, # { :source => { :pointer => '/data/attributes/name' }, :detail => 'must be longer' }, # { :source => { :pointer => '/data/attributes/id' }, :detail => 'must be a uuid' } # ] # }.to_json ``` ## Direct error document generation ```ruby options = nil resource = ModelWithErrors.new resource.errors.add(:name, 'must be awesome') serializable_resource = ActiveModelSerializers::SerializableResource.new( resource, { serializer: ActiveModel::Serializer::ErrorSerializer, adapter: :json_api }) serializable_resource.as_json(options) # #=> # { # :errors => # [ # { :source => { :pointer => '/data/attributes/name' }, :detail => 'must be awesome' } # ] # } ``` active_model_serializers-0.10.10/docs/jsonapi/schema.md000066400000000000000000000232131351232231100230670ustar00rootroot00000000000000[Back to Guides](../README.md) [![JSON API 1.0](https://img.shields.io/badge/JSON%20API-1.0-lightgrey.svg)](http://jsonapi.org/) ## JSON API Requests - [Query Parameters Spec](http://jsonapi.org/format/#query-parameters) Headers: - Request: `Accept: application/vnd.api+json` - Response: `Content-Type: application/vnd.api+json` ### [Fetching Data](http://jsonapi.org/format/#fetching) A server MUST support fetching resource data for every URL provided as: - a `self` link as part of the top-level links object - a `self` link as part of a resource-level links object - a `related` link as part of a relationship-level links object Example supported requests - Individual resource or collection - GET /articles - GET /articles/1 - GET /articles/1/author - Relationships - GET /articles/1/relationships/comments - GET /articles/1/relationships/author - Optional: [Inclusion of related resources](http://jsonapi.org/format/#fetching-includes) `JSONAPI::IncludeDirective` - GET /articles/1?`include`=comments - GET /articles/1?`include`=comments.author - GET /articles/1?`include`=author,comments.author - GET /articles/1/relationships/comments?`include`=comments.author - Optional: [Sparse Fieldsets](http://jsonapi.org/format/#fetching-sparse-fieldsets) `ActiveModel::Serializer::Fieldset` - GET /articles?`include`=author&`fields`[articles]=title,body&`fields`[people]=name - Optional: [Sorting](http://jsonapi.org/format/#fetching-sorting) - GET /people?`sort`=age - GET /people?`sort`=age,author.name - GET /articles?`sort`=-created,title - Optional: [Pagination](http://jsonapi.org/format/#fetching-pagination) - GET /articles?`page`[number]=3&`page`[size]=1 - Optional: [Filtering](http://jsonapi.org/format/#fetching-filtering) - GET /comments?`filter`[post]=1 - GET /comments?`filter`[post]=1,2 - GET /comments?`filter`[post]=1,2 ### [CRUD Actions](http://jsonapi.org/format/#crud) ### [Asynchronous Processing](http://jsonapi.org/recommendations/#asynchronous-processing) ### [Bulk Operations Extension](http://jsonapi.org/extensions/bulk/) ## JSON API Document Schema | JSON API object | JSON API properties | Required | ActiveModelSerializers representation | |-----------------------|----------------------------------------------------------------------------------------------------|----------|---------------------------------------| | schema | oneOf (success, failure, info) | | | success | data, included, meta, links, jsonapi | | AM::SerializableResource | success.meta | meta | | AMS::Adapter::Base#meta | success.included | UniqueArray(resource) | | AMS::Adapter::JsonApi#serializable_hash_for_collection | success.data | data | | | success.links | allOf (links, pagination) | | AMS::Adapter::JsonApi#links_for | success.jsonapi | jsonapi | | | failure | errors, meta, jsonapi | errors | AMS::Adapter::JsonApi#failure_document, #1004 | failure.errors | UniqueArray(error) | | AM::S::ErrorSerializer, #1004 | meta | Object | | | data | oneOf (resource, UniqueArray(resource)) | | AMS::Adapter::JsonApi#serializable_hash_for_collection,#serializable_hash_for_single_resource | resource | String(type), String(id),
attributes, relationships,
links, meta | type, id | AM::S::Adapter::JsonApi#primary_data_for | links | Uri(self), Link(related) | | #1028, #1246, #1282 | link | oneOf (linkString, linkObject) | | | link.linkString | Uri | | | link.linkObject | Uri(href), meta | href | | attributes | patternProperties(
`"^(?!relationships$|links$)\\w[-\\w_]*$"`),
any valid JSON | | AM::Serializer#attributes, AMS::Adapter::JsonApi#resource_object_for | relationships | patternProperties(
`"^\\w[-\\w_]*$"`);
links, relationships.data, meta | | AMS::Adapter::JsonApi#relationships_for | relationships.data | oneOf (relationshipToOne, relationshipToMany) | | AMS::Adapter::JsonApi#resource_identifier_for | relationshipToOne | anyOf(empty, linkage) | | | relationshipToMany | UniqueArray(linkage) | | | empty | null | | | linkage | String(type), String(id), meta | type, id | AMS::Adapter::JsonApi#primary_data_for | pagination | pageObject(first), pageObject(last),
pageObject(prev), pageObject(next) | | AMS::Adapter::JsonApi::PaginationLinks#serializable_hash | pagination.pageObject | oneOf(Uri, null) | | | jsonapi | String(version), meta | | AMS::Adapter::JsonApi::Jsonapi#as_json | error | String(id), links, String(status),
String(code), String(title),
String(detail), error.source, meta | | AM::S::ErrorSerializer, AMS::Adapter::JsonApi::Error.resource_errors | error.source | String(pointer), String(parameter) | | AMS::Adapter::JsonApi::Error.error_source | pointer | [JSON Pointer RFC6901](https://tools.ietf.org/html/rfc6901) | | AMS::JsonPointer The [http://jsonapi.org/schema](schema/schema.json) makes a nice roadmap. ### Success Document - [ ] success - [ ] data: `"$ref": "#/definitions/data"` - [ ] included: array of unique items of type `"$ref": "#/definitions/resource"` - [ ] meta: `"$ref": "#/definitions/meta"` - [ ] links: - [ ] link: `"$ref": "#/definitions/links"` - [ ] pagination: ` "$ref": "#/definitions/pagination"` - [ ] jsonapi: ` "$ref": "#/definitions/jsonapi"` ### Failure Document - [ ] failure - [x] errors: array of unique items of type ` "$ref": "#/definitions/error"` - [ ] meta: `"$ref": "#/definitions/meta"` - [ ] jsonapi: `"$ref": "#/definitions/jsonapi"` ### Info Document - [ ] info - [ ] meta: `"$ref": "#/definitions/meta"` - [ ] links: `"$ref": "#/definitions/links"` - [ ] jsonapi: ` "$ref": "#/definitions/jsonapi"` ### Definitions - [ ] definitions: - [ ] meta - [ ] data: oneOf (resource, array of unique resources) - [ ] resource - [ ] attributes - [ ] relationships - [ ] relationshipToOne - [ ] empty - [ ] linkage - [ ] meta - [ ] relationshipToMany - [ ] linkage - [ ] meta - [ ] links - [ ] meta - [ ] links - [ ] link - [ ] uri - [ ] href, meta - [ ] pagination - [ ] jsonapi - [ ] meta - [ ] error - [ ] id: a unique identifier for this particular occurrence of the problem. - [ ] links: a links object containing the following members: - [ ] about: a link that leads to further details about this particular occurrence of the problem. - [ ] status: the HTTP status code applicable to this problem, expressed as a string value. - [ ] code: an application-specific error code, expressed as a string value. - [ ] title: a short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization. - [x] detail: a human-readable explanation specific to this occurrence of the problem. - [x] source: an object containing references to the source of the error, optionally including any of the following members: - [x] pointer: a JSON Pointer [RFC6901](https://tools.ietf.org/html/rfc6901) to the associated entity in the request document [e.g. "/data" for a primary data object, or "/data/attributes/title" for a specific attribute]. - [x] parameter: a string indicating which query parameter caused the error. - [ ] meta: a meta object containing non-standard meta-information about the error. active_model_serializers-0.10.10/docs/jsonapi/schema/000077500000000000000000000000001351232231100225445ustar00rootroot00000000000000active_model_serializers-0.10.10/docs/jsonapi/schema/schema.json000066400000000000000000000245131351232231100247040ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "title": "JSON API Schema", "description": "This is a schema for responses in the JSON API format. For more, see http://jsonapi.org", "oneOf": [ { "$ref": "#/definitions/success" }, { "$ref": "#/definitions/failure" }, { "$ref": "#/definitions/info" } ], "definitions": { "success": { "type": "object", "required": [ "data" ], "properties": { "data": { "$ref": "#/definitions/data" }, "included": { "description": "To reduce the number of HTTP requests, servers **MAY** allow responses that include related resources along with the requested primary resources. Such responses are called \"compound documents\".", "type": "array", "items": { "$ref": "#/definitions/resource" }, "uniqueItems": true }, "meta": { "$ref": "#/definitions/meta" }, "links": { "description": "Link members related to the primary data.", "allOf": [ { "$ref": "#/definitions/links" }, { "$ref": "#/definitions/pagination" } ] }, "jsonapi": { "$ref": "#/definitions/jsonapi" } }, "additionalProperties": false }, "failure": { "type": "object", "required": [ "errors" ], "properties": { "errors": { "type": "array", "items": { "$ref": "#/definitions/error" }, "uniqueItems": true }, "meta": { "$ref": "#/definitions/meta" }, "jsonapi": { "$ref": "#/definitions/jsonapi" } }, "additionalProperties": false }, "info": { "type": "object", "required": [ "meta" ], "properties": { "meta": { "$ref": "#/definitions/meta" }, "links": { "$ref": "#/definitions/links" }, "jsonapi": { "$ref": "#/definitions/jsonapi" } }, "additionalProperties": false }, "meta": { "description": "Non-standard meta-information that can not be represented as an attribute or relationship.", "type": "object", "additionalProperties": true }, "data": { "description": "The document's \"primary data\" is a representation of the resource or collection of resources targeted by a request.", "oneOf": [ { "$ref": "#/definitions/resource" }, { "description": "An array of resource objects, an array of resource identifier objects, or an empty array ([]), for requests that target resource collections.", "type": "array", "items": { "$ref": "#/definitions/resource" }, "uniqueItems": true } ] }, "resource": { "description": "\"Resource objects\" appear in a JSON API document to represent resources.", "type": "object", "required": [ "type", "id" ], "properties": { "type": { "type": "string" }, "id": { "type": "string" }, "attributes": { "$ref": "#/definitions/attributes" }, "relationships": { "$ref": "#/definitions/relationships" }, "links": { "$ref": "#/definitions/links" }, "meta": { "$ref": "#/definitions/meta" } }, "additionalProperties": false }, "links": { "description": "A resource object **MAY** contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.", "type": "object", "properties": { "self": { "description": "A `self` member, whose value is a URL for the relationship itself (a \"relationship URL\"). This URL allows the client to directly manipulate the relationship. For example, it would allow a client to remove an `author` from an `article` without deleting the people resource itself.", "type": "string", "format": "uri" }, "related": { "$ref": "#/definitions/link" } }, "additionalProperties": true }, "link": { "description": "A link **MUST** be represented as either: a string containing the link's URL or a link object.", "oneOf": [ { "description": "A string containing the link's URL.", "type": "string", "format": "uri" }, { "type": "object", "required": [ "href" ], "properties": { "href": { "description": "A string containing the link's URL.", "type": "string", "format": "uri" }, "meta": { "$ref": "#/definitions/meta" } } } ] }, "attributes": { "description": "Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined.", "type": "object", "patternProperties": { "^(?!relationships$|links$)\\w[-\\w_]*$": { "description": "Attributes may contain any valid JSON value." } }, "additionalProperties": false }, "relationships": { "description": "Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects.", "type": "object", "patternProperties": { "^\\w[-\\w_]*$": { "properties": { "links": { "$ref": "#/definitions/links" }, "data": { "description": "Member, whose value represents \"resource linkage\".", "oneOf": [ { "$ref": "#/definitions/relationshipToOne" }, { "$ref": "#/definitions/relationshipToMany" } ] }, "meta": { "$ref": "#/definitions/meta" } }, "additionalProperties": false } }, "additionalProperties": false }, "relationshipToOne": { "description": "References to other resource objects in a to-one (\"relationship\"). Relationships can be specified by including a member in a resource's links object.", "anyOf": [ { "$ref": "#/definitions/empty" }, { "$ref": "#/definitions/linkage" } ] }, "relationshipToMany": { "description": "An array of objects each containing \"type\" and \"id\" members for to-many relationships.", "type": "array", "items": { "$ref": "#/definitions/linkage" }, "uniqueItems": true }, "empty": { "description": "Describes an empty to-one relationship.", "type": "null" }, "linkage": { "description": "The \"type\" and \"id\" to non-empty members.", "type": "object", "required": [ "type", "id" ], "properties": { "type": { "type": "string" }, "id": { "type": "string" }, "meta": { "$ref": "#/definitions/meta" } }, "additionalProperties": false }, "pagination": { "type": "object", "properties": { "first": { "description": "The first page of data", "oneOf": [ { "type": "string", "format": "uri" }, { "type": "null" } ] }, "last": { "description": "The last page of data", "oneOf": [ { "type": "string", "format": "uri" }, { "type": "null" } ] }, "prev": { "description": "The previous page of data", "oneOf": [ { "type": "string", "format": "uri" }, { "type": "null" } ] }, "next": { "description": "The next page of data", "oneOf": [ { "type": "string", "format": "uri" }, { "type": "null" } ] } } }, "jsonapi": { "description": "An object describing the server's implementation", "type": "object", "properties": { "version": { "type": "string" }, "meta": { "$ref": "#/definitions/meta" } }, "additionalProperties": false }, "error": { "type": "object", "properties": { "id": { "description": "A unique identifier for this particular occurrence of the problem.", "type": "string" }, "links": { "$ref": "#/definitions/links" }, "status": { "description": "The HTTP status code applicable to this problem, expressed as a string value.", "type": "string" }, "code": { "description": "An application-specific error code, expressed as a string value.", "type": "string" }, "title": { "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.", "type": "string" }, "detail": { "description": "A human-readable explanation specific to this occurrence of the problem.", "type": "string" }, "source": { "type": "object", "properties": { "pointer": { "description": "A JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].", "type": "string" }, "parameter": { "description": "A string indicating which query parameter caused the error.", "type": "string" } } }, "meta": { "$ref": "#/definitions/meta" } }, "additionalProperties": false } } } active_model_serializers-0.10.10/docs/rfcs/000077500000000000000000000000001351232231100205765ustar00rootroot00000000000000active_model_serializers-0.10.10/docs/rfcs/0000-namespace.md000066400000000000000000000113351351232231100234340ustar00rootroot00000000000000- Start Date: (2015-10-29) - RFC PR: https://github.com/rails-api/active_model_serializers/pull/1310 - ActiveModelSerializers Issue: https://github.com/rails-api/active_model_serializers/issues/1298 # Summary Provide a consistent API for the user of the AMS. # Motivation The actual public API is defined under `ActiveModelSerializers`, `ActiveModel::Serializer` and `ActiveModel`. At the `ActiveModel::Serializer` we have: - `ActiveModel::Serializer.config` - `ActiveModel::Serializer` At the `ActiveModelSerializers` we have: - `ActiveModelSerializers::Model` - `ActiveModelSerializers.logger` At `ActiveModel` we have: - `ActiveModel::SerializableResource` The idea here is to provide a single namespace `ActiveModelSerializers` to the user. Following the same idea we have on other gems like [Devise](https://github.com/plataformatec/devise/blob/e9c82472ffe7c43a448945f77e034a0e47dde0bb/lib/devise.rb), [Refile](https://github.com/refile/refile/blob/6b24c293d044862dafbf1bfa4606672a64903aa2/lib/refile.rb) and [Active Job](https://github.com/rails/rails/blob/30bacc26f8f258b39e12f63fe52389a968d9c1ea/activejob/lib/active_job.rb) for example. This way we are clarifing the boundaries of [ActiveModelSerializers and Rails](https://github.com/rails-api/active_model_serializers/blob/master/CHANGELOG.md#prehistory) and make clear that the `ActiveModel::Serializer` class is no longer the primary behavior of the ActiveModelSerializers. # Detailed design ## New classes and modules organization Since this will be a big change we can do this on baby steps, read small pull requests. A possible approach is: - All new code will be in `lib/active_model_serializers/` using the module namespace `ActiveModelSerializers`. - Move all content under `ActiveModel::Serializer` to be under `ActiveModelSerializers`, the adapter is on this steps; - Move all content under `ActiveModel` to be under `ActiveModelSerializers`, the `SerializableResource` is on this step; - Change all public API that doesn't make sense, keeping in mind only to keep this in the same namespace - Update the README; - Update the docs; The following table represents the current and the desired classes and modules at the first moment. | Current | Desired | Notes | |--------------------------------------------------------|--------------------------------------------------|--------------------| | `ActiveModelSerializers` and `ActiveModel::Serializer` | `ActiveModelSerializers` | The main namespace | | `ActiveModelSerializers.logger` | `ActiveModelSerializers.logger` || | `ActiveModelSerializers::Model` | `ActiveModelSerializers::Model` || | `ActiveModel::SerializableResource` | `ActiveModelSerializers::SerializableResource` || | `ActiveModel::Serializer` | `ActiveModelSerializers::Serializer` | The name can be discussed in a future pull request. For example, we can rename this to `Resource` [following this idea](https://github.com/rails-api/active_model_serializers/pull/1301/files#r42963185) more info about naming in the next section| | `ActiveModel::Serializer.config` | `ActiveModelSerializers.config` || ## Renaming of class and modules When moving some content to the new namespace we can find some names that does not make much sense like `ActiveModel::Serializer::Adapter::JsonApi`. Discussion of renaming existing classes / modules and JsonApi objects will happen in separate pull requests, and issues, and in the google doc https://docs.google.com/document/d/1rcrJr0sVcazY2Opd_6Kmv1iIwuHbI84s1P_NzFn-05c/edit?usp=sharing Some of names already have a definition. - Adapters get their own namespace under ActiveModelSerializers. E.g `ActiveModelSerializers::Adapter` - Serializers get their own namespace under ActiveModelSerializers. E.g `ActiveModelSerializers::Serializer` ## Keeping compatibility All moved classes or modules be aliased to their old name and location with deprecation warnings, such as [was done for CollectionSerializer](https://github.com/rails-api/active_model_serializers/pull/1251). # Drawbacks This will be a breaking change, so all users serializers will be broken after a major bump. All pull requests will need to rebase since the architeture will change a lot. # Alternatives We can keep the way it is, and keep in mind to not add another namespace as a public API. # Unresolved questions What is the better class name to be used to the class that will be inherited at the creation of a serializer. This can be discussed in other RFC or directly via pull request. active_model_serializers-0.10.10/docs/rfcs/template.md000066400000000000000000000004501351232231100227320ustar00rootroot00000000000000- Start Date: (YYYY-MM-DD) - RFC PR: https://github.com/rails-api/active_model_serializers/pull/dddd - ActiveModelSerializers Issue: https://github.com/rails-api/active_model_serializers/issues/dddd # Summary # Motivation # Detailed design # Drawbacks # Alternatives # Unresolved questions active_model_serializers-0.10.10/lib/000077500000000000000000000000001351232231100174575ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/action_controller/000077500000000000000000000000001351232231100231775ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/action_controller/serialization.rb000066400000000000000000000050371351232231100264060ustar00rootroot00000000000000# frozen_string_literal: true require 'active_support/core_ext/class/attribute' require 'active_model_serializers/serialization_context' module ActionController module Serialization extend ActiveSupport::Concern include ActionController::Renderers module ClassMethods def serialization_scope(scope) self._serialization_scope = scope end end included do class_attribute :_serialization_scope self._serialization_scope = :current_user attr_writer :namespace_for_serializer end def namespace_for_serializer @namespace_for_serializer ||= namespace_for_class(self.class) unless namespace_for_class(self.class) == Object end def namespace_for_class(klass) if Module.method_defined?(:module_parent) klass.module_parent else klass.parent end end def serialization_scope return unless _serialization_scope && respond_to?(_serialization_scope, true) send(_serialization_scope) end def get_serializer(resource, options = {}) unless use_adapter? warn 'ActionController::Serialization#use_adapter? has been removed. '\ "Please pass 'adapter: false' or see ActiveSupport::SerializableResource.new" options[:adapter] = false end options.fetch(:namespace) { options[:namespace] = namespace_for_serializer } serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options) serializable_resource.serialization_scope ||= options.fetch(:scope) { serialization_scope } serializable_resource.serialization_scope_name = options.fetch(:scope_name) { _serialization_scope } # For compatibility with the JSON renderer: `json.to_json(options) if json.is_a?(String)`. # Otherwise, since `serializable_resource` is not a string, the renderer would call # `to_json` on a String and given odd results, such as `"".to_json #=> '""'` serializable_resource.adapter.is_a?(String) ? serializable_resource.adapter : serializable_resource end # Deprecated def use_adapter? true end [:_render_option_json, :_render_with_renderer_json].each do |renderer_method| define_method renderer_method do |resource, options| options.fetch(:serialization_context) do options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request, options) end serializable_resource = get_serializer(resource, options) super(serializable_resource, options) end end end end active_model_serializers-0.10.10/lib/active_model/000077500000000000000000000000001351232231100221125ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/active_model/serializable_resource.rb000066400000000000000000000004021351232231100270100ustar00rootroot00000000000000# frozen_string_literal: true require 'set' module ActiveModel class SerializableResource class << self extend ActiveModelSerializers::Deprecate delegate_and_deprecate :new, ActiveModelSerializers::SerializableResource end end end active_model_serializers-0.10.10/lib/active_model/serializer.rb000066400000000000000000000350711351232231100246160ustar00rootroot00000000000000# frozen_string_literal: true require 'thread_safe' require 'jsonapi/include_directive' require 'active_model/serializer/collection_serializer' require 'active_model/serializer/array_serializer' require 'active_model/serializer/error_serializer' require 'active_model/serializer/errors_serializer' require 'active_model/serializer/concerns/caching' require 'active_model/serializer/fieldset' require 'active_model/serializer/lint' # ActiveModel::Serializer is an abstract class that is # reified when subclassed to decorate a resource. module ActiveModel class Serializer undef_method :select, :display # These IO methods, which are mixed into Kernel, # sometimes conflict with attribute names. We don't need these IO methods. # @see #serializable_hash for more details on these valid keys. SERIALIZABLE_HASH_VALID_KEYS = [:only, :except, :methods, :include, :root].freeze extend ActiveSupport::Autoload eager_autoload do autoload :Adapter autoload :Null autoload :Attribute autoload :Link autoload :Association autoload :Reflection autoload :BelongsToReflection autoload :HasOneReflection autoload :HasManyReflection end include ActiveSupport::Configurable include Caching # @param resource [ActiveRecord::Base, ActiveModelSerializers::Model] # @return [ActiveModel::Serializer] # Preferentially returns # 1. resource.serializer_class # 2. ArraySerializer when resource is a collection # 3. options[:serializer] # 4. lookup serializer when resource is a Class def self.serializer_for(resource_or_class, options = {}) if resource_or_class.respond_to?(:serializer_class) resource_or_class.serializer_class elsif resource_or_class.respond_to?(:to_ary) config.collection_serializer else resource_class = resource_or_class.class == Class ? resource_or_class : resource_or_class.class options.fetch(:serializer) { get_serializer_for(resource_class, options[:namespace]) } end end # @see ActiveModelSerializers::Adapter.lookup # Deprecated def self.adapter ActiveModelSerializers::Adapter.lookup(config.adapter) end class << self extend ActiveModelSerializers::Deprecate deprecate :adapter, 'ActiveModelSerializers::Adapter.configured_adapter' end # @api private def self.serializer_lookup_chain_for(klass, namespace = nil) lookups = ActiveModelSerializers.config.serializer_lookup_chain Array[*lookups].flat_map do |lookup| lookup.call(klass, self, namespace) end.compact end # Used to cache serializer name => serializer class # when looked up by Serializer.get_serializer_for. def self.serializers_cache @serializers_cache ||= ThreadSafe::Cache.new end # @api private # Find a serializer from a class and caches the lookup. # Preferentially returns: # 1. class name appended with "Serializer" # 2. try again with superclass, if present # 3. nil def self.get_serializer_for(klass, namespace = nil) return nil unless config.serializer_lookup_enabled cache_key = ActiveSupport::Cache.expand_cache_key(klass, namespace) serializers_cache.fetch_or_store(cache_key) do # NOTE(beauby): When we drop 1.9.3 support we can lazify the map for perfs. lookup_chain = serializer_lookup_chain_for(klass, namespace) serializer_class = lookup_chain.map(&:safe_constantize).find { |x| x && x < ActiveModel::Serializer } if serializer_class serializer_class elsif klass.superclass get_serializer_for(klass.superclass, namespace) else nil # No serializer found end end end # @api private def self.include_directive_from_options(options) if options[:include_directive] options[:include_directive] elsif options[:include] JSONAPI::IncludeDirective.new(options[:include], allow_wildcard: true) else ActiveModelSerializers.default_include_directive end end # @api private def self.serialization_adapter_instance @serialization_adapter_instance ||= ActiveModelSerializers::Adapter::Attributes end # Preferred interface is ActiveModelSerializers.config # BEGIN DEFAULT CONFIGURATION config.collection_serializer = ActiveModel::Serializer::CollectionSerializer config.serializer_lookup_enabled = true # @deprecated Use {#config.collection_serializer=} instead of this. Is # compatibility layer for ArraySerializer. def config.array_serializer=(collection_serializer) self.collection_serializer = collection_serializer end # @deprecated Use {#config.collection_serializer} instead of this. Is # compatibility layer for ArraySerializer. def config.array_serializer collection_serializer end config.default_includes = '*' config.adapter = :attributes config.key_transform = nil config.jsonapi_pagination_links_enabled = true config.jsonapi_resource_type = :plural config.jsonapi_namespace_separator = '-'.freeze config.jsonapi_version = '1.0' config.jsonapi_toplevel_meta = {} # Make JSON API top-level jsonapi member opt-in # ref: http://jsonapi.org/format/#document-top-level config.jsonapi_include_toplevel_object = false config.jsonapi_use_foreign_key_on_belongs_to_relationship = false config.include_data_default = true # For configuring how serializers are found. # This should be an array of procs. # # The priority of the output is that the first item # in the evaluated result array will take precedence # over other possible serializer paths. # # i.e.: First match wins. # # @example output # => [ # "CustomNamespace::ResourceSerializer", # "ParentSerializer::ResourceSerializer", # "ResourceNamespace::ResourceSerializer" , # "ResourceSerializer"] # # If CustomNamespace::ResourceSerializer exists, it will be used # for serialization config.serializer_lookup_chain = ActiveModelSerializers::LookupChain::DEFAULT.dup config.schema_path = 'test/support/schemas' # END DEFAULT CONFIGURATION with_options instance_writer: false, instance_reader: false do |serializer| serializer.class_attribute :_attributes_data # @api private self._attributes_data ||= {} end with_options instance_writer: false, instance_reader: true do |serializer| serializer.class_attribute :_reflections self._reflections ||= {} serializer.class_attribute :_links # @api private self._links ||= {} serializer.class_attribute :_meta # @api private serializer.class_attribute :_type # @api private end def self.inherited(base) super base._attributes_data = _attributes_data.dup base._reflections = _reflections.dup base._links = _links.dup end # @return [Array] Key names of declared attributes # @see Serializer::attribute def self._attributes _attributes_data.keys end # BEGIN SERIALIZER MACROS # @example # class AdminAuthorSerializer < ActiveModel::Serializer # attributes :id, :name, :recent_edits def self.attributes(*attrs) attrs = attrs.first if attrs.first.class == Array attrs.each do |attr| attribute(attr) end end # @example # class AdminAuthorSerializer < ActiveModel::Serializer # attributes :id, :recent_edits # attribute :name, key: :title # # attribute :full_name do # "#{object.first_name} #{object.last_name}" # end # # def recent_edits # object.edits.last(5) # end def self.attribute(attr, options = {}, &block) key = options.fetch(:key, attr) _attributes_data[key] = Attribute.new(attr, options, block) end # @param [Symbol] name of the association # @param [Hash any>] options for the reflection # @return [void] # # @example # has_many :comments, serializer: CommentSummarySerializer # def self.has_many(name, options = {}, &block) # rubocop:disable Style/PredicateName associate(HasManyReflection.new(name, options, block)) end # @param [Symbol] name of the association # @param [Hash any>] options for the reflection # @return [void] # # @example # belongs_to :author, serializer: AuthorSerializer # def self.belongs_to(name, options = {}, &block) associate(BelongsToReflection.new(name, options, block)) end # @param [Symbol] name of the association # @param [Hash any>] options for the reflection # @return [void] # # @example # has_one :author, serializer: AuthorSerializer # def self.has_one(name, options = {}, &block) # rubocop:disable Style/PredicateName associate(HasOneReflection.new(name, options, block)) end # Add reflection and define {name} accessor. # @param [ActiveModel::Serializer::Reflection] reflection # @return [void] # # @api private def self.associate(reflection) key = reflection.options[:key] || reflection.name self._reflections[key] = reflection end private_class_method :associate # Define a link on a serializer. # @example # link(:self) { resource_url(object) } # @example # link(:self) { "http://example.com/resource/#{object.id}" } # @example # link :resource, "http://example.com/resource" # @example # link(:callback, if: :internal?), { "http://example.com/callback" } # def self.link(name, *args, &block) options = args.extract_options! # For compatibility with the use case of passing link directly as string argument # without block, we are creating a wrapping block _links[name] = Link.new(name, options, block || ->(_serializer) { args.first }) end # Set the JSON API meta attribute of a serializer. # @example # class AdminAuthorSerializer < ActiveModel::Serializer # meta { stuff: 'value' } # @example # meta do # { comment_count: object.comments.count } # end def self.meta(value = nil, &block) self._meta = block || value end # Set the JSON API type of a serializer. # @example # class AdminAuthorSerializer < ActiveModel::Serializer # type 'authors' def self.type(type) self._type = type && type.to_s end # END SERIALIZER MACROS attr_accessor :object, :root, :scope # `scope_name` is set as :current_user by default in the controller. # If the instance does not have a method named `scope_name`, it # defines the method so that it calls the +scope+. def initialize(object, options = {}) self.object = object self.instance_options = options self.root = instance_options[:root] self.scope = instance_options[:scope] return if !(scope_name = instance_options[:scope_name]) || respond_to?(scope_name) define_singleton_method scope_name, -> { scope } end def success? true end # Return the +attributes+ of +object+ as presented # by the serializer. def attributes(requested_attrs = nil, reload = false) @attributes = nil if reload @attributes ||= self.class._attributes_data.each_with_object({}) do |(key, attr), hash| next if attr.excluded?(self) next unless requested_attrs.nil? || requested_attrs.include?(key) hash[key] = attr.value(self) end end # @param [JSONAPI::IncludeDirective] include_directive (defaults to the # +default_include_directive+ config value when not provided) # @return [Enumerator] def associations(include_directive = ActiveModelSerializers.default_include_directive, include_slice = nil) include_slice ||= include_directive return Enumerator.new {} unless object Enumerator.new do |y| (self.instance_reflections ||= self.class._reflections.deep_dup).each do |key, reflection| next if reflection.excluded?(self) next unless include_directive.key?(key) association = reflection.build_association(self, instance_options, include_slice) y.yield association end end end # @return [Hash] containing the attributes and first level # associations, similar to how ActiveModel::Serializers::JSON is used # in ActiveRecord::Base. def serializable_hash(adapter_options = nil, options = {}, adapter_instance = self.class.serialization_adapter_instance) adapter_options ||= {} options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options) if (fieldset = adapter_options[:fieldset]) options[:fields] = fieldset.fields_for(json_key) end resource = attributes_hash(adapter_options, options, adapter_instance) relationships = associations_hash(adapter_options, options, adapter_instance) resource.merge(relationships) end alias to_hash serializable_hash alias to_h serializable_hash # @see #serializable_hash def as_json(adapter_opts = nil) serializable_hash(adapter_opts) end # Used by adapter as resource root. def json_key root || _type || begin object.class.model_name.to_s.underscore rescue ArgumentError 'anonymous_object' end end def read_attribute_for_serialization(attr) if respond_to?(attr) send(attr) else object.read_attribute_for_serialization(attr) end end # @api private def attributes_hash(_adapter_options, options, adapter_instance) if self.class.cache_enabled? fetch_attributes(options[:fields], options[:cached_attributes] || {}, adapter_instance) elsif self.class.fragment_cache_enabled? fetch_attributes_fragment(adapter_instance, options[:cached_attributes] || {}) else attributes(options[:fields], true) end end # @api private def associations_hash(adapter_options, options, adapter_instance) include_directive = options.fetch(:include_directive) include_slice = options[:include_slice] associations(include_directive, include_slice).each_with_object({}) do |association, relationships| adapter_opts = adapter_options.merge(include_directive: include_directive[association.key], adapter_instance: adapter_instance) relationships[association.key] = association.serializable_hash(adapter_opts, adapter_instance) end end protected attr_accessor :instance_options, :instance_reflections end end active_model_serializers-0.10.10/lib/active_model/serializer/000077500000000000000000000000001351232231100242635ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/active_model/serializer/adapter.rb000066400000000000000000000014631351232231100262340ustar00rootroot00000000000000# frozen_string_literal: true require 'active_model_serializers/adapter' require 'active_model_serializers/deprecate' module ActiveModel class Serializer # @deprecated Use ActiveModelSerializers::Adapter instead module Adapter class << self extend ActiveModelSerializers::Deprecate DEPRECATED_METHODS = [:create, :adapter_class, :adapter_map, :adapters, :register, :lookup].freeze DEPRECATED_METHODS.each do |method| delegate_and_deprecate method, ActiveModelSerializers::Adapter end end end end end require 'active_model/serializer/adapter/base' require 'active_model/serializer/adapter/null' require 'active_model/serializer/adapter/attributes' require 'active_model/serializer/adapter/json' require 'active_model/serializer/adapter/json_api' active_model_serializers-0.10.10/lib/active_model/serializer/adapter/000077500000000000000000000000001351232231100257035ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/active_model/serializer/adapter/attributes.rb000066400000000000000000000007631351232231100304240ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer module Adapter class Attributes < DelegateClass(ActiveModelSerializers::Adapter::Attributes) def initialize(serializer, options = {}) super(ActiveModelSerializers::Adapter::Attributes.new(serializer, options)) end class << self extend ActiveModelSerializers::Deprecate deprecate :new, 'ActiveModelSerializers::Adapter::Json.' end end end end end active_model_serializers-0.10.10/lib/active_model/serializer/adapter/base.rb000066400000000000000000000010141351232231100271360ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer module Adapter class Base < DelegateClass(ActiveModelSerializers::Adapter::Base) class << self extend ActiveModelSerializers::Deprecate deprecate :inherited, 'ActiveModelSerializers::Adapter::Base.' end # :nocov: def initialize(serializer, options = {}) super(ActiveModelSerializers::Adapter::Base.new(serializer, options)) end # :nocov: end end end end active_model_serializers-0.10.10/lib/active_model/serializer/adapter/json.rb000066400000000000000000000007441351232231100272060ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer module Adapter class Json < DelegateClass(ActiveModelSerializers::Adapter::Json) def initialize(serializer, options = {}) super(ActiveModelSerializers::Adapter::Json.new(serializer, options)) end class << self extend ActiveModelSerializers::Deprecate deprecate :new, 'ActiveModelSerializers::Adapter::Json.new' end end end end end active_model_serializers-0.10.10/lib/active_model/serializer/adapter/json_api.rb000066400000000000000000000007601351232231100300350ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer module Adapter class JsonApi < DelegateClass(ActiveModelSerializers::Adapter::JsonApi) def initialize(serializer, options = {}) super(ActiveModelSerializers::Adapter::JsonApi.new(serializer, options)) end class << self extend ActiveModelSerializers::Deprecate deprecate :new, 'ActiveModelSerializers::Adapter::JsonApi.new' end end end end end active_model_serializers-0.10.10/lib/active_model/serializer/adapter/null.rb000066400000000000000000000007441351232231100272070ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer module Adapter class Null < DelegateClass(ActiveModelSerializers::Adapter::Null) def initialize(serializer, options = {}) super(ActiveModelSerializers::Adapter::Null.new(serializer, options)) end class << self extend ActiveModelSerializers::Deprecate deprecate :new, 'ActiveModelSerializers::Adapter::Null.new' end end end end end active_model_serializers-0.10.10/lib/active_model/serializer/array_serializer.rb000066400000000000000000000005311351232231100301560ustar00rootroot00000000000000# frozen_string_literal: true require 'active_model/serializer/collection_serializer' module ActiveModel class Serializer class ArraySerializer < CollectionSerializer class << self extend ActiveModelSerializers::Deprecate deprecate :new, 'ActiveModel::Serializer::CollectionSerializer.' end end end end active_model_serializers-0.10.10/lib/active_model/serializer/association.rb000066400000000000000000000036701351232231100271320ustar00rootroot00000000000000# frozen_string_literal: true require 'active_model/serializer/lazy_association' module ActiveModel class Serializer # This class holds all information about serializer's association. # # @api private Association = Struct.new(:reflection, :association_options) do attr_reader :lazy_association delegate :object, :include_data?, :virtual_value, :collection?, to: :lazy_association def initialize(*) super @lazy_association = LazyAssociation.new(reflection, association_options) end # @return [Symbol] delegate :name, to: :reflection # @return [Symbol] def key reflection_options.fetch(:key, name) end # @return [True,False] def key? reflection_options.key?(:key) end # @return [Hash] def links reflection_options.fetch(:links) || {} end # @return [Hash, nil] # This gets mutated, so cannot use the cached reflection_options def meta reflection.options[:meta] end def belongs_to? reflection.foreign_key_on == :self end def polymorphic? true == reflection_options[:polymorphic] end # @api private def serializable_hash(adapter_options, adapter_instance) association_serializer = lazy_association.serializer return virtual_value if virtual_value association_object = association_serializer && association_serializer.object return unless association_object serialization = association_serializer.serializable_hash(adapter_options, {}, adapter_instance) if polymorphic? && serialization polymorphic_type = association_object.class.name.underscore serialization = { type: polymorphic_type, polymorphic_type.to_sym => serialization } end serialization end private delegate :reflection_options, to: :lazy_association end end end active_model_serializers-0.10.10/lib/active_model/serializer/attribute.rb000066400000000000000000000012351351232231100266140ustar00rootroot00000000000000# frozen_string_literal: true require 'active_model/serializer/field' module ActiveModel class Serializer # Holds all the meta-data about an attribute as it was specified in the # ActiveModel::Serializer class. # # @example # class PostSerializer < ActiveModel::Serializer # attribute :content # attribute :name, key: :title # attribute :email, key: :author_email, if: :user_logged_in? # attribute :preview do # truncate(object.content) # end # # def user_logged_in? # current_user.logged_in? # end # end # class Attribute < Field end end end active_model_serializers-0.10.10/lib/active_model/serializer/belongs_to_reflection.rb000066400000000000000000000003331351232231100311540ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer # @api private class BelongsToReflection < Reflection # @api private def foreign_key_on :self end end end end active_model_serializers-0.10.10/lib/active_model/serializer/collection_serializer.rb000066400000000000000000000070111351232231100311730ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer class CollectionSerializer include Enumerable delegate :each, to: :@serializers attr_reader :object, :root def initialize(resources, options = {}) @object = resources @options = options @root = options[:root] @serializers = serializers_from_resources end def success? true end # @api private def serializable_hash(adapter_options, options, adapter_instance) options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options) options[:cached_attributes] ||= ActiveModel::Serializer.cache_read_multi(self, adapter_instance, options[:include_directive]) serializers.map do |serializer| serializer.serializable_hash(adapter_options, options, adapter_instance) end end # TODO: unify naming of root, json_key, and _type. Right now, a serializer's # json_key comes from the root option or the object's model name, by default. # But, if a dev defines a custom `json_key` method with an explicit value, # we have no simple way to know that it is safe to call that instance method. # (which is really a class property at this point, anyhow). # rubocop:disable Metrics/CyclomaticComplexity # Disabling cop since it's good to highlight the complexity of this method by # including all the logic right here. def json_key return root if root # 1. get from options[:serializer] for empty resource collection key = object.empty? && (explicit_serializer_class = options[:serializer]) && explicit_serializer_class._type # 2. get from first serializer instance in collection key ||= (serializer = serializers.first) && serializer.json_key # 3. get from collection name, if a named collection key ||= object.respond_to?(:name) ? object.name && object.name.underscore : nil # 4. key may be nil for empty collection and no serializer option key &&= key.pluralize # 5. fail if the key cannot be determined key || fail(ArgumentError, 'Cannot infer root key from collection type. Please specify the root or each_serializer option, or render a JSON String') end # rubocop:enable Metrics/CyclomaticComplexity def paginated? ActiveModelSerializers.config.jsonapi_pagination_links_enabled && object.respond_to?(:current_page) && object.respond_to?(:total_pages) && object.respond_to?(:size) end protected attr_reader :serializers, :options private def serializers_from_resources serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer) object.map do |resource| serializer_from_resource(resource, serializer_context_class, options) end end def serializer_from_resource(resource, serializer_context_class, options) serializer_class = options.fetch(:serializer) do serializer_context_class.serializer_for(resource, namespace: options[:namespace]) end if serializer_class.nil? ActiveModelSerializers.logger.debug "No serializer found for resource: #{resource.inspect}" throw :no_serializer else serializer_class.new(resource, options.except(:serializer)) end end end end end active_model_serializers-0.10.10/lib/active_model/serializer/concerns/000077500000000000000000000000001351232231100260755ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/active_model/serializer/concerns/caching.rb000066400000000000000000000314141351232231100300210ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer UndefinedCacheKey = Class.new(StandardError) module Caching extend ActiveSupport::Concern included do with_options instance_writer: false, instance_reader: false do |serializer| serializer.class_attribute :_cache # @api private : the cache store serializer.class_attribute :_cache_key # @api private : when present, is first item in cache_key. Ignored if the serializable object defines #cache_key. serializer.class_attribute :_cache_only # @api private : when fragment caching, whitelists fetch_attributes. Cannot combine with except serializer.class_attribute :_cache_except # @api private : when fragment caching, blacklists fetch_attributes. Cannot combine with only serializer.class_attribute :_cache_options # @api private : used by CachedSerializer, passed to _cache.fetch # _cache_options include: # expires_in # compress # force # race_condition_ttl # Passed to ::_cache as # serializer.cache_store.fetch(cache_key, @klass._cache_options) # Passed as second argument to serializer.cache_store.fetch(cache_key, serializer_class._cache_options) serializer.class_attribute :_cache_digest_file_path # @api private : Derived at inheritance end end # Matches # "c:/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb:1:in `'" # AND # "/c/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb:1:in `'" # AS # c/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb CALLER_FILE = / \A # start of string .+ # file path (one or more characters) (?= # stop previous match when :\d+ # a colon is followed by one or more digits :in # followed by a colon followed by in ) /x module ClassMethods def inherited(base) caller_line = caller[1] base._cache_digest_file_path = caller_line super end def _cache_digest return @_cache_digest if defined?(@_cache_digest) @_cache_digest = digest_caller_file(_cache_digest_file_path) end # Hashes contents of file for +_cache_digest+ def digest_caller_file(caller_line) serializer_file_path = caller_line[CALLER_FILE] serializer_file_contents = IO.read(serializer_file_path) Digest::MD5.hexdigest(serializer_file_contents) rescue TypeError, Errno::ENOENT warn <<-EOF.strip_heredoc Cannot digest non-existent file: '#{caller_line}'. Please set `::_cache_digest` of the serializer if you'd like to cache it. EOF ''.freeze end def _skip_digest? _cache_options && _cache_options[:skip_digest] end # @api private # maps attribute value to explicit key name # @see Serializer::attribute # @see Serializer::fragmented_attributes def _attributes_keys _attributes_data .each_with_object({}) do |(key, attr), hash| next if key == attr.name hash[attr.name] = { key: key } end end def fragmented_attributes cached = _cache_only ? _cache_only : _attributes - _cache_except cached = cached.map! { |field| _attributes_keys.fetch(field, field) } non_cached = _attributes - cached non_cached = non_cached.map! { |field| _attributes_keys.fetch(field, field) } { cached: cached, non_cached: non_cached } end # Enables a serializer to be automatically cached # # Sets +::_cache+ object to ActionController::Base.cache_store # when Rails.configuration.action_controller.perform_caching # # @param options [Hash] with valid keys: # cache_store : @see ::_cache # key : @see ::_cache_key # only : @see ::_cache_only # except : @see ::_cache_except # skip_digest : does not include digest in cache_key # all else : @see ::_cache_options # # @example # class PostSerializer < ActiveModel::Serializer # cache key: 'post', expires_in: 3.hours # attributes :title, :body # # has_many :comments # end # # @todo require less code comments. See # https://github.com/rails-api/active_model_serializers/pull/1249#issuecomment-146567837 def cache(options = {}) self._cache = options.delete(:cache_store) || ActiveModelSerializers.config.cache_store || ActiveSupport::Cache.lookup_store(:null_store) self._cache_key = options.delete(:key) self._cache_only = options.delete(:only) self._cache_except = options.delete(:except) self._cache_options = options.empty? ? nil : options end # Value is from ActiveModelSerializers.config.perform_caching. Is used to # globally enable or disable all serializer caching, just like # Rails.configuration.action_controller.perform_caching, which is its # default value in a Rails application. # @return [true, false] # Memoizes value of config first time it is called with a non-nil value. # rubocop:disable Style/ClassVars def perform_caching return @@perform_caching if defined?(@@perform_caching) && !@@perform_caching.nil? @@perform_caching = ActiveModelSerializers.config.perform_caching end alias perform_caching? perform_caching # rubocop:enable Style/ClassVars # The canonical method for getting the cache store for the serializer. # # @return [nil] when _cache is not set (i.e. when `cache` has not been called) # @return [._cache] when _cache is not the NullStore # @return [ActiveModelSerializers.config.cache_store] when _cache is the NullStore. # This is so we can use `cache` being called to mean the serializer should be cached # even if ActiveModelSerializers.config.cache_store has not yet been set. # That means that when _cache is the NullStore and ActiveModelSerializers.config.cache_store # is configured, `cache_store` becomes `ActiveModelSerializers.config.cache_store`. # @return [nil] when _cache is the NullStore and ActiveModelSerializers.config.cache_store is nil. def cache_store return nil if _cache.nil? return _cache if _cache.class != ActiveSupport::Cache::NullStore if ActiveModelSerializers.config.cache_store self._cache = ActiveModelSerializers.config.cache_store else nil end end def cache_enabled? perform_caching? && cache_store && !_cache_only && !_cache_except end def fragment_cache_enabled? perform_caching? && cache_store && (_cache_only && !_cache_except || !_cache_only && _cache_except) end # Read cache from cache_store # @return [Hash] # Used in CollectionSerializer to set :cached_attributes def cache_read_multi(collection_serializer, adapter_instance, include_directive) return {} if ActiveModelSerializers.config.cache_store.blank? keys = object_cache_keys(collection_serializer, adapter_instance, include_directive) return {} if keys.blank? ActiveModelSerializers.config.cache_store.read_multi(*keys) end # Find all cache_key for the collection_serializer # @param serializers [ActiveModel::Serializer::CollectionSerializer] # @param adapter_instance [ActiveModelSerializers::Adapter::Base] # @param include_directive [JSONAPI::IncludeDirective] # @return [Array] all cache_key of collection_serializer def object_cache_keys(collection_serializer, adapter_instance, include_directive) cache_keys = [] collection_serializer.each do |serializer| cache_keys << object_cache_key(serializer, adapter_instance) serializer.associations(include_directive).each do |association| # TODO(BF): Process relationship without evaluating lazy_association association_serializer = association.lazy_association.serializer if association_serializer.respond_to?(:each) association_serializer.each do |sub_serializer| cache_keys << object_cache_key(sub_serializer, adapter_instance) end else cache_keys << object_cache_key(association_serializer, adapter_instance) end end end cache_keys.compact.uniq end # @return [String, nil] the cache_key of the serializer or nil def object_cache_key(serializer, adapter_instance) return unless serializer.present? && serializer.object.present? (serializer.class.cache_enabled? || serializer.class.fragment_cache_enabled?) ? serializer.cache_key(adapter_instance) : nil end end ### INSTANCE METHODS def fetch_attributes(fields, cached_attributes, adapter_instance) key = cache_key(adapter_instance) cached_attributes.fetch(key) do fetch(adapter_instance, serializer_class._cache_options, key) do attributes(fields, true) end end end def fetch(adapter_instance, cache_options = serializer_class._cache_options, key = nil) if serializer_class.cache_store key ||= cache_key(adapter_instance) serializer_class.cache_store.fetch(key, cache_options) do yield end else yield end end # 1. Determine cached fields from serializer class options # 2. Get non_cached_fields and fetch cache_fields # 3. Merge the two hashes using adapter_instance#fragment_cache def fetch_attributes_fragment(adapter_instance, cached_attributes = {}) serializer_class._cache_options ||= {} serializer_class._cache_options[:key] = serializer_class._cache_key if serializer_class._cache_key fields = serializer_class.fragmented_attributes non_cached_fields = fields[:non_cached].dup non_cached_hash = attributes(non_cached_fields, true) include_directive = JSONAPI::IncludeDirective.new(non_cached_fields - non_cached_hash.keys) non_cached_hash.merge! associations_hash({}, { include_directive: include_directive }, adapter_instance) cached_fields = fields[:cached].dup key = cache_key(adapter_instance) cached_hash = cached_attributes.fetch(key) do fetch(adapter_instance, serializer_class._cache_options, key) do hash = attributes(cached_fields, true) include_directive = JSONAPI::IncludeDirective.new(cached_fields - hash.keys) hash.merge! associations_hash({}, { include_directive: include_directive }, adapter_instance) end end # Merge both results adapter_instance.fragment_cache(cached_hash, non_cached_hash) end def cache_key(adapter_instance) return @cache_key if defined?(@cache_key) parts = [] parts << object_cache_key parts << adapter_instance.cache_key parts << serializer_class._cache_digest unless serializer_class._skip_digest? @cache_key = expand_cache_key(parts) end def expand_cache_key(parts) ActiveSupport::Cache.expand_cache_key(parts) end # Use object's cache_key if available, else derive a key from the object # Pass the `key` option to the `cache` declaration or override this method to customize the cache key def object_cache_key if object.respond_to?(:cache_key_with_version) object.cache_key_with_version elsif object.respond_to?(:cache_key) object.cache_key elsif (serializer_cache_key = (serializer_class._cache_key || serializer_class._cache_options[:key])) object_time_safe = object.updated_at object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime) "#{serializer_cache_key}/#{object.id}-#{object_time_safe}" else fail UndefinedCacheKey, "#{object.class} must define #cache_key, or the 'key:' option must be passed into '#{serializer_class}.cache'" end end def serializer_class @serializer_class ||= self.class end end end end active_model_serializers-0.10.10/lib/active_model/serializer/error_serializer.rb000066400000000000000000000004521351232231100301730ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer class ErrorSerializer < ActiveModel::Serializer # @return [Hash>] def as_json object.errors.messages end def success? false end end end end active_model_serializers-0.10.10/lib/active_model/serializer/errors_serializer.rb000066400000000000000000000013551351232231100303610ustar00rootroot00000000000000# frozen_string_literal: true require 'active_model/serializer/error_serializer' module ActiveModel class Serializer class ErrorsSerializer include Enumerable delegate :each, to: :@serializers attr_reader :object, :root def initialize(resources, options = {}) @root = options[:root] @object = resources @serializers = resources.map do |resource| serializer_class = options.fetch(:serializer) { ActiveModel::Serializer::ErrorSerializer } serializer_class.new(resource, options.except(:serializer)) end end def success? false end def json_key nil end protected attr_reader :serializers end end end active_model_serializers-0.10.10/lib/active_model/serializer/field.rb000066400000000000000000000043551351232231100257020ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer # Holds all the meta-data about a field (i.e. attribute or association) as it was # specified in the ActiveModel::Serializer class. # Notice that the field block is evaluated in the context of the serializer. Field = Struct.new(:name, :options, :block) do def initialize(*) super validate_condition! end # Compute the actual value of a field for a given serializer instance. # @param [Serializer] The serializer instance for which the value is computed. # @return [Object] value # # @api private # def value(serializer) if block serializer.instance_eval(&block) else serializer.read_attribute_for_serialization(name) end end # Decide whether the field should be serialized by the given serializer instance. # @param [Serializer] The serializer instance # @return [Bool] # # @api private # def excluded?(serializer) case condition_type when :if !evaluate_condition(serializer) when :unless evaluate_condition(serializer) else false end end private def validate_condition! return if condition_type == :none case condition when Symbol, String, Proc # noop else fail TypeError, "#{condition_type.inspect} should be a Symbol, String or Proc" end end def evaluate_condition(serializer) case condition when Symbol serializer.public_send(condition) when String serializer.instance_eval(condition) when Proc if condition.arity.zero? serializer.instance_exec(&condition) else serializer.instance_exec(serializer, &condition) end else nil end end def condition_type @condition_type ||= if options.key?(:if) :if elsif options.key?(:unless) :unless else :none end end def condition options[condition_type] end end end end active_model_serializers-0.10.10/lib/active_model/serializer/fieldset.rb000066400000000000000000000011431351232231100264060ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer class Fieldset def initialize(fields) @raw_fields = fields || {} end def fields @fields ||= parsed_fields end def fields_for(type) fields[type.singularize.to_sym] || fields[type.pluralize.to_sym] end protected attr_reader :raw_fields private def parsed_fields if raw_fields.is_a?(Hash) raw_fields.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.map(&:to_sym) } else {} end end end end end active_model_serializers-0.10.10/lib/active_model/serializer/has_many_reflection.rb000066400000000000000000000003001351232231100306120ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer # @api private class HasManyReflection < Reflection def collection? true end end end end active_model_serializers-0.10.10/lib/active_model/serializer/has_one_reflection.rb000066400000000000000000000002221351232231100304320ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer # @api private class HasOneReflection < Reflection end end end active_model_serializers-0.10.10/lib/active_model/serializer/lazy_association.rb000066400000000000000000000071071351232231100301700ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer # @api private LazyAssociation = Struct.new(:reflection, :association_options) do REFLECTION_OPTIONS = %i(key links polymorphic meta serializer virtual_value namespace).freeze delegate :collection?, to: :reflection def reflection_options @reflection_options ||= reflection.options.select { |k, _| REFLECTION_OPTIONS.include?(k) } end def object return @object if defined?(@object) @object = reflection.value( association_options.fetch(:parent_serializer), association_options.fetch(:include_slice) ) end alias_method :eval_reflection_block, :object def include_data? eval_reflection_block if reflection.block reflection.include_data?( association_options.fetch(:include_slice) ) end # @return [ActiveModel::Serializer, nil] def serializer return @serializer if defined?(@serializer) if serializer_class serialize_object!(object) elsif !object.nil? && !object.instance_of?(Object) cached_result[:virtual_value] = object end @serializer = cached_result[:serializer] end def virtual_value cached_result[:virtual_value] || reflection_options[:virtual_value] end def serializer_class return @serializer_class if defined?(@serializer_class) serializer_for_options = { namespace: namespace } serializer_for_options[:serializer] = reflection_options[:serializer] if reflection_options.key?(:serializer) @serializer_class = association_options.fetch(:parent_serializer).class.serializer_for(object, serializer_for_options) end private def cached_result @cached_result ||= {} end def serialize_object!(object) if collection? if (serializer = instantiate_collection_serializer(object)).nil? # BUG: per #2027, JSON API resource relationships are only id and type, and hence either # *require* a serializer or we need to be a little clever about figuring out the id/type. # In either case, returning the raw virtual value will almost always be incorrect. # # Should be reflection_options[:virtual_value] or adapter needs to figure out what to do # with an object that is non-nil and has no defined serializer. cached_result[:virtual_value] = object.try(:as_json) || object else cached_result[:serializer] = serializer end else cached_result[:serializer] = instantiate_serializer(object) end end def instantiate_serializer(object) serializer_options = association_options.fetch(:parent_serializer_options).except(:serializer) serializer_options[:serializer_context_class] = association_options.fetch(:parent_serializer).class serializer = reflection_options.fetch(:serializer, nil) serializer_options[:serializer] = serializer if serializer serializer_options[:namespace] = reflection_options[:namespace] if reflection_options[:namespace] serializer_class.new(object, serializer_options) end def instantiate_collection_serializer(object) serializer = catch(:no_serializer) do instantiate_serializer(object) end serializer end def namespace reflection_options[:namespace] || association_options.fetch(:parent_serializer_options)[:namespace] end end end end active_model_serializers-0.10.10/lib/active_model/serializer/link.rb000066400000000000000000000007351351232231100255520ustar00rootroot00000000000000# frozen_string_literal: true require 'active_model/serializer/field' module ActiveModel class Serializer # Holds all the data about a serializer link # # @example # class PostSerializer < ActiveModel::Serializer # link :callback, if: :internal? do # object.callback_link # end # # def internal? # instance_options[:internal] == true # end # end # class Link < Field end end end active_model_serializers-0.10.10/lib/active_model/serializer/lint.rb000066400000000000000000000146201351232231100255610ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer module Lint # == Active \Model \Serializer \Lint \Tests # # You can test whether an object is compliant with the Active \Model \Serializers # API by including ActiveModel::Serializer::Lint::Tests in your TestCase. # It will include tests that tell you whether your object is fully compliant, # or if not, which aspects of the API are not implemented. # # Note an object is not required to implement all APIs in order to work # with Active \Model \Serializers. This module only intends to provide guidance in case # you want all features out of the box. # # These tests do not attempt to determine the semantic correctness of the # returned values. For instance, you could implement serializable_hash to # always return +{}+, and the tests would pass. It is up to you to ensure # that the values are semantically meaningful. module Tests # Passes if the object responds to serializable_hash and if it takes # zero or one arguments. # Fails otherwise. # # serializable_hash returns a hash representation of a object's attributes. # Typically, it is implemented by including ActiveModel::Serialization. def test_serializable_hash assert_respond_to resource, :serializable_hash, 'The resource should respond to serializable_hash' resource.serializable_hash resource.serializable_hash(nil) end # Passes if the object responds to read_attribute_for_serialization # and if it requires one argument (the attribute to be read). # Fails otherwise. # # read_attribute_for_serialization gets the attribute value for serialization # Typically, it is implemented by including ActiveModel::Serialization. def test_read_attribute_for_serialization assert_respond_to resource, :read_attribute_for_serialization, 'The resource should respond to read_attribute_for_serialization' actual_arity = resource.method(:read_attribute_for_serialization).arity # using absolute value since arity is: # 1 for def read_attribute_for_serialization(name); end # -1 for alias :read_attribute_for_serialization :send assert_equal 1, actual_arity.abs, "expected #{actual_arity.inspect}.abs to be 1 or -1" end # Passes if the object responds to as_json and if it takes # zero or one arguments. # Fails otherwise. # # as_json returns a hash representation of a serialized object. # It may delegate to serializable_hash # Typically, it is implemented either by including ActiveModel::Serialization # which includes ActiveModel::Serializers::JSON. # or by the JSON gem when required. def test_as_json assert_respond_to resource, :as_json resource.as_json resource.as_json(nil) end # Passes if the object responds to to_json and if it takes # zero or one arguments. # Fails otherwise. # # to_json returns a string representation (JSON) of a serialized object. # It may be called on the result of as_json. # Typically, it is implemented on all objects when the JSON gem is required. def test_to_json assert_respond_to resource, :to_json resource.to_json resource.to_json(nil) end # Passes if the object responds to cache_key # Fails otherwise. # # cache_key returns a (self-expiring) unique key for the object, # and is part of the (self-expiring) cache_key, which is used by the # adapter. It is not required unless caching is enabled. def test_cache_key assert_respond_to resource, :cache_key actual_arity = resource.method(:cache_key).arity assert_includes [-1, 0], actual_arity, "expected #{actual_arity.inspect} to be 0 or -1" end # Passes if the object responds to updated_at and if it takes no # arguments. # Fails otherwise. # # updated_at returns a Time object or iso8601 string and # is part of the (self-expiring) cache_key, which is used by the adapter. # It is not required unless caching is enabled. def test_updated_at assert_respond_to resource, :updated_at actual_arity = resource.method(:updated_at).arity assert_equal 0, actual_arity end # Passes if the object responds to id and if it takes no # arguments. # Fails otherwise. # # id returns a unique identifier for the object. # It is not required unless caching is enabled. def test_id assert_respond_to resource, :id assert_equal 0, resource.method(:id).arity end # Passes if the object's class responds to model_name and if it # is in an instance of +ActiveModel::Name+. # Fails otherwise. # # model_name returns an ActiveModel::Name instance. # It is used by the serializer to identify the object's type. # It is not required unless caching is enabled. def test_model_name resource_class = resource.class assert_respond_to resource_class, :model_name assert_instance_of resource_class.model_name, ActiveModel::Name end def test_active_model_errors assert_respond_to resource, :errors end def test_active_model_errors_human_attribute_name assert_respond_to resource.class, :human_attribute_name assert_equal(-2, resource.class.method(:human_attribute_name).arity) end def test_active_model_errors_lookup_ancestors assert_respond_to resource.class, :lookup_ancestors assert_equal 0, resource.class.method(:lookup_ancestors).arity end private def resource @resource or fail "'@resource' must be set as the linted object" end def assert_instance_of(result, name) assert result.instance_of?(name), "#{result} should be an instance of #{name}" end end end end end active_model_serializers-0.10.10/lib/active_model/serializer/null.rb000066400000000000000000000004051351232231100255610ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer class Null < Serializer def attributes(*) {} end def associations(*) {} end def serializable_hash(*) {} end end end end active_model_serializers-0.10.10/lib/active_model/serializer/reflection.rb000066400000000000000000000154331351232231100267500ustar00rootroot00000000000000# frozen_string_literal: true require 'active_model/serializer/field' require 'active_model/serializer/association' module ActiveModel class Serializer # Holds all the meta-data about an association as it was specified in the # ActiveModel::Serializer class. # # @example # class PostSerializer < ActiveModel::Serializer # has_one :author, serializer: AuthorSerializer # belongs_to :boss, type: :users, foreign_key: :boss_id # has_many :comments # has_many :comments, key: :last_comments do # object.comments.last(1) # end # has_many :secret_meta_data, if: :is_admin? # # has_one :blog do |serializer| # meta count: object.roles.count # serializer.cached_blog # end # # private # # def cached_blog # cache_store.fetch("cached_blog:#{object.updated_at}") do # Blog.find(object.blog_id) # end # end # # def is_admin? # current_user.admin? # end # end # # Specifically, the association 'comments' is evaluated two different ways: # 1) as 'comments' and named 'comments'. # 2) as 'object.comments.last(1)' and named 'last_comments'. # # PostSerializer._reflections # => # # { # # author: HasOneReflection.new(:author, serializer: AuthorSerializer), # # comments: HasManyReflection.new(:comments) # # last_comments: HasManyReflection.new(:comments, { key: :last_comments }, #) # # secret_meta_data: HasManyReflection.new(:secret_meta_data, { if: :is_admin? }) # # } # # So you can inspect reflections in your Adapters. class Reflection < Field attr_reader :foreign_key, :type def initialize(*) super options[:links] = {} options[:include_data_setting] = Serializer.config.include_data_default options[:meta] = nil @type = options.fetch(:type) do class_name = options.fetch(:class_name, name.to_s.camelize.singularize) class_name.underscore.pluralize.to_sym end @foreign_key = options.fetch(:foreign_key) do if collection? "#{name.to_s.singularize}_ids".to_sym else "#{name}_id".to_sym end end end # @api public # @example # has_one :blog do # include_data false # link :self, 'a link' # link :related, 'another link' # link :self, '//example.com/link_author/relationships/bio' # id = object.profile.id # link :related do # "//example.com/profiles/#{id}" if id != 123 # end # link :related do # ids = object.likes.map(&:id).join(',') # href "//example.com/likes/#{ids}" # meta ids: ids # end # end def link(name, value = nil) options[:links][name] = block_given? ? Proc.new : value :nil end # @api public # @example # has_one :blog do # include_data false # meta(id: object.blog.id) # meta liked: object.likes.any? # link :self do # href object.blog.id.to_s # meta(id: object.blog.id) # end def meta(value = nil) options[:meta] = block_given? ? Proc.new : value :nil end # @api public # @example # has_one :blog do # include_data false # link :self, 'a link' # link :related, 'another link' # end # # has_one :blog do # include_data false # link :self, 'a link' # link :related, 'another link' # end # # belongs_to :reviewer do # meta name: 'Dan Brown' # include_data true # end # # has_many :tags, serializer: TagSerializer do # link :self, '//example.com/link_author/relationships/tags' # include_data :if_sideloaded # end def include_data(value = true) options[:include_data_setting] = value :nil end def collection? false end def include_data?(include_slice) include_data_setting = options[:include_data_setting] case include_data_setting when :if_sideloaded then include_slice.key?(options.fetch(:key, name)) when true then true when false then false else fail ArgumentError, "Unknown include_data_setting '#{include_data_setting.inspect}'" end end # @param serializer [ActiveModel::Serializer] # @yield [ActiveModel::Serializer] # @return [:nil, associated resource or resource collection] def value(serializer, include_slice) # NOTE(BF): This method isn't thread-safe because the _reflections class attribute is not thread-safe # Therefore, when we build associations from reflections, we dup the entire reflection instance. # Better solutions much appreciated! @object = serializer.object @scope = serializer.scope block_value = instance_exec(serializer, &block) if block return unless include_data?(include_slice) if block && block_value != :nil block_value else serializer.read_attribute_for_serialization(name) end end # @api private def foreign_key_on :related end # Build association. This method is used internally to # build serializer's association by its reflection. # # @param [Serializer] parent_serializer for given association # @param [Hash{Symbol => Object}] parent_serializer_options # # @example # # Given the following serializer defined: # class PostSerializer < ActiveModel::Serializer # has_many :comments, serializer: CommentSummarySerializer # end # # # Then you instantiate your serializer # post_serializer = PostSerializer.new(post, foo: 'bar') # # # to build association for comments you need to get reflection # comments_reflection = PostSerializer._reflections.detect { |r| r.name == :comments } # # and #build_association # comments_reflection.build_association(post_serializer, foo: 'bar') # # @api private def build_association(parent_serializer, parent_serializer_options, include_slice = {}) association_options = { parent_serializer: parent_serializer, parent_serializer_options: parent_serializer_options, include_slice: include_slice } Association.new(self, association_options) end protected # used in instance exec attr_accessor :object, :scope end end end active_model_serializers-0.10.10/lib/active_model/serializer/version.rb000066400000000000000000000001561351232231100262770ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer VERSION = '0.10.10'.freeze end end active_model_serializers-0.10.10/lib/active_model_serializers.rb000066400000000000000000000032161351232231100250550ustar00rootroot00000000000000# frozen_string_literal: true require 'active_model' require 'active_support' require 'active_support/core_ext/object/with_options' require 'active_support/core_ext/string/inflections' require 'active_support/json' module ActiveModelSerializers extend ActiveSupport::Autoload eager_autoload do autoload :Model autoload :Callbacks autoload :SerializableResource autoload :SerializationContext autoload :Logging autoload :Test autoload :Adapter autoload :JsonPointer autoload :Deprecate autoload :LookupChain autoload :Deserialization end class << self; attr_accessor :logger; end self.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT)) def self.config ActiveModel::Serializer.config end # The file name and line number of the caller of the caller of this method. def self.location_of_caller caller[1] =~ /(.*?):(\d+).*?$/i file = Regexp.last_match(1) lineno = Regexp.last_match(2).to_i [file, lineno] end # Memoized default include directive # @return [JSONAPI::IncludeDirective] def self.default_include_directive @default_include_directive ||= JSONAPI::IncludeDirective.new(config.default_includes, allow_wildcard: true) end def self.silence_warnings original_verbose = $VERBOSE $VERBOSE = nil yield ensure $VERBOSE = original_verbose end def self.eager_load! super ActiveModel::Serializer.eager_load! end require 'active_model/serializer/version' require 'active_model/serializer' require 'active_model/serializable_resource' require 'active_model_serializers/railtie' if defined?(::Rails::Railtie) end active_model_serializers-0.10.10/lib/active_model_serializers/000077500000000000000000000000001351232231100245265ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/active_model_serializers/adapter.rb000066400000000000000000000066501351232231100265020ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Adapter UnknownAdapterError = Class.new(ArgumentError) ADAPTER_MAP = {} # rubocop:disable Style/MutableConstant private_constant :ADAPTER_MAP if defined?(private_constant) class << self # All methods are class functions # :nocov: def new(*args) fail ArgumentError, 'Adapters inherit from Adapter::Base.' \ "Adapter.new called with args: '#{args.inspect}', from" \ "'caller[0]'." end # :nocov: def configured_adapter lookup(ActiveModelSerializers.config.adapter) end def create(resource, options = {}) override = options.delete(:adapter) klass = override ? adapter_class(override) : configured_adapter klass.new(resource, options) end # @see ActiveModelSerializers::Adapter.lookup def adapter_class(adapter) ActiveModelSerializers::Adapter.lookup(adapter) end # @return [Hash] def adapter_map ADAPTER_MAP end # @return [Array] list of adapter names def adapters adapter_map.keys.sort! end # Adds an adapter 'klass' with 'name' to the 'adapter_map' # Names are stringified and underscored # @param name [Symbol, String, Class] name of the registered adapter # @param klass [Class] adapter class itself, optional if name is the class # @example # AMS::Adapter.register(:my_adapter, MyAdapter) # @note The registered name strips out 'ActiveModelSerializers::Adapter::' # so that registering 'ActiveModelSerializers::Adapter::Json' and # 'Json' will both register as 'json'. def register(name, klass = name) name = name.to_s.gsub(/\AActiveModelSerializers::Adapter::/, ''.freeze) adapter_map[name.underscore] = klass self end def registered_name(adapter_class) ADAPTER_MAP.key adapter_class end # @param adapter [String, Symbol, Class] name to fetch adapter by # @return [ActiveModelSerializers::Adapter] subclass of Adapter # @raise [UnknownAdapterError] def lookup(adapter) # 1. return if is a class return adapter if adapter.is_a?(Class) adapter_name = adapter.to_s.underscore # 2. return if registered adapter_map.fetch(adapter_name) do # 3. try to find adapter class from environment adapter_class = find_by_name(adapter_name) register(adapter_name, adapter_class) adapter_class end rescue NameError, ArgumentError => e failure_message = "NameError: #{e.message}. Unknown adapter: #{adapter.inspect}. Valid adapters are: #{adapters}" raise UnknownAdapterError, failure_message, e.backtrace end # @api private def find_by_name(adapter_name) adapter_name = adapter_name.to_s.classify.tr('API', 'Api') "ActiveModelSerializers::Adapter::#{adapter_name}".safe_constantize || "ActiveModelSerializers::Adapter::#{adapter_name.pluralize}".safe_constantize or # rubocop:disable Style/AndOr fail UnknownAdapterError end private :find_by_name end # Gotta be at the bottom to use the code above it :( extend ActiveSupport::Autoload autoload :Base autoload :Null autoload :Attributes autoload :Json autoload :JsonApi end end active_model_serializers-0.10.10/lib/active_model_serializers/adapter/000077500000000000000000000000001351232231100261465ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/active_model_serializers/adapter/attributes.rb000066400000000000000000000022451351232231100306640ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Adapter class Attributes < Base def initialize(*) super instance_options[:fieldset] ||= ActiveModel::Serializer::Fieldset.new(fields_to_fieldset(instance_options.delete(:fields))) end def serializable_hash(options = nil) options = serialization_options(options) options[:fields] ||= instance_options[:fields] serialized_hash = serializer.serializable_hash(instance_options, options, self) self.class.transform_key_casing!(serialized_hash, instance_options) end private def fields_to_fieldset(fields) return fields if fields.nil? resource_fields = [] relationship_fields = {} fields.each do |field| case field when Symbol, String then resource_fields << field when Hash then relationship_fields.merge!(field) else fail ArgumentError, "Unknown conversion of fields to fieldset: '#{field.inspect}' in '#{fields.inspect}'" end end relationship_fields.merge!(serializer.json_key.to_sym => resource_fields) end end end end active_model_serializers-0.10.10/lib/active_model_serializers/adapter/base.rb000066400000000000000000000047721351232231100274170ustar00rootroot00000000000000# frozen_string_literal: true require 'case_transform' module ActiveModelSerializers module Adapter class Base # Automatically register adapters when subclassing def self.inherited(subclass) ActiveModelSerializers::Adapter.register(subclass) end # Sets the default transform for the adapter. # # @return [Symbol] the default transform for the adapter def self.default_key_transform :unaltered end # Determines the transform to use in order of precedence: # adapter option, global config, adapter default. # # @param options [Object] # @return [Symbol] the transform to use def self.transform(options) return options[:key_transform] if options && options[:key_transform] ActiveModelSerializers.config.key_transform || default_key_transform end # Transforms the casing of the supplied value. # # @param value [Object] the value to be transformed # @param options [Object] serializable resource options # @return [Symbol] the default transform for the adapter def self.transform_key_casing!(value, options) CaseTransform.send(transform(options), value) end def self.cache_key @cache_key ||= ActiveModelSerializers::Adapter.registered_name(self) end def self.fragment_cache(cached_hash, non_cached_hash) non_cached_hash.merge cached_hash end attr_reader :serializer, :instance_options def initialize(serializer, options = {}) @serializer = serializer @instance_options = options end # Subclasses that implement this method must first call # options = serialization_options(options) def serializable_hash(_options = nil) fail NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.' end def as_json(options = nil) serializable_hash(options) end def cache_key self.class.cache_key end def fragment_cache(cached_hash, non_cached_hash) self.class.fragment_cache(cached_hash, non_cached_hash) end private # see https://github.com/rails-api/active_model_serializers/pull/965 # When options is +nil+, sets it to +{}+ def serialization_options(options) options ||= {} # rubocop:disable Lint/UselessAssignment end def root serializer.json_key.to_sym if serializer.json_key end end end end active_model_serializers-0.10.10/lib/active_model_serializers/adapter/json.rb000066400000000000000000000011571351232231100274500ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Adapter class Json < Base def serializable_hash(options = nil) options = serialization_options(options) serialized_hash = { root => Attributes.new(serializer, instance_options).serializable_hash(options) } serialized_hash[meta_key] = meta unless meta.blank? self.class.transform_key_casing!(serialized_hash, instance_options) end def meta instance_options.fetch(:meta, nil) end def meta_key instance_options.fetch(:meta_key, 'meta'.freeze) end end end end active_model_serializers-0.10.10/lib/active_model_serializers/adapter/json_api.rb000066400000000000000000000432231351232231100303010ustar00rootroot00000000000000# frozen_string_literal: true # {http://jsonapi.org/format/ JSON API specification} # rubocop:disable Style/AsciiComments # TODO: implement! # ☐ https://github.com/rails-api/active_model_serializers/issues/1235 # TODO: use uri_template in link generation? # ☐ https://github.com/rails-api/active_model_serializers/pull/1282#discussion_r42528812 # see gem https://github.com/hannesg/uri_template # spec http://tools.ietf.org/html/rfc6570 # impl https://developer.github.com/v3/#schema https://api.github.com/ # TODO: validate against a JSON schema document? # ☐ https://github.com/rails-api/active_model_serializers/issues/1162 # ā˜‘ https://github.com/rails-api/active_model_serializers/pull/1270 # TODO: Routing # ☐ https://github.com/rails-api/active_model_serializers/pull/1476 # TODO: Query Params # ā˜‘ `include` https://github.com/rails-api/active_model_serializers/pull/1131 # ā˜‘ `fields` https://github.com/rails-api/active_model_serializers/pull/700 # ā˜‘ `page[number]=3&page[size]=1` https://github.com/rails-api/active_model_serializers/pull/1041 # ☐ `filter` # ☐ `sort` module ActiveModelSerializers module Adapter class JsonApi < Base extend ActiveSupport::Autoload eager_autoload do autoload :Jsonapi autoload :ResourceIdentifier autoload :Link autoload :PaginationLinks autoload :Meta autoload :Error autoload :Deserialization autoload :Relationship end def self.default_key_transform :dash end def self.fragment_cache(cached_hash, non_cached_hash, root = true) core_cached = cached_hash.first core_non_cached = non_cached_hash.first no_root_cache = cached_hash.delete_if { |key, _value| key == core_cached[0] } no_root_non_cache = non_cached_hash.delete_if { |key, _value| key == core_non_cached[0] } cached_resource = (core_cached[1]) ? core_cached[1].deep_merge(core_non_cached[1]) : core_non_cached[1] hash = root ? { root => cached_resource } : cached_resource hash.deep_merge no_root_non_cache.deep_merge no_root_cache end def initialize(serializer, options = {}) super @include_directive = JSONAPI::IncludeDirective.new(options[:include], allow_wildcard: true) @fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields)) end # {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure} # {http://jsonapi.org/format/#document-top-level data and errors MUST NOT coexist in the same document.} def serializable_hash(*) document = if serializer.success? success_document else failure_document end self.class.transform_key_casing!(document, instance_options) end def fragment_cache(cached_hash, non_cached_hash) root = !instance_options.include?(:include) self.class.fragment_cache(cached_hash, non_cached_hash, root) end # {http://jsonapi.org/format/#document-top-level Primary data} # definition: # ☐ toplevel_data (required) # ☐ toplevel_included # ā˜‘ toplevel_meta # ā˜‘ toplevel_links # ā˜‘ toplevel_jsonapi # structure: # { # data: toplevel_data, # included: toplevel_included, # meta: toplevel_meta, # links: toplevel_links, # jsonapi: toplevel_jsonapi # }.reject! {|_,v| v.nil? } # rubocop:disable Metrics/CyclomaticComplexity def success_document is_collection = serializer.respond_to?(:each) serializers = is_collection ? serializer : [serializer] primary_data, included = resource_objects_for(serializers) hash = {} # toplevel_data # definition: # oneOf # resource # array of unique items of type 'resource' # null # # description: # The document's "primary data" is a representation of the resource or collection of resources # targeted by a request. # # Singular: the resource object. # # Collection: one of an array of resource objects, an array of resource identifier objects, or # an empty array ([]), for requests that target resource collections. # # None: null if the request is one that might correspond to a single resource, but doesn't currently. # structure: # if serializable_resource.resource? # resource # elsif serializable_resource.collection? # [ # resource, # resource # ] # else # nil # end hash[:data] = is_collection ? primary_data : primary_data[0] # toplevel_included # alias included # definition: # array of unique items of type 'resource' # # description: # To reduce the number of HTTP requests, servers **MAY** allow # responses that include related resources along with the requested primary # resources. Such responses are called "compound documents". # structure: # [ # resource, # resource # ] hash[:included] = included if included.any? Jsonapi.add!(hash) if instance_options[:links] hash[:links] ||= {} hash[:links].update(instance_options[:links]) end if is_collection && serializer.paginated? hash[:links] ||= {} hash[:links].update(pagination_links_for(serializer)) end hash[:meta] = instance_options[:meta] unless instance_options[:meta].blank? hash end # rubocop:enable Metrics/CyclomaticComplexity # {http://jsonapi.org/format/#errors JSON API Errors} # TODO: look into caching # definition: # ā˜‘ toplevel_errors array (required) # ☐ toplevel_meta # ☐ toplevel_jsonapi # structure: # { # errors: toplevel_errors, # meta: toplevel_meta, # jsonapi: toplevel_jsonapi # }.reject! {|_,v| v.nil? } # prs: # https://github.com/rails-api/active_model_serializers/pull/1004 def failure_document hash = {} # PR Please :) # Jsonapi.add!(hash) # toplevel_errors # definition: # array of unique items of type 'error' # structure: # [ # error, # error # ] if serializer.respond_to?(:each) hash[:errors] = serializer.flat_map do |error_serializer| Error.resource_errors(error_serializer, instance_options) end else hash[:errors] = Error.resource_errors(serializer, instance_options) end hash end protected attr_reader :fieldset private # {http://jsonapi.org/format/#document-resource-objects Primary data} # resource # definition: # JSON Object # # properties: # type (required) : String # id (required) : String # attributes # relationships # links # meta # # description: # "Resource objects" appear in a JSON API document to represent resources # structure: # { # type: 'admin--some-user', # id: '1336', # attributes: attributes, # relationships: relationships, # links: links, # meta: meta, # }.reject! {|_,v| v.nil? } # prs: # type # https://github.com/rails-api/active_model_serializers/pull/1122 # [x] https://github.com/rails-api/active_model_serializers/pull/1213 # https://github.com/rails-api/active_model_serializers/pull/1216 # https://github.com/rails-api/active_model_serializers/pull/1029 # links # [x] https://github.com/rails-api/active_model_serializers/pull/1246 # [x] url helpers https://github.com/rails-api/active_model_serializers/issues/1269 # meta # [x] https://github.com/rails-api/active_model_serializers/pull/1340 def resource_objects_for(serializers) @primary = [] @included = [] @resource_identifiers = Set.new serializers.each { |serializer| process_resource(serializer, true, @include_directive) } serializers.each { |serializer| process_relationships(serializer, @include_directive) } [@primary, @included] end def process_resource(serializer, primary, include_slice = {}) resource_identifier = ResourceIdentifier.new(serializer, instance_options).as_json return false unless @resource_identifiers.add?(resource_identifier) resource_object = resource_object_for(serializer, include_slice) if primary @primary << resource_object else @included << resource_object end true end def process_relationships(serializer, include_slice) serializer.associations(include_slice).each do |association| # TODO(BF): Process relationship without evaluating lazy_association process_relationship(association.lazy_association.serializer, include_slice[association.key]) end end def process_relationship(serializer, include_slice) if serializer.respond_to?(:each) serializer.each { |s| process_relationship(s, include_slice) } return end return unless serializer && serializer.object return unless process_resource(serializer, false, include_slice) process_relationships(serializer, include_slice) end # {http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes} # attributes # definition: # JSON Object # # patternProperties: # ^(?!relationships$|links$)\\w[-\\w_]*$ # # description: # Members of the attributes object ("attributes") represent information about the resource # object in which it's defined. # Attributes may contain any valid JSON value # structure: # { # foo: 'bar' # } def attributes_for(serializer, fields) serializer.attributes(fields).except(:id) end # {http://jsonapi.org/format/#document-resource-objects Document Resource Objects} def resource_object_for(serializer, include_slice = {}) resource_object = data_for(serializer, include_slice) # toplevel_links # definition: # allOf # ☐ links # ☐ pagination # # description: # Link members related to the primary data. # structure: # links.merge!(pagination) # prs: # https://github.com/rails-api/active_model_serializers/pull/1247 # https://github.com/rails-api/active_model_serializers/pull/1018 if (links = links_for(serializer)).any? resource_object ||= {} resource_object[:links] = links end # toplevel_meta # alias meta # definition: # meta # structure # { # :'git-ref' => 'abc123' # } if (meta = meta_for(serializer)).present? resource_object ||= {} resource_object[:meta] = meta end resource_object end def data_for(serializer, include_slice) data = serializer.fetch(self) do resource_object = ResourceIdentifier.new(serializer, instance_options).as_json break nil if resource_object.nil? requested_fields = fieldset && fieldset.fields_for(resource_object[:type]) attributes = attributes_for(serializer, requested_fields) resource_object[:attributes] = attributes if attributes.any? resource_object end data.tap do |resource_object| next if resource_object.nil? # NOTE(BF): the attributes are cached above, separately from the relationships, below. requested_associations = fieldset.fields_for(resource_object[:type]) || '*' relationships = relationships_for(serializer, requested_associations, include_slice) resource_object[:relationships] = relationships if relationships.any? end end # {http://jsonapi.org/format/#document-resource-object-relationships Document Resource Object Relationship} # relationships # definition: # JSON Object # # patternProperties: # ^\\w[-\\w_]*$" # # properties: # data : relationshipsData # links # meta # # description: # # Members of the relationships object ("relationships") represent references from the # resource object in which it's defined to other resource objects." # structure: # { # links: links, # meta: meta, # data: relationshipsData # }.reject! {|_,v| v.nil? } # # prs: # links # [x] https://github.com/rails-api/active_model_serializers/pull/1454 # meta # [x] https://github.com/rails-api/active_model_serializers/pull/1454 # polymorphic # [ ] https://github.com/rails-api/active_model_serializers/pull/1420 # # relationshipsData # definition: # oneOf # relationshipToOne # relationshipToMany # # description: # Member, whose value represents "resource linkage" # structure: # if has_one? # relationshipToOne # else # relationshipToMany # end # # definition: # anyOf # null # linkage # # relationshipToOne # description: # # References to other resource objects in a to-one ("relationship"). Relationships can be # specified by including a member in a resource's links object. # # None: Describes an empty to-one relationship. # structure: # if has_related? # linkage # else # nil # end # # relationshipToMany # definition: # array of unique items of type 'linkage' # # description: # An array of objects each containing "type" and "id" members for to-many relationships # structure: # [ # linkage, # linkage # ] # prs: # polymorphic # [ ] https://github.com/rails-api/active_model_serializers/pull/1282 # # linkage # definition: # type (required) : String # id (required) : String # meta # # description: # The "type" and "id" to non-empty members. # structure: # { # type: 'required-type', # id: 'required-id', # meta: meta # }.reject! {|_,v| v.nil? } def relationships_for(serializer, requested_associations, include_slice) include_directive = JSONAPI::IncludeDirective.new( requested_associations, allow_wildcard: true ) serializer.associations(include_directive, include_slice).each_with_object({}) do |association, hash| hash[association.key] = Relationship.new(serializer, instance_options, association).as_json end end # {http://jsonapi.org/format/#document-links Document Links} # links # definition: # JSON Object # # properties: # self : URI # related : link # # description: # A resource object **MAY** contain references to other resource objects ("relationships"). # Relationships may be to-one or to-many. Relationships can be specified by including a member # in a resource's links object. # # A `self` member’s value is a URL for the relationship itself (a "relationship URL"). This # URL allows the client to directly manipulate the relationship. For example, it would allow # a client to remove an `author` from an `article` without deleting the people resource # itself. # structure: # { # self: 'http://example.com/etc', # related: link # }.reject! {|_,v| v.nil? } def links_for(serializer) serializer._links.each_with_object({}) do |(name, value), hash| next if value.excluded?(serializer) result = Link.new(serializer, value.block).as_json hash[name] = result if result end end # {http://jsonapi.org/format/#fetching-pagination Pagination Links} # pagination # definition: # first : pageObject # last : pageObject # prev : pageObject # next : pageObject # structure: # { # first: pageObject, # last: pageObject, # prev: pageObject, # next: pageObject # } # # pageObject # definition: # oneOf # URI # null # # description: # The page of data # structure: # if has_page? # 'http://example.com/some-page?page[number][x]' # else # nil # end # prs: # https://github.com/rails-api/active_model_serializers/pull/1041 def pagination_links_for(serializer) PaginationLinks.new(serializer.object, instance_options).as_json end # {http://jsonapi.org/format/#document-meta Docment Meta} def meta_for(serializer) Meta.new(serializer).as_json end end end end # rubocop:enable Style/AsciiComments active_model_serializers-0.10.10/lib/active_model_serializers/adapter/json_api/000077500000000000000000000000001351232231100277505ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/active_model_serializers/adapter/json_api/deserialization.rb000066400000000000000000000163201351232231100334650ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Adapter class JsonApi # NOTE(Experimental): # This is an experimental feature. Both the interface and internals could be subject # to changes. module Deserialization InvalidDocument = Class.new(ArgumentError) module_function # Transform a JSON API document, containing a single data object, # into a hash that is ready for ActiveRecord::Base.new() and such. # Raises InvalidDocument if the payload is not properly formatted. # # @param [Hash|ActionController::Parameters] document # @param [Hash] options # only: Array of symbols of whitelisted fields. # except: Array of symbols of blacklisted fields. # keys: Hash of translated keys (e.g. :author => :user). # polymorphic: Array of symbols of polymorphic fields. # @return [Hash] # # @example # document = { # data: { # id: 1, # type: 'post', # attributes: { # title: 'Title 1', # date: '2015-12-20' # }, # associations: { # author: { # data: { # type: 'user', # id: 2 # } # }, # second_author: { # data: nil # }, # comments: { # data: [{ # type: 'comment', # id: 3 # },{ # type: 'comment', # id: 4 # }] # } # } # } # } # # parse(document) #=> # # { # # title: 'Title 1', # # date: '2015-12-20', # # author_id: 2, # # second_author_id: nil # # comment_ids: [3, 4] # # } # # parse(document, only: [:title, :date, :author], # keys: { date: :published_at }, # polymorphic: [:author]) #=> # # { # # title: 'Title 1', # # published_at: '2015-12-20', # # author_id: '2', # # author_type: 'people' # # } # def parse!(document, options = {}) parse(document, options) do |invalid_payload, reason| fail InvalidDocument, "Invalid payload (#{reason}): #{invalid_payload}" end end # Same as parse!, but returns an empty hash instead of raising InvalidDocument # on invalid payloads. def parse(document, options = {}) document = document.dup.permit!.to_h if document.is_a?(ActionController::Parameters) validate_payload(document) do |invalid_document, reason| yield invalid_document, reason if block_given? return {} end primary_data = document['data'] attributes = primary_data['attributes'] || {} attributes['id'] = primary_data['id'] if primary_data['id'] relationships = primary_data['relationships'] || {} filter_fields(attributes, options) filter_fields(relationships, options) hash = {} hash.merge!(parse_attributes(attributes, options)) hash.merge!(parse_relationships(relationships, options)) hash end # Checks whether a payload is compliant with the JSON API spec. # # @api private # rubocop:disable Metrics/CyclomaticComplexity def validate_payload(payload) unless payload.is_a?(Hash) yield payload, 'Expected hash' return end primary_data = payload['data'] unless primary_data.is_a?(Hash) yield payload, { data: 'Expected hash' } return end attributes = primary_data['attributes'] || {} unless attributes.is_a?(Hash) yield payload, { data: { attributes: 'Expected hash or nil' } } return end relationships = primary_data['relationships'] || {} unless relationships.is_a?(Hash) yield payload, { data: { relationships: 'Expected hash or nil' } } return end relationships.each do |(key, value)| unless value.is_a?(Hash) && value.key?('data') yield payload, { data: { relationships: { key => 'Expected hash with :data key' } } } end end end # rubocop:enable Metrics/CyclomaticComplexity # @api private def filter_fields(fields, options) if (only = options[:only]) fields.slice!(*Array(only).map(&:to_s)) elsif (except = options[:except]) fields.except!(*Array(except).map(&:to_s)) end end # @api private def field_key(field, options) (options[:keys] || {}).fetch(field.to_sym, field).to_sym end # @api private def parse_attributes(attributes, options) transform_keys(attributes, options) .map { |(k, v)| { field_key(k, options) => v } } .reduce({}, :merge) end # Given an association name, and a relationship data attribute, build a hash # mapping the corresponding ActiveRecord attribute to the corresponding value. # # @example # parse_relationship(:comments, [{ 'id' => '1', 'type' => 'comments' }, # { 'id' => '2', 'type' => 'comments' }], # {}) # # => { :comment_ids => ['1', '2'] } # parse_relationship(:author, { 'id' => '1', 'type' => 'users' }, {}) # # => { :author_id => '1' } # parse_relationship(:author, nil, {}) # # => { :author_id => nil } # @param [Symbol] assoc_name # @param [Hash] assoc_data # @param [Hash] options # @return [Hash{Symbol, Object}] # # @api private def parse_relationship(assoc_name, assoc_data, options) prefix_key = field_key(assoc_name, options).to_s.singularize hash = if assoc_data.is_a?(Array) { "#{prefix_key}_ids".to_sym => assoc_data.map { |ri| ri['id'] } } else { "#{prefix_key}_id".to_sym => assoc_data ? assoc_data['id'] : nil } end polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym) if polymorphic hash["#{prefix_key}_type".to_sym] = assoc_data.present? ? assoc_data['type'].classify : nil end hash end # @api private def parse_relationships(relationships, options) transform_keys(relationships, options) .map { |(k, v)| parse_relationship(k, v['data'], options) } .reduce({}, :merge) end # @api private def transform_keys(hash, options) transform = options[:key_transform] || :underscore CaseTransform.send(transform, hash) end end end end end active_model_serializers-0.10.10/lib/active_model_serializers/adapter/json_api/error.rb000066400000000000000000000067111351232231100314330ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Adapter class JsonApi < Base module Error # rubocop:disable Style/AsciiComments UnknownSourceTypeError = Class.new(ArgumentError) # Builds a JSON API Errors Object # {http://jsonapi.org/format/#errors JSON API Errors} # # @param [ActiveModel::Serializer::ErrorSerializer] error_serializer # @return [Array>] i.e. attribute_name, [attribute_errors] def self.resource_errors(error_serializer, options) error_serializer.as_json.flat_map do |attribute_name, attribute_errors| attribute_name = JsonApi.send(:transform_key_casing!, attribute_name, options) attribute_error_objects(attribute_name, attribute_errors) end end # definition: # JSON Object # # properties: # ☐ id : String # ☐ status : String # ☐ code : String # ☐ title : String # ā˜‘ detail : String # ☐ links # ☐ meta # ā˜‘ error_source # # description: # id : A unique identifier for this particular occurrence of the problem. # status : The HTTP status code applicable to this problem, expressed as a string value # code : An application-specific error code, expressed as a string value. # title : A short, human-readable summary of the problem. It **SHOULD NOT** change from # occurrence to occurrence of the problem, except for purposes of localization. # detail : A human-readable explanation specific to this occurrence of the problem. # structure: # { # title: 'SystemFailure', # detail: 'something went terribly wrong', # status: '500' # }.merge!(errorSource) def self.attribute_error_objects(attribute_name, attribute_errors) attribute_errors.map do |attribute_error| { source: error_source(:pointer, attribute_name), detail: attribute_error } end end # errorSource # description: # oneOf # ā˜‘ pointer : String # ā˜‘ parameter : String # # description: # pointer: A JSON Pointer RFC6901 to the associated entity in the request document e.g. "/data" # for a primary data object, or "/data/attributes/title" for a specific attribute. # https://tools.ietf.org/html/rfc6901 # # parameter: A string indicating which query parameter caused the error # structure: # if is_attribute? # { # pointer: '/data/attributes/red-button' # } # else # { # parameter: 'pres' # } # end def self.error_source(source_type, attribute_name) case source_type when :pointer { pointer: ActiveModelSerializers::JsonPointer.new(:attribute, attribute_name) } when :parameter { parameter: attribute_name } else fail UnknownSourceTypeError, "Unknown source type '#{source_type}' for attribute_name '#{attribute_name}'" end end # rubocop:enable Style/AsciiComments end end end end active_model_serializers-0.10.10/lib/active_model_serializers/adapter/json_api/jsonapi.rb000066400000000000000000000024621351232231100317440ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Adapter class JsonApi < Base # {http://jsonapi.org/format/#document-jsonapi-object Jsonapi Object} # toplevel_jsonapi # definition: # JSON Object # # properties: # version : String # meta # # description: # An object describing the server's implementation # structure: # { # version: ActiveModelSerializers.config.jsonapi_version, # meta: ActiveModelSerializers.config.jsonapi_toplevel_meta # }.reject! { |_, v| v.blank? } # prs: # https://github.com/rails-api/active_model_serializers/pull/1050 module Jsonapi module_function def add!(hash) hash.merge!(object) if include_object? end def include_object? ActiveModelSerializers.config.jsonapi_include_toplevel_object end # TODO: see if we can cache this def object object = { jsonapi: { version: ActiveModelSerializers.config.jsonapi_version, meta: ActiveModelSerializers.config.jsonapi_toplevel_meta } } object[:jsonapi].reject! { |_, v| v.blank? } object end end end end end active_model_serializers-0.10.10/lib/active_model_serializers/adapter/json_api/link.rb000066400000000000000000000036411351232231100312360ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Adapter class JsonApi # link # definition: # oneOf # linkString # linkObject # # description: # A link **MUST** be represented as either: a string containing the link's URL or a link # object." # structure: # if href? # linkString # else # linkObject # end # # linkString # definition: # URI # # description: # A string containing the link's URL. # structure: # 'http://example.com/link-string' # # linkObject # definition: # JSON Object # # properties: # href (required) : URI # meta # structure: # { # href: 'http://example.com/link-object', # meta: meta, # }.reject! {|_,v| v.nil? } class Link include SerializationContext::UrlHelpers def initialize(serializer, value) @_routes ||= nil # handles warning # actionpack-4.0.13/lib/action_dispatch/routing/route_set.rb:417: warning: instance variable @_routes not initialized @object = serializer.object @scope = serializer.scope # Use the return value of the block unless it is nil. if value.respond_to?(:call) @value = instance_eval(&value) else @value = value end end def href(value) @href = value nil end def meta(value) @meta = value nil end def as_json return @value if @value hash = {} hash[:href] = @href if defined?(@href) hash[:meta] = @meta if defined?(@meta) hash.any? ? hash : nil end protected attr_reader :object, :scope end end end end active_model_serializers-0.10.10/lib/active_model_serializers/adapter/json_api/meta.rb000066400000000000000000000015421351232231100312250ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Adapter class JsonApi # meta # definition: # JSON Object # # description: # Non-standard meta-information that can not be represented as an attribute or relationship. # structure: # { # attitude: 'adjustable' # } class Meta def initialize(serializer) @object = serializer.object @scope = serializer.scope # Use the return value of the block unless it is nil. if serializer._meta.respond_to?(:call) @value = instance_eval(&serializer._meta) else @value = serializer._meta end end def as_json @value end protected attr_reader :object, :scope end end end end active_model_serializers-0.10.10/lib/active_model_serializers/adapter/json_api/pagination_links.rb000066400000000000000000000045771351232231100336430ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Adapter class JsonApi < Base class PaginationLinks MissingSerializationContextError = Class.new(KeyError) FIRST_PAGE = 1 attr_reader :collection, :context def initialize(collection, adapter_options) @collection = collection @adapter_options = adapter_options @context = adapter_options.fetch(:serialization_context) do fail MissingSerializationContextError, <<-EOF.freeze JsonApi::PaginationLinks requires a ActiveModelSerializers::SerializationContext. Please pass a ':serialization_context' option or override CollectionSerializer#paginated? to return 'false'. EOF end end def as_json { self: location_url, first: first_page_url, prev: prev_page_url, next: next_page_url, last: last_page_url } end protected attr_reader :adapter_options private def location_url url_for_page(collection.current_page) end def first_page_url url_for_page(1) end def last_page_url if collection.total_pages == 0 url_for_page(FIRST_PAGE) else url_for_page(collection.total_pages) end end def prev_page_url return nil if collection.current_page == FIRST_PAGE url_for_page(collection.current_page - FIRST_PAGE) end def next_page_url return nil if collection.total_pages == 0 || collection.current_page == collection.total_pages url_for_page(collection.next_page) end def url_for_page(number) params = query_parameters.dup params[:page] = { size: per_page, number: number } "#{url(adapter_options)}?#{params.to_query}" end def url(options = {}) @url ||= options.fetch(:links, {}).fetch(:self, nil) || request_url end def request_url @request_url ||= context.request_url end def query_parameters @query_parameters ||= context.query_parameters end def per_page @per_page ||= collection.try(:per_page) || collection.try(:limit_value) || collection.size end end end end end active_model_serializers-0.10.10/lib/active_model_serializers/adapter/json_api/relationship.rb000066400000000000000000000075101351232231100330010ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Adapter class JsonApi class Relationship # {http://jsonapi.org/format/#document-resource-object-related-resource-links Document Resource Object Related Resource Links} # {http://jsonapi.org/format/#document-links Document Links} # {http://jsonapi.org/format/#document-resource-object-linkage Document Resource Relationship Linkage} # {http://jsonapi.org/format/#document-meta Document Meta} def initialize(parent_serializer, serializable_resource_options, association) @parent_serializer = parent_serializer @association = association @serializable_resource_options = serializable_resource_options end def as_json hash = {} hash[:data] = data_for(association) if association.include_data? links = links_for(association) hash[:links] = links if links.any? meta = meta_for(association) hash[:meta] = meta if meta hash[:meta] = {} if hash.empty? hash end protected attr_reader :parent_serializer, :serializable_resource_options, :association private # TODO(BF): Avoid db hit on belong_to_ releationship by using foreign_key on self def data_for(association) if association.collection? data_for_many(association) else data_for_one(association) end end def data_for_one(association) if belongs_to_id_on_self?(association) id = parent_serializer.read_attribute_for_serialization(association.reflection.foreign_key) type = if association.polymorphic? # We can't infer resource type for polymorphic relationships from the serializer. # We can ONLY know a polymorphic resource type by inspecting each resource. association.lazy_association.serializer.json_key else association.reflection.type.to_s end ResourceIdentifier.for_type_with_id(type, id, serializable_resource_options) else # TODO(BF): Process relationship without evaluating lazy_association serializer = association.lazy_association.serializer if (virtual_value = association.virtual_value) virtual_value elsif serializer && association.object ResourceIdentifier.new(serializer, serializable_resource_options).as_json else nil end end end def data_for_many(association) # TODO(BF): Process relationship without evaluating lazy_association collection_serializer = association.lazy_association.serializer if collection_serializer.respond_to?(:each) collection_serializer.map do |serializer| ResourceIdentifier.new(serializer, serializable_resource_options).as_json end elsif (virtual_value = association.virtual_value) virtual_value else [] end end def links_for(association) association.links.each_with_object({}) do |(key, value), hash| result = Link.new(parent_serializer, value).as_json hash[key] = result if result end end def meta_for(association) meta = association.meta meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta end def belongs_to_id_on_self?(association) parent_serializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship && association.belongs_to? && parent_serializer.object.respond_to?(association.reflection.foreign_key) end end end end end resource_identifier.rb000066400000000000000000000040431351232231100342500ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/active_model_serializers/adapter/json_api# frozen_string_literal: true module ActiveModelSerializers module Adapter class JsonApi class ResourceIdentifier def self.type_for(serializer, serializer_type = nil, transform_options = {}) raw_type = serializer_type ? serializer_type : raw_type_from_serializer_object(serializer.object) JsonApi.send(:transform_key_casing!, raw_type, transform_options) end def self.for_type_with_id(type, id, options) type = inflect_type(type) type = type_for(:no_class_needed, type, options) if id.blank? nil else { id: id.to_s, type: type } end end def self.raw_type_from_serializer_object(object) class_name = object.class.name # should use model_name raw_type = class_name.underscore raw_type = inflect_type(raw_type) raw_type .gsub!('/'.freeze, ActiveModelSerializers.config.jsonapi_namespace_separator) raw_type end def self.inflect_type(type) singularize = ActiveModelSerializers.config.jsonapi_resource_type == :singular inflection = singularize ? :singularize : :pluralize ActiveSupport::Inflector.public_send(inflection, type) end # {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects} def initialize(serializer, options) @id = id_for(serializer) @type = type_for(serializer, options) end def as_json if id.blank? { type: type } else { id: id.to_s, type: type } end end protected attr_reader :id, :type private def type_for(serializer, transform_options) serializer_type = serializer._type self.class.type_for(serializer, serializer_type, transform_options) end def id_for(serializer) serializer.read_attribute_for_serialization(:id).to_s end end end end end active_model_serializers-0.10.10/lib/active_model_serializers/adapter/null.rb000066400000000000000000000002521351232231100274440ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Adapter class Null < Base def serializable_hash(*) {} end end end end active_model_serializers-0.10.10/lib/active_model_serializers/callbacks.rb000066400000000000000000000032401351232231100267710ustar00rootroot00000000000000# frozen_string_literal: true # Adapted from # https://github.com/rails/rails/blob/7f18ea14c8/activejob/lib/active_job/callbacks.rb require 'active_support/callbacks' module ActiveModelSerializers # = ActiveModelSerializers Callbacks # # ActiveModelSerializers provides hooks during the life cycle of serialization and # allow you to trigger logic. Available callbacks are: # # * around_render # module Callbacks extend ActiveSupport::Concern include ActiveSupport::Callbacks included do define_callbacks :render end # These methods will be included into any ActiveModelSerializers object, adding # callbacks for +render+. module ClassMethods # Defines a callback that will get called around the render method, # whether it is as_json, to_json, or serializable_hash # # class ActiveModelSerializers::SerializableResource # include ActiveModelSerializers::Callbacks # # around_render do |args, block| # tag_logger do # notify_render do # block.call(args) # end # end # end # # def as_json # run_callbacks :render do # adapter.as_json # end # end # # Note: So that we can re-use the instrumenter for as_json, to_json, and # # serializable_hash, we aren't using the usual format, which would be: # # def render(args) # # adapter.as_json # # end # end # def around_render(*filters, &blk) set_callback(:render, :around, *filters, &blk) end end end end active_model_serializers-0.10.10/lib/active_model_serializers/deprecate.rb000066400000000000000000000033071351232231100270120ustar00rootroot00000000000000# frozen_string_literal: true ## # Provides a single method +deprecate+ to be used to declare when # something is going away. # # class Legacy # def self.klass_method # # ... # end # # def instance_method # # ... # end # # extend ActiveModelSerializers::Deprecate # deprecate :instance_method, "ActiveModelSerializers::NewPlace#new_method" # # class << self # extend ActiveModelSerializers::Deprecate # deprecate :klass_method, :none # end # end # # Adapted from https://github.com/rubygems/rubygems/blob/1591331/lib/rubygems/deprecate.rb module ActiveModelSerializers module Deprecate ## # Simple deprecation method that deprecates +name+ by wrapping it up # in a dummy method. It warns on each call to the dummy method # telling the user of +replacement+ (unless +replacement+ is :none) that it is planned to go away. def deprecate(name, replacement) old = "_deprecated_#{name}" alias_method old, name class_eval do define_method(name) do |*args, &block| target = is_a?(Module) ? "#{self}." : "#{self.class}#" msg = ["NOTE: #{target}#{name} is deprecated", replacement == :none ? ' with no replacement' : "; use #{replacement} instead", "\n#{target}#{name} called from #{ActiveModelSerializers.location_of_caller.join(':')}"] warn "#{msg.join}." send old, *args, &block end end end def delegate_and_deprecate(method, delegee) delegate method, to: delegee deprecate method, "#{delegee.name}." end module_function :deprecate module_function :delegate_and_deprecate end end active_model_serializers-0.10.10/lib/active_model_serializers/deserialization.rb000066400000000000000000000005101351232231100302350ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Deserialization module_function def jsonapi_parse(*args) Adapter::JsonApi::Deserialization.parse(*args) end # :nocov: def jsonapi_parse!(*args) Adapter::JsonApi::Deserialization.parse!(*args) end # :nocov: end end active_model_serializers-0.10.10/lib/active_model_serializers/json_pointer.rb000066400000000000000000000005021351232231100275610ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module JsonPointer module_function POINTERS = { attribute: '/data/attributes/%s'.freeze, primary_data: '/data%s'.freeze }.freeze def new(pointer_type, value = nil) format(POINTERS[pointer_type], value) end end end active_model_serializers-0.10.10/lib/active_model_serializers/logging.rb000066400000000000000000000067771351232231100265220ustar00rootroot00000000000000# frozen_string_literal: true ## # ActiveModelSerializers::Logging # # https://github.com/rails/rails/blob/280654ef88/activejob/lib/active_job/logging.rb # module ActiveModelSerializers module Logging RENDER_EVENT = 'render.active_model_serializers'.freeze extend ActiveSupport::Concern included do include ActiveModelSerializers::Callbacks extend Macros instrument_rendering end module ClassMethods def instrument_rendering around_render do |args, block| tag_logger do notify_render do block.call(args) end end end end end # Macros that can be used to customize the logging of class or instance methods, # by extending the class or its singleton. # # Adapted from: # https://github.com/rubygems/rubygems/blob/cb28f5e991/lib/rubygems/deprecate.rb # # Provides a single method +notify+ to be used to declare when # something a method notifies, with the argument +callback_name+ of the notification callback. # # class Adapter # def self.klass_method # # ... # end # # def instance_method # # ... # end # # include ActiveModelSerializers::Logging::Macros # notify :instance_method, :render # # class << self # extend ActiveModelSerializers::Logging::Macros # notify :klass_method, :render # end # end module Macros ## # Simple notify method that wraps up +name+ # in a dummy method. It notifies on with the +callback_name+ notifier on # each call to the dummy method, telling what the current serializer and adapter # are being rendered. # Adapted from: # https://github.com/rubygems/rubygems/blob/cb28f5e991/lib/rubygems/deprecate.rb def notify(name, callback_name) class_eval do old = "_notifying_#{callback_name}_#{name}" alias_method old, name define_method name do |*args, &block| run_callbacks callback_name do send old, *args, &block end end end end end def notify_render(*) event_name = RENDER_EVENT ActiveSupport::Notifications.instrument(event_name, notify_render_payload) do yield end end def notify_render_payload { serializer: serializer || ActiveModel::Serializer::Null, adapter: adapter || ActiveModelSerializers::Adapter::Null } end private def tag_logger(*tags) if ActiveModelSerializers.logger.respond_to?(:tagged) tags.unshift 'active_model_serializers'.freeze unless logger_tagged_by_active_model_serializers? ActiveModelSerializers.logger.tagged(*tags) { yield } else yield end end def logger_tagged_by_active_model_serializers? ActiveModelSerializers.logger.formatter.current_tags.include?('active_model_serializers'.freeze) end class LogSubscriber < ActiveSupport::LogSubscriber def render(event) info do serializer = event.payload[:serializer] adapter = event.payload[:adapter] duration = event.duration.round(2) "Rendered #{serializer.name} with #{adapter.class} (#{duration}ms)" end end def logger ActiveModelSerializers.logger end end end end ActiveModelSerializers::Logging::LogSubscriber.attach_to :active_model_serializers active_model_serializers-0.10.10/lib/active_model_serializers/lookup_chain.rb000066400000000000000000000044651351232231100275370ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module LookupChain # Standard appending of Serializer to the resource name. # # Example: # Author => AuthorSerializer BY_RESOURCE = lambda do |resource_class, _serializer_class, _namespace| serializer_from(resource_class) end # Uses the namespace of the resource to find the serializer # # Example: # British::Author => British::AuthorSerializer BY_RESOURCE_NAMESPACE = lambda do |resource_class, _serializer_class, _namespace| resource_namespace = namespace_for(resource_class) serializer_name = serializer_from(resource_class) "#{resource_namespace}::#{serializer_name}" end # Uses the controller namespace of the resource to find the serializer # # Example: # Api::V3::AuthorsController => Api::V3::AuthorSerializer BY_NAMESPACE = lambda do |resource_class, _serializer_class, namespace| resource_name = resource_class_name(resource_class) namespace ? "#{namespace}::#{resource_name}Serializer" : nil end # Allows for serializers to be defined in parent serializers # - useful if a relationship only needs a different set of attributes # than if it were rendered independently. # # Example: # class BlogSerializer < ActiveModel::Serializer # class AuthorSerialier < ActiveModel::Serializer # ... # end # # belongs_to :author # ... # end # # The belongs_to relationship would be rendered with # BlogSerializer::AuthorSerialier BY_PARENT_SERIALIZER = lambda do |resource_class, serializer_class, _namespace| return if serializer_class == ActiveModel::Serializer serializer_name = serializer_from(resource_class) "#{serializer_class}::#{serializer_name}" end DEFAULT = [ BY_PARENT_SERIALIZER, BY_NAMESPACE, BY_RESOURCE_NAMESPACE, BY_RESOURCE ].freeze module_function def namespace_for(klass) klass.name.deconstantize end def resource_class_name(klass) klass.name.demodulize end def serializer_from_resource_name(name) "#{name}Serializer" end def serializer_from(klass) name = resource_class_name(klass) serializer_from_resource_name(name) end end end active_model_serializers-0.10.10/lib/active_model_serializers/model.rb000066400000000000000000000123251351232231100261560ustar00rootroot00000000000000# frozen_string_literal: true # ActiveModelSerializers::Model is a convenient superclass for making your models # from Plain-Old Ruby Objects (PORO). It also serves as a reference implementation # that satisfies ActiveModel::Serializer::Lint::Tests. require 'active_support/core_ext/hash' module ActiveModelSerializers class Model include ActiveModel::Serializers::JSON include ActiveModel::Model # Declare names of attributes to be included in +attributes+ hash. # Is only available as a class-method since the ActiveModel::Serialization mixin in Rails # uses an +attribute_names+ local variable, which may conflict if we were to add instance methods here. # # @overload attribute_names # @return [Array] class_attribute :attribute_names, instance_writer: false, instance_reader: false # Initialize +attribute_names+ for all subclasses. The array is usually # mutated in the +attributes+ method, but can be set directly, as well. self.attribute_names = [] # Easily declare instance attributes with setters and getters for each. # # To initialize an instance, all attributes must have setters. # However, the hash returned by +attributes+ instance method will ALWAYS # be the value of the initial attributes, regardless of what accessors are defined. # The only way to change the change the attributes after initialization is # to mutate the +attributes+ directly. # Accessor methods do NOT mutate the attributes. (This is a bug). # # @note For now, the Model only supports the notion of 'attributes'. # In the tests, there is a special Model that also supports 'associations'. This is # important so that we can add accessors for values that should not appear in the # attributes hash when modeling associations. It is not yet clear if it # makes sense for a PORO to have associations outside of the tests. # # @overload attributes(names) # @param names [Array] # @param name [String, Symbol] def self.attributes(*names) self.attribute_names |= names.map(&:to_sym) # Silence redefinition of methods warnings ActiveModelSerializers.silence_warnings do attr_accessor(*names) end end # Opt-in to breaking change def self.derive_attributes_from_names_and_fix_accessors unless included_modules.include?(DeriveAttributesFromNamesAndFixAccessors) prepend(DeriveAttributesFromNamesAndFixAccessors) end end module DeriveAttributesFromNamesAndFixAccessors def self.included(base) # NOTE that +id+ will always be in +attributes+. base.attributes :id end # Override the +attributes+ method so that the hash is derived from +attribute_names+. # # The fields in +attribute_names+ determines the returned hash. # +attributes+ are returned frozen to prevent any expectations that mutation affects # the actual values in the model. def attributes self.class.attribute_names.each_with_object({}) do |attribute_name, result| result[attribute_name] = public_send(attribute_name).freeze end.with_indifferent_access.freeze end end # Support for validation and other ActiveModel::Errors # @return [ActiveModel::Errors] attr_reader :errors # (see #updated_at) attr_writer :updated_at # The only way to change the attributes of an instance is to directly mutate the attributes. # @example # # model.attributes[:foo] = :bar # @return [Hash] attr_reader :attributes # @param attributes [Hash] def initialize(attributes = {}) attributes ||= {} # protect against nil @attributes = attributes.symbolize_keys.with_indifferent_access @errors = ActiveModel::Errors.new(self) super end # Defaults to the downcased model name. # This probably isn't a good default, since it's not a unique instance identifier, # but that's what is currently implemented \_('-')_/. # # @note Though +id+ is defined, it will only show up # in +attributes+ when it is passed in to the initializer or added to +attributes+, # such as attributes[:id] = 5. # @return [String, Numeric, Symbol] def id attributes.fetch(:id) do defined?(@id) ? @id : self.class.model_name.name && self.class.model_name.name.downcase end end # When not set, defaults to the time the file was modified. # # @note Though +updated_at+ and +updated_at=+ are defined, it will only show up # in +attributes+ when it is passed in to the initializer or added to +attributes+, # such as attributes[:updated_at] = Time.current. # @return [String, Numeric, Time] def updated_at attributes.fetch(:updated_at) do defined?(@updated_at) ? @updated_at : File.mtime(__FILE__) end end # To customize model behavior, this method must be redefined. However, # there are other ways of setting the +cache_key+ a serializer uses. # @return [String] def cache_key ActiveSupport::Cache.expand_cache_key([ self.class.model_name.name.downcase, "#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}" ].compact) end end end active_model_serializers-0.10.10/lib/active_model_serializers/railtie.rb000066400000000000000000000037561351232231100265170ustar00rootroot00000000000000# frozen_string_literal: true require 'rails/railtie' require 'action_controller' require 'action_controller/railtie' require 'action_controller/serialization' module ActiveModelSerializers class Railtie < Rails::Railtie config.eager_load_namespaces << ActiveModelSerializers config.to_prepare do ActiveModel::Serializer.serializers_cache.clear end initializer 'active_model_serializers.action_controller' do ActiveSupport.on_load(:action_controller) do include(::ActionController::Serialization) end end initializer 'active_model_serializers.prepare_serialization_context' do SerializationContext.url_helpers = Rails.application.routes.url_helpers SerializationContext.default_url_options = Rails.application.routes.default_url_options end # This hook is run after the action_controller railtie has set the configuration # based on the *environment* configuration and before any config/initializers are run # and also before eager_loading (if enabled). initializer 'active_model_serializers.set_configs', after: 'action_controller.set_configs' do ActiveModelSerializers.logger = Rails.configuration.action_controller.logger ActiveModelSerializers.config.perform_caching = Rails.configuration.action_controller.perform_caching # We want this hook to run after the config has been set, even if ActionController has already loaded. ActiveSupport.on_load(:action_controller) do ActiveModelSerializers.config.cache_store = ActionController::Base.cache_store end end # :nocov: generators do |app| Rails::Generators.configure!(app.config.generators) Rails::Generators.hidden_namespaces.uniq! require 'generators/rails/resource_override' end # :nocov: if Rails.env.test? ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Schema) ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Serializer) end end end active_model_serializers-0.10.10/lib/active_model_serializers/register_jsonapi_renderer.rb000066400000000000000000000050271351232231100323140ustar00rootroot00000000000000# frozen_string_literal: true # Based on discussion in https://github.com/rails/rails/pull/23712#issuecomment-184977238, # the JSON API media type will have its own format/renderer. # # > We recommend the media type be registered on its own as jsonapi # when a jsonapi Renderer and deserializer (Http::Parameters::DEFAULT_PARSERS) are added. # # Usage: # # ActiveSupport.on_load(:action_controller) do # require 'active_model_serializers/register_jsonapi_renderer' # end # # And then in controllers, use `render jsonapi: model` rather than `render json: model, adapter: :json_api`. # # For example, in a controller action, we can: # respond_to do |format| # format.jsonapi { render jsonapi: model } # end # # or # # render jsonapi: model # # No wrapper format needed as it does not apply (i.e. no `wrap_parameters format: [jsonapi]`) module ActiveModelSerializers module Jsonapi MEDIA_TYPE = 'application/vnd.api+json'.freeze HEADERS = { response: { 'CONTENT_TYPE'.freeze => MEDIA_TYPE }, request: { 'ACCEPT'.freeze => MEDIA_TYPE } }.freeze def self.install # actionpack/lib/action_dispatch/http/mime_types.rb Mime::Type.register MEDIA_TYPE, :jsonapi if Rails::VERSION::MAJOR >= 5 ActionDispatch::Request.parameter_parsers[:jsonapi] = parser else ActionDispatch::ParamsParser::DEFAULT_PARSERS[Mime[:jsonapi]] = parser end # ref https://github.com/rails/rails/pull/21496 ActionController::Renderers.add :jsonapi do |json, options| json = serialize_jsonapi(json, options).to_json(options) unless json.is_a?(String) self.content_type ||= Mime[:jsonapi] self.response_body = json end end # Proposal: should actually deserialize the JSON API params # to the hash format expected by `ActiveModel::Serializers::JSON` # actionpack/lib/action_dispatch/http/parameters.rb def self.parser lambda do |body| data = JSON.parse(body) data = { _json: data } unless data.is_a?(Hash) data.with_indifferent_access end end module ControllerSupport def serialize_jsonapi(json, options) options[:adapter] = :json_api options.fetch(:serialization_context) do options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request) end get_serializer(json, options) end end end end ActiveModelSerializers::Jsonapi.install ActiveSupport.on_load(:action_controller) do include ActiveModelSerializers::Jsonapi::ControllerSupport end active_model_serializers-0.10.10/lib/active_model_serializers/serializable_resource.rb000066400000000000000000000051361351232231100314350ustar00rootroot00000000000000# frozen_string_literal: true require 'set' module ActiveModelSerializers class SerializableResource ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links, :serialization_context, :key_transform]) include ActiveModelSerializers::Logging delegate :serializable_hash, :as_json, :to_json, to: :adapter notify :serializable_hash, :render notify :as_json, :render notify :to_json, :render # Primary interface to composing a resource with a serializer and adapter. # @return the serializable_resource, ready for #as_json/#to_json/#serializable_hash. def initialize(resource, options = {}) @resource = resource @adapter_opts = options.select { |k, _| ADAPTER_OPTION_KEYS.include? k } @serializer_opts = options.reject { |k, _| ADAPTER_OPTION_KEYS.include? k } end def serialization_scope=(scope) serializer_opts[:scope] = scope end def serialization_scope serializer_opts[:scope] end def serialization_scope_name=(scope_name) serializer_opts[:scope_name] = scope_name end # NOTE: if no adapter is available, returns the resource itself. (i.e. adapter is a no-op) def adapter @adapter ||= find_adapter end alias adapter_instance adapter def find_adapter return resource unless serializer? adapter = catch :no_serializer do ActiveModelSerializers::Adapter.create(serializer_instance, adapter_opts) end adapter || resource end def serializer_instance @serializer_instance ||= serializer.new(resource, serializer_opts) end # Get serializer either explicitly :serializer or implicitly from resource # Remove :serializer key from serializer_opts # Remove :each_serializer if present and set as :serializer key def serializer @serializer ||= begin @serializer = serializer_opts.delete(:serializer) @serializer ||= ActiveModel::Serializer.serializer_for(resource, serializer_opts) if serializer_opts.key?(:each_serializer) serializer_opts[:serializer] = serializer_opts.delete(:each_serializer) end @serializer end end alias serializer_class serializer # True when no explicit adapter given, or explicit appear is truthy (non-nil) # False when explicit adapter is falsy (nil or false) def use_adapter? !(adapter_opts.key?(:adapter) && !adapter_opts[:adapter]) end def serializer? use_adapter? && !serializer.nil? end protected attr_reader :resource, :adapter_opts, :serializer_opts end end active_model_serializers-0.10.10/lib/active_model_serializers/serialization_context.rb000066400000000000000000000022751351232231100315020ustar00rootroot00000000000000# frozen_string_literal: true require 'active_support/core_ext/array/extract_options' module ActiveModelSerializers class SerializationContext class << self attr_writer :url_helpers, :default_url_options def url_helpers @url_helpers ||= Module.new end def default_url_options @default_url_options ||= {} end end module UrlHelpers def self.included(base) base.send(:include, SerializationContext.url_helpers) end def default_url_options SerializationContext.default_url_options end end attr_reader :request_url, :query_parameters, :key_transform def initialize(*args) options = args.extract_options! if args.size == 1 request = args.pop options[:request_url] = request.original_url[/\A[^?]+/] options[:query_parameters] = request.query_parameters end @request_url = options.delete(:request_url) @query_parameters = options.delete(:query_parameters) @url_helpers = options.delete(:url_helpers) || self.class.url_helpers @default_url_options = options.delete(:default_url_options) || self.class.default_url_options end end end active_model_serializers-0.10.10/lib/active_model_serializers/test.rb000066400000000000000000000002461351232231100260340ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Test extend ActiveSupport::Autoload autoload :Serializer autoload :Schema end end active_model_serializers-0.10.10/lib/active_model_serializers/test/000077500000000000000000000000001351232231100255055ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/active_model_serializers/test/schema.rb000066400000000000000000000103321351232231100272710ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModelSerializers module Test module Schema # A Minitest Assertion that test the response is valid against a schema. # @param schema_path [String] a custom schema path # @param message [String] a custom error message # @return [Boolean] true when the response is valid # @return [Minitest::Assertion] when the response is invalid # @example # get :index # assert_response_schema def assert_response_schema(schema_path = nil, message = nil) matcher = AssertResponseSchema.new(schema_path, request, response, message) assert(matcher.call, matcher.message) end def assert_request_schema(schema_path = nil, message = nil) matcher = AssertRequestSchema.new(schema_path, request, response, message) assert(matcher.call, matcher.message) end # May be renamed def assert_request_response_schema(schema_path = nil, message = nil) assert_request_schema(schema_path, message) assert_response_schema(schema_path, message) end def assert_schema(payload, schema_path = nil, message = nil) matcher = AssertSchema.new(schema_path, request, response, message, payload) assert(matcher.call, matcher.message) end MissingSchema = Class.new(Minitest::Assertion) InvalidSchemaError = Class.new(Minitest::Assertion) class AssertSchema attr_reader :schema_path, :request, :response, :message, :payload # Interface may change. def initialize(schema_path, request, response, message, payload = nil) require_json_schema! @request = request @response = response @payload = payload @schema_path = schema_path || schema_path_default @message = message @document_store = JsonSchema::DocumentStore.new add_schema_to_document_store end def call json_schema.expand_references!(store: document_store) status, errors = json_schema.validate(response_body) @message = [message, errors.map(&:to_s).to_sentence].compact.join(': ') status end protected attr_reader :document_store def controller_path request.filtered_parameters.with_indifferent_access[:controller] end def action request.filtered_parameters.with_indifferent_access[:action] end def schema_directory ActiveModelSerializers.config.schema_path end def schema_full_path "#{schema_directory}/#{schema_path}" end def schema_path_default "#{controller_path}/#{action}.json" end def schema_data load_json_file(schema_full_path) end def response_body load_json(response.body) end def request_params request.env['action_dispatch.request.request_parameters'] end def json_schema @json_schema ||= JsonSchema.parse!(schema_data) end def add_schema_to_document_store Dir.glob("#{schema_directory}/**/*.json").each do |path| schema_data = load_json_file(path) extra_schema = JsonSchema.parse!(schema_data) document_store.add_schema(extra_schema) end end def load_json(json) JSON.parse(json) rescue JSON::ParserError => ex raise InvalidSchemaError, ex.message end def load_json_file(path) load_json(File.read(path)) rescue Errno::ENOENT raise MissingSchema, "No Schema file at #{schema_full_path}" end def require_json_schema! require 'json_schema' rescue LoadError raise LoadError, "You don't have json_schema installed in your application. Please add it to your Gemfile and run bundle install" end end class AssertResponseSchema < AssertSchema def initialize(*) super @payload = response_body end end class AssertRequestSchema < AssertSchema def initialize(*) super @payload = request_params end end end end end active_model_serializers-0.10.10/lib/active_model_serializers/test/serializer.rb000066400000000000000000000072261351232231100302120ustar00rootroot00000000000000# frozen_string_literal: true require 'set' module ActiveModelSerializers module Test module Serializer extend ActiveSupport::Concern included do setup :setup_serialization_subscriptions teardown :teardown_serialization_subscriptions end # Asserts that the request was rendered with the appropriate serializers. # # # assert that the "PostSerializer" serializer was rendered # assert_serializer "PostSerializer" # # # return a custom error message # assert_serializer "PostSerializer", "PostSerializer not rendered" # # # assert that the instance of PostSerializer was rendered # assert_serializer PostSerializer # # # assert that the "PostSerializer" serializer was rendered # assert_serializer :post_serializer # # # assert that the rendered serializer starts with "Post" # assert_serializer %r{\APost.+\Z} # # # assert that no serializer was rendered # assert_serializer nil # def assert_serializer(expectation, message = nil) @assert_serializer.expectation = expectation @assert_serializer.message = message @assert_serializer.response = response assert(@assert_serializer.matches?, @assert_serializer.message) end class AssertSerializer attr_reader :serializers, :message attr_accessor :response, :expectation def initialize @serializers = Set.new @_subscribers = [] end def message=(message) @message = message || "expecting <#{expectation.inspect}> but rendering with <#{serializers.to_a}>" end def matches? # Force body to be read in case the template is being streamed. response.body case expectation when a_serializer? then matches_class? when Symbol then matches_symbol? when String then matches_string? when Regexp then matches_regexp? when NilClass then matches_nil? else fail ArgumentError, 'assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil' end end def subscribe @_subscribers << ActiveSupport::Notifications.subscribe(event_name) do |_name, _start, _finish, _id, payload| serializer = payload[:serializer].name serializers << serializer end end def unsubscribe @_subscribers.each do |subscriber| ActiveSupport::Notifications.unsubscribe(subscriber) end end private def matches_class? serializers.include?(expectation.name) end def matches_symbol? camelize_expectation = expectation.to_s.camelize serializers.include?(camelize_expectation) end def matches_string? !expectation.empty? && serializers.include?(expectation) end def matches_regexp? serializers.any? do |serializer| serializer.match(expectation) end end def matches_nil? serializers.empty? end def a_serializer? ->(exp) { exp.is_a?(Class) && exp < ActiveModel::Serializer } end def event_name ::ActiveModelSerializers::Logging::RENDER_EVENT end end private def setup_serialization_subscriptions @assert_serializer = AssertSerializer.new @assert_serializer.subscribe end def teardown_serialization_subscriptions @assert_serializer.unsubscribe end end end end active_model_serializers-0.10.10/lib/generators/000077500000000000000000000000001351232231100216305ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/generators/rails/000077500000000000000000000000001351232231100227425ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/generators/rails/USAGE000066400000000000000000000002031351232231100235240ustar00rootroot00000000000000Description: Generates a serializer for the given resource. Example: `rails generate serializer Account name created_at` active_model_serializers-0.10.10/lib/generators/rails/resource_override.rb000066400000000000000000000004011351232231100270100ustar00rootroot00000000000000# frozen_string_literal: true require 'rails/generators' require 'rails/generators/rails/resource/resource_generator' module Rails module Generators class ResourceGenerator hook_for :serializer, default: true, type: :boolean end end end active_model_serializers-0.10.10/lib/generators/rails/serializer_generator.rb000066400000000000000000000020331351232231100275040ustar00rootroot00000000000000# frozen_string_literal: true module Rails module Generators class SerializerGenerator < NamedBase source_root File.expand_path('../templates', __FILE__) check_class_collision suffix: 'Serializer' argument :attributes, type: :array, default: [], banner: 'field:type field:type' class_option :parent, type: :string, desc: 'The parent class for the generated serializer' def create_serializer_file template 'serializer.rb.erb', File.join('app/serializers', class_path, "#{file_name}_serializer.rb") end private def attributes_names [:id] + attributes.reject(&:reference?).map! { |a| a.name.to_sym } end def association_names attributes.select(&:reference?).map! { |a| a.name.to_sym } end def parent_class_name if options[:parent] options[:parent] elsif 'ApplicationSerializer'.safe_constantize 'ApplicationSerializer' else 'ActiveModel::Serializer' end end end end end active_model_serializers-0.10.10/lib/generators/rails/templates/000077500000000000000000000000001351232231100247405ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/generators/rails/templates/serializer.rb.erb000066400000000000000000000003741351232231100302110ustar00rootroot00000000000000<% module_namespacing do -%> class <%= class_name %>Serializer < <%= parent_class_name %> attributes <%= attributes_names.map(&:inspect).join(", ") %> <% association_names.each do |attribute| -%> has_one :<%= attribute %> <% end -%> end <% end -%> active_model_serializers-0.10.10/lib/grape/000077500000000000000000000000001351232231100205555ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/grape/active_model_serializers.rb000066400000000000000000000011141351232231100261460ustar00rootroot00000000000000# frozen_string_literal: true # To add Grape support, require 'grape/active_model_serializers' in the base of your Grape endpoints # Then add 'include Grape::ActiveModelSerializers' to enable the formatter and helpers require 'active_model_serializers' require 'grape/formatters/active_model_serializers' require 'grape/helpers/active_model_serializers' module Grape module ActiveModelSerializers extend ActiveSupport::Concern included do formatter :json, Grape::Formatters::ActiveModelSerializers helpers Grape::Helpers::ActiveModelSerializers end end end active_model_serializers-0.10.10/lib/grape/formatters/000077500000000000000000000000001351232231100227435ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/grape/formatters/active_model_serializers.rb000066400000000000000000000022301351232231100303340ustar00rootroot00000000000000# frozen_string_literal: true # A Grape response formatter that can be used as 'formatter :json, Grape::Formatters::ActiveModelSerializers' # # Serializer options can be passed as a hash from your Grape endpoint using env[:active_model_serializer_options], # or better yet user the render helper in Grape::Helpers::ActiveModelSerializers require 'active_model_serializers/serialization_context' module Grape module Formatters module ActiveModelSerializers def self.call(resource, env) serializer_options = build_serializer_options(env) ::ActiveModelSerializers::SerializableResource.new(resource, serializer_options).to_json end def self.build_serializer_options(env) ams_options = env[:active_model_serializer_options] || {} # Add serialization context ams_options.fetch(:serialization_context) do request = env['grape.request'] ams_options[:serialization_context] = ::ActiveModelSerializers::SerializationContext.new( request_url: request.url[/\A[^?]+/], query_parameters: request.params ) end ams_options end end end end active_model_serializers-0.10.10/lib/grape/helpers/000077500000000000000000000000001351232231100222175ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/grape/helpers/active_model_serializers.rb000066400000000000000000000013001351232231100276050ustar00rootroot00000000000000# frozen_string_literal: true # Helpers can be included in your Grape endpoint as: helpers Grape::Helpers::ActiveModelSerializers module Grape module Helpers module ActiveModelSerializers # A convenience method for passing ActiveModelSerializers serializer options # # Example: To include relationships in the response: render(post, include: ['comments']) # # Example: To include pagination meta data: render(posts, meta: { page: posts.page, total_pages: posts.total_pages }) def render(resource, active_model_serializer_options = {}) env[:active_model_serializer_options] = active_model_serializer_options resource end end end end active_model_serializers-0.10.10/lib/tasks/000077500000000000000000000000001351232231100206045ustar00rootroot00000000000000active_model_serializers-0.10.10/lib/tasks/rubocop.rake000066400000000000000000000031701351232231100231220ustar00rootroot00000000000000# frozen_string_literal: true begin require 'rubocop' require 'rubocop/rake_task' rescue LoadError # rubocop:disable Lint/HandleExceptions else require 'rbconfig' # https://github.com/bundler/bundler/blob/1b3eb2465a/lib/bundler/constants.rb#L2 windows_platforms = /(msdos|mswin|djgpp|mingw)/ if RbConfig::CONFIG['host_os'] =~ windows_platforms desc 'No-op rubocop on Windows-- unsupported platform' task :rubocop do puts 'Skipping rubocop on Windows' end elsif defined?(::Rubinius) desc 'No-op rubocop to avoid rbx segfault' task :rubocop do puts 'Skipping rubocop on rbx due to segfault' puts 'https://github.com/rubinius/rubinius/issues/3499' end else Rake::Task[:rubocop].clear if Rake::Task.task_defined?(:rubocop) patterns = [ 'Gemfile', 'Rakefile', 'lib/**/*.{rb,rake}', 'config/**/*.rb', 'app/**/*.rb', 'test/**/*.rb' ] desc 'Execute rubocop' RuboCop::RakeTask.new(:rubocop) do |task| task.options = ['--rails', '--display-cop-names', '--display-style-guide'] task.formatters = ['progress'] task.patterns = patterns task.fail_on_error = true end namespace :rubocop do desc 'Auto-gen rubocop config' task :auto_gen_config do options = ['--auto-gen-config'].concat patterns require 'benchmark' result = 0 cli = RuboCop::CLI.new time = Benchmark.realtime do result = cli.run(options) end puts "Finished in #{time} seconds" if cli.options[:debug] abort('RuboCop failed!') if result.nonzero? end end end end active_model_serializers-0.10.10/test/000077500000000000000000000000001351232231100176705ustar00rootroot00000000000000active_model_serializers-0.10.10/test/action_controller/000077500000000000000000000000001351232231100234105ustar00rootroot00000000000000active_model_serializers-0.10.10/test/action_controller/adapter_selector_test.rb000066400000000000000000000037271351232231100303250ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActionController module Serialization class AdapterSelectorTest < ActionController::TestCase class Profile < Model attributes :id, :name, :description associations :comments end class ProfileSerializer < ActiveModel::Serializer type 'profiles' attributes :name, :description end class AdapterSelectorTestController < ActionController::Base def render_using_default_adapter @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') render json: @profile end def render_using_adapter_override @profile = Profile.new(id: 'render_using_adapter_override', name: 'Name 1', description: 'Description 1', comments: 'Comments 1') render json: @profile, adapter: :json_api end def render_skipping_adapter @profile = Profile.new(id: 'render_skipping_adapter_id', name: 'Name 1', description: 'Description 1', comments: 'Comments 1') render json: @profile, adapter: false end end tests AdapterSelectorTestController def test_render_using_default_adapter get :render_using_default_adapter assert_equal '{"name":"Name 1","description":"Description 1"}', response.body end def test_render_using_adapter_override get :render_using_adapter_override expected = { data: { id: 'render_using_adapter_override', type: 'profiles', attributes: { name: 'Name 1', description: 'Description 1' } } } assert_equal expected.to_json, response.body end def test_render_skipping_adapter get :render_skipping_adapter assert_equal '{"id":"render_skipping_adapter_id","name":"Name 1","description":"Description 1"}', response.body end end end end active_model_serializers-0.10.10/test/action_controller/explicit_serializer_test.rb000066400000000000000000000110011351232231100310370ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActionController module Serialization class ExplicitSerializerTest < ActionController::TestCase class ExplicitSerializerTestController < ActionController::Base def render_using_explicit_serializer @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') render json: @profile, serializer: ProfilePreviewSerializer end def render_array_using_explicit_serializer array = [ Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1'), Profile.new(name: 'Name 2', description: 'Description 2', comments: 'Comments 2') ] render json: array, serializer: PaginatedSerializer, each_serializer: ProfilePreviewSerializer end def render_array_using_implicit_serializer array = [ Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1'), Profile.new(name: 'Name 2', description: 'Description 2', comments: 'Comments 2') ] render json: array, each_serializer: ProfilePreviewSerializer end def render_array_using_explicit_serializer_and_custom_serializers @post = Post.new(title: 'New Post', body: 'Body') @author = Author.new(name: 'Jane Blogger') @author.posts = [@post] @post.author = @author @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment] @first_comment.post = @post @first_comment.author = nil @second_comment.post = @post @second_comment.author = nil @blog = Blog.new(id: 23, name: 'AMS Blog') @post.blog = @blog render json: [@post], each_serializer: PostPreviewSerializer end def render_using_explicit_each_serializer location = Location.new(id: 42, lat: '-23.550520', lng: '-46.633309') place = Place.new(id: 1337, name: 'Amazing Place', locations: [location]) render json: place, each_serializer: PlaceSerializer end end tests ExplicitSerializerTestController def test_render_using_explicit_serializer get :render_using_explicit_serializer assert_equal 'application/json', @response.content_type assert_equal '{"name":"Name 1"}', @response.body end def test_render_array_using_explicit_serializer get :render_array_using_explicit_serializer assert_equal 'application/json', @response.content_type expected = [ { 'name' => 'Name 1' }, { 'name' => 'Name 2' } ] assert_equal expected.to_json, @response.body end def test_render_array_using_implicit_serializer get :render_array_using_implicit_serializer assert_equal 'application/json', @response.content_type expected = [ { 'name' => 'Name 1' }, { 'name' => 'Name 2' } ] assert_equal expected.to_json, @response.body end def test_render_array_using_explicit_serializer_and_custom_serializers get :render_array_using_explicit_serializer_and_custom_serializers expected = [ { 'title' => 'New Post', 'body' => 'Body', 'id' => @controller.instance_variable_get(:@post).id, 'comments' => [{ 'id' => 1 }, { 'id' => 2 }], 'author' => { 'id' => @controller.instance_variable_get(:@author).id } } ] assert_equal expected.to_json, @response.body end def test_render_using_explicit_each_serializer get :render_using_explicit_each_serializer expected = { id: 1337, name: 'Amazing Place', locations: [ { id: 42, lat: '-23.550520', lng: '-46.633309', address: 'Nowhere' # is a virtual attribute on LocationSerializer } ] } assert_equal expected.to_json, response.body end end end end active_model_serializers-0.10.10/test/action_controller/json/000077500000000000000000000000001351232231100243615ustar00rootroot00000000000000active_model_serializers-0.10.10/test/action_controller/json/include_test.rb000066400000000000000000000167271351232231100274050ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActionController module Serialization class Json class IncludeTest < ActionController::TestCase INCLUDE_STRING = 'posts.comments'.freeze INCLUDE_HASH = { posts: :comments }.freeze DEEP_INCLUDE = 'posts.comments.author'.freeze class IncludeTestController < ActionController::Base def setup_data ActionController::Base.cache_store.clear @author = Author.new(id: 1, name: 'Steve K.') @post = Post.new(id: 42, title: 'New Post', body: 'Body') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment] @post.author = @author @first_comment.post = @post @second_comment.post = @post @blog = Blog.new(id: 1, name: 'My Blog!!') @post.blog = @blog @author.posts = [@post] @first_comment.author = @author @second_comment.author = @author @author.comments = [@first_comment, @second_comment] @author.roles = [] @author.bio = {} end def render_without_include setup_data render json: @author, adapter: :json end def render_resource_with_include_hash setup_data render json: @author, include: INCLUDE_HASH, adapter: :json end def render_resource_with_include_string setup_data render json: @author, include: INCLUDE_STRING, adapter: :json end def render_resource_with_deep_include setup_data render json: @author, include: DEEP_INCLUDE, adapter: :json end def render_without_recursive_relationships # testing recursive includes ('**') can't have any cycles in the # relationships, or we enter an infinite loop. author = Author.new(id: 11, name: 'Jane Doe') post = Post.new(id: 12, title: 'Hello World', body: 'My first post') comment = Comment.new(id: 13, body: 'Commentary') author.posts = [post] post.comments = [comment] render json: author end end tests IncludeTestController def test_render_without_include get :render_without_include response = JSON.parse(@response.body) expected = { 'author' => { 'id' => 1, 'name' => 'Steve K.', 'posts' => [ { 'id' => 42, 'title' => 'New Post', 'body' => 'Body' } ], 'roles' => [], 'bio' => {} } } assert_equal(expected, response) end def test_render_resource_with_include_hash get :render_resource_with_include_hash response = JSON.parse(@response.body) assert_equal(expected_include_response, response) end def test_render_resource_with_include_string get :render_resource_with_include_string response = JSON.parse(@response.body) assert_equal(expected_include_response, response) end def test_render_resource_with_deep_include get :render_resource_with_deep_include response = JSON.parse(@response.body) assert_equal(expected_deep_include_response, response) end def test_render_with_empty_default_includes with_default_includes '' do get :render_without_include response = JSON.parse(@response.body) expected = { 'author' => { 'id' => 1, 'name' => 'Steve K.' } } assert_equal(expected, response) end end def test_render_with_recursive_default_includes with_default_includes '**' do get :render_without_recursive_relationships response = JSON.parse(@response.body) expected = { 'id' => 11, 'name' => 'Jane Doe', 'roles' => nil, 'bio' => nil, 'posts' => [ { 'id' => 12, 'title' => 'Hello World', 'body' => 'My first post', 'comments' => [ { 'id' => 13, 'body' => 'Commentary', 'post' => nil, # not set to avoid infinite recursion 'author' => nil, # not set to avoid infinite recursion } ], 'blog' => { 'id' => 999, 'name' => 'Custom blog', 'writer' => nil, 'articles' => nil }, 'author' => nil # not set to avoid infinite recursion } ] } assert_equal(expected, response) end end def test_render_with_includes_overrides_default_includes with_default_includes '' do get :render_resource_with_include_hash response = JSON.parse(@response.body) assert_equal(expected_include_response, response) end end private def expected_include_response { 'author' => { 'id' => 1, 'name' => 'Steve K.', 'posts' => [ { 'id' => 42, 'title' => 'New Post', 'body' => 'Body', 'comments' => [ { 'id' => 1, 'body' => 'ZOMG A COMMENT' }, { 'id' => 2, 'body' => 'ZOMG ANOTHER COMMENT' } ] } ] } } end def expected_deep_include_response { 'author' => { 'id' => 1, 'name' => 'Steve K.', 'posts' => [ { 'id' => 42, 'title' => 'New Post', 'body' => 'Body', 'comments' => [ { 'id' => 1, 'body' => 'ZOMG A COMMENT', 'author' => { 'id' => 1, 'name' => 'Steve K.' } }, { 'id' => 2, 'body' => 'ZOMG ANOTHER COMMENT', 'author' => { 'id' => 1, 'name' => 'Steve K.' } } ] } ] } } end def with_default_includes(include_directive) original = ActiveModelSerializers.config.default_includes ActiveModelSerializers.config.default_includes = include_directive clear_include_directive_cache yield ensure ActiveModelSerializers.config.default_includes = original clear_include_directive_cache end def clear_include_directive_cache ActiveModelSerializers .instance_variable_set(:@default_include_directive, nil) end end end end end active_model_serializers-0.10.10/test/action_controller/json_api/000077500000000000000000000000001351232231100252125ustar00rootroot00000000000000active_model_serializers-0.10.10/test/action_controller/json_api/deserialization_test.rb000066400000000000000000000065271351232231100317760ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActionController module Serialization class JsonApi class DeserializationTest < ActionController::TestCase class DeserializationTestController < ActionController::Base def render_parsed_payload parsed_hash = ActiveModelSerializers::Deserialization.jsonapi_parse(params) render json: parsed_hash end def render_polymorphic_parsed_payload parsed_hash = ActiveModelSerializers::Deserialization.jsonapi_parse( params, polymorphic: [:restriction_for, :restricted_to] ) render json: parsed_hash end end tests DeserializationTestController def test_deserialization_of_relationship_only_object hash = { 'data' => { 'type' => 'restraints', 'relationships' => { 'restriction_for' => { 'data' => { 'type' => 'discounts', 'id' => '67' } }, 'restricted_to' => { 'data' => nil } } }, 'restraint' => {} } post :render_polymorphic_parsed_payload, params: hash response = JSON.parse(@response.body) expected = { 'restriction_for_id' => '67', 'restriction_for_type' => 'Discount', 'restricted_to_id' => nil, 'restricted_to_type' => nil } assert_equal(expected, response) end def test_deserialization hash = { 'data' => { 'type' => 'photos', 'id' => 'zorglub', 'attributes' => { 'title' => 'Ember Hamster', 'src' => 'http://example.com/images/productivity.png', 'image-width' => '200', 'imageHeight' => '200', 'ImageSize' => '1024' }, 'relationships' => { 'author' => { 'data' => nil }, 'photographer' => { 'data' => { 'type' => 'people', 'id' => '9' } }, 'comments' => { 'data' => [ { 'type' => 'comments', 'id' => '1' }, { 'type' => 'comments', 'id' => '2' } ] }, 'related-images' => { 'data' => [ { 'type' => 'image', 'id' => '7' }, { 'type' => 'image', 'id' => '8' } ] } } } } post :render_parsed_payload, params: hash response = JSON.parse(@response.body) expected = { 'id' => 'zorglub', 'title' => 'Ember Hamster', 'src' => 'http://example.com/images/productivity.png', 'image_width' => '200', 'image_height' => '200', 'image_size' => '1024', 'author_id' => nil, 'photographer_id' => '9', 'comment_ids' => %w(1 2), 'related_image_ids' => %w(7 8) } assert_equal(expected, response) end end end end end active_model_serializers-0.10.10/test/action_controller/json_api/errors_test.rb000066400000000000000000000026511351232231100301160ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActionController module Serialization class JsonApi class ErrorsTest < ActionController::TestCase def test_active_model_with_multiple_errors get :render_resource_with_errors expected_errors_object = { errors: [ { source: { pointer: '/data/attributes/name' }, detail: 'cannot be nil' }, { source: { pointer: '/data/attributes/name' }, detail: 'must be longer' }, { source: { pointer: '/data/attributes/id' }, detail: 'must be a uuid' } ] }.to_json assert_equal json_response_body.to_json, expected_errors_object end def json_response_body JSON.load(@response.body) end class ErrorsTestController < ActionController::Base def render_resource_with_errors resource = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') resource.errors.add(:name, 'cannot be nil') resource.errors.add(:name, 'must be longer') resource.errors.add(:id, 'must be a uuid') render json: resource, adapter: :json_api, serializer: ActiveModel::Serializer::ErrorSerializer end end tests ErrorsTestController end end end end active_model_serializers-0.10.10/test/action_controller/json_api/fields_test.rb000066400000000000000000000043171351232231100300510ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActionController module Serialization class JsonApi class FieldsTest < ActionController::TestCase class FieldsTestController < ActionController::Base class AuthorWithName < Author attributes :first_name, :last_name end class AuthorWithNameSerializer < AuthorSerializer type 'authors' end class PostWithPublishAt < Post attributes :publish_at end class PostWithPublishAtSerializer < ActiveModel::Serializer type 'posts' attributes :title, :body, :publish_at belongs_to :author has_many :comments end def setup_post ActionController::Base.cache_store.clear @author = AuthorWithName.new(id: 1, first_name: 'Bob', last_name: 'Jones') @comment1 = Comment.new(id: 7, body: 'cool', author: @author) @comment2 = Comment.new(id: 12, body: 'awesome', author: @author) @post = PostWithPublishAt.new(id: 1337, title: 'Title 1', body: 'Body 1', author: @author, comments: [@comment1, @comment2], publish_at: '2020-03-16T03:55:25.291Z') @comment1.post = @post @comment2.post = @post end def render_fields_works_on_relationships setup_post render json: @post, serializer: PostWithPublishAtSerializer, adapter: :json_api, fields: { posts: [:author] } end end tests FieldsTestController test 'fields works on relationships' do get :render_fields_works_on_relationships response = JSON.parse(@response.body) expected = { 'data' => { 'id' => '1337', 'type' => 'posts', 'relationships' => { 'author' => { 'data' => { 'id' => '1', 'type' => 'authors' } } } } } assert_equal expected, response end end end end end active_model_serializers-0.10.10/test/action_controller/json_api/linked_test.rb000066400000000000000000000162321351232231100300500ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActionController module Serialization class JsonApi class LinkedTest < ActionDispatch::IntegrationTest class LinkedTestController < ActionController::Base def setup_post ActionController::Base.cache_store.clear @role1 = Role.new(id: 1, name: 'admin') @role2 = Role.new(id: 2, name: 'colab') @author = Author.new(id: 1, name: 'Steve K.') @author.posts = [] @author.bio = nil @author.roles = [@role1, @role2] @role1.author = @author @role2.author = @author @author2 = Author.new(id: 2, name: 'Anonymous') @author2.posts = [] @author2.bio = nil @author2.roles = [] @post = Post.new(id: 1, title: 'New Post', body: 'Body') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment] @post.author = @author @first_comment.post = @post @first_comment.author = @author2 @second_comment.post = @post @second_comment.author = nil @post2 = Post.new(id: 2, title: 'Another Post', body: 'Body') @post2.author = @author @post2.comments = [] @blog = Blog.new(id: 1, name: 'My Blog!!') @post.blog = @blog @post2.blog = @blog end def render_resource_without_include setup_post render json: @post end def render_resource_with_include setup_post render json: @post, adapter: :json_api, include: [:author] end def render_resource_with_include_of_custom_key_by_original setup_post render json: @post, adapter: :json_api, include: [:reviews], serializer: PostWithCustomKeysSerializer end def render_resource_with_nested_include setup_post render json: @post, adapter: :json_api, include: [comments: [:author]] end def render_resource_with_nested_has_many_include_wildcard setup_post render json: @post, adapter: :json_api, include: 'author.*' end def render_resource_with_missing_nested_has_many_include setup_post @post.author = @author2 # author2 has no roles. render json: @post, adapter: :json_api, include: [author: [:roles]] end def render_collection_with_missing_nested_has_many_include setup_post @post.author = @author2 render json: [@post, @post2], adapter: :json_api, include: [author: [:roles]] end def render_collection_without_include setup_post render json: [@post], adapter: :json_api end def render_collection_with_include setup_post render json: [@post], adapter: :json_api, include: 'author,comments' end end setup do @routes = Rails.application.routes.draw do ActiveSupport::Deprecation.silence do match ':action', to: LinkedTestController, via: [:get, :post] end end end def test_render_resource_without_include get '/render_resource_without_include' response = JSON.parse(@response.body) refute response.key? 'included' end def test_render_resource_with_include get '/render_resource_with_include' response = JSON.parse(@response.body) assert response.key? 'included' assert_equal 1, response['included'].size assert_equal 'Steve K.', response['included'].first['attributes']['name'] end def test_render_resource_with_nested_has_many_include get '/render_resource_with_nested_has_many_include_wildcard' response = JSON.parse(@response.body) expected_linked = [ { 'id' => '1', 'type' => 'authors', 'attributes' => { 'name' => 'Steve K.' }, 'relationships' => { 'posts' => { 'data' => [] }, 'roles' => { 'data' => [{ 'type' => 'roles', 'id' => '1' }, { 'type' => 'roles', 'id' => '2' }] }, 'bio' => { 'data' => nil } } }, { 'id' => '1', 'type' => 'roles', 'attributes' => { 'name' => 'admin', 'description' => nil, 'slug' => 'admin-1' }, 'relationships' => { 'author' => { 'data' => { 'type' => 'authors', 'id' => '1' } } } }, { 'id' => '2', 'type' => 'roles', 'attributes' => { 'name' => 'colab', 'description' => nil, 'slug' => 'colab-2' }, 'relationships' => { 'author' => { 'data' => { 'type' => 'authors', 'id' => '1' } } } } ] assert_equal expected_linked, response['included'] end def test_render_resource_with_include_of_custom_key_by_original get '/render_resource_with_include_of_custom_key_by_original' response = JSON.parse(@response.body) assert response.key? 'included' relationships = response['data']['relationships'] assert_includes relationships, 'reviews' assert_includes relationships, 'writer' assert_includes relationships, 'site' end def test_render_resource_with_nested_include get '/render_resource_with_nested_include' response = JSON.parse(@response.body) assert response.key? 'included' assert_equal 3, response['included'].size end def test_render_collection_without_include get '/render_collection_without_include' response = JSON.parse(@response.body) refute response.key? 'included' end def test_render_collection_with_include get '/render_collection_with_include' response = JSON.parse(@response.body) assert response.key? 'included' end def test_render_resource_with_nested_attributes_even_when_missing_associations get '/render_resource_with_missing_nested_has_many_include' response = JSON.parse(@response.body) assert response.key? 'included' refute include_type?(response['included'], 'roles') end def test_render_collection_with_missing_nested_has_many_include get '/render_collection_with_missing_nested_has_many_include' response = JSON.parse(@response.body) assert response.key? 'included' assert include_type?(response['included'], 'roles') end def include_type?(collection, value) collection.detect { |i| i['type'] == value } end end end end end active_model_serializers-0.10.10/test/action_controller/json_api/pagination_test.rb000066400000000000000000000147261351232231100307410ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' require 'will_paginate/array' require 'kaminari' require 'kaminari/hooks' ::Kaminari::Hooks.init module ActionController module Serialization class JsonApi class PaginationTest < ActionController::TestCase KAMINARI_URI = 'http://test.host/action_controller/serialization/json_api/pagination_test/pagination_test/render_pagination_using_kaminari'.freeze WILL_PAGINATE_URI = 'http://test.host/action_controller/serialization/json_api/pagination_test/pagination_test/render_pagination_using_will_paginate'.freeze class PaginationTestController < ActionController::Base def setup @array = [ Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1'), Profile.new(name: 'Name 2', description: 'Description 2', comments: 'Comments 2'), Profile.new(name: 'Name 3', description: 'Description 3', comments: 'Comments 3') ] end def using_kaminari setup Kaminari.paginate_array(@array).page(params[:page][:number]).per(params[:page][:size]) end def using_will_paginate setup @array.paginate(page: params[:page][:number], per_page: params[:page][:size]) end def render_pagination_using_kaminari render json: using_kaminari, adapter: :json_api end def render_pagination_using_will_paginate render json: using_will_paginate, adapter: :json_api end def render_array_without_pagination_links setup render json: @array, adapter: :json_api end end tests PaginationTestController def test_render_pagination_links_with_will_paginate expected_links = { 'self' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1", 'first' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", 'prev' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", 'next' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1", 'last' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1" } get :render_pagination_using_will_paginate, params: { page: { number: 2, size: 1 } } response = JSON.parse(@response.body) assert_equal expected_links, response['links'] end def test_render_only_first_last_and_next_pagination_links expected_links = { 'self' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2", 'first' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2", 'prev' => nil, 'next' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2", 'last' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2" } get :render_pagination_using_will_paginate, params: { page: { number: 1, size: 2 } } response = JSON.parse(@response.body) assert_equal expected_links, response['links'] end def test_render_pagination_links_with_kaminari expected_links = { 'self' => "#{KAMINARI_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1", 'first' => "#{KAMINARI_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", 'prev' => "#{KAMINARI_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", 'next' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1", 'last' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1" } get :render_pagination_using_kaminari, params: { page: { number: 2, size: 1 } } response = JSON.parse(@response.body) assert_equal expected_links, response['links'] end def test_render_only_prev_first_and_last_pagination_links expected_links = { 'self' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1", 'first' => "#{KAMINARI_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1", 'prev' => "#{KAMINARI_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1", 'next' => nil, 'last' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1" } get :render_pagination_using_kaminari, params: { page: { number: 3, size: 1 } } response = JSON.parse(@response.body) assert_equal expected_links, response['links'] end def test_render_only_first_last_and_next_pagination_links_with_additional_params expected_links = { 'self' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2&teste=additional", 'first' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2&teste=additional", 'prev' => nil, 'next' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2&teste=additional", 'last' => "#{WILL_PAGINATE_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2&teste=additional" } get :render_pagination_using_will_paginate, params: { page: { number: 1, size: 2 }, teste: 'additional' } response = JSON.parse(@response.body) assert_equal expected_links, response['links'] end def test_render_only_prev_first_and_last_pagination_links_with_additional_params expected_links = { 'self' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1&teste=additional", 'first' => "#{KAMINARI_URI}?page%5Bnumber%5D=1&page%5Bsize%5D=1&teste=additional", 'prev' => "#{KAMINARI_URI}?page%5Bnumber%5D=2&page%5Bsize%5D=1&teste=additional", 'next' => nil, 'last' => "#{KAMINARI_URI}?page%5Bnumber%5D=3&page%5Bsize%5D=1&teste=additional" } get :render_pagination_using_kaminari, params: { page: { number: 3, size: 1 }, teste: 'additional' } response = JSON.parse(@response.body) assert_equal expected_links, response['links'] end def test_array_without_pagination_links get :render_array_without_pagination_links, params: { page: { number: 2, size: 1 } } response = JSON.parse(@response.body) refute response.key? 'links' end end end end end active_model_serializers-0.10.10/test/action_controller/json_api/transform_test.rb000066400000000000000000000143061351232231100306150ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActionController module Serialization class JsonApi class KeyTransformTest < ActionController::TestCase class KeyTransformTestController < ActionController::Base class Post < ::Model attributes :title, :body, :publish_at associations :author, :top_comments end class Author < ::Model attributes :first_name, :last_name end class TopComment < ::Model attributes :body associations :author, :post end class PostSerializer < ActiveModel::Serializer type 'posts' attributes :title, :body, :publish_at belongs_to :author has_many :top_comments link(:post_authors) { 'https://example.com/post_authors' } meta do { rating: 5, favorite_count: 10 } end end class AuthorSerializer < ActiveModel::Serializer type 'authors' attributes :first_name, :last_name end class TopCommentSerializer < ActiveModel::Serializer type 'top_comments' attributes :body belongs_to :author end def setup_post ActionController::Base.cache_store.clear @author = Author.new(id: 1, first_name: 'Bob', last_name: 'Jones') @comment1 = TopComment.new(id: 7, body: 'cool', author: @author) @comment2 = TopComment.new(id: 12, body: 'awesome', author: @author) @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', author: @author, top_comments: [@comment1, @comment2], publish_at: '2020-03-16T03:55:25.291Z') @comment1.post = @post @comment2.post = @post end def render_resource_with_transform setup_post render json: @post, serializer: PostSerializer, adapter: :json_api, key_transform: :camel end def render_resource_with_transform_nil setup_post render json: @post, serializer: PostSerializer, adapter: :json_api, key_transform: nil end def render_resource_with_transform_with_global_config old_transform = ActiveModelSerializers.config.key_transform setup_post ActiveModelSerializers.config.key_transform = :camel_lower render json: @post, serializer: PostSerializer, adapter: :json_api ensure ActiveModelSerializers.config.key_transform = old_transform end end tests KeyTransformTestController def test_render_resource_with_transform get :render_resource_with_transform response = JSON.parse(@response.body) expected = { 'Data' => { 'Id' => '1337', 'Type' => 'Posts', 'Attributes' => { 'Title' => 'Title 1', 'Body' => 'Body 1', 'PublishAt' => '2020-03-16T03:55:25.291Z' }, 'Relationships' => { 'Author' => { 'Data' => { 'Id' => '1', 'Type' => 'Authors' } }, 'TopComments' => { 'Data' => [ { 'Id' => '7', 'Type' => 'TopComments' }, { 'Id' => '12', 'Type' => 'TopComments' } ] } }, 'Links' => { 'PostAuthors' => 'https://example.com/post_authors' }, 'Meta' => { 'Rating' => 5, 'FavoriteCount' => 10 } } } assert_equal expected, response end def test_render_resource_with_transform_nil get :render_resource_with_transform_nil response = JSON.parse(@response.body) expected = { 'data' => { 'id' => '1337', 'type' => 'posts', 'attributes' => { 'title' => 'Title 1', 'body' => 'Body 1', 'publish-at' => '2020-03-16T03:55:25.291Z' }, 'relationships' => { 'author' => { 'data' => { 'id' => '1', 'type' => 'authors' } }, 'top-comments' => { 'data' => [ { 'id' => '7', 'type' => 'top-comments' }, { 'id' => '12', 'type' => 'top-comments' } ] } }, 'links' => { 'post-authors' => 'https://example.com/post_authors' }, 'meta' => { 'rating' => 5, 'favorite-count' => 10 } } } assert_equal expected, response end def test_render_resource_with_transform_with_global_config get :render_resource_with_transform_with_global_config response = JSON.parse(@response.body) expected = { 'data' => { 'id' => '1337', 'type' => 'posts', 'attributes' => { 'title' => 'Title 1', 'body' => 'Body 1', 'publishAt' => '2020-03-16T03:55:25.291Z' }, 'relationships' => { 'author' => { 'data' => { 'id' => '1', 'type' => 'authors' } }, 'topComments' => { 'data' => [ { 'id' => '7', 'type' => 'topComments' }, { 'id' => '12', 'type' => 'topComments' } ] } }, 'links' => { 'postAuthors' => 'https://example.com/post_authors' }, 'meta' => { 'rating' => 5, 'favoriteCount' => 10 } } } assert_equal expected, response end end end end end active_model_serializers-0.10.10/test/action_controller/lookup_proc_test.rb000066400000000000000000000026371351232231100273400ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActionController module Serialization class LookupProcTest < ActionController::TestCase module Api module V3 class PostCustomSerializer < ActiveModel::Serializer attributes :title, :body belongs_to :author end class AuthorCustomSerializer < ActiveModel::Serializer attributes :name end class LookupProcTestController < ActionController::Base def implicit_namespaced_serializer author = Author.new(name: 'Bob') post = Post.new(title: 'New Post', body: 'Body', author: author) render json: post end end end end tests Api::V3::LookupProcTestController test 'implicitly uses namespaced serializer' do controller_namespace = lambda do |resource_class, _parent_serializer_class, namespace| "#{namespace}::#{resource_class}CustomSerializer" if namespace end with_prepended_lookup(controller_namespace) do get :implicit_namespaced_serializer assert_serializer Api::V3::PostCustomSerializer expected = { 'title' => 'New Post', 'body' => 'Body', 'author' => { 'name' => 'Bob' } } actual = JSON.parse(@response.body) assert_equal expected, actual end end end end end active_model_serializers-0.10.10/test/action_controller/namespace_lookup_test.rb000066400000000000000000000156241351232231100303310ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActionController module Serialization class NamespaceLookupTest < ActionController::TestCase class Book < ::Model attributes :id, :title, :body associations :writer, :chapters end class Chapter < ::Model attributes :title end class Writer < ::Model attributes :name end module Api module V2 class BookSerializer < ActiveModel::Serializer attributes :title end end module VHeader class BookSerializer < ActiveModel::Serializer attributes :title, :body def body 'header' end end end module V3 class BookSerializer < ActiveModel::Serializer attributes :title, :body belongs_to :writer has_many :chapters end class ChapterSerializer < ActiveModel::Serializer attribute :title do "Chapter - #{object.title}" end end class WriterSerializer < ActiveModel::Serializer attributes :name end class LookupTestController < ActionController::Base before_action only: [:namespace_set_in_before_filter] do self.namespace_for_serializer = Api::V2 end def implicit_namespaced_serializer writer = Writer.new(name: 'Bob') book = Book.new(title: 'New Post', body: 'Body', writer: writer, chapters: []) render json: book end def implicit_namespaced_collection_serializer chapter1 = Chapter.new(title: 'Oh') chapter2 = Chapter.new(title: 'Oh my') render json: [chapter1, chapter2] end def implicit_has_many_namespaced_serializer chapter1 = Chapter.new(title: 'Odd World') chapter2 = Chapter.new(title: 'New World') book = Book.new(title: 'New Post', body: 'Body', chapters: [chapter1, chapter2]) render json: book end def explicit_namespace_as_module book = Book.new(title: 'New Post', body: 'Body') render json: book, namespace: Api::V2 end def explicit_namespace_as_string book = Book.new(title: 'New Post', body: 'Body') # because this is a string, ruby can't auto-lookup the constant, so otherwise # the lookup thinks we mean ::Api::V2 render json: book, namespace: 'ActionController::Serialization::NamespaceLookupTest::Api::V2' end def explicit_namespace_as_symbol book = Book.new(title: 'New Post', body: 'Body') # because this is a string, ruby can't auto-lookup the constant, so otherwise # the lookup thinks we mean ::Api::V2 render json: book, namespace: :'ActionController::Serialization::NamespaceLookupTest::Api::V2' end def invalid_namespace book = Book.new(id: 'invalid_namespace_book_id', title: 'New Post', body: 'Body') render json: book, namespace: :api_v2 end def namespace_set_in_before_filter book = Book.new(title: 'New Post', body: 'Body') render json: book end def namespace_set_by_request_headers book = Book.new(title: 'New Post', body: 'Body') version_from_header = request.headers['X-API_VERSION'] namespace = "ActionController::Serialization::NamespaceLookupTest::#{version_from_header}" render json: book, namespace: namespace end end end end tests Api::V3::LookupTestController setup do @test_namespace = if Module.method_defined?(:module_parent) self.class.module_parent else self.class.parent end end test 'uses request headers to determine the namespace' do request.env['X-API_VERSION'] = 'Api::VHeader' get :namespace_set_by_request_headers assert_serializer Api::VHeader::BookSerializer end test 'implicitly uses namespaced serializer' do get :implicit_namespaced_serializer assert_serializer Api::V3::BookSerializer expected = { 'title' => 'New Post', 'body' => 'Body', 'writer' => { 'name' => 'Bob' }, 'chapters' => [] } actual = JSON.parse(@response.body) assert_equal expected, actual end test 'implicitly uses namespaced serializer for collection' do get :implicit_namespaced_collection_serializer assert_serializer 'ActiveModel::Serializer::CollectionSerializer' expected = [{ 'title' => 'Chapter - Oh' }, { 'title' => 'Chapter - Oh my' }] actual = JSON.parse(@response.body) assert_equal expected, actual end test 'implicitly uses namespaced serializer for has_many' do get :implicit_has_many_namespaced_serializer assert_serializer Api::V3::BookSerializer expected = { 'title' => 'New Post', 'body' => 'Body', 'writer' => nil, 'chapters' => [ { 'title' => 'Chapter - Odd World' }, { 'title' => 'Chapter - New World' } ] } actual = JSON.parse(@response.body) assert_equal expected, actual end test 'explicit namespace as module' do get :explicit_namespace_as_module assert_serializer Api::V2::BookSerializer expected = { 'title' => 'New Post' } actual = JSON.parse(@response.body) assert_equal expected, actual end test 'explicit namespace as string' do get :explicit_namespace_as_string assert_serializer Api::V2::BookSerializer expected = { 'title' => 'New Post' } actual = JSON.parse(@response.body) assert_equal expected, actual end test 'explicit namespace as symbol' do get :explicit_namespace_as_symbol assert_serializer Api::V2::BookSerializer expected = { 'title' => 'New Post' } actual = JSON.parse(@response.body) assert_equal expected, actual end test 'invalid namespace' do get :invalid_namespace assert_serializer ActiveModel::Serializer::Null expected = { 'id' => 'invalid_namespace_book_id', 'title' => 'New Post', 'body' => 'Body' } actual = JSON.parse(@response.body) assert_equal expected, actual end test 'namespace set in before filter' do get :namespace_set_in_before_filter assert_serializer Api::V2::BookSerializer expected = { 'title' => 'New Post' } actual = JSON.parse(@response.body) assert_equal expected, actual end end end end active_model_serializers-0.10.10/test/action_controller/serialization_scope_name_test.rb000066400000000000000000000144051351232231100320460ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module SerializationScopeTesting class User < ActiveModelSerializers::Model attributes :id, :name, :admin def admin? admin end end class Comment < ActiveModelSerializers::Model attributes :id, :body end class Post < ActiveModelSerializers::Model attributes :id, :title, :body, :comments end class PostSerializer < ActiveModel::Serializer attributes :id, :title, :body, :comments def body "The 'scope' is the 'current_user': #{scope == current_user}" end def comments if current_user.admin? [Comment.new(id: 1, body: 'Admin')] else [Comment.new(id: 2, body: 'Scoped')] end end def json_key 'post' end end class PostTestController < ActionController::Base attr_writer :current_user def render_post_by_non_admin self.current_user = User.new(id: 3, name: 'Pete', admin: false) render json: new_post, serializer: serializer, adapter: :json end def render_post_by_admin self.current_user = User.new(id: 3, name: 'Pete', admin: true) render json: new_post, serializer: serializer, adapter: :json end def current_user defined?(@current_user) ? @current_user : :current_user_not_set end private def new_post Post.new(id: 4, title: 'Title') end def serializer PostSerializer end end class PostViewContextSerializer < PostSerializer def body "The 'scope' is the 'view_context': #{scope == view_context}" end def comments if view_context.controller.current_user.admin? [Comment.new(id: 1, body: 'Admin')] else [Comment.new(id: 2, body: 'Scoped')] end end end class DefaultScopeTest < ActionController::TestCase tests PostTestController def test_default_serialization_scope assert_equal :current_user, @controller._serialization_scope end def test_default_serialization_scope_object assert_equal :current_user_not_set, @controller.current_user assert_equal :current_user_not_set, @controller.serialization_scope end def test_default_scope_non_admin get :render_post_by_non_admin expected_json = { post: { id: 4, title: 'Title', body: "The 'scope' is the 'current_user': true", comments: [ { id: 2, body: 'Scoped' } ] } }.to_json assert_equal expected_json, @response.body end def test_default_scope_admin get :render_post_by_admin expected_json = { post: { id: 4, title: 'Title', body: "The 'scope' is the 'current_user': true", comments: [ { id: 1, body: 'Admin' } ] } }.to_json assert_equal expected_json, @response.body end end class SerializationScopeTest < ActionController::TestCase class PostViewContextTestController < PostTestController serialization_scope :view_context private def serializer PostViewContextSerializer end end tests PostViewContextTestController def test_defined_serialization_scope assert_equal :view_context, @controller._serialization_scope end def test_defined_serialization_scope_object assert_equal @controller.view_context.controller, @controller.serialization_scope.controller end def test_serialization_scope_non_admin get :render_post_by_non_admin expected_json = { post: { id: 4, title: 'Title', body: "The 'scope' is the 'view_context': true", comments: [ { id: 2, body: 'Scoped' } ] } }.to_json assert_equal expected_json, @response.body end def test_serialization_scope_admin get :render_post_by_admin expected_json = { post: { id: 4, title: 'Title', body: "The 'scope' is the 'view_context': true", comments: [ { id: 1, body: 'Admin' } ] } }.to_json assert_equal expected_json, @response.body end end class NilSerializationScopeTest < ActionController::TestCase class PostViewContextTestController < ActionController::Base serialization_scope nil attr_accessor :current_user def render_post_with_no_scope self.current_user = User.new(id: 3, name: 'Pete', admin: false) render json: new_post, serializer: PostSerializer, adapter: :json end def render_post_with_passed_in_scope self.current_user = User.new(id: 3, name: 'Pete', admin: false) render json: new_post, serializer: PostSerializer, adapter: :json, scope: current_user, scope_name: :current_user end def render_post_with_passed_in_scope_without_scope_name self.current_user = User.new(id: 3, name: 'Pete', admin: false) render json: new_post, serializer: PostSerializer, adapter: :json, scope: current_user end private def new_post Post.new(id: 4, title: 'Title') end end tests PostViewContextTestController def test_nil_serialization_scope assert_nil @controller._serialization_scope end def test_nil_serialization_scope_object assert_nil @controller.serialization_scope end def test_nil_scope exception_matcher = /current_user/ exception = assert_raises(NameError) do get :render_post_with_no_scope end assert_match exception_matcher, exception.message end def test_serialization_scope_is_and_nil_scope_passed_in_current_user get :render_post_with_passed_in_scope expected_json = { post: { id: 4, title: 'Title', body: "The 'scope' is the 'current_user': true", comments: [ { id: 2, body: 'Scoped' } ] } }.to_json assert_equal expected_json, @response.body end def test_serialization_scope_is_nil_and_scope_passed_in_current_user_without_scope_name exception_matcher = /current_user/ exception = assert_raises(NameError) do get :render_post_with_passed_in_scope_without_scope_name end assert_match exception_matcher, exception.message end end end active_model_serializers-0.10.10/test/action_controller/serialization_test.rb000066400000000000000000000354131351232231100276570ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActionController module Serialization class ImplicitSerializerTest < ActionController::TestCase class ImplicitSerializationTestController < ActionController::Base include SerializationTesting def render_using_implicit_serializer @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') render json: @profile end def render_using_default_adapter_root @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') render json: @profile end def render_array_using_custom_root @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') render json: [@profile], root: 'custom_root' end def render_array_that_is_empty_using_custom_root render json: [], root: 'custom_root' end def render_object_using_custom_root @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') render json: @profile, root: 'custom_root' end def render_array_using_implicit_serializer array = [ Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1'), Profile.new(name: 'Name 2', description: 'Description 2', comments: 'Comments 2') ] render json: array end def render_array_using_implicit_serializer_and_meta @profiles = [ Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') ] render json: @profiles, meta: { total: 10 } end def render_array_using_implicit_serializer_and_links with_adapter ActiveModelSerializers::Adapter::JsonApi do @profiles = [ Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') ] render json: @profiles, links: { self: 'http://example.com/api/profiles/1' } end end def render_object_with_cache_enabled @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @author = Author.new(id: 1, name: 'Joao Moura.') @post = Post.new(id: 1, title: 'New Post', body: 'Body', comments: [@comment], author: @author) generate_cached_serializer(@post) @post.title = 'ZOMG a New Post' render json: @post end def render_json_object_without_serializer render json: { error: 'Result is Invalid' } end def render_json_array_object_without_serializer render json: [{ error: 'Result is Invalid' }] end def update_and_render_object_with_cache_enabled @post.updated_at = Time.zone.now generate_cached_serializer(@post) render json: @post end def render_object_expired_with_cache_enabled comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') author = Author.new(id: 1, name: 'Joao Moura.') post = Post.new(id: 1, title: 'New Post', body: 'Body', comments: [comment], author: author) generate_cached_serializer(post) post.title = 'ZOMG a New Post' expires_in = [ PostSerializer._cache_options[:expires_in], CommentSerializer._cache_options[:expires_in] ].max + 200 Timecop.travel(Time.zone.now + expires_in) do render json: post end end def render_changed_object_with_cache_enabled comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') author = Author.new(id: 1, name: 'Joao Moura.') post = Post.new(id: 1, title: 'ZOMG a New Post', body: 'Body', comments: [comment], author: author) render json: post end def render_fragment_changed_object_with_only_cache_enabled author = Author.new(id: 1, name: 'Joao Moura.') role = Role.new(id: 42, name: 'ZOMG A ROLE', description: 'DESCRIPTION HERE', author: author) generate_cached_serializer(role) role.name = 'lol' role.description = 'HUEHUEBRBR' render json: role end def render_fragment_changed_object_with_except_cache_enabled author = Author.new(id: 1, name: 'Joao Moura.') bio = Bio.new(id: 42, content: 'ZOMG A ROLE', rating: 5, author: author) generate_cached_serializer(bio) bio.content = 'lol' bio.rating = 0 render json: bio end def render_fragment_changed_object_with_relationship comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') comment2 = Comment.new(id: 1, body: 'ZOMG AN UPDATED-BUT-NOT-CACHE-EXPIRED COMMENT') like = Like.new(id: 1, likeable: comment, time: 3.days.ago) generate_cached_serializer(like) like.likeable = comment2 like.time = Time.zone.now.to_s render json: like end end tests ImplicitSerializationTestController # We just have Null for now, this will change def test_render_using_implicit_serializer get :render_using_implicit_serializer expected = { name: 'Name 1', description: 'Description 1' } assert_equal 'application/json', @response.content_type assert_equal expected.to_json, @response.body end def test_render_using_default_root with_adapter :json_api do get :render_using_default_adapter_root end expected = { data: { id: @controller.instance_variable_get(:@profile).id.to_s, type: 'profiles', attributes: { name: 'Name 1', description: 'Description 1' } } } assert_equal 'application/json', @response.content_type assert_equal expected.to_json, @response.body end def test_render_array_using_custom_root with_adapter :json do get :render_array_using_custom_root end expected = { custom_root: [{ name: 'Name 1', description: 'Description 1' }] } assert_equal 'application/json', @response.content_type assert_equal expected.to_json, @response.body end def test_render_array_that_is_empty_using_custom_root with_adapter :json do get :render_array_that_is_empty_using_custom_root end expected = { custom_root: [] } assert_equal 'application/json', @response.content_type assert_equal expected.to_json, @response.body end def test_render_object_using_custom_root with_adapter :json do get :render_object_using_custom_root end expected = { custom_root: { name: 'Name 1', description: 'Description 1' } } assert_equal 'application/json', @response.content_type assert_equal expected.to_json, @response.body end def test_render_json_object_without_serializer get :render_json_object_without_serializer assert_equal 'application/json', @response.content_type expected_body = { error: 'Result is Invalid' } assert_equal expected_body.to_json, @response.body end def test_render_json_array_object_without_serializer get :render_json_array_object_without_serializer assert_equal 'application/json', @response.content_type expected_body = [{ error: 'Result is Invalid' }] assert_equal expected_body.to_json, @response.body end def test_render_array_using_implicit_serializer get :render_array_using_implicit_serializer assert_equal 'application/json', @response.content_type expected = [ { name: 'Name 1', description: 'Description 1' }, { name: 'Name 2', description: 'Description 2' } ] assert_equal expected.to_json, @response.body end def test_render_array_using_implicit_serializer_and_meta with_adapter :json_api do get :render_array_using_implicit_serializer_and_meta end expected = { data: [ { id: @controller.instance_variable_get(:@profiles).first.id.to_s, type: 'profiles', attributes: { name: 'Name 1', description: 'Description 1' } } ], meta: { total: 10 } } assert_equal 'application/json', @response.content_type assert_equal expected.to_json, @response.body end def test_render_array_using_implicit_serializer_and_links get :render_array_using_implicit_serializer_and_links expected = { data: [ { id: @controller.instance_variable_get(:@profiles).first.id.to_s, type: 'profiles', attributes: { name: 'Name 1', description: 'Description 1' } } ], links: { self: 'http://example.com/api/profiles/1' } } assert_equal 'application/json', @response.content_type assert_equal expected.to_json, @response.body end def test_render_with_cache_enable expected = { id: 1, title: 'New Post', body: 'Body', comments: [ { id: 1, body: 'ZOMG A COMMENT' } ], blog: { id: 999, name: 'Custom blog' }, author: { id: 1, name: 'Joao Moura.' } } ActionController::Base.cache_store.clear Timecop.freeze(Time.zone.now) do get :render_object_with_cache_enabled assert_equal 'application/json', @response.content_type assert_equal expected.to_json, @response.body get :render_changed_object_with_cache_enabled assert_equal expected.to_json, @response.body end ActionController::Base.cache_store.clear get :render_changed_object_with_cache_enabled assert_not_equal expected.to_json, @response.body end def test_render_with_cache_enable_and_expired ActionController::Base.cache_store.clear get :render_object_expired_with_cache_enabled expected = { id: 1, title: 'ZOMG a New Post', body: 'Body', comments: [ { id: 1, body: 'ZOMG A COMMENT' } ], blog: { id: 999, name: 'Custom blog' }, author: { id: 1, name: 'Joao Moura.' } } assert_equal 'application/json', @response.content_type actual = @response.body expected = expected.to_json if ENV['APPVEYOR'] && actual != expected skip('Cache expiration tests sometimes fail on Appveyor. FIXME :)') else assert_equal actual, expected end end def test_render_with_fragment_only_cache_enable ActionController::Base.cache_store.clear get :render_fragment_changed_object_with_only_cache_enabled response = JSON.parse(@response.body) assert_equal 'application/json', @response.content_type assert_equal 'ZOMG A ROLE', response['name'] assert_equal 'HUEHUEBRBR', response['description'] end def test_render_with_fragment_except_cache_enable ActionController::Base.cache_store.clear get :render_fragment_changed_object_with_except_cache_enabled response = JSON.parse(@response.body) assert_equal 'application/json', @response.content_type assert_equal 5, response['rating'] assert_equal 'lol', response['content'] end def test_render_fragment_changed_object_with_relationship ActionController::Base.cache_store.clear Timecop.freeze(Time.zone.now) do get :render_fragment_changed_object_with_relationship response = JSON.parse(@response.body) expected_return = { 'id' => 1, 'time' => Time.zone.now.to_s, 'likeable' => { 'id' => 1, 'body' => 'ZOMG A COMMENT' } } assert_equal 'application/json', @response.content_type assert_equal expected_return, response end end def test_cache_expiration_on_update ActionController::Base.cache_store.clear get :render_object_with_cache_enabled expected = { id: 1, title: 'ZOMG a New Post', body: 'Body', comments: [ { id: 1, body: 'ZOMG A COMMENT' } ], blog: { id: 999, name: 'Custom blog' }, author: { id: 1, name: 'Joao Moura.' } } get :update_and_render_object_with_cache_enabled assert_equal 'application/json', @response.content_type actual = @response.body expected = expected.to_json if ENV['APPVEYOR'] && actual != expected skip('Cache expiration tests sometimes fail on Appveyor. FIXME :)') else assert_equal actual, expected end end def test_warn_overridding_use_adapter_as_falsy_on_controller_instance controller = Class.new(ImplicitSerializationTestController) do def use_adapter? false end end.new assert_output(nil, /adapter: false/) do controller.get_serializer(Profile.new) end end def test_dont_warn_overridding_use_adapter_as_truthy_on_controller_instance controller = Class.new(ImplicitSerializationTestController) do def use_adapter? true end end.new assert_output(nil, '') do controller.get_serializer(Profile.new) end end def test_render_event_is_emitted subscriber = ::ActiveSupport::Notifications.subscribe('render.active_model_serializers') do |subscribed_event| @subscribed_event = subscribed_event end get :render_using_implicit_serializer subscribed_event_name = if @subscribed_event.is_a?(String) @subscribed_event else @subscribed_event.name # is a ActiveSupport::Notifications::Event end assert_equal 'render.active_model_serializers', subscribed_event_name ensure ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber end end end end active_model_serializers-0.10.10/test/active_model_serializers/000077500000000000000000000000001351232231100247375ustar00rootroot00000000000000active_model_serializers-0.10.10/test/active_model_serializers/adapter_for_test.rb000066400000000000000000000172561351232231100306240ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers class AdapterForTest < ::ActiveSupport::TestCase UnknownAdapterError = ::ActiveModelSerializers::Adapter::UnknownAdapterError def test_serializer_adapter_returns_configured_adapter assert_output(nil, /ActiveModelSerializers::Adapter.configured_adapter/) do assert_equal ActiveModelSerializers::Adapter.configured_adapter, ActiveModel::Serializer.adapter end end def test_returns_default_adapter with_adapter_config_setup do adapter = ActiveModelSerializers::Adapter.configured_adapter assert_equal ActiveModelSerializers::Adapter::Attributes, adapter end end def test_overwrite_adapter_with_symbol with_adapter_config_setup do ActiveModelSerializers.config.adapter = :null adapter = ActiveModelSerializers::Adapter.configured_adapter assert_equal ActiveModelSerializers::Adapter::Null, adapter end end def test_overwrite_adapter_with_camelcased_symbol with_adapter_config_setup do ActiveModelSerializers.config.adapter = :JsonApi adapter = ActiveModelSerializers::Adapter.configured_adapter assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter end end def test_overwrite_adapter_with_string with_adapter_config_setup do ActiveModelSerializers.config.adapter = 'json_api' adapter = ActiveModelSerializers::Adapter.configured_adapter assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter end end def test_overwrite_adapter_with_a_camelcased_string with_adapter_config_setup do ActiveModelSerializers.config.adapter = 'JsonApi' adapter = ActiveModelSerializers::Adapter.configured_adapter assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter end end def test_overwrite_adapter_with_class with_adapter_config_setup do ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::Null adapter = ActiveModelSerializers::Adapter.configured_adapter assert_equal ActiveModelSerializers::Adapter::Null, adapter end end def test_raises_exception_if_invalid_symbol_given with_adapter_config_setup do ActiveModelSerializers.config.adapter = :unknown assert_raises UnknownAdapterError do ActiveModelSerializers::Adapter.configured_adapter end end end def test_raises_exception_if_it_does_not_know_hot_to_infer_adapter with_adapter_config_setup do ActiveModelSerializers.config.adapter = 42 assert_raises UnknownAdapterError do ActiveModelSerializers::Adapter.configured_adapter end end end def test_adapter_class_for_known_adapter klass = ActiveModelSerializers::Adapter.adapter_class(:json_api) assert_equal ActiveModelSerializers::Adapter::JsonApi, klass end def test_adapter_class_for_unknown_adapter assert_raises UnknownAdapterError do ActiveModelSerializers::Adapter.adapter_class(:json_simple) end end def test_adapter_map expected_adapter_map = { 'null'.freeze => ActiveModelSerializers::Adapter::Null, 'json'.freeze => ActiveModelSerializers::Adapter::Json, 'attributes'.freeze => ActiveModelSerializers::Adapter::Attributes, 'json_api'.freeze => ActiveModelSerializers::Adapter::JsonApi } actual = ActiveModelSerializers::Adapter.adapter_map assert_equal actual, expected_adapter_map end def test_adapters assert_equal ActiveModelSerializers::Adapter.adapters.sort, [ 'attributes'.freeze, 'json'.freeze, 'json_api'.freeze, 'null'.freeze ] end def test_lookup_adapter_by_string_name assert_equal ActiveModelSerializers::Adapter.lookup('json'.freeze), ActiveModelSerializers::Adapter::Json end def test_lookup_adapter_by_symbol_name assert_equal ActiveModelSerializers::Adapter.lookup(:json), ActiveModelSerializers::Adapter::Json end def test_lookup_adapter_by_class klass = ActiveModelSerializers::Adapter::Json assert_equal ActiveModelSerializers::Adapter.lookup(klass), klass end def test_lookup_adapter_from_environment_registers_adapter ActiveModelSerializers::Adapter.const_set(:AdapterFromEnvironment, Class.new) klass = ::ActiveModelSerializers::Adapter::AdapterFromEnvironment name = 'adapter_from_environment'.freeze assert_equal ActiveModelSerializers::Adapter.lookup(name), klass assert ActiveModelSerializers::Adapter.adapters.include?(name) ensure ActiveModelSerializers::Adapter.adapter_map.delete(name) ActiveModelSerializers::Adapter.send(:remove_const, :AdapterFromEnvironment) end def test_lookup_adapter_for_unknown_name assert_raises UnknownAdapterError do ActiveModelSerializers::Adapter.lookup(:json_simple) end end def test_adapter assert_equal ActiveModelSerializers.config.adapter, :attributes assert_equal ActiveModelSerializers::Adapter.configured_adapter, ActiveModelSerializers::Adapter::Attributes end def test_register_adapter new_adapter_name = :foo new_adapter_klass = Class.new ActiveModelSerializers::Adapter.register(new_adapter_name, new_adapter_klass) assert ActiveModelSerializers::Adapter.adapters.include?('foo'.freeze) assert ActiveModelSerializers::Adapter.lookup(:foo), new_adapter_klass ensure ActiveModelSerializers::Adapter.adapter_map.delete(new_adapter_name.to_s) end def test_inherited_adapter_hooks_register_adapter Object.const_set(:MyAdapter, Class.new) my_adapter = MyAdapter ActiveModelSerializers::Adapter::Base.inherited(my_adapter) assert_equal ActiveModelSerializers::Adapter.lookup(:my_adapter), my_adapter ensure ActiveModelSerializers::Adapter.adapter_map.delete('my_adapter'.freeze) Object.send(:remove_const, :MyAdapter) end def test_inherited_adapter_hooks_register_namespaced_adapter Object.const_set(:MyNamespace, Module.new) MyNamespace.const_set(:MyAdapter, Class.new) my_adapter = MyNamespace::MyAdapter ActiveModelSerializers::Adapter::Base.inherited(my_adapter) assert_equal ActiveModelSerializers::Adapter.lookup(:'my_namespace/my_adapter'), my_adapter ensure ActiveModelSerializers::Adapter.adapter_map.delete('my_namespace/my_adapter'.freeze) MyNamespace.send(:remove_const, :MyAdapter) Object.send(:remove_const, :MyNamespace) end def test_inherited_adapter_hooks_register_subclass_of_registered_adapter Object.const_set(:MyAdapter, Class.new) my_adapter = MyAdapter Object.const_set(:MySubclassedAdapter, Class.new(MyAdapter)) my_subclassed_adapter = MySubclassedAdapter ActiveModelSerializers::Adapter::Base.inherited(my_adapter) ActiveModelSerializers::Adapter::Base.inherited(my_subclassed_adapter) assert_equal ActiveModelSerializers::Adapter.lookup(:my_adapter), my_adapter assert_equal ActiveModelSerializers::Adapter.lookup(:my_subclassed_adapter), my_subclassed_adapter ensure ActiveModelSerializers::Adapter.adapter_map.delete('my_adapter'.freeze) ActiveModelSerializers::Adapter.adapter_map.delete('my_subclassed_adapter'.freeze) Object.send(:remove_const, :MyAdapter) Object.send(:remove_const, :MySubclassedAdapter) end private def with_adapter_config_setup previous_adapter = ActiveModelSerializers.config.adapter yield ensure ActiveModelSerializers.config.adapter = previous_adapter end end end active_model_serializers-0.10.10/test/active_model_serializers/json_pointer_test.rb000066400000000000000000000012061351232231100310330ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers class JsonPointerTest < ActiveSupport::TestCase def test_attribute_pointer attribute_name = 'title' pointer = ActiveModelSerializers::JsonPointer.new(:attribute, attribute_name) assert_equal '/data/attributes/title', pointer end def test_primary_data_pointer pointer = ActiveModelSerializers::JsonPointer.new(:primary_data) assert_equal '/data', pointer end def test_unknown_data_pointer assert_raises(TypeError) do ActiveModelSerializers::JsonPointer.new(:unknown) end end end end active_model_serializers-0.10.10/test/active_model_serializers/logging_test.rb000066400000000000000000000045151351232231100277560ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class LoggingTest < ActiveSupport::TestCase class TestLogger < ActiveSupport::Logger def initialize @file = StringIO.new super(@file) end def messages @file.rewind @file.read end end def setup @author = Author.new(name: 'Steve K.') @post = Post.new(title: 'New Post', body: 'Body') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @post.comments = [@comment] @comment.post = @post @post.author = @author @author.posts = [@post] @post_serializer = PostSerializer.new(@post, custom_options: true) @old_logger = ActiveModelSerializers.logger @logger = ActiveSupport::TaggedLogging.new(TestLogger.new) logger @logger end def teardown logger @old_logger end def logger(logger) ActiveModelSerializers.logger = logger end def test_uses_ams_as_tag ActiveModelSerializers::SerializableResource.new(@post).serializable_hash assert_match(/\[active_model_serializers\]/, @logger.messages) end def test_logs_when_call_serializable_hash ActiveModelSerializers::SerializableResource.new(@post).serializable_hash assert_match(/Rendered/, @logger.messages) end def test_logs_when_call_as_json ActiveModelSerializers::SerializableResource.new(@post).as_json assert_match(/Rendered/, @logger.messages) end def test_logs_when_call_to_json ActiveModelSerializers::SerializableResource.new(@post).to_json assert_match(/Rendered/, @logger.messages) end def test_logs_correct_serializer ActiveModelSerializers::SerializableResource.new(@post).serializable_hash assert_match(/PostSerializer/, @logger.messages) end def test_logs_correct_adapter ActiveModelSerializers::SerializableResource.new(@post).serializable_hash assert_match(/ActiveModelSerializers::Adapter::Attributes/, @logger.messages) end def test_logs_the_duration ActiveModelSerializers::SerializableResource.new(@post).serializable_hash assert_match(/\(\d+\.\d+ms\)/, @logger.messages) end end end end active_model_serializers-0.10.10/test/active_model_serializers/model_test.rb000066400000000000000000000130241351232231100274230ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers class ModelTest < ActiveSupport::TestCase include ActiveModel::Serializer::Lint::Tests setup do @resource = ActiveModelSerializers::Model.new end def test_initialization_with_string_keys klass = Class.new(ActiveModelSerializers::Model) do attributes :key end value = 'value' model_instance = klass.new('key' => value) assert_equal model_instance.read_attribute_for_serialization(:key), value end def test_attributes_can_be_read_for_serialization klass = Class.new(ActiveModelSerializers::Model) do attributes :one, :two, :three end original_attributes = { one: 1, two: 2, three: 3 } original_instance = klass.new(original_attributes) # Initial value instance = original_instance expected_attributes = { one: 1, two: 2, three: 3 }.with_indifferent_access assert_equal expected_attributes, instance.attributes assert_equal 1, instance.one assert_equal 1, instance.read_attribute_for_serialization(:one) # FIXME: Change via accessor has no effect on attributes. instance = original_instance.dup instance.one = :not_one assert_equal expected_attributes, instance.attributes assert_equal :not_one, instance.one assert_equal :not_one, instance.read_attribute_for_serialization(:one) # FIXME: Change via mutating attributes instance = original_instance.dup instance.attributes[:one] = :not_one expected_attributes = { one: :not_one, two: 2, three: 3 }.with_indifferent_access assert_equal expected_attributes, instance.attributes assert_equal 1, instance.one assert_equal 1, instance.read_attribute_for_serialization(:one) end def test_attributes_can_be_read_for_serialization_with_attributes_accessors_fix klass = Class.new(ActiveModelSerializers::Model) do derive_attributes_from_names_and_fix_accessors attributes :one, :two, :three end original_attributes = { one: 1, two: 2, three: 3 } original_instance = klass.new(original_attributes) # Initial value instance = original_instance expected_attributes = { one: 1, two: 2, three: 3 }.with_indifferent_access assert_equal expected_attributes, instance.attributes assert_equal 1, instance.one assert_equal 1, instance.read_attribute_for_serialization(:one) expected_attributes = { one: :not_one, two: 2, three: 3 }.with_indifferent_access # Change via accessor instance = original_instance.dup instance.one = :not_one assert_equal expected_attributes, instance.attributes assert_equal :not_one, instance.one assert_equal :not_one, instance.read_attribute_for_serialization(:one) # Attributes frozen assert instance.attributes.frozen? end def test_id_attribute_can_be_read_for_serialization klass = Class.new(ActiveModelSerializers::Model) do attributes :id, :one, :two, :three end self.class.const_set(:SomeTestModel, klass) original_attributes = { id: :ego, one: 1, two: 2, three: 3 } original_instance = klass.new(original_attributes) # Initial value instance = original_instance.dup expected_attributes = { id: :ego, one: 1, two: 2, three: 3 }.with_indifferent_access assert_equal expected_attributes, instance.attributes assert_equal :ego, instance.id assert_equal :ego, instance.read_attribute_for_serialization(:id) # FIXME: Change via accessor has no effect on attributes. instance = original_instance.dup instance.id = :superego assert_equal expected_attributes, instance.attributes assert_equal :superego, instance.id assert_equal :superego, instance.read_attribute_for_serialization(:id) # FIXME: Change via mutating attributes instance = original_instance.dup instance.attributes[:id] = :superego expected_attributes = { id: :superego, one: 1, two: 2, three: 3 }.with_indifferent_access assert_equal expected_attributes, instance.attributes assert_equal :ego, instance.id assert_equal :ego, instance.read_attribute_for_serialization(:id) ensure self.class.send(:remove_const, :SomeTestModel) end def test_id_attribute_can_be_read_for_serialization_with_attributes_accessors_fix klass = Class.new(ActiveModelSerializers::Model) do derive_attributes_from_names_and_fix_accessors attributes :id, :one, :two, :three end self.class.const_set(:SomeTestModel, klass) original_attributes = { id: :ego, one: 1, two: 2, three: 3 } original_instance = klass.new(original_attributes) # Initial value instance = original_instance.dup expected_attributes = { id: :ego, one: 1, two: 2, three: 3 }.with_indifferent_access assert_equal expected_attributes, instance.attributes assert_equal :ego, instance.id assert_equal :ego, instance.read_attribute_for_serialization(:id) expected_attributes = { id: :superego, one: 1, two: 2, three: 3 }.with_indifferent_access # Change via accessor instance = original_instance.dup instance.id = :superego assert_equal expected_attributes, instance.attributes assert_equal :superego, instance.id assert_equal :superego, instance.read_attribute_for_serialization(:id) # Attributes frozen assert instance.attributes.frozen? ensure self.class.send(:remove_const, :SomeTestModel) end end end active_model_serializers-0.10.10/test/active_model_serializers/railtie_test_isolated.rb000066400000000000000000000051071351232231100316430ustar00rootroot00000000000000# frozen_string_literal: true # Execute this test in isolation require 'support/isolated_unit' class RailtieTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation class WithRailsRequiredFirst < RailtieTest setup do require 'rails' require 'active_model_serializers' make_basic_app do |app| app.config.action_controller.perform_caching = true end end test 'mixes ActionController::Serialization into ActionController::Base' do assert ActionController.const_defined?(:Serialization), "ActionController::Serialization should be defined, but isn't" assert ::ActionController::Base.included_modules.include?(::ActionController::Serialization), "ActionController::Serialization should be included in ActionController::Base, but isn't" end test 'prepares url_helpers for SerializationContext' do assert ActiveModelSerializers::SerializationContext.url_helpers.respond_to? :url_for assert_equal Rails.application.routes.default_url_options, ActiveModelSerializers::SerializationContext.default_url_options end test 'sets the ActiveModelSerializers.logger to Rails.logger' do refute_nil Rails.logger refute_nil ActiveModelSerializers.logger assert_equal Rails.logger, ActiveModelSerializers.logger end test 'it is configured for caching' do assert_equal ActionController::Base.cache_store, ActiveModelSerializers.config.cache_store assert_equal true, Rails.configuration.action_controller.perform_caching assert_equal true, ActiveModelSerializers.config.perform_caching end end class WithoutRailsRequiredFirst < RailtieTest setup do require 'active_model_serializers' make_basic_app do |app| app.config.action_controller.perform_caching = true end end test 'does not mix ActionController::Serialization into ActionController::Base' do refute ActionController.const_defined?(:Serialization), 'ActionController::Serialization should not be defined, but is' end test 'has its own logger at ActiveModelSerializers.logger' do refute_nil Rails.logger refute_nil ActiveModelSerializers.logger refute_equal Rails.logger, ActiveModelSerializers.logger end test 'it is not configured for caching' do refute_nil ActionController::Base.cache_store assert_nil ActiveModelSerializers.config.cache_store assert_equal true, Rails.configuration.action_controller.perform_caching assert_nil ActiveModelSerializers.config.perform_caching end end end register_jsonapi_renderer_test_isolated.rb000066400000000000000000000116311351232231100353670ustar00rootroot00000000000000active_model_serializers-0.10.10/test/active_model_serializers# frozen_string_literal: true require 'support/isolated_unit' require 'minitest/mock' require 'action_dispatch' require 'action_controller' class JsonApiRendererTest < ActionDispatch::IntegrationTest include ActiveSupport::Testing::Isolation class TestController < ActionController::Base class << self attr_accessor :last_request_parameters end def render_with_jsonapi_renderer permitted_params = params.permit(data: [:id, :type, attributes: [:name]]) permitted_params = permitted_params.to_h.with_indifferent_access attributes = if permitted_params[:data] permitted_params[:data][:attributes].merge(id: permitted_params[:data][:id]) else # Rails returns empty params when no mime type can be negotiated. # (Until https://github.com/rails/rails/pull/26632 is reviewed.) permitted_params end author = Author.new(attributes) render jsonapi: author end def parse self.class.last_request_parameters = request.request_parameters head :ok end end def teardown TestController.last_request_parameters = nil end def assert_parses(expected, actual, headers = {}) post '/parse', params: actual, headers: headers assert_response :ok assert_equal(expected, TestController.last_request_parameters) end def define_author_model_and_serializer TestController.const_set(:Author, Class.new(ActiveModelSerializers::Model) do attributes :id, :name end) TestController.const_set(:AuthorSerializer, Class.new(ActiveModel::Serializer) do type 'users' attribute :id attribute :name end) end class WithoutRenderer < JsonApiRendererTest setup do require 'rails' require 'active_record' require 'support/rails5_shims' require 'active_model_serializers' require 'fixtures/poro' make_basic_app Rails.application.routes.draw do ActiveSupport::Deprecation.silence do match ':action', to: TestController, via: [:get, :post] end end define_author_model_and_serializer end def test_jsonapi_parser_not_registered parsers = if Rails::VERSION::MAJOR >= 5 ActionDispatch::Request.parameter_parsers else ActionDispatch::ParamsParser::DEFAULT_PARSERS end assert_nil parsers[Mime[:jsonapi]] end def test_jsonapi_renderer_not_registered payload = '{"data": {"attributes": {"name": "Johnny Rico"}, "type": "users", "id": "36c9c04e-86b1-4636-a5b0-8616672d1765"}}' headers = { 'CONTENT_TYPE' => 'application/vnd.api+json' } post '/render_with_jsonapi_renderer', params: payload, headers: headers assert_equal '', response.body assert_equal 500, response.status assert_equal ActionView::MissingTemplate, request.env['action_dispatch.exception'].class end def test_jsonapi_parser assert_parses( {}, '', 'CONTENT_TYPE' => 'application/vnd.api+json' ) end end class WithRenderer < JsonApiRendererTest setup do require 'rails' require 'active_record' require 'support/rails5_shims' require 'active_model_serializers' require 'fixtures/poro' require 'active_model_serializers/register_jsonapi_renderer' make_basic_app Rails.application.routes.draw do ActiveSupport::Deprecation.silence do match ':action', to: TestController, via: [:get, :post] end end define_author_model_and_serializer end def test_jsonapi_parser_registered if Rails::VERSION::MAJOR >= 5 parsers = ActionDispatch::Request.parameter_parsers assert_equal Proc, parsers[:jsonapi].class else parsers = ActionDispatch::ParamsParser::DEFAULT_PARSERS assert_equal Proc, parsers[Mime[:jsonapi]].class end end def test_jsonapi_renderer_registered expected = { 'data' => { 'id' => '36c9c04e-86b1-4636-a5b0-8616672d1765', 'type' => 'users', 'attributes' => { 'name' => 'Johnny Rico' } } } payload = '{"data": {"attributes": {"name": "Johnny Rico"}, "type": "users", "id": "36c9c04e-86b1-4636-a5b0-8616672d1765"}}' headers = { 'CONTENT_TYPE' => 'application/vnd.api+json' } post '/render_with_jsonapi_renderer', params: payload, headers: headers assert_equal expected.to_json, response.body end def test_jsonapi_parser assert_parses( { 'data' => { 'attributes' => { 'name' => 'John Doe' }, 'type' => 'users', 'id' => '36c9c04e-86b1-4636-a5b0-8616672d1765' } }, '{"data": {"attributes": {"name": "John Doe"}, "type": "users", "id": "36c9c04e-86b1-4636-a5b0-8616672d1765"}}', 'CONTENT_TYPE' => 'application/vnd.api+json' ) end end end serialization_context_test_isolated.rb000066400000000000000000000045031351232231100345530ustar00rootroot00000000000000active_model_serializers-0.10.10/test/active_model_serializers# frozen_string_literal: true # Execute this test in isolation require 'support/isolated_unit' require 'minitest/mock' class SerializationContextTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation class WithRails < SerializationContextTest def create_request request = ActionDispatch::Request.new({}) def request.original_url 'http://example.com/articles?page=2' end def request.query_parameters { 'page' => 2 } end request end setup do require 'rails' require 'active_model_serializers' make_basic_app @context = ActiveModelSerializers::SerializationContext.new(create_request) end test 'create context with request url and query parameters' do assert_equal @context.request_url, 'http://example.com/articles' assert_equal @context.query_parameters, 'page' => 2 end test 'url_helpers is set up for Rails url_helpers' do assert_equal Module, ActiveModelSerializers::SerializationContext.url_helpers.class assert ActiveModelSerializers::SerializationContext.url_helpers.respond_to? :url_for end test 'default_url_options returns Rails.application.routes.default_url_options' do assert_equal Rails.application.routes.default_url_options, ActiveModelSerializers::SerializationContext.default_url_options end end class WithoutRails < SerializationContextTest def create_request { request_url: 'http://example.com/articles', query_parameters: { 'page' => 2 } } end setup do require 'active_model_serializers/serialization_context' @context = ActiveModelSerializers::SerializationContext.new(create_request) end test 'create context with request url and query parameters' do assert_equal @context.request_url, 'http://example.com/articles' assert_equal @context.query_parameters, 'page' => 2 end test 'url_helpers is a module when Rails is not present' do assert_equal Module, ActiveModelSerializers::SerializationContext.url_helpers.class refute ActiveModelSerializers::SerializationContext.url_helpers.respond_to? :url_for end test 'default_url_options return a Hash' do assert Hash, ActiveModelSerializers::SerializationContext.default_url_options.class end end end active_model_serializers-0.10.10/test/active_model_serializers/test/000077500000000000000000000000001351232231100257165ustar00rootroot00000000000000active_model_serializers-0.10.10/test/active_model_serializers/test/schema_test.rb000066400000000000000000000075021351232231100305460ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Test class SchemaTest < ActionController::TestCase include ActiveModelSerializers::Test::Schema class MyController < ActionController::Base def index render json: profile end def show index end def name_as_a_integer profile.name = 1 index end def render_using_json_api render json: profile, adapter: :json_api end def invalid_json_body render json: '' end private def profile @profile ||= Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') end end tests MyController def test_that_assert_with_a_valid_schema get :index assert_response_schema end def test_that_raises_a_minitest_error_with_a_invalid_schema message = "#/name: failed schema #/properties/name: For 'properties/name', \"Name 1\" is not an integer. and #/description: failed schema #/properties/description: For 'properties/description', \"Description 1\" is not a boolean." get :show error = assert_raises Minitest::Assertion do assert_response_schema end assert_equal(message, error.message) end def test_that_raises_error_with_a_custom_message_with_a_invalid_schema message = 'oh boy the show is broken' exception_message = "#/name: failed schema #/properties/name: For 'properties/name', \"Name 1\" is not an integer. and #/description: failed schema #/properties/description: For 'properties/description', \"Description 1\" is not a boolean." expected_message = "#{message}: #{exception_message}" get :show error = assert_raises Minitest::Assertion do assert_response_schema(nil, message) end assert_equal(expected_message, error.message) end def test_that_assert_with_a_custom_schema get :show assert_response_schema('custom/show.json') end def test_that_assert_with_a_hyper_schema get :show assert_response_schema('hyper_schema.json') end def test_simple_json_pointers get :show assert_response_schema('simple_json_pointers.json') end def test_simple_json_pointers_that_doesnt_match get :name_as_a_integer assert_raises Minitest::Assertion do assert_response_schema('simple_json_pointers.json') end end def test_json_api_schema get :render_using_json_api assert_response_schema('render_using_json_api.json') end def test_that_assert_with_a_custom_schema_directory original_schema_path = ActiveModelSerializers.config.schema_path ActiveModelSerializers.config.schema_path = 'test/support/custom_schemas' get :index assert_response_schema ActiveModelSerializers.config.schema_path = original_schema_path end def test_with_a_non_existent_file message = 'No Schema file at test/support/schemas/non-existent.json' get :show error = assert_raises ActiveModelSerializers::Test::Schema::MissingSchema do assert_response_schema('non-existent.json') end assert_equal(message, error.message) end def test_that_raises_with_a_invalid_json_body # message changes from JSON gem 2.0.2 to 2.2.0 message = /A JSON text must at least contain two octets!|unexpected token at ''/ get :invalid_json_body error = assert_raises ActiveModelSerializers::Test::Schema::InvalidSchemaError do assert_response_schema('custom/show.json') end assert_match(message, error.message) end end end end active_model_serializers-0.10.10/test/active_model_serializers/test/serializer_test.rb000066400000000000000000000036701351232231100314610ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Test class SerializerTest < ActionController::TestCase include ActiveModelSerializers::Test::Serializer class MyController < ActionController::Base def render_using_serializer render json: Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') end def render_some_text render(plain: 'ok') end end tests MyController def test_supports_specifying_serializers_with_a_serializer_class get :render_using_serializer assert_serializer ProfileSerializer end def test_supports_specifying_serializers_with_a_regexp get :render_using_serializer assert_serializer(/\AProfile.+\Z/) end def test_supports_specifying_serializers_with_a_string get :render_using_serializer assert_serializer 'ProfileSerializer' end def test_supports_specifying_serializers_with_a_symbol get :render_using_serializer assert_serializer :profile_serializer end def test_supports_specifying_serializers_with_a_nil get :render_some_text assert_serializer nil end def test_raises_descriptive_error_message_when_serializer_was_not_rendered get :render_using_serializer e = assert_raise ActiveSupport::TestCase::Assertion do assert_serializer 'PostSerializer' end assert_match 'expecting <"PostSerializer"> but rendering with <["ProfileSerializer"]>', e.message end def test_raises_argument_error_when_asserting_with_invalid_object get :render_using_serializer e = assert_raise ArgumentError do assert_serializer Hash end assert_match 'assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil', e.message end end end end active_model_serializers-0.10.10/test/active_record_test.rb000066400000000000000000000003201351232231100240600ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' class ActiveRecordTest < ActiveSupport::TestCase include ActiveModel::Serializer::Lint::Tests def setup @resource = ARModels::Post.new end end active_model_serializers-0.10.10/test/adapter/000077500000000000000000000000001351232231100213105ustar00rootroot00000000000000active_model_serializers-0.10.10/test/adapter/attributes_test.rb000066400000000000000000000023201351232231100250570ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class AttributesTest < ActiveSupport::TestCase class Person < ActiveModelSerializers::Model attributes :first_name, :last_name end class PersonSerializer < ActiveModel::Serializer attributes :first_name, :last_name end def setup ActionController::Base.cache_store.clear end def test_serializable_hash person = Person.new(first_name: 'Arthur', last_name: 'Dent') serializer = PersonSerializer.new(person) adapter = ActiveModelSerializers::Adapter::Attributes.new(serializer) assert_equal({ first_name: 'Arthur', last_name: 'Dent' }, adapter.serializable_hash) end def test_serializable_hash_with_transform_key_casing person = Person.new(first_name: 'Arthur', last_name: 'Dent') serializer = PersonSerializer.new(person) adapter = ActiveModelSerializers::Adapter::Attributes.new( serializer, key_transform: :camel_lower ) assert_equal({ firstName: 'Arthur', lastName: 'Dent' }, adapter.serializable_hash) end end end end active_model_serializers-0.10.10/test/adapter/deprecation_test.rb000066400000000000000000000047751351232231100252060ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer module Adapter class DeprecationTest < ActiveSupport::TestCase class PostSerializer < ActiveModel::Serializer attribute :body end setup do post = Post.new(id: 1, body: 'Hello') @serializer = PostSerializer.new(post) end def test_null_adapter_serialization_deprecation expected = {} assert_deprecated do assert_equal(expected, Null.new(@serializer).as_json) end end def test_json_adapter_serialization_deprecation expected = { post: { body: 'Hello' } } assert_deprecated do assert_equal(expected, Json.new(@serializer).as_json) end end def test_jsonapi_adapter_serialization_deprecation expected = { data: { id: '1', type: 'posts', attributes: { body: 'Hello' } } } assert_deprecated do assert_equal(expected, JsonApi.new(@serializer).as_json) end end def test_attributes_adapter_serialization_deprecation expected = { body: 'Hello' } assert_deprecated do assert_equal(expected, Attributes.new(@serializer).as_json) end end def test_adapter_create_deprecation assert_deprecated do Adapter.create(@serializer) end end def test_adapter_adapter_map_deprecation assert_deprecated do Adapter.adapter_map end end def test_adapter_adapters_deprecation assert_deprecated do Adapter.adapters end end def test_adapter_adapter_class_deprecation assert_deprecated do Adapter.adapter_class(:json_api) end end def test_adapter_register_deprecation assert_deprecated do begin Adapter.register(:test, Class.new) ensure Adapter.adapter_map.delete('test') end end end def test_adapter_lookup_deprecation assert_deprecated do Adapter.lookup(:json_api) end end private def assert_deprecated assert_output(nil, /deprecated/) do yield end end end end end end active_model_serializers-0.10.10/test/adapter/json/000077500000000000000000000000001351232231100222615ustar00rootroot00000000000000active_model_serializers-0.10.10/test/adapter/json/belongs_to_test.rb000066400000000000000000000034171351232231100260050ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class Json class BelongsToTest < ActiveSupport::TestCase def setup @post = Post.new(id: 42, title: 'New Post', body: 'Body') @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @post.comments = [@comment] @anonymous_post.comments = [] @comment.post = @post @comment.author = nil @anonymous_post.author = nil @blog = Blog.new(id: 1, name: 'My Blog!!') @post.blog = @blog @anonymous_post.blog = nil @serializer = CommentSerializer.new(@comment) @adapter = ActiveModelSerializers::Adapter::Json.new(@serializer) ActionController::Base.cache_store.clear end def test_includes_post assert_equal({ id: 42, title: 'New Post', body: 'Body' }, @adapter.serializable_hash[:comment][:post]) end def test_include_nil_author serializer = PostSerializer.new(@anonymous_post) adapter = ActiveModelSerializers::Adapter::Json.new(serializer) assert_equal({ post: { title: 'Hello!!', body: 'Hello, world!!', id: 43, comments: [], blog: { id: 999, name: 'Custom blog' }, author: nil } }, adapter.serializable_hash) end def test_include_nil_author_with_specified_serializer serializer = PostPreviewSerializer.new(@anonymous_post) adapter = ActiveModelSerializers::Adapter::Json.new(serializer) assert_equal({ post: { title: 'Hello!!', body: 'Hello, world!!', id: 43, comments: [], author: nil } }, adapter.serializable_hash) end end end end end active_model_serializers-0.10.10/test/adapter/json/collection_test.rb000066400000000000000000000067121351232231100260060ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class Json class Collection < ActiveSupport::TestCase def setup @author = Author.new(id: 1, name: 'Steve K.') @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') @first_post.comments = [] @second_post.comments = [] @first_post.author = @author @second_post.author = @author @blog = Blog.new(id: 1, name: 'My Blog!!') @first_post.blog = @blog @second_post.blog = nil ActionController::Base.cache_store.clear end def test_with_serializer_option @blog.special_attribute = 'Special' @blog.articles = [@first_post, @second_post] serializer = ActiveModel::Serializer::CollectionSerializer.new([@blog], serializer: CustomBlogSerializer) adapter = ActiveModelSerializers::Adapter::Json.new(serializer) expected = { blogs: [{ id: 1, special_attribute: 'Special', articles: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' }, { id: 2, title: 'New Post', body: 'Body' }] }] } assert_equal expected, adapter.serializable_hash end def test_include_multiple_posts serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post]) adapter = ActiveModelSerializers::Adapter::Json.new(serializer) expected = { posts: [{ title: 'Hello!!', body: 'Hello, world!!', id: 1, comments: [], author: { id: 1, name: 'Steve K.' }, blog: { id: 999, name: 'Custom blog' } }, { title: 'New Post', body: 'Body', id: 2, comments: [], author: { id: 1, name: 'Steve K.' }, blog: { id: 999, name: 'Custom blog' } }] } assert_equal expected, adapter.serializable_hash end def test_root_is_underscored virtual_value = VirtualValue.new(id: 1) serializer = ActiveModel::Serializer::CollectionSerializer.new([virtual_value]) adapter = ActiveModelSerializers::Adapter::Json.new(serializer) assert_equal 1, adapter.serializable_hash[:virtual_values].length end def test_include_option serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post]) adapter = ActiveModelSerializers::Adapter::Json.new(serializer, include: '') actual = adapter.serializable_hash expected = { posts: [{ id: 1, title: 'Hello!!', body: 'Hello, world!!' }, { id: 2, title: 'New Post', body: 'Body' }] } assert_equal(expected, actual) end def test_fields_with_no_associations_include_option actual = ActiveModelSerializers::SerializableResource.new( [@first_post, @second_post], adapter: :json, fields: [:id], include: [] ).as_json expected = { posts: [{ id: 1 }, { id: 2 }] } assert_equal(expected, actual) end end end end end active_model_serializers-0.10.10/test/adapter/json/fields_test.rb000066400000000000000000000037611351232231100251220ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class Json class FieldsTest < ActiveSupport::TestCase class Post < ::Model attributes :title, :body associations :author, :comments end class Author < ::Model attributes :name, :birthday end class Comment < ::Model attributes :title, :body associations :author, :post end class PostSerializer < ActiveModel::Serializer type 'post' attributes :title, :body belongs_to :author has_many :comments end class AuthorSerializer < ActiveModel::Serializer attributes :name, :birthday end class CommentSerializer < ActiveModel::Serializer type 'comment' attributes :title, :body belongs_to :author end def setup @author = Author.new(id: 1, name: 'Lucas', birthday: '10.01.1990') @comment1 = Comment.new(id: 7, body: 'cool', author: @author) @comment2 = Comment.new(id: 12, body: 'awesome', author: @author) @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', author: @author, comments: [@comment1, @comment2]) @comment1.post = @post @comment2.post = @post end def test_fields_attributes fields = [:title] hash = serializable(@post, adapter: :json, fields: fields, include: []).serializable_hash expected = { title: 'Title 1' } assert_equal(expected, hash[:post]) end def test_fields_included fields = [:title, { comments: [:body] }] hash = serializable(@post, adapter: :json, include: [:comments], fields: fields).serializable_hash expected = [{ body: @comment1.body }, { body: @comment2.body }] assert_equal(expected, hash[:post][:comments]) end end end end end active_model_serializers-0.10.10/test/adapter/json/has_many_test.rb000066400000000000000000000035531351232231100254520ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class Json class HasManyTestTest < ActiveSupport::TestCase class ModelWithoutSerializer < ::Model attributes :id, :name end def setup ActionController::Base.cache_store.clear @author = Author.new(id: 1, name: 'Steve K.') @post = Post.new(id: 42, title: 'New Post', body: 'Body') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment] @post.author = @author @first_comment.post = @post @second_comment.post = @post @blog = Blog.new(id: 1, name: 'My Blog!!') @post.blog = @blog @tag = ModelWithoutSerializer.new(id: 1, name: '#hash_tag') @post.tags = [@tag] end def test_has_many serializer = PostSerializer.new(@post) adapter = ActiveModelSerializers::Adapter::Json.new(serializer) assert_equal([ { id: 1, body: 'ZOMG A COMMENT' }, { id: 2, body: 'ZOMG ANOTHER COMMENT' } ], adapter.serializable_hash[:post][:comments]) end def test_has_many_with_no_serializer post_serializer_class = Class.new(ActiveModel::Serializer) do attributes :id has_many :tags end serializer = post_serializer_class.new(@post) adapter = ActiveModelSerializers::Adapter::Json.new(serializer) assert_equal({ id: 42, tags: [ { 'id' => 1, 'name' => '#hash_tag' } ] }.to_json, adapter.serializable_hash[:post].to_json) end end end end end active_model_serializers-0.10.10/test/adapter/json/transform_test.rb000066400000000000000000000060341351232231100256630ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class Json class KeyCaseTest < ActiveSupport::TestCase def mock_request(key_transform = nil) context = Minitest::Mock.new context.expect(:request_url, URI) context.expect(:query_parameters, {}) options = {} options[:key_transform] = key_transform if key_transform options[:serialization_context] = context serializer = CustomBlogSerializer.new(@blog) @adapter = ActiveModelSerializers::Adapter::Json.new(serializer, options) end class Post < ::Model; end class PostSerializer < ActiveModel::Serializer attributes :id, :title, :body, :publish_at end setup do ActionController::Base.cache_store.clear @blog = Blog.new(id: 1, name: 'My Blog!!', special_attribute: 'neat') end def test_transform_default mock_request assert_equal({ blog: { id: 1, special_attribute: 'neat', articles: nil } }, @adapter.serializable_hash) end def test_transform_global_config mock_request result = with_config(key_transform: :camel_lower) do @adapter.serializable_hash end assert_equal({ blog: { id: 1, specialAttribute: 'neat', articles: nil } }, result) end def test_transform_serialization_ctx_overrides_global_config mock_request(:camel) result = with_config(key_transform: :camel_lower) do @adapter.serializable_hash end assert_equal({ Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil } }, result) end def test_transform_undefined mock_request(:blam) result = nil assert_raises NoMethodError do result = @adapter.serializable_hash end end def test_transform_dash mock_request(:dash) assert_equal({ blog: { id: 1, :"special-attribute" => 'neat', articles: nil } }, @adapter.serializable_hash) end def test_transform_unaltered mock_request(:unaltered) assert_equal({ blog: { id: 1, special_attribute: 'neat', articles: nil } }, @adapter.serializable_hash) end def test_transform_camel mock_request(:camel) assert_equal({ Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil } }, @adapter.serializable_hash) end def test_transform_camel_lower mock_request(:camel_lower) assert_equal({ blog: { id: 1, specialAttribute: 'neat', articles: nil } }, @adapter.serializable_hash) end end end end end active_model_serializers-0.10.10/test/adapter/json_api/000077500000000000000000000000001351232231100231125ustar00rootroot00000000000000active_model_serializers-0.10.10/test/adapter/json_api/belongs_to_test.rb000066400000000000000000000123431351232231100266340ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi class BelongsToTest < ActiveSupport::TestCase def setup @author = Author.new(id: 1, name: 'Steve K.') @author.bio = nil @author.roles = [] @blog = Blog.new(id: 23, name: 'AMS Blog') @post = Post.new(id: 42, title: 'New Post', body: 'Body') @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @post.comments = [@comment] @post.blog = @blog @anonymous_post.comments = [] @anonymous_post.blog = nil @comment.post = @post @comment.author = nil @post.author = @author @anonymous_post.author = nil @blog = Blog.new(id: 1, name: 'My Blog!!') @blog.writer = @author @blog.articles = [@post, @anonymous_post] @author.posts = [] @serializer = CommentSerializer.new(@comment) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer) ActionController::Base.cache_store.clear end def test_includes_post_id expected = { data: { type: 'posts', id: '42' } } assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:post]) end def test_includes_linked_post @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:post]) expected = [{ id: '42', type: 'posts', attributes: { title: 'New Post', body: 'Body' }, relationships: { comments: { data: [{ type: 'comments', id: '1' }] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } }] assert_equal expected, @adapter.serializable_hash[:included] end def test_limiting_linked_post_fields @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:post], fields: { post: [:title, :comments, :blog, :author] }) expected = [{ id: '42', type: 'posts', attributes: { title: 'New Post' }, relationships: { comments: { data: [{ type: 'comments', id: '1' }] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } }] assert_equal expected, @adapter.serializable_hash[:included] end def test_include_nil_author serializer = PostSerializer.new(@anonymous_post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) assert_equal({ comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: nil } }, adapter.serializable_hash[:data][:relationships]) end def test_include_type_for_association_when_different_than_name serializer = BlogSerializer.new(@blog) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) relationships = adapter.serializable_hash[:data][:relationships] expected = { writer: { data: { type: 'authors', id: '1' } }, articles: { data: [ { type: 'posts', id: '42' }, { type: 'posts', id: '43' } ] } } assert_equal expected, relationships end def test_include_linked_resources_with_type_name serializer = BlogSerializer.new(@blog) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, include: [:writer, :articles]) linked = adapter.serializable_hash[:included] expected = [ { id: '1', type: 'authors', attributes: { name: 'Steve K.' }, relationships: { posts: { data: [] }, roles: { data: [] }, bio: { data: nil } } }, { id: '42', type: 'posts', attributes: { title: 'New Post', body: 'Body' }, relationships: { comments: { data: [{ type: 'comments', id: '1' }] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } }, { id: '43', type: 'posts', attributes: { title: 'Hello!!', body: 'Hello, world!!' }, relationships: { comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: nil } } } ] assert_equal expected, linked end end end end end active_model_serializers-0.10.10/test/adapter/json_api/collection_test.rb000066400000000000000000000060421351232231100266330ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi class CollectionTest < ActiveSupport::TestCase def setup @author = Author.new(id: 1, name: 'Steve K.') @author.bio = nil @blog = Blog.new(id: 23, name: 'AMS Blog') @first_post = Post.new(id: 1, title: 'Hello!!', body: 'Hello, world!!') @second_post = Post.new(id: 2, title: 'New Post', body: 'Body') @first_post.comments = [] @second_post.comments = [] @first_post.blog = @blog @second_post.blog = nil @first_post.author = @author @second_post.author = @author @author.posts = [@first_post, @second_post] @serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post]) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer) ActionController::Base.cache_store.clear end def test_include_multiple_posts expected = [ { id: '1', type: 'posts', attributes: { title: 'Hello!!', body: 'Hello, world!!' }, relationships: { comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } }, { id: '2', type: 'posts', attributes: { title: 'New Post', body: 'Body' }, relationships: { comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } } ] assert_equal(expected, @adapter.serializable_hash[:data]) end def test_limiting_fields actual = ActiveModelSerializers::SerializableResource.new( [@first_post, @second_post], adapter: :json_api, fields: { posts: %w(title comments blog author) } ).serializable_hash expected = [ { id: '1', type: 'posts', attributes: { title: 'Hello!!' }, relationships: { comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } }, { id: '2', type: 'posts', attributes: { title: 'New Post' }, relationships: { comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } } ] assert_equal(expected, actual[:data]) end end end end end active_model_serializers-0.10.10/test/adapter/json_api/errors_test.rb000066400000000000000000000053231351232231100260150ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi < Base class ErrorsTest < Minitest::Test include ActiveModel::Serializer::Lint::Tests def setup @resource = ModelWithErrors.new end def test_active_model_with_error options = { serializer: ActiveModel::Serializer::ErrorSerializer, adapter: :json_api } @resource.errors.add(:name, 'cannot be nil') serializable_resource = ActiveModelSerializers::SerializableResource.new(@resource, options) assert_equal serializable_resource.serializer_instance.attributes, {} assert_equal serializable_resource.serializer_instance.object, @resource expected_errors_object = { errors: [ { source: { pointer: '/data/attributes/name' }, detail: 'cannot be nil' } ] } assert_equal serializable_resource.as_json, expected_errors_object end def test_active_model_with_multiple_errors options = { serializer: ActiveModel::Serializer::ErrorSerializer, adapter: :json_api } @resource.errors.add(:name, 'cannot be nil') @resource.errors.add(:name, 'must be longer') @resource.errors.add(:id, 'must be a uuid') serializable_resource = ActiveModelSerializers::SerializableResource.new(@resource, options) assert_equal serializable_resource.serializer_instance.attributes, {} assert_equal serializable_resource.serializer_instance.object, @resource expected_errors_object = { errors: [ { source: { pointer: '/data/attributes/name' }, detail: 'cannot be nil' }, { source: { pointer: '/data/attributes/name' }, detail: 'must be longer' }, { source: { pointer: '/data/attributes/id' }, detail: 'must be a uuid' } ] } assert_equal serializable_resource.as_json, expected_errors_object end # see http://jsonapi.org/examples/ def test_parameter_source_type_error parameter = 'auther' error_source = ActiveModelSerializers::Adapter::JsonApi::Error.error_source(:parameter, parameter) assert_equal({ parameter: parameter }, error_source) end def test_unknown_source_type_error value = 'auther' assert_raises(ActiveModelSerializers::Adapter::JsonApi::Error::UnknownSourceTypeError) do ActiveModelSerializers::Adapter::JsonApi::Error.error_source(:hyper, value) end end end end end end active_model_serializers-0.10.10/test/adapter/json_api/fields_test.rb000066400000000000000000000053161351232231100257510ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi class FieldsTest < ActiveSupport::TestCase class Post < ::Model attributes :title, :body associations :author, :comments end class Author < ::Model attributes :name, :birthday end class Comment < ::Model attributes :body associations :author, :post end class PostSerializer < ActiveModel::Serializer type 'posts' attributes :title, :body belongs_to :author has_many :comments end class AuthorSerializer < ActiveModel::Serializer type 'authors' attributes :name, :birthday end class CommentSerializer < ActiveModel::Serializer type 'comments' attributes :body belongs_to :author end def setup @author = Author.new(id: 1, name: 'Lucas', birthday: '10.01.1990') @comment1 = Comment.new(id: 7, body: 'cool', author: @author) @comment2 = Comment.new(id: 12, body: 'awesome', author: @author) @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', author: @author, comments: [@comment1, @comment2]) @comment1.post = @post @comment2.post = @post end def test_fields_attributes fields = { posts: [:title] } hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash expected = { title: 'Title 1' } assert_equal(expected, hash[:data][:attributes]) end def test_fields_relationships fields = { posts: [:author] } hash = serializable(@post, adapter: :json_api, fields: fields).serializable_hash expected = { author: { data: { type: 'authors', id: '1' } } } assert_equal(expected, hash[:data][:relationships]) end def test_fields_included fields = { posts: [:author], comments: [:body] } hash = serializable(@post, adapter: :json_api, fields: fields, include: 'comments').serializable_hash expected = [ { type: 'comments', id: '7', attributes: { body: 'cool' } }, { type: 'comments', id: '12', attributes: { body: 'awesome' } } ] assert_equal(expected, hash[:included]) end end end end end active_model_serializers-0.10.10/test/adapter/json_api/has_many_explicit_serializer_test.rb000066400000000000000000000057121351232231100324340ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi # Test 'has_many :assocs, serializer: AssocXSerializer' class HasManyExplicitSerializerTest < ActiveSupport::TestCase def setup @post = Post.new(title: 'New Post', body: 'Body') @author = Author.new(name: 'Jane Blogger') @author.posts = [@post] @post.author = @author @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment] @first_comment.post = @post @first_comment.author = nil @second_comment.post = @post @second_comment.author = nil @blog = Blog.new(id: 23, name: 'AMS Blog') @post.blog = @blog @serializer = PostPreviewSerializer.new(@post) @adapter = ActiveModelSerializers::Adapter::JsonApi.new( @serializer, include: [:comments, :author] ) end def test_includes_comment_ids expected = { data: [ { type: 'comments', id: '1' }, { type: 'comments', id: '2' } ] } assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) end def test_includes_linked_data # If CommentPreviewSerializer is applied correctly the body text will not be present in the output expected = [ { id: '1', type: 'comments', relationships: { post: { data: { type: 'posts', id: @post.id.to_s } } } }, { id: '2', type: 'comments', relationships: { post: { data: { type: 'posts', id: @post.id.to_s } } } }, { id: @author.id.to_s, type: 'authors', relationships: { posts: { data: [{ type: 'posts', id: @post.id.to_s }] } } } ] assert_equal(expected, @adapter.serializable_hash[:included]) end def test_includes_author_id expected = { data: { type: 'authors', id: @author.id.to_s } } assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author]) end def test_explicit_serializer_with_null_resource @post.author = nil expected = { data: nil } assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:author]) end def test_explicit_serializer_with_null_collection @post.comments = [] expected = { data: [] } assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) end end end end end active_model_serializers-0.10.10/test/adapter/json_api/has_many_test.rb000066400000000000000000000137151351232231100263040ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi class HasManyTest < ActiveSupport::TestCase class ModelWithoutSerializer < ::Model attributes :id, :name end def setup ActionController::Base.cache_store.clear @author = Author.new(id: 1, name: 'Steve K.') @author.posts = [] @author.bio = nil @post = Post.new(id: 1, title: 'New Post', body: 'Body') @post_without_comments = Post.new(id: 2, title: 'Second Post', body: 'Second') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @first_comment.author = nil @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @second_comment.author = nil @post.comments = [@first_comment, @second_comment] @post_without_comments.comments = [] @first_comment.post = @post @second_comment.post = @post @post.author = @author @post_without_comments.author = nil @blog = Blog.new(id: 1, name: 'My Blog!!') @blog.writer = @author @blog.articles = [@post] @post.blog = @blog @post_without_comments.blog = nil @tag = ModelWithoutSerializer.new(id: 1, name: '#hash_tag') @post.tags = [@tag] @serializer = PostSerializer.new(@post) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer) @virtual_value = VirtualValue.new(id: 1) end def test_includes_comment_ids expected = { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] } assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:comments]) end test 'relationships can be whitelisted via fields' do @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, fields: { posts: [:author] }) result = @adapter.serializable_hash expected = { data: { id: '1', type: 'posts', relationships: { author: { data: { id: '1', type: 'authors' } } } } } assert_equal expected, result end def test_includes_linked_comments @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:comments]) expected = [{ id: '1', type: 'comments', attributes: { body: 'ZOMG A COMMENT' }, relationships: { post: { data: { type: 'posts', id: '1' } }, author: { data: nil } } }, { id: '2', type: 'comments', attributes: { body: 'ZOMG ANOTHER COMMENT' }, relationships: { post: { data: { type: 'posts', id: '1' } }, author: { data: nil } } }] assert_equal expected, @adapter.serializable_hash[:included] end def test_limit_fields_of_linked_comments @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:comments], fields: { comment: [:id, :post, :author] }) expected = [{ id: '1', type: 'comments', relationships: { post: { data: { type: 'posts', id: '1' } }, author: { data: nil } } }, { id: '2', type: 'comments', relationships: { post: { data: { type: 'posts', id: '1' } }, author: { data: nil } } }] assert_equal expected, @adapter.serializable_hash[:included] end def test_no_include_linked_if_comments_is_empty serializer = PostSerializer.new(@post_without_comments) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) assert_nil adapter.serializable_hash[:linked] end def test_include_type_for_association_when_different_than_name serializer = BlogSerializer.new(@blog) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) actual = adapter.serializable_hash[:data][:relationships][:articles] expected = { data: [{ type: 'posts', id: '1' }] } assert_equal expected, actual end def test_has_many_with_no_serializer post_serializer_class = Class.new(ActiveModel::Serializer) do attributes :id has_many :tags end serializer = post_serializer_class.new(@post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) assert_equal({ data: { id: '1', type: 'posts', relationships: { tags: { data: [@tag.as_json] } } } }, adapter.serializable_hash) end def test_has_many_with_virtual_value serializer = VirtualValueSerializer.new(@virtual_value) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) assert_equal({ data: { id: '1', type: 'virtual-values', relationships: { maker: { data: { type: 'makers', id: '1' } }, reviews: { data: [{ type: 'reviews', id: '1' }, { type: 'reviews', id: '2' }] } } } }, adapter.serializable_hash) end end end end end active_model_serializers-0.10.10/test/adapter/json_api/has_one_test.rb000066400000000000000000000051101351232231100261070ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi class HasOneTest < ActiveSupport::TestCase def setup @author = Author.new(id: 1, name: 'Steve K.') @bio = Bio.new(id: 43, content: 'AMS Contributor') @author.bio = @bio @bio.author = @author @post = Post.new(id: 42, title: 'New Post', body: 'Body') @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @post.comments = [@comment] @anonymous_post.comments = [] @comment.post = @post @comment.author = nil @post.author = @author @anonymous_post.author = nil @blog = Blog.new(id: 1, name: 'My Blog!!') @blog.writer = @author @blog.articles = [@post, @anonymous_post] @author.posts = [] @author.roles = [] @virtual_value = VirtualValue.new(id: 1) @serializer = AuthorSerializer.new(@author) @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:bio, :posts]) end def test_includes_bio_id expected = { data: { type: 'bios', id: '43' } } assert_equal(expected, @adapter.serializable_hash[:data][:relationships][:bio]) end def test_includes_linked_bio @adapter = ActiveModelSerializers::Adapter::JsonApi.new(@serializer, include: [:bio]) expected = [ { id: '43', type: 'bios', attributes: { content: 'AMS Contributor', rating: nil }, relationships: { author: { data: { type: 'authors', id: '1' } } } } ] assert_equal(expected, @adapter.serializable_hash[:included]) end def test_has_one_with_virtual_value serializer = VirtualValueSerializer.new(@virtual_value) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) expected = { data: { id: '1', type: 'virtual-values', relationships: { maker: { data: { type: 'makers', id: '1' } }, reviews: { data: [{ type: 'reviews', id: '1' }, { type: 'reviews', id: '2' }] } } } } assert_equal(expected, adapter.serializable_hash) end end end end end active_model_serializers-0.10.10/test/adapter/json_api/include_data_if_sideloaded_test.rb000066400000000000000000000141041351232231100317450ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer module Adapter class JsonApi class IncludeParamTest < ActiveSupport::TestCase IncludeParamAuthor = Class.new(::Model) do associations :tags, :posts, :roles end class CustomCommentLoader def all [{ foo: 'bar' }] end end class Tag < ::Model attributes :id, :name end class TagSerializer < ActiveModel::Serializer type 'tags' attributes :id, :name end class PostWithTagsSerializer < ActiveModel::Serializer type 'posts' attributes :id has_many :tags end class IncludeParamAuthorSerializer < ActiveModel::Serializer class_attribute :comment_loader has_many :tags, serializer: TagSerializer do link :self, '//example.com/link_author/relationships/tags' include_data :if_sideloaded end has_many :unlinked_tags, serializer: TagSerializer do include_data :if_sideloaded end has_many :posts, serializer: PostWithTagsSerializer do include_data :if_sideloaded end has_many :locations do include_data :if_sideloaded end has_many :comments do include_data :if_sideloaded IncludeParamAuthorSerializer.comment_loader.all end has_many :roles, key: :granted_roles do include_data :if_sideloaded end end def setup IncludeParamAuthorSerializer.comment_loader = Class.new(CustomCommentLoader).new @tag = Tag.new(id: 1337, name: 'mytag') @role = Role.new(id: 1337, name: 'myrole') @author = IncludeParamAuthor.new( id: 1337, tags: [@tag], roles: [@role] ) end def test_relationship_not_loaded_when_not_included expected = { links: { self: '//example.com/link_author/relationships/tags' } } @author.define_singleton_method(:read_attribute_for_serialization) do |attr| fail 'should not be called' if attr == :tags super(attr) end assert_relationship(:tags, expected) end def test_relationship_included expected = { data: [ { id: '1337', type: 'tags' } ], links: { self: '//example.com/link_author/relationships/tags' } } assert_relationship(:tags, expected, include: :tags) end def test_sideloads_included expected = [ { id: '1337', type: 'tags', attributes: { name: 'mytag' } } ] hash = result(include: :tags) assert_equal(expected, hash[:included]) end def test_sideloads_included_when_using_key expected = [ { id: '1337', type: 'roles', attributes: { name: 'myrole', description: nil, slug: 'myrole-1337' }, relationships: { author: { data: nil } } } ] hash = result(include: :granted_roles) assert_equal(expected, hash[:included]) end def test_sideloads_not_included_when_using_name_when_key_defined hash = result(include: :roles) assert_nil(hash[:included]) end def test_nested_relationship expected = { data: [ { id: '1337', type: 'tags' } ], links: { self: '//example.com/link_author/relationships/tags' } } expected_no_data = { links: { self: '//example.com/link_author/relationships/tags' } } assert_relationship(:tags, expected, include: [:tags, { posts: :tags }]) @author.define_singleton_method(:read_attribute_for_serialization) do |attr| fail 'should not be called' if attr == :tags super(attr) end assert_relationship(:tags, expected_no_data, include: { posts: :tags }) end def test_include_params_with_no_block @author.define_singleton_method(:read_attribute_for_serialization) do |attr| fail 'should not be called' if attr == :locations super(attr) end expected = { meta: {} } assert_relationship(:locations, expected) end def test_block_relationship expected = { data: [ { 'foo' => 'bar' } ] } assert_relationship(:comments, expected, include: [:comments]) end def test_node_not_included_when_no_link expected = { meta: {} } assert_relationship(:unlinked_tags, expected, key_transform: :unaltered) end private def assert_relationship(relationship_name, expected, opts = {}) actual = relationship_data(relationship_name, opts) assert_equal(expected, actual) end def result(opts) opts = { adapter: :json_api }.merge(opts) serializable(@author, opts).serializable_hash end def relationship_data(relationship_name, opts = {}) hash = result(opts) hash[:data][:relationships][relationship_name] end end end end end end active_model_serializers-0.10.10/test/adapter/json_api/json_api_test.rb000066400000000000000000000024251351232231100263030ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApiTest < ActiveSupport::TestCase def setup ActionController::Base.cache_store.clear @author = Author.new(id: 1, name: 'Steve K.') @post = Post.new(id: 1, title: 'New Post', body: 'Body') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment] @first_comment.post = @post @second_comment.post = @post @post.author = @author @blog = Blog.new(id: 1, name: 'My Blog!!') @post.blog = @blog end def test_custom_keys serializer = PostWithCustomKeysSerializer.new(@post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) assert_equal({ reviews: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, writer: { data: { type: 'authors', id: '1' } }, site: { data: { type: 'blogs', id: '1' } } }, adapter.serializable_hash[:data][:relationships]) end end end end active_model_serializers-0.10.10/test/adapter/json_api/linked_test.rb000066400000000000000000000332471351232231100257550ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' class NestedPost < ::Model; associations :nested_posts end class NestedPostSerializer < ActiveModel::Serializer has_many :nested_posts end module ActiveModelSerializers module Adapter class JsonApi class LinkedTest < ActiveSupport::TestCase def setup @author1 = Author.new(id: 1, name: 'Steve K.') @author2 = Author.new(id: 2, name: 'Tenderlove') @bio1 = Bio.new(id: 1, content: 'AMS Contributor') @bio2 = Bio.new(id: 2, content: 'Rails Contributor') @first_post = Post.new(id: 10, title: 'Hello!!', body: 'Hello, world!!') @second_post = Post.new(id: 20, title: 'New Post', body: 'Body') @third_post = Post.new(id: 30, title: 'Yet Another Post', body: 'Body') @blog = Blog.new(name: 'AMS Blog') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @first_post.blog = @blog @second_post.blog = @blog @third_post.blog = nil @first_post.comments = [@first_comment, @second_comment] @second_post.comments = [] @third_post.comments = [] @first_post.author = @author1 @second_post.author = @author2 @third_post.author = @author1 @first_comment.post = @first_post @first_comment.author = nil @second_comment.post = @first_post @second_comment.author = nil @author1.posts = [@first_post, @third_post] @author1.bio = @bio1 @author1.roles = [] @author2.posts = [@second_post] @author2.bio = @bio2 @author2.roles = [] @bio1.author = @author1 @bio2.author = @author2 end def test_include_multiple_posts_and_linked_array serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_post, @second_post]) adapter = ActiveModelSerializers::Adapter::JsonApi.new( serializer, include: [:comments, author: [:bio]] ) alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new( serializer, include: [:comments, author: [:bio]] ) expected = { data: [ { id: '10', type: 'posts', attributes: { title: 'Hello!!', body: 'Hello, world!!' }, relationships: { comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } }, { id: '20', type: 'posts', attributes: { title: 'New Post', body: 'Body' }, relationships: { comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '2' } } } } ], included: [ { id: '1', type: 'comments', attributes: { body: 'ZOMG A COMMENT' }, relationships: { post: { data: { type: 'posts', id: '10' } }, author: { data: nil } } }, { id: '2', type: 'comments', attributes: { body: 'ZOMG ANOTHER COMMENT' }, relationships: { post: { data: { type: 'posts', id: '10' } }, author: { data: nil } } }, { id: '1', type: 'authors', attributes: { name: 'Steve K.' }, relationships: { posts: { data: [{ type: 'posts', id: '10' }, { type: 'posts', id: '30' }] }, roles: { data: [] }, bio: { data: { type: 'bios', id: '1' } } } }, { id: '1', type: 'bios', attributes: { content: 'AMS Contributor', rating: nil }, relationships: { author: { data: { type: 'authors', id: '1' } } } }, { id: '2', type: 'authors', attributes: { name: 'Tenderlove' }, relationships: { posts: { data: [{ type: 'posts', id: '20' }] }, roles: { data: [] }, bio: { data: { type: 'bios', id: '2' } } } }, { id: '2', type: 'bios', attributes: { rating: nil, content: 'Rails Contributor' }, relationships: { author: { data: { type: 'authors', id: '2' } } } } ] } assert_equal expected, adapter.serializable_hash assert_equal expected, alt_adapter.serializable_hash end def test_include_multiple_posts_and_linked serializer = BioSerializer.new @bio1 adapter = ActiveModelSerializers::Adapter::JsonApi.new( serializer, include: [author: [:posts]] ) alt_adapter = ActiveModelSerializers::Adapter::JsonApi.new( serializer, include: [author: [:posts]] ) expected = [ { id: '1', type: 'authors', attributes: { name: 'Steve K.' }, relationships: { posts: { data: [{ type: 'posts', id: '10' }, { type: 'posts', id: '30' }] }, roles: { data: [] }, bio: { data: { type: 'bios', id: '1' } } } }, { id: '10', type: 'posts', attributes: { title: 'Hello!!', body: 'Hello, world!!' }, relationships: { comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } }, { id: '30', type: 'posts', attributes: { title: 'Yet Another Post', body: 'Body' }, relationships: { comments: { data: [] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } } ] assert_equal expected, adapter.serializable_hash[:included] assert_equal expected, alt_adapter.serializable_hash[:included] end def test_underscore_model_namespace_for_linked_resource_type spammy_post = Post.new(id: 123) spammy_post.related = [Spam::UnrelatedLink.new(id: 456)] serializer = SpammyPostSerializer.new(spammy_post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) relationships = adapter.serializable_hash[:data][:relationships] expected = { related: { data: [{ type: 'spam-unrelated-links', id: '456' }] } } assert_equal expected, relationships end def test_underscore_model_namespace_with_namespace_separator_for_linked_resource_type spammy_post = Post.new(id: 123) spammy_post.related = [Spam::UnrelatedLink.new(id: 456)] serializer = SpammyPostSerializer.new(spammy_post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) relationships = with_namespace_separator '--' do adapter.serializable_hash[:data][:relationships] end expected = { related: { data: [{ type: 'spam--unrelated-links', id: '456' }] } } assert_equal expected, relationships end def test_multiple_references_to_same_resource serializer = ActiveModel::Serializer::CollectionSerializer.new([@first_comment, @second_comment]) adapter = ActiveModelSerializers::Adapter::JsonApi.new( serializer, include: [:post] ) expected = [ { id: '10', type: 'posts', attributes: { title: 'Hello!!', body: 'Hello, world!!' }, relationships: { comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, blog: { data: { type: 'blogs', id: '999' } }, author: { data: { type: 'authors', id: '1' } } } } ] assert_equal expected, adapter.serializable_hash[:included] end def test_nil_link_with_specified_serializer @first_post.author = nil serializer = PostPreviewSerializer.new(@first_post) adapter = ActiveModelSerializers::Adapter::JsonApi.new( serializer, include: [:author] ) expected = { data: { id: '10', type: 'posts', attributes: { title: 'Hello!!', body: 'Hello, world!!' }, relationships: { comments: { data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }] }, author: { data: nil } } } } assert_equal expected, adapter.serializable_hash end end class NoDuplicatesTest < ActiveSupport::TestCase class Post < ::Model; associations :author end class Author < ::Model; associations :posts, :roles, :bio end class PostSerializer < ActiveModel::Serializer type 'posts' belongs_to :author end class AuthorSerializer < ActiveModel::Serializer type 'authors' has_many :posts end def setup @author = Author.new(id: 1, posts: [], roles: [], bio: nil) @post1 = Post.new(id: 1, author: @author) @post2 = Post.new(id: 2, author: @author) @author.posts << @post1 @author.posts << @post2 @nestedpost1 = NestedPost.new(id: 1, nested_posts: []) @nestedpost2 = NestedPost.new(id: 2, nested_posts: []) @nestedpost1.nested_posts << @nestedpost1 @nestedpost1.nested_posts << @nestedpost2 @nestedpost2.nested_posts << @nestedpost1 @nestedpost2.nested_posts << @nestedpost2 end def test_no_duplicates hash = ActiveModelSerializers::SerializableResource.new(@post1, adapter: :json_api, include: '*.*') .serializable_hash expected = [ { type: 'authors', id: '1', relationships: { posts: { data: [ { type: 'posts', id: '1' }, { type: 'posts', id: '2' } ] } } }, { type: 'posts', id: '2', relationships: { author: { data: { type: 'authors', id: '1' } } } } ] assert_equal(expected, hash[:included]) end def test_no_duplicates_collection hash = ActiveModelSerializers::SerializableResource.new( [@post1, @post2], adapter: :json_api, include: '*.*' ).serializable_hash expected = [ { type: 'authors', id: '1', relationships: { posts: { data: [ { type: 'posts', id: '1' }, { type: 'posts', id: '2' } ] } } } ] assert_equal(expected, hash[:included]) end def test_no_duplicates_global hash = ActiveModelSerializers::SerializableResource.new( @nestedpost1, adapter: :json_api, include: '*' ).serializable_hash expected = [ type: 'nested-posts', id: '2', relationships: { :"nested-posts" => { data: [ { type: 'nested-posts', id: '1' }, { type: 'nested-posts', id: '2' } ] } } ] assert_equal(expected, hash[:included]) end def test_no_duplicates_collection_global hash = ActiveModelSerializers::SerializableResource.new( [@nestedpost1, @nestedpost2], adapter: :json_api, include: '*' ).serializable_hash assert_nil(hash[:included]) end end end end end active_model_serializers-0.10.10/test/adapter/json_api/links_test.rb000066400000000000000000000066521351232231100256270ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi class LinksTest < ActiveSupport::TestCase class LinkAuthor < ::Model; associations :posts end class LinkAuthorSerializer < ActiveModel::Serializer link :self do href "http://example.com/link_author/#{object.id}" meta stuff: 'value' end link(:author) { link_author_url(object.id) } link(:link_authors) { url_for(controller: 'link_authors', action: 'index', only_path: false) } link(:posts) { link_author_posts_url(object.id) } link :resource, 'http://example.com/resource' link :yet_another do "http://example.com/resource/#{object.id}" end link :conditional1, if: -> { instance_truth } do "http://example.com/conditional1/#{object.id}" end link :conditional2, if: :instance_falsey do "http://example.com/conditional2/#{object.id}" end link(:nil) { nil } def instance_truth true end def instance_falsey false end end def setup Rails.application.routes.draw do resources :link_authors do resources :posts end end @post = Post.new(id: 1337, comments: [], author: nil) @author = LinkAuthor.new(id: 1337, posts: [@post]) end def test_toplevel_links hash = ActiveModelSerializers::SerializableResource.new( @post, adapter: :json_api, links: { self: { href: 'http://example.com/posts', meta: { stuff: 'value' } } } ).serializable_hash expected = { self: { href: 'http://example.com/posts', meta: { stuff: 'value' } } } assert_equal(expected, hash[:links]) end def test_nil_toplevel_links hash = ActiveModelSerializers::SerializableResource.new( @post, adapter: :json_api, links: nil ).serializable_hash refute hash.key?(:links), 'No links key to be output' end def test_nil_toplevel_links_json_adapter hash = ActiveModelSerializers::SerializableResource.new( @post, adapter: :json, links: nil ).serializable_hash refute hash.key?(:links), 'No links key to be output' end def test_resource_links hash = serializable(@author, adapter: :json_api).serializable_hash expected = { self: { href: 'http://example.com/link_author/1337', meta: { stuff: 'value' } }, author: 'http://example.com/link_authors/1337', :"link-authors" => 'http://example.com/link_authors', resource: 'http://example.com/resource', posts: 'http://example.com/link_authors/1337/posts', :"yet-another" => 'http://example.com/resource/1337', conditional1: 'http://example.com/conditional1/1337' } assert_equal(expected, hash[:data][:links]) end end end end end active_model_serializers-0.10.10/test/adapter/json_api/pagination_links_test.rb000066400000000000000000000166331351232231100300400ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' require 'will_paginate/array' require 'kaminari' require 'kaminari/hooks' ::Kaminari::Hooks.init module ActiveModelSerializers module Adapter class JsonApi class PaginationLinksTest < ActiveSupport::TestCase URI = 'http://example.com'.freeze def setup ActionController::Base.cache_store.clear @array = [ Profile.new(id: 1, name: 'Name 1', description: 'Description 1', comments: 'Comments 1'), Profile.new(id: 2, name: 'Name 2', description: 'Description 2', comments: 'Comments 2'), Profile.new(id: 3, name: 'Name 3', description: 'Description 3', comments: 'Comments 3'), Profile.new(id: 4, name: 'Name 4', description: 'Description 4', comments: 'Comments 4'), Profile.new(id: 5, name: 'Name 5', description: 'Description 5', comments: 'Comments 5') ] end def mock_request(query_parameters = {}, original_url = URI) context = Minitest::Mock.new context.expect(:request_url, original_url) context.expect(:query_parameters, query_parameters) context.expect(:key_transform, nil) end def load_adapter(paginated_collection, mock_request = nil) render_options = { adapter: :json_api } render_options[:serialization_context] = mock_request if mock_request serializable(paginated_collection, render_options) end def using_kaminari(page = 2) Kaminari.paginate_array(@array).page(page).per(2) end def using_will_paginate(page = 2) @array.paginate(page: page, per_page: 2) end def data { data: [ { id: '1', type: 'profiles', attributes: { name: 'Name 1', description: 'Description 1' } }, { id: '2', type: 'profiles', attributes: { name: 'Name 2', description: 'Description 2' } }, { id: '3', type: 'profiles', attributes: { name: 'Name 3', description: 'Description 3' } }, { id: '4', type: 'profiles', attributes: { name: 'Name 4', description: 'Description 4' } }, { id: '5', type: 'profiles', attributes: { name: 'Name 5', description: 'Description 5' } } ] } end def empty_collection_links { self: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2", first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2", prev: nil, next: nil, last: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2" } end def links { links: { self: "#{URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2", first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2", prev: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2", next: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=2", last: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=2" } } end def last_page_links { links: { self: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=2", first: "#{URI}?page%5Bnumber%5D=1&page%5Bsize%5D=2", prev: "#{URI}?page%5Bnumber%5D=2&page%5Bsize%5D=2", next: nil, last: "#{URI}?page%5Bnumber%5D=3&page%5Bsize%5D=2" } } end def expected_response_when_unpaginatable data end def expected_response_with_pagination_links {}.tap do |hash| hash[:data] = data.values.flatten[2..3] hash.merge! links end end def expected_response_without_pagination_links {}.tap do |hash| hash[:data] = data.values.flatten[2..3] end end def expected_response_with_pagination_links_and_additional_params new_links = links[:links].each_with_object({}) { |(key, value), hash| hash[key] = "#{value}&test=test" } {}.tap do |hash| hash[:data] = data.values.flatten[2..3] hash.merge! links: new_links end end def expected_response_with_last_page_pagination_links {}.tap do |hash| hash[:data] = [data.values.flatten.last] hash.merge! last_page_links end end def expected_response_with_empty_collection_pagination_links {}.tap do |hash| hash[:data] = [] hash.merge! links: empty_collection_links end end def test_pagination_links_using_kaminari adapter = load_adapter(using_kaminari, mock_request) assert_equal expected_response_with_pagination_links, adapter.serializable_hash end def test_pagination_links_using_will_paginate adapter = load_adapter(using_will_paginate, mock_request) assert_equal expected_response_with_pagination_links, adapter.serializable_hash end def test_pagination_links_with_additional_params adapter = load_adapter(using_will_paginate, mock_request(test: 'test')) assert_equal expected_response_with_pagination_links_and_additional_params, adapter.serializable_hash end def test_pagination_links_when_zero_results_kaminari @array = [] adapter = load_adapter(using_kaminari(1), mock_request) assert_equal expected_response_with_empty_collection_pagination_links, adapter.serializable_hash end def test_pagination_links_when_zero_results_will_paginate @array = [] adapter = load_adapter(using_will_paginate(1), mock_request) assert_equal expected_response_with_empty_collection_pagination_links, adapter.serializable_hash end def test_last_page_pagination_links_using_kaminari adapter = load_adapter(using_kaminari(3), mock_request) assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash end def test_last_page_pagination_links_using_will_paginate adapter = load_adapter(using_will_paginate(3), mock_request) assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash end def test_not_showing_pagination_links adapter = load_adapter(@array, mock_request) assert_equal expected_response_when_unpaginatable, adapter.serializable_hash end def test_raises_descriptive_error_when_serialization_context_unset render_options = { adapter: :json_api } adapter = serializable(using_kaminari, render_options) exception_class = ActiveModelSerializers::Adapter::JsonApi::PaginationLinks::MissingSerializationContextError exception = assert_raises(exception_class) do adapter.as_json end assert_equal exception_class, exception.class assert_match(/CollectionSerializer#paginated\?/, exception.message) end def test_pagination_links_not_present_when_disabled ActiveModel::Serializer.config.jsonapi_pagination_links_enabled = false adapter = load_adapter(using_kaminari, mock_request) assert_equal expected_response_without_pagination_links, adapter.serializable_hash ensure ActiveModel::Serializer.config.jsonapi_pagination_links_enabled = true end end end end end active_model_serializers-0.10.10/test/adapter/json_api/parse_test.rb000066400000000000000000000116551351232231100256200ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi module Deserialization class ParseTest < Minitest::Test def setup @hash = { 'data' => { 'type' => 'photos', 'id' => 'zorglub', 'attributes' => { 'title' => 'Ember Hamster', 'src' => 'http://example.com/images/productivity.png' }, 'relationships' => { 'author' => { 'data' => nil }, 'photographer' => { 'data' => { 'type' => 'people', 'id' => '9' } }, 'comments' => { 'data' => [ { 'type' => 'comments', 'id' => '1' }, { 'type' => 'comments', 'id' => '2' } ] } } } } @params = ActionController::Parameters.new(@hash) @expected = { id: 'zorglub', title: 'Ember Hamster', src: 'http://example.com/images/productivity.png', author_id: nil, photographer_id: '9', comment_ids: %w(1 2) } @illformed_payloads = [nil, {}, { 'data' => nil }, { 'data' => { 'attributes' => [] } }, { 'data' => { 'relationships' => [] } }, { 'data' => { 'relationships' => { 'rel' => nil } } }, { 'data' => { 'relationships' => { 'rel' => {} } } }] end def test_hash parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash) assert_equal(@expected, parsed_hash) end def test_actioncontroller_parameters assert_equal(false, @params.permitted?) parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@params) assert_equal(@expected, parsed_hash) end def test_illformed_payloads_safe @illformed_payloads.each do |p| parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse(p) assert_equal({}, parsed_hash) end end def test_illformed_payloads_unsafe @illformed_payloads.each do |p| assert_raises(InvalidDocument) do ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(p) end end end def test_filter_fields_only parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, only: [:id, :title, :author]) expected = { id: 'zorglub', title: 'Ember Hamster', author_id: nil } assert_equal(expected, parsed_hash) end def test_filter_fields_except parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, except: [:id, :title, :author]) expected = { src: 'http://example.com/images/productivity.png', photographer_id: '9', comment_ids: %w(1 2) } assert_equal(expected, parsed_hash) end def test_keys parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, keys: { author: :user, title: :post_title }) expected = { id: 'zorglub', post_title: 'Ember Hamster', src: 'http://example.com/images/productivity.png', user_id: nil, photographer_id: '9', comment_ids: %w(1 2) } assert_equal(expected, parsed_hash) end def test_polymorphic parsed_hash = ActiveModelSerializers::Adapter::JsonApi::Deserialization.parse!(@hash, polymorphic: [:photographer]) expected = { id: 'zorglub', title: 'Ember Hamster', src: 'http://example.com/images/productivity.png', author_id: nil, photographer_id: '9', photographer_type: 'Person', comment_ids: %w(1 2) } assert_equal(expected, parsed_hash) end end end end end end active_model_serializers-0.10.10/test/adapter/json_api/relationship_test.rb000066400000000000000000000321161351232231100272020ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi class RelationshipTest < ActiveSupport::TestCase def test_relationship_with_data expected = { data: { id: '1', type: 'blogs' } } model_attributes = { blog: Blog.new(id: 1) } relationship_name = :blog model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_one :blog end assert_equal(expected, actual) end def test_relationship_with_nil_model expected = { data: nil } model_attributes = { blog: nil } relationship_name = :blog model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_one :blog end assert_equal(expected, actual) end def test_relationship_with_nil_model_and_belongs_to_id_on_self original_config = ActiveModelSerializers.config.jsonapi_use_foreign_key_on_belongs_to_relationship ActiveModelSerializers.config.jsonapi_use_foreign_key_on_belongs_to_relationship = true expected = { data: nil } model_attributes = { blog: nil } relationship_name = :blog model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do belongs_to :blog end assert_equal(expected, actual) ensure ActiveModelSerializers.config.jsonapi_use_foreign_key_on_belongs_to_relationship = original_config end def test_relationship_with_data_array expected = { data: [ { id: '1', type: 'posts' }, { id: '2', type: 'posts' } ] } model_attributes = { posts: [Post.new(id: 1), Post.new(id: 2)] } relationship_name = :posts model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_many :posts end assert_equal(expected, actual) end def test_relationship_data_not_included expected = { meta: {} } model_attributes = { blog: :does_not_matter } relationship_name = :blog model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_one :blog do include_data false end end assert_equal(expected, actual) end def test_relationship_many_links expected = { links: { self: 'a link', related: 'another link' } } model_attributes = { blog: :does_not_matter } relationship_name = :blog model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_one :blog do include_data false link :self, 'a link' link :related, 'another link' end end assert_equal(expected, actual) end def test_relationship_block_link_with_meta expected = { links: { self: { href: '1', meta: { id: 1 } } } } model_attributes = { blog: Blog.new(id: 1) } relationship_name = :blog model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_one :blog do include_data false link :self do href object.blog.id.to_s meta(id: object.blog.id) end end end assert_equal(expected, actual) end def test_relationship_simple_meta expected = { meta: { id: '1' } } model_attributes = { blog: Blog.new(id: 1) } relationship_name = :blog model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_one :blog do include_data false meta(id: object.blog.id.to_s) end end assert_equal(expected, actual) end def test_relationship_block_meta expected = { meta: { id: 1 } } model_attributes = { blog: Blog.new(id: 1) } relationship_name = :blog model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_one :blog do include_data false meta(id: object.blog.id) end end assert_equal(expected, actual) end def test_relationship_simple_link expected = { data: { id: '1337', type: 'bios' }, links: { self: '//example.com/link_author/relationships/bio' } } model_attributes = { bio: Bio.new(id: 1337) } relationship_name = :bio model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_one :bio do link :self, '//example.com/link_author/relationships/bio' end end assert_equal(expected, actual) end def test_relationship_block_link expected = { data: { id: '1337', type: 'profiles' }, links: { related: '//example.com/profiles/1337' } } model_attributes = { profile: Profile.new(id: 1337) } relationship_name = :profile model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_one :profile do id = object.profile.id link :related do "//example.com/profiles/#{id}" if id != 123 end end end assert_equal(expected, actual) end def test_relationship_with_everything expected = { data: [{ id: '1337', type: 'likes' }], links: { related: { href: '//example.com/likes/1337', meta: { ids: '1337' } } }, meta: { liked: true } } model_attributes = { likes: [Like.new(id: 1337)] } relationship_name = :likes model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_many :likes do link :related do ids = object.likes.map(&:id).join(',') href "//example.com/likes/#{ids}" meta ids: ids end meta liked: object.likes.any? end end assert_equal(expected, actual) end def test_relationship_nil_link expected = { data: { id: '123', type: 'profiles' } } model_attributes = { profile: Profile.new(id: 123) } relationship_name = :profile model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_one :profile do id = object.profile.id link :related do "//example.com/profiles/#{id}" if id != 123 end end end assert_equal(expected, actual) end def test_relationship_block_link_href expected = { data: [{ id: '1337', type: 'locations' }], links: { related: { href: '//example.com/locations/1337' } } } model_attributes = { locations: [Location.new(id: 1337)] } relationship_name = :locations model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_many :locations do link :related do ids = object.locations.map(&:id).join(',') href "//example.com/locations/#{ids}" end end end assert_equal(expected, actual) end def test_relationship_block_link_href_and_meta expected = { data: [{ id: '1337', type: 'posts' }], links: { related: { href: '//example.com/posts/1337', meta: { ids: '1337' } } } } model_attributes = { posts: [Post.new(id: 1337, comments: [], author: nil)] } relationship_name = :posts model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_many :posts do link :related do ids = object.posts.map(&:id).join(',') href "//example.com/posts/#{ids}" meta ids: ids end end end assert_equal(expected, actual) end def test_relationship_block_link_meta expected = { data: [{ id: '1337', type: 'comments' }], links: { self: { meta: { ids: [1] } } } } model_attributes = { comments: [Comment.new(id: 1337)] } relationship_name = :comments model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_many :comments do link :self do meta ids: [1] end end end assert_equal(expected, actual) end def test_relationship_meta expected = { data: [{ id: 'from-serializer-method', type: 'roles' }], meta: { count: 1 } } model_attributes = { roles: [Role.new(id: 'from-record')] } relationship_name = :roles model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_many :roles do |serializer| meta count: object.roles.count serializer.cached_roles end def cached_roles [ Role.new(id: 'from-serializer-method') ] end end assert_equal(expected, actual) end def test_relationship_not_including_data expected = { links: { self: '//example.com/link_author/relationships/blog' } } model_attributes = { blog: Object } relationship_name = :blog model = new_model(model_attributes) model.define_singleton_method(:read_attribute_for_serialization) do |attr| fail 'should not be called' if attr == :blog super(attr) end assert_nothing_raised do actual = build_serializer_and_serialize_relationship(model, relationship_name) do has_one :blog do link :self, '//example.com/link_author/relationships/blog' include_data false end end assert_equal(expected, actual) end end def test_relationship_including_data_explicit expected = { data: { id: '1337', type: 'authors' }, meta: { name: 'Dan Brown' } } model_attributes = { reviewer: Author.new(id: 1337) } relationship_name = :reviewer model = new_model(model_attributes) actual = build_serializer_and_serialize_relationship(model, relationship_name) do belongs_to :reviewer do meta name: 'Dan Brown' include_data true end end assert_equal(expected, actual) end private def build_serializer_and_serialize_relationship(model, relationship_name, &block) serializer_class = Class.new(ActiveModel::Serializer, &block) hash = serializable(model, serializer: serializer_class, adapter: :json_api).serializable_hash hash[:data][:relationships][relationship_name] end def new_model(model_attributes) Class.new(ActiveModelSerializers::Model) do attributes(*model_attributes.keys) def self.name 'TestModel' end end.new(model_attributes) end end end end end active_model_serializers-0.10.10/test/adapter/json_api/resource_meta_test.rb000066400000000000000000000060351351232231100273370ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer module Adapter class JsonApi class ResourceMetaTest < Minitest::Test class MetaHashPostSerializer < ActiveModel::Serializer attributes :id meta stuff: 'value' end class MetaBlockPostSerializer < ActiveModel::Serializer attributes :id meta do { comments_count: object.comments.count } end end class MetaBlockPostBlankMetaSerializer < ActiveModel::Serializer attributes :id meta do {} end end class MetaBlockPostEmptyStringSerializer < ActiveModel::Serializer attributes :id meta do '' end end def setup @post = Post.new(id: 1337, comments: [], author: nil) end def test_meta_hash_object_resource hash = ActiveModelSerializers::SerializableResource.new( @post, serializer: MetaHashPostSerializer, adapter: :json_api ).serializable_hash expected = { stuff: 'value' } assert_equal(expected, hash[:data][:meta]) end def test_meta_block_object_resource hash = ActiveModelSerializers::SerializableResource.new( @post, serializer: MetaBlockPostSerializer, adapter: :json_api ).serializable_hash expected = { :"comments-count" => @post.comments.count } assert_equal(expected, hash[:data][:meta]) end def test_meta_object_resource_in_array post2 = Post.new(id: 1339, comments: [Comment.new]) posts = [@post, post2] hash = ActiveModelSerializers::SerializableResource.new( posts, each_serializer: MetaBlockPostSerializer, adapter: :json_api ).serializable_hash expected = { data: [ { id: '1337', type: 'posts', meta: { :"comments-count" => 0 } }, { id: '1339', type: 'posts', meta: { :"comments-count" => 1 } } ] } assert_equal(expected, hash) end def test_meta_object_blank_omitted hash = ActiveModelSerializers::SerializableResource.new( @post, serializer: MetaBlockPostBlankMetaSerializer, adapter: :json_api ).serializable_hash refute hash[:data].key? :meta end def test_meta_object_empty_string_omitted hash = ActiveModelSerializers::SerializableResource.new( @post, serializer: MetaBlockPostEmptyStringSerializer, adapter: :json_api ).serializable_hash refute hash[:data].key? :meta end end end end end end active_model_serializers-0.10.10/test/adapter/json_api/toplevel_jsonapi_test.rb000066400000000000000000000051641351232231100300610ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi class TopLevelJsonApiTest < ActiveSupport::TestCase def setup @author = Author.new(id: 1, name: 'Steve K.') @author.bio = nil @author.roles = [] @blog = Blog.new(id: 23, name: 'AMS Blog') @post = Post.new(id: 42, title: 'New Post', body: 'Body') @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @post.comments = [@comment] @post.blog = @blog @anonymous_post.comments = [] @anonymous_post.blog = nil @comment.post = @post @comment.author = nil @post.author = @author @anonymous_post.author = nil @blog = Blog.new(id: 1, name: 'My Blog!!') @blog.writer = @author @blog.articles = [@post, @anonymous_post] @author.posts = [] end def test_toplevel_jsonapi_defaults_to_false assert_equal config.fetch(:jsonapi_include_toplevel_object), false end def test_disable_toplevel_jsonapi with_config(jsonapi_include_toplevel_object: false) do hash = serialize(@post) assert_nil(hash[:jsonapi]) end end def test_enable_toplevel_jsonapi with_config(jsonapi_include_toplevel_object: true) do hash = serialize(@post) refute_nil(hash[:jsonapi]) end end def test_default_toplevel_jsonapi_version with_config(jsonapi_include_toplevel_object: true) do hash = serialize(@post) assert_equal('1.0', hash[:jsonapi][:version]) end end def test_toplevel_jsonapi_no_meta with_config(jsonapi_include_toplevel_object: true) do hash = serialize(@post) assert_nil(hash[:jsonapi][:meta]) end end def test_toplevel_jsonapi_meta new_config = { jsonapi_include_toplevel_object: true, jsonapi_toplevel_meta: { 'copyright' => 'Copyright 2015 Example Corp.' } } with_config(new_config) do hash = serialize(@post) assert_equal(new_config[:jsonapi_toplevel_meta], hash.fetch(:jsonapi).fetch(:meta)) end end private def serialize(resource, options = {}) serializable(resource, { adapter: :json_api }.merge!(options)).serializable_hash end end end end end active_model_serializers-0.10.10/test/adapter/json_api/transform_test.rb000066400000000000000000000503441351232231100265170ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi class KeyCaseTest < ActiveSupport::TestCase class Post < ::Model attributes :title, :body, :publish_at associations :author, :comments end class Author < ::Model attributes :first_name, :last_name end class Comment < ::Model attributes :body associations :author, :post end class PostSerializer < ActiveModel::Serializer type 'posts' attributes :title, :body, :publish_at belongs_to :author has_many :comments link(:self) { post_url(object.id) } link(:post_authors) { post_authors_url(object.id) } link(:subscriber_comments) { post_comments_url(object.id) } meta do { rating: 5, favorite_count: 10 } end end class AuthorSerializer < ActiveModel::Serializer type 'authors' attributes :first_name, :last_name end class CommentSerializer < ActiveModel::Serializer type 'comments' attributes :body belongs_to :author end def mock_request(transform = nil) context = Minitest::Mock.new context.expect(:request_url, URI) context.expect(:query_parameters, {}) context.expect(:url_helpers, Rails.application.routes.url_helpers) @options = {} @options[:key_transform] = transform if transform @options[:serialization_context] = context end def setup Rails.application.routes.draw do resources :posts do resources :authors resources :comments end end @publish_at = 1.day.from_now @author = Author.new(id: 1, first_name: 'Bob', last_name: 'Jones') @comment1 = Comment.new(id: 7, body: 'cool', author: @author) @comment2 = Comment.new(id: 12, body: 'awesome', author: @author) @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', author: @author, comments: [@comment1, @comment2], publish_at: @publish_at) @comment1.post = @post @comment2.post = @post end def test_success_document_transform_default mock_request serializer = PostSerializer.new(@post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) result = adapter.serializable_hash assert_equal({ data: { id: '1337', type: 'posts', attributes: { title: 'Title 1', body: 'Body 1', :"publish-at" => @publish_at }, relationships: { author: { data: { id: '1', type: 'authors' } }, comments: { data: [ { id: '7', type: 'comments' }, { id: '12', type: 'comments' } ] } }, links: { self: 'http://example.com/posts/1337', :"post-authors" => 'http://example.com/posts/1337/authors', :"subscriber-comments" => 'http://example.com/posts/1337/comments' }, meta: { rating: 5, :"favorite-count" => 10 } } }, result) end def test_success_document_transform_global_config mock_request result = with_config(key_transform: :camel_lower) do serializer = PostSerializer.new(@post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) adapter.serializable_hash end assert_equal({ data: { id: '1337', type: 'posts', attributes: { title: 'Title 1', body: 'Body 1', publishAt: @publish_at }, relationships: { author: { data: { id: '1', type: 'authors' } }, comments: { data: [ { id: '7', type: 'comments' }, { id: '12', type: 'comments' } ] } }, links: { self: 'http://example.com/posts/1337', postAuthors: 'http://example.com/posts/1337/authors', subscriberComments: 'http://example.com/posts/1337/comments' }, meta: { rating: 5, favoriteCount: 10 } } }, result) end def test_success_doc_transform_serialization_ctx_overrides_global mock_request(:camel) result = with_config(key_transform: :camel_lower) do serializer = PostSerializer.new(@post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) adapter.serializable_hash end assert_equal({ Data: { Id: '1337', Type: 'Posts', Attributes: { Title: 'Title 1', Body: 'Body 1', PublishAt: @publish_at }, Relationships: { Author: { Data: { Id: '1', Type: 'Authors' } }, Comments: { Data: [ { Id: '7', Type: 'Comments' }, { Id: '12', Type: 'Comments' } ] } }, Links: { Self: 'http://example.com/posts/1337', PostAuthors: 'http://example.com/posts/1337/authors', SubscriberComments: 'http://example.com/posts/1337/comments' }, Meta: { Rating: 5, FavoriteCount: 10 } } }, result) end def test_success_document_transform_dash mock_request(:dash) serializer = PostSerializer.new(@post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) result = adapter.serializable_hash assert_equal({ data: { id: '1337', type: 'posts', attributes: { title: 'Title 1', body: 'Body 1', :"publish-at" => @publish_at }, relationships: { author: { data: { id: '1', type: 'authors' } }, comments: { data: [ { id: '7', type: 'comments' }, { id: '12', type: 'comments' } ] } }, links: { self: 'http://example.com/posts/1337', :"post-authors" => 'http://example.com/posts/1337/authors', :"subscriber-comments" => 'http://example.com/posts/1337/comments' }, meta: { rating: 5, :"favorite-count" => 10 } } }, result) end def test_success_document_transform_unaltered mock_request(:unaltered) serializer = PostSerializer.new(@post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) result = adapter.serializable_hash assert_equal({ data: { id: '1337', type: 'posts', attributes: { title: 'Title 1', body: 'Body 1', publish_at: @publish_at }, relationships: { author: { data: { id: '1', type: 'authors' } }, comments: { data: [ { id: '7', type: 'comments' }, { id: '12', type: 'comments' } ] } }, links: { self: 'http://example.com/posts/1337', post_authors: 'http://example.com/posts/1337/authors', subscriber_comments: 'http://example.com/posts/1337/comments' }, meta: { rating: 5, favorite_count: 10 } } }, result) end def test_success_document_transform_undefined mock_request(:zoot) serializer = PostSerializer.new(@post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) exception = assert_raises NoMethodError do adapter.serializable_hash end assert_match(/undefined method.*zoot/, exception.message) end def test_success_document_transform_camel mock_request(:camel) serializer = PostSerializer.new(@post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) result = adapter.serializable_hash assert_equal({ Data: { Id: '1337', Type: 'Posts', Attributes: { Title: 'Title 1', Body: 'Body 1', PublishAt: @publish_at }, Relationships: { Author: { Data: { Id: '1', Type: 'Authors' } }, Comments: { Data: [ { Id: '7', Type: 'Comments' }, { Id: '12', Type: 'Comments' } ] } }, Links: { Self: 'http://example.com/posts/1337', PostAuthors: 'http://example.com/posts/1337/authors', SubscriberComments: 'http://example.com/posts/1337/comments' }, Meta: { Rating: 5, FavoriteCount: 10 } } }, result) end def test_success_document_transform_camel_lower mock_request(:camel_lower) serializer = PostSerializer.new(@post) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) result = adapter.serializable_hash assert_equal({ data: { id: '1337', type: 'posts', attributes: { title: 'Title 1', body: 'Body 1', publishAt: @publish_at }, relationships: { author: { data: { id: '1', type: 'authors' } }, comments: { data: [ { id: '7', type: 'comments' }, { id: '12', type: 'comments' } ] } }, links: { self: 'http://example.com/posts/1337', postAuthors: 'http://example.com/posts/1337/authors', subscriberComments: 'http://example.com/posts/1337/comments' }, meta: { rating: 5, favoriteCount: 10 } } }, result) end def test_error_document_transform_default mock_request resource = ModelWithErrors.new resource.errors.add(:published_at, 'must be in the future') resource.errors.add(:title, 'must be longer') serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) result = adapter.serializable_hash expected_errors_object = { errors: [ { source: { pointer: '/data/attributes/published-at' }, detail: 'must be in the future' }, { source: { pointer: '/data/attributes/title' }, detail: 'must be longer' } ] } assert_equal expected_errors_object, result end def test_error_document_transform_global_config mock_request result = with_config(key_transform: :camel) do resource = ModelWithErrors.new resource.errors.add(:published_at, 'must be in the future') resource.errors.add(:title, 'must be longer') serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) adapter.serializable_hash end expected_errors_object = { Errors: [ { Source: { Pointer: '/data/attributes/PublishedAt' }, Detail: 'must be in the future' }, { Source: { Pointer: '/data/attributes/Title' }, Detail: 'must be longer' } ] } assert_equal expected_errors_object, result end def test_error_document_transform_serialization_ctx_overrides_global mock_request(:camel) result = with_config(key_transform: :camel_lower) do resource = ModelWithErrors.new resource.errors.add(:published_at, 'must be in the future') resource.errors.add(:title, 'must be longer') serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) adapter.serializable_hash end expected_errors_object = { Errors: [ { Source: { Pointer: '/data/attributes/PublishedAt' }, Detail: 'must be in the future' }, { Source: { Pointer: '/data/attributes/Title' }, Detail: 'must be longer' } ] } assert_equal expected_errors_object, result end def test_error_document_transform_dash mock_request(:dash) resource = ModelWithErrors.new resource.errors.add(:published_at, 'must be in the future') resource.errors.add(:title, 'must be longer') serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) result = adapter.serializable_hash expected_errors_object = { errors: [ { source: { pointer: '/data/attributes/published-at' }, detail: 'must be in the future' }, { source: { pointer: '/data/attributes/title' }, detail: 'must be longer' } ] } assert_equal expected_errors_object, result end def test_error_document_transform_unaltered mock_request(:unaltered) resource = ModelWithErrors.new resource.errors.add(:published_at, 'must be in the future') resource.errors.add(:title, 'must be longer') serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) result = adapter.serializable_hash expected_errors_object = { errors: [ { source: { pointer: '/data/attributes/published_at' }, detail: 'must be in the future' }, { source: { pointer: '/data/attributes/title' }, detail: 'must be longer' } ] } assert_equal expected_errors_object, result end def test_error_document_transform_undefined mock_request(:krazy) resource = ModelWithErrors.new resource.errors.add(:published_at, 'must be in the future') resource.errors.add(:title, 'must be longer') serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) exception = assert_raises NoMethodError do adapter.serializable_hash end assert_match(/undefined method.*krazy/, exception.message) end def test_error_document_transform_camel mock_request(:camel) resource = ModelWithErrors.new resource.errors.add(:published_at, 'must be in the future') resource.errors.add(:title, 'must be longer') serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) result = adapter.serializable_hash expected_errors_object = { Errors: [ { Source: { Pointer: '/data/attributes/PublishedAt' }, Detail: 'must be in the future' }, { Source: { Pointer: '/data/attributes/Title' }, Detail: 'must be longer' } ] } assert_equal expected_errors_object, result end def test_error_document_transform_camel_lower mock_request(:camel_lower) resource = ModelWithErrors.new resource.errors.add(:published_at, 'must be in the future') resource.errors.add(:title, 'must be longer') serializer = ActiveModel::Serializer::ErrorSerializer.new(resource) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options) result = adapter.serializable_hash expected_errors_object = { errors: [ { source: { pointer: '/data/attributes/publishedAt' }, detail: 'must be in the future' }, { source: { pointer: '/data/attributes/title' }, detail: 'must be longer' } ] } assert_equal expected_errors_object, result end end end end end active_model_serializers-0.10.10/test/adapter/json_api/type_test.rb000066400000000000000000000142051351232231100254610ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonApi class TypeTest < ActiveSupport::TestCase class StringTypeSerializer < ActiveModel::Serializer attribute :name type 'profile' end class SymbolTypeSerializer < ActiveModel::Serializer attribute :name type :profile end setup do @author = Author.new(id: 1, name: 'Steve K.') end def test_config_plural with_jsonapi_inflection :plural do assert_type(@author, 'authors') end end def test_config_singular with_jsonapi_inflection :singular do assert_type(@author, 'author') end end def test_explicit_string_type_value assert_type(@author, 'profile', serializer: StringTypeSerializer) end def test_explicit_symbol_type_value assert_type(@author, 'profile', serializer: SymbolTypeSerializer) end private def assert_type(resource, expected_type, opts = {}) opts = opts.reverse_merge(adapter: :json_api) hash = serializable(resource, opts).serializable_hash assert_equal(expected_type, hash.fetch(:data).fetch(:type)) end end class ResourceIdentifierTest < ActiveSupport::TestCase class WithDefinedTypeSerializer < ActiveModel::Serializer type 'with_defined_types' end class WithDefinedIdSerializer < ActiveModel::Serializer def id 'special_id' end end class FragmentedSerializer < ActiveModel::Serializer cache only: :id def id 'special_id' end end setup do @model = Author.new(id: 1, name: 'Steve K.') ActionController::Base.cache_store.clear end def test_defined_type actual = with_jsonapi_inflection :plural do actual_resource_identifier_object(WithDefinedTypeSerializer, @model) end expected = { id: expected_model_id(@model), type: 'with-defined-types' } assert_equal actual, expected end def test_defined_type_not_inflected actual = with_jsonapi_inflection :singular do actual_resource_identifier_object(WithDefinedTypeSerializer, @model) end expected = { id: expected_model_id(@model), type: 'with-defined-types' } assert_equal actual, expected end def test_singular_type actual = with_jsonapi_inflection :singular do actual_resource_identifier_object(AuthorSerializer, @model) end expected = { id: expected_model_id(@model), type: 'author' } assert_equal actual, expected end def test_plural_type actual = with_jsonapi_inflection :plural do actual_resource_identifier_object(AuthorSerializer, @model) end expected = { id: expected_model_id(@model), type: 'authors' } assert_equal actual, expected end def test_type_with_namespace Object.const_set(:Admin, Module.new) model = Class.new(::Model) Admin.const_set(:PowerUser, model) serializer = Class.new(ActiveModel::Serializer) Admin.const_set(:PowerUserSerializer, serializer) with_namespace_separator '--' do admin_user = Admin::PowerUser.new serializer = Admin::PowerUserSerializer.new(admin_user) expected = { id: admin_user.id, type: 'admin--power-users' } identifier = ResourceIdentifier.new(serializer, {}) actual = identifier.as_json assert_equal(expected, actual) end end def test_id_defined_on_object actual = actual_resource_identifier_object(AuthorSerializer, @model) expected = { id: @model.id.to_s, type: expected_model_type(@model) } assert_equal actual, expected end def test_blank_id model = Author.new(id: nil, name: 'Steve K.') actual = actual_resource_identifier_object(AuthorSerializer, model) expected = { type: expected_model_type(model) } assert_equal actual, expected end def test_for_type_with_id id = 1 actual = ResourceIdentifier.for_type_with_id('admin_user', id, {}) expected = { id: '1', type: 'admin-users' } assert_equal actual, expected end def test_for_type_with_id_given_blank_id id = '' actual = ResourceIdentifier.for_type_with_id('admin_user', id, {}) assert_nil actual end def test_for_type_with_id_inflected id = 2 actual = with_jsonapi_inflection :singular do ResourceIdentifier.for_type_with_id('admin_users', id, {}) end expected = { id: '2', type: 'admin-user' } assert_equal actual, expected end def test_id_defined_on_serializer actual = actual_resource_identifier_object(WithDefinedIdSerializer, @model) expected = { id: 'special_id', type: expected_model_type(@model) } assert_equal actual, expected end def test_id_defined_on_fragmented actual = actual_resource_identifier_object(FragmentedSerializer, @model) expected = { id: 'special_id', type: expected_model_type(@model) } assert_equal actual, expected end private def actual_resource_identifier_object(serializer_class, model) serializer = serializer_class.new(model) resource_identifier = ResourceIdentifier.new(serializer, nil) resource_identifier.as_json end def expected_model_type(model, inflection = ActiveModelSerializers.config.jsonapi_resource_type) with_jsonapi_inflection inflection do model.class.model_name.send(inflection) end end def expected_model_id(model) model.id.to_s end end end end end active_model_serializers-0.10.10/test/adapter/json_test.rb000066400000000000000000000032441351232231100236500ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class JsonTest < ActiveSupport::TestCase def setup ActionController::Base.cache_store.clear @author = Author.new(id: 1, name: 'Steve K.') @post = Post.new(id: 1, title: 'New Post', body: 'Body') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment] @first_comment.post = @post @second_comment.post = @post @post.author = @author @blog = Blog.new(id: 1, name: 'My Blog!!') @post.blog = @blog @serializer = PostSerializer.new(@post) @adapter = ActiveModelSerializers::Adapter::Json.new(@serializer) end def test_has_many assert_equal([ { id: 1, body: 'ZOMG A COMMENT' }, { id: 2, body: 'ZOMG ANOTHER COMMENT' } ], @adapter.serializable_hash[:post][:comments]) end def test_custom_keys serializer = PostWithCustomKeysSerializer.new(@post) adapter = ActiveModelSerializers::Adapter::Json.new(serializer) assert_equal({ id: 1, reviews: [ { id: 1, body: 'ZOMG A COMMENT' }, { id: 2, body: 'ZOMG ANOTHER COMMENT' } ], writer: { id: 1, name: 'Steve K.' }, site: { id: 1, name: 'My Blog!!' } }, adapter.serializable_hash[:post]) end end end end active_model_serializers-0.10.10/test/adapter/null_test.rb000066400000000000000000000010751351232231100236510ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers module Adapter class NullTest < ActiveSupport::TestCase def setup profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') serializer = ProfileSerializer.new(profile) @adapter = Null.new(serializer) end def test_serializable_hash assert_equal({}, @adapter.serializable_hash) end def test_it_returns_empty_json assert_equal('{}', @adapter.to_json) end end end end active_model_serializers-0.10.10/test/adapter/polymorphic_test.rb000066400000000000000000000137241351232231100252500ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer module Adapter class PolymorphicTest < ActiveSupport::TestCase setup do @employee = Employee.new(id: 42, name: 'Zoop Zoopler', email: 'zoop@example.com') @picture = @employee.pictures.new(id: 1, title: 'headshot-1.jpg') @picture.imageable = @employee end def serialization(resource, adapter = :attributes) serializable(resource, adapter: adapter, serializer: PolymorphicBelongsToSerializer).as_json end def tag_serialization(adapter = :attributes) tag = PolyTag.new(id: 1, phrase: 'foo') tag.object_tags << ObjectTag.new(id: 1, poly_tag_id: 1, taggable: @employee) tag.object_tags << ObjectTag.new(id: 5, poly_tag_id: 1, taggable: @picture) serializable(tag, adapter: adapter, serializer: PolymorphicTagSerializer, include: '*.*').as_json end def test_attributes_serialization expected = { id: 1, title: 'headshot-1.jpg', imageable: { type: 'employee', employee: { id: 42, name: 'Zoop Zoopler' } } } assert_equal(expected, serialization(@picture)) end def test_attributes_serialization_without_polymorphic_association expected = { id: 2, title: 'headshot-2.jpg', imageable: nil } simple_picture = Picture.new(id: 2, title: 'headshot-2.jpg') assert_equal(expected, serialization(simple_picture)) end def test_attributes_serialization_with_polymorphic_has_many expected = { id: 1, phrase: 'foo', object_tags: [ { id: 1, taggable: { type: 'employee', employee: { id: 42 } } }, { id: 5, taggable: { type: 'picture', picture: { id: 1 } } } ] } assert_equal(expected, tag_serialization) end def test_json_serialization expected = { picture: { id: 1, title: 'headshot-1.jpg', imageable: { type: 'employee', employee: { id: 42, name: 'Zoop Zoopler' } } } } assert_equal(expected, serialization(@picture, :json)) end def test_json_serialization_without_polymorphic_association expected = { picture: { id: 2, title: 'headshot-2.jpg', imageable: nil } } simple_picture = Picture.new(id: 2, title: 'headshot-2.jpg') assert_equal(expected, serialization(simple_picture, :json)) end def test_json_serialization_with_polymorphic_has_many expected = { poly_tag: { id: 1, phrase: 'foo', object_tags: [ { id: 1, taggable: { type: 'employee', employee: { id: 42 } } }, { id: 5, taggable: { type: 'picture', picture: { id: 1 } } } ] } } assert_equal(expected, tag_serialization(:json)) end def test_json_api_serialization expected = { data: { id: '1', type: 'pictures', attributes: { title: 'headshot-1.jpg' }, relationships: { imageable: { data: { id: '42', type: 'employees' } } } } } assert_equal(expected, serialization(@picture, :json_api)) end def test_json_api_serialization_with_polymorphic_belongs_to expected = { data: { id: '1', type: 'poly-tags', attributes: { phrase: 'foo' }, relationships: { :"object-tags" => { data: [ { id: '1', type: 'object-tags' }, { id: '5', type: 'object-tags' } ] } } }, included: [ { id: '1', type: 'object-tags', relationships: { taggable: { data: { id: '42', type: 'employees' } } } }, { id: '42', type: 'employees' }, { id: '5', type: 'object-tags', relationships: { taggable: { data: { id: '1', type: 'pictures' } } } }, { id: '1', type: 'pictures' } ] } assert_equal(expected, tag_serialization(:json_api)) end end end end end active_model_serializers-0.10.10/test/adapter_test.rb000066400000000000000000000046261351232231100227040ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers class AdapterTest < ActiveSupport::TestCase def setup profile = Profile.new @serializer = ProfileSerializer.new(profile) @adapter = ActiveModelSerializers::Adapter::Base.new(@serializer) end def test_serializable_hash_is_abstract_method assert_raises(NotImplementedError) do @adapter.serializable_hash(only: [:name]) end end def test_serialization_options_ensures_option_is_a_hash adapter = Class.new(ActiveModelSerializers::Adapter::Base) do def serializable_hash(options = nil) serialization_options(options) end end.new(@serializer) assert_equal({}, adapter.serializable_hash(nil)) assert_equal({}, adapter.serializable_hash({})) ensure ActiveModelSerializers::Adapter.adapter_map.delete_if { |k, _| k =~ /class/ } end def test_serialization_options_ensures_option_is_one_of_valid_options adapter = Class.new(ActiveModelSerializers::Adapter::Base) do def serializable_hash(options = nil) serialization_options(options) end end.new(@serializer) filtered_options = { now: :see_me, then: :not } valid_options = ActiveModel::Serializer::SERIALIZABLE_HASH_VALID_KEYS.each_with_object({}) do |option, result| result[option] = option end assert_equal(valid_options, adapter.serializable_hash(filtered_options.merge(valid_options))) ensure ActiveModelSerializers::Adapter.adapter_map.delete_if { |k, _| k =~ /class/ } end def test_serializer assert_equal @serializer, @adapter.serializer end def test_create_adapter adapter = ActiveModelSerializers::Adapter.create(@serializer) assert_equal ActiveModelSerializers::Adapter::Attributes, adapter.class end def test_create_adapter_with_override adapter = ActiveModelSerializers::Adapter.create(@serializer, adapter: :json_api) assert_equal ActiveModelSerializers::Adapter::JsonApi, adapter.class end def test_inflected_adapter_class_for_known_adapter ActiveSupport::Inflector.inflections(:en) { |inflect| inflect.acronym 'API' } klass = ActiveModelSerializers::Adapter.adapter_class(:json_api) ActiveSupport::Inflector.inflections.acronyms.clear assert_equal ActiveModelSerializers::Adapter::JsonApi, klass end end end active_model_serializers-0.10.10/test/array_serializer_test.rb000066400000000000000000000010621351232231100246220ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' require_relative 'collection_serializer_test' module ActiveModel class Serializer class ArraySerializerTest < CollectionSerializerTest extend Minitest::Assertions def self.run_one_method(*) _, stderr = capture_io do super end if stderr !~ /NOTE: ActiveModel::Serializer::ArraySerializer.new is deprecated/ fail Minitest::Assertion, stderr end end def collection_serializer ArraySerializer end end end end active_model_serializers-0.10.10/test/benchmark/000077500000000000000000000000001351232231100216225ustar00rootroot00000000000000active_model_serializers-0.10.10/test/benchmark/app.rb000066400000000000000000000035461351232231100227370ustar00rootroot00000000000000# frozen_string_literal: true # https://github.com/rails-api/active_model_serializers/pull/872 # approx ref 792fb8a9053f8db3c562dae4f40907a582dd1720 to test against require 'bundler/setup' require 'rails' require 'active_model' require 'active_support' require 'active_support/json' require 'action_controller' require 'action_controller/test_case' require 'action_controller/railtie' abort "Rails application already defined: #{Rails.application.class}" if Rails.application class NullLogger < Logger def initialize(*_args) end def add(*_args, &_block) end end class BenchmarkLogger < ActiveSupport::Logger def initialize @file = StringIO.new super(@file) end def messages @file.rewind @file.read end end # ref: https://gist.github.com/bf4/8744473 class BenchmarkApp < Rails::Application # Set up production configuration config.eager_load = true config.cache_classes = true # CONFIG: CACHE_ON={on,off} config.action_controller.perform_caching = ENV['CACHE_ON'] != 'off' config.action_controller.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) config.active_support.test_order = :random config.secret_token = 'S' * 30 config.secret_key_base = 'abc123' config.consider_all_requests_local = false # otherwise deadlock occurred config.middleware.delete 'Rack::Lock' # to disable log files config.logger = NullLogger.new config.active_support.deprecation = :log config.log_level = :info end require 'active_model_serializers' # Initialize app before any serializers are defined, for running across revisions. # ref: https://github.com/rails-api/active_model_serializers/pull/1478 Rails.application.initialize! # HACK: Serializer::cache depends on the ActionController-dependent configs being set. ActiveSupport.on_load(:action_controller) do require_relative 'fixtures' end require_relative 'controllers' active_model_serializers-0.10.10/test/benchmark/benchmarking_support.rb000066400000000000000000000033661351232231100264030ustar00rootroot00000000000000# frozen_string_literal: true require 'benchmark/ips' require 'json' # Add benchmarking runner from ruby-bench-suite # https://github.com/ruby-bench/ruby-bench-suite/blob/master/rails/benchmarks/support/benchmark_rails.rb module Benchmark module ActiveModelSerializers module TestMethods def request(method, path) response = Rack::MockRequest.new(BenchmarkApp).send(method, path) if response.status.in?([404, 500]) fail "omg, #{method}, #{path}, '#{response.status}', '#{response.body}'" end response end end # extend Benchmark with an `ams` method def ams(label = nil, time:, disable_gc: true, warmup: 3, &block) fail ArgumentError.new, 'block should be passed' unless block_given? if disable_gc GC.disable else GC.enable end report = Benchmark.ips(time, warmup, true) do |x| x.report(label) { yield } end entry = report.entries.first output = { label: label, version: ::ActiveModel::Serializer::VERSION.to_s, rails_version: ::Rails.version.to_s, iterations_per_second: entry.ips, iterations_per_second_standard_deviation: entry.error_percentage, total_allocated_objects_per_iteration: count_total_allocated_objects(&block) }.to_json puts output output end def count_total_allocated_objects if block_given? key = if RUBY_VERSION < '2.2' :total_allocated_object else :total_allocated_objects end before = GC.stat[key] yield after = GC.stat[key] after - before else -1 end end end extend Benchmark::ActiveModelSerializers end active_model_serializers-0.10.10/test/benchmark/bm_active_record.rb000066400000000000000000000043261351232231100254430ustar00rootroot00000000000000# frozen_string_literal: true require_relative './benchmarking_support' require_relative './app' time = 10 disable_gc = true # This is to disable any key transform effects that may impact performance ActiveModelSerializers.config.key_transform = :unaltered ########################################### # Setup active record models ########################################## require 'active_record' require 'sqlite3' # For debugging SQL output # ActiveRecord::Base.logger = Logger.new(STDERR) # Change the following to reflect your database settings ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ':memory:' ) # Don't show migration output when constructing fake db ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :authors, force: true do |t| t.string :name end create_table :posts, force: true do |t| t.text :body t.string :title t.references :author end create_table :profiles, force: true do |t| t.text :project_url t.text :bio t.date :birthday t.references :author end end class Author < ActiveRecord::Base has_one :profile has_many :posts end class Post < ActiveRecord::Base belongs_to :author end class Profile < ActiveRecord::Base belongs_to :author end # Build out the data to serialize author = Author.create(name: 'Preston Sego') Profile.create(project_url: 'https://github.com/NullVoxPopuli', author: author) 50.times do Post.create( body: 'something about how password restrictions are evil, and less secure, and with the math to prove it.', title: 'Your bank is does not know how to do security', author: author ) end Benchmark.ams('AR: attributes', time: time, disable_gc: disable_gc) do ActiveModelSerializers::SerializableResource.new(author, adapter: :attributes, include: 'profile,posts').serializable_hash end Benchmark.ams('AR: json', time: time, disable_gc: disable_gc) do ActiveModelSerializers::SerializableResource.new(author, adapter: :json, include: 'profile,posts').serializable_hash end Benchmark.ams('AR: JSON API', time: time, disable_gc: disable_gc) do ActiveModelSerializers::SerializableResource.new(author, adapter: :json_api, include: 'profile,posts').serializable_hash end active_model_serializers-0.10.10/test/benchmark/bm_adapter.rb000066400000000000000000000022101351232231100242400ustar00rootroot00000000000000# frozen_string_literal: true require_relative './benchmarking_support' require_relative './app' time = 10 disable_gc = true ActiveModelSerializers.config.key_transform = :unaltered has_many_relationships = (0..60).map do |i| HasManyRelationship.new(id: i, body: 'ZOMG A HAS MANY RELATIONSHIP') end has_one_relationship = HasOneRelationship.new( id: 42, first_name: 'Joao', last_name: 'Moura' ) primary_resource = PrimaryResource.new( id: 1337, title: 'New PrimaryResource', virtual_attribute: nil, body: 'Body', has_many_relationships: has_many_relationships, has_one_relationship: has_one_relationship ) serializer = PrimaryResourceSerializer.new(primary_resource) Benchmark.ams('attributes', time: time, disable_gc: disable_gc) do attributes = ActiveModelSerializers::Adapter::Attributes.new(serializer) attributes.as_json end Benchmark.ams('json_api', time: time, disable_gc: disable_gc) do json_api = ActiveModelSerializers::Adapter::JsonApi.new(serializer) json_api.as_json end Benchmark.ams('json', time: time, disable_gc: disable_gc) do json = ActiveModelSerializers::Adapter::Json.new(serializer) json.as_json end active_model_serializers-0.10.10/test/benchmark/bm_caching.rb000066400000000000000000000076751351232231100242400ustar00rootroot00000000000000# frozen_string_literal: true require_relative './benchmarking_support' require_relative './app' # https://github.com/ruby-bench/ruby-bench-suite/blob/8ad567f7e43a044ae48c36833218423bb1e2bd9d/rails/benchmarks/actionpack_router.rb class ApiAssertion include Benchmark::ActiveModelSerializers::TestMethods class BadRevisionError < StandardError; end def valid? caching = get_caching caching[:body].delete('meta') non_caching = get_non_caching non_caching[:body].delete('meta') assert_responses(caching, non_caching) rescue BadRevisionError => e msg = { error: e.message } STDERR.puts msg STDOUT.puts msg exit 1 end def get_status(on_off = 'on'.freeze) get("/status/#{on_off}") end def clear get('/clear') end def get_caching(on_off = 'on'.freeze) get("/caching/#{on_off}") end def get_fragment_caching(on_off = 'on'.freeze) get("/fragment_caching/#{on_off}") end def get_non_caching(on_off = 'on'.freeze) get("/non_caching/#{on_off}") end def debug(msg = '') if block_given? && ENV['DEBUG'] =~ /\Atrue|on|0\z/i STDERR.puts yield else STDERR.puts msg end end private def assert_responses(caching, non_caching) assert_equal(caching[:code], 200, "Caching response failed: #{caching}") assert_equal(caching[:body], expected, "Caching response format failed: \n+ #{caching[:body]}\n- #{expected}") assert_equal(caching[:content_type], 'application/json; charset=utf-8', "Caching response content type failed: \n+ #{caching[:content_type]}\n- application/json") assert_equal(non_caching[:code], 200, "Non caching response failed: #{non_caching}") assert_equal(non_caching[:body], expected, "Non Caching response format failed: \n+ #{non_caching[:body]}\n- #{expected}") assert_equal(non_caching[:content_type], 'application/json; charset=utf-8', "Non caching response content type failed: \n+ #{non_caching[:content_type]}\n- application/json") end def get(url) response = request(:get, url) { code: response.status, body: JSON.load(response.body), content_type: response.content_type } end def expected @expected ||= { 'primary_resource' => { 'id' => 1337, 'title' => 'New PrimaryResource', 'body' => 'Body', 'virtual_attribute' => { 'id' => 999, 'name' => 'Free-Range Virtual Attribute' }, 'has_one_relationship' => { 'id' => 42, 'first_name' => 'Joao', 'last_name' => 'Moura' }, 'has_many_relationships' => [ { 'id' => 1, 'body' => 'ZOMG A HAS MANY RELATIONSHIP' } ] } } end def assert_equal(expected, actual, message) return true if expected == actual if ENV['FAIL_ASSERTION'] =~ /\Atrue|on|0\z/i # rubocop:disable Style/GuardClause fail BadRevisionError, message else STDERR.puts message unless ENV['SUMMARIZE'] end end end assertion = ApiAssertion.new assertion.valid? assertion.debug { assertion.get_status } time = 10 { 'caching on: caching serializers: gc off' => { disable_gc: true, send: [:get_caching, 'on'] }, 'caching on: fragment caching serializers: gc off' => { disable_gc: true, send: [:get_fragment_caching, 'on'] }, 'caching on: non-caching serializers: gc off' => { disable_gc: true, send: [:get_non_caching, 'on'] }, 'caching off: caching serializers: gc off' => { disable_gc: true, send: [:get_caching, 'off'] }, 'caching off: fragment caching serializers: gc off' => { disable_gc: true, send: [:get_fragment_caching, 'off'] }, 'caching off: non-caching serializers: gc off' => { disable_gc: true, send: [:get_non_caching, 'off'] } }.each do |label, options| assertion.clear Benchmark.ams(label, time: time, disable_gc: options[:disable_gc]) do assertion.send(*options[:send]) end assertion.debug { assertion.get_status(options[:send][-1]) } end active_model_serializers-0.10.10/test/benchmark/bm_lookup_chain.rb000066400000000000000000000044411351232231100253030ustar00rootroot00000000000000# frozen_string_literal: true require_relative './benchmarking_support' require_relative './app' time = 10 disable_gc = true ActiveModelSerializers.config.key_transform = :unaltered module AmsBench module Api module V1 class PrimaryResourceSerializer < ActiveModel::Serializer attributes :title, :body has_many :has_many_relationships end class HasManyRelationshipSerializer < ActiveModel::Serializer attribute :body end end end class PrimaryResourceSerializer < ActiveModel::Serializer attributes :title, :body has_many :has_many_relationships class HasManyRelationshipSerializer < ActiveModel::Serializer attribute :body end end end resource = PrimaryResource.new( id: 1, title: 'title', body: 'body', has_many_relationships: [ HasManyRelationship.new(id: 1, body: 'body1'), HasManyRelationship.new(id: 2, body: 'body1') ] ) serialization = lambda do ActiveModelSerializers::SerializableResource.new(resource, serializer: AmsBench::PrimaryResourceSerializer).as_json ActiveModelSerializers::SerializableResource.new(resource, namespace: AmsBench::Api::V1).as_json ActiveModelSerializers::SerializableResource.new(resource).as_json end def clear_cache AmsBench::PrimaryResourceSerializer.serializers_cache.clear AmsBench::Api::V1::PrimaryResourceSerializer.serializers_cache.clear ActiveModel::Serializer.serializers_cache.clear end configurable = lambda do clear_cache Benchmark.ams('Configurable Lookup Chain', time: time, disable_gc: disable_gc, &serialization) end old = lambda do clear_cache module ActiveModel class Serializer def self.serializer_lookup_chain_for(klass, namespace = nil) chain = [] resource_class_name = klass.name.demodulize resource_namespace = klass.name.deconstantize serializer_class_name = "#{resource_class_name}Serializer" chain.push("#{namespace}::#{serializer_class_name}") if namespace chain.push("#{name}::#{serializer_class_name}") if self != ActiveModel::Serializer chain.push("#{resource_namespace}::#{serializer_class_name}") chain end end end Benchmark.ams('Old Lookup Chain (v0.10)', time: time, disable_gc: disable_gc, &serialization) end configurable.call old.call active_model_serializers-0.10.10/test/benchmark/bm_transform.rb000066400000000000000000000024701351232231100246430ustar00rootroot00000000000000# frozen_string_literal: true require_relative './benchmarking_support' require_relative './app' time = 10 disable_gc = true ActiveModelSerializers.config.key_transform = :unaltered has_many_relationships = (0..50).map do |i| HasManyRelationship.new(id: i, body: 'ZOMG A HAS MANY RELATIONSHIP') end has_one_relationship = HasOneRelationship.new( id: 42, first_name: 'Joao', last_name: 'Moura' ) primary_resource = PrimaryResource.new( id: 1337, title: 'New PrimaryResource', virtual_attribute: nil, body: 'Body', has_many_relationships: has_many_relationships, has_one_relationship: has_one_relationship ) serializer = PrimaryResourceSerializer.new(primary_resource) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) serialization = adapter.as_json Benchmark.ams('camel', time: time, disable_gc: disable_gc) do CaseTransform.camel(serialization) end Benchmark.ams('camel_lower', time: time, disable_gc: disable_gc) do CaseTransform.camel_lower(serialization) end Benchmark.ams('dash', time: time, disable_gc: disable_gc) do CaseTransform.dash(serialization) end Benchmark.ams('unaltered', time: time, disable_gc: disable_gc) do CaseTransform.unaltered(serialization) end Benchmark.ams('underscore', time: time, disable_gc: disable_gc) do CaseTransform.underscore(serialization) end active_model_serializers-0.10.10/test/benchmark/config.ru000066400000000000000000000001761351232231100234430ustar00rootroot00000000000000# frozen_string_literal: true require File.expand_path(['..', 'app'].join(File::SEPARATOR), __FILE__) run Rails.application active_model_serializers-0.10.10/test/benchmark/controllers.rb000066400000000000000000000065651351232231100245310ustar00rootroot00000000000000# frozen_string_literal: true class PrimaryResourceController < ActionController::Base PRIMARY_RESOURCE = begin if ENV['BENCH_STRESS'] has_many_relationships = (0..50).map do |i| HasManyRelationship.new(id: i, body: 'ZOMG A HAS MANY RELATIONSHIP') end else has_many_relationships = [HasManyRelationship.new(id: 1, body: 'ZOMG A HAS MANY RELATIONSHIP')] end has_one_relationship = HasOneRelationship.new(id: 42, first_name: 'Joao', last_name: 'Moura') PrimaryResource.new(id: 1337, title: 'New PrimaryResource', virtual_attribute: nil, body: 'Body', has_many_relationships: has_many_relationships, has_one_relationship: has_one_relationship) end def render_with_caching_serializer toggle_cache_status render json: PRIMARY_RESOURCE, serializer: CachingPrimaryResourceSerializer, adapter: :json, meta: { caching: perform_caching } end def render_with_fragment_caching_serializer toggle_cache_status render json: PRIMARY_RESOURCE, serializer: FragmentCachingPrimaryResourceSerializer, adapter: :json, meta: { caching: perform_caching } end def render_with_non_caching_serializer toggle_cache_status render json: PRIMARY_RESOURCE, adapter: :json, meta: { caching: perform_caching } end def render_cache_status toggle_cache_status # Uncomment to debug # STDERR.puts cache_store.class # STDERR.puts cache_dependencies # ActiveSupport::Cache::Store.logger.debug [ActiveModelSerializers.config.cache_store, ActiveModelSerializers.config.perform_caching, CachingPrimaryResourceSerializer._cache, perform_caching, params].inspect render json: { caching: perform_caching, meta: { cache_log: cache_messages, cache_status: cache_status } }.to_json end def clear ActionController::Base.cache_store.clear # Test caching is on # Uncomment to turn on logger; possible performance issue # logger = BenchmarkLogger.new # ActiveSupport::Cache::Store.logger = logger # seems to be the best way # # the below is used in some rails tests but isn't available/working in all versions, so far as I can tell # https://github.com/rails/rails/pull/15943 # ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args| # logger.debug ActiveSupport::Notifications::Event.new(*args) # end render json: 'ok'.to_json end private def cache_status { controller: perform_caching, app: Rails.configuration.action_controller.perform_caching, serializers: Rails.configuration.serializers.each_with_object({}) { |serializer, data| data[serializer.name] = serializer._cache.present? } } end def cache_messages ActiveSupport::Cache::Store.logger.is_a?(BenchmarkLogger) && ActiveSupport::Cache::Store.logger.messages.split("\n") end def toggle_cache_status case params[:on] when 'on'.freeze then self.perform_caching = true when 'off'.freeze then self.perform_caching = false else nil # no-op end end end Rails.application.routes.draw do get '/status(/:on)' => 'primary_resource#render_cache_status' get '/clear' => 'primary_resource#clear' get '/caching(/:on)' => 'primary_resource#render_with_caching_serializer' get '/fragment_caching(/:on)' => 'primary_resource#render_with_fragment_caching_serializer' get '/non_caching(/:on)' => 'primary_resource#render_with_non_caching_serializer' end active_model_serializers-0.10.10/test/benchmark/fixtures.rb000066400000000000000000000152631351232231100240270ustar00rootroot00000000000000# frozen_string_literal: true Rails.configuration.serializers = [] class HasOneRelationshipSerializer < ActiveModel::Serializer attributes :id, :first_name, :last_name has_many :primary_resources, embed: :ids has_one :bio end Rails.configuration.serializers << HasOneRelationshipSerializer class VirtualAttributeSerializer < ActiveModel::Serializer attributes :id, :name end Rails.configuration.serializers << VirtualAttributeSerializer class HasManyRelationshipSerializer < ActiveModel::Serializer attributes :id, :body belongs_to :primary_resource belongs_to :has_one_relationship end Rails.configuration.serializers << HasManyRelationshipSerializer class PrimaryResourceSerializer < ActiveModel::Serializer attributes :id, :title, :body has_many :has_many_relationships, serializer: HasManyRelationshipSerializer belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer belongs_to :has_one_relationship, serializer: HasOneRelationshipSerializer link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' } meta do { rating: 5, favorite_count: 10 } end def virtual_attribute VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute') end end Rails.configuration.serializers << PrimaryResourceSerializer class CachingHasOneRelationshipSerializer < HasOneRelationshipSerializer cache key: 'writer', skip_digest: true end Rails.configuration.serializers << CachingHasOneRelationshipSerializer class CachingHasManyRelationshipSerializer < HasManyRelationshipSerializer cache expires_in: 1.day, skip_digest: true end Rails.configuration.serializers << CachingHasManyRelationshipSerializer # see https://github.com/rails-api/active_model_serializers/pull/1690/commits/68715b8f99bc29677e8a47bb3f305f23c077024b#r60344532 class CachingPrimaryResourceSerializer < ActiveModel::Serializer cache key: 'primary_resource', expires_in: 0.1, skip_digest: true attributes :id, :title, :body belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer belongs_to :has_one_relationship, serializer: CachingHasOneRelationshipSerializer has_many :has_many_relationships, serializer: CachingHasManyRelationshipSerializer link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' } meta do { rating: 5, favorite_count: 10 } end def virtual_attribute VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute') end end Rails.configuration.serializers << CachingPrimaryResourceSerializer class FragmentCachingHasOneRelationshipSerializer < HasOneRelationshipSerializer cache key: 'writer', only: [:first_name, :last_name], skip_digest: true end Rails.configuration.serializers << FragmentCachingHasOneRelationshipSerializer class FragmentCachingHasManyRelationshipSerializer < HasManyRelationshipSerializer cache expires_in: 1.day, except: [:body], skip_digest: true end Rails.configuration.serializers << CachingHasManyRelationshipSerializer # see https://github.com/rails-api/active_model_serializers/pull/1690/commits/68715b8f99bc29677e8a47bb3f305f23c077024b#r60344532 class FragmentCachingPrimaryResourceSerializer < ActiveModel::Serializer cache key: 'primary_resource', expires_in: 0.1, skip_digest: true attributes :id, :title, :body belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer belongs_to :has_one_relationship, serializer: FragmentCachingHasOneRelationshipSerializer has_many :has_many_relationships, serializer: FragmentCachingHasManyRelationshipSerializer link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' } meta do { rating: 5, favorite_count: 10 } end def virtual_attribute VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute') end end Rails.configuration.serializers << FragmentCachingPrimaryResourceSerializer if ENV['ENABLE_ACTIVE_RECORD'] == 'true' require 'active_record' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') ActiveRecord::Schema.define do self.verbose = false create_table :virtual_attributes, force: true do |t| t.string :name t.timestamps null: false end create_table :has_one_relationships, force: true do |t| t.string :first_name t.string :last_name t.timestamps null: false end create_table :primary_resources, force: true do |t| t.string :title t.text :body t.references :has_one_relationship t.references :virtual_attribute t.timestamps null: false end create_table :has_many_relationships, force: true do |t| t.text :body t.references :has_one_relationship t.references :primary_resource t.timestamps null: false end end class HasManyRelationship < ActiveRecord::Base belongs_to :has_one_relationship belongs_to :primary_resource end class HasOneRelationship < ActiveRecord::Base has_many :primary_resources has_many :has_many_relationships end class PrimaryResource < ActiveRecord::Base has_many :has_many_relationships belongs_to :has_one_relationship belongs_to :virtual_attribute end class VirtualAttribute < ActiveRecord::Base has_many :primary_resources end else # ActiveModelSerializers::Model is a convenient # serializable class to inherit from when making # serializable non-activerecord objects. class BenchmarkModel include ActiveModel::Model include ActiveModel::Serializers::JSON attr_reader :attributes def initialize(attributes = {}) @attributes = attributes super end # Defaults to the downcased model name. def id attributes.fetch(:id) { self.class.name.downcase } end # Defaults to the downcased model name and updated_at def cache_key attributes.fetch(:cache_key) { "#{self.class.name.downcase}/#{id}" } end # Defaults to the time the serializer file was modified. def updated_at @updated_at ||= attributes.fetch(:updated_at) { File.mtime(__FILE__) } end def read_attribute_for_serialization(key) if key == :id || key == 'id' attributes.fetch(key) { id } else attributes[key] end end end class HasManyRelationship < BenchmarkModel attr_accessor :id, :body end class HasOneRelationship < BenchmarkModel attr_accessor :id, :first_name, :last_name, :primary_resources end class PrimaryResource < BenchmarkModel attr_accessor :id, :title, :body, :has_many_relationships, :virtual_attribute, :has_one_relationship end class VirtualAttribute < BenchmarkModel attr_accessor :id, :name end end active_model_serializers-0.10.10/test/cache_test.rb000066400000000000000000000655671351232231100223420ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' require 'tmpdir' require 'tempfile' module ActiveModelSerializers class CacheTest < ActiveSupport::TestCase class Article < ::Model attributes :title # To confirm error is raised when cache_key is not set and cache_key option not passed to cache undef_method :cache_key end class ArticleSerializer < ActiveModel::Serializer cache only: [:place], skip_digest: true attributes :title end class Author < ::Model attributes :id, :name associations :posts, :bio, :roles end # Instead of a primitive cache key (i.e. a string), this class # returns a list of objects that require to be expanded themselves. class AuthorWithExpandableCacheElements < Author # For the test purposes it's important that #to_s for HasCacheKey differs # between instances, hence not a Struct. class HasCacheKey attr_reader :cache_key def initialize(cache_key) @cache_key = cache_key end def to_s "HasCacheKey##{object_id}" end end def cache_key [ HasCacheKey.new(name), HasCacheKey.new(id) ] end end class UncachedAuthor < Author # To confirm cache_key is set using updated_at and cache_key option passed to cache undef_method :cache_key end class AuthorSerializer < ActiveModel::Serializer cache key: 'writer', skip_digest: true attributes :id, :name has_many :posts has_many :roles has_one :bio end class AuthorSerializerWithCache < ActiveModel::Serializer cache attributes :name end class Blog < ::Model attributes :name associations :writer end class BlogSerializer < ActiveModel::Serializer cache key: 'blog' attributes :id, :name belongs_to :writer end class Comment < ::Model attributes :id, :body associations :post, :author # Uses a custom non-time-based cache key def cache_key "comment/#{id}" end end class CommentSerializer < ActiveModel::Serializer cache expires_in: 1.day, skip_digest: true attributes :id, :body belongs_to :post belongs_to :author end class Post < ::Model attributes :id, :title, :body associations :author, :comments, :blog end class PostSerializer < ActiveModel::Serializer cache key: 'post', expires_in: 0.1, skip_digest: true attributes :id, :title, :body has_many :comments belongs_to :blog belongs_to :author end class Role < ::Model attributes :name, :description, :special_attribute associations :author end class RoleSerializer < ActiveModel::Serializer cache only: [:name, :slug], skip_digest: true attributes :id, :name, :description attribute :friendly_id, key: :slug belongs_to :author def friendly_id "#{object.name}-#{object.id}" end end class InheritedRoleSerializer < RoleSerializer cache key: 'inherited_role', only: [:name, :special_attribute] attribute :special_attribute end setup do cache_store.clear @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @post = Post.new(id: 'post', title: 'New Post', body: 'Body') @bio = Bio.new(id: 1, content: 'AMS Contributor') @author = Author.new(id: 'author', name: 'Joao M. D. Moura') @blog = Blog.new(id: 999, name: 'Custom blog', writer: @author) @role = Role.new(name: 'Great Author') @location = Location.new(lat: '-23.550520', lng: '-46.633309') @place = Place.new(name: 'Amazing Place') @author.posts = [@post] @author.roles = [@role] @role.author = @author @author.bio = @bio @bio.author = @author @post.comments = [@comment] @post.author = @author @comment.post = @post @comment.author = @author @post.blog = @blog @location.place = @place @location_serializer = LocationSerializer.new(@location) @bio_serializer = BioSerializer.new(@bio) @role_serializer = RoleSerializer.new(@role) @post_serializer = PostSerializer.new(@post) @author_serializer = AuthorSerializer.new(@author) @comment_serializer = CommentSerializer.new(@comment) @blog_serializer = BlogSerializer.new(@blog) end def test_expiring_of_cache_at_update_of_record original_cache_versioning = :none if ARModels::Author.respond_to?(:cache_versioning) original_cache_versioning = ARModels::Author.cache_versioning ARModels::Author.cache_versioning = true end author = ARModels::Author.create(name: 'Foo') author_json = AuthorSerializerWithCache.new(author).as_json assert_equal 'Foo', author_json[:name] author.update(name: 'Bar') author_json = AuthorSerializerWithCache.new(author).as_json expected = 'Bar' actual = author_json[:name] if ENV['APPVEYOR'] && actual != expected skip('Cache expiration tests sometimes fail on Appveyor. FIXME :)') else assert_equal expected, actual end ensure ARModels::Author.cache_versioning = original_cache_versioning unless original_cache_versioning == :none end def test_cache_expiration_in_collection_on_update_of_record original_cache_versioning = :none if ARModels::Author.respond_to?(:cache_versioning) original_cache_versioning = ARModels::Author.cache_versioning ARModels::Author.cache_versioning = true end foo = 'Foo' foo2 = 'Foo2' author = ARModels::Author.create(name: foo) author2 = ARModels::Author.create(name: foo2) author_collection = [author, author, author2] collection_json = render_object_with_cache(author_collection, each_serializer: AuthorSerializerWithCache) actual = collection_json expected = [{ name: foo }, { name: foo }, { name: foo2 }] if ENV['APPVEYOR'] && actual != expected skip('Cache expiration tests sometimes fail on Appveyor. FIXME :)') else assert_equal expected, actual end bar = 'Bar' Timecop.travel(10.seconds.from_now) do author.update!(name: bar) collection_json = render_object_with_cache(author_collection, each_serializer: AuthorSerializerWithCache) assert_equal [{ name: bar }, { name: bar }, { name: foo2 }], collection_json end ensure ARModels::Author.cache_versioning = original_cache_versioning unless original_cache_versioning == :none end def test_explicit_cache_store default_store = Class.new(ActiveModel::Serializer) do cache end explicit_store = Class.new(ActiveModel::Serializer) do cache cache_store: ActiveSupport::Cache::FileStore end assert ActiveSupport::Cache::MemoryStore, ActiveModelSerializers.config.cache_store assert ActiveSupport::Cache::MemoryStore, default_store.cache_store assert ActiveSupport::Cache::FileStore, explicit_store.cache_store end def test_inherited_cache_configuration inherited_serializer = Class.new(PostSerializer) assert_equal PostSerializer._cache_key, inherited_serializer._cache_key assert_equal PostSerializer._cache_options, inherited_serializer._cache_options end def test_override_cache_configuration inherited_serializer = Class.new(PostSerializer) do cache key: 'new-key' end assert_equal PostSerializer._cache_key, 'post' assert_equal inherited_serializer._cache_key, 'new-key' end def test_cache_definition assert_equal(cache_store, @post_serializer.class._cache) assert_equal(cache_store, @author_serializer.class._cache) assert_equal(cache_store, @comment_serializer.class._cache) end def test_cache_key_definition assert_equal('post', @post_serializer.class._cache_key) assert_equal('writer', @author_serializer.class._cache_key) assert_nil(@comment_serializer.class._cache_key) end def test_cache_key_interpolation_with_updated_at_when_cache_key_is_not_defined_on_object uncached_author = UncachedAuthor.new(name: 'Joao M. D. Moura') uncached_author_serializer = AuthorSerializer.new(uncached_author) render_object_with_cache(uncached_author) key = "#{uncached_author_serializer.class._cache_key}/#{uncached_author_serializer.object.id}-#{uncached_author_serializer.object.updated_at.strftime('%Y%m%d%H%M%S%9N')}" key = "#{key}/#{adapter.cache_key}" assert_equal(uncached_author_serializer.attributes.to_json, cache_store.fetch(key).to_json) end def test_cache_key_expansion author = AuthorWithExpandableCacheElements.new(id: 10, name: 'hello') same_author = AuthorWithExpandableCacheElements.new(id: 10, name: 'hello') diff_author = AuthorWithExpandableCacheElements.new(id: 11, name: 'hello') author_serializer = AuthorSerializer.new(author) same_author_serializer = AuthorSerializer.new(same_author) diff_author_serializer = AuthorSerializer.new(diff_author) adapter = AuthorSerializer.serialization_adapter_instance assert_equal(author_serializer.cache_key(adapter), same_author_serializer.cache_key(adapter)) refute_equal(author_serializer.cache_key(adapter), diff_author_serializer.cache_key(adapter)) end def test_default_cache_key_fallback render_object_with_cache(@comment) key = "#{@comment.cache_key}/#{adapter.cache_key}" assert_equal(@comment_serializer.attributes.to_json, cache_store.fetch(key).to_json) end def test_error_is_raised_if_cache_key_is_not_defined_on_object_or_passed_as_cache_option article = Article.new(title: 'Must Read') e = assert_raises ActiveModel::Serializer::UndefinedCacheKey do render_object_with_cache(article) end assert_match(/ActiveModelSerializers::CacheTest::Article must define #cache_key, or the 'key:' option must be passed into 'ActiveModelSerializers::CacheTest::ArticleSerializer.cache'/, e.message) end def test_cache_options_definition assert_equal({ expires_in: 0.1, skip_digest: true }, @post_serializer.class._cache_options) assert_nil(@blog_serializer.class._cache_options) assert_equal({ expires_in: 1.day, skip_digest: true }, @comment_serializer.class._cache_options) end def test_fragment_cache_definition assert_equal([:name, :slug], @role_serializer.class._cache_only) assert_equal([:content], @bio_serializer.class._cache_except) end def test_associations_separately_cache cache_store.clear assert_nil(cache_store.fetch(@post.cache_key)) assert_nil(cache_store.fetch(@comment.cache_key)) Timecop.freeze(Time.current) do render_object_with_cache(@post) key = "#{@post.cache_key}/#{adapter.cache_key}" assert_equal(@post_serializer.attributes, cache_store.fetch(key)) key = "#{@comment.cache_key}/#{adapter.cache_key}" assert_equal(@comment_serializer.attributes, cache_store.fetch(key)) end end def test_associations_cache_when_updated Timecop.freeze(Time.current) do # Generate a new Cache of Post object and each objects related to it. render_object_with_cache(@post) # Check if it cached the objects separately key = "#{@post.cache_key}/#{adapter.cache_key}" assert_equal(@post_serializer.attributes, cache_store.fetch(key)) key = "#{@comment.cache_key}/#{adapter.cache_key}" assert_equal(@comment_serializer.attributes, cache_store.fetch(key)) # Simulating update on comments relationship with Post new_comment = Comment.new(id: 2567, body: 'ZOMG A NEW COMMENT') new_comment_serializer = CommentSerializer.new(new_comment) @post.comments = [new_comment] # Ask for the serialized object render_object_with_cache(@post) # Check if the the new comment was cached key = "#{new_comment.cache_key}/#{adapter.cache_key}" assert_equal(new_comment_serializer.attributes, cache_store.fetch(key)) key = "#{@post.cache_key}/#{adapter.cache_key}" assert_equal(@post_serializer.attributes, cache_store.fetch(key)) end end def test_fragment_fetch_with_virtual_associations expected_result = { id: @location.id, lat: @location.lat, lng: @location.lng, address: 'Nowhere' } hash = render_object_with_cache(@location) assert_equal(hash, expected_result) key = "#{@location.cache_key}/#{adapter.cache_key}" assert_equal({ address: 'Nowhere' }, cache_store.fetch(key)) end def test_fragment_cache_with_inheritance inherited = render_object_with_cache(@role, serializer: InheritedRoleSerializer) base = render_object_with_cache(@role) assert_includes(inherited.keys, :special_attribute) refute_includes(base.keys, :special_attribute) end def test_uses_adapter_in_cache_key render_object_with_cache(@post) key = "#{@post.cache_key}/#{adapter.class.to_s.demodulize.underscore}" assert_equal(@post_serializer.attributes, cache_store.fetch(key)) end # Based on original failing test by @kevintyll # rubocop:disable Metrics/AbcSize def test_a_serializer_rendered_by_two_adapter_returns_differently_fetch_attributes Object.const_set(:Alert, Class.new(ActiveModelSerializers::Model) do attributes :id, :status, :resource, :started_at, :ended_at, :updated_at, :created_at end) Object.const_set(:UncachedAlertSerializer, Class.new(ActiveModel::Serializer) do attributes :id, :status, :resource, :started_at, :ended_at, :updated_at, :created_at end) Object.const_set(:AlertSerializer, Class.new(UncachedAlertSerializer) do cache end) alert = Alert.new( id: 1, status: 'fail', resource: 'resource-1', started_at: Time.new(2016, 3, 31, 21, 36, 35, 0), ended_at: nil, updated_at: Time.new(2016, 3, 31, 21, 27, 35, 0), created_at: Time.new(2016, 3, 31, 21, 37, 35, 0) ) expected_fetch_attributes = { id: 1, status: 'fail', resource: 'resource-1', started_at: alert.started_at, ended_at: nil, updated_at: alert.updated_at, created_at: alert.created_at }.with_indifferent_access expected_cached_jsonapi_attributes = { id: '1', type: 'alerts', attributes: { status: 'fail', resource: 'resource-1', started_at: alert.started_at, ended_at: nil, updated_at: alert.updated_at, created_at: alert.created_at } }.with_indifferent_access # Assert attributes are serialized correctly serializable_alert = serializable(alert, serializer: AlertSerializer, adapter: :attributes) attributes_serialization = serializable_alert.as_json.with_indifferent_access assert_equal expected_fetch_attributes, alert.attributes assert_equal alert.attributes, attributes_serialization attributes_cache_key = serializable_alert.adapter.serializer.cache_key(serializable_alert.adapter) assert_equal attributes_serialization, cache_store.fetch(attributes_cache_key).with_indifferent_access serializable_alert = serializable(alert, serializer: AlertSerializer, adapter: :json_api) jsonapi_cache_key = serializable_alert.adapter.serializer.cache_key(serializable_alert.adapter) # Assert cache keys differ refute_equal attributes_cache_key, jsonapi_cache_key # Assert (cached) serializations differ jsonapi_serialization = serializable_alert.as_json assert_equal alert.status, jsonapi_serialization.fetch(:data).fetch(:attributes).fetch(:status) serializable_alert = serializable(alert, serializer: UncachedAlertSerializer, adapter: :json_api) assert_equal serializable_alert.as_json, jsonapi_serialization cached_serialization = cache_store.fetch(jsonapi_cache_key).with_indifferent_access assert_equal expected_cached_jsonapi_attributes, cached_serialization ensure Object.send(:remove_const, :Alert) Object.send(:remove_const, :AlertSerializer) Object.send(:remove_const, :UncachedAlertSerializer) end # rubocop:enable Metrics/AbcSize def test_uses_file_digest_in_cache_key render_object_with_cache(@blog) file_digest = Digest::MD5.hexdigest(File.open(__FILE__).read) key = "#{@blog.cache_key}/#{adapter.cache_key}/#{file_digest}" assert_equal(@blog_serializer.attributes, cache_store.fetch(key)) end def test_cache_digest_definition file_digest = Digest::MD5.hexdigest(File.open(__FILE__).read) assert_equal(file_digest, @post_serializer.class._cache_digest) end def test_object_cache_keys serializable = ActiveModelSerializers::SerializableResource.new([@comment, @comment]) include_directive = JSONAPI::IncludeDirective.new('*', allow_wildcard: true) actual = ActiveModel::Serializer.object_cache_keys(serializable.adapter.serializer, serializable.adapter, include_directive) assert_equal 3, actual.size expected_key = "comment/1/#{serializable.adapter.cache_key}" assert actual.any? { |key| key == expected_key }, "actual '#{actual}' should include #{expected_key}" expected_key = %r{post/post-\d+} assert actual.any? { |key| key =~ expected_key }, "actual '#{actual}' should match '#{expected_key}'" expected_key = %r{author/author-\d+} assert actual.any? { |key| key =~ expected_key }, "actual '#{actual}' should match '#{expected_key}'" end # rubocop:disable Metrics/AbcSize def test_fetch_attributes_from_cache serializers = ActiveModel::Serializer::CollectionSerializer.new([@comment, @comment]) Timecop.freeze(Time.current) do render_object_with_cache(@comment) options = {} adapter_options = {} adapter_instance = ActiveModelSerializers::Adapter::Attributes.new(serializers, adapter_options) serializers.serializable_hash(adapter_options, options, adapter_instance) cached_attributes = options.fetch(:cached_attributes).with_indifferent_access include_directive = ActiveModelSerializers.default_include_directive manual_cached_attributes = ActiveModel::Serializer.cache_read_multi(serializers, adapter_instance, include_directive).with_indifferent_access assert_equal manual_cached_attributes, cached_attributes assert_equal cached_attributes["#{@comment.cache_key}/#{adapter_instance.cache_key}"], Comment.new(id: 1, body: 'ZOMG A COMMENT').attributes assert_equal cached_attributes["#{@comment.post.cache_key}/#{adapter_instance.cache_key}"], Post.new(id: 'post', title: 'New Post', body: 'Body').attributes writer = @comment.post.blog.writer writer_cache_key = writer.cache_key assert_equal cached_attributes["#{writer_cache_key}/#{adapter_instance.cache_key}"], Author.new(id: 'author', name: 'Joao M. D. Moura').attributes end end # rubocop:enable Metrics/AbcSize def test_cache_read_multi_with_fragment_cache_enabled post_serializer = Class.new(ActiveModel::Serializer) do cache except: [:body] end serializers = ActiveModel::Serializer::CollectionSerializer.new([@post, @post], serializer: post_serializer) Timecop.freeze(Time.current) do # Warming up. options = {} adapter_options = {} adapter_instance = ActiveModelSerializers::Adapter::Attributes.new(serializers, adapter_options) serializers.serializable_hash(adapter_options, options, adapter_instance) # Should find something with read_multi now options = {} serializers.serializable_hash(adapter_options, options, adapter_instance) cached_attributes = options.fetch(:cached_attributes) include_directive = ActiveModelSerializers.default_include_directive manual_cached_attributes = ActiveModel::Serializer.cache_read_multi(serializers, adapter_instance, include_directive) refute_equal 0, cached_attributes.size refute_equal 0, manual_cached_attributes.size assert_equal manual_cached_attributes, cached_attributes end end def test_serializer_file_path_on_nix path = '/Users/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb' caller_line = "#{path}:1:in `'" assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path end def test_serializer_file_path_on_windows path = 'c:/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb' caller_line = "#{path}:1:in `'" assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path end def test_serializer_file_path_with_space path = '/Users/git/ember js/ember-crm-backend/app/serializers/lead_serializer.rb' caller_line = "#{path}:1:in `'" assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path end def test_serializer_file_path_with_submatch # The submatch in the path ensures we're using a correctly greedy regexp. path = '/Users/git/ember js/ember:123:in x/app/serializers/lead_serializer.rb' caller_line = "#{path}:1:in `'" assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path end def test_digest_caller_file contents = "puts 'AMS rocks'!" dir = Dir.mktmpdir('space char') file = Tempfile.new('some_ruby.rb', dir) file.write(contents) path = file.path caller_line = "#{path}:1:in `'" file.close assert_equal ActiveModel::Serializer.digest_caller_file(caller_line), Digest::MD5.hexdigest(contents) ensure file.unlink FileUtils.remove_entry dir end def test_warn_on_serializer_not_defined_in_file called = false serializer = Class.new(ActiveModel::Serializer) assert_output(nil, /_cache_digest/) do serializer.digest_caller_file('') called = true end assert called end def test_cached_false_without_cache_store cached_serializer = build_cached_serializer do |serializer| serializer._cache = nil end refute cached_serializer.class.cache_enabled? end def test_cached_true_with_cache_store_and_without_cache_only_and_cache_except cached_serializer = build_cached_serializer do |serializer| serializer._cache = Object end assert cached_serializer.class.cache_enabled? end def test_cached_false_with_cache_store_and_with_cache_only cached_serializer = build_cached_serializer do |serializer| serializer._cache = Object serializer._cache_only = [:name] end refute cached_serializer.class.cache_enabled? end def test_cached_false_with_cache_store_and_with_cache_except cached_serializer = build_cached_serializer do |serializer| serializer._cache = Object serializer._cache_except = [:content] end refute cached_serializer.class.cache_enabled? end def test_fragment_cached_false_without_cache_store cached_serializer = build_cached_serializer do |serializer| serializer._cache = nil serializer._cache_only = [:name] end refute cached_serializer.class.fragment_cache_enabled? end def test_fragment_cached_true_with_cache_store_and_cache_only cached_serializer = build_cached_serializer do |serializer| serializer._cache = Object serializer._cache_only = [:name] end assert cached_serializer.class.fragment_cache_enabled? end def test_fragment_cached_true_with_cache_store_and_cache_except cached_serializer = build_cached_serializer do |serializer| serializer._cache = Object serializer._cache_except = [:content] end assert cached_serializer.class.fragment_cache_enabled? end def test_fragment_cached_false_with_cache_store_and_cache_except_and_cache_only cached_serializer = build_cached_serializer do |serializer| serializer._cache = Object serializer._cache_except = [:content] serializer._cache_only = [:name] end refute cached_serializer.class.fragment_cache_enabled? end def test_fragment_fetch_with_virtual_attributes author = Author.new(name: 'Joao M. D. Moura') role = Role.new(name: 'Great Author', description: nil) role.author = [author] role_serializer = RoleSerializer.new(role) adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(role_serializer) expected_result = { id: role.id, description: role.description, slug: "#{role.name}-#{role.id}", name: role.name } cache_store.clear role_hash = role_serializer.fetch_attributes_fragment(adapter_instance) assert_equal(role_hash, expected_result) role.id = 'this has been updated' role.name = 'this was cached' role_hash = role_serializer.fetch_attributes_fragment(adapter_instance) assert_equal(expected_result.merge(id: role.id), role_hash) end def test_fragment_fetch_with_except adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(@bio_serializer) expected_result = { id: @bio.id, rating: nil, content: @bio.content } cache_store.clear bio_hash = @bio_serializer.fetch_attributes_fragment(adapter_instance) assert_equal(expected_result, bio_hash) @bio.content = 'this has been updated' @bio.rating = 'this was cached' bio_hash = @bio_serializer.fetch_attributes_fragment(adapter_instance) assert_equal(expected_result.merge(content: @bio.content), bio_hash) end def test_fragment_fetch_with_namespaced_object @spam = Spam::UnrelatedLink.new(id: 'spam-id-1') @spam_serializer = Spam::UnrelatedLinkSerializer.new(@spam) adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(@spam_serializer) @spam_hash = @spam_serializer.fetch_attributes_fragment(adapter_instance) expected_result = { id: @spam.id } assert_equal(@spam_hash, expected_result) end private def cache_store ActiveModelSerializers.config.cache_store end def build_cached_serializer serializer = Class.new(ActiveModel::Serializer) serializer._cache_key = nil serializer._cache_options = nil yield serializer if block_given? serializer.new(Object) end def render_object_with_cache(obj, options = {}) @serializable_resource = serializable(obj, options) @serializable_resource.serializable_hash end def adapter @serializable_resource.adapter end end end active_model_serializers-0.10.10/test/collection_serializer_test.rb000066400000000000000000000101141351232231100256350ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class CollectionSerializerTest < ActiveSupport::TestCase class SingularModel < ::Model; end class SingularModelSerializer < ActiveModel::Serializer end class HasManyModel < ::Model associations :singular_models end class HasManyModelSerializer < ActiveModel::Serializer has_many :singular_models def custom_options instance_options end end class MessagesSerializer < ActiveModel::Serializer type 'messages' end def setup @singular_model = SingularModel.new @has_many_model = HasManyModel.new @resource = build_named_collection @singular_model, @has_many_model @serializer = collection_serializer.new(@resource, some: :options) end def collection_serializer CollectionSerializer end def build_named_collection(*resource) resource.define_singleton_method(:name) { 'MeResource' } resource end def test_has_object_reader_serializer_interface assert_equal @serializer.object, @resource end def test_respond_to_each assert_respond_to @serializer, :each end def test_each_object_should_be_serialized_with_appropriate_serializer serializers = @serializer.to_a assert_kind_of SingularModelSerializer, serializers.first assert_kind_of SingularModel, serializers.first.object assert_kind_of HasManyModelSerializer, serializers.last assert_kind_of HasManyModel, serializers.last.object assert_equal :options, serializers.last.custom_options[:some] end def test_serializer_option_not_passed_to_each_serializer serializers = collection_serializer.new([@has_many_model], serializer: HasManyModelSerializer).to_a refute serializers.first.custom_options.key?(:serializer) end def test_root_default @serializer = collection_serializer.new([@singular_model, @has_many_model]) assert_nil @serializer.root end def test_root expected = 'custom_root' @serializer = collection_serializer.new([@singular_model, @has_many_model], root: expected) assert_equal expected, @serializer.root end def test_root_with_no_serializers expected = 'custom_root' @serializer = collection_serializer.new([], root: expected) assert_equal expected, @serializer.root end def test_json_key_with_resource_with_serializer singular_key = @serializer.send(:serializers).first.json_key assert_equal singular_key.pluralize, @serializer.json_key end def test_json_key_with_resource_with_name_and_no_serializers serializer = collection_serializer.new(build_named_collection) assert_equal 'me_resources', serializer.json_key end def test_json_key_with_resource_with_nil_name_and_no_serializers resource = [] resource.define_singleton_method(:name) { nil } serializer = collection_serializer.new(resource) assert_raise ArgumentError do serializer.json_key end end def test_json_key_with_resource_without_name_and_no_serializers serializer = collection_serializer.new([]) assert_raise ArgumentError do serializer.json_key end end def test_json_key_with_empty_resources_with_serializer resource = [] serializer = collection_serializer.new(resource, serializer: MessagesSerializer) assert_equal 'messages', serializer.json_key end def test_json_key_with_root expected = 'custom_root' serializer = collection_serializer.new(@resource, root: expected) assert_equal expected, serializer.json_key end def test_json_key_with_root_and_no_serializers expected = 'custom_root' serializer = collection_serializer.new(build_named_collection, root: expected) assert_equal expected, serializer.json_key end end end end active_model_serializers-0.10.10/test/fixtures/000077500000000000000000000000001351232231100215415ustar00rootroot00000000000000active_model_serializers-0.10.10/test/fixtures/active_record.rb000066400000000000000000000053751351232231100247110ustar00rootroot00000000000000# frozen_string_literal: true require 'active_record' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') ActiveRecord::Schema.define do self.verbose = false create_table :posts, force: true do |t| t.string :title t.text :body t.references :author t.timestamps null: false end create_table :authors, force: true do |t| t.string :name t.timestamps null: false end create_table :comments, force: true do |t| t.text :contents t.references :author t.references :post t.timestamps null: false end create_table :employees, force: true do |t| t.string :name t.string :email t.timestamps null: false end create_table :object_tags, force: true do |t| t.string :poly_tag_id t.string :taggable_type t.string :taggable_id t.timestamps null: false end create_table :poly_tags, force: true do |t| t.string :phrase t.timestamps null: false end create_table :pictures, force: true do |t| t.string :title t.string :imageable_type t.string :imageable_id t.timestamps null: false end end module ARModels class Post < ActiveRecord::Base has_many :comments belongs_to :author end class PostSerializer < ActiveModel::Serializer attributes :id, :title, :body has_many :comments belongs_to :author end class Comment < ActiveRecord::Base belongs_to :post belongs_to :author end class CommentSerializer < ActiveModel::Serializer attributes :id, :contents belongs_to :author end class Author < ActiveRecord::Base has_many :posts end class AuthorSerializer < ActiveModel::Serializer attributes :id, :name has_many :posts end end class Employee < ActiveRecord::Base has_many :pictures, as: :imageable has_many :object_tags, as: :taggable end class PolymorphicSimpleSerializer < ActiveModel::Serializer attributes :id end class ObjectTag < ActiveRecord::Base belongs_to :poly_tag belongs_to :taggable, polymorphic: true end class PolymorphicObjectTagSerializer < ActiveModel::Serializer attributes :id belongs_to :taggable, serializer: PolymorphicSimpleSerializer, polymorphic: true end class PolyTag < ActiveRecord::Base has_many :object_tags end class PolymorphicTagSerializer < ActiveModel::Serializer attributes :id, :phrase has_many :object_tags, serializer: PolymorphicObjectTagSerializer end class Picture < ActiveRecord::Base belongs_to :imageable, polymorphic: true has_many :object_tags, as: :taggable end class PolymorphicHasManySerializer < ActiveModel::Serializer attributes :id, :name end class PolymorphicBelongsToSerializer < ActiveModel::Serializer attributes :id, :title belongs_to :imageable, serializer: PolymorphicHasManySerializer, polymorphic: true end active_model_serializers-0.10.10/test/fixtures/poro.rb000066400000000000000000000121261351232231100230470ustar00rootroot00000000000000# frozen_string_literal: true class Model < ActiveModelSerializers::Model rand(2).zero? && derive_attributes_from_names_and_fix_accessors attr_writer :id # At this time, just for organization of intent class_attribute :association_names self.association_names = [] def self.associations(*names) self.association_names |= names.map(&:to_sym) # Silence redefinition of methods warnings ActiveModelSerializers.silence_warnings do attr_accessor(*names) end end def associations association_names.each_with_object({}) do |association_name, result| result[association_name] = public_send(association_name).freeze end.with_indifferent_access.freeze end def attributes super.except(*association_names) end end # see # https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/errors.rb # The below allows you to do: # # model = ModelWithErrors.new # model.validate! # => ["cannot be nil"] # model.errors.full_messages # => ["name cannot be nil"] class ModelWithErrors < Model attributes :name end class Profile < Model attributes :name, :description associations :comments end class ProfileSerializer < ActiveModel::Serializer attributes :name, :description end class ProfilePreviewSerializer < ActiveModel::Serializer attributes :name end class Author < Model attributes :name associations :posts, :bio, :roles, :comments end class AuthorSerializer < ActiveModel::Serializer cache key: 'writer', skip_digest: true attribute :id attribute :name has_many :posts has_many :roles has_one :bio end class AuthorPreviewSerializer < ActiveModel::Serializer attributes :id has_many :posts end class Comment < Model attributes :body, :date associations :post, :author, :likes end class CommentSerializer < ActiveModel::Serializer cache expires_in: 1.day, skip_digest: true attributes :id, :body belongs_to :post belongs_to :author end class CommentPreviewSerializer < ActiveModel::Serializer attributes :id belongs_to :post end class Post < Model attributes :title, :body associations :author, :comments, :blog, :tags, :related end class PostSerializer < ActiveModel::Serializer cache key: 'post', expires_in: 0.1, skip_digest: true attributes :id, :title, :body has_many :comments belongs_to :blog belongs_to :author def blog Blog.new(id: 999, name: 'Custom blog') end end class SpammyPostSerializer < ActiveModel::Serializer attributes :id has_many :related end class PostPreviewSerializer < ActiveModel::Serializer attributes :title, :body, :id has_many :comments, serializer: ::CommentPreviewSerializer belongs_to :author, serializer: ::AuthorPreviewSerializer end class PostWithCustomKeysSerializer < ActiveModel::Serializer attributes :id has_many :comments, key: :reviews belongs_to :author, key: :writer has_one :blog, key: :site end class Bio < Model attributes :content, :rating associations :author end class BioSerializer < ActiveModel::Serializer cache except: [:content], skip_digest: true attributes :id, :content, :rating belongs_to :author end class Blog < Model attributes :name, :type, :special_attribute associations :writer, :articles end class BlogSerializer < ActiveModel::Serializer cache key: 'blog' attributes :id, :name belongs_to :writer has_many :articles end class AlternateBlogSerializer < ActiveModel::Serializer attribute :id attribute :name, key: :title end class CustomBlogSerializer < ActiveModel::Serializer attribute :id attribute :special_attribute has_many :articles end class Role < Model attributes :name, :description, :special_attribute associations :author end class RoleSerializer < ActiveModel::Serializer cache only: [:name, :slug], skip_digest: true attributes :id, :name, :description attribute :friendly_id, key: :slug belongs_to :author def friendly_id "#{object.name}-#{object.id}" end end class Location < Model attributes :lat, :lng associations :place end class LocationSerializer < ActiveModel::Serializer cache only: [:address], skip_digest: true attributes :id, :lat, :lng belongs_to :place, key: :address def place 'Nowhere' end end class Place < Model attributes :name associations :locations end class PlaceSerializer < ActiveModel::Serializer attributes :id, :name has_many :locations end class Like < Model attributes :time associations :likeable end class LikeSerializer < ActiveModel::Serializer attributes :id, :time belongs_to :likeable end module Spam class UnrelatedLink < Model end class UnrelatedLinkSerializer < ActiveModel::Serializer cache only: [:id] attributes :id end end class VirtualValue < Model; end class VirtualValueSerializer < ActiveModel::Serializer attributes :id has_many :reviews, virtual_value: [{ type: 'reviews', id: '1' }, { type: 'reviews', id: '2' }] has_one :maker, virtual_value: { type: 'makers', id: '1' } def reviews end def maker end end class PaginatedSerializer < ActiveModel::Serializer::CollectionSerializer def json_key 'paginated' end end active_model_serializers-0.10.10/test/generators/000077500000000000000000000000001351232231100220415ustar00rootroot00000000000000active_model_serializers-0.10.10/test/generators/scaffold_controller_generator_test.rb000066400000000000000000000013311351232231100315150ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' require 'generators/rails/resource_override' class ResourceGeneratorTest < Rails::Generators::TestCase destination File.expand_path('../../../tmp/generators', __FILE__) setup :prepare_destination, :copy_routes tests Rails::Generators::ResourceGenerator arguments %w(account) def test_serializer_file_is_generated run_generator assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < ActiveModel::Serializer/ end private def copy_routes config_dir = File.join(destination_root, 'config') FileUtils.mkdir_p(config_dir) File.write(File.join(config_dir, 'routes.rb'), 'Rails.application.routes.draw {}') end end active_model_serializers-0.10.10/test/generators/serializer_generator_test.rb000066400000000000000000000045441351232231100276530ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' require 'generators/rails/resource_override' require 'generators/rails/serializer_generator' class SerializerGeneratorTest < Rails::Generators::TestCase destination File.expand_path('../../../tmp/generators', __FILE__) setup :prepare_destination tests Rails::Generators::SerializerGenerator arguments %w(account name:string description:text business:references) def test_generates_a_serializer run_generator assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < ActiveModel::Serializer/ end def test_generates_a_namespaced_serializer run_generator ['admin/account'] assert_file 'app/serializers/admin/account_serializer.rb', /class Admin::AccountSerializer < ActiveModel::Serializer/ end def test_uses_application_serializer_if_one_exists stub_safe_constantize(expected: 'ApplicationSerializer') do run_generator assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < ApplicationSerializer/ end end def test_uses_given_parent Object.const_set(:ApplicationSerializer, Class.new) run_generator ['Account', '--parent=MySerializer'] assert_file 'app/serializers/account_serializer.rb', /class AccountSerializer < MySerializer/ ensure Object.send :remove_const, :ApplicationSerializer end def test_generates_attributes_and_associations run_generator assert_file 'app/serializers/account_serializer.rb' do |serializer| assert_match(/^ attributes :id, :name, :description$/, serializer) assert_match(/^ has_one :business$/, serializer) assert_match(/^end\n*\z/, serializer) end end def test_with_no_attributes_does_not_add_extra_space run_generator ['account'] assert_file 'app/serializers/account_serializer.rb' do |content| if RUBY_PLATFORM =~ /mingw/ assert_no_match(/\r\n\r\nend/, content) else assert_no_match(/\n\nend/, content) end end end private def stub_safe_constantize(expected:) String.class_eval do alias_method :old, :safe_constantize end String.send(:define_method, :safe_constantize) do Class if self == expected end yield ensure String.class_eval do undef_method :safe_constantize alias_method :safe_constantize, :old undef_method :old end end end active_model_serializers-0.10.10/test/grape_test.rb000066400000000000000000000125451351232231100223610ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' TestHelper.silence_warnings do require 'grape' end require 'grape/active_model_serializers' require 'kaminari' require 'kaminari/hooks' ::Kaminari::Hooks.init module ActiveModelSerializers class GrapeTest < ActiveSupport::TestCase include Rack::Test::Methods module Models def self.model1 ARModels::Post.new(id: 1, title: 'Dummy Title', body: 'Lorem Ipsum') end def self.model2 ARModels::Post.new(id: 2, title: 'Second Dummy Title', body: 'Second Lorem Ipsum') end def self.all @all ||= begin model1.save! model2.save! ARModels::Post.all end end def self.reset_all ARModels::Post.delete_all @all = nil end def self.collection_per 2 end def self.collection @collection ||= begin Kaminari.paginate_array( [ Profile.new(id: 1, name: 'Name 1', description: 'Description 1', comments: 'Comments 1'), Profile.new(id: 2, name: 'Name 2', description: 'Description 2', comments: 'Comments 2'), Profile.new(id: 3, name: 'Name 3', description: 'Description 3', comments: 'Comments 3'), Profile.new(id: 4, name: 'Name 4', description: 'Description 4', comments: 'Comments 4'), Profile.new(id: 5, name: 'Name 5', description: 'Description 5', comments: 'Comments 5') ] ).page(1).per(collection_per) end end end class GrapeTest < Grape::API format :json TestHelper.silence_warnings do include Grape::ActiveModelSerializers end def self.resources(*) TestHelper.silence_warnings do super end end resources :grape do get '/render' do render Models.model1 end get '/render_with_json_api' do post = Models.model1 render post, meta: { page: 1, total_pages: 2 }, adapter: :json_api end get '/render_array_with_json_api' do posts = Models.all render posts, adapter: :json_api end get '/render_collection_with_json_api' do posts = Models.collection render posts, adapter: :json_api end get '/render_with_implicit_formatter' do Models.model1 end get '/render_array_with_implicit_formatter' do Models.all end get '/render_collection_with_implicit_formatter' do Models.collection end end end def app Grape::Middleware::Globals.new(GrapeTest.new) end extend Minitest::Assertions def self.run_one_method(*) _, stderr = capture_io do super end fail Minitest::Assertion, stderr if stderr !~ /grape/ end def test_formatter_returns_json get '/grape/render' post = Models.model1 serializable_resource = serializable(post) assert last_response.ok? assert_equal serializable_resource.to_json, last_response.body end def test_render_helper_passes_through_options_correctly get '/grape/render_with_json_api' post = Models.model1 serializable_resource = serializable(post, serializer: ARModels::PostSerializer, adapter: :json_api, meta: { page: 1, total_pages: 2 }) assert last_response.ok? assert_equal serializable_resource.to_json, last_response.body end def test_formatter_handles_arrays get '/grape/render_array_with_json_api' posts = Models.all serializable_resource = serializable(posts, adapter: :json_api) assert last_response.ok? assert_equal serializable_resource.to_json, last_response.body ensure Models.reset_all end def test_formatter_handles_collections get '/grape/render_collection_with_json_api' assert last_response.ok? representation = JSON.parse(last_response.body) assert representation.include?('data') assert representation['data'].count == Models.collection_per assert representation.include?('links') assert representation['links'].count > 0 end def test_implicit_formatter post = Models.model1 serializable_resource = serializable(post, adapter: :json_api) with_adapter :json_api do get '/grape/render_with_implicit_formatter' end assert last_response.ok? assert_equal serializable_resource.to_json, last_response.body end def test_implicit_formatter_handles_arrays posts = Models.all serializable_resource = serializable(posts, adapter: :json_api) with_adapter :json_api do get '/grape/render_array_with_implicit_formatter' end assert last_response.ok? assert_equal serializable_resource.to_json, last_response.body ensure Models.reset_all end def test_implicit_formatter_handles_collections with_adapter :json_api do get '/grape/render_collection_with_implicit_formatter' end representation = JSON.parse(last_response.body) assert last_response.ok? assert representation.include?('data') assert representation['data'].count == Models.collection_per assert representation.include?('links') assert representation['links'].count > 0 end end end active_model_serializers-0.10.10/test/lint_test.rb000066400000000000000000000015451351232231100222270ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class LintTest < ActiveSupport::TestCase include ActiveModel::Serializer::Lint::Tests class CompliantResource def serializable_hash(options = nil) end def read_attribute_for_serialization(name) end def as_json(options = nil) end def to_json(options = nil) end def cache_key end def id end def updated_at end def errors end def self.human_attribute_name(_, _ = {}) end def self.lookup_ancestors end def self.model_name @_model_name ||= ActiveModel::Name.new(self) end end def setup @resource = CompliantResource.new end end end end active_model_serializers-0.10.10/test/logger_test.rb000066400000000000000000000011701351232231100225320ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers class LoggerTest < ActiveSupport::TestCase def test_logger_is_set_to_action_controller_logger_when_initializer_runs assert_equal $action_controller_logger, ActionController::Base.logger # rubocop:disable Style/GlobalVars end def test_logger_can_be_set original_logger = ActiveModelSerializers.logger logger = Logger.new(STDOUT) ActiveModelSerializers.logger = logger assert_equal ActiveModelSerializers.logger, logger ensure ActiveModelSerializers.logger = original_logger end end end active_model_serializers-0.10.10/test/poro_test.rb000066400000000000000000000002771351232231100222410ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' class PoroTest < ActiveSupport::TestCase include ActiveModel::Serializer::Lint::Tests def setup @resource = Model.new end end active_model_serializers-0.10.10/test/serializable_resource_test.rb000066400000000000000000000060521351232231100256340ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModelSerializers class SerializableResourceTest < ActiveSupport::TestCase def setup @resource = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') @serializer = ProfileSerializer.new(@resource) @adapter = ActiveModelSerializers::Adapter.create(@serializer) @serializable_resource = SerializableResource.new(@resource) end def test_deprecation assert_output(nil, /deprecated/) do deprecated_serializable_resource = ActiveModel::SerializableResource.new(@resource) assert_equal(@serializable_resource.as_json, deprecated_serializable_resource.as_json) end end def test_serializable_resource_delegates_serializable_hash_to_the_adapter options = nil assert_equal @adapter.serializable_hash(options), @serializable_resource.serializable_hash(options) end def test_serializable_resource_delegates_to_json_to_the_adapter options = nil assert_equal @adapter.to_json(options), @serializable_resource.to_json(options) end def test_serializable_resource_delegates_as_json_to_the_adapter options = nil assert_equal @adapter.as_json(options), @serializable_resource.as_json(options) end def test_use_adapter_with_adapter_option assert SerializableResource.new(@resource, adapter: 'json').use_adapter? end def test_use_adapter_with_adapter_option_as_false refute SerializableResource.new(@resource, adapter: false).use_adapter? end class SerializableResourceErrorsTest < Minitest::Test def test_serializable_resource_with_errors options = nil resource = ModelWithErrors.new resource.errors.add(:name, 'must be awesome') serializable_resource = ActiveModelSerializers::SerializableResource.new( resource, serializer: ActiveModel::Serializer::ErrorSerializer, adapter: :json_api ) expected_response_document = { errors: [ { source: { pointer: '/data/attributes/name' }, detail: 'must be awesome' } ] } assert_equal serializable_resource.as_json(options), expected_response_document end def test_serializable_resource_with_collection_containing_errors options = nil resources = [] resources << resource = ModelWithErrors.new resource.errors.add(:title, 'must be amazing') resources << ModelWithErrors.new serializable_resource = SerializableResource.new( resources, serializer: ActiveModel::Serializer::ErrorsSerializer, each_serializer: ActiveModel::Serializer::ErrorSerializer, adapter: :json_api ) expected_response_document = { errors: [ { source: { pointer: '/data/attributes/title' }, detail: 'must be amazing' } ] } assert_equal serializable_resource.as_json(options), expected_response_document end end end end active_model_serializers-0.10.10/test/serializers/000077500000000000000000000000001351232231100222245ustar00rootroot00000000000000active_model_serializers-0.10.10/test/serializers/association_macros_test.rb000066400000000000000000000021111351232231100274630ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class AssociationMacrosTest < ActiveSupport::TestCase class AuthorSummarySerializer < ActiveModel::Serializer; end class AssociationsTestSerializer < Serializer belongs_to :author, serializer: AuthorSummarySerializer has_many :comments has_one :category end def before_setup @reflections = AssociationsTestSerializer._reflections.values end def test_has_one_defines_reflection has_one_reflection = HasOneReflection.new(:category, {}) assert_includes(@reflections, has_one_reflection) end def test_has_many_defines_reflection has_many_reflection = HasManyReflection.new(:comments, {}) assert_includes(@reflections, has_many_reflection) end def test_belongs_to_defines_reflection belongs_to_reflection = BelongsToReflection.new(:author, serializer: AuthorSummarySerializer) assert_includes(@reflections, belongs_to_reflection) end end end end active_model_serializers-0.10.10/test/serializers/associations_test.rb000066400000000000000000000474151351232231100263220ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class AssociationsTest < ActiveSupport::TestCase class ModelWithoutSerializer < ::Model attributes :id, :name end def setup @author = Author.new(name: 'Steve K.') @author.bio = nil @author.roles = [] @blog = Blog.new(name: 'AMS Blog') @post = Post.new(title: 'New Post', body: 'Body') @tag = ModelWithoutSerializer.new(id: 'tagid', name: '#hashtagged') @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @post.comments = [@comment] @post.tags = [@tag] @post.blog = @blog @comment.post = @post @comment.author = nil @post.author = @author @author.posts = [@post] @post_serializer = PostSerializer.new(@post, custom_options: true) @author_serializer = AuthorSerializer.new(@author) @comment_serializer = CommentSerializer.new(@comment) end def test_has_many_and_has_one @author_serializer.associations.each do |association| key = association.key serializer = association.lazy_association.serializer case key when :posts assert_equal true, association.include_data? assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer) when :bio assert_equal true, association.include_data? assert_nil serializer when :roles assert_equal true, association.include_data? assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer) else flunk "Unknown association: #{key}" end end end def test_has_many_with_no_serializer post_serializer_class = Class.new(ActiveModel::Serializer) do attributes :id has_many :tags end post_serializer_class.new(@post).associations.each do |association| key = association.key serializer = association.lazy_association.serializer assert_equal :tags, key assert_nil serializer assert_equal [{ id: 'tagid', name: '#hashtagged' }].to_json, association.virtual_value.to_json end end def test_serializer_options_are_passed_into_associations_serializers association = @post_serializer .associations .detect { |assoc| assoc.key == :comments } comment_serializer = association.lazy_association.serializer.first class << comment_serializer def custom_options instance_options end end assert comment_serializer.custom_options.fetch(:custom_options) end def test_belongs_to @comment_serializer.associations.each do |association| key = association.key serializer = association.lazy_association.serializer case key when :post assert_kind_of(PostSerializer, serializer) when :author assert_nil serializer else flunk "Unknown association: #{key}" end assert_equal true, association.include_data? end end def test_belongs_to_with_custom_method assert( @post_serializer.associations.any? do |association| association.key == :blog end ) end def test_associations_inheritance inherited_klass = Class.new(PostSerializer) assert_equal(PostSerializer._reflections, inherited_klass._reflections) end def test_associations_inheritance_with_new_association inherited_klass = Class.new(PostSerializer) do has_many :top_comments, serializer: CommentSerializer end assert( PostSerializer._reflections.values.all? do |reflection| inherited_klass._reflections.values.include?(reflection) end ) assert( inherited_klass._reflections.values.any? do |reflection| reflection.name == :top_comments end ) end def test_associations_custom_keys serializer = PostWithCustomKeysSerializer.new(@post) expected_association_keys = serializer.associations.map(&:key) assert expected_association_keys.include? :reviews assert expected_association_keys.include? :writer assert expected_association_keys.include? :site end class BelongsToBlogModel < ::Model attributes :id, :title associations :blog end class BelongsToBlogModelSerializer < ActiveModel::Serializer type :posts belongs_to :blog end def test_belongs_to_doesnt_load_record attributes = { id: 1, title: 'Belongs to Blog', blog: Blog.new(id: 5) } post = BelongsToBlogModel.new(attributes) class << post def blog fail 'should use blog_id' end def blog_id 5 end end actual = begin original_option = BelongsToBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship BelongsToBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = true serializable(post, adapter: :json_api, serializer: BelongsToBlogModelSerializer).as_json ensure BelongsToBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = original_option end expected = { data: { id: '1', type: 'posts', relationships: { blog: { data: { id: '5', type: 'blogs' } } } } } assert_equal expected, actual end class ExternalBlog < Blog attributes :external_id end class BelongsToExternalBlogModel < ::Model attributes :id, :title, :external_blog_id associations :external_blog end class BelongsToExternalBlogModelSerializer < ActiveModel::Serializer type :posts belongs_to :external_blog def external_blog_id object.external_blog.external_id end end def test_belongs_to_allows_id_overwriting attributes = { id: 1, title: 'Title', external_blog: ExternalBlog.new(id: 5, external_id: 6) } post = BelongsToExternalBlogModel.new(attributes) actual = begin original_option = BelongsToExternalBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship BelongsToExternalBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = true serializable(post, adapter: :json_api, serializer: BelongsToExternalBlogModelSerializer).as_json ensure BelongsToExternalBlogModelSerializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship = original_option end expected = { data: { id: '1', type: 'posts', relationships: { :'external-blog' => { data: { id: '6', type: 'external-blogs' } } } } } assert_equal expected, actual end class InlineAssociationTestPostSerializer < ActiveModel::Serializer has_many :comments has_many :comments, key: :last_comments do object.comments.last(1) end end def test_virtual_attribute_block comment1 = ::ARModels::Comment.create!(contents: 'first comment') comment2 = ::ARModels::Comment.create!(contents: 'last comment') post = ::ARModels::Post.create!( title: 'inline association test', body: 'etc', comments: [comment1, comment2] ) actual = serializable(post, adapter: :attributes, serializer: InlineAssociationTestPostSerializer).as_json expected = { comments: [ { id: 1, contents: 'first comment' }, { id: 2, contents: 'last comment' } ], last_comments: [ { id: 2, contents: 'last comment' } ] } assert_equal expected, actual ensure ::ARModels::Post.delete_all ::ARModels::Comment.delete_all end class NamespacedResourcesTest < ActiveSupport::TestCase class ResourceNamespace class Post < ::Model associations :comments, :author, :description end class Comment < ::Model; end class Author < ::Model; end class Description < ::Model; end class PostSerializer < ActiveModel::Serializer has_many :comments belongs_to :author has_one :description end class CommentSerializer < ActiveModel::Serializer; end class AuthorSerializer < ActiveModel::Serializer; end class DescriptionSerializer < ActiveModel::Serializer; end end def setup @comment = ResourceNamespace::Comment.new @author = ResourceNamespace::Author.new @description = ResourceNamespace::Description.new @post = ResourceNamespace::Post.new(comments: [@comment], author: @author, description: @description) @post_serializer = ResourceNamespace::PostSerializer.new(@post) end def test_associations_namespaced_resources @post_serializer.associations.each do |association| case association.key when :comments assert_instance_of(ResourceNamespace::CommentSerializer, association.lazy_association.serializer.first) when :author assert_instance_of(ResourceNamespace::AuthorSerializer, association.lazy_association.serializer) when :description assert_instance_of(ResourceNamespace::DescriptionSerializer, association.lazy_association.serializer) else flunk "Unknown association: #{key}" end end end end class AssociationsNamespacedSerializersTest < ActiveSupport::TestCase class Post < ::Model associations :comments, :author, :description def latest_comments comments[0..3] end end class Comment < ::Model; end class Author < ::Model; end class Description < ::Model; end class ResourceNamespace class PostSerializer < ActiveModel::Serializer has_many :comments, namespace: ResourceNamespace has_many :latest_comments, namespace: ResourceNamespace belongs_to :author, namespace: ResourceNamespace has_one :description, namespace: ResourceNamespace end class CommentSerializer < ActiveModel::Serializer; end class AuthorSerializer < ActiveModel::Serializer; end class DescriptionSerializer < ActiveModel::Serializer; end end def setup @comment = Comment.new @author = Author.new @description = Description.new @post = Post.new(comments: [@comment], author: @author, description: @description) @post_serializer = ResourceNamespace::PostSerializer.new(@post) end def test_associations_namespaced_serializers @post_serializer.associations.each do |association| case association.key when :comments, :latest_comments assert_instance_of(ResourceNamespace::CommentSerializer, association.lazy_association.serializer.first) when :author assert_instance_of(ResourceNamespace::AuthorSerializer, association.lazy_association.serializer) when :description assert_instance_of(ResourceNamespace::DescriptionSerializer, association.lazy_association.serializer) else flunk "Unknown association: #{key}" end end end end class NestedSerializersTest < ActiveSupport::TestCase class Post < ::Model associations :comments, :author, :description end class Comment < ::Model; end class Author < ::Model; end class Description < ::Model; end class PostSerializer < ActiveModel::Serializer has_many :comments class CommentSerializer < ActiveModel::Serializer; end belongs_to :author class AuthorSerializer < ActiveModel::Serializer; end has_one :description class DescriptionSerializer < ActiveModel::Serializer; end end def setup @comment = Comment.new @author = Author.new @description = Description.new @post = Post.new(comments: [@comment], author: @author, description: @description) @post_serializer = PostSerializer.new(@post) end def test_associations_namespaced_resources @post_serializer.associations.each do |association| case association.key when :comments assert_instance_of(PostSerializer::CommentSerializer, association.lazy_association.serializer.first) when :author assert_instance_of(PostSerializer::AuthorSerializer, association.lazy_association.serializer) when :description assert_instance_of(PostSerializer::DescriptionSerializer, association.lazy_association.serializer) else flunk "Unknown association: #{key}" end end end # rubocop:disable Metrics/AbcSize def test_conditional_associations model = Class.new(::Model) do attributes :true, :false associations :something end.new(true: true, false: false) scenarios = [ { options: { if: :true }, included: true }, { options: { if: :false }, included: false }, { options: { unless: :false }, included: true }, { options: { unless: :true }, included: false }, { options: { if: 'object.true' }, included: true }, { options: { if: 'object.false' }, included: false }, { options: { unless: 'object.false' }, included: true }, { options: { unless: 'object.true' }, included: false }, { options: { if: -> { object.true } }, included: true }, { options: { if: -> { object.false } }, included: false }, { options: { unless: -> { object.false } }, included: true }, { options: { unless: -> { object.true } }, included: false }, { options: { if: -> (s) { s.object.true } }, included: true }, { options: { if: -> (s) { s.object.false } }, included: false }, { options: { unless: -> (s) { s.object.false } }, included: true }, { options: { unless: -> (s) { s.object.true } }, included: false } ] scenarios.each do |s| serializer = Class.new(ActiveModel::Serializer) do belongs_to :something, s[:options] def true true end def false false end end hash = serializable(model, serializer: serializer).serializable_hash assert_equal(s[:included], hash.key?(:something), "Error with #{s[:options]}") end end def test_illegal_conditional_associations exception = assert_raises(TypeError) do Class.new(ActiveModel::Serializer) do belongs_to :x, if: nil end end assert_match(/:if should be a Symbol, String or Proc/, exception.message) end end class InheritedSerializerTest < ActiveSupport::TestCase class PostSerializer < ActiveModel::Serializer belongs_to :author has_many :comments belongs_to :blog end class InheritedPostSerializer < PostSerializer belongs_to :author, polymorphic: true has_many :comments, key: :reviews end class AuthorSerializer < ActiveModel::Serializer has_many :posts has_many :roles has_one :bio end class InheritedAuthorSerializer < AuthorSerializer has_many :roles, polymorphic: true has_one :bio, polymorphic: true end def setup @author = Author.new(name: 'Steve K.') @post = Post.new(title: 'New Post', body: 'Body') @post_serializer = PostSerializer.new(@post) @author_serializer = AuthorSerializer.new(@author) @inherited_post_serializer = InheritedPostSerializer.new(@post) @inherited_author_serializer = InheritedAuthorSerializer.new(@author) @author_associations = @author_serializer.associations.to_a.sort_by(&:name) @inherited_author_associations = @inherited_author_serializer.associations.to_a.sort_by(&:name) @post_associations = @post_serializer.associations.to_a @inherited_post_associations = @inherited_post_serializer.associations.to_a end test 'an author serializer must have [posts,roles,bio] associations' do expected = [:posts, :roles, :bio].sort result = @author_serializer.associations.map(&:name).sort assert_equal(result, expected) end test 'a post serializer must have [author,comments,blog] associations' do expected = [:author, :comments, :blog].sort result = @post_serializer.associations.map(&:name).sort assert_equal(result, expected) end test 'a serializer inheriting from another serializer can redefine has_many and has_one associations' do expected = [:roles, :bio].sort result = (@inherited_author_associations.map(&:reflection) - @author_associations.map(&:reflection)).map(&:name) assert_equal(result, expected) assert_equal [true, false, true], @inherited_author_associations.map(&:polymorphic?) assert_equal [false, false, false], @author_associations.map(&:polymorphic?) end test 'a serializer inheriting from another serializer can redefine belongs_to associations' do assert_equal [:author, :comments, :blog], @post_associations.map(&:name) assert_equal [:author, :comments, :blog, :comments], @inherited_post_associations.map(&:name) refute @post_associations.detect { |assoc| assoc.name == :author }.polymorphic? assert @inherited_post_associations.detect { |assoc| assoc.name == :author }.polymorphic? refute @post_associations.detect { |assoc| assoc.name == :comments }.key? original_comment_assoc, new_comments_assoc = @inherited_post_associations.select { |assoc| assoc.name == :comments } refute original_comment_assoc.key? assert_equal :reviews, new_comments_assoc.key original_blog = @post_associations.detect { |assoc| assoc.name == :blog } inherited_blog = @inherited_post_associations.detect { |assoc| assoc.name == :blog } original_parent_serializer = original_blog.lazy_association.association_options.delete(:parent_serializer) inherited_parent_serializer = inherited_blog.lazy_association.association_options.delete(:parent_serializer) assert_equal PostSerializer, original_parent_serializer.class assert_equal InheritedPostSerializer, inherited_parent_serializer.class end test 'a serializer inheriting from another serializer can have an additional association with the same name but with different key' do expected = [:author, :comments, :blog, :reviews].sort result = @inherited_post_serializer.associations.map(&:key).sort assert_equal(result, expected) end end end end end active_model_serializers-0.10.10/test/serializers/attribute_test.rb000066400000000000000000000126421351232231100256200ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class AttributeTest < ActiveSupport::TestCase def setup @blog = Blog.new(id: 1, name: 'AMS Hints', type: 'stuff') @blog_serializer = AlternateBlogSerializer.new(@blog) end def test_attributes_definition assert_equal([:id, :title], @blog_serializer.class._attributes) end def test_json_serializable_hash adapter = ActiveModelSerializers::Adapter::Json.new(@blog_serializer) assert_equal({ blog: { id: 1, title: 'AMS Hints' } }, adapter.serializable_hash) end def test_attribute_inheritance_with_key inherited_klass = Class.new(AlternateBlogSerializer) blog_serializer = inherited_klass.new(@blog) adapter = ActiveModelSerializers::Adapter::Attributes.new(blog_serializer) assert_equal({ id: 1, title: 'AMS Hints' }, adapter.serializable_hash) end def test_multiple_calls_with_the_same_attribute serializer_class = Class.new(ActiveModel::Serializer) do attribute :title attribute :title end assert_equal([:title], serializer_class._attributes) end def test_id_attribute_override serializer = Class.new(ActiveModel::Serializer) do attribute :name, key: :id end adapter = ActiveModelSerializers::Adapter::Json.new(serializer.new(@blog)) assert_equal({ blog: { id: 'AMS Hints' } }, adapter.serializable_hash) end def test_object_attribute_override serializer = Class.new(ActiveModel::Serializer) do attribute :name, key: :object end adapter = ActiveModelSerializers::Adapter::Json.new(serializer.new(@blog)) assert_equal({ blog: { object: 'AMS Hints' } }, adapter.serializable_hash) end def test_type_attribute attribute_serializer = Class.new(ActiveModel::Serializer) do attribute :id, key: :type end attributes_serializer = Class.new(ActiveModel::Serializer) do attributes :type end adapter = ActiveModelSerializers::Adapter::Json.new(attribute_serializer.new(@blog)) assert_equal({ blog: { type: 1 } }, adapter.serializable_hash) adapter = ActiveModelSerializers::Adapter::Json.new(attributes_serializer.new(@blog)) assert_equal({ blog: { type: 'stuff' } }, adapter.serializable_hash) end def test_id_attribute_override_before serializer = Class.new(ActiveModel::Serializer) do def id 'custom' end attribute :id end hash = ActiveModelSerializers::SerializableResource.new(@blog, adapter: :json, serializer: serializer).serializable_hash assert_equal('custom', hash[:blog][:id]) end class PostWithVirtualAttribute < ::Model; attributes :first_name, :last_name end class PostWithVirtualAttributeSerializer < ActiveModel::Serializer attribute :name do "#{object.first_name} #{object.last_name}" end end def test_virtual_attribute_block post = PostWithVirtualAttribute.new(first_name: 'Lucas', last_name: 'Hosseini') hash = serializable(post).serializable_hash expected = { name: 'Lucas Hosseini' } assert_equal(expected, hash) end # rubocop:disable Metrics/AbcSize def test_conditional_associations model = Class.new(::Model) do attributes :true, :false, :attribute end.new(true: true, false: false) scenarios = [ { options: { if: :true }, included: true }, { options: { if: :false }, included: false }, { options: { unless: :false }, included: true }, { options: { unless: :true }, included: false }, { options: { if: 'object.true' }, included: true }, { options: { if: 'object.false' }, included: false }, { options: { unless: 'object.false' }, included: true }, { options: { unless: 'object.true' }, included: false }, { options: { if: -> { object.true } }, included: true }, { options: { if: -> { object.false } }, included: false }, { options: { unless: -> { object.false } }, included: true }, { options: { unless: -> { object.true } }, included: false }, { options: { if: -> (s) { s.object.true } }, included: true }, { options: { if: -> (s) { s.object.false } }, included: false }, { options: { unless: -> (s) { s.object.false } }, included: true }, { options: { unless: -> (s) { s.object.true } }, included: false } ] scenarios.each do |s| serializer = Class.new(ActiveModel::Serializer) do attribute :attribute, s[:options] def true true end def false false end end hash = serializable(model, serializer: serializer).serializable_hash assert_equal(s[:included], hash.key?(:attribute), "Error with #{s[:options]}") end end def test_illegal_conditional_attributes exception = assert_raises(TypeError) do Class.new(ActiveModel::Serializer) do attribute :x, if: nil end end assert_match(/:if should be a Symbol, String or Proc/, exception.message) end end end end active_model_serializers-0.10.10/test/serializers/attributes_test.rb000066400000000000000000000034611351232231100260020ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class AttributesTest < ActiveSupport::TestCase def setup @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') @profile_serializer = ProfileSerializer.new(@profile) @comment = Comment.new(id: 1, body: 'ZOMG!!', date: '2015') @serializer_klass = Class.new(CommentSerializer) @serializer_klass_with_new_attributes = Class.new(CommentSerializer) do attributes :date, :likes end end def test_attributes_definition assert_equal([:name, :description], @profile_serializer.class._attributes) end def test_attributes_inheritance_definition assert_equal([:id, :body], @serializer_klass._attributes) end def test_attributes_inheritance serializer = @serializer_klass.new(@comment) assert_equal({ id: 1, body: 'ZOMG!!' }, serializer.attributes) end def test_attribute_inheritance_with_new_attribute_definition assert_equal([:id, :body, :date, :likes], @serializer_klass_with_new_attributes._attributes) assert_equal([:id, :body], CommentSerializer._attributes) end def test_attribute_inheritance_with_new_attribute serializer = @serializer_klass_with_new_attributes.new(@comment) assert_equal({ id: 1, body: 'ZOMG!!', date: '2015', likes: nil }, serializer.attributes) end def test_multiple_calls_with_the_same_attribute serializer_class = Class.new(ActiveModel::Serializer) do attributes :id, :title attributes :id, :title, :title, :body end assert_equal([:id, :title, :body], serializer_class._attributes) end end end end active_model_serializers-0.10.10/test/serializers/caching_configuration_test_isolated.rb000066400000000000000000000154511351232231100320250ustar00rootroot00000000000000# frozen_string_literal: true # Execute this test in isolation require 'support/isolated_unit' class CachingConfigurationTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation setup do require 'rails' # AMS needs to be required before Rails.application is initialized for # Railtie's to fire in Rails.application.initialize! # (and make_basic_app initializes the app) require 'active_model_serializers' # Create serializers before Rails.application.initialize! # To ensure we're testing that the cache settings depend on # the Railtie firing, not on the ActionController being loaded. create_serializers end def create_serializers @cached_serializer = Class.new(ActiveModel::Serializer) do cache skip_digest: true attributes :id, :name, :title end @fragment_cached_serializer = Class.new(ActiveModel::Serializer) do cache only: :id attributes :id, :name, :title end @non_cached_serializer = Class.new(ActiveModel::Serializer) do attributes :id, :name, :title end end class PerformCachingTrue < CachingConfigurationTest setup do # Let's make that Rails app and initialize it! make_basic_app do |app| app.config.action_controller.perform_caching = true app.config.action_controller.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) end controller_cache_store # Force ActiveSupport.on_load(:action_controller) to run end test 'it sets perform_caching to true on AMS.config and serializers' do assert Rails.configuration.action_controller.perform_caching assert ActiveModelSerializers.config.perform_caching assert ActiveModel::Serializer.perform_caching? assert @cached_serializer.perform_caching? assert @non_cached_serializer.perform_caching? assert @fragment_cached_serializer.perform_caching? end test 'it sets the AMS.config.cache_store to the controller cache_store' do assert_equal controller_cache_store, ActiveSupport::Cache::MemoryStore assert_equal controller_cache_store, ActiveModelSerializers.config.cache_store.class end test 'it sets the cached serializer cache_store to the ActionController::Base.cache_store' do assert_equal ActiveSupport::Cache::NullStore, @cached_serializer._cache.class assert_equal controller_cache_store, @cached_serializer.cache_store.class assert_equal ActiveSupport::Cache::MemoryStore, @cached_serializer._cache.class end test 'the cached serializer has cache_enabled?' do assert @cached_serializer.cache_enabled? end test 'the cached serializer does not have fragment_cache_enabled?' do refute @cached_serializer.fragment_cache_enabled? end test 'the non-cached serializer cache_store is nil' do assert_nil @non_cached_serializer._cache assert_nil @non_cached_serializer.cache_store assert_nil @non_cached_serializer._cache end test 'the non-cached serializer does not have cache_enabled?' do refute @non_cached_serializer.cache_enabled? end test 'the non-cached serializer does not have fragment_cache_enabled?' do refute @non_cached_serializer.fragment_cache_enabled? end test 'it sets the fragment cached serializer cache_store to the ActionController::Base.cache_store' do assert_equal ActiveSupport::Cache::NullStore, @fragment_cached_serializer._cache.class assert_equal controller_cache_store, @fragment_cached_serializer.cache_store.class assert_equal ActiveSupport::Cache::MemoryStore, @fragment_cached_serializer._cache.class end test 'the fragment cached serializer does not have cache_enabled?' do refute @fragment_cached_serializer.cache_enabled? end test 'the fragment cached serializer has fragment_cache_enabled?' do assert @fragment_cached_serializer.fragment_cache_enabled? end end class PerformCachingFalse < CachingConfigurationTest setup do # Let's make that Rails app and initialize it! make_basic_app do |app| app.config.action_controller.perform_caching = false app.config.action_controller.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) end controller_cache_store # Force ActiveSupport.on_load(:action_controller) to run end test 'it sets perform_caching to false on AMS.config and serializers' do refute Rails.configuration.action_controller.perform_caching refute ActiveModelSerializers.config.perform_caching refute ActiveModel::Serializer.perform_caching? refute @cached_serializer.perform_caching? refute @non_cached_serializer.perform_caching? refute @fragment_cached_serializer.perform_caching? end test 'it sets the AMS.config.cache_store to the controller cache_store' do assert_equal controller_cache_store, ActiveSupport::Cache::MemoryStore assert_equal controller_cache_store, ActiveModelSerializers.config.cache_store.class end test 'it sets the cached serializer cache_store to the ActionController::Base.cache_store' do assert_equal ActiveSupport::Cache::NullStore, @cached_serializer._cache.class assert_equal controller_cache_store, @cached_serializer.cache_store.class assert_equal ActiveSupport::Cache::MemoryStore, @cached_serializer._cache.class end test 'the cached serializer does not have cache_enabled?' do refute @cached_serializer.cache_enabled? end test 'the cached serializer does not have fragment_cache_enabled?' do refute @cached_serializer.fragment_cache_enabled? end test 'the non-cached serializer cache_store is nil' do assert_nil @non_cached_serializer._cache assert_nil @non_cached_serializer.cache_store assert_nil @non_cached_serializer._cache end test 'the non-cached serializer does not have cache_enabled?' do refute @non_cached_serializer.cache_enabled? end test 'the non-cached serializer does not have fragment_cache_enabled?' do refute @non_cached_serializer.fragment_cache_enabled? end test 'it sets the fragment cached serializer cache_store to the ActionController::Base.cache_store' do assert_equal ActiveSupport::Cache::NullStore, @fragment_cached_serializer._cache.class assert_equal controller_cache_store, @fragment_cached_serializer.cache_store.class assert_equal ActiveSupport::Cache::MemoryStore, @fragment_cached_serializer._cache.class end test 'the fragment cached serializer does not have cache_enabled?' do refute @fragment_cached_serializer.cache_enabled? end test 'the fragment cached serializer does not have fragment_cache_enabled?' do refute @fragment_cached_serializer.fragment_cache_enabled? end end def controller_cache_store ActionController::Base.cache_store.class end end active_model_serializers-0.10.10/test/serializers/configuration_test.rb000066400000000000000000000021301351232231100264530ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class ConfigurationTest < ActiveSupport::TestCase def test_collection_serializer assert_equal ActiveModel::Serializer::CollectionSerializer, ActiveModelSerializers.config.collection_serializer end def test_array_serializer assert_equal ActiveModel::Serializer::CollectionSerializer, ActiveModelSerializers.config.array_serializer end def test_setting_array_serializer_sets_collection_serializer config = ActiveModelSerializers.config old_config = config.dup begin assert_equal ActiveModel::Serializer::CollectionSerializer, config.collection_serializer config.array_serializer = :foo assert_equal config.array_serializer, :foo assert_equal config.collection_serializer, :foo ensure ActiveModelSerializers.config.replace(old_config) end end def test_default_adapter assert_equal :attributes, ActiveModelSerializers.config.adapter end end end end active_model_serializers-0.10.10/test/serializers/fieldset_test.rb000066400000000000000000000006411351232231100254100ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class FieldsetTest < ActiveSupport::TestCase def test_fieldset_with_hash fieldset = ActiveModel::Serializer::Fieldset.new('post' => %w(id title), 'comment' => ['body']) expected = { post: [:id, :title], comment: [:body] } assert_equal(expected, fieldset.fields) end end end end active_model_serializers-0.10.10/test/serializers/meta_test.rb000066400000000000000000000121631351232231100245410ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class MetaTest < ActiveSupport::TestCase def setup @blog = Blog.new(id: 1, name: 'AMS Hints', writer: Author.new(id: 2, name: 'Steve'), articles: [Post.new(id: 3, title: 'AMS')]) end def test_meta_is_present_with_root actual = ActiveModelSerializers::SerializableResource.new( @blog, adapter: :json, serializer: AlternateBlogSerializer, meta: { total: 10 } ).as_json expected = { blog: { id: 1, title: 'AMS Hints' }, 'meta' => { total: 10 } } assert_equal(expected, actual) end def test_meta_is_not_included_when_blank actual = ActiveModelSerializers::SerializableResource.new( @blog, adapter: :json, serializer: AlternateBlogSerializer, meta: {} ).as_json expected = { blog: { id: 1, title: 'AMS Hints' } } assert_equal(expected, actual) end def test_meta_is_not_included_when_empty_string actual = ActiveModelSerializers::SerializableResource.new( @blog, adapter: :json, serializer: AlternateBlogSerializer, meta: '' ).as_json expected = { blog: { id: 1, title: 'AMS Hints' } } assert_equal(expected, actual) end def test_meta_is_not_included_when_root_is_missing actual = ActiveModelSerializers::SerializableResource.new( @blog, adapter: :attributes, serializer: AlternateBlogSerializer, meta: { total: 10 } ).as_json expected = { id: 1, title: 'AMS Hints' } assert_equal(expected, actual) end def test_meta_key_is_used actual = ActiveModelSerializers::SerializableResource.new( @blog, adapter: :json, serializer: AlternateBlogSerializer, meta: { total: 10 }, meta_key: 'haha_meta' ).as_json expected = { blog: { id: 1, title: 'AMS Hints' }, 'haha_meta' => { total: 10 } } assert_equal(expected, actual) end def test_meta_key_is_not_used_with_json_api actual = ActiveModelSerializers::SerializableResource.new( @blog, adapter: :json_api, serializer: AlternateBlogSerializer, meta: { total: 10 }, meta_key: 'haha_meta' ).as_json expected = { data: { id: '1', type: 'blogs', attributes: { title: 'AMS Hints' } }, meta: { total: 10 } } assert_equal(expected, actual) end def test_meta_key_is_not_present_when_empty_hash_with_json_api actual = ActiveModelSerializers::SerializableResource.new( @blog, adapter: :json_api, serializer: AlternateBlogSerializer, meta: {} ).as_json expected = { data: { id: '1', type: 'blogs', attributes: { title: 'AMS Hints' } } } assert_equal(expected, actual) end def test_meta_key_is_not_present_when_empty_string_with_json_api actual = ActiveModelSerializers::SerializableResource.new( @blog, adapter: :json_api, serializer: AlternateBlogSerializer, meta: '' ).as_json expected = { data: { id: '1', type: 'blogs', attributes: { title: 'AMS Hints' } } } assert_equal(expected, actual) end def test_meta_is_not_present_on_arrays_without_root actual = ActiveModelSerializers::SerializableResource.new( [@blog], adapter: :attributes, meta: { total: 10 } ).as_json expected = [{ id: 1, name: 'AMS Hints', writer: { id: 2, name: 'Steve' }, articles: [{ id: 3, title: 'AMS', body: nil }] }] assert_equal(expected, actual) end def test_meta_is_present_on_arrays_with_root actual = ActiveModelSerializers::SerializableResource.new( [@blog], adapter: :json, meta: { total: 10 }, meta_key: 'haha_meta' ).as_json expected = { blogs: [{ id: 1, name: 'AMS Hints', writer: { id: 2, name: 'Steve' }, articles: [{ id: 3, title: 'AMS', body: nil }] }], 'haha_meta' => { total: 10 } } assert_equal(expected, actual) end end end end active_model_serializers-0.10.10/test/serializers/options_test.rb000066400000000000000000000020251351232231100253020ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class OptionsTest < ActiveSupport::TestCase class ModelWithOptions < ActiveModelSerializers::Model attributes :name, :description end class ModelWithOptionsSerializer < ActiveModel::Serializer attributes :name, :description def arguments_passed_in? instance_options[:my_options] == :accessible end end setup do @model_with_options = ModelWithOptions.new(name: 'Name 1', description: 'Description 1') end def test_options_are_accessible model_with_options_serializer = ModelWithOptionsSerializer.new(@model_with_options, my_options: :accessible) assert model_with_options_serializer.arguments_passed_in? end def test_no_option_is_passed_in model_with_options_serializer = ModelWithOptionsSerializer.new(@model_with_options) refute model_with_options_serializer.arguments_passed_in? end end end end active_model_serializers-0.10.10/test/serializers/read_attribute_for_serialization_test.rb000066400000000000000000000055051351232231100324160ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class ReadAttributeForSerializationTest < ActiveSupport::TestCase # https://github.com/rails-api/active_model_serializers/issues/1653 class Parent < ActiveModelSerializers::Model attributes :id end class Child < Parent attributes :name end class ParentSerializer < ActiveModel::Serializer attributes :$id define_method(:$id) do object.id end end class ChildSerializer < ParentSerializer attributes :name end def test_child_serializer_calls_dynamic_method_in_parent_serializer parent = ParentSerializer.new(Parent.new(id: 5)) child = ChildSerializer.new(Child.new(id: 6, name: 'Child')) assert_equal 5, parent.read_attribute_for_serialization(:$id) assert_equal 6, child.read_attribute_for_serialization(:$id) end # https://github.com/rails-api/active_model_serializers/issues/1658 class ErrorResponse < ActiveModelSerializers::Model attributes :error end class ApplicationSerializer < ActiveModel::Serializer attributes :status def status object.try(:errors).blank? && object.try(:error).blank? end end class ErrorResponseSerializer < ApplicationSerializer attributes :error end class ErrorResponseWithSuperSerializer < ApplicationSerializer attributes :error def success super end end def test_child_serializer_with_error_attribute error = ErrorResponse.new(error: 'i have an error') serializer = ErrorResponseSerializer.new(error) serializer_with_super = ErrorResponseWithSuperSerializer.new(error) assert_equal false, serializer.read_attribute_for_serialization(:status) assert_equal false, serializer_with_super.read_attribute_for_serialization(:status) end def test_child_serializer_with_errors error = ErrorResponse.new error.errors.add(:invalid, 'i am not valid') serializer = ErrorResponseSerializer.new(error) serializer_with_super = ErrorResponseWithSuperSerializer.new(error) assert_equal false, serializer.read_attribute_for_serialization(:status) assert_equal false, serializer_with_super.read_attribute_for_serialization(:status) end def test_child_serializer_no_error_attribute_or_errors error = ErrorResponse.new serializer = ErrorResponseSerializer.new(error) serializer_with_super = ErrorResponseWithSuperSerializer.new(error) assert_equal true, serializer.read_attribute_for_serialization(:status) assert_equal true, serializer_with_super.read_attribute_for_serialization(:status) end end end end active_model_serializers-0.10.10/test/serializers/reflection_test.rb000066400000000000000000000432261351232231100257510ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class ReflectionTest < ActiveSupport::TestCase class Blog < ActiveModelSerializers::Model attributes :id end class BlogSerializer < ActiveModel::Serializer type 'blog' attributes :id end setup do @expected_meta = { id: 1 } @expected_links = { self: 'no_uri_validation' } @empty_links = {} model_attributes = { blog: Blog.new(@expected_meta) } @model = Class.new(ActiveModelSerializers::Model) do attributes(*model_attributes.keys) def self.name 'TestModel' end end.new(model_attributes) @instance_options = {} end def evaluate_association_value(association) association.lazy_association.eval_reflection_block end # TODO: Remaining tests # test_reflection_value_block_with_scope # test_reflection_value_uses_serializer_instance_method # test_reflection_excluded_eh_blank_is_false # test_reflection_excluded_eh_if # test_reflection_excluded_eh_unless # test_evaluate_condition_symbol_serializer_method # test_evaluate_condition_string_serializer_method # test_evaluate_condition_proc # test_evaluate_condition_proc_yields_serializer # test_evaluate_condition_other # test_options_key # test_options_polymorphic # test_options_serializer # test_options_virtual_value # test_options_namespace def test_reflection_value serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) # Assert assert_nil reflection.block assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting) assert_equal true, reflection.options.fetch(:include_data_setting) include_slice = :does_not_matter assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice) end def test_reflection_value_block serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do object.blog end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) # Assert assert_respond_to reflection.block, :call assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting) assert_equal true, reflection.options.fetch(:include_data_setting) include_slice = :does_not_matter assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice) end def test_reflection_value_block_with_explicit_include_data_true serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do include_data true object.blog end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) # Assert assert_respond_to reflection.block, :call assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting) assert_equal true, reflection.options.fetch(:include_data_setting) include_slice = :does_not_matter assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice) end def test_reflection_value_block_with_include_data_false_mutates_the_reflection_include_data serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do include_data false object.blog end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) # Assert assert_respond_to reflection.block, :call assert_equal true, reflection.options.fetch(:include_data_setting) include_slice = :does_not_matter assert_nil reflection.send(:value, serializer_instance, include_slice) assert_equal false, reflection.options.fetch(:include_data_setting) end def test_reflection_value_block_with_include_data_if_sideloaded_included_mutates_the_reflection_include_data serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do include_data :if_sideloaded object.blog end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) # Assert assert_respond_to reflection.block, :call assert_equal true, reflection.options.fetch(:include_data_setting) include_slice = {} assert_nil reflection.send(:value, serializer_instance, include_slice) assert_equal :if_sideloaded, reflection.options.fetch(:include_data_setting) end def test_reflection_value_block_with_include_data_if_sideloaded_excluded_mutates_the_reflection_include_data serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do include_data :if_sideloaded object.blog end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) # Assert assert_respond_to reflection.block, :call assert_equal true, reflection.options.fetch(:include_data_setting) include_slice = { blog: :does_not_matter } assert_equal @model.blog, reflection.send(:value, serializer_instance, include_slice) assert_equal :if_sideloaded, reflection.options.fetch(:include_data_setting) end def test_reflection_block_with_link_mutates_the_reflection_links serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do link :self, 'no_uri_validation' end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) assert_equal @empty_links, reflection.options.fetch(:links) # Build Association association = reflection.build_association(serializer_instance, @instance_options) # Assert association links empty when not yet evaluated assert_equal @empty_links, reflection.options.fetch(:links) assert_equal @empty_links, association.links evaluate_association_value(association) assert_equal @expected_links, association.links assert_equal @expected_links, reflection.options.fetch(:links) end def test_reflection_block_with_link_block_mutates_the_reflection_links serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do link :self do 'no_uri_validation' end end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) assert_equal @empty_links, reflection.options.fetch(:links) # Build Association association = reflection.build_association(serializer_instance, @instance_options) # Assert association links empty when not yet evaluated assert_equal @empty_links, association.links evaluate_association_value(association) # Assert before instance_eval link link = association.links.fetch(:self) assert_respond_to link, :call assert_respond_to reflection.options.fetch(:links).fetch(:self), :call # Assert after instance_eval link assert_equal @expected_links.fetch(:self), reflection.instance_eval(&link) assert_respond_to reflection.options.fetch(:links).fetch(:self), :call end def test_reflection_block_with_meta_mutates_the_reflection_meta serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do meta(id: object.blog.id) end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) assert_nil reflection.options.fetch(:meta) # Build Association association = reflection.build_association(serializer_instance, @instance_options) evaluate_association_value(association) assert_equal @expected_meta, association.meta assert_equal @expected_meta, reflection.options.fetch(:meta) end def test_reflection_block_with_meta_block_mutates_the_reflection_meta serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do meta do { id: object.blog.id } end end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) assert_nil reflection.options.fetch(:meta) # Build Association association = reflection.build_association(serializer_instance, @instance_options) # Assert before instance_eval meta evaluate_association_value(association) assert_respond_to association.meta, :call assert_respond_to reflection.options.fetch(:meta), :call # Assert after instance_eval meta assert_equal @expected_meta, reflection.instance_eval(&association.meta) assert_respond_to reflection.options.fetch(:meta), :call assert_respond_to association.meta, :call end # rubocop:disable Metrics/AbcSize def test_reflection_block_with_meta_in_link_block_mutates_the_reflection_meta serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do link :self do meta(id: object.blog.id) 'no_uri_validation' end end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) assert_nil reflection.options.fetch(:meta) assert_equal @empty_links, reflection.options.fetch(:links) # Build Association association = reflection.build_association(serializer_instance, @instance_options) # Assert before instance_eval link meta assert_nil association.meta assert_nil reflection.options.fetch(:meta) evaluate_association_value(association) link = association.links.fetch(:self) assert_respond_to link, :call assert_respond_to reflection.options.fetch(:links).fetch(:self), :call assert_nil reflection.options.fetch(:meta) # Assert after instance_eval link assert_equal 'no_uri_validation', reflection.instance_eval(&link) assert_equal @expected_meta, reflection.options.fetch(:meta) assert_equal @expected_meta, association.meta end # rubocop:enable Metrics/AbcSize # rubocop:disable Metrics/AbcSize def test_reflection_block_with_meta_block_in_link_block_mutates_the_reflection_meta serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do link :self do meta do { id: object.blog.id } end 'no_uri_validation' end end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) assert_nil reflection.options.fetch(:meta) # Build Association association = reflection.build_association(serializer_instance, @instance_options) assert_nil association.meta assert_nil reflection.options.fetch(:meta) # Assert before instance_eval link evaluate_association_value(association) link = association.links.fetch(:self) assert_nil reflection.options.fetch(:meta) assert_respond_to link, :call assert_respond_to association.links.fetch(:self), :call # Assert after instance_eval link assert_equal 'no_uri_validation', reflection.instance_eval(&link) assert_respond_to association.links.fetch(:self), :call # Assert before instance_eval link meta assert_respond_to reflection.options.fetch(:meta), :call assert_respond_to association.meta, :call # Assert after instance_eval link meta assert_equal @expected_meta, reflection.instance_eval(&reflection.options.fetch(:meta)) assert_respond_to association.meta, :call end # rubocop:enable Metrics/AbcSize def test_no_href_in_vanilla_reflection serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do link :self do href 'no_uri_validation' end end end serializer_instance = serializer_class.new(@model, @instance_options) # Get Reflection reflection = serializer_class._reflections.fetch(:blog) assert_equal @empty_links, reflection.options.fetch(:links) # Build Association association = reflection.build_association(serializer_instance, @instance_options) # Assert before instance_eval link evaluate_association_value(association) link = association.links.fetch(:self) assert_respond_to link, :call # Assert after instance_eval link exception = assert_raise(NoMethodError) do reflection.instance_eval(&link) end assert_match(/undefined method `href'/, exception.message) end # rubocop:disable Metrics/AbcSize def test_mutating_reflection_block_is_not_thread_safe serializer_class = Class.new(ActiveModel::Serializer) do has_one :blog do meta(id: object.blog.id) end end model1_meta = @expected_meta # Evaluate reflection meta for model with id 1 serializer_instance = serializer_class.new(@model, @instance_options) reflection = serializer_class._reflections.fetch(:blog) assert_nil reflection.options.fetch(:meta) association = reflection.build_association(serializer_instance, @instance_options) evaluate_association_value(association) assert_equal model1_meta, association.meta assert_equal model1_meta, reflection.options.fetch(:meta) model2_meta = @expected_meta.merge(id: 2) # Evaluate reflection meta for model with id 2 @model.blog.id = 2 assert_equal 2, @model.blog.id # sanity check serializer_instance = serializer_class.new(@model, @instance_options) reflection = serializer_class._reflections.fetch(:blog) # WARN: Thread-safety issue # Before the reflection is evaluated, it has the value from the previous evaluation assert_equal model1_meta, reflection.options.fetch(:meta) association = reflection.build_association(serializer_instance, @instance_options) evaluate_association_value(association) assert_equal model2_meta, association.meta assert_equal model2_meta, reflection.options.fetch(:meta) end # rubocop:enable Metrics/AbcSize end class ThreadedReflectionTest < ActiveSupport::TestCase class Post < ::Model attributes :id, :title, :body associations :comments end class Comment < ::Model attributes :id, :body associations :post end class CommentSerializer < ActiveModel::Serializer type 'comment' attributes :id, :body has_one :post end class PostSerializer < ActiveModel::Serializer type 'post' attributes :id, :title, :body has_many :comments, serializer: CommentSerializer do sleep 0.1 object.comments end end # per https://github.com/rails-api/active_model_serializers/issues/2270 def test_concurrent_serialization post1 = Post.new(id: 1, title: 'Post 1 Title', body: 'Post 1 Body') post1.comments = [Comment.new(id: 1, body: 'Comment on Post 1', post: post1)] post2 = Post.new(id: 2, title: 'Post 2 Title', body: 'Post 2 Body') post2.comments = [Comment.new(id: 2, body: 'Comment on Post 2', post: post2)] serialized_posts = { first: Set.new, second: Set.new } t1 = Thread.new do 10.times do serialized_posts[:first] << PostSerializer.new(post1, {}).to_json end end t2 = Thread.new do 10.times do serialized_posts[:second] << PostSerializer.new(post2, {}).to_json end end t1.join t2.join expected_first_post_serialization = '{"id":1,"title":"Post 1 Title","body":"Post 1 Body","comments":[{"id":1,"body":"Comment on Post 1"}]}' expected_second_post_serialization = '{"id":2,"title":"Post 2 Title","body":"Post 2 Body","comments":[{"id":2,"body":"Comment on Post 2"}]}' assert_equal [expected_second_post_serialization], serialized_posts[:second].to_a assert_equal [expected_first_post_serialization], serialized_posts[:first].to_a end end end end active_model_serializers-0.10.10/test/serializers/root_test.rb000066400000000000000000000010671351232231100245770ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class RootTest < ActiveSupport::TestCase def setup @virtual_value = VirtualValue.new(id: 1) end def test_overwrite_root serializer = VirtualValueSerializer.new(@virtual_value, root: 'smth') assert_equal('smth', serializer.json_key) end def test_underscore_in_root serializer = VirtualValueSerializer.new(@virtual_value) assert_equal('virtual_value', serializer.json_key) end end end end active_model_serializers-0.10.10/test/serializers/serialization_test.rb000066400000000000000000000043251351232231100264710ustar00rootroot00000000000000# frozen_string_literal: true module ActiveModel class Serializer class SerializationTest < ActiveSupport::TestCase class Blog < ActiveModelSerializers::Model attributes :id, :name, :authors end class Author < ActiveModelSerializers::Model attributes :id, :name end class BlogSerializer < ActiveModel::Serializer attributes :id attribute :name, key: :title has_many :authors end class AuthorSerializer < ActiveModel::Serializer attributes :id, :name end setup do @authors = [Author.new(id: 1, name: 'Blog Author')] @blog = Blog.new(id: 2, name: 'The Blog', authors: @authors) @serializer_instance = BlogSerializer.new(@blog) @serializable = ActiveModelSerializers::SerializableResource.new(@blog, serializer: BlogSerializer, adapter: :attributes) @expected_hash = { id: 2, title: 'The Blog', authors: [{ id: 1, name: 'Blog Author' }] } @expected_json = '{"id":2,"title":"The Blog","authors":[{"id":1,"name":"Blog Author"}]}' end test '#serializable_hash is the same as generated by the attributes adapter' do assert_equal @serializable.serializable_hash, @serializer_instance.serializable_hash assert_equal @expected_hash, @serializer_instance.serializable_hash end test '#as_json is the same as generated by the attributes adapter' do assert_equal @serializable.as_json, @serializer_instance.as_json assert_equal @expected_hash, @serializer_instance.as_json end test '#to_json is the same as generated by the attributes adapter' do assert_equal @serializable.to_json, @serializer_instance.to_json assert_equal @expected_json, @serializer_instance.to_json end test '#to_h is an alias for #serializable_hash' do assert_equal @serializable.serializable_hash, @serializer_instance.to_h assert_equal @expected_hash, @serializer_instance.to_h end test '#to_hash is an alias for #serializable_hash' do assert_equal @serializable.serializable_hash, @serializer_instance.to_hash assert_equal @expected_hash, @serializer_instance.to_hash end end end end active_model_serializers-0.10.10/test/serializers/serializer_for_test.rb000066400000000000000000000110541351232231100266300ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class SerializerForTest < ActiveSupport::TestCase class CollectionSerializerTest < ActiveSupport::TestCase def setup @array = [1, 2, 3] @previous_collection_serializer = ActiveModelSerializers.config.collection_serializer end def teardown ActiveModelSerializers.config.collection_serializer = @previous_collection_serializer end def test_serializer_for_array serializer = ActiveModel::Serializer.serializer_for(@array) assert_equal ActiveModelSerializers.config.collection_serializer, serializer end def test_overwritten_serializer_for_array new_collection_serializer = Class.new ActiveModelSerializers.config.collection_serializer = new_collection_serializer serializer = ActiveModel::Serializer.serializer_for(@array) assert_equal new_collection_serializer, serializer end end class SerializerTest < ActiveSupport::TestCase module ResourceNamespace class Post < ::Model; end class Comment < ::Model; end class PostSerializer < ActiveModel::Serializer class CommentSerializer < ActiveModel::Serializer end end end class MyProfile < Profile end class CustomProfile def serializer_class ProfileSerializer end end class Tweet < ::Model; end TweetSerializer = Class.new def setup @profile = Profile.new @my_profile = MyProfile.new @custom_profile = CustomProfile.new @model = ::Model.new @tweet = Tweet.new end def test_serializer_for_non_ams_serializer serializer = ActiveModel::Serializer.serializer_for(@tweet) assert_nil serializer end def test_serializer_for_existing_serializer serializer = ActiveModel::Serializer.serializer_for(@profile) assert_equal ProfileSerializer, serializer end def test_serializer_for_existing_serializer_with_lookup_disabled serializer = with_serializer_lookup_disabled do ActiveModel::Serializer.serializer_for(@profile) end assert_nil serializer end def test_serializer_for_not_existing_serializer serializer = ActiveModel::Serializer.serializer_for(@model) assert_nil serializer end def test_serializer_inherited_serializer serializer = ActiveModel::Serializer.serializer_for(@my_profile) assert_equal ProfileSerializer, serializer end def test_serializer_inherited_serializer_with_lookup_disabled serializer = with_serializer_lookup_disabled do ActiveModel::Serializer.serializer_for(@my_profile) end assert_nil serializer end def test_serializer_custom_serializer serializer = ActiveModel::Serializer.serializer_for(@custom_profile) assert_equal ProfileSerializer, serializer end def test_serializer_custom_serializer_with_lookup_disabled serializer = with_serializer_lookup_disabled do ActiveModel::Serializer.serializer_for(@custom_profile) end assert_equal ProfileSerializer, serializer end def test_serializer_for_namespaced_resource post = ResourceNamespace::Post.new serializer = ActiveModel::Serializer.serializer_for(post) assert_equal ResourceNamespace::PostSerializer, serializer end def test_serializer_for_namespaced_resource_with_lookup_disabled post = ResourceNamespace::Post.new serializer = with_serializer_lookup_disabled do ActiveModel::Serializer.serializer_for(post) end assert_nil serializer end def test_serializer_for_nested_resource comment = ResourceNamespace::Comment.new serializer = ResourceNamespace::PostSerializer.serializer_for(comment) assert_equal ResourceNamespace::PostSerializer::CommentSerializer, serializer end def test_serializer_for_nested_resource_with_lookup_disabled comment = ResourceNamespace::Comment.new serializer = with_serializer_lookup_disabled do ResourceNamespace::PostSerializer.serializer_for(comment) end assert_nil serializer end end end end end active_model_serializers-0.10.10/test/serializers/serializer_for_with_namespace_test.rb000066400000000000000000000060701351232231100317010ustar00rootroot00000000000000# frozen_string_literal: true require 'test_helper' module ActiveModel class Serializer class SerializerForWithNamespaceTest < ActiveSupport::TestCase class Book < ::Model attributes :title, :author_name associations :publisher, :pages end class Ebook < Book; end class Page < ::Model; attributes :number, :text end class Publisher < ::Model; attributes :name end module Api module V3 class BookSerializer < ActiveModel::Serializer attributes :title, :author_name has_many :pages belongs_to :publisher end class PageSerializer < ActiveModel::Serializer attributes :number, :text end class PublisherSerializer < ActiveModel::Serializer attributes :name end end end class BookSerializer < ActiveModel::Serializer attributes :title, :author_name end test 'resource without a namespace' do book = Book.new(title: 'A Post', author_name: 'hello') # TODO: this should be able to pull up this serializer without explicitly specifying the serializer # currently, with no options, it still uses the Api::V3 serializer result = ActiveModelSerializers::SerializableResource.new(book, serializer: BookSerializer).serializable_hash expected = { title: 'A Post', author_name: 'hello' } assert_equal expected, result end test 'resource with namespace' do book = Book.new(title: 'A Post', author_name: 'hi') result = ActiveModelSerializers::SerializableResource.new(book, namespace: Api::V3).serializable_hash expected = { title: 'A Post', author_name: 'hi', pages: nil, publisher: nil } assert_equal expected, result end test 'has_many with nested serializer under the namespace' do page = Page.new(number: 1, text: 'hello') book = Book.new(title: 'A Post', author_name: 'hi', pages: [page]) result = ActiveModelSerializers::SerializableResource.new(book, namespace: Api::V3).serializable_hash expected = { title: 'A Post', author_name: 'hi', publisher: nil, pages: [{ number: 1, text: 'hello' }] } assert_equal expected, result end test 'belongs_to with nested serializer under the namespace' do publisher = Publisher.new(name: 'Disney') book = Book.new(title: 'A Post', author_name: 'hi', publisher: publisher) result = ActiveModelSerializers::SerializableResource.new(book, namespace: Api::V3).serializable_hash expected = { title: 'A Post', author_name: 'hi', pages: nil, publisher: { name: 'Disney' } } assert_equal expected, result end test 'follows inheritance with a namespace' do serializer = ActiveModel::Serializer.serializer_for(Ebook.new, namespace: Api::V3) assert_equal Api::V3::BookSerializer, serializer end end end end active_model_serializers-0.10.10/test/support/000077500000000000000000000000001351232231100214045ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/custom_schemas/000077500000000000000000000000001351232231100244215ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/custom_schemas/active_model_serializers/000077500000000000000000000000001351232231100314705ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/custom_schemas/active_model_serializers/test/000077500000000000000000000000001351232231100324475ustar00rootroot00000000000000schema_test/000077500000000000000000000000001351232231100346675ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/custom_schemas/active_model_serializers/testmy/000077500000000000000000000000001351232231100353145ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/custom_schemas/active_model_serializers/test/schema_testindex.json000066400000000000000000000001501351232231100373120ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/custom_schemas/active_model_serializers/test/schema_test/my{ "properties": { "name" : { "type" : "string" }, "description" : { "type" : "string" } } } active_model_serializers-0.10.10/test/support/isolated_unit.rb000066400000000000000000000047711351232231100246050ustar00rootroot00000000000000# frozen_string_literal: true # https://github.com/rails/rails/blob/v5.0.0.beta1/railties/test/isolation/abstract_unit.rb # Usage Example: # # require 'support/isolated_unit' # # class RailtieTest < ActiveSupport::TestCase # include ActiveSupport::Testing::Isolation # # class WithRailsDefinedOnLoad < RailtieTest # setup do # require 'rails' # require 'active_model_serializers' # make_basic_app # end # # # some tests # end # # class WithoutRailsDefinedOnLoad < RailtieTest # setup do # require 'active_model_serializers' # make_basic_app # end # # # some tests # end # end # # Note: # It is important to keep this file as light as possible # the goal for tests that require this is to test booting up # rails from an empty state, so anything added here could # hide potential failures # # It is also good to know what is the bare minimum to get # Rails booted up. require 'bundler/setup' unless defined?(Bundler) require 'active_support' require 'active_support/core_ext/string/access' # These files do not require any others and are needed # to run the tests require 'active_support/testing/autorun' require 'active_support/testing/isolation' module TestHelpers module Generation module_function # Make a very basic app, without creating the whole directory structure. # Is faster and simpler than generating a Rails app in a temp directory def make_basic_app require 'rails' require 'action_controller/railtie' app = Class.new(Rails::Application) do config.eager_load = false config.session_store :cookie_store, key: '_myapp_session' config.active_support.deprecation = :log config.active_support.test_order = :parallel ActiveSupport::TestCase.respond_to?(:test_order=) && ActiveSupport::TestCase.test_order = :parallel config.root = File.dirname(__FILE__) config.log_level = :info # Set a fake logger to avoid creating the log directory automatically fake_logger = Logger.new(nil) config.logger = fake_logger Rails.application.routes.default_url_options = { host: 'example.com' } end def app.name; 'IsolatedRailsApp'; end # rubocop:disable Style/SingleLineMethods app.respond_to?(:secrets) && app.secrets.secret_key_base = '3b7cd727ee24e8444053437c36cc66c4' @app = app yield @app if block_given? @app.initialize! end end end module ActiveSupport class TestCase include TestHelpers::Generation end end active_model_serializers-0.10.10/test/support/rails5_shims.rb000066400000000000000000000025421351232231100243360ustar00rootroot00000000000000# frozen_string_literal: true module Rails5Shims module ControllerTests # https://github.com/rails/rails/blob/b217354/actionpack/lib/action_controller/test_case.rb REQUEST_KWARGS = [:params, :headers, :session, :flash, :method, :body, :xhr].freeze def get(path, *args) fold_kwargs!(args) super end def post(path, *args) fold_kwargs!(args) super end def patch(path, *args) fold_kwargs!(args) super end def put(path, *args) fold_kwargs!(args) super end # Fold kwargs from test request into args # Band-aid for DEPRECATION WARNING def fold_kwargs!(args) hash = args && args[0] return unless hash.respond_to?(:key) Rails5Shims::ControllerTests::REQUEST_KWARGS.each do |kwarg| next unless hash.key?(kwarg) value = hash.delete(kwarg) if value.is_a? String args.insert(0, value) else hash.merge! value end end end # Uncomment for debugging where the kwargs warnings come from # def non_kwarg_request_warning # super.tap do # STDOUT.puts caller[2..3] # end # end end end if Rails::VERSION::MAJOR < 5 ActionController::TestCase.send :include, Rails5Shims::ControllerTests ActionDispatch::IntegrationTest.send :include, Rails5Shims::ControllerTests end active_model_serializers-0.10.10/test/support/rails_app.rb000066400000000000000000000025571351232231100237140ustar00rootroot00000000000000# frozen_string_literal: true require 'support/isolated_unit' module ActiveModelSerializers RailsApplication = TestHelpers::Generation.make_basic_app do |app| app.configure do config.secret_key_base = 'abc123' config.active_support.test_order = :random config.action_controller.perform_caching = true config.action_controller.cache_store = :memory_store config.filter_parameters += [:password] end app.routes.default_url_options = { host: 'example.com' } end end Routes = ActionDispatch::Routing::RouteSet.new Routes.draw do get ':controller(/:action(/:id))' get ':controller(/:action)' end ActionController::Base.send :include, Routes.url_helpers ActionController::TestCase.class_eval do def setup @routes = Routes end end # ActiveRecord::Migrator.migrations_paths = [File.expand_path("../../test/dummy/db/migrate", __FILE__)] # ActiveRecord::Migrator.migrations_paths << File.expand_path('../../db/migrate', __FILE__) # # Load fixtures from the engine # if ActiveSupport::TestCase.respond_to?(:fixture_path=) # ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__) # ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path # ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files" # ActiveSupport::TestCase.fixtures :all # end active_model_serializers-0.10.10/test/support/ruby_2_6_rails_4_2_patch.rb000066400000000000000000000012371351232231100264000ustar00rootroot00000000000000# frozen_string_literal: true if RUBY_VERSION >= '2.6.0' if Rails::VERSION::MAJOR < 5 module ActionController class TestResponse < ActionDispatch::TestResponse def recycle! # HACK: to avoid MonitorMixin double-initialize error: @mon_mutex_owner_object_id = nil @mon_mutex = nil initialize end end end else msg = 'Monkeypatch for ActionController::TestResponse not needed for '\ 'Rails 5+. We can drop this patch once we drop support for Rails < 5. '\ "Current Rails version: #{ENV['RAILS_VERSION']}" puts puts "\033[33m **** #{msg} **** \033[0m" puts end end active_model_serializers-0.10.10/test/support/schemas/000077500000000000000000000000001351232231100230275ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/schemas/active_model_serializers/000077500000000000000000000000001351232231100300765ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/schemas/active_model_serializers/test/000077500000000000000000000000001351232231100310555ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/schemas/active_model_serializers/test/schema_test/000077500000000000000000000000001351232231100333545ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/schemas/active_model_serializers/test/schema_test/my/000077500000000000000000000000001351232231100340015ustar00rootroot00000000000000index.json000066400000000000000000000001501351232231100357200ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/schemas/active_model_serializers/test/schema_test/my{ "properties": { "name" : { "type" : "string" }, "description" : { "type" : "string" } } } show.json000066400000000000000000000001521351232231100355730ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/schemas/active_model_serializers/test/schema_test/my{ "properties": { "name" : { "type" : "integer" }, "description" : { "type" : "boolean" } } } active_model_serializers-0.10.10/test/support/schemas/custom/000077500000000000000000000000001351232231100243415ustar00rootroot00000000000000active_model_serializers-0.10.10/test/support/schemas/custom/show.json000066400000000000000000000002141351232231100262110ustar00rootroot00000000000000{ "id": "file://custom/show.json#", "properties": { "name" : { "type" : "string" }, "description" : { "type" : "string" } } } active_model_serializers-0.10.10/test/support/schemas/hyper_schema.json000066400000000000000000000037701351232231100264000ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/hyper-schema", "title": "Profile", "description": "Profile schema", "stability": "prototype", "strictProperties": true, "type": [ "object" ], "definitions": { "name": { "description": "unique name of profile", "readOnly": true, "type": [ "string" ] }, "description": { "description": "description of profile", "readOnly": true, "type": [ "string" ] }, "identity": { "anyOf": [ { "$ref": "/schemata/profile#/definitions/name" } ] } }, "links": [ { "description": "Create a new profile.", "href": "/profiles", "method": "POST", "rel": "create", "schema": { "properties": { }, "type": [ "object" ] }, "title": "Create" }, { "description": "Delete an existing profile.", "href": "/profiles/{(%2Fschemata%2Fprofile%23%2Fdefinitions%2Fidentity)}", "method": "DELETE", "rel": "destroy", "title": "Delete" }, { "description": "Info for existing profile.", "href": "/profiles/{(%2Fschemata%2Fprofile%23%2Fdefinitions%2Fidentity)}", "method": "GET", "rel": "self", "title": "Info" }, { "description": "List existing profiles.", "href": "/profiles", "method": "GET", "rel": "instances", "title": "List" }, { "description": "Update an existing profile.", "href": "/profiles/{(%2Fschemata%2Fprofile%23%2Fdefinitions%2Fidentity)}", "method": "PATCH", "rel": "update", "schema": { "properties": { }, "type": [ "object" ] }, "title": "Update" } ], "properties": { "name": { "$ref": "/schemata/profile#/definitions/name" }, "description": { "$ref": "/schemata/profile#/definitions/description" } }, "id": "/schemata/profile" } active_model_serializers-0.10.10/test/support/schemas/render_using_json_api.json000066400000000000000000000015401351232231100302700ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema#", "id": "", "type": "object", "properties": { "data": { "id": "/data", "type": "object", "properties": { "id": { "id": "/data/id", "type": "string" }, "type": { "id": "/data/type", "type": "string" }, "attributes": { "id": "/data/attributes", "type": "object", "properties": { "name": { "id": "/data/attributes/name", "type": "string" }, "description": { "id": "/data/attributes/description", "type": "string" } } } }, "required": [ "id", "type", "attributes" ] } }, "required": [ "data" ] } active_model_serializers-0.10.10/test/support/schemas/simple_json_pointers.json000066400000000000000000000003031351232231100301630ustar00rootroot00000000000000{ "properties": { "name": { "$ref": "file://custom/show.json#/properties/name" }, "description": { "$ref": "file://custom/show.json#/properties/description" } } } active_model_serializers-0.10.10/test/support/serialization_testing.rb000066400000000000000000000047041351232231100263500ustar00rootroot00000000000000# frozen_string_literal: true module SerializationTesting def config ActiveModelSerializers.config end private def generate_cached_serializer(obj) ActiveModelSerializers::SerializableResource.new(obj).to_json end def with_namespace_separator(separator) original_separator = ActiveModelSerializers.config.jsonapi_namespace_separator ActiveModelSerializers.config.jsonapi_namespace_separator = separator yield ensure ActiveModelSerializers.config.jsonapi_namespace_separator = original_separator end def with_prepended_lookup(lookup_proc) original_lookup = ActiveModelSerializers.config.serializer_lookup_cahin ActiveModelSerializers.config.serializer_lookup_chain.unshift lookup_proc yield ensure ActiveModelSerializers.config.serializer_lookup_cahin = original_lookup end # Aliased as :with_configured_adapter to clarify that # this method tests the configured adapter. # When not testing configuration, it may be preferable # to pass in the +adapter+ option to ActiveModelSerializers::SerializableResource. # e.g ActiveModelSerializers::SerializableResource.new(resource, adapter: :json_api) def with_adapter(adapter) old_adapter = ActiveModelSerializers.config.adapter ActiveModelSerializers.config.adapter = adapter yield ensure ActiveModelSerializers.config.adapter = old_adapter end alias with_configured_adapter with_adapter def with_config(hash) old_config = config.dup ActiveModelSerializers.config.update(hash) yield ensure ActiveModelSerializers.config.replace(old_config) end def with_jsonapi_inflection(inflection) original_inflection = ActiveModelSerializers.config.jsonapi_resource_type ActiveModelSerializers.config.jsonapi_resource_type = inflection yield ensure ActiveModelSerializers.config.jsonapi_resource_type = original_inflection end def with_serializer_lookup_disabled original_serializer_lookup = ActiveModelSerializers.config.serializer_lookup_enabled ActiveModelSerializers.config.serializer_lookup_enabled = false yield ensure ActiveModelSerializers.config.serializer_lookup_enabled = original_serializer_lookup end def serializable(resource, options = {}) ActiveModelSerializers::SerializableResource.new(resource, options) end end module Minitest class Test def before_setup ActionController::Base.cache_store.clear end include SerializationTesting end end active_model_serializers-0.10.10/test/test_helper.rb000066400000000000000000000035471351232231100225440ustar00rootroot00000000000000# frozen_string_literal: true # Configure Rails Environment ENV['RAILS_ENV'] = 'test' require 'bundler/setup' begin require 'simplecov' AppCoverage.start rescue LoadError STDERR.puts 'Running without SimpleCov' end require 'pry' require 'timecop' require 'rails' require 'action_controller' require 'action_controller/test_case' require 'action_controller/railtie' require 'active_model_serializers' # For now, we only restrict the options to serializable_hash/as_json/to_json # in tests, to ensure developers don't add any unsupported options. # There's no known benefit, at this time, to having the filtering run in # production when the excluded options would simply not be used. # # However, for documentation purposes, the constant # ActiveModel::Serializer::SERIALIZABLE_HASH_VALID_KEYS is defined # in the Serializer. ActiveModelSerializers::Adapter::Base.class_eval do alias_method :original_serialization_options, :serialization_options def serialization_options(options) original_serialization_options(options) .slice(*ActiveModel::Serializer::SERIALIZABLE_HASH_VALID_KEYS) end end require 'fileutils' FileUtils.mkdir_p(File.expand_path('../../tmp/cache', __FILE__)) gem 'minitest' require 'minitest' require 'minitest/autorun' Minitest.backtrace_filter = Minitest::BacktraceFilter.new module TestHelper module_function def silence_warnings original_verbose = $VERBOSE $VERBOSE = nil yield ensure $VERBOSE = original_verbose end end require 'support/rails_app' require 'support/ruby_2_6_rails_4_2_patch' # require "rails/test_help" require 'support/serialization_testing' require 'support/rails5_shims' require 'fixtures/active_record' require 'fixtures/poro' ActiveSupport.on_load(:action_controller) do $action_controller_logger = ActiveModelSerializers.logger ActiveModelSerializers.logger = Logger.new(IO::NULL) end