gladwords 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +34 -0
  3. data/.gitignore +4 -0
  4. data/.projections.json +5 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +57 -0
  7. data/.rubocop_todo.yml +32 -0
  8. data/.vim/coc-settings.json +12 -0
  9. data/.vim/install.sh +38 -0
  10. data/.vscode/launch.json +13 -0
  11. data/.vscode/settings.json +9 -0
  12. data/.vscode/tasks.json +21 -0
  13. data/Gemfile +20 -0
  14. data/Gemfile.lock +200 -0
  15. data/LICENSE.txt +21 -0
  16. data/README.md +71 -0
  17. data/Rakefile +15 -0
  18. data/bin/rake +31 -0
  19. data/bin/rspec +31 -0
  20. data/bin/solargraph +29 -0
  21. data/config/environment.rb +3 -0
  22. data/gladwords.code-workspace +11 -0
  23. data/gladwords.gemspec +27 -0
  24. data/lib/ext/rom/inflector.rb +8 -0
  25. data/lib/gladwords.rb +22 -0
  26. data/lib/gladwords/associations.rb +7 -0
  27. data/lib/gladwords/associations/many_to_many.rb +18 -0
  28. data/lib/gladwords/associations/many_to_one.rb +22 -0
  29. data/lib/gladwords/associations/one_to_many.rb +19 -0
  30. data/lib/gladwords/associations/one_to_one.rb +10 -0
  31. data/lib/gladwords/associations/one_to_one_through.rb +8 -0
  32. data/lib/gladwords/commands.rb +7 -0
  33. data/lib/gladwords/commands/core.rb +76 -0
  34. data/lib/gladwords/commands/create.rb +18 -0
  35. data/lib/gladwords/commands/delete.rb +22 -0
  36. data/lib/gladwords/commands/error_wrapper.rb +25 -0
  37. data/lib/gladwords/commands/update.rb +17 -0
  38. data/lib/gladwords/errors.rb +7 -0
  39. data/lib/gladwords/gateway.rb +48 -0
  40. data/lib/gladwords/inflector.rb +20 -0
  41. data/lib/gladwords/relation.rb +197 -0
  42. data/lib/gladwords/relation/association_methods.rb +29 -0
  43. data/lib/gladwords/relation/joined_relation.rb +52 -0
  44. data/lib/gladwords/schema.rb +26 -0
  45. data/lib/gladwords/schema/attributes_inferrer.rb +171 -0
  46. data/lib/gladwords/schema/dsl.rb +28 -0
  47. data/lib/gladwords/schema/inferrer.rb +19 -0
  48. data/lib/gladwords/selector_fields_db.rb +30 -0
  49. data/lib/gladwords/selector_fields_db/v201806.json +3882 -0
  50. data/lib/gladwords/selector_fields_db/v201809.json +4026 -0
  51. data/lib/gladwords/struct.rb +24 -0
  52. data/lib/gladwords/types.rb +27 -0
  53. data/lib/gladwords/version.rb +5 -0
  54. data/rakelib/generate_selector_fields_db.rake +72 -0
  55. data/spec/integration/commands/create_spec.rb +24 -0
  56. data/spec/integration/commands/delete_spec.rb +47 -0
  57. data/spec/integration/commands/update_spec.rb +24 -0
  58. data/spec/shared/campaigns.rb +56 -0
  59. data/spec/shared/labels.rb +17 -0
  60. data/spec/spec_helper.rb +33 -0
  61. data/spec/support/adwords_helpers.rb +41 -0
  62. data/spec/unit/commands/create_spec.rb +85 -0
  63. data/spec/unit/commands/delete_spec.rb +32 -0
  64. data/spec/unit/commands/update_spec.rb +96 -0
  65. data/spec/unit/inflector_spec.rb +11 -0
  66. data/spec/unit/relation/association_methods_spec.rb +91 -0
  67. data/spec/unit/relation_spec.rb +187 -0
  68. data/spec/unit/schema/attributes_inferrer_spec.rb +83 -0
  69. data/spec/unit/selector_fields_db_spec.rb +29 -0
  70. data/spec/unit/types_spec.rb +49 -0
  71. metadata +190 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Gladwords Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,71 @@
1
+ # Gladwords
2
+
3
+ A saner Ruby wrapper over the AdWords API, using [ROM.rb](https://rom-rb.org).
4
+
5
+ # Clout
6
+
7
+ [![CircleCI](https://circleci.com/gh/adHawk/gladwords.svg?style=svg)](https://circleci.com/gh/adHawk/gladwords)
8
+
9
+ ## Usage
10
+
11
+ ```ruby
12
+ # setup your client following these instructions:
13
+ # https://github.com/googleads/google-api-ads-ruby/blob/master/adwords_api/README.md#2---using-the-client-library
14
+ client = AdwordsApi::Api.new
15
+
16
+ ROM::Configuration.new(:adwords, client: client) do |config|
17
+ config.relation(:campaigns) do
18
+ auto_struct(true)
19
+ auto_map(true)
20
+
21
+ schema(infer: true) do
22
+ attribute :id, Gladwords::Types::ID
23
+
24
+ primary_key :id
25
+
26
+ associations do
27
+ has_many :ad_groups, combine_key: :campaign_id
28
+ end
29
+ end
30
+ end
31
+
32
+ config.relation(:ad_groups) do
33
+ auto_struct(true)
34
+ auto_map(true)
35
+
36
+ schema(infer: true) do
37
+ attribute :id, Gladwords::Types::ID
38
+
39
+ primary_key :id
40
+
41
+ associations do
42
+ belongs_to :campaign, combine_key: :id
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ scope = subject.select(:id, :name).where(name: 'Test Campaign 2')
49
+ gladwords = ROM.container(configuration)
50
+
51
+ # query the relations
52
+ campaigns = gladwords.relations[:campaigns]
53
+ campaigns.select(:id, :name).where(name: 'Campaign 2').to_a # [{ id: '123', name: 'Campaign 2']]
54
+ ```
55
+
56
+ ## Installation
57
+
58
+ Add this line to your application's Gemfile:
59
+
60
+ ```ruby
61
+ gem 'attr-gather'
62
+ ```
63
+
64
+ And then execute:
65
+
66
+ $ bundle
67
+
68
+ Or install it yourself as:
69
+
70
+ $ gem install attr-gather
71
+
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake'
4
+ require 'bundler/gem_tasks'
5
+ require 'rspec/core/rake_task'
6
+ require 'rubocop/rake_task'
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+ RuboCop::RakeTask.new(:lint)
10
+
11
+ task :default do
12
+ sh "curl -fLSs https://circle.ci/cli | DESTDIR=#{__dir__}/tmp bash" unless File.exist?('./tmp/circleci')
13
+
14
+ sh './tmp/circleci local execute build'
15
+ end
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rake' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ # rubocop:disable all
12
+ require "pathname"
13
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
14
+ Pathname.new(__FILE__).realpath)
15
+
16
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
17
+
18
+ if File.file?(bundle_binstub)
19
+ if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
20
+ load(bundle_binstub)
21
+ else
22
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
23
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
24
+ end
25
+ end
26
+
27
+ require "rubygems"
28
+ require "bundler/setup"
29
+
30
+ load Gem.bin_path("rake", "rake")
31
+ # rubocop:enable all
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ # rubocop:disable all
12
+ require "pathname"
13
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
14
+ Pathname.new(__FILE__).realpath)
15
+
16
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
17
+
18
+ if File.file?(bundle_binstub)
19
+ if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
20
+ load(bundle_binstub)
21
+ else
22
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
23
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
24
+ end
25
+ end
26
+
27
+ require "rubygems"
28
+ require "bundler/setup"
29
+
30
+ load Gem.bin_path("rspec-core", "rspec")
31
+ # rubocop:enable all
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'solargraph' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("solargraph", "solargraph")
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ # stub file for projectionist.vim
@@ -0,0 +1,11 @@
1
+ {
2
+ "settings": {
3
+ "editor.tabSize": 2,
4
+ "editor.formatOnSave": true,
5
+ "editor.formatOnSaveTimeout": 2000,
6
+ "ruby.format": "rubocop",
7
+ "ruby.lint": {
8
+ "rubocop": true
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'gladwords/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'gladwords'
9
+ spec.version = Gladwords::VERSION.dup
10
+ spec.authors = ['Ian Ker-Seymer', 'Patrick Sparrow']
11
+ spec.email = ['ian@tryadhawk.com', 'patrick@tryadhawk.com']
12
+ spec.summary = 'AdWords support for ROM.rb'
13
+ spec.description = spec.summary
14
+ spec.homepage = 'https://github.com/adHawk/gladwords'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_runtime_dependency 'google-adwords-api'
23
+ spec.add_runtime_dependency 'rom', '~> 4.2.0'
24
+
25
+ spec.add_development_dependency 'bundler'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/support/inflector'
4
+
5
+ module ROM
6
+ remove_const('Inflector')
7
+ Inflector = Gladwords::Inflector
8
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom'
4
+
5
+ require_relative 'gladwords/inflector'
6
+ require_relative 'ext/rom/inflector'
7
+ require_relative 'gladwords/selector_fields_db'
8
+ require_relative 'gladwords/errors'
9
+ require_relative 'gladwords/version'
10
+ require_relative 'gladwords/gateway'
11
+ require_relative 'gladwords/relation'
12
+ require_relative 'gladwords/commands'
13
+ require_relative 'gladwords/types'
14
+
15
+ # rom-rb adapter for Google AdWords
16
+ #
17
+ # @api public
18
+ module Gladwords
19
+ include Dry::Core::Constants
20
+ end
21
+
22
+ ROM.register_adapter(:adwords, Gladwords)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gladwords/associations/many_to_many'
4
+ require 'gladwords/associations/many_to_one'
5
+ require 'gladwords/associations/one_to_many'
6
+ require 'gladwords/associations/one_to_one'
7
+ require 'gladwords/associations/one_to_one_through'
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/associations/many_to_many'
4
+
5
+ module Gladwords
6
+ module Associations
7
+ # Many to many implementation
8
+ class ManyToMany < ROM::Associations::ManyToMany
9
+ def call(target:)
10
+ target
11
+ end
12
+
13
+ def preload(target, _loaded)
14
+ target
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/associations/many_to_one'
4
+
5
+ module Gladwords
6
+ module Associations
7
+ # A class which represents a many to one relationship (i.e. belongs_to)
8
+ #
9
+ # @api private
10
+ class ManyToOne < ROM::Associations::ManyToOne
11
+ def call(target:)
12
+ target
13
+ end
14
+
15
+ def preload(target, loaded)
16
+ target_pks = loaded.pluck(:campaign_id)
17
+
18
+ target.where(target.schema.primary_key_name => target_pks)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/associations/one_to_many'
4
+
5
+ module Gladwords
6
+ module Associations
7
+ # @api private
8
+ class OneToMany < ROM::Associations::OneToMany
9
+ def call(target:)
10
+ target
11
+ end
12
+
13
+ def preload(target, loaded)
14
+ target_pks = loaded.pluck(:id)
15
+ target.where(foreign_key => target_pks)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gladwords/associations/one_to_many'
4
+
5
+ module Gladwords
6
+ module Associations
7
+ class OneToOne < OneToMany
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gladwords
4
+ module Associations
5
+ class OneToOneThrough < ManyToMany
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/commands'
4
+
5
+ require 'gladwords/commands/create'
6
+ require 'gladwords/commands/update'
7
+ require 'gladwords/commands/delete'
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gladwords/commands/error_wrapper'
4
+
5
+ module Gladwords
6
+ module Commands
7
+ # @api private
8
+ module Core
9
+ extend Dry::Core::ClassAttributes
10
+
11
+ # @api private
12
+ module Types
13
+ include Dry::Types.module
14
+
15
+ Operation = Hash.schema(
16
+ operator: Coercible::String.enum('ADD', 'SET', 'REMOVE'),
17
+ operand: Hash
18
+ )
19
+ end
20
+
21
+ def self.included(klass)
22
+ super
23
+
24
+ klass.include ErrorWrapper
25
+ klass.defines :adwords_operator
26
+ klass.defines :operand_mapper
27
+ klass.option :mutator, default: -> { method(:mutate) }
28
+ end
29
+
30
+ def execute(tuples)
31
+ perform_operations(tuples)
32
+ end
33
+
34
+ def perform_operations(tuples)
35
+ operations = build_operations(tuples)
36
+ raw_result = mutator.call(operations)
37
+
38
+ unwrap_result(raw_result)
39
+ end
40
+
41
+ private
42
+
43
+ def build_operations(tuples)
44
+ ensure_enumerable(tuples).map do |tuple|
45
+ operator(tuple)
46
+ end
47
+ end
48
+
49
+ def mutate(operations)
50
+ relation.dataset.mutate(operations)
51
+ end
52
+
53
+ def unwrap_result(result)
54
+ result[:value]
55
+ end
56
+
57
+ def ensure_enumerable(tuples)
58
+ if tuples.is_a?(Array)
59
+ tuples
60
+ else
61
+ [tuples]
62
+ end
63
+ end
64
+
65
+ def operator(operand)
66
+ op = if self.class.operand_mapper
67
+ self.class.operand_mapper.call(operand)
68
+ else
69
+ operand
70
+ end
71
+
72
+ Types::Operation[operator: self.class.adwords_operator, operand: op]
73
+ end
74
+ end
75
+ end
76
+ end