20,173 Matching Annotations
  1. Mar 2021
    1. Remember, in a railway activity each task has two standard outputs with the “semantics” success and failure.
    2. By using Output(:semantic), you can select an existing output of the task and rewire it.
    3. Why don’t we put the “create user” task onto the failure track, and in case of successfully persisting the new user, we deviate back to the happy path?
    4. This is my absolute favorite feature ever and the official reason for (re-)writing Trailblazer 2.1. It makes me happy every time I use it.
    5. Trailblazer::Developer.wtf?(Signup, ctx)
    6. Admittedly, both the signature and the return values of invoke feel a bit clumsy. That’s becaus we’re currently working with the low-level interfaces.
    7. Originally designed to “only” implement railways for CRUD logic, we now use activities in many parts of Trailblazer itself
    8. Instead of one big code pile, activities will gently enforce a clean, standardized way for organizing code.
    9. the Activity component is the heart of TRB
    10. To implement such an activity, we only need to rewire the second step’s failure output to a new terminus.
    11. Since you can reference outputs by their semantic, you as a modeller only connect conceptual termini to ongoing connections!
    12. it’s a bit as if the following wiring is applied to every task added via #step
    13. This is where all our learnings about semantics, outputs, signals and the DSL come together.
    14. Visualized, our new composed structure would look as follows.
    15. Additionally, you may add debugging steps, error handler or rewire the conditions dynamically without touching the original snippet.
    16. So why the over-complication? What we got now is replicating a chain of && in the former version. This time, however, you will know which condition failed and what went in by using tracing. Look at the trace above - it’s impossible to not understand what was going on.
    17. signal.to_h[:semantic]

      Why not just allow us to call signal.semantic?

    18. Suppose that the validate task was getting quite complex and bloated. When writing “normal” Ruby, you’d break up one method into several. In Trailblazer, that’s when you introduce a new, smaller activity.
    19. Hey, that’s is an imaginary complication of our example - please don’t do this with every condition you have in your app.
    20. To demonstrate that, we need to complicate out example application a bit.
    21. Knowing about the wiring mechanics in Trailblazer is one thing. However, the real fun starts with nesting activities. That’s when the ideas of encapsulation, interfaces and reducing dependencies really come into play.
    22. Nesting an activity into another is a bit like calling a library method from another method
    23. the explicit modelling has one massive advantage: all possible outcomes of the nested activity are visible and have to be connected in the outer diagram
    1. A leaked snippet of the endpoint architectural design draft document, highly confidential.
    2. we have a helpful little framework behind the page rendering that pulls code snippets from real tests out of the actual gems
    3. Writing documentation for the new website has been fun. Yes, fun!
    4. It almost feels unreal finishing up this release post. It’s been so long!
    5. After around 3 years of silence, Trailblazer is back with its 2.1 release.
    6. We have fully documented the migration path to 2.1 while upgrading several complex apps. It worked.
    1. result["result.policy.default"].success?
    2. The guard name defaults to default and can be set via name:. This allows having multiple guards.
    3. the guard can also be a Callable-marked object

      elaborate...

    4. The Model macro literally does what our model! step did.

      That information is not generally relevant. Only makes sense for someone who actually knew about or used the old interface.

      The docs shouldn't assume you are an experienced user / a user of the previous version.

      Would have been appropriate in a Changelog entry or announcement, but not in the general docs.

    1. Our new feature, Total Cookie Protection, works by maintaining a separate “cookie jar” for each website you visit. Any time a website, or third-party content embedded in a website, deposits a cookie in your browser, that cookie is confined to the cookie jar assigned to that website, such that it is not allowed to be shared with any other website.
    1. You need to create a copy of the method or the class of your callable task in order to fix this and have two identical steps.
    2. interferring
    3. You don’t have to stick to the task interface! The [circuit interface] is a bit more clumsy, but gives you much better control over how ctx and signals are handled.
    4. And trust us, we’ve been playing with different APIs for two years and this was the easiest and fastest outcome.
    5. The circuit interface is a bit more clumsy but it gives you unlimited power over the way the activity will be run.
    6. Using this interface empowers you to fully take control of the flow™.
    7. Advanced features like tracing, input/output filters or type checking leverage the framework argument flow_options
    8. Note that the :task option for step configures this element as a low-level circuit interface, or in other words, it will skip the wrapping with the task interface.

      This bit me because I didn't realize that. Was getting error:

      TypeError: no implicit conversion of Symbol into Integer
      

      Finally checked ctx and found it was an array...

      At least this is documented here.

    9. Do not fear those syntactical finesses unfamiliar to you, young padawan.
    10. There’s no additional logic from Trailblazer happening here. The function returns a well-defined hash which is passed as an argument to step.
    11. patch: -> { step Destroy.method(:tidy_storage), before: :delete_model }
    1. 2021年の旧正月セールで買いました。まだすべてのステージをクリアしてはいないのですが、30分ほど遊んだところで良いところ・悪いところが見えてきたのでひとまず日本人がレビュー一番乗りです。良いところ・グラフィックは無機質ですが悪くないと思います・音楽も自己主張しすぎず、アクションを盛り上げる感じで悪くはないです気になるところ・WASDで動けるのですが動きが半端にバイオハザードみたいなラジコン操作感があり、ちょっと気に食わないですただ、これは個人の感想の範疇ですね・ジャンプの性能は低く、距離ぎりぎりを要求されます。これは慣れですね悪いところ・ウォールランや動く壁といったギミックがありますが、これらの調整がとにかくできていません。特に斜め移動などでは自動で高度の調整が入るのですが、始点で普通につかまっているのに終点で床の下に潜り込んだり、果ては床のない場所にズレたりします。パルクール的なゲーム性を損ない、ものすごいストレスとなります。以上です。パルクールアクション的なものを期待して購入し、実際予想通りのゲーム性でした。しかし残念ですが自分にとっては上記の悪いところがとにかくストレスとして感じられます。買った以上はクリアまでは進めたいですが、他人におすすめはできません。

      I bought it at the 2021 Chinese New Year sale. I haven't cleared all the stages yet, but after playing for about 30 minutes, I could see the good and bad points, so for the time being, the Japanese are the first to review.

      Good point ・ The graphics are inorganic, but I don't think they are bad. ・ The music is not too self-assertive, and it's not bad because it makes the action exciting.

      Where to worry ・ I can move with WASD, but the movement is odd and there is a feeling of radio control operation like biohazard, so I do not like it a little. However, this is a category of personal impressions. ・ Jump performance is low, and it is required to be close to the distance. This is familiar

      bad place -There are gimmicks such as wall runs and moving walls, but these adjustments have not been made anyway. Especially when moving diagonally, the altitude is automatically adjusted, but even though it is normally held at the start point, it may slip under the floor at the end point, or it may shift to a place without a floor. It spoils the parkour-like gameplay and causes tremendous stress.

      that's all. I bought it in anticipation of something like parkour action, and the game was actually as expected. But unfortunately, the above bad points are stressful to me anyway. As long as I bought it, I would like to proceed to clear it, but I cannot recommend it to others.

    1. a strategy game for me is mostly about the mechanics and the replayability. I love good art and design, but function follow form!
    1. This game runs on Flash. Effective Jan. 12, 2021, Adobe (the company that made Flash) began blocking its use everywhere. This is unfortunate, but outside of Kongregate's control.
    1. If you get this game for some reason, it's worth mucking around in for 20 minutes or so, but you probably shouldn't buy this on purpose.
  2. Feb 2021
    1. [0.4] Controls & Training & Help[0.2] Menu & Settings[0.2] Sound & Music[0.1] Graphics[0.2] Game Design[0.3] Game Story[0.2] Game Content[0.4] Time to complete feels ok? (& if the Game can be repeatedly played again)[0] is it Enjoyable & Fun?[0] Could it hold a spot in Favorites?[0] BONUS point: Multi-Player related[0] BONUS point: Review for VR
    1. To whom did you sell your keys and at what kind of rates? https://steamcommunity.com/id/playa131 for example resells them and might be the source for DIG's purchases.
    2. Indiegala is mentioned because it was a legitimate mutually signed contractual agreement as far as previous business is concerned.
    3. Do you have collaborators who could have generated keys and sold them on their own? DIG's Steam keys and other stores' Steam keys must have some source, after all. Keys don't generate themselves, and only your accounts should be able to request them.This particular game was in Bunch Keys Indie Wizardry Bundle. I assume you had a proper contract for that. Maybe DIG or an intermediary bought 50-200 copies of it?
    4. It isn't stealing because you or an associate must have generated and given them the keys in some way or another?Ideally you would ask a DIG bundle buyer to show you their key for your game, so you can figure out what key request batch it came from, and then you can scratch your head and wonder who you gave those keys to and what journey they took afterwards.
    1. For years, retailers, no matter what time of day or year, have offered sales and advertisements that lure bargain-drooling customers in the doors.
    2. Retailers use a combination of promotional pricing, fake pricing, and price anchoring to form a comprehensive psychological pricing strategy that creates a pricing trigger that minimizes the sales cycle significantly
    3. For instance, when faced with a 60% off, 12 hour only coupon that reduces a $1,200 winter coat to $400, you can’t help but rush to the store to buy it, even if it is 95 degrees out. A few things are happening here. For one, the $1200 acts as an anchor price that psychologically forces you to realize you’re getting an enormous deal at the $400 price point. Plus, the promotion limiting the time the offer is available forces you into an impulse.
    4. So, where did J.C. Penney go wrong? Well, while we admire their attempt to change, they attempted to destroy over a century’s worth of price conditioning consumers have been through with department stores and pricing in general. They weren’t completely off base, as consumers with more and more access to information (comparison shopping engines, consumer reports, etc.), are beginning to realize that the value of products is determined much differently than a sticker would suggest. Yet, assuming that most soccer moms (and dads) wouldn’t fall prey to the colorful print ads tucked within the comics section in the Sunday paper, overlooks how much the majority of consumers value “winning” the retail game. Simply, deflating the perceived value causes customers to value the actual product less.
    5. Almost no one ever pays full price. In fact, studies show that people are much more inclined to pay $25 for an item valued at $50, than paying for the same item without a sale at $25. It’s all about the “price framing” of a product that creates a perceived value, which all leads to the excitement of getting a good deal
    1. This is Fantatical's annual "Bundlefest" so in this case it's just timing. They'll do this once a year and we just so happened to have a bundle with them not too long ago. It's not something we typically do.
    2. Apologies: it's hyperbole. The parent site has a bunch of "spend x to get one of y Steam games" deals, which is what I was referring to. it was not meant literally. Just an attempt to build common ground with the poster who was talking about gambling and fomo.I thought they were referencing the larger site, so I wanted to acknowledge that so I didn't come off as dismissive of their concerns.Which turned out to be entirely separate concerns! Obviating the reason for the comment in the first place.Anyway, sorry for the short novel. But that's the danger of pithy one-liners: assumed context for the poster can be entirely lost in translation.Thanks for coming to my public apology press release?
    3. Anyway, sorry for the short novel. But that's the danger of pithy one-liners: assumed context for the poster can be entirely lost in translation.
    4. Eh, that's just sales. Humans are dumb, panicky animals: just look at how J.C. Penny's "Fair and Square" initiative went.Short version: they went full-on no bullshit: no limited time sales, no fake prices discounted, things cost what they cost, no more FOMO, no waiting for deals.It tanked. Horribly.
    5. Why do companies insist on making deals a gamble? Is it basically just to capture FOMO sales?Both rhetorical questions.Edit: I'm talking about how you can pay one price for a deal and then a couple months later it's even cheaper.
    1. I went by the reviews and now i am seeing a pattern on STEAM where even good reviews are bought and paid for and not really player revews and that actuallly watching game play from google will be my best option in the future. AGAIN don;t trust bought and paid for reviews from STEAM....I just learned and realised this now
    1. It is based on the idea that each validation is encapsulated by a simple, stateless predicate that receives some input and returns either true or false.
    2. URI::MailTo::EMAIL_REGEXP

      First time I've seen someone create a validator by simply matching against URI::MailTo::EMAIL_REGEXP from std lib. More often you see people copying and pasting some really long regex that they don't understand and is probably not loose enough. It's much better, though, to simply reuse a standard one from a library — by reference, rather than copying and pasting!!

    3. It will return the result as a Dry::Monads::Result object which can be either successful or failure.
    1. To understand this helper, you should understand that every step invocation calls Output() for you behind the scenes. The following DSL use is identical to the one [above]. class Execute < Trailblazer::Activity::Railway step :find_provider, Output(Trailblazer::Activity::Left, :failure) => Track(:failure), Output(Trailblazer::Activity::Right, :success) => Track(:success)
    2. In combination with [Track()], the :magnetic_to option allows for a neat way to spawn custom tracks outside of the conventional Railway or FastTrack schema.

      Instead of magnetic_to:, I propose wrapping the steps that are on a separate track in something like...

        DefTrack do :paypal do
          step :charge_paypal
        end
      

      or

        paypal_track = RailwayTrack do :paypal do
          step :charge_paypal
        end
      

      so we can reference it from outputs, like we can with tracks created with Path helper.

    3. The Subprocess macro will go through all outputs of the nested activity, query their semantics and search for tracks with the same semantic.
    4. For branching out a separate path in an activity, use the Path() macro. It’s a convenient, simple way to declare alternative routes

      Seems like this would be a very common need: once you switch to a custom failure track, you want it to stay on that track until the end!!!

      The problem is that in a Railway, everything automatically has 2 outputs. But we really only need one (which is exactly what Path gives us). And you end up fighting the defaults when there are the automatic 2 outputs, because you have to remember to explicitly/verbosely redirect all of those outputs or they may end up going somewhere you don't want them to go.

      The default behavior of everything going to the next defined step is not helpful for doing that, and in fact is quite frustrating because you don't want unrelated steps to accidentally end up on one of the tasks in your custom failure track.

      And you can't use fail for custom-track steps becase that breaks magnetic_to for some reason.

      I was finding myself very in need of something like this, and was about to write my own DSL, but then I discovered this. I still think it needs a better DSL than this, but at least they provided a way to do this. Much needed.

      For this example, I might write something like this:

      step :decide_type, Output(Activity::Left, :credit_card) => Track(:with_credit_card)
      
      # Create the track, which would automatically create an implicit End with the same id.
      Track(:with_credit_card) do
          step :authorize
          step :charge
      end
      

      I guess that's not much different than theirs. Main improvement is it avoids ugly need to specify end_id/end_task.

      But that wouldn't actually be enough either in this example, because you would actually want to have a failure track there and a path doesn't have one ... so it sounds like Subprocess and a new self-contained ProcessCreditCard Railway would be the best solution for this particular example... Subprocess is the ultimate in flexibility and gives us all the flexibility we need)


      But what if you had a path that you needed to direct to from 2 different tasks' outputs?

      Example: I came up with this, but it takes a lot of effort to keep my custom path/track hidden/"isolated" and prevent other tasks from automatically/implicitly going into those steps:

      class Example::ValidationErrorTrack < Trailblazer::Activity::Railway
        step :validate_model, Output(:failure) => Track(:validation_error)
        step :save,           Output(:failure) => Track(:validation_error)
      
        # Can't use fail here or the magnetic_to won't work and  Track(:validation_error) won't work
        step :log_validation_error, magnetic_to: :validation_error,
          Output(:success) => End(:validation_error), 
          Output(:failure) => End(:validation_error) 
      end
      
      puts Trailblazer::Developer.render o
      Reloading...
      
      #<Start/:default>
       {Trailblazer::Activity::Right} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=validate_model>
      #<Trailblazer::Activity::TaskBuilder::Task user_proc=validate_model>
       {Trailblazer::Activity::Left} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=log_validation_error>
       {Trailblazer::Activity::Right} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=save>
      #<Trailblazer::Activity::TaskBuilder::Task user_proc=save>
       {Trailblazer::Activity::Left} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=log_validation_error>
       {Trailblazer::Activity::Right} => #<End/:success>
      #<Trailblazer::Activity::TaskBuilder::Task user_proc=log_validation_error>
       {Trailblazer::Activity::Left} => #<End/:validation_error>
       {Trailblazer::Activity::Right} => #<End/:validation_error>
      #<End/:success>
      
      #<End/:validation_error>
      
      #<End/:failure>
      

      Now attempt to do it with Path... Does the Path() have an ID we can reference? Or maybe we just keep a reference to the object and use it directly in 2 different places?

      class Example::ValidationErrorTrack::VPathHelper1 < Trailblazer::Activity::Railway
         validation_error_path = Path(end_id: "End.validation_error", end_task: End(:validation_error)) do
          step :log_validation_error
        end
        step :validate_model, Output(:failure) => validation_error_path
        step :save,           Output(:failure) => validation_error_path
      end
      
      o=Example::ValidationErrorTrack::VPathHelper1; puts Trailblazer::Developer.render o
      Reloading...
      
      #<Start/:default>
       {Trailblazer::Activity::Right} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=validate_model>
      #<Trailblazer::Activity::TaskBuilder::Task user_proc=validate_model>
       {Trailblazer::Activity::Left} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=log_validation_error>
       {Trailblazer::Activity::Right} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=save>
      #<Trailblazer::Activity::TaskBuilder::Task user_proc=log_validation_error>
       {Trailblazer::Activity::Right} => #<End/:validation_error>
      #<Trailblazer::Activity::TaskBuilder::Task user_proc=save>
       {Trailblazer::Activity::Left} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=log_validation_error>
       {Trailblazer::Activity::Right} => #<End/:success>
      #<End/:success>
      
      #<End/:validation_error>
      
      #<End/:failure>
      

      It's just too bad that:

      • there's not a Railway helper in case you want multiple outputs, though we could probably create one pretty easily using Path as our template
      • we can't "inline" a separate Railway acitivity (Subprocess "nests" it rather than "inlines")
    5. Subprocess will try to match the nested ends’ semantics to the tracks it knows. You may wire custom ends using Output.
    6. A nested activity doesn’t have to have two ends, only.
    7. class Charge < Trailblazer::Activity::Path # ... step :validate step :decide_type, Output(Activity::Left, :credit_card) => Path(end_id: "End.cc", end_task: End(:with_cc)) do step :authorize step :charge end step :direct_debit end

      if you want to copy and paste to console:

      module Trailblazer
      class Charge < Trailblazer::Activity::Path
        # ...
        step :validate
        step :decide_type, Output(Activity::Left, :credit_card) => Path(end_id: "End.cc", end_task: End(:with_cc)) do
          step :authorize
          step :charge
        end
        step :direct_debit
      end
      end
      
      puts Trailblazer::Developer.render a
      
      #<Start/:default>
       {Trailblazer::Activity::Right} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=validate>
      #<Trailblazer::Activity::TaskBuilder::Task user_proc=validate>
       {Trailblazer::Activity::Right} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=decide_type>
      #<Trailblazer::Activity::TaskBuilder::Task user_proc=decide_type>
       {Trailblazer::Activity::Right} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=direct_debit>
       {Trailblazer::Activity::Left} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=authorize>
      #<Trailblazer::Activity::TaskBuilder::Task user_proc=authorize>
       {Trailblazer::Activity::Right} => #<Trailblazer::Activity::TaskBuilder::Task user_proc=charge>
      #<Trailblazer::Activity::TaskBuilder::Task user_proc=charge>
       {Trailblazer::Activity::Right} => #<End/:with_cc>
      #<Trailblazer::Activity::TaskBuilder::Task user_proc=direct_debit>
       {Trailblazer::Activity::Right} => #<End/:success>
      #<End/:success>
      
    8. step :direct_debit

      I don't think we would/should really want to make this the "success" (Right) path and :credit_card be the "failure" (Left) track.

      Maybe it's okay to repurpose Left and Right for something other than failure/success ... but only if we can actually change the default semantic of those signals/outputs. Is that possible? Maybe there's a way to override or delete the default outputs?

    9. Using Track() with a new track semantic only makes sense when using the [:magnetic_to option] on other tasks.
    10. Defaults names are given to steps without the :id options, but these might be awkward sometimes.

      Why would those default names ever be awkward?

      If you the default name is whatever comes after step:

      step :default_name
      

      then why can't you just change that name to whatever you want?

      To answer my own question: I think you can do that, as long as the name is the 1st argument to step. But below I noticed an example where a Subprocess was the 1st argument instead, and so it needs a name in this case:

      step Subprocess(DeleteAssets), id: :delete_assets
      

      Why are they inconsistent about calling it name or id? Which one is it? I guess it's an id since that's what the key is called, and since there's an Id() helper to reference a task by its id.

    11. Patching has no implicit, magical side-effects and is strongly encouraged to customize flows for a specific case in a quick and consise way.
    12. However, it can be a PITA if you want to customize one of those deeply nested components and add or remove a certain step, for example.
    13. In order to customize the latter one and add another step tidy_storage, you’d normally have to subclass all three activities and override steps.
    14. step :policy, before: :create_model
    15. the validate task gets removed, assuming the Admin won’t need a validation
    16. magnetic_to: :paypal
    17. step Subprocess(Memo::Validate), Output(:invalid_params) => Track(:failure)
    18. While you could nest an activity into another manually, the Subprocess macro will come in handy.
    19. The macro automatically wires all of Validate’s ends to the known counter-part tracks.
    20. Output() in combination with Path() allow very simple modelling for alternive routes.
    21. Note that the path ends in its very own end, signalizing a new end state, or outcome. The end’s semantic is :with_cc.
    22. Since notify sits on the “failure” track and hence is “magnetic to” :failure, find_provider will be connected to it.
    23. The Track() function will snap the output to the next task that is “magnetic to” the track’s semantic.
    24. This connects the failure output to the previous task, which might create an infinity loop and waste your computing time - it is solely here for demonstrational purposes.
    25. step :charge_creditcard, Output(:failure) => End(:declined)
    26. Adding ends to an activity is a beautiful way to communicate more than two outcomes to the outer world without having to use a state field in the ctx. It also allows wiring those outcomes to different tracks in the container activity.
    27. step :charge_creditcard, Output(:failure) => End(:success) end This reconnects both outputs to the same end, always ending in a - desirable, yet unrealistic - successful state.
    28. step :find_provider, Output(UsePaypal, :paypal) => Track(:paypal)
    29. The DSL will, per default, wrap every task with the Binary interface, meaning returning true will result in Activity::Right, and false in Activity::Left.
    30. Currently, only Right signals are wired up.
    31. Activity::Left

      Needs to be Trailblazer::Activity::Left

    32. Note that you can return either a boolean value or a [signal subclass] in order to dictate the direction of flow.
    1. You’re free to test this activity in a separate unit test.
    2. # Yes, you can use lambdas as steps, too! step ->(ctx, params:, **) { params.is_a?(Hash) }
    3. User.init!(User.new("apotonick@gmail.com"))

      That's not valid ActiveRecord. To be consistent with your example, it probably should be...

    4. The fact we’re using ActiveRecord (or something looking like it) doesn’t mean Trailblazer only works with Rails! Most people are familiar with its API, so we chose to use “ActiveRecord” in this tutorial.
    5. Keyword arguments allow to define particular parameters as required. Should the parameter be missing, they also provide a way to set a default value. This is all done with pure Ruby.
    1. Currently, only Right signals are wired up.

      So what happens if a task returns a Left signal?? Will it still go Right? Will it error?

    1. The sole purpose to add Dev::Trace::Inspector module is to make custom inspection possible and efficient while tracing. For example, ActiveRecord::Relation#inspect makes additional queries to fetch top 10 records and generate the output everytime. To avoid this, Inspector will not call inspect method when it finds such objects (deeply nested anywhere). Instead, it’ll call AR::Relation#to_sql to get plain SQL query which doesn’t make additional queries and is better to understand in tracing output.
    2. Please note that this is a higher-level debugging tool that does not confront you with a 200-lines stack trace the way Ruby does it, but pinpoints the exceptional code and locates the problem on a task level. This is possible due to you structuring code into higher abstractions, tasks and activities.
    1. At the request of the publisher, Tales of Monkey Island Complete Pack: Chapter 2 - The Siege of Spinner Cay is unlisted on the Steam store and will not appear in search.
    1. I think one thing would have been a solution to basically everything here: Player created maps. As Im involved in many modding communities, I know for a fact that player created content can be vital in making games last so much longer, and the quality can shoot for the stars, Player created maps would have been fantastic for this game.
    1. Licensed under the LGPLv3 license. We also offer a commercial-friendly license.
    2. With Activity, modeling business processes turns out to be ridiculously simple: You define what should happen and when, and Trailblazer makes sure that it happens.
    1. now that I realize how easy it is to just manually include this in my app: <%= javascript_include_tag 'xray', nonce: true if Rails.env.development? %> I regret even wasting my time getting it to automatically look for and add a nonce to the auto-injected xray.js script
    2. Wasted too much time getting it to work with old Rubies/Rails, when I think the correct path should be to just remove support for them going forward
    3. Open a separate PR to add Rails 6 to the CI matrix
    4. I'm not very familiar with this feature. Do you know what version of Rails is required for this code to work? Is it Rails 6.0+? We run the test suite via Travis CI for many versions of Rails so I am concerned that this will cause test failures on older versions. Can we write the tests so that they gracefully exclude the CSP stuff on older versions where CSP is not supported?
    5. (match = html.match(/<meta name="csp-nonce" content="([^"]*)"/)) && match[1] html[/<meta name="csp-nonce" content="([^"]*)"/, 1]
    6. Non-blocking suggestion: This is a little more elegant, if you prefer this syntax:
    7. This is failing CI because CI is testing against Rails < 6. I think the appropriate next steps are: Open a separate PR to add Rails 6 to the CI matrix Update this PR to only run CSP-related test code for Rails >= 6.0.0 Can you help with either or both of those?
    1. Maintaining the builds of your repositories should be everyone’s job. Instead of relying on that one build person in the team, Travis CI makes infrastructure and configuration a team responsibility.
    1. Rebasing For feature/topic branches, you should always use the --rebase flag to git pull, or if you are usually handling many temporary "to be in a github pull request" branches, run the following to automate this: git config branch.autosetuprebase local

      That's what I keep telling people. Glad to see I'm not the only one...

    1. While I certainly don’t think that all configuration should be “self hosted” in this kind of way

      how is it "self hosted"? in what way?

      I think I found the answer here https://github.com/rails/sprockets/blob/master/UPGRADING.md:

      One benefit of using a manifest.js file for this type of configuration is that now Sprockets is using Sprockets to understand what files need to be generated instead of a non-portable framework-specific interface.

      So it is "self-hosted" in that Sprockets is using Sprockets itself for this...?

    2. That’s it. If you have a previous “precompile” array, in your app config, it will continue to work. For continuity sake I recommend moving over those declarations to your manifest.js file so that it will be consistent.
    3. programmers can try to be aware of their configuration systems and the cognitive overhead they impose on people.
    4. As we know, naming is hard.
    5. Another thing I don’t like is the name of the config file manifest.js. Internally Sprockets has the concept of a manifest already Sprockets::Manifest, but the two aren’t directly coupled. We also already have a “manifest” JSON file that gets generated in public/assets/ and has manifest in the name .Sprockets-manifest-140998229eec5a9a5802b31d0ef6ed25.json. I know one is a JS file and one is a JSON file, but it’s a bit confusing to talk about.

      When I first heard of app/assets/config/manifest.js, I was a bit confused too, and assumed/wondered if it was related to the manifest it generates under public.

    6. The name makes me think of “The Legend of Zelda”. I imagine the original Sprockets author saying “It’s dangerous to go alone” and then handing me a javascript file.
    7. The link name is not very helpful, it doesn’t explain what it does very well.
    8. Instead of having this confusing maze of lambdas, regexes, and strings, we could, in theory, introduce a single entry point of configuration for Sprockets to use, and in that file declare all assets we wanted to compile. Well, that’s exactly what the manifest.js file is.
    9. I guess in short you could say that I don’t like this interface very much.
    10. Another big issue is that the config wasn’t really expressive enough. From the beginning Rails needed a way to say “only compile application.css and application.js, but compile ALL images” by default. With our previous interface, we’re limited to only strings.
    11. That’s pretty gnarly. While the name of the constant LOOSE_APP_ASSETS gives me some idea of what it does, it still takes a second to wrap your mind around. If you were trying to figure out what assets are being precompiled and you did a puts config.assets.precompile that lambda object would be utterly baffling.
    12. Another thing I don’t like: our asset behavior is decoupled from the assets. If you’re mucking around in your app/assets folder, then you have to first know that such a config exists, and then hunt it down in a totally different config folder. It would be nice if, while we’re working in asset land, we didn’t have to mentally jump around.
    13. For example, what if your site has a customer interface and an “admin” interface? If the two have totally different designs and features, then it might be considerable overhead to ship the entirety of the admin interface to every customer on the regular site.
    14. Before we get into what the manifest.js does, let’s look at what it is replacing.
    15. When Sprockets was introduced, one of the opinions that it held strongly, is that assets such as CSS and JS should be bundled together and served in one file.
    1. Local variables can even be declared with the same name as a global variable. If this happens, there are actually two different variables with the same name: one local and one global. This helps ensure that an author writing a local variable doesn’t accidentally change the value of a global variable they aren’t even aware of.
    2. Sass variables, like all Sass identifiers, treat hyphens and underscores as identical. This means that $font-size and $font_size both refer to the same variable. This is a historical holdover from the very early days of Sass, when it only allowed underscores in identifier names. Once Sass added support for hyphens to match CSS’s syntax, the two were made equivalent to make migration easier.
    1. At work, we often mention "throwing something over the fence" and "wrong rock" so there is (to us) a proverbial fence and a proverbial wrong rock.
    1. Ruby on Rails 6 Note:: With the release of Rails 6 there have been some minor changes made to the default configuration for The Asset Pipeline. In specific, by default Sprockets no longer processes JavaScript and instead Webpack is set as the default. The twbs/bootstrap-rubygem is for use with Sprockets not Webpack.
    1. Eyeglass provides a way to distribute Sass files and their associated assets and javascript extensions via npm such that any build system can be adapted to automatically expose those Sass files to Sass's @import directive by just installing the code.
    2. This project is provided by the LinkedIn Presentation Infrastructure team as open source software
    1. Keeping bootstrap-sass in sync with upstream changes from Bootstrap used to be an error prone and time consuming manual process. With Bootstrap 3 we have introduced a converter that automates this.
    2. Do not use *= require in Sass or your other stylesheets will not be able to access the Bootstrap mixins or variables.
    1. STATSD_SAMPLE_RATE: (default: 1.0)

      It's recommended to configure this library by setting environment variables.

      The thing I don't like about configuration via environment variables is that everything is limited/reduced to the string type. You can't even use simple numeric types, let alone nice rich value objects like you could if configuration were done in the native language (Ruby).

      If you try to, you get:

      ENV['STATSD_SAMPLE_RATE'] = 1
      config/initializers/statsd.rb:8:in `[]=': no implicit conversion of Integer into String (TypeError)
      
    2. This version makes the new client that was added in version 2.6+ the default client, and removes the legacy client. All previously deprecated functionality has been removed (since version 2.5, see below).
    1. Keep in mind that third party code with references to other files also processed by the asset Pipeline (images, stylesheets, etc.), will need to be rewritten to use helpers like asset_path.
    2. vendor/assets is for assets that are owned by outside entities, such as code for JavaScript plugins and CSS frameworks.
    1. Some assets will be compiled as top-level assets when they are referenced from inside of another asset. For example, the asset_url erb helper will automatically link assets:
    2. but if you were previously using regexp or proc values, they won't work at all with Sprockets 4, and if you try you'll get an exception raised that looks like NoMethodError: undefined method 'start_with?'
    3. To correct this, you can move these files to some subdirectory of ./app/assets/stylesheets or javascripts; or you can change the manifest.js to be more like how Rails with Sprockets 3 works, linking only the specific application files as top-level targets
    4. When compiling assets with Sprockets, Sprockets needs to decide which top-level targets to compile, usually application.css, application.js, and images.
    5. Here's the last issue where source maps were discussed before the beta release.
    6. How do you know if source maps are working correctly? Try adding a syntax error to one of your assets and use the console to debug. Does it show the correct file and source location? Or does it reference the top level application.js file?
    7. Source maps eliminate the need to serve these separate files. Instead, a special source map file can be read by the browser to help it understand how to unpack your assets. It "maps" the current, modified asset to its "source" so you can view the source when debugging. This way you can serve assets in development in the exact same way as in production.
    8. Source maps are a major new feature.
    9. Your Rails app Gemfile may have a line requiring sass-rails 5.0: gem 'sass-rails', '~> 5.0' # or gem 'sass-rails', '~> 5' These will prevent upgrade to sprockets 4, if you'd like to upgrade to sprockets 4 change to: gem 'sass-rails', '>= 5'
    10. Sprockets 3 was a compatibility release to bridge Sprockets 4, and many deprecated things have been removed in version 4.
    1. every human has a defined cognitive load that the memory can process. Making anyone process more information than defined will result in cognitive overloading.
    1. Please be aware travis-ci.org will be shutting down in several weeks, with all accounts migrating to travis-ci.com.
    2. Testing your open source projects will always be free! Seriously. Always. We like to think of it as our way of giving back to a community that connects so many people.