diff --git a/.circleci/config.yml b/.circleci/config.yml index 23fcf9e1..9ad5d45b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,35 +1,91 @@ -version: 2 +version: 2.1 jobs: - "Test against Ruby 2.4": + test-ruby-2-4: docker: - image: circleci/ruby:2.4.9 working_directory: ~/intercom-ruby steps: - checkout + - run: + name: Route Bundler through the Socket Firewall registry + command: | + set -euo pipefail + if [ -z "${SOCKET_GEM_URL:-}" ]; then + # CircleCI does not share context credentials with forked PRs, so a + # fork cannot be routed. Say so rather than failing the contributor. + if [ -n "${CIRCLE_PR_REPONAME:-}" ]; then + echo "Forked PR: no Socket Firewall credentials available, gems will come from rubygems.org." + exit 0 + fi + echo "FATAL: SOCKET_GEM_URL is required (provided by the socket-firewall context)" + exit 1 + fi + # Legacy two-argument form: these images ship Bundler 1.x, where + # `bundle config set --global` does not exist and silently no-ops. + bundle config mirror.https://rubygems.org "$SOCKET_GEM_URL" + bundle config mirror.https://rubygems.org | grep -q socket-firewall-registry \ + || { echo "FAIL: bundler mirror not pointing at Socket Firewall"; exit 1; } - run: bundle install - run: bundle exec rake - "Test against Ruby 2.5": + test-ruby-2-5: docker: - image: circleci/ruby:2.5.7 working_directory: ~/intercom-ruby steps: - checkout + - run: + name: Route Bundler through the Socket Firewall registry + command: | + set -euo pipefail + if [ -z "${SOCKET_GEM_URL:-}" ]; then + if [ -n "${CIRCLE_PR_REPONAME:-}" ]; then + echo "Forked PR: no Socket Firewall credentials available, gems will come from rubygems.org." + exit 0 + fi + echo "FATAL: SOCKET_GEM_URL is required (provided by the socket-firewall context)" + exit 1 + fi + bundle config mirror.https://rubygems.org "$SOCKET_GEM_URL" + bundle config mirror.https://rubygems.org | grep -q socket-firewall-registry \ + || { echo "FAIL: bundler mirror not pointing at Socket Firewall"; exit 1; } - run: bundle install - run: bundle exec rake - "Test against Ruby 2.6": + test-ruby-2-6: docker: - image: circleci/ruby:2.6.5 working_directory: ~/intercom-ruby steps: - checkout + - run: + name: Route Bundler through the Socket Firewall registry + command: | + set -euo pipefail + if [ -z "${SOCKET_GEM_URL:-}" ]; then + if [ -n "${CIRCLE_PR_REPONAME:-}" ]; then + echo "Forked PR: no Socket Firewall credentials available, gems will come from rubygems.org." + exit 0 + fi + echo "FATAL: SOCKET_GEM_URL is required (provided by the socket-firewall context)" + exit 1 + fi + bundle config mirror.https://rubygems.org "$SOCKET_GEM_URL" + bundle config mirror.https://rubygems.org | grep -q socket-firewall-registry \ + || { echo "FAIL: bundler mirror not pointing at Socket Firewall"; exit 1; } - run: bundle install - run: bundle exec rake workflows: - version: 2 build_and_test: jobs: - - "Test against Ruby 2.4" - - "Test against Ruby 2.5" - - "Test against Ruby 2.6" - + - test-ruby-2-4: + name: "Test against Ruby 2.4" + context: + - socket-firewall + - test-ruby-2-5: + name: "Test against Ruby 2.5" + context: + - socket-firewall + - test-ruby-2-6: + name: "Test against Ruby 2.6" + context: + - socket-firewall diff --git a/Gemfile b/Gemfile index 4516ffc0..0cfe52d3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ -source "http://rubygems.org" +source "https://rubygems.org" gem 'webmock' gemspec diff --git a/changes.txt b/changes.txt index cac520a9..a881bab0 100644 --- a/changes.txt +++ b/changes.txt @@ -1,4 +1,7 @@ -4.2.1 +4.2.3 +- Fixed SyntaxError when reading articles with hyphenated locales + +4.2.2 - Fixed FlatStore to skip hash values when building API request payloads - Removed hash validation on FlatStore reads to allow for custom objects diff --git a/lib/intercom/lib/dynamic_accessors.rb b/lib/intercom/lib/dynamic_accessors.rb index 9d26b4fc..70543397 100644 --- a/lib/intercom/lib/dynamic_accessors.rb +++ b/lib/intercom/lib/dynamic_accessors.rb @@ -17,39 +17,46 @@ def define_accessors(attribute, value, object) private def define_flat_store_based_accessors(attribute, value, object) - object.instance_eval %Q" - def #{attribute}=(value) - mark_field_as_changed!(:#{attribute}) - @#{attribute} = Intercom::Lib::FlatStore.new(value) + ivar = ivar_name(attribute, object) + object.singleton_class.class_eval do + define_method("#{attribute}=") do |val| + mark_field_as_changed!(attribute.to_sym) + instance_variable_set(ivar, Intercom::Lib::FlatStore.new(val)) end - def #{attribute} - @#{attribute} - end - " + define_method(attribute) { instance_variable_get(ivar) } + end end def define_date_based_accessors(attribute, value, object) - object.instance_eval %Q" - def #{attribute}=(value) - mark_field_as_changed!(:#{attribute}) - @#{attribute} = value.nil? ? nil : value.to_i + ivar = ivar_name(attribute, object) + object.singleton_class.class_eval do + define_method("#{attribute}=") do |val| + mark_field_as_changed!(attribute.to_sym) + instance_variable_set(ivar, val.nil? ? nil : val.to_i) end - def #{attribute} - @#{attribute}.nil? ? nil : Time.at(@#{attribute}) + define_method(attribute) do + time = instance_variable_get(ivar) + time.nil? ? nil : Time.at(time) end - " + end end def define_standard_accessors(attribute, value, object) - object.instance_eval %Q" - def #{attribute}=(value) - mark_field_as_changed!(:#{attribute}) - @#{attribute} = value - end - def #{attribute} - @#{attribute} - end - " + ivar = ivar_name(attribute, object) + object.singleton_class.class_eval do + define_method("#{attribute}=") do |val| + mark_field_as_changed!(attribute.to_sym) + instance_variable_set(ivar, val) + end + define_method(attribute) { instance_variable_get(ivar) } + end + end + + # Keys such as "pt-BR" are not valid identifiers, so normalise them for the ivar name. + def ivar_name(attribute, object) + ivar = "@#{attribute.to_s.gsub(/\W/, '_')}" + object.register_attribute_key(ivar, attribute) if ivar != "@#{attribute}" + ivar end end diff --git a/lib/intercom/traits/api_resource.rb b/lib/intercom/traits/api_resource.rb index 8749c196..7a27b399 100644 --- a/lib/intercom/traits/api_resource.rb +++ b/lib/intercom/traits/api_resource.rb @@ -37,7 +37,7 @@ def from_hash(hash) def to_hash instance_variables_excluding_dirty_tracking_field.each_with_object({}) do |variable, hash| - hash[variable.to_s.delete('@')] = instance_variable_get(variable) + hash[attribute_key(variable)] = instance_variable_get(variable) end end @@ -46,10 +46,18 @@ def to_json(*args) next if variable == :@client value = instance_variable_get(variable) - hash[variable.to_s.delete('@')] = value.respond_to?(:to_json) ? value.to_json(*args) : value + hash[attribute_key(variable)] = value.respond_to?(:to_json) ? value.to_json(*args) : value end end + def register_attribute_key(ivar, attribute) + (@attribute_keys ||= {})[ivar.to_sym] = attribute.to_s + end + + def attribute_key(variable) + (@attribute_keys || {}).fetch(variable, variable.to_s.delete('@')) + end + def to_submittable_hash submittable_hash = {} to_hash.each do |attribute, value| diff --git a/lib/intercom/traits/dirty_tracking.rb b/lib/intercom/traits/dirty_tracking.rb index d155c2d9..baad8508 100644 --- a/lib/intercom/traits/dirty_tracking.rb +++ b/lib/intercom/traits/dirty_tracking.rb @@ -22,7 +22,7 @@ def mark_field_as_changed!(field_name) def field_changed?(field_name) @changed_fields ||= Set.new - field = instance_variable_get("@#{field_name}") + field = instance_variable_get("@#{field_name.to_s.gsub(/\W/, '_')}") if field.respond_to?(:field_changed?) field.to_hash.any? do |attribute, _| field.field_changed?(attribute) @@ -33,7 +33,7 @@ def field_changed?(field_name) end def instance_variables_excluding_dirty_tracking_field - instance_variables.reject{|var| var == :@changed_fields} + instance_variables.reject{|var| var == :@changed_fields || var == :@attribute_keys} end end end diff --git a/lib/intercom/version.rb b/lib/intercom/version.rb index 8e27d974..a5795aa0 100644 --- a/lib/intercom/version.rb +++ b/lib/intercom/version.rb @@ -1,3 +1,3 @@ module Intercom #:nodoc: - VERSION = "4.2.1" + VERSION = "4.2.3" end diff --git a/spec/unit/intercom/article_spec.rb b/spec/unit/intercom/article_spec.rb index 9fa3a695..67007ee7 100644 --- a/spec/unit/intercom/article_spec.rb +++ b/spec/unit/intercom/article_spec.rb @@ -37,4 +37,22 @@ client.articles.delete(article) end end -end \ No newline at end of file + + describe "Parsing an Article" do + it "handles hyphenated locale keys in translated content" do + article = Intercom::Article.from_api( + "id" => "1", + "type" => "article", + "translated_content" => { + "type" => "article_translated_content", + "fr" => { "type" => "article_content", "title" => "Bonjour" }, + "pt-BR" => { "type" => "article_content", "title" => "Ola" } + } + ) + _(article.translated_content.fr.title).must_equal "Bonjour" + _(article.translated_content.public_send("pt-BR").title).must_equal "Ola" + article.translated_content.public_send("pt-BR").title = "Oi" + _(article.to_submittable_hash).must_equal("translated_content" => { "pt-BR" => { "title" => "Oi" } }) + end + end +end