From fbdf7120f419bde8b2d905920f559ebf55419e74 Mon Sep 17 00:00:00 2001 From: Vedran Date: Mon, 1 Sep 2025 15:37:24 +0100 Subject: [PATCH 1/4] chore: bump version to 4.2.2 (#623) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Version bump from 4.2.1 to 4.2.2 - Updated changelog with FlatStore improvements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Claude --- changes.txt | 2 +- lib/intercom/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changes.txt b/changes.txt index cac520a..b9f617f 100644 --- a/changes.txt +++ b/changes.txt @@ -1,4 +1,4 @@ -4.2.1 +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/version.rb b/lib/intercom/version.rb index 8e27d97..7406b33 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.2" end From acb7deb7627fdd8e536a4175cc08e412788e9c20 Mon Sep 17 00:00:00 2001 From: DecIntercom <49399969+DecIntercom@users.noreply.github.com> Date: Wed, 5 Aug 2026 15:47:30 +0100 Subject: [PATCH 2/4] Route CI gem installs through a scanning registry mirror (#625) --- .circleci/config.yml | 62 +++++++++++++++++++++++++++++++++++++++++--- Gemfile | 2 +- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 23fcf9e..ce24562 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,6 +6,25 @@ jobs: 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": @@ -14,6 +33,21 @@ jobs: 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": @@ -22,6 +56,21 @@ jobs: 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 @@ -29,7 +78,12 @@ workflows: version: 2 build_and_test: jobs: - - "Test against Ruby 2.4" - - "Test against Ruby 2.5" - - "Test against Ruby 2.6" - + - "Test against Ruby 2.4": + context: + - socket-firewall + - "Test against Ruby 2.5": + context: + - socket-firewall + - "Test against Ruby 2.6": + context: + - socket-firewall diff --git a/Gemfile b/Gemfile index 4516ffc..0cfe52d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ -source "http://rubygems.org" +source "https://rubygems.org" gem 'webmock' gemspec From d0a805931a99eea54736c8a5696d942730fd0244 Mon Sep 17 00:00:00 2001 From: Anubhav Bohidar Date: Wed, 7 Oct 2026 13:33:45 +0100 Subject: [PATCH 3/4] Fix SyntaxError on articles with hyphenated locales (#626) * Define dynamic accessors without evaluating source strings Parsing an article, collection or section whose translated_content had a locale key such as "pt-BR" or "zh-CN" raised a SyntaxError, because the key was interpolated into a string passed to instance_eval and is not a valid method name. The error is not a StandardError so callers could not rescue it. Define the accessors with define_method and instance_variable_get/set instead, normalising the instance variable name. Behaviour for ordinary keys is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) * Keep original attribute keys in serialised output Hyphenated keys such as pt-BR were emitted as pt_BR by to_hash, to_json and to_submittable_hash. Co-Authored-By: Claude Opus 5.5 (1M context) * Upgrade CircleCI config to version 2.1 CircleCI no longer accepts version 2 configs, so pipelines on this repo error before any job runs and the required test checks never report. Co-Authored-By: Claude Opus 5.5 (1M context) * Keep CI check names while using 2.1-compatible job keys Version 2.1 rejects dots in job keys. Setting the original names on the workflow jobs keeps the reported check names unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Opus 5.5 (1M context) --- .circleci/config.yml | 18 +++++---- lib/intercom/lib/dynamic_accessors.rb | 55 +++++++++++++++------------ lib/intercom/traits/api_resource.rb | 12 +++++- lib/intercom/traits/dirty_tracking.rb | 4 +- spec/unit/intercom/article_spec.rb | 20 +++++++++- 5 files changed, 72 insertions(+), 37 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ce24562..9ad5d45 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ -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 @@ -27,7 +27,7 @@ jobs: || { 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 @@ -50,7 +50,7 @@ jobs: || { 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 @@ -75,15 +75,17 @@ jobs: - run: bundle exec rake workflows: - version: 2 build_and_test: jobs: - - "Test against Ruby 2.4": + - test-ruby-2-4: + name: "Test against Ruby 2.4" context: - socket-firewall - - "Test against Ruby 2.5": + - test-ruby-2-5: + name: "Test against Ruby 2.5" context: - socket-firewall - - "Test against Ruby 2.6": + - test-ruby-2-6: + name: "Test against Ruby 2.6" context: - socket-firewall diff --git a/lib/intercom/lib/dynamic_accessors.rb b/lib/intercom/lib/dynamic_accessors.rb index 9d26b4f..7054339 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 8749c19..7a27b39 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 d155c2d..baad850 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/spec/unit/intercom/article_spec.rb b/spec/unit/intercom/article_spec.rb index 9fa3a69..67007ee 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 From 7d8a2a98a9af2d1f5f35621f29ee092e968f4e77 Mon Sep 17 00:00:00 2001 From: Anubhav Bohidar Date: Thu, 8 Oct 2026 13:11:45 +0100 Subject: [PATCH 4/4] Bump version to 4.2.3 (#627) Co-authored-by: Claude Opus 5.5 (1M context) --- changes.txt | 3 +++ lib/intercom/version.rb | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index b9f617f..a881bab 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,6 @@ +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/version.rb b/lib/intercom/version.rb index 7406b33..a5795aa 100644 --- a/lib/intercom/version.rb +++ b/lib/intercom/version.rb @@ -1,3 +1,3 @@ module Intercom #:nodoc: - VERSION = "4.2.2" + VERSION = "4.2.3" end