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.
228 229 230 231 232 233 234 235 |
# File 'spaceship/lib/spaceship/base.rb', line 228 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.
85 86 87 |
# File 'spaceship/lib/spaceship/base.rb', line 85 def client @client end |
Instance Attribute Details
#client ⇒ Spaceship::Client (readonly)
Returns The current spaceship client used by the model to make requests.
216 217 218 |
# File 'spaceship/lib/spaceship/base.rb', line 216 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
247 248 249 250 251 |
# File 'spaceship/lib/spaceship/base.rb', line 247 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
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'spaceship/lib/spaceship/base.rb', line 136 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
253 254 255 256 257 258 259 260 |
# File 'spaceship/lib/spaceship/base.rb', line 253 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 ... >
209 210 211 |
# File 'spaceship/lib/spaceship/base.rb', line 209 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.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'spaceship/lib/spaceship/base.rb', line 104 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
183 184 185 186 187 188 189 190 191 192 193 |
# File 'spaceship/lib/spaceship/base.rb', line 183 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 Naming/AccessorMethodName
93 94 95 96 |
# File 'spaceship/lib/spaceship/base.rb', line 93 def set_client(client) self.client = client self end |
Instance Method Details
#attributes ⇒ Object
262 263 264 |
# File 'spaceship/lib/spaceship/base.rb', line 262 def attributes self.class.attributes end |
#inspect ⇒ Object
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'spaceship/lib/spaceship/base.rb', line 270 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`
239 |
# File 'spaceship/lib/spaceship/base.rb', line 239 def setup; end |
#to_s ⇒ Object
304 305 306 |
# File 'spaceship/lib/spaceship/base.rb', line 304 def to_s self.inspect end |