you 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f36cf247e4c4409e3c694d736bd3b99bd4b2e5d8
4
- data.tar.gz: 8e570fa36c538495ac6ab438af61a733271aec17
2
+ SHA256:
3
+ metadata.gz: 762cbe06265ac26412bf45bdae45eb6042a833f13937ce307568b665172f70f4
4
+ data.tar.gz: 99a4ab5032391bdb2ed74af99718e66bc4cb1fe8b6748a17bc1c4c1ca55e6baf
5
5
  SHA512:
6
- metadata.gz: 398b6f6b72b5384c96cc8e271c45331cd15dd2916dde3c542725ff99c6ae323cc91d5738357e39ff89796238b26f1ed8aa28aa116a87087b09858d0dbd975988
7
- data.tar.gz: 1f90db5a3fc9c6c1e60023446c4d2fe3f99e218cd155fe1e97a5512644bcaf6e029b7c2c3e58b0aa1b002a311d3c5c174247174d1606477cdb6ef0d2c1c8af36
6
+ metadata.gz: e228d33f33aa685e596009bf5063ef7ae42530ff377f7ccb0b6e21baef59d44e8f49642a9ffae8f43d1506928bdbff89028f763323c97431b49fab6211693f96
7
+ data.tar.gz: 3c7ac63bff78c50aafba7e460090a71be53ee5e0e063444f8cbcceb9eba7c0c921f958649ae6cdfea6e31cfbe4008cfaa925086f961509734e39878c5764c329
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ For more information about changelogs, check [Keep a Changelog](http://keepachangelog.com) and
6
+ [Vandamme](http://tech-angels.github.io/vandamme).
7
+
8
+ ## 0.1.0 - 2026-08-28
9
+
10
+ * [Feature] First release: a library for the people
data/LICENSE.txt CHANGED
@@ -1,22 +1,21 @@
1
- Copyright (c) 2014 claudiob
1
+ The MIT License (MIT)
2
2
 
3
- MIT License
3
+ Copyright (c) 2026 HouseAccount
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
5
+ Permission is hereby granted, free of charge, to any you 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 yous to whom the Software is
10
+ furnished to do so, subject to the following conditions:
12
11
 
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
15
14
 
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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
21
+ THE SOFTWARE.
data/README.md CHANGED
@@ -1,29 +1,13 @@
1
1
  # You
2
2
 
3
- TODO: Write a gem description
3
+ A library for the people.
4
4
 
5
- ## Installation
5
+ ## Reference
6
6
 
7
- Add this line to your application's Gemfile:
7
+ The API reference is built from what RubyGems holds, at
8
+ [rubydoc.info/gems/you](https://rubydoc.info/gems/you). The source is at
9
+ [github.com/claudiob/you](https://github.com/claudiob/you).
8
10
 
9
- gem 'you'
11
+ ## License
10
12
 
11
- And then execute:
12
-
13
- $ bundle
14
-
15
- Or install it yourself as:
16
-
17
- $ gem install you
18
-
19
- ## Usage
20
-
21
- TODO: Write usage instructions here
22
-
23
- ## Contributing
24
-
25
- 1. Fork it ( https://github.com/[my-github-username]/you/fork )
26
- 2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Add some feature'`)
28
- 4. Push to the branch (`git push origin my-new-feature`)
29
- 5. Create a new Pull Request
13
+ MIT, see [LICENSE.txt](LICENSE.txt).
@@ -0,0 +1,28 @@
1
+ module You
2
+ # The gateway a set of credentials opens on a platform: the business they belong to and the
3
+ # records it holds. A gem subclasses it and answers what its platform offers; a reader it
4
+ # leaves out raises NotImplementedError, and the leads it leaves out refuse to file one.
5
+ class Account
6
+ # @return [Business] business the credentials belong to.
7
+ def business = unanswered :business
8
+
9
+ # @return [Collection] jobs of the business, each a {Job}; a platform may also `find` one.
10
+ def jobs = unanswered :jobs
11
+
12
+ # @return [Visits] visits of the business, each a {Visit}; a platform may also `find` one.
13
+ def visits = unanswered :visits
14
+
15
+ # @return [Collection] technicians of the business, each a {Technician}.
16
+ def technicians = unanswered :technicians
17
+
18
+ # @return [Windows] free time of the business, each a {Window}, where the platform works it out.
19
+ def windows = unanswered :windows
20
+
21
+ # @return [Leads] leads of the business: `create` files one, where the platform takes leads.
22
+ def leads = Leads.new
23
+
24
+ private
25
+
26
+ def unanswered(name) = raise NotImplementedError, "#{self.class} does not answer #{name}"
27
+ end
28
+ end
@@ -0,0 +1,47 @@
1
+ module You
2
+ # A list of records an account holds, walked however the platform pages it and narrowed to a
3
+ # window measured from now, or to one technician. A gem answers `each`, and `between` with
4
+ # the same list narrowed to what starts between two moments, either one open.
5
+ class Collection
6
+ include Enumerable
7
+
8
+ # Both ends are measured from one moment, so nothing slides between them.
9
+ # @param within [ActiveSupport::Duration, nil] how far ahead to look, or as far as there is.
10
+ # @return [Collection] the same list, narrowed to what starts from now on.
11
+ def upcoming(within = nil)
12
+ now = Time.now
13
+ between now, within && now + within
14
+ end
15
+
16
+ # @param within [ActiveSupport::Duration, nil] how far back to look, or as far as there is.
17
+ # @return [Collection] the same list, narrowed to what started before now.
18
+ def past(within = nil)
19
+ now = Time.now
20
+ between within && now - within, now
21
+ end
22
+
23
+ # What to bring back beside each record, for a platform that charges for the asking. One
24
+ # that answers a record whole has nothing to ask for and answers the same list, so a caller
25
+ # names what it reads without knowing which kind of platform it is talking to.
26
+ # @param names [Array<Symbol, Hash>] what to read beside each record, as the gem names them.
27
+ # @return [Collection] the same list, bringing those back where that costs anything.
28
+ def includes(*names) = self
29
+
30
+ # A platform that can ask its server for one technician's work narrows the list there; one
31
+ # that cannot walks the list and keeps what they turn out to be on. It takes the ID the
32
+ # platform files them under rather than the technician, that being the whole of what any
33
+ # platform asks by: a caller holding a record of its own then names the ID it knows, rather
34
+ # than dressing it up as a technician it does not have.
35
+ # @param id [String] ID the platform files whoever the work is booked for under.
36
+ # @return [Collection] the same list, narrowed to theirs.
37
+ def of(id)
38
+ Selection.new(collection: self) do |record|
39
+ record.technicians.any? { |technician| technician.id == id }
40
+ end
41
+ end
42
+
43
+ # @return [Array<String>] ID of every record, the list walked; a platform with a cheaper
44
+ # way to ask answers it that way instead.
45
+ def ids = map(&:id)
46
+ end
47
+ end
@@ -0,0 +1,18 @@
1
+ module You
2
+ # The leads of a business: who asked it for work, before there is a job. A gem answers
3
+ # `create` with the lead its platform filed, dropping what the platform has no field for.
4
+ class Leads
5
+ # @param name [String] given name of who asked, or the business's where a you has none.
6
+ # @param surname [String, nil] their surname.
7
+ # @param phone [String, nil] number they are reached on.
8
+ # @param email [String, nil] address they are written to.
9
+ # @param address [Hash, nil] where the work would happen: :street, :city, :state and :zip.
10
+ # @param description [String] what the work is called.
11
+ # @param notes [String, nil] what else was said about it.
12
+ # @param source [String, nil] where the lead came from, as the business names its sources.
13
+ # @return [Lead] the lead as the platform filed it.
14
+ def create(name:, surname:, phone:, email:, address:, description:, notes:, source:)
15
+ raise NotImplementedError, "#{self.class} does not file a lead"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ module You
2
+ # The visits of a business: every stop somebody is booked for, of a job or of a lead where
3
+ # the work is still being looked at. A gem answers `create` where its platform books a stop
4
+ # against a lead, and `for_jobs` or `for_leads` where it can ask its platform for one kind
5
+ # alone; where it cannot, the list is walked and the other kind let go.
6
+ class Visits < Collection
7
+ # Books a stop to look at work nobody has priced yet, opening the customer and whatever the
8
+ # platform hangs the stop off wherever it has none.
9
+ # @param name [String] given name of who asked, or the business's where a you has none.
10
+ # @param surname [String, nil] their surname.
11
+ # @param phone [String, nil] number they are reached on.
12
+ # @param email [String, nil] address they are written to.
13
+ # @param address [Hash, nil] where the work would happen: :street, :city, :state and :zip.
14
+ # @param description [String] what the work is called.
15
+ # @param notes [String, nil] what else was said about it.
16
+ # @param source [String, nil] where the lead came from, as the business names its sources.
17
+ # @param starts_at [Time] moment somebody is booked to arrive.
18
+ # @param ends_at [Time, nil] moment they are booked to leave, or nothing for the platform's.
19
+ # @param technicians [Array<Technician>] whoever is booked to go.
20
+ # @return [Visit] stop as the platform booked it.
21
+ def create(name:, surname:, phone:, email:, address:, description:, notes:, source:,
22
+ starts_at:, ends_at:, technicians:)
23
+ raise NotImplementedError, "#{self.class} does not book a visit"
24
+ end
25
+
26
+ # @return [Collection] the same list, narrowed to the stops of jobs.
27
+ def for_jobs = Selection.new(collection: self, &:job)
28
+
29
+ # @return [Collection] the same list, narrowed to the stops of leads.
30
+ def for_leads = Selection.new(collection: self, &:lead)
31
+
32
+ # Booked time a platform can say something about: the stops of jobs and of leads together,
33
+ # without the hours blocked out around them, which name no work and cost a list of their own
34
+ # on a platform that files them apart.
35
+ # @return [Collection] the same list, narrowed to the stops of work of either kind.
36
+ def for_work = Selection.new(collection: self) { |visit| visit.job || visit.lead }
37
+ end
38
+ end
@@ -0,0 +1,16 @@
1
+ module You
2
+ # The free time of a business: every stretch somebody could be sent out in, over the window
3
+ # asked for. A platform that works it out answers this; one that does not know when its
4
+ # business is open cannot, and says so rather than answering with none, an empty list being
5
+ # the same shape as a fully booked week.
6
+ class Windows < Collection
7
+ # A window names nobody, a list of them having been narrowed to one technician already, so
8
+ # there is nothing here to walk and keep: only the platform can tell one you's free time
9
+ # from another's.
10
+ # @param id [String] ID the platform files whoever is free under.
11
+ # @return [Windows] the same list, as that technician's alone.
12
+ def of(id)
13
+ raise NotImplementedError, "#{self.class} does not answer one technician's windows"
14
+ end
15
+ end
16
+ end
data/lib/you/error.rb ADDED
@@ -0,0 +1,5 @@
1
+ module You
2
+ # What every error a you raises descends from, so one rescue still catches the lot.
3
+ class Error < StandardError
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ module You
2
+ # Raised where a platform holds a request to a rate: the same question asked a little later
3
+ # is answered. Nothing here sleeps; a caller with a queue brings the whole job back.
4
+ class Throttled < Error
5
+ end
6
+ end
data/lib/you/phone.rb ADDED
@@ -0,0 +1,14 @@
1
+ module You
2
+ # The ten digits a North American number is, whatever punctuation it arrived wearing.
3
+ class Phone
4
+ # Ten digits whose area code and exchange open with 2 through 9.
5
+ NANP = /\A[2-9]\d{2}[2-9]\d{6}\z/
6
+
7
+ # @param number [String, nil] number as the platform holds it.
8
+ # @return [String, nil] ten digits to dial, or nil where none is held or none can be dialed.
9
+ def self.from(number)
10
+ digits = number.to_s.delete('^0-9').delete_prefix '1'
11
+ digits if NANP.match? digits
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,42 @@
1
+ module You
2
+ # What every record shares: the node the platform answered it as. Built by the gem that read
3
+ # it, never by a caller.
4
+ class Resource
5
+ # The node keys the platform spells otherwise than the vocabulary, by the reader they
6
+ # answer: `{ phone: :phone_number }` reads {Business#phone} off `phone_number`. A gem
7
+ # declares its own; a reader left out reads the key of its own name.
8
+ # @return [Hash] reader-to-key exceptions, empty where every name is its key.
9
+ def self.keys = {}
10
+
11
+ # The keys a platform is expected to answer a node with: each attribute the kind reads,
12
+ # through {.keys}, so a gem asks its platform for exactly these and writes none by hand.
13
+ # @return [Array<Symbol>] node keys, in the order the attributes are read.
14
+ def self.node_keys = attributes.map { |name| keys.fetch name, name }
15
+
16
+ # @param node [Hash] record as the platform answered it, under either kind of key.
17
+ def initialize(node: {})
18
+ @node = node.with_indifferent_access
19
+ end
20
+
21
+ # @return [String] ID the platform files the record under.
22
+ def id = attribute :id
23
+
24
+ private
25
+
26
+ def attribute(name) = @node[self.class.keys.fetch(name, name)]
27
+
28
+ def time(name)
29
+ value = attribute name
30
+ value.is_a?(String) ? (Time.iso8601 value if value.present?) : value
31
+ end
32
+
33
+ def decimal(name)
34
+ value = attribute name
35
+ BigDecimal value.to_s if value.present?
36
+ end
37
+
38
+ def record(type, key) = (type.new node: @node[key] if @node[key])
39
+
40
+ def records(type, key) = Array(@node[key]).map { |each| type.new node: each }
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ module You
2
+ # Who the account belongs to: the one business behind a set of credentials.
3
+ class Business < Resource
4
+ # What every business reads, by the vocabulary's names.
5
+ def self.attributes = %i[id name phone]
6
+
7
+ # @return [String] business name.
8
+ def name = attribute :name
9
+
10
+ # @return [String, nil] ten digits the business is reached on, nil where none can be dialed.
11
+ def phone = Phone.from attribute(:phone)
12
+
13
+ # Itself first, then every business under it at any depth, in the order the platform lists
14
+ # them: a business on its own is its own one subsidiary, and a franchise reads flat. A gem
15
+ # whose platform nests businesses answers the ones directly under this one from the private
16
+ # `below`; the rest inherit none.
17
+ # @return [Array<Business>] businesses an account may act as, this one included.
18
+ def subsidiaries = [ self, *below.flat_map(&:subsidiaries) ]
19
+
20
+ private
21
+
22
+ def below = []
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ module You
2
+ # A you the business works for.
3
+ class Customer < Resource
4
+ # What every customer reads, by the vocabulary's names.
5
+ def self.attributes = %i[id name surname email phone]
6
+
7
+ # @return [String, nil] what they go by: a given name, or a business's where a you has none.
8
+ def name = attribute :name
9
+
10
+ # @return [String, nil] their surname.
11
+ def surname = attribute :surname
12
+
13
+ # @return [String, nil] address they are written to.
14
+ def email = attribute :email
15
+
16
+ # @return [String, nil] ten digits they are reached on, nil where none can be dialed.
17
+ def phone = Phone.from attribute(:phone)
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ module You
2
+ # Work the business accepted and scheduled.
3
+ class Job < Resource
4
+ # What every job reads, by the vocabulary's names.
5
+ def self.attributes = %i[id description notes created_at scheduled_at completed_at amount]
6
+
7
+ # @return [String, nil] what the work is called, in the words of whoever opened the job.
8
+ def description = attribute :description
9
+
10
+ # @return [String, nil] what was written on the job for the crew to mind.
11
+ def notes = attribute :notes
12
+
13
+ # @return [Time] moment the job was opened.
14
+ def created_at = time :created_at
15
+
16
+ # @return [Time, nil] moment the work is booked for.
17
+ def scheduled_at = time :scheduled_at
18
+
19
+ # @return [Time, nil] moment the work was finished.
20
+ def completed_at = time :completed_at
21
+
22
+ # @return [BigDecimal, nil] what the job comes to, in dollars.
23
+ def amount = decimal :amount
24
+
25
+ # @return [Quote, nil] quote the job was won with, where it came back beside the job.
26
+ def quote = record Quote, :quote
27
+
28
+ # @return [Array<Line>] lines the work is billed as, empty where none came back.
29
+ def lines = records Line, :lines
30
+
31
+ # @return [Location, nil] where the work happens, where it came back beside the job.
32
+ def location = record Location, :location
33
+ end
34
+ end
@@ -0,0 +1,13 @@
1
+ module You
2
+ # Somebody who asked the business for work and is not a customer yet.
3
+ class Lead < Resource
4
+ # What every lead reads, by the vocabulary's names.
5
+ def self.attributes = %i[id]
6
+
7
+ # @return [Customer, nil] customer the lead was filed for, where they came back beside it.
8
+ def customer = record Customer, :customer
9
+
10
+ # @return [Location, nil] where the work would happen, where the platform named it.
11
+ def location = record Location, :location
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module You
2
+ # One line of a document: how many of a thing, what it is called, and what it comes to.
3
+ class Line < Resource
4
+ # What every line reads, by the vocabulary's names.
5
+ def self.attributes = %i[id name description quantity amount]
6
+
7
+ # @return [String, nil] what the line is called.
8
+ def name = attribute :name
9
+
10
+ # @return [String, nil] what the line says beyond what it is called.
11
+ def description = attribute :description
12
+
13
+ # A whole quantity reads as an Integer -- `3 Faucets` rather than `3.0 Faucets` -- and a
14
+ # fraction keeps its point, since rounding it would lie about what was billed.
15
+ # @return [Integer, Float, nil] how many of it the document is for.
16
+ def quantity = whole attribute(:quantity)
17
+
18
+ # @return [BigDecimal, nil] what the line comes to, in dollars.
19
+ def amount = decimal :amount
20
+
21
+ private
22
+
23
+ def whole(number) = number && ((number % 1).zero? ? number.to_i : number)
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module You
2
+ # Where work happens: one address the business drives to.
3
+ class Location < Resource
4
+ # What every location reads, by the vocabulary's names.
5
+ def self.attributes = %i[id street city zip latitude longitude]
6
+
7
+ # @return [String, nil] street, number included.
8
+ def street = attribute :street
9
+
10
+ # @return [String, nil] town.
11
+ def city = attribute :city
12
+
13
+ # @return [String, nil] ZIP code.
14
+ def zip = attribute :zip
15
+
16
+ # @return [Numeric, nil] how far north the place is.
17
+ def latitude = attribute :latitude
18
+
19
+ # @return [Numeric, nil] how far east the place is.
20
+ def longitude = attribute :longitude
21
+
22
+ # @return [Customer, nil] whose place it is, where they came back beside the location.
23
+ def customer = record Customer, :customer
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ module You
2
+ # A price the business sent a customer, which a job names where it was won with one.
3
+ class Quote < Resource
4
+ # What every quote reads, by the vocabulary's names.
5
+ def self.attributes = %i[id amount]
6
+
7
+ # @return [BigDecimal, nil] what the quote comes to, in dollars.
8
+ def amount = decimal :amount
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module You
2
+ # A you the business sends out: whoever a visit is booked for.
3
+ class Technician < Resource
4
+ # What every technician reads, by the vocabulary's names.
5
+ def self.attributes = %i[id name surname]
6
+
7
+ # @return [String, nil] what they go by: a given name.
8
+ def name = attribute :name
9
+
10
+ # @return [String, nil] their surname.
11
+ def surname = attribute :surname
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ module You
2
+ # Booked time: somebody is somewhere for an hour. Where it is, who is going and when are what
3
+ # a visit is for; the job or the lead it was booked against says why, and either may be absent.
4
+ class Visit < Resource
5
+ # What every visit reads, by the vocabulary's names.
6
+ def self.attributes = %i[id description starts_at ends_at anytime]
7
+
8
+ # @return [String, nil] what the stop is, in the words of whoever booked it.
9
+ def description = attribute :description
10
+
11
+ # @return [Time, nil] moment the visit is booked to start.
12
+ def starts_at = time :starts_at
13
+
14
+ # @return [Time, nil] moment the visit is booked to end.
15
+ def ends_at = time :ends_at
16
+
17
+ # @return [Boolean, nil] whether the visit may happen any time that day rather than at an hour.
18
+ def anytime? = attribute :anytime
19
+
20
+ # A stop says where it is without being asked what it was booked for, so a caller reading a
21
+ # schedule never has to reach through a job to find an address.
22
+ # @return [Location, nil] where the work happens, nil where the stop is booked nowhere.
23
+ def location = record Location, :location
24
+
25
+ # @return [Job, nil] job the stop belongs to, nil where no job was booked for it.
26
+ def job = record Job, :job
27
+
28
+ # @return [Lead, nil] lead the stop belongs to, nil where no lead was booked for it.
29
+ def lead = record Lead, :lead
30
+
31
+ # @return [Array<Technician>] whoever the stop is booked for, where the platform names them.
32
+ def technicians = records Technician, :technicians
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ module You
2
+ # Free time: a stretch nobody is booked for, which somebody could be sent out in. It names no
3
+ # work and nobody going, a list of them having been asked for one technician already, and it
4
+ # is as long as it is -- cut it into offerable pieces where an offer is being made, since only
5
+ # the caller making one knows how long it needs them to be.
6
+ class Window < Resource
7
+ # What every window reads, by the vocabulary's names.
8
+ def self.attributes = %i[starts_at ends_at]
9
+
10
+ # @return [Time] moment the free stretch opens.
11
+ def starts_at = time :starts_at
12
+
13
+ # @return [Time] moment the free stretch closes.
14
+ def ends_at = time :ends_at
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ module You
2
+ # A list narrowed by something the platform could not be asked to narrow by: the list it came
3
+ # from is walked as it was, and only what the rule keeps comes through.
4
+ class Selection < Collection
5
+ # @param collection [Collection] list to walk.
6
+ # @yield [Resource] each record of it, to keep or to let go.
7
+ def initialize(collection:, &kept)
8
+ @collection = collection
9
+ @kept = kept
10
+ end
11
+
12
+ # @yield [Resource] each record the rule kept, in the order the list answered it.
13
+ def each
14
+ @collection.each { |record| yield record if @kept.call record }
15
+ end
16
+
17
+ # @param from [Time, nil] moment the window opens, or nothing for as far back as it goes.
18
+ # @param to [Time, nil] moment the window closes, or nothing for as far ahead as it goes.
19
+ # @return [Selection] the same rule, over the list narrowed to the window.
20
+ def between(from, to) = self.class.new(collection: @collection.between(from, to), &@kept)
21
+ end
22
+ end
data/lib/you/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # The vocabulary two platform gems share: an account opens a business and the records it holds.
1
2
  module You
2
- VERSION = "0.0.1"
3
+ # The version of this gem, as RubyGems knows it.
4
+ VERSION = '0.1.1'
3
5
  end
data/lib/you.rb CHANGED
@@ -1,5 +1,34 @@
1
- require "you/version"
1
+ require 'bigdecimal'
2
+ require 'time'
2
3
 
3
- module You
4
- # Your code goes here...
5
- end
4
+ require 'you/version'
5
+
6
+ # Only the Active Support files whose methods are used: a name a platform holds nothing for
7
+ # arrives as readily empty as null, a node reads under either kind of key, the lines of a job
8
+ # join into one sentence, and a moment plus a duration is a moment.
9
+ require 'active_support'
10
+ require 'active_support/core_ext/object/blank'
11
+ require 'active_support/core_ext/hash/indifferent_access'
12
+ require 'active_support/core_ext/array/conversions'
13
+ require 'active_support/core_ext/numeric/time'
14
+
15
+ require 'you/error'
16
+ require 'you/errors/throttled'
17
+ require 'you/phone'
18
+ require 'you/resource'
19
+ require 'you/collection'
20
+ require 'you/selection'
21
+ require 'you/collections/leads'
22
+ require 'you/collections/visits'
23
+ require 'you/collections/windows'
24
+ require 'you/account'
25
+ require 'you/resources/business'
26
+ require 'you/resources/customer'
27
+ require 'you/resources/technician'
28
+ require 'you/resources/location'
29
+ require 'you/resources/line'
30
+ require 'you/resources/lead'
31
+ require 'you/resources/quote'
32
+ require 'you/resources/visit'
33
+ require 'you/resources/window'
34
+ require 'you/resources/job'
metadata CHANGED
@@ -1,63 +1,68 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: you
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
- - claudiob
8
- autorequire:
7
+ - Claudio Baccigalupo
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2014-10-10 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.6'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.6'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
13
+ name: activesupport
29
14
  requirement: !ruby/object:Gem::Requirement
30
15
  requirements:
31
16
  - - ">="
32
17
  - !ruby/object:Gem::Version
33
18
  version: '0'
34
- type: :development
19
+ type: :runtime
35
20
  prerelease: false
36
21
  version_requirements: !ruby/object:Gem::Requirement
37
22
  requirements:
38
23
  - - ">="
39
24
  - !ruby/object:Gem::Version
40
25
  version: '0'
41
- description: Write a longer description. Optional.
26
+ description: A library for the people
42
27
  email:
43
- - claudiob@gmail.com
28
+ - claudiob@users.noreply.github.com
44
29
  executables: []
45
30
  extensions: []
46
31
  extra_rdoc_files: []
47
32
  files:
48
- - ".gitignore"
49
- - Gemfile
33
+ - CHANGELOG.md
50
34
  - LICENSE.txt
51
35
  - README.md
52
- - Rakefile
53
36
  - lib/you.rb
37
+ - lib/you/account.rb
38
+ - lib/you/collection.rb
39
+ - lib/you/collections/leads.rb
40
+ - lib/you/collections/visits.rb
41
+ - lib/you/collections/windows.rb
42
+ - lib/you/error.rb
43
+ - lib/you/errors/throttled.rb
44
+ - lib/you/phone.rb
45
+ - lib/you/resource.rb
46
+ - lib/you/resources/business.rb
47
+ - lib/you/resources/customer.rb
48
+ - lib/you/resources/job.rb
49
+ - lib/you/resources/lead.rb
50
+ - lib/you/resources/line.rb
51
+ - lib/you/resources/location.rb
52
+ - lib/you/resources/quote.rb
53
+ - lib/you/resources/technician.rb
54
+ - lib/you/resources/visit.rb
55
+ - lib/you/resources/window.rb
56
+ - lib/you/selection.rb
54
57
  - lib/you/version.rb
55
- - you.gemspec
56
- homepage: ''
58
+ homepage: https://github.com/claudiob/you
57
59
  licenses:
58
60
  - MIT
59
- metadata: {}
60
- post_install_message:
61
+ metadata:
62
+ homepage_uri: https://github.com/claudiob/you
63
+ source_code_uri: https://github.com/claudiob/you/
64
+ changelog_uri: https://github.com/claudiob/you/blob/main/CHANGELOG.md
65
+ documentation_uri: https://rubydoc.info/gems/you
61
66
  rdoc_options: []
62
67
  require_paths:
63
68
  - lib
@@ -65,17 +70,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
65
70
  requirements:
66
71
  - - ">="
67
72
  - !ruby/object:Gem::Version
68
- version: '0'
73
+ version: 3.2.0
69
74
  required_rubygems_version: !ruby/object:Gem::Requirement
70
75
  requirements:
71
76
  - - ">="
72
77
  - !ruby/object:Gem::Version
73
78
  version: '0'
74
79
  requirements: []
75
- rubyforge_project:
76
- rubygems_version: 2.2.2
77
- signing_key:
80
+ rubygems_version: 4.0.3
78
81
  specification_version: 4
79
- summary: Write a short summary. Required.
82
+ summary: Any you
80
83
  test_files: []
81
- has_rdoc:
data/.gitignore DELETED
@@ -1,22 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
18
- *.bundle
19
- *.so
20
- *.o
21
- *.a
22
- mkmf.log
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in you.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,2 +0,0 @@
1
- require "bundler/gem_tasks"
2
-
data/you.gemspec DELETED
@@ -1,23 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'you/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "you"
8
- spec.version = You::VERSION
9
- spec.authors = ["claudiob"]
10
- spec.email = ["claudiob@gmail.com"]
11
- spec.summary = %q{Write a short summary. Required.}
12
- spec.description = %q{Write a longer description. Optional.}
13
- spec.homepage = ""
14
- spec.license = "MIT"
15
-
16
- spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
20
-
21
- spec.add_development_dependency "bundler", "~> 1.6"
22
- spec.add_development_dependency "rake"
23
- end