Class: Spaceship::Base
- Inherits:
-
Object
- Object
- Spaceship::Base
- Defined in:
- spaceship/lib/spaceship/base.rb
Overview
Spaceship::Base is the superclass for models in Apple Developer Portal. It’s mainly responsible for mapping responses to objects.
A class-level attribute ‘client` is used to maintain the spaceship which we are using to talk to ADP.
Example of creating a new ADP model:
class Widget < Spaceship::Base
attr_accessor :id, :name, :foo_bar, :wiz_baz
attr_mapping({
'name' => :name,
'fooBar' => :foo_bar,
'wizBaz' => :wiz_baz
})
end
When you want to instantiate a model pass in the parsed response: ‘Widget.new(widget_json)`
Direct Known Subclasses
Defined Under Namespace
Classes: DataHash
Class Attribute Summary collapse
-
.client ⇒ Spaceship::Client
The client used to make requests.
Instance Attribute Summary collapse
-
#client ⇒ Spaceship::Client
readonly
The current spaceship client used by the model to make requests.
-
#raw_data ⇒ Hash/Array
Holds the raw data we got from Apple’s server to use it later.
Storing the `attr_accessor` collapse
-
.attr_accessor(*vars) ⇒ Object
From stackoverflow.com/questions/2487333/fastest-one-liner-way-to-list-attr-accessors-in-ruby This will store a list of defined attr_accessors to easily access them when inspecting the values.
- .attributes ⇒ Object
- #attributes ⇒ Object
Inspect related code collapse
Class Method Summary collapse
-
.attr_mapping(attr_map = nil) ⇒ Object
Defines the attribute mapping between the response from Apple and our model objects.
-
.factory(attrs, existing_client = nil) ⇒ Object
The factory class-method.
-
.mapping_module(attr_mapping) ⇒ Module
Binds attributes getters and setters to underlying data returned from the API.
-
.method_missing(method_sym, *args, &block) ⇒ Object
Call a method to return a subclass constant.
-
.set_client(client) ⇒ Spaceship::Base
Sets client and returns self for chaining.
Instance Method Summary collapse
-
#initialize(attrs = {}, existing_client = nil) ⇒ Base
constructor
The initialize method accepts a parsed response from Apple and sets all attributes that are defined by ‘attr_mapping`.
-
#setup ⇒ Object
This method can be used by subclasses to do additional initialisation using the ‘raw_data`.
Constructor Details
#initialize(attrs = {}, existing_client = nil) ⇒ Base
The initialize method accepts a parsed response from Apple and sets all attributes that are defined by ‘attr_mapping`
Do not override ‘initialize` in your own models.
223 224 225 226 227 228 229 230 |
# File 'spaceship/lib/spaceship/base.rb', line 223 def initialize(attrs = {}, existing_client = nil) attrs.each do |key, val| self.send("#{key}=", val) if respond_to?("#{key}=") end self.raw_data = DataHash.new(attrs) @client = existing_client || self.class.client self.setup end |
Class Attribute Details
.client ⇒ Spaceship::Client
The client used to make requests.
80 81 82 |
# File 'spaceship/lib/spaceship/base.rb', line 80 def client @client end |
Instance Attribute Details
#client ⇒ Spaceship::Client (readonly)
Returns The current spaceship client used by the model to make requests.
211 212 213 |
# File 'spaceship/lib/spaceship/base.rb', line 211 def client @client end |
Class Method Details
.attr_accessor(*vars) ⇒ Object
From stackoverflow.com/questions/2487333/fastest-one-liner-way-to-list-attr-accessors-in-ruby This will store a list of defined attr_accessors to easily access them when inspecting the values
242 243 244 245 246 |
# File 'spaceship/lib/spaceship/base.rb', line 242 def self.attr_accessor(*vars) @attributes ||= [] @attributes.concat(vars) super(*vars) end |
.attr_mapping(attr_map = nil) ⇒ Object
Defines the attribute mapping between the response from Apple and our model objects. Keys are to match keys in the response and the values are to match attributes on the model.
Example of using ‘attr_mapping`
class Widget < Spaceship::Base
attr_accessor :id, :name, :foo_bar, :wiz_baz
attr_mapping({
'name' => :name,
'fooBar' => :foo_bar,
'wizBaz' => :wiz_baz
})
end
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'spaceship/lib/spaceship/base.rb', line 131 def attr_mapping(attr_map = nil) if attr_map @attr_mapping = attr_map @attr_mapping.values.each do |method_name| getter = method_name.to_sym setter = "#{method_name}=".to_sym # Seems like the `public_instance_methods.include?` doesn't always work # More context https://github.com/fastlane/fastlane/issues/11481 # That's why we have the `begin` `rescue` code here begin remove_method(getter) if public_instance_methods.include?(getter) rescue NameError end begin remove_method(setter) if public_instance_methods.include?(setter) rescue NameError end end include(mapping_module(@attr_mapping)) else begin @attr_mapping ||= ancestors[1].attr_mapping rescue NoMethodError rescue NameError end end return @attr_mapping end |
.attributes ⇒ Object
248 249 250 251 252 253 254 255 |
# File 'spaceship/lib/spaceship/base.rb', line 248 def self.attributes @attributes ||= [] par = [] par = (self.superclass.attributes || []) unless self == Base @attributes + par end |
.factory(attrs, existing_client = nil) ⇒ Object
The factory class-method. This should only be used or overridden in very specific use-cases
The only time it makes sense to use or override this method is when we want a base class to return a sub-class based on attributes.
Here, we define the method to be the same as ‘Spaceship::Base.new(attrs)`, be it should be used only by classes that override it.
Example:
Certificate.factory(attrs)
#=> #<PushCertificate ... >
204 205 206 |
# File 'spaceship/lib/spaceship/base.rb', line 204 def factory(attrs, existing_client = nil) self.new(attrs, existing_client) end |
.mapping_module(attr_mapping) ⇒ Module
Binds attributes getters and setters to underlying data returned from the API. Setting any properties will alter the ‘raw_data` hash.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'spaceship/lib/spaceship/base.rb', line 99 def mapping_module(attr_mapping) Module.new do attr_mapping.each do |source, dest| getter = dest.to_sym setter = "#{dest}=".to_sym define_method(getter) do raw_data.get(*source.split('.')) end define_method(setter) do |value| self.raw_data ||= DataHash.new({}) raw_data.set(source.split('.'), value) end end end end |
.method_missing(method_sym, *args, &block) ⇒ Object
Call a method to return a subclass constant.
If ‘method_sym` is an underscored name of a class, return the class with the current client passed into it. If the method does not match, NoMethodError is raised.
Example:
Certificate.production_push
#=> Certificate::ProductionPush
ProvisioningProfile.ad_hoc
#=> ProvisioningProfile::AdHoc
ProvisioningProfile.some_other_method
#=> NoMethodError: undefined method `some_other_method' for ProvisioningProfile
178 179 180 181 182 183 184 185 186 187 188 |
# File 'spaceship/lib/spaceship/base.rb', line 178 def method_missing(method_sym, *args, &block) module_name = method_sym.to_s module_name.sub!(/^[a-z\d]/) { $&.upcase } module_name.gsub!(%r{(?:_|(/))([a-z\d])}) { $2.upcase } if const_defined?(module_name) klass = const_get(module_name) klass.set_client(@client) else super end end |
.set_client(client) ⇒ Spaceship::Base
Sets client and returns self for chaining. rubocop:disable Style/AccessorMethodName
88 89 90 91 |
# File 'spaceship/lib/spaceship/base.rb', line 88 def set_client(client) self.client = client self end |
Instance Method Details
#attributes ⇒ Object
257 258 259 |
# File 'spaceship/lib/spaceship/base.rb', line 257 def attributes self.class.attributes end |
#inspect ⇒ Object
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'spaceship/lib/spaceship/base.rb', line 265 def inspect # To avoid circular references, we keep track of the references # of all objects already inspected from the first call to inspect # in this call stack # We use a Thread local storage for multi-thread friendliness thread = Thread.current tree_root = thread[:inspected_objects].nil? thread[:inspected_objects] = Set.new if tree_root if thread[:inspected_objects].include?(self) # already inspected objects have a default value, # let's follow Ruby's convention for circular references value = "#<Object ...>" else thread[:inspected_objects].add(self) begin value = inspect_value ensure thread[:inspected_objects] = nil if tree_root end end "<#{self.class.name} \n#{value}>" end |
#setup ⇒ Object
This method can be used by subclasses to do additional initialisation using the ‘raw_data`
234 |
# File 'spaceship/lib/spaceship/base.rb', line 234 def setup; end |
#to_s ⇒ Object
299 300 301 |
# File 'spaceship/lib/spaceship/base.rb', line 299 def to_s self.inspect end |